Skip to content

Conversation

@Kitenite
Copy link
Contributor

@Kitenite Kitenite commented Sep 20, 2025

Description

Related Issues

Type of Change

  • Bug fix
  • New feature
  • Documentation update
  • Release
  • Refactor
  • Other (please describe):

Testing

Screenshots (if applicable)

Additional Notes


Important

Introduces a new conversation-style chat layout with sticky-to-bottom behavior and refactors message rendering to use unified components.

  • New Features:
    • Introduces Conversation, ConversationContent, ConversationScrollButton, and ConversationEmptyState components in conversation.tsx for a new chat layout.
    • Adds sticky-to-bottom behavior and improved scroll-to-bottom button using use-stick-to-bottom.
  • Refactor:
    • Replaces ChatMessageList with Conversation components in index.tsx.
    • Removes markdown-renderer.tsx and use-auto-scroll.tsx.
    • Updates message rendering to use Response component in message-content/index.tsx.
  • Chores:
    • Adds use-stick-to-bottom dependency in package.json.
    • Updates globals.css to include streamdown styles.

This description was created by Ellipsis for 3e0e263. You can customize this summary. It will automatically update as commits are pushed.


Summary by CodeRabbit

  • New Features

    • New conversation-style chat layout with sticky-to-bottom behavior and an improved "scroll to bottom" button.
    • Enhanced conversation empty-state presentation.
  • Refactor

    • Message rendering updated to use unified conversation/response components; streaming indicator and error display preserved.
    • Reduced retry toast noise by removing redundant loading/success messages.
  • Chores

    • Added a runtime dependency to support updated scrolling behavior.

@vercel
Copy link

vercel bot commented Sep 20, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
docs Ready Ready Preview Comment Sep 20, 2025 1:46am
web Ready Ready Preview Comment Sep 20, 2025 1:46am

@supabase
Copy link

supabase bot commented Sep 20, 2025

This pull request has been ignored for the connected project wowaemfasoptxrdjhilu because there are no changes detected in apps/backend/supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@coderabbitai
Copy link

coderabbitai bot commented Sep 20, 2025

Warning

Rate limit exceeded

@Kitenite has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 9 minutes and 30 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between d7b2ed6 and 3e0e263.

📒 Files selected for processing (1)
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/index.tsx (4 hunks)

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Replaces legacy chat scroll/renderer primitives with new ai-elements Conversation components, removes ChatMessageList/useAutoScroll/MarkdownRenderer, swaps MarkdownRenderer for Response, updates streaming render path, and adds a stick-to-bottom runtime dependency and new Conversation exports.

Changes

Cohort / File(s) Summary of Changes
Web chat messages integration
apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/index.tsx
Replaced ChatMessageList/StreamMessage rendering with Conversation + ConversationContent and ConversationScrollButton; per-message wrappers now include my-2; streaming handled via separate loading block; updated imports.
Message content rendering
apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/message-content/index.tsx
Replaced MarkdownRenderer usage for text parts with Response from @onlook/ui/ai-elements; tool and reasoning parts remain unchanged.
Removed markdown renderer
apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/markdown-renderer.tsx
Deleted MarkdownRenderer component and its export.
User message toast copy
apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/user-message.tsx
Removed loading and success messages from toast.promise in retry handler; retained error handling.
New AI elements: Conversation
packages/ui/src/components/ai-elements/conversation.tsx, packages/ui/src/components/ai-elements/index.tsx
Added Conversation, ConversationContent, ConversationEmptyState, and ConversationScrollButton components and related exported prop types; re-exported conversation from ai-elements index.
UI package dependency
packages/ui/package.json
Added runtime dependency use-stick-to-bottom ^1.1.1.
Removed chat auto-scroll primitives
packages/ui/src/components/chat/chat-message-list.tsx, packages/ui/src/components/chat/hooks/use-auto-scroll.tsx
Removed ChatMessageList component, ChatMessageListProps and the useAutoScroll hook and their public exports.
Global styles update
packages/ui/src/globals.css
Added @source "../../../node_modules/streamdown/dist/index.js"; directive.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant ChatTab as Chat Messages (component)
  participant Conv as Conversation (StickToBottom)
  participant Content as ConversationContent
  participant Btn as ConversationScrollButton

  User->>ChatTab: Open chat / send message
  ChatTab->>Conv: Render Conversation (role="log")
  Conv->>Content: Provide stick-to-bottom context
  Content-->>ChatTab: Render base messages (Response)
  ChatTab-->>Content: If isStreaming → render loading block
  Note over Conv,Content: Context tracks isAtBottom and scrollToBottom

  par User scrolls up
    User->>Conv: Scroll
    Conv-->>Btn: isAtBottom=false → show button
  and New messages arrive
    ChatTab->>Content: Append message(s)
    alt isAtBottom
      Conv->>Conv: Auto stick to bottom
    else not at bottom
      Conv-->>Btn: Keep button visible
    end
  end

  User->>Btn: Click
  Btn->>Conv: scrollToBottom()
  Conv-->>Btn: isAtBottom=true (hide)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

