Skip to content

feat(ui): improve chat UI#1506

Closed
rafavalls wants to merge 9 commits intomainfrom
valls/ui-chat
Closed

feat(ui): improve chat UI#1506
rafavalls wants to merge 9 commits intomainfrom
valls/ui-chat

Conversation

@rafavalls
Copy link
Copy Markdown
Collaborator

@rafavalls rafavalls commented Oct 15, 2025

Changes

  • Moves "Add context" functionality from standalone @ button into a new + dropdown menu in chat input
  • Updates chat input layout with improved spacing and styling
  • Consolidates rules display into a single compact tooltip
  • Simplifies audio button and model selector styling
  • Removes (for now) unused file upload dialog trigger

UI Updates

  • Chat input now has borderless design with consistent padding
  • Bottom action bar with + button for adding context and files
  • Integration selection opens from dropdown instead of separate button
  • Rules shown in numbered list within tooltip

Summary by CodeRabbit

  • New Features

    • “Add context” dropdown with integration picker.
    • Rich text input exposes insertText and focus via ref.
    • Resizable panels support a minimum size.
  • Accessibility

    • Audio button title now toggles “Start voice input” / “Stop recording”.
    • Alt text added to agent avatar.
  • Style

    • Simplified audio button to a single icon ghost button.
    • Chat input redesigned with context area, file previews, and loading-aware send/stop control.
    • Context resources refactored into unified cards with a rule tooltip and bulk remove; typography and spacing tweaks across markdown, model selector, tooltip, and layout.
  • Public API Changes

    • Audio button and model selector accept className.
    • Resizable panel accepts minSize.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Oct 15, 2025

Walkthrough

Refactors multiple chat UI components: simplifies AudioButton, converts RichTextArea to forwardRef exposing insertText/focus, restructures ChatInput to include a context dropdown and RichText ref, removes openFileDialog from ContextResources, tweaks markdown font sizing and ModelSelector styling, constrains Decopilot/resizable layout sizing, and updates tooltip color tokens.

Changes

Cohort / File(s) Summary
Audio button simplification & styling
apps/web/src/components/chat/audio-button.tsx
Replaced nested layout with a single ghost Button; added optional className prop; dynamic title based on listening state; simplified Icon rendering.
Chat input restructuring & context dropdown
apps/web/src/components/chat/chat-input.tsx
Added DropdownMenu for "Add context", wired SelectConnectionDialog flow, introduced RichTextArea ref and hasContextResources, moved file-upload handling into ContextResources, assemble/send UIMessage with attachments, clear input/files on submit; removed rightNode prop.
RichText editor forwardRef & handle
apps/web/src/components/chat/rich-text.tsx
Converted to forwardRef, exported RichTextAreaHandle with insertText and focus via useImperativeHandle; added mention/avatar wrappers, tool/resource wiring, and content sync (setContent).
Context resources UI/props refactor
apps/web/src/components/chat/context-resources.tsx
Removed openFileDialog prop and its usage; consolidated tools setter; replaced per-rule inline UI with a hover tooltip listing rules (per-item removal + bulk "Remove all prompts"); updated file/integration preview layout and styling.
Model selector styling + className prop
apps/web/src/components/chat/model-selector.tsx
Added className?: string to ModelSelectorProps and merged it into Trigger; adjusted trigger height, padding, gap, and typography (presentational).
Markdown text sizing tweak
apps/web/src/components/chat/chat-markdown.tsx
Appended text-[0.9375rem] to paragraph and list item classNames; presentational only.
Message font size tweak
apps/web/src/components/chat/chat-message.tsx
Adjusted chat message font size to text-[0.9375rem] (presentational).
Decopilot layout & panel sizing
apps/web/src/components/decopilot/index.tsx, apps/web/src/components/layout/decopilot-layout.tsx
Header/container sizing adjusted (min-h/padding changes); MainChat uses full height (h-full); ResizablePanel used with minSize={15} and now accepts optional minSize prop.
Agent chat layout adjustments
apps/web/src/components/agent/chat.tsx, apps/web/src/components/agent/chats.tsx
Replaced explicit height logic with h-full; made input area flex-none to prevent growth.
Tooltip token updates
packages/ui/src/components/tooltip.tsx
Switched TooltipContent/TooltipArrow classes from bg-primary/text-primary-foreground to bg-foreground/text-background and fill-foreground; styling-only change.
Hook memoization
apps/web/src/hooks/use-agent-settings-tools-set.ts
Wrapped several internal handlers (enableAllTools, disableAllTools, setIntegrationTools, appendIntegrationTool) with useCallback for stable references; no API change.

Sequence Diagram(s)

sequenceDiagram
  participant U as User
  participant CI as ChatInput
  participant DD as DropdownMenu
  participant CR as ContextResources
  participant RTA as RichTextArea (forwardRef)
  participant S as Sender

  U->>CI: Open "Add context" (Dropdown)
  CI->>DD: render options
  U->>DD: Select integration/file
  DD->>CI: selection
  CI->>CR: enable/show resources
  Note right of CR: ContextResources rendered above editor

  U->>RTA: Type message / mention
  U->>CI: Click Send
  CI->>RTA: Read markdown value
  CI->>CR: Gather attachments (status=done)
  CI->>S: Submit UIMessage { text, attachments }
  S-->>CI: ack/loading
  CI-->>RTA: Clear input
  CI-->>CR: Clear files
Loading
sequenceDiagram
  participant CI as ChatInput
  participant RTA as RichTextArea
  participant REF as Ref Handle

  CI->>RTA: Mount with ref
  RTA-->>REF: expose { insertText(), focus() }
  CI->>REF: call focus() / insertText("@") when needed
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • viktormarinho
  • guitavano
  • lucis

Poem

I nibble at code with twitchy delight,
Ghost buttons gleam and dropdowns take flight.
Mentions learn tricks, the editor grows wise,
Files find their place and tooltips surprise.
A bunny-approved patch — soft, neat, and bright. 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The title "feat(ui): improve chat UI" is technically related to the changeset, which does include significant chat UI improvements. However, the title is vague and generic, using non-descriptive language ("improve") without conveying what specifically was improved or what the main focus of the changes is. While the changeset includes comprehensive refactoring—such as moving context functionality to a dropdown menu, consolidating rules into tooltips, updating component APIs (removing rightNode prop, adding className props), and adjusting styling across multiple components—none of this specificity is reflected in the title. A teammate scanning the PR history would struggle to understand the primary changes from this title alone.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed The pull request description adequately covers the "What is this contribution about?" section with clear, well-organized details of the changes split into a "Changes" section and a "UI Updates" section. The description effectively explains the refactoring, including the shift to dropdown-based context addition, rules consolidation into tooltips, styling simplifications, and layout improvements. However, the description is missing the "Screenshots/Demonstration" section, which would be particularly valuable for a UI-focused PR that affects multiple chat components. The "Review Checklist" section is also not completed. Despite these omissions, the core information about the changes is comprehensive and specific enough to understand the PR's scope and intent.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch valls/ui-chat

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 21fc27d and 6fd8ae9.

📒 Files selected for processing (6)
  • apps/web/src/components/chat/chat-input.tsx (5 hunks)
  • apps/web/src/components/chat/chat-message.tsx (1 hunks)
  • apps/web/src/components/chat/context-resources.tsx (5 hunks)
  • apps/web/src/components/chat/rich-text.tsx (2 hunks)
  • apps/web/src/components/decopilot/index.tsx (3 hunks)
  • apps/web/src/hooks/use-agent-settings-tools-set.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (9)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/data-flow.mdc)

**/*.{ts,tsx}: MCP tools must use Zod schemas for input and, when applicable, output validation
Register tools with server.registerTool providing description, inputSchema.shape, and optional outputSchema.shape
In every MCP tool handler, perform authorization checks first and then call context.resourceAccess.grant() before business logic
Name tools using the {RESOURCE}_{ACTION} pattern (e.g., AGENTS_CREATE, THREADS_LIST)
Group related tools into typed collections (e.g., GLOBAL_TOOLS, WORKSPACE_TOOLS) and export them as const
Always check workspace/team access before executing operations that touch workspace resources
Return proper authorization errors and handle forbidden operations explicitly

Prefer specific TypeScript types over any

**/*.{ts,tsx}: Write concise, maintainable, and technically accurate TypeScript code
Use functional and declarative programming patterns; avoid classes
Favor iteration and modularization to follow DRY and avoid code duplication
Use descriptive variable names with auxiliary verbs (e.g., isLoading, hasError)
Organize files so each contains only related content (components, subcomponents, helpers, static content, types)
Prefer interfaces over types for object shapes
Avoid enums; use maps (objects/records) instead
Use the function keyword for pure functions to benefit from hoisting and clarity
Ensure dependency arrays use stable references; do not inline new objects/arrays/functions
Memoize options/objects used in hooks (e.g., useMemo for options passed to useMemo/useCallback)
Favor named exports for functions

Files:

  • apps/web/src/components/chat/chat-message.tsx
  • apps/web/src/components/decopilot/index.tsx
  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/chat/chat-input.tsx
  • apps/web/src/hooks/use-agent-settings-tools-set.ts
  • apps/web/src/components/chat/context-resources.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/data-flow.mdc)

**/*.tsx: Use the shared KEYS object to construct consistent React Query keys
Use useSuspenseQuery for critical data fetching hooks
Implement optimistic updates for mutations and provide rollback on error via onMutate/onError
After successful mutations, update relevant caches with setQueryData (e.g., entity detail and list keys)
Leverage React Query’s built-in request deduplication instead of manual throttling
Use parallel queries where appropriate to reduce total load time

**/*.tsx: Use functional React components typed with TypeScript interfaces
Prefer using existing UI components from packages/ui over custom duplicates
Always use design system components from @deco/ui in UI code instead of hand-rolled equivalents
Memoize expensive computations with useMemo (e.g., filtering/sorting)
Memoize objects/arrays used in renders to keep stable references (useMemo)
Use useDeferredValue to keep search inputs responsive during heavy filtering
Follow MCP Tool data-access patterns (e.g., useAgents, useIntegrations) with proper loading/error states
Prefer react-hook-form with schema validation (e.g., zod) over manual useState for forms
Avoid prop drilling for forms by using a form context/provider
Adhere to Single Responsibility Principle by splitting large components into focused subcomponents
Design for composability and reusability of small UI components (e.g., Avatar variants)
Provide consistent loading and error states for data-driven components (e.g., Spinner, ErrorMessage, EmptyState)
Extract complex inline logic from JSX (avoid IIFEs) into small components/functions

Files:

  • apps/web/src/components/chat/chat-message.tsx
  • apps/web/src/components/decopilot/index.tsx
  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/chat/chat-input.tsx
  • apps/web/src/components/chat/context-resources.tsx
apps/web/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (apps/web/.cursor/rules/posthog-integration.mdc)

