Conversation
WalkthroughRefactors multiple chat UI components: simplifies AudioButton, converts RichTextArea to forwardRef exposing insertText/focus, restructures ChatInput to include a context dropdown and RichText ref, removes Changes
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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (6)
🧰 Additional context used📓 Path-based instructions (9)**/*.{ts,tsx}📄 CodeRabbit inference engine (.cursor/rules/data-flow.mdc)
Files:
**/*.tsx📄 CodeRabbit inference engine (.cursor/rules/data-flow.mdc)
Files:
apps/web/**/*.{js,jsx,ts,tsx}📄 CodeRabbit inference engine (apps/web/.cursor/rules/posthog-integration.mdc)
Files:
apps/web/**/*.{ts,tsx}📄 CodeRabbit inference engine (apps/web/.cursor/rules/posthog-integration.mdc)
Files:
apps/web/**📄 CodeRabbit inference engine (AGENTS.md)
Files:
**/*.{ts,tsx,js,jsx}📄 CodeRabbit inference engine (AGENTS.md)
Files:
**/*.{tsx,jsx}📄 CodeRabbit inference engine (AGENTS.md)
Files:
{apps/web,packages}/**/*.{tsx,jsx}📄 CodeRabbit inference engine (AGENTS.md)
Files:
**/*.ts📄 CodeRabbit inference engine (.cursor/rules/data-flow.mdc)
Files:
🧠 Learnings (2)📚 Learning: 2025-10-03T17:14:55.800ZApplied to files:
📚 Learning: 2025-10-03T16:59:09.898ZApplied to files:
🧬 Code graph analysis (4)apps/web/src/components/decopilot/index.tsx (1)
apps/web/src/components/chat/rich-text.tsx (7)
apps/web/src/components/chat/chat-input.tsx (6)
apps/web/src/components/chat/context-resources.tsx (2)
🔇 Additional comments (18)
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. Comment |
There was a problem hiding this comment.
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 DOMDirect 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 booleanEnsure 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 EventThis 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 toolsCasting 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 tooltipImproves 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 itemsUsing 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 previewsImproves 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
📒 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 explicitlyPrefer 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.tsxapps/web/src/components/chat/audio-button.tsxpackages/ui/src/components/tooltip.tsxapps/web/src/components/decopilot/index.tsxapps/web/src/components/chat/model-selector.tsxapps/web/src/components/chat/context-resources.tsxapps/web/src/components/chat/rich-text.tsxapps/web/src/components/layout/decopilot-layout.tsxapps/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.tsxapps/web/src/components/chat/audio-button.tsxpackages/ui/src/components/tooltip.tsxapps/web/src/components/decopilot/index.tsxapps/web/src/components/chat/model-selector.tsxapps/web/src/components/chat/context-resources.tsxapps/web/src/components/chat/rich-text.tsxapps/web/src/components/layout/decopilot-layout.tsxapps/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.tsxapps/web/src/components/chat/audio-button.tsxapps/web/src/components/decopilot/index.tsxapps/web/src/components/chat/model-selector.tsxapps/web/src/components/chat/context-resources.tsxapps/web/src/components/chat/rich-text.tsxapps/web/src/components/layout/decopilot-layout.tsxapps/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.tsxapps/web/src/components/chat/audio-button.tsxapps/web/src/components/decopilot/index.tsxapps/web/src/components/chat/model-selector.tsxapps/web/src/components/chat/context-resources.tsxapps/web/src/components/chat/rich-text.tsxapps/web/src/components/layout/decopilot-layout.tsxapps/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.tsxapps/web/src/components/chat/audio-button.tsxapps/web/src/components/decopilot/index.tsxapps/web/src/components/chat/model-selector.tsxapps/web/src/components/chat/context-resources.tsxapps/web/src/components/chat/rich-text.tsxapps/web/src/components/layout/decopilot-layout.tsxapps/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.tsxapps/web/src/components/chat/audio-button.tsxpackages/ui/src/components/tooltip.tsxapps/web/src/components/decopilot/index.tsxapps/web/src/components/chat/model-selector.tsxapps/web/src/components/chat/context-resources.tsxapps/web/src/components/chat/rich-text.tsxapps/web/src/components/layout/decopilot-layout.tsxapps/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.tsxapps/web/src/components/chat/audio-button.tsxpackages/ui/src/components/tooltip.tsxapps/web/src/components/decopilot/index.tsxapps/web/src/components/chat/model-selector.tsxapps/web/src/components/chat/context-resources.tsxapps/web/src/components/chat/rich-text.tsxapps/web/src/components/layout/decopilot-layout.tsxapps/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.tsxapps/web/src/components/chat/audio-button.tsxpackages/ui/src/components/tooltip.tsxapps/web/src/components/decopilot/index.tsxapps/web/src/components/chat/model-selector.tsxapps/web/src/components/chat/context-resources.tsxapps/web/src/components/chat/rich-text.tsxapps/web/src/components/layout/decopilot-layout.tsxapps/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-xstotext-smalso 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 goodNice integration of SelectConnectionDialog as a dropdown item; onSelect closes the menu and enables tools.
| <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> |
There was a problem hiding this comment.
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.
| <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.
ae716b1 to
2bc172d
Compare
Deploying chat with
|
| 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 |
There was a problem hiding this comment.
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 accessibilityThe 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 changesThe 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. Addaria-labelandaria-pressedderived fromisListeningalongside the existingtitle.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 remoteuf.url, and the extramediaTypefield should go away to match the UIMessage shape. Filter foruf.urland 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 buttonTitle 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
📒 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 explicitlyPrefer 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.tsxapps/web/src/components/chat/rich-text.tsxapps/web/src/components/chat/chat-markdown.tsxapps/web/src/components/chat/model-selector.tsxapps/web/src/components/decopilot/index.tsxapps/web/src/components/chat/audio-button.tsxapps/web/src/components/chat/chat-input.tsxapps/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.tsxapps/web/src/components/chat/rich-text.tsxapps/web/src/components/chat/chat-markdown.tsxapps/web/src/components/chat/model-selector.tsxapps/web/src/components/decopilot/index.tsxapps/web/src/components/chat/audio-button.tsxapps/web/src/components/chat/chat-input.tsxapps/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.tsxapps/web/src/components/chat/rich-text.tsxapps/web/src/components/chat/chat-markdown.tsxapps/web/src/components/chat/model-selector.tsxapps/web/src/components/decopilot/index.tsxapps/web/src/components/chat/audio-button.tsxapps/web/src/components/chat/chat-input.tsxapps/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.tsxapps/web/src/components/chat/rich-text.tsxapps/web/src/components/chat/chat-markdown.tsxapps/web/src/components/chat/model-selector.tsxapps/web/src/components/decopilot/index.tsxapps/web/src/components/chat/audio-button.tsxapps/web/src/components/chat/chat-input.tsxapps/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.tsxapps/web/src/components/chat/rich-text.tsxapps/web/src/components/chat/chat-markdown.tsxapps/web/src/components/chat/model-selector.tsxapps/web/src/components/decopilot/index.tsxapps/web/src/components/chat/audio-button.tsxapps/web/src/components/chat/chat-input.tsxapps/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.tsxapps/web/src/components/chat/rich-text.tsxapps/web/src/components/chat/chat-markdown.tsxapps/web/src/components/chat/model-selector.tsxapps/web/src/components/decopilot/index.tsxapps/web/src/components/chat/audio-button.tsxapps/web/src/components/chat/chat-input.tsxapps/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.tsxapps/web/src/components/chat/rich-text.tsxapps/web/src/components/chat/chat-markdown.tsxapps/web/src/components/chat/model-selector.tsxapps/web/src/components/decopilot/index.tsxapps/web/src/components/chat/audio-button.tsxapps/web/src/components/chat/chat-input.tsxapps/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.tsxapps/web/src/components/chat/rich-text.tsxapps/web/src/components/chat/chat-markdown.tsxapps/web/src/components/chat/model-selector.tsxapps/web/src/components/decopilot/index.tsxapps/web/src/components/chat/audio-button.tsxapps/web/src/components/chat/chat-input.tsxapps/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 changeborder-0ensures borderless appearance across all viewports, whilemd:border-noneprovides 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 goodMargin 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 LGTMmin-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 LGTMSetting 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 LGTMConsistent 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
There was a problem hiding this comment.
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 initialappendIntegrationTool. When agent context (and the underlying hook) changes, we'll still call the stale function and mutate the wrong toolset. Please includeappendIntegrationToolin 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.createObjectURLcreates 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-0class on line 155 removes all borders unconditionally, making themd:border-nonecondition on line 156 redundant. Consider removing the borderless variant check or moving border-0 into it.Additionally, the
!importantmodifier onh-8is 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.
SelectedModelDisplayis wrapped inmemo(line 107), but theselectedModelpassed to it at line 137 is computed withmodels.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
memowrapper onSelectedModelDisplaycould be removed as it provides no benefit.Also applies to: 137-137
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 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 explicitlyPrefer 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.tsxapps/web/src/components/chat/chat-input.tsxapps/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.tsxapps/web/src/components/chat/chat-input.tsxapps/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.tsxapps/web/src/components/chat/chat-input.tsxapps/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.tsxapps/web/src/components/chat/chat-input.tsxapps/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.tsxapps/web/src/components/chat/chat-input.tsxapps/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.tsxapps/web/src/components/chat/chat-input.tsxapps/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.tsxapps/web/src/components/chat/chat-input.tsxapps/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.tsxapps/web/src/components/chat/chat-input.tsxapps/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
RichTextAreaHandleinterface provides a well-defined API for parent components to control the editor programmatically.
66-84: LGTM! Proper forwardRef implementation.The component correctly uses
forwardRefand 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
useImperativeHandlecorrectly 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
hasContextResourcescomputation 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
RichTextArearef 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.
… and user preferences" This reverts commit 2bc172d.
…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.
a19f0a5 to
c6287f7
Compare
There was a problem hiding this comment.
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 ChatInputdeclarations (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-labelandaria-pressedattributes, 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
divon line 197 with a semanticbuttonelement.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 initialappendIntegrationTool. When the agent context changes, the stale function reference will mutate the wrong toolset. IncludeappendIntegrationToolin 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
📒 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 explicitlyPrefer 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.tsxapps/web/src/components/chat/audio-button.tsxapps/web/src/components/decopilot/index.tsxapps/web/src/components/chat/rich-text.tsxapps/web/src/components/chat/model-selector.tsxapps/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.tsxapps/web/src/components/chat/audio-button.tsxapps/web/src/components/decopilot/index.tsxapps/web/src/components/chat/rich-text.tsxapps/web/src/components/chat/model-selector.tsxapps/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.tsxapps/web/src/components/chat/audio-button.tsxapps/web/src/components/decopilot/index.tsxapps/web/src/components/chat/rich-text.tsxapps/web/src/components/chat/model-selector.tsxapps/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.tsxapps/web/src/components/chat/audio-button.tsxapps/web/src/components/decopilot/index.tsxapps/web/src/components/chat/rich-text.tsxapps/web/src/components/chat/model-selector.tsxapps/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.tsxapps/web/src/components/chat/audio-button.tsxapps/web/src/components/decopilot/index.tsxapps/web/src/components/chat/rich-text.tsxapps/web/src/components/chat/model-selector.tsxapps/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.tsxapps/web/src/components/chat/audio-button.tsxapps/web/src/components/decopilot/index.tsxapps/web/src/components/chat/rich-text.tsxapps/web/src/components/chat/model-selector.tsxapps/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.tsxapps/web/src/components/chat/audio-button.tsxapps/web/src/components/decopilot/index.tsxapps/web/src/components/chat/rich-text.tsxapps/web/src/components/chat/model-selector.tsxapps/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.tsxapps/web/src/components/chat/audio-button.tsxapps/web/src/components/decopilot/index.tsxapps/web/src/components/chat/rich-text.tsxapps/web/src/components/chat/model-selector.tsxapps/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
openFileDialogand 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
insertTextandfocusmethods viaRichTextAreaHandle, 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: TherightNodeprop has not been removed and remains actively used.Based on the codebase inspection, the
rightNodeprop is still defined in the ChatInput function parameters (line 41 as an optionalReactNode) and is actively referenced on line 214 when passed to a child component. There is no namedChatInputPropsinterface 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.
- 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.
Changes
UI Updates
Summary by CodeRabbit
New Features
Accessibility
Style
Public API Changes