A nibble of code, a hop so spry,
Conversations stick while answers fly.
Old lists pruned, new pieces bloom,
I twitch my whiskers — scroll to zoom! 🐇✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The title "feat: use conversation list smooth" references the main change—migrating to a conversation-based chat layout with smoother scrolling—so it is related to the changeset; however the phrasing is terse and awkward which may reduce clarity when scanning history.
Description Check ✅ Passed The PR description includes the repository template and a detailed "IMPORTANT" summary that explains the new Conversation components, removal of ChatMessageList and useAutoScroll, and the added dependency, and it marks the change as a new feature; however several template sections remain as placeholders (Related Issues, Testing, Screenshots, Additional Notes). Because the core implementation and intent are described, the description is mostly complete but it lacks verification steps and links to related issues or migration notes.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.

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.


return (
!isAtBottom && (
<Button
Copy link
Contributor

Choose a reason for hiding this comment

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

For accessibility, consider adding an aria-label (e.g., 'Scroll to bottom') to the Button rendered by ConversationScrollButton.

Copy link

@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/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/user-message.tsx (1)

1-1: Must mark as client component

This file uses React state/effects and browser APIs; add 'use client' per app-dir rules.

+'use client';
🧹 Nitpick comments (5)
packages/ui/src/components/ai-elements/conversation.tsx (2)

12-20: Accessibility: provide label for the live region

Add an accessible name to the log container to help screen readers identify the region.

 export const Conversation = ({ className, ...props }: ConversationProps) => (
     <StickToBottom
         className={cn('relative flex-1 overflow-y-auto', className)}
         initial="smooth"
         resize="smooth"
-        role="log"
+        role="log"
+        aria-label="Conversation"
         {...props}
     />
 );

73-90: Icon-only button needs an accessible name

Add aria-label (and optional title) so screen readers can announce the action.

         !isAtBottom && (
             <Button
                 className={cn(
                     'absolute bottom-4 left-[50%] translate-x-[-50%] rounded-full',
                     className,
                 )}
                 onClick={handleScrollToBottom}
+                aria-label="Scroll to bottom"
+                title="Scroll to bottom"
                 size="icon"
                 type="button"
                 variant="outline"
                 {...props}
             >
                 <ArrowDownIcon className="size-4" />
             </Button>
         )
apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/user-message.tsx (2)

83-90: Localize hardcoded toast copy

'Failed to resubmit message' is hardcoded. In apps/web/client/src/app/**, use next-intl.

-        toast.promise(
-            onEditMessage(message.id, getUserMessageContent(message), ChatType.EDIT),
-            {
-                error: 'Failed to resubmit message',
-            }
-        )
+        const t = (await import('next-intl')).useTranslations();
+        toast.promise(
+            onEditMessage(message.id, getUserMessageContent(message), ChatType.EDIT),
+            { error: t(transKeys.chat.retryError) }
+        );

Confirm the actual translation key path (placeholder: transKeys.chat.retryError) exists.


216-218: Avoid random keys in lists

Using nanoid() in render causes unstable keys and remounts. Use a stable identifier from context or a deterministic fallback.

-                            {message.metadata?.context?.map((context) => (
-                                <SentContextPill key={nanoid()} context={context} />
-                            ))}
+                            {message.metadata?.context?.map((context, i) => (
+                                <SentContextPill
+                                    key={context.id ?? `${context.type}:${context.value ?? i}`}
+                                    context={context}
+                                />
+                            ))}
apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/index.tsx (1)

74-75: Prefer stable key; drop index

Indexes in keys can cause subtle bugs on reordering. message.id should be sufficient.

-            return <div key={`message-${message.id}-${index}`} className="my-2">{messageNode}</div>;
+            return <div key={`message-${message.id}`} className="my-2">{messageNode}</div>;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 107ff7d and 8e20328.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (7)
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/index.tsx (3 hunks)
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/user-message.tsx (1 hunks)
  • packages/ui/package.json (1 hunks)
  • packages/ui/src/components/ai-elements/conversation.tsx (1 hunks)
  • packages/ui/src/components/ai-elements/index.tsx (1 hunks)
  • packages/ui/src/components/chat/chat-message-list.tsx (0 hunks)
  • packages/ui/src/components/chat/hooks/use-auto-scroll.tsx (0 hunks)
💤 Files with no reviewable changes (2)
  • packages/ui/src/components/chat/chat-message-list.tsx
  • packages/ui/src/components/chat/hooks/use-auto-scroll.tsx
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Do not use the any type unless necessary

Files:

  • packages/ui/src/components/ai-elements/index.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/user-message.tsx
  • packages/ui/src/components/ai-elements/conversation.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/index.tsx
{apps,packages}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Avoid using the any type unless absolutely necessary

Files:

  • packages/ui/src/components/ai-elements/index.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/user-message.tsx
  • packages/ui/src/components/ai-elements/conversation.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/index.tsx
apps/web/client/src/app/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/client/src/app/**/*.tsx: Default to Server Components; add 'use client' when using events, state/effects, browser APIs, or client‑only libraries
Do not use process.env in client code; import env from @/env instead

Avoid hardcoded user-facing text; use next-intl messages/hooks

Files:

  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/user-message.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/index.tsx
apps/web/client/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/client/src/**/*.{ts,tsx}: Use path aliases @/* and ~/* for imports that map to apps/web/client/src/*
Avoid hardcoded user-facing text; use next-intl messages/hooks instead

Use path aliases @/* and ~/* for imports mapping to src/*

Files:

  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/user-message.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/index.tsx
apps/web/client/src/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/client/src/**/*.tsx: Create MobX store instances with useState(() => new Store()) for stable references across renders
Keep the active MobX store in a useRef and perform async cleanup with setTimeout(() => storeRef.current?.clear(), 0) to avoid route-change races
Avoid useMemo for creating MobX store instances
Avoid putting the MobX store instance in effect dependency arrays if it causes loops; split concerns by domain

apps/web/client/src/**/*.tsx: Create MobX store instances with useState(() => new Store()) for stable identities across renders
Keep the active MobX store in a useRef and clean up asynchronously with setTimeout(() => storeRef.current?.clear(), 0)
Do not use useMemo to create MobX stores
Avoid placing MobX store instances in effect dependency arrays if it causes loops; split concerns instead
observer components must be client components; place a single client boundary at the feature entry; child observers need not repeat 'use client'

Files:

  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/user-message.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/index.tsx
apps/web/client/src/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Default to Server Components; add 'use client' only when using events, state/effects, browser APIs, or client-only libs

Files:

  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/user-message.tsx
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/index.tsx
🧠 Learnings (2)
📚 Learning: 2025-09-16T19:22:52.461Z
Learnt from: CR
PR: onlook-dev/onlook#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-16T19:22:52.461Z
Learning: Applies to apps/web/client/src/app/**/*.tsx : Avoid hardcoded user-facing text; use next-intl messages/hooks

Applied to files:

  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/user-message.tsx
📚 Learning: 2025-09-14T01:44:21.209Z
Learnt from: CR
PR: onlook-dev/onlook#0
File: AGENTS.md:0-0
Timestamp: 2025-09-14T01:44:21.209Z
Learning: Applies to apps/web/client/src/**/*.{ts,tsx} : Avoid hardcoded user-facing text; use next-intl messages/hooks instead

Applied to files:

  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/user-message.tsx
🧬 Code graph analysis (1)
apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/index.tsx (2)
packages/ui/src/components/ai-elements/conversation.tsx (3)
  • Conversation (12-20)
  • ConversationContent (24-26)
  • ConversationScrollButton (63-90)
apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/error-message.tsx (1)
  • ErrorMessage (11-63)
🔇 Additional comments (4)
packages/ui/package.json (1)

116-116: LGTM: add runtime dependency for scroll behavior

Placing use-stick-to-bottom under dependencies (not dev/peer) is correct since it’s used at runtime by exported UI components.

packages/ui/src/components/ai-elements/index.tsx (1)

2-2: LGTM: re-export conversation primitives

Publicly exposing the conversation components is appropriate and aligns with usage downstream.

apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/index.tsx (2)

7-11: LGTM: move to Conversation primitives

Imports reflect the new Conversation-based layout and are consistent with the UI package.


93-103: Render computed streaming messages (use messages, not baseMessages)

Replace baseMessages with the computed messages array so the in‑progress assistant message replaces the "Thinking …" placeholder; localize the placeholder.

-            <ConversationContent className="p-0 m-0">
-                {baseMessages.map((message, index) => renderMessage(message, index))}
+            <ConversationContent className="p-0 m-0">
+                {messages.map((message, index) => renderMessage(message, index))}
                 {error && <ErrorMessage error={error} />}
                 {isStreaming && <div className="flex w-full h-full flex-row items-center gap-2 px-4 my-2 text-small content-start text-foreground-secondary">
-                    <Icons.LoadingSpinner className="animate-spin" />
-                    <p>Thinking ...</p>
+                    <Icons.LoadingSpinner className="animate-spin" />
+                    <p>{t(transKeys.editor.panels.edit.tabs.chat.thinking)}</p>
                 </div>}
             </ConversationContent>

Ensure transKeys.editor.panels.edit.tabs.chat.thinking exists; add it if missing.

Comment on lines +28 to +33
export type ConversationEmptyStateProps = ComponentProps<'div'> & {
title?: string;
description?: string;
icon?: React.ReactNode;
};

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix TS: avoid React.ReactNode without importing React

React.ReactNode requires importing the React namespace. Prefer importing ReactNode type to avoid namespace usage.

Apply:

-import type { ComponentProps } from 'react';
+import type { ComponentProps, ReactNode } from 'react';
@@
 export type ConversationEmptyStateProps = ComponentProps<'div'> & {
-    title?: string;
-    description?: string;
-    icon?: React.ReactNode;
+    title?: string;
+    description?: string;
+    icon?: ReactNode;
 };
📝 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
export type ConversationEmptyStateProps = ComponentProps<'div'> & {
title?: string;
description?: string;
icon?: React.ReactNode;
};
import type { ComponentProps, ReactNode } from 'react';
export type ConversationEmptyStateProps = ComponentProps<'div'> & {
title?: string;
description?: string;
icon?: ReactNode;
};
🤖 Prompt for AI Agents
In packages/ui/src/components/ai-elements/conversation.tsx around lines 28 to
33, the prop type uses React.ReactNode which requires the React namespace;
change it to import the ReactNode type and use that instead. Update the file
imports to include `ReactNode` from 'react' (or add it to the existing import)
and replace `icon?: React.ReactNode;` with `icon?: ReactNode;` so the file no
longer relies on the React namespace.

@vercel vercel bot temporarily deployed to Preview – docs September 20, 2025 01:36 Inactive
applied={applied}
isStream={isStream}
/>
<Response key={part.text}>
Copy link
Contributor

Choose a reason for hiding this comment

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

Replacing MarkdownRenderer with Response: ensure that any required markdown formatting is still handled. Also, using part.text as a key might lead to duplicates if texts repeat—consider a more unique identifier.

Suggested change
<Response key={part.text}>
<Response key={`${part.text}-${idx}`}>

@vercel vercel bot temporarily deployed to Preview – docs September 20, 2025 01:39 Inactive
@Kitenite Kitenite merged commit dfc9618 into main Sep 20, 2025
4 of 5 checks passed
@Kitenite Kitenite deleted the feat/convo-el branch September 20, 2025 01:39
Copy link

@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

♻️ Duplicate comments (1)
apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/message-content/index.tsx (1)

24-26: Use stable, unique keys for streaming and duplicate texts

key={part.text} can collide for repeated texts and can cause remounts while streaming as the text changes. Prefer a stable composite key.

-                    <Response key={part.text}>
+                    <Response key={`${messageId}-text-${idx}`}>
                         {part.text}
                     </Response>
🧹 Nitpick comments (1)
apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/message-content/index.tsx (1)

43-49: Same key issue for Reasoning

Avoid key={part.text} here as well; use a stable composite key.

-                return (
-                    <Reasoning key={part.text} className={cn(
+                return (
+                    <Reasoning key={`${messageId}-reasoning-${idx}`} className={cn(
                         "px-2 m-0 items-center gap-2 text-foreground-tertiary",
                         isStream && isLastPart && "bg-gradient-to-l from-white/20 via-white/90 to-white/20 bg-[length:200%_100%] bg-clip-text text-transparent animate-shimmer filter drop-shadow-[0_0_10px_rgba(255,255,255,0.4)]"
                     )} isStreaming={isStream}>
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8e20328 and d7b2ed6.

📒 Files selected for processing (3)
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/markdown-renderer.tsx (0 hunks)
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/message-content/index.tsx (2 hunks)
  • packages/ui/src/globals.css (1 hunks)
💤 Files with no reviewable changes (1)
  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/markdown-renderer.tsx
✅ Files skipped from review due to trivial changes (1)
  • packages/ui/src/globals.css
🧰 Additional context used
📓 Path-based instructions (6)
apps/web/client/src/app/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/client/src/app/**/*.tsx: Default to Server Components; add 'use client' when using events, state/effects, browser APIs, or client‑only libraries
Do not use process.env in client code; import env from @/env instead

Avoid hardcoded user-facing text; use next-intl messages/hooks

Files:

  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/message-content/index.tsx
apps/web/client/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/client/src/**/*.{ts,tsx}: Use path aliases @/* and ~/* for imports that map to apps/web/client/src/*
Avoid hardcoded user-facing text; use next-intl messages/hooks instead

Use path aliases @/* and ~/* for imports mapping to src/*

Files:

  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/message-content/index.tsx
apps/web/client/src/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/client/src/**/*.tsx: Create MobX store instances with useState(() => new Store()) for stable references across renders
Keep the active MobX store in a useRef and perform async cleanup with setTimeout(() => storeRef.current?.clear(), 0) to avoid route-change races
Avoid useMemo for creating MobX store instances
Avoid putting the MobX store instance in effect dependency arrays if it causes loops; split concerns by domain

apps/web/client/src/**/*.tsx: Create MobX store instances with useState(() => new Store()) for stable identities across renders
Keep the active MobX store in a useRef and clean up asynchronously with setTimeout(() => storeRef.current?.clear(), 0)
Do not use useMemo to create MobX stores
Avoid placing MobX store instances in effect dependency arrays if it causes loops; split concerns instead
observer components must be client components; place a single client boundary at the feature entry; child observers need not repeat 'use client'

Files:

  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/message-content/index.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Do not use the any type unless necessary

Files:

  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/message-content/index.tsx
apps/web/client/src/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Default to Server Components; add 'use client' only when using events, state/effects, browser APIs, or client-only libs

Files:

  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/message-content/index.tsx
{apps,packages}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Avoid using the any type unless absolutely necessary

Files:

  • apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/message-content/index.tsx
🧬 Code graph analysis (1)
apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/message-content/index.tsx (1)
packages/ui/src/components/ai-elements/response.tsx (1)
  • Response (9-17)
🔇 Additional comments (1)
apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/message-content/index.tsx (1)

2-2: Confirm 'use client' boundary for MobX + client-only ai-elements

apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/message-content/index.tsx uses mobx-react-lite's observer and @onlook/ui/ai-elements but contains no "use client" and no ancestor under apps/web/client/src/app/project includes "use client". Ensure this component is rendered under a client boundary — add "use client" at the feature entry (recommended) or convert this file to a client component; if it's unused, confirm and remove.

Comment on lines +24 to +26
<Response key={part.text}>
{part.text}
</Response>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 Analysis chain

Verify Streamdown/Response sanitization (XSS guard)

Rendering user-supplied markdown/HTML must be sanitized. Please confirm Response/Streamdown escapes or sanitizes content.


🏁 Script executed:

#!/bin/bash
# Inspect UI package for unsafe HTML usage
rg -n 'dangerouslySetInnerHTML|sanitize|dompurify|xss' packages/ui -C2
rg -nP 'class\s+Streamdown|function\s+Streamdown' packages/ui -C3

Length of output: 840


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "---- showing target file ----"
sed -n '1,240p' apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/message-content/index.tsx || true

echo
echo "---- find Response component definitions/usages ----"
rg -n --hidden --glob '!node_modules' -S "(\bfunction\s+Response\b|\bconst\s+Response\b|\bexport\s+const\s+Response\b|\bexport\s+function\s+Response\b)" || true
rg -n --hidden --glob '!node_modules' -S "\b<Response\b" || true

echo
echo "---- find Streamdown definitions/usages ----"
rg -n --hidden --glob '!node_modules' -S "Streamdown" || true

echo
echo "---- find dangerouslySetInnerHTML across repo ----"
rg -n --hidden --glob '!node_modules' -S "dangerouslySetInnerHTML" || true

echo
echo "---- find common sanitizer libraries/usages ----"
rg -n --hidden --glob '!node_modules' -S "dompurify|DOMPurify|sanitize-html|sanitizeHtml|sanitize\\(|xss|isTrusted" || true

Length of output: 3184


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "---- packages/ui src index (if exists) ----"
if [ -f packages/ui/src/index.ts ]; then
  sed -n '1,240p' packages/ui/src/index.ts
else
  echo "no packages/ui/src/index.ts"
fi
echo

echo "---- search for ai-elements export/import ----"
rg -n "@onlook/ui/ai-elements|\\bai-elements\\b" --hidden -S || true
echo

echo "---- search for Response component definitions/usages in packages/ui ----"
rg -n --hidden -S "(^export\\s+.*Response|\\bfunction\\s+Response\\b|\\bconst\\s+Response\\b|\\bclass\\s+Response\\b|<Response\\b)" packages/ui || true
echo

echo "---- search for Streamdown in repo ----"
rg -n --hidden -S "Streamdown" || true
echo

echo "---- search for dangerouslySetInnerHTML in repo ----"
rg -n --hidden -S "dangerouslySetInnerHTML" || true
echo

echo "---- search for DOMPurify or sanitize libs in repo ----"
rg -n --hidden -S "DOMPurify|dompurify|sanitize-html|sanitizeHtml|xss" || true
echo

echo "---- show any files that export from ai-elements in packages/ui ----"
rg -n --hidden -S "export .*from .*ai-elements" packages || true

Length of output: 3064


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "---- cat response.tsx ----"
sed -n '1,320p' packages/ui/src/components/ai-elements/response.tsx || true

echo
echo "---- show surrounding ai-elements files ----"
ls -la packages/ui/src/components/ai-elements || true

echo
echo "---- search for Streamdown usage across repo ----"
rg -n --hidden -S "Streamdown" || true

echo
echo "---- search for any HTML rendering in response.tsx ----"
rg -n --hidden -n --context 3 "dangerouslySetInnerHTML|innerHTML|html" packages/ui/src/components/ai-elements/response.tsx || true

Length of output: 1434


Ensure HTML sanitization for Response/Streamdown output
Response is a thin wrapper over Streamdown and does not perform any escaping or sanitization itself—confirm that Streamdown is configured to strip or escape user-supplied HTML, or explicitly sanitize the rendered output (e.g. with DOMPurify) before passing it to Streamdown.

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.

2 participants