apps/web/**/*.{js,jsx,ts,tsx}: Use each feature flag in as few places as possible; if a flag must appear at multiple callsites, explicitly flag this for careful developer review
Gate any flag-dependent code behind checks that verify the flag’s values are valid and expected
If a custom person or event property is referenced in two or more files or at two or more callsites in the same file, centralize the keys in an enum (TS) or const object (JS)

Files:

  • apps/web/src/components/chat/chat-message.tsx
  • apps/web/src/components/decopilot/index.tsx
  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/chat/chat-input.tsx
  • apps/web/src/hooks/use-agent-settings-tools-set.ts
  • apps/web/src/components/chat/context-resources.tsx
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/web/.cursor/rules/posthog-integration.mdc)

In TypeScript, store feature flag names in an enum with members written UPPERCASE_WITH_UNDERSCORE and use a consistent naming convention

Files:

  • apps/web/src/components/chat/chat-message.tsx
  • apps/web/src/components/decopilot/index.tsx
  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/chat/chat-input.tsx
  • apps/web/src/hooks/use-agent-settings-tools-set.ts
  • apps/web/src/components/chat/context-resources.tsx
apps/web/**

📄 CodeRabbit inference engine (AGENTS.md)

Place the Vite/React web client in apps/web

Files:

  • apps/web/src/components/chat/chat-message.tsx
  • apps/web/src/components/decopilot/index.tsx
  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/chat/chat-input.tsx
  • apps/web/src/hooks/use-agent-settings-tools-set.ts
  • apps/web/src/components/chat/context-resources.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Biome formatting: use two-space indentation and double quotes
Keep imports sorted
Name hooks and utility functions using camelCase

Files:

  • apps/web/src/components/chat/chat-message.tsx
  • apps/web/src/components/decopilot/index.tsx
  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/chat/chat-input.tsx
  • apps/web/src/hooks/use-agent-settings-tools-set.ts
  • apps/web/src/components/chat/context-resources.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Name React components and classes using PascalCase

Files:

  • apps/web/src/components/chat/chat-message.tsx
  • apps/web/src/components/decopilot/index.tsx
  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/chat/chat-input.tsx
  • apps/web/src/components/chat/context-resources.tsx
{apps/web,packages}/**/*.{tsx,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Keep Tailwind design tokens consistent with the design system

Files:

  • apps/web/src/components/chat/chat-message.tsx
  • apps/web/src/components/decopilot/index.tsx
  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/chat/chat-input.tsx
  • apps/web/src/components/chat/context-resources.tsx
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/data-flow.mdc)

Log authorization failures to aid debugging and auditing

Files:

  • apps/web/src/hooks/use-agent-settings-tools-set.ts
🧠 Learnings (2)
📚 Learning: 2025-10-03T17:14:55.800Z
Learnt from: CR
PR: deco-cx/chat#0
File: .cursor/rules/react-ts.mdc:0-0
Timestamp: 2025-10-03T17:14:55.800Z
Learning: Applies to **/*.tsx : Follow MCP Tool data-access patterns (e.g., useAgents, useIntegrations) with proper loading/error states

Applied to files:

  • apps/web/src/components/chat/context-resources.tsx
📚 Learning: 2025-10-03T16:59:09.898Z
Learnt from: CR
PR: deco-cx/chat#0
File: .cursor/rules/react-ts.mdc:0-0
Timestamp: 2025-10-03T16:59:09.898Z
Learning: Applies to **/*.tsx : Follow established MCP data-access patterns (e.g., useAgents, useIntegrations, useUpdateAgent) with proper loading/error states

Applied to files:

  • apps/web/src/components/chat/context-resources.tsx
🧬 Code graph analysis (4)
apps/web/src/components/decopilot/index.tsx (1)
apps/web/src/components/agent/chat.tsx (1)
  • MainChat (88-109)
apps/web/src/components/chat/rich-text.tsx (7)
apps/web/src/components/prompts/rich-text/index.tsx (2)
  • RichTextArea (58-282)
  • Tool (47-56)
apps/web/src/components/agent/provider.tsx (1)
  • useAgent (670-674)
packages/sdk/src/hooks/mcp.ts (1)
  • useIntegrations (148-178)
apps/web/src/components/rich-text-editor/types.ts (2)
  • Tool (5-14)
  • MentionItem (44-44)
packages/sdk/src/mcp/integrations/api.ts (1)
  • callTool (172-238)
apps/web/src/hooks/use-agent-settings-tools-set.ts (1)
  • useAgentSettingsToolsSet (6-93)
apps/web/src/components/rich-text-editor/components/mention-dropdown.tsx (1)
  • MentionDropdown (28-325)
apps/web/src/components/chat/chat-input.tsx (6)
apps/web/src/components/agent/provider.tsx (1)
  • useAgent (670-674)
apps/web/src/hooks/use-agent-settings-tools-set.ts (1)
  • useAgentSettingsToolsSet (6-93)
apps/web/src/components/chat/rich-text.tsx (2)
  • RichTextAreaHandle (61-64)
  • RichTextArea (66-314)
packages/runtime/src/mastra.ts (1)
  • Integration (331-334)
apps/web/src/components/chat/context-resources.tsx (1)
  • ContextResources (43-278)
apps/web/src/components/integrations/select-connection-dialog.tsx (1)
  • SelectConnectionDialog (860-893)
apps/web/src/components/chat/context-resources.tsx (2)
apps/web/src/hooks/use-agent-settings-tools-set.ts (1)
  • useAgentSettingsToolsSet (6-93)
apps/web/src/components/integrations/common.tsx (1)
  • IntegrationIcon (19-35)
🔇 Additional comments (18)
apps/web/src/components/chat/chat-message.tsx (1)

118-118: LGTM!

The font size adjustment to text-[0.9375rem] aligns with the design system updates across chat components.

apps/web/src/hooks/use-agent-settings-tools-set.ts (2)

16-48: LGTM!

The memoization of enableAllTools with proper dependencies ensures stable function references and prevents unnecessary re-renders. The optimistic update pattern (setting empty array first at line 23, then populating tools later) is correct for the current implementation.


50-83: LGTM!

All handler functions are properly memoized with correct dependency arrays, ensuring stable references for child components and preventing unnecessary re-renders.

apps/web/src/components/chat/context-resources.tsx (3)

34-41: LGTM!

The removal of openFileDialog from the component's public API aligns with the PR's objective to move this functionality into the chat input's dropdown. The prop interface is now cleaner and focused on file management only.

Also applies to: 43-50, 58-58


191-250: LGTM!

The tooltip-based rules display with a proper button trigger addresses the accessibility concern from the previous review. The numbered list format provides a cleaner UX, and the "Remove all prompts" action is a useful addition.


340-427: LGTM!

The integration resource items maintain consistent styling with rounded corners, proper spacing, and clear tool selection UI. The popover provides detailed tool information and checkboxes for enabling/disabling individual tools.

apps/web/src/components/chat/rich-text.tsx (4)

61-64: LGTM!

The forwardRef pattern is correctly implemented, exposing RichTextAreaHandle with insertText and focus methods for imperative control from parent components.

Also applies to: 66-81


156-185: LGTM!

The memoized MentionDropdownWithAvatar wrapper now correctly includes appendIntegrationTool in the dependency array, addressing the previous review concern. The wrapper properly intercepts mention selection to update the agent's toolset before inserting the mention.


273-288: LGTM!

useImperativeHandle correctly exposes insertText and focus methods with proper dependency tracking. The methods check for editor existence and disabled state before executing operations.


187-227: LGTM!

The extensions array is properly memoized with comprehensive dependencies, ensuring the editor configuration updates when relevant props or computed values change.

apps/web/src/components/chat/chat-input.tsx (5)

145-173: LGTM!

The file handling now correctly filters for uploaded files with resolved URLs (line 146-147) and uses uf.url! (line 169) instead of falling back to blob URLs, addressing the previous review concern about sending non-fetchable URLs to the server.


55-56: LGTM!

The richTextRef and isDropdownOpen state additions support the new dropdown-based context selection UI. The handleAddIntegration callback properly enables all tools for the selected integration and closes the dropdown.

Also applies to: 130-138


74-81: LGTM!

The hasContextResources memo efficiently determines when to render the ContextResources component by checking for uploaded files, rules, or enabled tools. Dependencies are correctly tracked.


234-272: LGTM!

The DropdownMenu provides a clean UI for adding context integrations and files. The integration with SelectConnectionDialog allows users to select and enable integrations directly from the input area.


33-39: Retain the rightNode prop. It’s still passed in apps/web/src/components/agent/edit.tsx:162, so keep it in ChatInput’s interface.

apps/web/src/components/decopilot/index.tsx (3)

320-326: LGTM! Dependency array is more precise.

The updated dependency array correctly tracks specific properties (activeTab?.threadId, activeTab?.title) rather than the entire activeTab object, which follows React best practices. The guard condition on line 312 prevents infinite loops, though the effect will run one extra time after self-updating the title—this is harmless and acceptable.


401-401: LGTM! Flexible header height aligns with PR objectives.

The change from h-10 to min-h-10 with shrink-0 provides a flexible layout that can grow when needed while maintaining a minimum height. This aligns well with the PR's goal of removing fixed heights.


479-479: Past review comment addressed correctly!

The change from flex-1 to h-full correctly addresses the previous review feedback. Since the parent container (line 452) uses absolute inset-0 rather than being a flex container, h-full is the appropriate choice. This ensures MainChat fills its absolutely positioned parent, allowing the internal ScrollArea to function properly with a constrained height.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (8)
apps/web/src/components/chat/chat-input.tsx (4)

76-84: Use the RichTextArea ref for focusing instead of querying the DOM

Direct DOM query is brittle. Use the provided ref’s focus().

 useEffect(() => {
-  if (!isLoading) {
-    const editor = document.querySelector(".ProseMirror") as HTMLElement;
-    if (editor) {
-      editor.focus();
-    }
-  }
+  if (!isLoading) {
+    richTextRef.current?.focus();
+  }
 }, [isLoading]);

As per coding guidelines.


147-159: Clear input/files after a successful send (avoid losing user input on failure)

Currently you clear before awaiting. If send fails, the message and files are lost.

-// Clear input immediately before sending
-setInput("");
-clearFiles();
-setIsLoading(true);
+setIsLoading(true);
 try {
-  await sendMessage(message);
+  await sendMessage(message);
+  // Clear only after a successful send
+  setInput("");
+  clearFiles();
 } catch (error) {
   console.error("Failed to send message:", error);
 } finally {
   setIsLoading(false);
 }

As per coding guidelines.


57-61: Make canSubmit a boolean

Ensure boolean type for clearer intent and fewer surprises in downstream expressions.

