Conversation
📝 WalkthroughWalkthroughReplaces the previous top-level layout with an AiLeftPanelLayout wrapper that embeds a resizable left AI panel (desktop) and a Drawer-based panel (mobile). Moves Footer inside the new layout and adjusts page chrome and scroll behavior; adds multiple AI panel components, a useIsMobile hook, and a Drawer UI primitive. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant AppLayout
participant AiLeftPanelLayout
participant useIsMobile
participant AiLeftPanel
participant Drawer
participant DesktopPanel
User->>AppLayout: Request page
AppLayout->>AiLeftPanelLayout: Render wrapper
AiLeftPanelLayout->>useIsMobile: query viewport
useIsMobile-->>AiLeftPanelLayout: isMobile
alt Mobile
AiLeftPanelLayout->>Drawer: render AiLeftPanel inside Drawer
User->>Drawer: open panel
Drawer->>AiLeftPanel: show panel (slide in)
else Desktop
AiLeftPanelLayout->>DesktopPanel: render persistent left panel
User->>DesktopPanel: drag resize handle
DesktopPanel->>DesktopPanel: update panelWidth (clamped)
end
User->>AiLeftPanel: interact (select model, type, send)
AiLeftPanel->>AiLeftPanel: update state (messages, input, suggestions)
AiLeftPanel->>AiLeftPanelLayout: render updated content
sequenceDiagram
participant User
participant AiLeftPanel
participant AiPanelHeader
participant AiPanelMessages
participant AiPanelChatBar
User->>AiLeftPanel: mount
AiLeftPanel->>AiPanelHeader: pass selectedModel, handlers
AiLeftPanel->>AiPanelMessages: pass messages, suggestions
AiLeftPanel->>AiPanelChatBar: pass input, onChange, onSend
User->>AiPanelHeader: open model menu / select
AiPanelHeader->>AiLeftPanel: onModelSelect
AiLeftPanel->>AiLeftPanel: set selectedModel
User->>AiPanelChatBar: type message / press Enter
AiPanelChatBar->>AiLeftPanel: onSend
AiLeftPanel->>AiLeftPanel: append message, clear input
AiLeftPanel->>AiPanelMessages: re-render messages
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
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: 4
🧹 Nitpick comments (6)
packages/epics/src/common/ai-panel/mock-data.ts (1)
23-29: Move sharedMessagetype out ofmock-data.ts.
Messageis now part of component contracts across files, so keeping it in a mock fixture module creates unnecessary coupling. Please extract it into a shared type module (e.g.,ai-panel.types.ts) and import it from both UI components and mock data.Based on learnings: DSanich prefers extracting repeated type declarations into shared modules for reusability and maintainability, particularly for token types that are used across multiple files in the codebase.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/epics/src/common/ai-panel/mock-data.ts` around lines 23 - 29, Extract the Message type from the mock module into a new shared types module (e.g., ai-panel.types.ts): declare and export "Message" there, remove the local declaration from the mock fixture (mock-data.ts), and replace it with an import of Message from the new module; then update all UI components and any other files that previously duplicated the type to import Message from ai-panel.types.ts so there is a single source of truth.apps/web/src/app/layout.tsx (1)
137-142: Remove unnecessary fragment wrapper.The
<>...</>fragment wrapping thedivandFooteris redundant sinceAiLeftPanelLayoutacceptschildrenasReact.ReactNode, which can be multiple elements.♻️ Suggested simplification
<AiLeftPanelLayout> - <> - <div className="w-full shrink-0 pb-8">{children}</div> - <Footer /> - </> + <div className="w-full shrink-0 pb-8">{children}</div> + <Footer /> </AiLeftPanelLayout>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/src/app/layout.tsx` around lines 137 - 142, Remove the redundant fragment wrapping the child elements passed to AiLeftPanelLayout: instead of passing <> <div className="w-full shrink-0 pb-8">{children}</div> <Footer /> </>, pass the two elements directly as children to AiLeftPanelLayout (the div and Footer). Update the JSX where AiLeftPanelLayout is used so it contains the div with {children} and the Footer as sibling children without the <>...</> fragment.packages/epics/src/common/ai-left-panel.tsx (1)
23-23: Consider defaulting safely instead of using non-null assertion.Using
!assumesMOCK_MODEL_OPTIONSis never empty. A safer pattern would handle the potential undefined case, though for mock data this is low risk.♻️ Optional safer approach
- const [selectedModel, setSelectedModel] = useState(MOCK_MODEL_OPTIONS[0]!); + const [selectedModel, setSelectedModel] = useState(MOCK_MODEL_OPTIONS[0] ?? MOCK_MODEL_OPTIONS[0]);Or if TypeScript config allows, trust the mock data array will always have at least one element and leave as-is.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/epics/src/common/ai-left-panel.tsx` at line 23, The initialization for selectedModel uses a non-null assertion on MOCK_MODEL_OPTIONS[0]; change it to safely handle an empty array by providing a fallback or allowing undefined: initialize selectedModel with MOCK_MODEL_OPTIONS[0] ?? a sensible default model (or initialize as undefined and update the state type accordingly), and update any code using selectedModel to handle the possible undefined case; refer to the useState call that declares selectedModel and setSelectedModel and the MOCK_MODEL_OPTIONS constant when making the change.packages/epics/src/common/ai-left-panel-layout.tsx (1)
57-96: Remove unnecessary fragment wrapper.The
<>...</>fragment wrapping the desktop panel content is unnecessary since it's the only child in that conditional branch.♻️ Suggested simplification
{!isMobile && ( - <> - <div - className={cn( - 'relative flex h-full flex-shrink-0 flex-col transition-all duration-300', - panelOpen ? '' : 'w-0 overflow-hidden', - )} - style={panelOpen ? { width: panelWidth } : undefined} - > - <AiLeftPanel onClose={() => setPanelOpen(false)} /> - - {panelOpen && ( - <div - role="separator" - ... - > - ... - </div> - )} - </div> - </> + <div + className={cn( + 'relative flex h-full flex-shrink-0 flex-col transition-all duration-300', + panelOpen ? '' : 'w-0 overflow-hidden', + )} + style={panelOpen ? { width: panelWidth } : undefined} + > + <AiLeftPanel onClose={() => setPanelOpen(false)} /> + + {panelOpen && ( + <div + role="separator" + ... + > + ... + </div> + )} + </div> )}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/epics/src/common/ai-left-panel-layout.tsx` around lines 57 - 96, Remove the unnecessary React fragment wrapping the desktop panel branch: replace the fragment that encloses the top-level div (the element with className built via cn and style using panelWidth) by directly returning that div when !isMobile is true; ensure AiLeftPanel, the conditional panelOpen block (with the separator div using onResizeMouseDown and aria attributes referencing panelWidth), and the isDragging-based classes remain unchanged.packages/epics/src/common/ai-panel/ai-panel-header.tsx (2)
27-39: Consider adding listener only when menu is open.The mousedown listener is added unconditionally and checks
showModelMenuinside the handler. For better performance, the listener could be added/removed based on menu state.♻️ Optional optimization
useEffect(() => { + if (!showModelMenu) return; + const handleClickOutside = (e: MouseEvent) => { - if ( - showModelMenu && - menuRef.current && - !menuRef.current.contains(e.target as Node) - ) { + if (menuRef.current && !menuRef.current.contains(e.target as Node)) { setShowModelMenu(false); } }; document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); }, [showModelMenu]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/epics/src/common/ai-panel/ai-panel-header.tsx` around lines 27 - 39, Currently the mousedown listener is always attached inside the useEffect and only checks showModelMenu inside the handler; update the effect (the useEffect containing handleClickOutside) to add the document.addEventListener('mousedown', handleClickOutside) only when showModelMenu is true and remove it in the cleanup (and also when showModelMenu flips to false) so the listener is not attached unnecessarily; keep using menuRef and setShowModelMenu inside the handler but ensure the effect's dependency array includes showModelMenu so the listener lifecycle is tied to the menu state.
89-96: Refresh button has no handler.The Refresh button renders but has no
onClickhandler. If this is intentional placeholder behavior, consider adding a TODO comment or disabling the button visually.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/epics/src/common/ai-panel/ai-panel-header.tsx` around lines 89 - 96, The Refresh button in ai-panel-header.tsx renders without an onClick handler (the <button> with title/aria-label "Refresh" and the RefreshCw icon), so either wire it to a real handler or mark it as intentionally inactive: add an onClick that calls a refresh function (e.g., handleRefresh defined in the component or passed as an onRefresh prop) to perform the refresh logic, or if it's a placeholder, add a clear TODO comment and set the button to disabled (and update aria-disabled/visual styles) so users and screen readers know it’s inactive; ensure you update any prop types/interfaces if you add an onRefresh prop and reference the button element and the handler name (handleRefresh or onRefresh) when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/epics/src/common/ai-left-panel-layout.tsx`:
- Around line 74-79: The conditional class string in ai-left-panel-layout.tsx
contains a duplicate Tailwind class ('hover:bg-primary/20') inside the
isDragging false branch; update the ternary in the className (the expression
with isDragging ? 'bg-primary' : 'hover:bg-primary/20 group
hover:bg-primary/20') to remove the duplicate so it only includes each utility
once (e.g., use 'group hover:bg-primary/20' as the false branch).
In `@packages/epics/src/common/ai-panel/ai-panel-chat-bar.tsx`:
- Around line 41-45: The Enter key handler (handleKeyDown) currently calls
onSend even when the input is empty and the Send/Stop button disabling logic
prevents clicking Stop while streaming; update handleKeyDown to only call onSend
when e.key === 'Enter', !e.shiftKey, and the current input (e.g., inputValue or
message) has non-empty trimmed content; change the Send button disabled
expression so it is disabled only when not streaming and the input is empty
(e.g., disabled = !isStreaming && input.trim() === ''), and ensure the Stop
action/button (which calls onStop) is enabled when isStreaming (do not include
isStreaming in the disabled clause for the Stop button).
In `@packages/epics/src/common/ai-panel/ai-panel-message-bubble.tsx`:
- Around line 53-80: The icon-only action buttons in ai-panel-message-bubble.tsx
(the <button> elements that render <Copy />, <ThumbsUp />, <ThumbsDown />, and
<RefreshCw />) currently rely only on title for accessibility; add explicit
aria-label attributes to each button (e.g., aria-label="Copy",
aria-label="Thumbs up", aria-label="Thumbs down", aria-label="Refresh" or more
descriptive variants like "Copy message") to match the visible title and provide
reliable screen-reader support.
In `@packages/ui/package.json`:
- Around line 19-21: Update the vaul dependency entry in package.json: replace
the current "vaul": "^0.9.9" with "vaul": "^1.1.2" so the project can pick up
the 1.x releases; after updating the dependency string run your package manager
install (npm/yarn/pnpm) to refresh lockfiles and verify no breaking changes
affect imports or APIs that reference vaul.
---
Nitpick comments:
In `@apps/web/src/app/layout.tsx`:
- Around line 137-142: Remove the redundant fragment wrapping the child elements
passed to AiLeftPanelLayout: instead of passing <> <div className="w-full
shrink-0 pb-8">{children}</div> <Footer /> </>, pass the two elements directly
as children to AiLeftPanelLayout (the div and Footer). Update the JSX where
AiLeftPanelLayout is used so it contains the div with {children} and the Footer
as sibling children without the <>...</> fragment.
In `@packages/epics/src/common/ai-left-panel-layout.tsx`:
- Around line 57-96: Remove the unnecessary React fragment wrapping the desktop
panel branch: replace the fragment that encloses the top-level div (the element
with className built via cn and style using panelWidth) by directly returning
that div when !isMobile is true; ensure AiLeftPanel, the conditional panelOpen
block (with the separator div using onResizeMouseDown and aria attributes
referencing panelWidth), and the isDragging-based classes remain unchanged.
In `@packages/epics/src/common/ai-left-panel.tsx`:
- Line 23: The initialization for selectedModel uses a non-null assertion on
MOCK_MODEL_OPTIONS[0]; change it to safely handle an empty array by providing a
fallback or allowing undefined: initialize selectedModel with
MOCK_MODEL_OPTIONS[0] ?? a sensible default model (or initialize as undefined
and update the state type accordingly), and update any code using selectedModel
to handle the possible undefined case; refer to the useState call that declares
selectedModel and setSelectedModel and the MOCK_MODEL_OPTIONS constant when
making the change.
In `@packages/epics/src/common/ai-panel/ai-panel-header.tsx`:
- Around line 27-39: Currently the mousedown listener is always attached inside
the useEffect and only checks showModelMenu inside the handler; update the
effect (the useEffect containing handleClickOutside) to add the
document.addEventListener('mousedown', handleClickOutside) only when
showModelMenu is true and remove it in the cleanup (and also when showModelMenu
flips to false) so the listener is not attached unnecessarily; keep using
menuRef and setShowModelMenu inside the handler but ensure the effect's
dependency array includes showModelMenu so the listener lifecycle is tied to the
menu state.
- Around line 89-96: The Refresh button in ai-panel-header.tsx renders without
an onClick handler (the <button> with title/aria-label "Refresh" and the
RefreshCw icon), so either wire it to a real handler or mark it as intentionally
inactive: add an onClick that calls a refresh function (e.g., handleRefresh
defined in the component or passed as an onRefresh prop) to perform the refresh
logic, or if it's a placeholder, add a clear TODO comment and set the button to
disabled (and update aria-disabled/visual styles) so users and screen readers
know it’s inactive; ensure you update any prop types/interfaces if you add an
onRefresh prop and reference the button element and the handler name
(handleRefresh or onRefresh) when making the change.
In `@packages/epics/src/common/ai-panel/mock-data.ts`:
- Around line 23-29: Extract the Message type from the mock module into a new
shared types module (e.g., ai-panel.types.ts): declare and export "Message"
there, remove the local declaration from the mock fixture (mock-data.ts), and
replace it with an import of Message from the new module; then update all UI
components and any other files that previously duplicated the type to import
Message from ai-panel.types.ts so there is a single source of truth.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (17)
apps/web/src/app/layout.tsxpackages/epics/src/common/ai-left-panel-layout.tsxpackages/epics/src/common/ai-left-panel.tsxpackages/epics/src/common/ai-panel/ai-panel-chat-bar.tsxpackages/epics/src/common/ai-panel/ai-panel-header.tsxpackages/epics/src/common/ai-panel/ai-panel-message-bubble.tsxpackages/epics/src/common/ai-panel/ai-panel-messages.tsxpackages/epics/src/common/ai-panel/ai-panel-suggestions.tsxpackages/epics/src/common/ai-panel/index.tspackages/epics/src/common/ai-panel/mock-data.tspackages/epics/src/common/index.tspackages/epics/src/hooks/index.tspackages/epics/src/hooks/use-is-mobile.tsxpackages/ui/package.jsonpackages/ui/src/drawer.tsxpackages/ui/src/index.tspackages/ui/src/organisms/footer.tsx
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (4)
packages/epics/src/common/ai-panel/ai-panel-messages.tsx (2)
22-23: Remove placeholder scroll anchor until scroll behavior is implemented.Line 22 and Line 38 keep an
endRefanchor, but there is no effect using it. Either wire auto-scroll behavior now or remove this placeholder to keep the component minimal.Minimal cleanup
-import { useRef } from 'react'; @@ - const endRef = useRef<HTMLDivElement>(null); @@ - <div ref={endRef} />Also applies to: 38-38
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/epics/src/common/ai-panel/ai-panel-messages.tsx` around lines 22 - 23, Remove the unused scroll anchor: delete the const endRef = useRef<HTMLDivElement>(null) declaration and the JSX anchor element that uses ref={endRef} inside the AiPanelMessages component, and also remove the now-unused useRef import to avoid linter warnings; leave a short TODO comment if you want to reintroduce auto-scroll later and run the build to ensure no unused-symbol errors remain.
7-7: Decouple UI typing from the mock-data module.Line 7 imports
Messagefrom./mock-data, which makes this production UI component depend on a mock-focused module boundary. MoveMessageto a sharedtypesmodule and import it from there.Proposed refactor
- import type { Message } from './mock-data'; + import type { Message } from './types';// packages/epics/src/common/ai-panel/types.ts export type Message = { id: string; role: 'user' | 'assistant'; content: string; timestamp: Date; isStreaming?: boolean; };Based on learnings: DSanich prefers extracting repeated type declarations into shared modules for reusability and maintainability, particularly for token types that are used across multiple files in the codebase.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/epics/src/common/ai-panel/ai-panel-messages.tsx` at line 7, The UI component imports the Message type from the mock-data module which couples production UI to mock fixtures; extract the Message type into a shared types module (e.g., export type Message = { id: string; role: 'user'|'assistant'; content: string; timestamp: Date; isStreaming?: boolean }) and update ai-panel-messages.tsx to import Message from that new types module instead of ./mock-data; ensure any other files currently importing Message from mock-data are updated to the new shared types export to keep typings consistent.packages/epics/src/common/ai-panel/ai-panel-header.tsx (2)
51-61: Add explicit menu semantics for assistive tech.The model dropdown works visually, but it is missing ARIA menu semantics (
aria-expanded,aria-haspopup, menu/menuitem roles), which hurts screen-reader clarity.Suggested accessibility refinement
<button type="button" onClick={() => setShowModelMenu(!showModelMenu)} + aria-haspopup="menu" + aria-expanded={showModelMenu} className="flex items-center gap-1.5 rounded-lg border border-border bg-secondary px-2.5 py-1.5 text-xs text-muted-foreground transition-colors hover:bg-muted hover:text-foreground" > @@ - <div className="absolute left-0 top-full z-50 mt-1 w-44 animate-in fade-in slide-in-from-top-2 rounded-xl border border-border bg-popover py-1 shadow-2xl duration-200"> + <div + role="menu" + aria-label="Select model" + className="absolute left-0 top-full z-50 mt-1 w-44 animate-in fade-in slide-in-from-top-2 rounded-xl border border-border bg-popover py-1 shadow-2xl duration-200" + > @@ <button key={m.id} type="button" + role="menuitemradio" + aria-checked={m.id === selectedModel.id} onClick={() => {Also applies to: 65-82
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/epics/src/common/ai-panel/ai-panel-header.tsx` around lines 51 - 61, The model dropdown currently lacks ARIA menu semantics; update the toggle button (the element using onClick and setShowModelMenu, currently rendering SelectedIcon and ChevronDown) to include aria-haspopup="menu" and aria-expanded={showModelMenu} and an id; wrap the conditional menu container (the div rendered when showModelMenu is true) with role="menu" and aria-labelledby pointing to the button id; ensure each selectable entry inside the menu uses role="menuitem" (or menuitemradio/menuitemcheckbox as appropriate) and keyboard focusability so assistive tech can announce and navigate the menu.
27-39: Avoid attaching a document listener when the menu is closed.The outside-click listener can be registered only while
showModelMenuis true to reduce global event work.Suggested optimization
useEffect(() => { + if (!showModelMenu) return; const handleClickOutside = (e: MouseEvent) => { - if ( - showModelMenu && - menuRef.current && - !menuRef.current.contains(e.target as Node) - ) { + if (menuRef.current && !menuRef.current.contains(e.target as Node)) { setShowModelMenu(false); } }; document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); }, [showModelMenu]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/epics/src/common/ai-panel/ai-panel-header.tsx` around lines 27 - 39, The effect currently always registers the document 'mousedown' listener even when the menu is closed; modify the useEffect (the hook that defines handleClickOutside) to only add the listener when showModelMenu is true (e.g., wrap the addEventListener/removeEventListener logic in an if (showModelMenu) block or return early when false), keep the same cleanup that removes the listener, and continue to reference menuRef and setShowModelMenu inside handleClickOutside to close the menu when a click occurs outside.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/epics/src/common/ai-panel/ai-panel-header.tsx`:
- Around line 87-94: The Refresh button in ai-panel-header.tsx is an interactive
control with no handler (the <button> containing <RefreshCw />), so either wire
it to the component's refresh logic or make it non-interactive; add an onClick
prop that calls the existing refresh/refreshState function or a passed-in prop
like onRefresh (or if none exists, expose a new prop and call the parent
handler), ensure accessibility by keeping aria-label and optionally add disabled
when refresh is unavailable, or replace the element with a non-button if it
should be purely decorative.
---
Nitpick comments:
In `@packages/epics/src/common/ai-panel/ai-panel-header.tsx`:
- Around line 51-61: The model dropdown currently lacks ARIA menu semantics;
update the toggle button (the element using onClick and setShowModelMenu,
currently rendering SelectedIcon and ChevronDown) to include
aria-haspopup="menu" and aria-expanded={showModelMenu} and an id; wrap the
conditional menu container (the div rendered when showModelMenu is true) with
role="menu" and aria-labelledby pointing to the button id; ensure each
selectable entry inside the menu uses role="menuitem" (or
menuitemradio/menuitemcheckbox as appropriate) and keyboard focusability so
assistive tech can announce and navigate the menu.
- Around line 27-39: The effect currently always registers the document
'mousedown' listener even when the menu is closed; modify the useEffect (the
hook that defines handleClickOutside) to only add the listener when
showModelMenu is true (e.g., wrap the addEventListener/removeEventListener logic
in an if (showModelMenu) block or return early when false), keep the same
cleanup that removes the listener, and continue to reference menuRef and
setShowModelMenu inside handleClickOutside to close the menu when a click occurs
outside.
In `@packages/epics/src/common/ai-panel/ai-panel-messages.tsx`:
- Around line 22-23: Remove the unused scroll anchor: delete the const endRef =
useRef<HTMLDivElement>(null) declaration and the JSX anchor element that uses
ref={endRef} inside the AiPanelMessages component, and also remove the
now-unused useRef import to avoid linter warnings; leave a short TODO comment if
you want to reintroduce auto-scroll later and run the build to ensure no
unused-symbol errors remain.
- Line 7: The UI component imports the Message type from the mock-data module
which couples production UI to mock fixtures; extract the Message type into a
shared types module (e.g., export type Message = { id: string; role:
'user'|'assistant'; content: string; timestamp: Date; isStreaming?: boolean })
and update ai-panel-messages.tsx to import Message from that new types module
instead of ./mock-data; ensure any other files currently importing Message from
mock-data are updated to the new shared types export to keep typings consistent.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
packages/epics/src/common/ai-panel/ai-panel-header.tsxpackages/epics/src/common/ai-panel/ai-panel-message-bubble.tsxpackages/epics/src/common/ai-panel/ai-panel-messages.tsxpackages/epics/src/common/ai-panel/mock-data.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/epics/src/common/ai-panel/mock-data.ts
- packages/epics/src/common/ai-panel/ai-panel-message-bubble.tsx
| <button | ||
| type="button" | ||
| className="flex h-7 w-7 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-muted hover:text-foreground" | ||
| title="Refresh" | ||
| aria-label="Refresh" | ||
| > | ||
| <RefreshCw className="h-3.5 w-3.5" /> | ||
| </button> |
There was a problem hiding this comment.
Refresh is a no-op clickable control.
Line 90-92 presents an actionable refresh button, but there is no handler. This is confusing UX unless intentionally disabled.
Suggested fix
type AiPanelHeaderProps = {
onClose: () => void;
+ onRefresh?: () => void;
modelOptions: ModelOption[];
selectedModel: ModelOption;
onModelSelect: (model: ModelOption) => void;
};
export function AiPanelHeader({
onClose,
+ onRefresh,
modelOptions,
selectedModel,
onModelSelect,
}: AiPanelHeaderProps) {
@@
<button
type="button"
+ onClick={onRefresh}
+ disabled={!onRefresh}
className="flex h-7 w-7 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-muted hover:text-foreground"
title="Refresh"
aria-label="Refresh"
>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/epics/src/common/ai-panel/ai-panel-header.tsx` around lines 87 - 94,
The Refresh button in ai-panel-header.tsx is an interactive control with no
handler (the <button> containing <RefreshCw />), so either wire it to the
component's refresh logic or make it non-interactive; add an onClick prop that
calls the existing refresh/refreshState function or a passed-in prop like
onRefresh (or if none exists, expose a new prop and call the parent handler),
ensure accessibility by keeping aria-label and optionally add disabled when
refresh is unavailable, or replace the element with a non-button if it should be
purely decorative.
Summary by CodeRabbit