-const canSubmit =
-  !isLoading &&
-  input?.trim() &&
-  !uploadedFiles.some((uf) => uf.status === "uploading");
+const canSubmit =
+  !isLoading &&
+  Boolean(input?.trim()) &&
+  !uploadedFiles.some((uf) => uf.status === "uploading");

91-102: Prefer requestSubmit over dispatching a custom submit Event

This triggers native form submission (and React onSubmit) more reliably.

-const formEvent = new Event("submit", {
-  bubbles: true,
-  cancelable: true,
-});
-e.currentTarget.closest("form")?.dispatchEvent(formEvent);
+const form = e.currentTarget.closest("form") as HTMLFormElement | null;
+if (form) {
+  if (typeof form.requestSubmit === "function") {
+    form.requestSubmit();
+  } else {
+    form.dispatchEvent(
+      new Event("submit", { bubbles: true, cancelable: true }),
+    );
+  }
+}
apps/web/src/components/chat/context-resources.tsx (4)

98-110: Avoid casts for tools count; type the integration shape with optional tools

Casting to Integration and using non-null assertions can hide type mismatches. Prefer a local type with optional tools or a type guard.

-const integrationsWithTotalTools = useMemo(() => {
-  return integrationsWithTools.map((item) => {
-    // Use integration.tools?.length for total tools count, handle nullable field
-    const totalTools =
-      (item.integration as Integration).tools?.length ||
-      item.enabledTools.length;
-    return {
-      ...item,
-      totalTools,
-    };
-  });
-}, [integrationsWithTools]);
+type IntegrationWithTools = Integration & {
+  tools?: { name: string; description?: string }[];
+};
+const integrationsWithTotalTools = useMemo(() => {
+  return integrationsWithTools.map((item) => {
+    const integ = item.integration as IntegrationWithTools;
+    const totalTools = integ.tools?.length ?? item.enabledTools.length;
+    return { ...item, totalTools };
+  });
+}, [integrationsWithTools]);

As per coding guidelines.


191-242: Use semantic ordered list for numbered rules in the tooltip

Improves accessibility and reflects the numbering structurally.

-<TooltipContent className="max-w-md p-0" align="start">
-  <div className="max-h-[300px] overflow-y-auto">
-    {persistedRules.map((rule, index) => (
-      <div key={rule.id} className="group/rule p-3 text-sm border-b border-border/15 last:border-0 hover:bg-accent/10 transition-colors relative">
-        <div className="flex items-start gap-2 pr-6">
-          <span className="text-xs text-muted-foreground font-medium flex-shrink-0 mt-0.5">
-            {index + 1}.
-          </span>
-          <p className="text-xs leading-relaxed break-words flex-1 line-clamp-4">
-            {rule.text}
-          </p>
-        </div>
-        ...
-      </div>
-    ))}
-  </div>
-</TooltipContent>
+<TooltipContent className="max-w-md p-0" align="start">
+  <ol className="max-h-[300px] overflow-y-auto list-decimal pl-5">
+    {persistedRules.map((rule) => (
+      <li
+        key={rule.id}
+        className="group/rule p-3 text-sm border-b border-border/15 last:border-0 hover:bg-accent/10 transition-colors relative"
+      >
+        <p className="text-xs leading-relaxed break-words flex-1 line-clamp-4">
+          {rule.text}
+        </p>
+        ...
+      </li>
+    ))}
+  </ol>
+</TooltipContent>

367-395: Use a stable key for tool items

Using index as key risks incorrect reconciliation on list changes. Use tool.name.

-{(integration as Integration).tools!.map(
-  (tool, index: number) => {
+{(integration as Integration).tools!.map(
+  (tool) => {
     const isEnabled = enabledTools.includes(tool.name);
     return (
-      <div
-        key={index}
+      <div
+        key={tool.name}
         className="flex items-center justify-between text-xs px-2 py-1 bg-muted rounded hover:bg-muted/80"
       >
         ...
       </div>
     );
   },
 )}

461-478: Add alt text to uploaded image previews

Improves accessibility; use the file name as alt.

-{file.type.startsWith("image/") && url ? (
-  <img src={url} className="h-full w-full object-cover" />
+{file.type.startsWith("image/") && url ? (
+  <img src={url} alt={file.name} className="h-full w-full object-cover" />
 ) : (
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a65f737 and ae716b1.

📒 Files selected for processing (9)
  • apps/web/src/components/chat/audio-button.tsx (1 hunks)
  • apps/web/src/components/chat/chat-input.tsx (4 hunks)
  • apps/web/src/components/chat/chat-markdown.tsx (2 hunks)
  • apps/web/src/components/chat/context-resources.tsx (6 hunks)
  • apps/web/src/components/chat/model-selector.tsx (2 hunks)
  • apps/web/src/components/chat/rich-text.tsx (2 hunks)
  • apps/web/src/components/decopilot/index.tsx (2 hunks)
  • apps/web/src/components/layout/decopilot-layout.tsx (1 hunks)
  • packages/ui/src/components/tooltip.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (12)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/data-flow.mdc)

**/*.{ts,tsx}: MCP tools must use Zod schemas for input and, when applicable, output validation
Register tools with server.registerTool providing description, inputSchema.shape, and optional outputSchema.shape
In every MCP tool handler, perform authorization checks first and then call context.resourceAccess.grant() before business logic
Name tools using the {RESOURCE}_{ACTION} pattern (e.g., AGENTS_CREATE, THREADS_LIST)
Group related tools into typed collections (e.g., GLOBAL_TOOLS, WORKSPACE_TOOLS) and export them as const
Always check workspace/team access before executing operations that touch workspace resources
Return proper authorization errors and handle forbidden operations explicitly

Prefer specific TypeScript types over any

**/*.{ts,tsx}: Write concise, maintainable, and technically accurate TypeScript code
Use functional and declarative programming patterns; avoid classes
Favor iteration and modularization to follow DRY and avoid code duplication
Use descriptive variable names with auxiliary verbs (e.g., isLoading, hasError)
Organize files so each contains only related content (components, subcomponents, helpers, static content, types)
Prefer interfaces over types for object shapes
Avoid enums; use maps (objects/records) instead
Use the function keyword for pure functions to benefit from hoisting and clarity
Ensure dependency arrays use stable references; do not inline new objects/arrays/functions
Memoize options/objects used in hooks (e.g., useMemo for options passed to useMemo/useCallback)
Favor named exports for functions

Files:

  • apps/web/src/components/chat/chat-markdown.tsx
  • apps/web/src/components/chat/audio-button.tsx
  • packages/ui/src/components/tooltip.tsx
  • apps/web/src/components/decopilot/index.tsx
  • apps/web/src/components/chat/model-selector.tsx
  • apps/web/src/components/chat/context-resources.tsx
  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/layout/decopilot-layout.tsx
  • apps/web/src/components/chat/chat-input.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/data-flow.mdc)

**/*.tsx: Use the shared KEYS object to construct consistent React Query keys
Use useSuspenseQuery for critical data fetching hooks
Implement optimistic updates for mutations and provide rollback on error via onMutate/onError
After successful mutations, update relevant caches with setQueryData (e.g., entity detail and list keys)
Leverage React Query’s built-in request deduplication instead of manual throttling
Use parallel queries where appropriate to reduce total load time

**/*.tsx: Use functional React components typed with TypeScript interfaces
Prefer using existing UI components from packages/ui over custom duplicates
Always use design system components from @deco/ui in UI code instead of hand-rolled equivalents
Memoize expensive computations with useMemo (e.g., filtering/sorting)
Memoize objects/arrays used in renders to keep stable references (useMemo)
Use useDeferredValue to keep search inputs responsive during heavy filtering
Follow MCP Tool data-access patterns (e.g., useAgents, useIntegrations) with proper loading/error states
Prefer react-hook-form with schema validation (e.g., zod) over manual useState for forms
Avoid prop drilling for forms by using a form context/provider
Adhere to Single Responsibility Principle by splitting large components into focused subcomponents
Design for composability and reusability of small UI components (e.g., Avatar variants)
Provide consistent loading and error states for data-driven components (e.g., Spinner, ErrorMessage, EmptyState)
Extract complex inline logic from JSX (avoid IIFEs) into small components/functions

Files:

  • apps/web/src/components/chat/chat-markdown.tsx
  • apps/web/src/components/chat/audio-button.tsx
  • packages/ui/src/components/tooltip.tsx
  • apps/web/src/components/decopilot/index.tsx
  • apps/web/src/components/chat/model-selector.tsx
  • apps/web/src/components/chat/context-resources.tsx
  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/layout/decopilot-layout.tsx
  • apps/web/src/components/chat/chat-input.tsx
apps/web/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (apps/web/.cursor/rules/posthog-integration.mdc)

apps/web/**/*.{js,jsx,ts,tsx}: Use each feature flag in as few places as possible; if a flag must appear at multiple callsites, explicitly flag this for careful developer review
Gate any flag-dependent code behind checks that verify the flag’s values are valid and expected
If a custom person or event property is referenced in two or more files or at two or more callsites in the same file, centralize the keys in an enum (TS) or const object (JS)

Files:

  • apps/web/src/components/chat/chat-markdown.tsx
  • apps/web/src/components/chat/audio-button.tsx
  • apps/web/src/components/decopilot/index.tsx
  • apps/web/src/components/chat/model-selector.tsx
  • apps/web/src/components/chat/context-resources.tsx
  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/layout/decopilot-layout.tsx
  • apps/web/src/components/chat/chat-input.tsx
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/web/.cursor/rules/posthog-integration.mdc)

In TypeScript, store feature flag names in an enum with members written UPPERCASE_WITH_UNDERSCORE and use a consistent naming convention

Files:

  • apps/web/src/components/chat/chat-markdown.tsx
  • apps/web/src/components/chat/audio-button.tsx
  • apps/web/src/components/decopilot/index.tsx
  • apps/web/src/components/chat/model-selector.tsx
  • apps/web/src/components/chat/context-resources.tsx
  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/layout/decopilot-layout.tsx
  • apps/web/src/components/chat/chat-input.tsx
apps/web/**

📄 CodeRabbit inference engine (AGENTS.md)

Place the Vite/React web client in apps/web

Files:

  • apps/web/src/components/chat/chat-markdown.tsx
  • apps/web/src/components/chat/audio-button.tsx
  • apps/web/src/components/decopilot/index.tsx
  • apps/web/src/components/chat/model-selector.tsx
  • apps/web/src/components/chat/context-resources.tsx
  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/layout/decopilot-layout.tsx
  • apps/web/src/components/chat/chat-input.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Biome formatting: use two-space indentation and double quotes
Keep imports sorted
Name hooks and utility functions using camelCase

Files:

  • apps/web/src/components/chat/chat-markdown.tsx
  • apps/web/src/components/chat/audio-button.tsx
  • packages/ui/src/components/tooltip.tsx
  • apps/web/src/components/decopilot/index.tsx
  • apps/web/src/components/chat/model-selector.tsx
  • apps/web/src/components/chat/context-resources.tsx
  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/layout/decopilot-layout.tsx
  • apps/web/src/components/chat/chat-input.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Name React components and classes using PascalCase

Files:

  • apps/web/src/components/chat/chat-markdown.tsx
  • apps/web/src/components/chat/audio-button.tsx
  • packages/ui/src/components/tooltip.tsx
  • apps/web/src/components/decopilot/index.tsx
  • apps/web/src/components/chat/model-selector.tsx
  • apps/web/src/components/chat/context-resources.tsx
  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/layout/decopilot-layout.tsx
  • apps/web/src/components/chat/chat-input.tsx
{apps/web,packages}/**/*.{tsx,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Keep Tailwind design tokens consistent with the design system

Files:

  • apps/web/src/components/chat/chat-markdown.tsx
  • apps/web/src/components/chat/audio-button.tsx
  • packages/ui/src/components/tooltip.tsx
  • apps/web/src/components/decopilot/index.tsx
  • apps/web/src/components/chat/model-selector.tsx
  • apps/web/src/components/chat/context-resources.tsx
  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/layout/decopilot-layout.tsx
  • apps/web/src/components/chat/chat-input.tsx
packages/ui/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/structure.mdc)

Place reusable design system components (shadcn/ui based) under packages/ui/src

Files:

  • packages/ui/src/components/tooltip.tsx
packages/**

📄 CodeRabbit inference engine (AGENTS.md)

Keep shared logic (UI kit, SDK, runtime, CLI tooling) under packages/

Files:

  • packages/ui/src/components/tooltip.tsx
packages/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Enforce kebab-case filenames in shared packages

Files:

  • packages/ui/src/components/tooltip.tsx
packages/ui/src/components/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/react-ts.mdc)

Use design system form primitives (Form, FormField, FormItem, etc.) when building forms

Files:

  • packages/ui/src/components/tooltip.tsx
🧬 Code graph analysis (5)
apps/web/src/components/decopilot/index.tsx (1)
apps/web/src/components/agent/chat.tsx (1)
  • MainChat (29-52)
apps/web/src/components/chat/context-resources.tsx (2)
apps/web/src/hooks/use-agent-settings-tools-set.ts (1)
  • useAgentSettingsToolsSet (5-80)
apps/web/src/components/integrations/common.tsx (1)
  • IntegrationIcon (19-35)
apps/web/src/components/chat/rich-text.tsx (8)
apps/web/src/components/prompts/rich-text/index.tsx (1)
  • Tool (47-56)
apps/web/src/components/agent/provider.tsx (1)
  • useAgent (664-668)
apps/web/src/hooks/use-agent-settings-tools-set.ts (1)
  • useAgentSettingsToolsSet (5-80)
apps/web/src/components/rich-text-editor/extensions/mention-node.tsx (1)
  • MentionNode (12-55)
apps/web/src/components/rich-text-editor/components/mention-dropdown.tsx (1)
  • MentionDropdown (28-296)
apps/web/src/components/chat/extensions/no-new-line.ts (1)
  • NoNewLine (7-21)
apps/web/src/components/rich-text-editor/extensions/unified-mentions.ts (1)
  • createUnifiedMentions (25-448)
packages/ui/src/lib/utils.ts (1)
  • cn (4-6)
apps/web/src/components/layout/decopilot-layout.tsx (1)
packages/ui/src/components/resizable.tsx (1)
  • ResizablePanel (56-56)
apps/web/src/components/chat/chat-input.tsx (7)
apps/web/src/components/agent/provider.tsx (1)
  • useAgent (664-668)
apps/web/src/hooks/use-user-preferences.ts (1)
  • useUserPreferences (35-54)
apps/web/src/hooks/use-agent-settings-tools-set.ts (1)
  • useAgentSettingsToolsSet (5-80)
apps/web/src/components/chat/rich-text.tsx (2)
  • RichTextAreaHandle (61-64)
  • RichTextArea (66-312)
packages/runtime/src/mastra.ts (1)
  • Integration (331-334)
packages/sdk/src/models/mcp.ts (1)
  • Integration (90-90)
apps/web/src/components/integrations/select-connection-dialog.tsx (1)
  • SelectConnectionDialog (860-893)
🔇 Additional comments (3)
apps/web/src/components/chat/model-selector.tsx (2)

113-115: LGTM! Styling improvements align with the UI refresh.

The spacing and font size adjustments improve visual consistency across the chat interface. The text size increase from text-xs to text-sm also enhances readability.


149-153: LGTM! Comprehensive styling update for the trigger button.

The multiple styling adjustments create a more cohesive visual design with improved hover states, consistent sizing, and refined spacing. The changes align well with the broader chat UI refresh objectives.

apps/web/src/components/chat/chat-input.tsx (1)

203-241: Add-context dropdown wiring looks good

Nice integration of SelectConnectionDialog as a dropdown item; onSelect closes the menu and enables tools.

Comment on lines +83 to +90
<button
type="button"
onClick={toggleListening}
className="flex size-8 items-center justify-center rounded-full p-1 hover:bg-accent transition-colors text-muted-foreground"
title={isListening ? "Stop recording" : "Start voice input"}
>
<Icon name={isListening ? "stop" : "mic"} size={20} />
</button>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Restore accessible labeling for the toggle button.

Switching to a naked <button> dropped the descriptive label/state. Screen readers now encounter an unlabeled control with no pressed state, which is an accessibility regression. Please add an aria-label matching the title and reflect the on/off state with aria-pressed.

       type="button"
       onClick={toggleListening}
       className="flex size-8 items-center justify-center rounded-full p-1 hover:bg-accent transition-colors text-muted-foreground"
       title={isListening ? "Stop recording" : "Start voice input"}
+      aria-label={isListening ? "Stop recording" : "Start voice input"}
+      aria-pressed={isListening}
     >
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<button
type="button"
onClick={toggleListening}
className="flex size-8 items-center justify-center rounded-full p-1 hover:bg-accent transition-colors text-muted-foreground"
title={isListening ? "Stop recording" : "Start voice input"}
>
<Icon name={isListening ? "stop" : "mic"} size={20} />
</button>
<button
type="button"
onClick={toggleListening}
className="flex size-8 items-center justify-center rounded-full p-1 hover:bg-accent transition-colors text-muted-foreground"
title={isListening ? "Stop recording" : "Start voice input"}
aria-label={isListening ? "Stop recording" : "Start voice input"}
aria-pressed={isListening}
>
<Icon name={isListening ? "stop" : "mic"} size={20} />
</button>
🤖 Prompt for AI Agents
In apps/web/src/components/chat/audio-button.tsx around lines 83 to 90, the
button was made without accessible labeling or state; add an aria-label
attribute whose value matches the title (use isListening ? "Stop recording" :
"Start voice input") and add aria-pressed={isListening} so screen readers
receive the descriptive label and the on/off state; keep the existing title and
icon but ensure these two ARIA attributes are present and derived from
isListening.

Comment thread apps/web/src/components/chat/chat-input.tsx Outdated
Comment thread apps/web/src/components/chat/rich-text.tsx Outdated
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Oct 15, 2025

Deploying chat with  Cloudflare Pages  Cloudflare Pages

Latest commit: 6fd8ae9
Status: ✅  Deploy successful!
Preview URL: https://0cd95d91.chat-46r.pages.dev
Branch Preview URL: https://valls-ui-chat.chat-46r.pages.dev

View logs

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/src/components/chat/context-resources.tsx (1)

336-355: Popover trigger uses a div; swap to button for accessibility

The trigger should be focusable/operable via keyboard. Use a button and add an aria-label.

-        <PopoverTrigger asChild>
-          <div className="flex items-center gap-2.5 cursor-pointer rounded flex-1">
+        <PopoverTrigger asChild>
+          <button
+            type="button"
+            aria-label={`Configure ${integration.name} tools`}
+            className="flex items-center gap-2.5 rounded flex-1 text-left"
+          >
             <div className="flex items-center justify-center size-8 rounded overflow-hidden bg-background border border-border flex-shrink-0">
               <IntegrationIcon
                 icon={integration.icon}
                 name={integration.name}
-                className="size-full"
+                className="size-full"
               />
             </div>
             <div className="flex flex-col min-w-0 flex-1">
               <div className="text-xs font-normal truncate leading-4">
                 {integration.name}
               </div>
               <div className="text-xs text-muted-foreground leading-4">
                 {enabledTools.length}/{totalTools}
               </div>
             </div>
-          </div>
+          </button>
         </PopoverTrigger>
♻️ Duplicate comments (3)
apps/web/src/components/chat/rich-text.tsx (1)

155-183: Refresh MentionDropdown memo when appendIntegrationTool changes

The empty dependency array captures a stale appendIntegrationTool when agent context changes. Recreate the wrapper when the handler changes.

-    }, []); // appendIntegrationTool should be stable, no need to list it
+    }, [appendIntegrationTool]);
apps/web/src/components/chat/audio-button.tsx (1)

83-90: Restore accessible labeling on the audio toggle.

The plain <button> still has no descriptive label or pressed state, so screen readers encounter an unlabeled control and lose on/off feedback—an accessibility regression from the previous implementation. Add aria-label and aria-pressed derived from isListening alongside the existing title.

       onClick={toggleListening}
       className="flex size-8 items-center justify-center rounded-full p-1 hover:bg-accent transition-colors text-muted-foreground"
       title={isListening ? "Stop recording" : "Start voice input"}
+      aria-label={isListening ? "Stop recording" : "Start voice input"}
+      aria-pressed={isListening}
     >
apps/web/src/components/chat/chat-input.tsx (1)

102-128: Only send uploaded files that expose a real URL.

We’re still attaching blob URLs via URL.createObjectURL, which the server can’t fetch and which can leak memory. Attachments should only ship once the uploader provides a remote uf.url, and the extra mediaType field should go away to match the UIMessage shape. Filter for uf.url and drop the blob fallback.

-    const doneFiles = uploadedFiles.filter((uf) => uf.status === "done");
+    const doneFiles = uploadedFiles.filter(
+      (uf) => uf.status === "done" && !!uf.url,
+    );
@@
-      const fileParts = doneFiles.map((uf) => ({
-        type: "file" as const,
-        name: uf.file.name,
-        contentType: uf.file.type,
-        mediaType: uf.file.type,
-        size: uf.file.size,
-        url: uf.url || URL.createObjectURL(uf.file),
-      }));
+      const fileParts = doneFiles.map((uf) => ({
+        type: "file" as const,
+        name: uf.file.name,
+        contentType: uf.file.type,
+        size: uf.file.size,
+        url: uf.url!,
+      }));
🧹 Nitpick comments (1)
apps/web/src/components/chat/context-resources.tsx (1)

403-421: Add aria-label to the remove integration button

Title alone isn’t sufficient for screen readers. Include an explicit aria-label.

-      <Button
+      <Button
+        aria-label={`Remove ${integration.name} integration`}
         type="button"
         variant="ghost"
         size="icon"
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ae716b1 and 2bc172d.

📒 Files selected for processing (10)
  • apps/web/src/components/chat/audio-button.tsx (1 hunks)
  • apps/web/src/components/chat/chat-input.tsx (3 hunks)
  • apps/web/src/components/chat/chat-markdown.tsx (2 hunks)
  • apps/web/src/components/chat/context-resources.tsx (6 hunks)
  • apps/web/src/components/chat/model-selector.tsx (2 hunks)
  • apps/web/src/components/chat/rich-text.tsx (2 hunks)
  • apps/web/src/components/decopilot/index.tsx (2 hunks)
  • apps/web/src/components/layout/decopilot-layout.tsx (1 hunks)
  • apps/web/src/components/sidebar/index.tsx (3 hunks)
  • packages/ui/src/components/tooltip.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/ui/src/components/tooltip.tsx
  • apps/web/src/components/layout/decopilot-layout.tsx
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/data-flow.mdc)

**/*.{ts,tsx}: MCP tools must use Zod schemas for input and, when applicable, output validation
Register tools with server.registerTool providing description, inputSchema.shape, and optional outputSchema.shape
In every MCP tool handler, perform authorization checks first and then call context.resourceAccess.grant() before business logic
Name tools using the {RESOURCE}_{ACTION} pattern (e.g., AGENTS_CREATE, THREADS_LIST)
Group related tools into typed collections (e.g., GLOBAL_TOOLS, WORKSPACE_TOOLS) and export them as const
Always check workspace/team access before executing operations that touch workspace resources
Return proper authorization errors and handle forbidden operations explicitly

Prefer specific TypeScript types over any

**/*.{ts,tsx}: Write concise, maintainable, and technically accurate TypeScript code
Use functional and declarative programming patterns; avoid classes
Favor iteration and modularization to follow DRY and avoid code duplication
Use descriptive variable names with auxiliary verbs (e.g., isLoading, hasError)
Organize files so each contains only related content (components, subcomponents, helpers, static content, types)
Prefer interfaces over types for object shapes
Avoid enums; use maps (objects/records) instead
Use the function keyword for pure functions to benefit from hoisting and clarity
Ensure dependency arrays use stable references; do not inline new objects/arrays/functions
Memoize options/objects used in hooks (e.g., useMemo for options passed to useMemo/useCallback)
Favor named exports for functions

Files:

  • apps/web/src/components/sidebar/index.tsx
  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/chat/chat-markdown.tsx
  • apps/web/src/components/chat/model-selector.tsx
  • apps/web/src/components/decopilot/index.tsx
  • apps/web/src/components/chat/audio-button.tsx
  • apps/web/src/components/chat/chat-input.tsx
  • apps/web/src/components/chat/context-resources.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/data-flow.mdc)

**/*.tsx: Use the shared KEYS object to construct consistent React Query keys
Use useSuspenseQuery for critical data fetching hooks
Implement optimistic updates for mutations and provide rollback on error via onMutate/onError
After successful mutations, update relevant caches with setQueryData (e.g., entity detail and list keys)
Leverage React Query’s built-in request deduplication instead of manual throttling
Use parallel queries where appropriate to reduce total load time

**/*.tsx: Use functional React components typed with TypeScript interfaces
Prefer using existing UI components from packages/ui over custom duplicates
Always use design system components from @deco/ui in UI code instead of hand-rolled equivalents
Memoize expensive computations with useMemo (e.g., filtering/sorting)
Memoize objects/arrays used in renders to keep stable references (useMemo)
Use useDeferredValue to keep search inputs responsive during heavy filtering
Follow MCP Tool data-access patterns (e.g., useAgents, useIntegrations) with proper loading/error states
Prefer react-hook-form with schema validation (e.g., zod) over manual useState for forms
Avoid prop drilling for forms by using a form context/provider
Adhere to Single Responsibility Principle by splitting large components into focused subcomponents
Design for composability and reusability of small UI components (e.g., Avatar variants)
Provide consistent loading and error states for data-driven components (e.g., Spinner, ErrorMessage, EmptyState)
Extract complex inline logic from JSX (avoid IIFEs) into small components/functions

Files:

  • apps/web/src/components/sidebar/index.tsx
  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/chat/chat-markdown.tsx
  • apps/web/src/components/chat/model-selector.tsx
  • apps/web/src/components/decopilot/index.tsx
  • apps/web/src/components/chat/audio-button.tsx
  • apps/web/src/components/chat/chat-input.tsx
  • apps/web/src/components/chat/context-resources.tsx
apps/web/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (apps/web/.cursor/rules/posthog-integration.mdc)

apps/web/**/*.{js,jsx,ts,tsx}: Use each feature flag in as few places as possible; if a flag must appear at multiple callsites, explicitly flag this for careful developer review
Gate any flag-dependent code behind checks that verify the flag’s values are valid and expected
If a custom person or event property is referenced in two or more files or at two or more callsites in the same file, centralize the keys in an enum (TS) or const object (JS)

Files:

  • apps/web/src/components/sidebar/index.tsx
  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/chat/chat-markdown.tsx
  • apps/web/src/components/chat/model-selector.tsx
  • apps/web/src/components/decopilot/index.tsx
  • apps/web/src/components/chat/audio-button.tsx
  • apps/web/src/components/chat/chat-input.tsx
  • apps/web/src/components/chat/context-resources.tsx
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/web/.cursor/rules/posthog-integration.mdc)

In TypeScript, store feature flag names in an enum with members written UPPERCASE_WITH_UNDERSCORE and use a consistent naming convention

Files:

  • apps/web/src/components/sidebar/index.tsx
  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/chat/chat-markdown.tsx
  • apps/web/src/components/chat/model-selector.tsx
  • apps/web/src/components/decopilot/index.tsx
  • apps/web/src/components/chat/audio-button.tsx
  • apps/web/src/components/chat/chat-input.tsx
  • apps/web/src/components/chat/context-resources.tsx
apps/web/**

📄 CodeRabbit inference engine (AGENTS.md)

Place the Vite/React web client in apps/web

Files:

  • apps/web/src/components/sidebar/index.tsx
  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/chat/chat-markdown.tsx
  • apps/web/src/components/chat/model-selector.tsx
  • apps/web/src/components/decopilot/index.tsx
  • apps/web/src/components/chat/audio-button.tsx
  • apps/web/src/components/chat/chat-input.tsx
  • apps/web/src/components/chat/context-resources.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Biome formatting: use two-space indentation and double quotes
Keep imports sorted
Name hooks and utility functions using camelCase

Files:

  • apps/web/src/components/sidebar/index.tsx
  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/chat/chat-markdown.tsx
  • apps/web/src/components/chat/model-selector.tsx
  • apps/web/src/components/decopilot/index.tsx
  • apps/web/src/components/chat/audio-button.tsx
  • apps/web/src/components/chat/chat-input.tsx
  • apps/web/src/components/chat/context-resources.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Name React components and classes using PascalCase

Files:

  • apps/web/src/components/sidebar/index.tsx
  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/chat/chat-markdown.tsx
  • apps/web/src/components/chat/model-selector.tsx
  • apps/web/src/components/decopilot/index.tsx
  • apps/web/src/components/chat/audio-button.tsx
  • apps/web/src/components/chat/chat-input.tsx
  • apps/web/src/components/chat/context-resources.tsx
{apps/web,packages}/**/*.{tsx,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Keep Tailwind design tokens consistent with the design system

Files:

  • apps/web/src/components/sidebar/index.tsx
  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/chat/chat-markdown.tsx
  • apps/web/src/components/chat/model-selector.tsx
  • apps/web/src/components/decopilot/index.tsx
  • apps/web/src/components/chat/audio-button.tsx
  • apps/web/src/components/chat/chat-input.tsx
  • apps/web/src/components/chat/context-resources.tsx
🧬 Code graph analysis (4)
apps/web/src/components/chat/rich-text.tsx (9)
apps/web/src/components/prompts/rich-text/index.tsx (1)
  • Tool (47-56)
apps/web/src/components/agent/provider.tsx (1)
  • useAgent (670-674)
packages/sdk/src/hooks/mcp.ts (1)
  • useIntegrations (148-178)
apps/web/src/components/rich-text-editor/types.ts (1)
  • Tool (5-14)
packages/sdk/src/mcp/integrations/api.ts (1)
  • callTool (172-238)
apps/web/src/hooks/use-agent-settings-tools-set.ts (1)
  • useAgentSettingsToolsSet (5-80)
apps/web/src/components/rich-text-editor/extensions/mention-node.tsx (1)
  • MentionNode (12-55)
apps/web/src/components/common/avatar/integration.tsx (1)
  • IntegrationAvatar (23-61)
apps/web/src/components/rich-text-editor/components/mention-dropdown.tsx (1)
  • MentionDropdown (28-296)
apps/web/src/components/decopilot/index.tsx (1)
apps/web/src/components/agent/chat.tsx (1)
  • MainChat (29-52)
apps/web/src/components/chat/chat-input.tsx (5)
apps/web/src/components/agent/provider.tsx (1)
  • useAgent (670-674)
apps/web/src/hooks/use-agent-settings-tools-set.ts (1)
  • useAgentSettingsToolsSet (5-80)
apps/web/src/components/chat/rich-text.tsx (2)
  • RichTextAreaHandle (61-64)
  • RichTextArea (66-312)
apps/web/src/components/chat/context-resources.tsx (1)
  • ContextResources (43-274)
apps/web/src/components/integrations/select-connection-dialog.tsx (1)
  • SelectConnectionDialog (860-893)
apps/web/src/components/chat/context-resources.tsx (2)
apps/web/src/hooks/use-agent-settings-tools-set.ts (1)
  • useAgentSettingsToolsSet (5-80)
apps/web/src/components/integrations/common.tsx (1)
  • IntegrationIcon (19-35)
🔇 Additional comments (7)
apps/web/src/components/chat/model-selector.tsx (2)

113-116: LGTM! Consistent spacing and typography improvements.

The gap and text size increases improve readability and visual consistency across the SelectedModelDisplay component.


149-153: LGTM! Well-coordinated trigger styling updates.

The styling changes work together coherently:

  • Height reduction (h-8) with added padding (py-1) maintains proper content fit
  • Text size increase (text-sm) matches the SelectedModelDisplay change
  • border-0 ensures borderless appearance across all viewports, while md:border-none provides additional control for the borderless variant
  • Added hover state, rounded corners, and gap improve the interactive experience
apps/web/src/components/sidebar/index.tsx (1)

445-446: Separator spacing change looks good

Margin adjustments align separators with surrounding content; no functional impact observed.

Also applies to: 505-506, 957-958

apps/web/src/components/decopilot/index.tsx (1)

33-34: Layout refinements LGTM

min-h-10/shrink-0 on header and flex-1 min-h-0 on MainChat prevent overflow and enable proper flex sizing.

Also applies to: 73-74

apps/web/src/components/chat/chat-markdown.tsx (1)

150-151: Typography tune LGTM

Setting 0.9375rem for paragraphs and list items aligns chat text sizing consistently.

Also applies to: 173-174

apps/web/src/components/chat/context-resources.tsx (1)

461-490: File preview layout changes LGTM

Consistent tokens (size-8, rounded, text-xs/leading-4) and states look coherent with the new design.

apps/web/src/components/chat/rich-text.tsx (1)

66-85: RichTextArea forwardRef API: solid and minimal

  • Ref exposes insertText and focus; handlers are correctly guarded and memoized.
  • Editor config and controlled updates look sound.

Consider confirming upstream: parent components treat value as the expected format for setContent (HTML vs Markdown). If value is Markdown, ensure consistent parsing to avoid content drift.

Also applies to: 270-286, 288-311

Comment thread apps/web/src/components/chat/context-resources.tsx
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
apps/web/src/components/chat/rich-text.tsx (1)

155-183: Refresh MentionDropdown memo when appendIntegrationTool changes.

The wrapper is memoized with [], so it captures the initial appendIntegrationTool. When agent context (and the underlying hook) changes, we'll still call the stale function and mutate the wrong toolset. Please include appendIntegrationTool in the dependency list so the memo tracks the current handler.

-    }, []); // appendIntegrationTool should be stable, no need to list it
+    }, [appendIntegrationTool]);
apps/web/src/components/chat/chat-input.tsx (1)

137-163: Don't send blob object URLs; require uploaded URLs for attachments.

URL.createObjectURL creates blob URLs that are not fetchable by servers and can leak memory. Only attach files that have a resolved remote URL from the uploader.

-const doneFiles = uploadedFiles.filter((uf) => uf.status === "done");
+const doneFiles = uploadedFiles.filter(
+  (uf) => uf.status === "done" && !!uf.url,
+);

 ...

-const fileParts = doneFiles.map((uf) => ({
-  type: "file" as const,
-  name: uf.file.name,
-  contentType: uf.file.type,
-  mediaType: uf.file.type,
-  size: uf.file.size,
-  url: uf.url || URL.createObjectURL(uf.file),
-}));
+const fileParts = doneFiles.map((uf) => ({
+  type: "file" as const,
+  name: uf.file.name,
+  contentType: uf.file.type,
+  size: uf.file.size,
+  url: uf.url!, // ensured by filter above
+}));
🧹 Nitpick comments (2)
apps/web/src/components/chat/model-selector.tsx (2)

155-157: Consider simplifying border styling.

The border-0 class on line 155 removes all borders unconditionally, making the md:border-none condition on line 156 redundant. Consider removing the borderless variant check or moving border-0 into it.

Additionally, the !important modifier on h-8 is a code smell. If this is needed to override ResponsiveSelectTrigger's defaults, consider if the base component should expose a size prop instead.

Apply this diff to remove the redundancy:

-          "!h-8 text-sm hover:bg-accent rounded-xl py-1 px-2 gap-1 shadow-none cursor-pointer border-0 group",
-          variant === "borderless" && "md:border-none",
+          "!h-8 text-sm hover:bg-accent rounded-xl py-1 px-2 gap-1 shadow-none cursor-pointer group",
+          variant === "borderless" ? "border-0" : "border md:border-none",
           className,

That said, the overall styling changes appropriately use design system tokens and correctly pass through the className prop.


107-120: Optional: SelectedModelDisplay memo may be ineffective.

SelectedModelDisplay is wrapped in memo (line 107), but the selectedModel passed to it at line 137 is computed with models.find(), which creates a new object reference on every render. This makes the memoization ineffective.

If optimizing renders is important, consider memoizing selectedModel:

+  const selectedModel = useMemo(
+    () => models.find((m) => m.id === model) || DEFAULT_MODEL,
+    [models, model]
+  );
-  const selectedModel = models.find((m) => m.id === model) || DEFAULT_MODEL;

Otherwise, the memo wrapper on SelectedModelDisplay could be removed as it provides no benefit.

Also applies to: 137-137

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2bc172d and a19f0a5.

📒 Files selected for processing (5)
  • apps/web/src/components/chat/audio-button.tsx (2 hunks)
  • apps/web/src/components/chat/chat-input.tsx (3 hunks)
  • apps/web/src/components/chat/context-resources.tsx (6 hunks)
  • apps/web/src/components/chat/model-selector.tsx (3 hunks)
  • apps/web/src/components/chat/rich-text.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/web/src/components/chat/audio-button.tsx
  • apps/web/src/components/chat/context-resources.tsx
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/data-flow.mdc)

**/*.{ts,tsx}: MCP tools must use Zod schemas for input and, when applicable, output validation
Register tools with server.registerTool providing description, inputSchema.shape, and optional outputSchema.shape
In every MCP tool handler, perform authorization checks first and then call context.resourceAccess.grant() before business logic
Name tools using the {RESOURCE}_{ACTION} pattern (e.g., AGENTS_CREATE, THREADS_LIST)
Group related tools into typed collections (e.g., GLOBAL_TOOLS, WORKSPACE_TOOLS) and export them as const
Always check workspace/team access before executing operations that touch workspace resources
Return proper authorization errors and handle forbidden operations explicitly

Prefer specific TypeScript types over any

**/*.{ts,tsx}: Write concise, maintainable, and technically accurate TypeScript code
Use functional and declarative programming patterns; avoid classes
Favor iteration and modularization to follow DRY and avoid code duplication
Use descriptive variable names with auxiliary verbs (e.g., isLoading, hasError)
Organize files so each contains only related content (components, subcomponents, helpers, static content, types)
Prefer interfaces over types for object shapes
Avoid enums; use maps (objects/records) instead
Use the function keyword for pure functions to benefit from hoisting and clarity
Ensure dependency arrays use stable references; do not inline new objects/arrays/functions
Memoize options/objects used in hooks (e.g., useMemo for options passed to useMemo/useCallback)
Favor named exports for functions

Files:

  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/chat/chat-input.tsx
  • apps/web/src/components/chat/model-selector.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/data-flow.mdc)

**/*.tsx: Use the shared KEYS object to construct consistent React Query keys
Use useSuspenseQuery for critical data fetching hooks
Implement optimistic updates for mutations and provide rollback on error via onMutate/onError
After successful mutations, update relevant caches with setQueryData (e.g., entity detail and list keys)
Leverage React Query’s built-in request deduplication instead of manual throttling
Use parallel queries where appropriate to reduce total load time

**/*.tsx: Use functional React components typed with TypeScript interfaces
Prefer using existing UI components from packages/ui over custom duplicates
Always use design system components from @deco/ui in UI code instead of hand-rolled equivalents
Memoize expensive computations with useMemo (e.g., filtering/sorting)
Memoize objects/arrays used in renders to keep stable references (useMemo)
Use useDeferredValue to keep search inputs responsive during heavy filtering
Follow MCP Tool data-access patterns (e.g., useAgents, useIntegrations) with proper loading/error states
Prefer react-hook-form with schema validation (e.g., zod) over manual useState for forms
Avoid prop drilling for forms by using a form context/provider
Adhere to Single Responsibility Principle by splitting large components into focused subcomponents
Design for composability and reusability of small UI components (e.g., Avatar variants)
Provide consistent loading and error states for data-driven components (e.g., Spinner, ErrorMessage, EmptyState)
Extract complex inline logic from JSX (avoid IIFEs) into small components/functions

Files:

  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/chat/chat-input.tsx
  • apps/web/src/components/chat/model-selector.tsx
apps/web/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (apps/web/.cursor/rules/posthog-integration.mdc)

apps/web/**/*.{js,jsx,ts,tsx}: Use each feature flag in as few places as possible; if a flag must appear at multiple callsites, explicitly flag this for careful developer review
Gate any flag-dependent code behind checks that verify the flag’s values are valid and expected
If a custom person or event property is referenced in two or more files or at two or more callsites in the same file, centralize the keys in an enum (TS) or const object (JS)

Files:

  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/chat/chat-input.tsx
  • apps/web/src/components/chat/model-selector.tsx
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/web/.cursor/rules/posthog-integration.mdc)

In TypeScript, store feature flag names in an enum with members written UPPERCASE_WITH_UNDERSCORE and use a consistent naming convention

Files:

  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/chat/chat-input.tsx
  • apps/web/src/components/chat/model-selector.tsx
apps/web/**

📄 CodeRabbit inference engine (AGENTS.md)

Place the Vite/React web client in apps/web

Files:

  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/chat/chat-input.tsx
  • apps/web/src/components/chat/model-selector.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Biome formatting: use two-space indentation and double quotes
Keep imports sorted
Name hooks and utility functions using camelCase

Files:

  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/chat/chat-input.tsx
  • apps/web/src/components/chat/model-selector.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Name React components and classes using PascalCase

Files:

  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/chat/chat-input.tsx
  • apps/web/src/components/chat/model-selector.tsx
{apps/web,packages}/**/*.{tsx,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Keep Tailwind design tokens consistent with the design system

Files:

  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/chat/chat-input.tsx
  • apps/web/src/components/chat/model-selector.tsx
🧬 Code graph analysis (3)
apps/web/src/components/chat/rich-text.tsx (14)
apps/web/src/components/prompts/rich-text/index.tsx (2)
  • RichTextArea (58-280)
  • Tool (47-56)
apps/web/src/components/agent/provider.tsx (1)
  • useAgent (670-674)
packages/sdk/src/hooks/mcp.ts (1)
  • useIntegrations (148-178)
packages/ai/src/tools.ts (1)
  • tools (872-881)
apps/web/src/components/rich-text-editor/types.ts (2)
  • Tool (5-14)
  • MentionItem (44-44)
packages/sdk/src/mcp/integrations/api.ts (2)
  • IntegrationWithTools (789-791)
  • callTool (172-238)
packages/sdk/src/mcp/index.ts (1)
  • IntegrationWithTools (58-58)
packages/runtime/src/mastra.ts (1)
  • Integration (331-334)
apps/web/src/hooks/use-agent-settings-tools-set.ts (1)
  • useAgentSettingsToolsSet (5-80)
apps/web/src/components/rich-text-editor/extensions/mention-node.tsx (1)
  • MentionNode (12-55)
apps/web/src/components/rich-text-editor/components/mention-dropdown.tsx (1)
  • MentionDropdown (28-296)
apps/web/src/components/chat/extensions/no-new-line.ts (1)
  • NoNewLine (7-21)
packages/create-deco/index.js (1)
  • args (15-15)
packages/ui/src/lib/utils.ts (1)
  • cn (4-6)
apps/web/src/components/chat/chat-input.tsx (5)
apps/web/src/components/agent/provider.tsx (1)
  • useAgent (670-674)
apps/web/src/hooks/use-agent-settings-tools-set.ts (1)
  • useAgentSettingsToolsSet (5-80)
apps/web/src/components/chat/rich-text.tsx (2)
  • RichTextAreaHandle (61-64)
  • RichTextArea (66-312)
packages/runtime/src/mastra.ts (1)
  • Integration (331-334)
apps/web/src/components/integrations/select-connection-dialog.tsx (1)
  • SelectConnectionDialog (860-893)
apps/web/src/components/chat/model-selector.tsx (1)
packages/sdk/src/constants.ts (1)
  • DEFAULT_MODEL (209-209)
🔇 Additional comments (15)
apps/web/src/components/chat/model-selector.tsx (3)

113-118: LGTM! Typography and spacing updates align with the UI refresh.

The gap and text styling changes appropriately use design system tokens and implement the group-hover interaction correctly.


126-126: LGTM! className prop enables external styling.

The optional className prop follows the pattern established in the broader UI refactor and maintains type safety.


133-133: LGTM! Destructuring consistent with interface.

apps/web/src/components/chat/rich-text.tsx (7)

61-64: LGTM! Clean imperative handle interface.

The RichTextAreaHandle interface provides a well-defined API for parent components to control the editor programmatically.


66-84: LGTM! Proper forwardRef implementation.

The component correctly uses forwardRef and integrates well with the agent context and integrations hooks.


85-108: LGTM! Correct tools flattening logic.

The tools are properly flattened from integrations with appropriate filtering and unique ID generation.


110-136: LGTM! Well-structured resource searcher logic.

The resource searchers are correctly filtered and structured, with appropriate pattern matching and duplicate prevention.


185-225: LGTM! Proper extensions configuration.

The extensions are correctly configured with appropriate dependencies and conditional logic for tool mentions.


227-268: LGTM! Robust editor setup with error handling.

The editor initialization and synchronization effects are well-implemented with appropriate error handling and proper dependencies.


270-310: LGTM! Well-implemented imperative handle.

The useImperativeHandle correctly exposes editor methods with proper dependencies. The inline styles approach for scoped styling is acceptable, though consider extracting to a CSS module if this pattern expands.

apps/web/src/components/chat/chat-input.tsx (5)

2-33: LGTM! Clean imports and dependencies.

All new imports are appropriately used in the component implementation.


49-73: LGTM! Proper state and ref management.

The component correctly initializes state and refs, and the hasContextResources computation properly checks all relevant sources.


122-130: LGTM! Clean integration handler.

The callback properly enables tools and closes the dropdown, with correct dependencies.


192-220: LGTM! Clean layout with proper ref integration.

The context resources and input area are well-structured, with the RichTextArea ref properly wired for programmatic control.


222-302: LGTM! Well-structured action bar with dropdown integration.

The bottom action bar is cleanly implemented with proper dropdown behavior, conditional rendering, and good accessibility. The submit/stop button logic correctly handles the loading state.

…utton and ChatInput components, enhancing accessibility and styling consistency
…n integration and improved styling

- Added a dropdown menu to the ChatInput component for adding context and integrations.
- Updated AudioButton styling to include text-muted-foreground for better visibility.
- Refactored ContextResources to remove unused openFileDialog prop and adjusted layout for better responsiveness.
- Improved RichTextArea to support ref forwarding for better integration with parent components.
…r preferences

- Removed the model selector and associated state management from ChatInput for a cleaner interface.
- Updated SidebarSeparator components to adjust margin styles for improved layout consistency.
…roved styling and functionality

- Updated AudioButton to use the Button component for better styling and accessibility.
- Added className prop to AudioButton for customizable styling.
- Enhanced ChatInput to check for context resources before displaying them.
- Refactored button styles in ChatInput for consistency and improved hover effects.
- Adjusted RichTextArea opacity for better visibility in the editor.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/src/components/chat/chat-input.tsx (1)

36-55: Fix duplicate function export declarations.

The file has two export function ChatInput declarations (lines 36 and 45), causing a syntax error. Remove the first declaration (lines 36-44) to resolve the pipeline failure.

Apply this diff:

-export function ChatInput({
-  disabled,
-  rightNode,
-}: {
-  disabled?: boolean;
-  rightNode?: ReactNode;
-} = {}) {
-  const { chat, uiOptions, input, setInput, isLoading, setIsLoading } =
-    useAgent();
 export function ChatInput({ disabled }: { disabled?: boolean } = {}) {
   const {
     chat,
♻️ Duplicate comments (4)
apps/web/src/components/chat/audio-button.tsx (1)

88-102: Critical accessibility issue: Missing ARIA attributes for screen readers.

The button lacks aria-label and aria-pressed attributes, making it inaccessible to screen reader users. Without these attributes:

  • Screen readers cannot describe the button's purpose
  • The recording/stopped state is not announced
  • This violates WCAG accessibility guidelines

This issue was flagged in a previous review and remains unresolved.

Apply this diff to add the required ARIA attributes:

     <Button
       type="button"
       variant="ghost"
       size="icon"
       onClick={toggleListening}
       className={cn(
         "size-8 rounded-full transition-colors text-muted-foreground",
         className,
       )}
       title={isListening ? "Stop recording" : "Start voice input"}
+      aria-label={isListening ? "Stop recording" : "Start voice input"}
+      aria-pressed={isListening}
     >
       <Icon name={isListening ? "stop" : "mic"} size={20} />
     </Button>
apps/web/src/components/chat/context-resources.tsx (1)

193-248: Tooltip trigger is a non-focusable div; use a button for keyboard accessibility.

Keyboard users can't focus or activate the rules tooltip. Replace the div on line 197 with a semantic button element.

Apply this diff:

-              <TooltipTrigger asChild>
-                <div className="flex items-center justify-center size-10 rounded-lg bg-muted cursor-pointer">
-                  <Icon name="rule" size={20} className="text-foreground" />
-                </div>
-              </TooltipTrigger>
+              <TooltipTrigger asChild>
+                <button
+                  type="button"
+                  aria-label="Show prompts"
+                  className="flex items-center justify-center size-10 rounded-lg bg-muted"
+                >
+                  <Icon name="rule" size={20} className="text-foreground" />
+                </button>
+              </TooltipTrigger>

As per coding guidelines.

apps/web/src/components/chat/rich-text.tsx (1)

157-185: Refresh MentionDropdown memo when appendIntegrationTool changes.

The wrapper is memoized with [], so it captures the initial appendIntegrationTool. When the agent context changes, the stale function reference will mutate the wrong toolset. Include appendIntegrationTool in the dependency array.

Apply this diff:

-    }, []); // appendIntegrationTool should be stable, no need to list it
+    }, [appendIntegrationTool]);
apps/web/src/components/chat/chat-input.tsx (1)

151-177: Don't send blob object URLs; require uploaded URLs for attachments.

URL.createObjectURL (line 173) creates blob URLs that are not fetchable by servers and can leak memory. Only attach files that have a resolved remote URL from the uploader.

Apply this diff:

-const doneFiles = uploadedFiles.filter((uf) => uf.status === "done");
+const doneFiles = uploadedFiles.filter(
+  (uf) => uf.status === "done" && !!uf.url,
+);

 ...

-const fileParts = doneFiles.map((uf) => ({
-  type: "file" as const,
-  name: uf.file.name,
-  contentType: uf.file.type,
-  mediaType: uf.file.type,
-  size: uf.file.size,
-  url: uf.url || URL.createObjectURL(uf.file),
-}));
+const fileParts = doneFiles.map((uf) => ({
+  type: "file" as const,
+  name: uf.file.name,
+  contentType: uf.file.type,
+  size: uf.file.size,
+  url: uf.url!, // ensured by filter above
+}));
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a19f0a5 and c6287f7.

📒 Files selected for processing (9)
  • apps/web/src/components/chat/audio-button.tsx (2 hunks)
  • apps/web/src/components/chat/chat-input.tsx (6 hunks)
  • apps/web/src/components/chat/chat-markdown.tsx (2 hunks)
  • apps/web/src/components/chat/context-resources.tsx (5 hunks)
  • apps/web/src/components/chat/model-selector.tsx (3 hunks)
  • apps/web/src/components/chat/rich-text.tsx (2 hunks)
  • apps/web/src/components/decopilot/index.tsx (2 hunks)
  • apps/web/src/components/layout/decopilot-layout.tsx (1 hunks)
  • packages/ui/src/components/tooltip.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/web/src/components/layout/decopilot-layout.tsx
  • apps/web/src/components/chat/chat-markdown.tsx
  • packages/ui/src/components/tooltip.tsx
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/data-flow.mdc)

**/*.{ts,tsx}: MCP tools must use Zod schemas for input and, when applicable, output validation
Register tools with server.registerTool providing description, inputSchema.shape, and optional outputSchema.shape
In every MCP tool handler, perform authorization checks first and then call context.resourceAccess.grant() before business logic
Name tools using the {RESOURCE}_{ACTION} pattern (e.g., AGENTS_CREATE, THREADS_LIST)
Group related tools into typed collections (e.g., GLOBAL_TOOLS, WORKSPACE_TOOLS) and export them as const
Always check workspace/team access before executing operations that touch workspace resources
Return proper authorization errors and handle forbidden operations explicitly

Prefer specific TypeScript types over any

**/*.{ts,tsx}: Write concise, maintainable, and technically accurate TypeScript code
Use functional and declarative programming patterns; avoid classes
Favor iteration and modularization to follow DRY and avoid code duplication
Use descriptive variable names with auxiliary verbs (e.g., isLoading, hasError)
Organize files so each contains only related content (components, subcomponents, helpers, static content, types)
Prefer interfaces over types for object shapes
Avoid enums; use maps (objects/records) instead
Use the function keyword for pure functions to benefit from hoisting and clarity
Ensure dependency arrays use stable references; do not inline new objects/arrays/functions
Memoize options/objects used in hooks (e.g., useMemo for options passed to useMemo/useCallback)
Favor named exports for functions

Files:

  • apps/web/src/components/chat/context-resources.tsx
  • apps/web/src/components/chat/audio-button.tsx
  • apps/web/src/components/decopilot/index.tsx
  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/chat/model-selector.tsx
  • apps/web/src/components/chat/chat-input.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/data-flow.mdc)

**/*.tsx: Use the shared KEYS object to construct consistent React Query keys
Use useSuspenseQuery for critical data fetching hooks
Implement optimistic updates for mutations and provide rollback on error via onMutate/onError
After successful mutations, update relevant caches with setQueryData (e.g., entity detail and list keys)
Leverage React Query’s built-in request deduplication instead of manual throttling
Use parallel queries where appropriate to reduce total load time

**/*.tsx: Use functional React components typed with TypeScript interfaces
Prefer using existing UI components from packages/ui over custom duplicates
Always use design system components from @deco/ui in UI code instead of hand-rolled equivalents
Memoize expensive computations with useMemo (e.g., filtering/sorting)
Memoize objects/arrays used in renders to keep stable references (useMemo)
Use useDeferredValue to keep search inputs responsive during heavy filtering
Follow MCP Tool data-access patterns (e.g., useAgents, useIntegrations) with proper loading/error states
Prefer react-hook-form with schema validation (e.g., zod) over manual useState for forms
Avoid prop drilling for forms by using a form context/provider
Adhere to Single Responsibility Principle by splitting large components into focused subcomponents
Design for composability and reusability of small UI components (e.g., Avatar variants)
Provide consistent loading and error states for data-driven components (e.g., Spinner, ErrorMessage, EmptyState)
Extract complex inline logic from JSX (avoid IIFEs) into small components/functions

Files:

  • apps/web/src/components/chat/context-resources.tsx
  • apps/web/src/components/chat/audio-button.tsx
  • apps/web/src/components/decopilot/index.tsx
  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/chat/model-selector.tsx
  • apps/web/src/components/chat/chat-input.tsx
apps/web/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (apps/web/.cursor/rules/posthog-integration.mdc)

apps/web/**/*.{js,jsx,ts,tsx}: Use each feature flag in as few places as possible; if a flag must appear at multiple callsites, explicitly flag this for careful developer review
Gate any flag-dependent code behind checks that verify the flag’s values are valid and expected
If a custom person or event property is referenced in two or more files or at two or more callsites in the same file, centralize the keys in an enum (TS) or const object (JS)

Files:

  • apps/web/src/components/chat/context-resources.tsx
  • apps/web/src/components/chat/audio-button.tsx
  • apps/web/src/components/decopilot/index.tsx
  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/chat/model-selector.tsx
  • apps/web/src/components/chat/chat-input.tsx
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/web/.cursor/rules/posthog-integration.mdc)

In TypeScript, store feature flag names in an enum with members written UPPERCASE_WITH_UNDERSCORE and use a consistent naming convention

Files:

  • apps/web/src/components/chat/context-resources.tsx
  • apps/web/src/components/chat/audio-button.tsx
  • apps/web/src/components/decopilot/index.tsx
  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/chat/model-selector.tsx
  • apps/web/src/components/chat/chat-input.tsx
apps/web/**

📄 CodeRabbit inference engine (AGENTS.md)

Place the Vite/React web client in apps/web

Files:

  • apps/web/src/components/chat/context-resources.tsx
  • apps/web/src/components/chat/audio-button.tsx
  • apps/web/src/components/decopilot/index.tsx
  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/chat/model-selector.tsx
  • apps/web/src/components/chat/chat-input.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Biome formatting: use two-space indentation and double quotes
Keep imports sorted
Name hooks and utility functions using camelCase

Files:

  • apps/web/src/components/chat/context-resources.tsx
  • apps/web/src/components/chat/audio-button.tsx
  • apps/web/src/components/decopilot/index.tsx
  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/chat/model-selector.tsx
  • apps/web/src/components/chat/chat-input.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Name React components and classes using PascalCase

Files:

  • apps/web/src/components/chat/context-resources.tsx
  • apps/web/src/components/chat/audio-button.tsx
  • apps/web/src/components/decopilot/index.tsx
  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/chat/model-selector.tsx
  • apps/web/src/components/chat/chat-input.tsx
{apps/web,packages}/**/*.{tsx,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Keep Tailwind design tokens consistent with the design system

Files:

  • apps/web/src/components/chat/context-resources.tsx
  • apps/web/src/components/chat/audio-button.tsx
  • apps/web/src/components/decopilot/index.tsx
  • apps/web/src/components/chat/rich-text.tsx
  • apps/web/src/components/chat/model-selector.tsx
  • apps/web/src/components/chat/chat-input.tsx
🧠 Learnings (2)
📚 Learning: 2025-10-03T17:14:55.800Z
Learnt from: CR
PR: deco-cx/chat#0
File: .cursor/rules/react-ts.mdc:0-0
Timestamp: 2025-10-03T17:14:55.800Z
Learning: Applies to **/*.tsx : Follow MCP Tool data-access patterns (e.g., useAgents, useIntegrations) with proper loading/error states

Applied to files:

  • apps/web/src/components/chat/context-resources.tsx
📚 Learning: 2025-10-03T16:59:09.898Z
Learnt from: CR
PR: deco-cx/chat#0
File: .cursor/rules/react-ts.mdc:0-0
Timestamp: 2025-10-03T16:59:09.898Z
Learning: Applies to **/*.tsx : Follow established MCP data-access patterns (e.g., useAgents, useIntegrations, useUpdateAgent) with proper loading/error states

Applied to files:

  • apps/web/src/components/chat/context-resources.tsx
🧬 Code graph analysis (6)
apps/web/src/components/chat/context-resources.tsx (2)
apps/web/src/hooks/use-agent-settings-tools-set.ts (1)
  • useAgentSettingsToolsSet (5-80)
apps/web/src/components/integrations/common.tsx (1)
  • IntegrationIcon (19-35)
apps/web/src/components/chat/audio-button.tsx (1)
packages/ui/src/lib/utils.ts (1)
  • cn (4-6)
apps/web/src/components/decopilot/index.tsx (1)
apps/web/src/components/agent/chat.tsx (1)
  • MainChat (90-113)
apps/web/src/components/chat/rich-text.tsx (9)
apps/web/src/components/prompts/rich-text/index.tsx (2)
  • RichTextArea (58-282)
  • Tool (47-56)
apps/web/src/components/agent/provider.tsx (1)
  • useAgent (670-674)
apps/web/src/hooks/use-agent-settings-tools-set.ts (1)
  • useAgentSettingsToolsSet (5-80)
apps/web/src/components/rich-text-editor/extensions/mention-node.tsx (1)
  • MentionNode (23-69)
apps/web/src/components/common/avatar/integration.tsx (1)
  • IntegrationAvatar (23-61)
apps/web/src/components/rich-text-editor/components/mention-dropdown.tsx (1)
  • MentionDropdown (28-325)
apps/web/src/components/chat/extensions/no-new-line.ts (1)
  • NoNewLine (7-21)
apps/web/src/components/rich-text-editor/extensions/unified-mentions.ts (1)
  • createUnifiedMentions (25-415)
packages/ui/src/lib/utils.ts (1)
  • cn (4-6)
apps/web/src/components/chat/model-selector.tsx (1)
packages/sdk/src/constants.ts (1)
  • DEFAULT_MODEL (209-209)
apps/web/src/components/chat/chat-input.tsx (8)
apps/web/src/components/agent/provider.tsx (1)
  • useAgent (670-674)
apps/web/src/hooks/use-user-preferences.ts (1)
  • useUserPreferences (30-48)
apps/web/src/hooks/use-agent-settings-tools-set.ts (1)
  • useAgentSettingsToolsSet (5-80)
apps/web/src/components/chat/rich-text.tsx (2)
  • RichTextAreaHandle (61-64)
  • RichTextArea (66-314)
packages/runtime/src/mastra.ts (1)
  • Integration (331-334)
packages/sdk/src/models/mcp.ts (1)
  • Integration (90-90)
apps/web/src/components/chat/context-resources.tsx (1)
  • ContextResources (44-276)
apps/web/src/components/integrations/select-connection-dialog.tsx (1)
  • SelectConnectionDialog (860-893)
🪛 GitHub Actions: Tests
apps/web/src/components/chat/chat-input.tsx

[error] 45-45: Illegal use of an export declaration not at the top level. (formatter parse error) - while running 'bun run fmt:check'


[error] 324-324: Expected '}' but end of file reached. (formatter parse error) - while running 'bun run fmt:check'

🔇 Additional comments (8)
apps/web/src/components/chat/audio-button.tsx (3)

9-10: LGTM!

The imports follow the coding guidelines to use design system components from @deco/ui and the standard cn utility for className composition.


12-15: LGTM!

Adding the optional className prop enables external styling customization, aligning with the broader prop-driven styling pattern across chat UI components.


17-20: LGTM!

The component signature properly destructures the props and maintains proper TypeScript typing.

apps/web/src/components/chat/model-selector.tsx (1)

113-118: LGTM!

The styling updates and className prop addition are well-implemented. The use of cn() to merge external styling ensures composability without breaking existing styles, and the transition effects on hover enhance the user experience.

Also applies to: 126-126, 133-133, 155-157

apps/web/src/components/chat/context-resources.tsx (1)

60-60: LGTM!

The removal of openFileDialog and the refactored rules/context resources UI align well with the PR objectives. The consolidated tooltip-based rules display with per-rule and bulk removal actions improves the user experience while maintaining existing functionality.

Also applies to: 178-272

apps/web/src/components/chat/rich-text.tsx (1)

61-64: LGTM!

The forwardRef refactor correctly exposes insertText and focus methods via RichTextAreaHandle, enabling parent components to control the editor imperatively while maintaining encapsulation.

Also applies to: 66-81, 272-288

apps/web/src/components/chat/chat-input.tsx (1)

80-87: The rightNode prop has not been removed and remains actively used.

Based on the codebase inspection, the rightNode prop is still defined in the ChatInput function parameters (line 41 as an optional ReactNode) and is actively referenced on line 214 when passed to a child component. There is no named ChatInputProps interface at line 45—the component uses inline destructuring. The prop is functioning as intended and the original concern is unfounded.

Likely an incorrect or invalid review comment.

apps/web/src/components/decopilot/index.tsx (1)

395-395: Flexible header layout looks good.

The change from fixed height (h-10) to minimum height (min-h-10) makes the header responsive to content, while the updated padding (px-2) and reduced gap (gap-2) align with the PR's goal of consistent spacing.

Comment thread apps/web/src/components/chat/chat-input.tsx Outdated
Comment thread apps/web/src/components/decopilot/index.tsx Outdated
- Updated MainChatSkeleton and MainChat to use consistent height classes for better layout.
- Adjusted ChatInput and ContextResources components to remove unused props, streamlining the code.
- Enhanced styling for input areas to ensure consistent appearance across chat components.
- Added rightNode prop to ChatInput for customizable right-side content.
- Updated ChatInput to ensure uploaded files have a valid URL before processing.
- Improved styling in ChatMessage for better text readability.
- Refactored ContextResources to use a button element for better accessibility.
- Adjusted dependencies in use-agent-settings-tools-set for improved performance.
@rafavalls rafavalls closed this Oct 21, 2025
@rafavalls rafavalls deleted the valls/ui-chat branch October 21, 2025 00:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant