Unify quick actions into the general AI chat + delete legacy ChatQuickAction path#1500
Conversation
…Action path; global chat drawer 50% default [preview:none] GuideWelcome/MingoWelcome render the shared QuickActionWall (brick rows, up to 4) instead of the bespoke ChatQuickActionRow. Relocated QuickActionChip type + QuickActionChipFromData mapper into quick-action-chip.tsx. Global Mingo AI drawer opens at 50% of viewport (drawerDefaultWidth, storageKey -v3). Deleted dead ChatQuickAction/ChatQuickActionRow/use-quick-action-hint + ChatQuickActionProps + sidecars + barrel exports; stories updated. Co-Authored-By: Claude Opus 4.8 <[email protected]>
📝 WalkthroughWalkthroughThe PR replaces legacy chat quick-action components with ChangesQuick-action consolidation
Responsive drawer sizing
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 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: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
openframe-frontend-core/src/components/chat/mingo-welcome.tsx (1)
196-198: 🚀 Performance & Scalability | 🟠 Major | 🏗️ Heavy liftMemoize quick actions to prevent marquee track re-renders on scroll.
MingoWelcomeupdates its state frequently on scroll due touseScrollFade. Because thequickActions.map()is inline, it creates a new array reference on every single scroll frame. This defeatsQuickActionWall's internaluseMemoand forces the marquee to continuously re-measure and re-render all chips while the user scrolls.Memoize the mapped array so the
QuickActionWallmaintains a stable reference across scroll-driven renders (similar toGuideWelcome). I also updated the inline comment here which mentions "2 stacked row marquees" to correctly reflectrows={4}.⚡ Proposed fix for the scroll performance leak and comment
const showPinnedRegion = !!dialogHistory || (!isLoadingHistory && !loadError) + const chipItems = React.useMemo( + () => + quickActions?.map((action) => ({ + id: action.id, + label: action.label, + icon: action.icon, + variant: action.variant, + onSelect: action.onClick, + onHoverStart: () => onQuickActionHover?.(action), + onHoverEnd: () => onQuickActionHoverEnd?.(), + })) ?? [], + [quickActions, onQuickActionHover, onQuickActionHoverEnd], + ) + return (- {/* Quick actions. "Start Guide Chat" stays pinned above the wall — it's - the primary mode switch and must never scroll out of reach. The rest - render in the shared {`@link` QuickActionWall} in BRICK mode (the SAME - chip wall the website hero uses): 2 stacked row marquees under left/ - right edge fades, so a long agent action set gets "reach" without - squeezing the composer. `pauseOnHover` freezes the hovered row so a - moving chip never dodges a click; hover/focus previews the action's - full prompt in the composer. */} + {/* Quick actions. "Start Guide Chat" stays pinned above the wall — it's + the primary mode switch and must never scroll out of reach. The rest + render in the shared {`@link` QuickActionWall} in BRICK mode (the SAME + chip wall the website hero uses): 4 stacked row marquees under left/ + right edge fades, so a long agent action set gets "reach" without + squeezing the composer. `pauseOnHover` freezes the hovered row so a + moving chip never dodges a click; hover/focus previews the action's + full prompt in the composer. */} {(onStartGuideChat || (quickActions && quickActions.length > 0)) && ( <div className="flex shrink-0 flex-col gap-[var(--spacing-system-xsf)]"> {onStartGuideChat && ( <QuickActionChipButton label="Start Guide Chat" icon={<CompassIcon size={16} />} variant={hasExistingChats ? 'outline' : 'primary'} onSelect={onStartGuideChat} className="self-start" /> )} {quickActions && quickActions.length > 0 && ( <QuickActionWall - chips={quickActions.map((action) => ({ - id: action.id, - label: action.label, - icon: action.icon, - variant: action.variant, - onSelect: action.onClick, - onHoverStart: () => onQuickActionHover?.(action), - onHoverEnd: () => onQuickActionHoverEnd?.(), - }))} + chips={chipItems} rows={4} pauseOnHoverAlso applies to: 310-349
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@openframe-frontend-core/src/components/chat/mingo-welcome.tsx` around lines 196 - 198, Memoize the mapped quick-actions array in MingoWelcome before passing it to QuickActionWall, using the existing quickActions data as the dependency so scroll-driven useScrollFade renders preserve a stable reference. Also update the nearby inline comment to describe the four stacked row marquees configured by rows={4}, matching the GuideWelcome pattern.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@openframe-frontend-core/src/components/chat/embeddable-chat.tsx`:
- Around line 115-125: The initial drawer width is inconsistent between SSR and
the first client render because drawerDefaultWidth() uses 750px on the server
and viewport width in the browser. Update drawerDefaultWidth() or the
DrawerContent initialization so SSR and hydration use the same deterministic
default, deferring the viewport-based 50vw calculation until after mount while
preserving subsequent resizing behavior.
---
Outside diff comments:
In `@openframe-frontend-core/src/components/chat/mingo-welcome.tsx`:
- Around line 196-198: Memoize the mapped quick-actions array in MingoWelcome
before passing it to QuickActionWall, using the existing quickActions data as
the dependency so scroll-driven useScrollFade renders preserve a stable
reference. Also update the nearby inline comment to describe the four stacked
row marquees configured by rows={4}, matching the GuideWelcome pattern.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: a55fded0-40c1-4b67-981d-ce6db769cc80
📒 Files selected for processing (17)
openframe-frontend-core/src/components/chat/.chat-quick-action.mdopenframe-frontend-core/src/components/chat/chat-quick-action-row.tsxopenframe-frontend-core/src/components/chat/chat-quick-action.tsxopenframe-frontend-core/src/components/chat/embeddable-chat.tsxopenframe-frontend-core/src/components/chat/guide-welcome.tsxopenframe-frontend-core/src/components/chat/index.tsopenframe-frontend-core/src/components/chat/mingo-welcome.tsxopenframe-frontend-core/src/components/chat/quick-action-chip.tsxopenframe-frontend-core/src/components/chat/quick-action-marquee.tsxopenframe-frontend-core/src/components/chat/quick-action-wall.tsxopenframe-frontend-core/src/components/chat/types/component.types.tsopenframe-frontend-core/src/hooks/.index.mdopenframe-frontend-core/src/hooks/.use-quick-action-hint.mdopenframe-frontend-core/src/hooks/index.tsopenframe-frontend-core/src/hooks/use-quick-action-hint.tsopenframe-frontend-core/src/stories/ChatContainer.stories.tsxopenframe-frontend-core/src/stories/QuickActionWall.stories.tsx
💤 Files with no reviewable changes (9)
- openframe-frontend-core/src/components/chat/.chat-quick-action.md
- openframe-frontend-core/src/hooks/use-quick-action-hint.ts
- openframe-frontend-core/src/components/chat/chat-quick-action.tsx
- openframe-frontend-core/src/hooks/.index.md
- openframe-frontend-core/src/components/chat/index.ts
- openframe-frontend-core/src/hooks/index.ts
- openframe-frontend-core/src/components/chat/chat-quick-action-row.tsx
- openframe-frontend-core/src/hooks/.use-quick-action-hint.md
- openframe-frontend-core/src/components/chat/types/component.types.ts
| // Desktop drawer opens at this fraction of the viewport width (clamped to the | ||
| // Drawer's own min/max). The user can still resize (persisted per DRAWER_WIDTH_KEY). | ||
| const DRAWER_DEFAULT_WIDTH_RATIO = 0.5 | ||
| // Bump the suffix whenever the default policy changes so previously-persisted | ||
| // widths (e.g. the old fixed 750px / earlier 30% default) reset on next open. | ||
| const DRAWER_WIDTH_KEY = 'mingo-chat-width-v3' | ||
| const DRAWER_DEFAULT_WIDTH_PX = 750 // SSR fallback before the viewport is known | ||
| function drawerDefaultWidth(): number { | ||
| if (typeof window === 'undefined') return DRAWER_DEFAULT_WIDTH_PX | ||
| return Math.round(window.innerWidth * DRAWER_DEFAULT_WIDTH_RATIO) | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
rg -n -A80 -B20 'function DrawerContent|const DrawerContent|useResizableSize' openframe-frontend-core/src/components/ui/drawer.tsx
rg -n -A8 -B8 'defaultOpen|<DrawerContent|drawerDefaultWidth' openframe-frontend-core/src/components/chat/embeddable-chat.tsxRepository: flamingo-stack/openframe-oss-lib
Length of output: 12362
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n -A40 -B20 'function useControllableState|const useControllableState|useControllableState<' openframe-frontend-core/src
rg -n -A40 -B20 '<DrawerContent|<Drawer open=|defaultOpen|open=' openframe-frontend-core/src/components/chat/embeddable-chat.tsxRepository: flamingo-stack/openframe-oss-lib
Length of output: 17505
Keep the initial drawer width deterministic across SSR and hydration.
When the drawer is open on the server, drawerDefaultWidth() returns 750px, but the first client render returns 50vw. Since DrawerContent seeds its resizable state from defaultSize, the panel can hydrate at a different width. Use the same initial value on both sides, or defer the viewport-based width until after mount.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@openframe-frontend-core/src/components/chat/embeddable-chat.tsx` around lines
115 - 125, The initial drawer width is inconsistent between SSR and the first
client render because drawerDefaultWidth() uses 750px on the server and viewport
width in the browser. Update drawerDefaultWidth() or the DrawerContent
initialization so SSR and hydration use the same deterministic default,
deferring the viewport-based 50vw calculation until after mount while preserving
subsequent resizing behavior.
What
Makes the general AI chat empty state render the same shared
QuickActionWallthe website hero + deck use, and removes the deprecated bespoke chat quick-action components.Chat empty state → shared wall
GuideWelcomeandMingoWelcomenow renderQuickActionWall(brick mode, up to 4 rows, left/right edge fades,pauseOnHover) instead of the bespokeChatQuickActionRow. Hover→composer-preview and click→send are preserved (the chip already supportsonHoverStart/End+onSelect).MingoWelcomekeeps its pinned "Start Guide Chat" chip above the wall.Shared chip mapper relocated
QuickActionChiptype +QuickActionChipFromDatamapper fromchat-quick-action-row.tsxintoquick-action-chip.tsx(the SSOT the wall + marquee import). Repointedquick-action-wall,quick-action-marquee, and both stories.Global Mingo AI drawer width
EmbeddableChat's drawer now opens at 50% of viewport width (drawerDefaultWidth(), was a fixed 750px), clamped by the existing 480px min. Bumped the persistedstorageKeytomingo-chat-width-v3so the new default applies over stale saved widths.Hard deletions (dead code)
chat-quick-action.tsx(deprecatedChatQuickAction),chat-quick-action-row.tsx(ChatQuickActionRow),use-quick-action-hint.ts,ChatQuickActionProps, their.mddoc sidecars, and barrel exports. Zero remaining references (code + comments). Storybook stories updated to the wall.Why
The chip primitive was already shared, but the chat used a separate static row while every marketing surface used the richer
QuickActionWall— a DRY gap. This closes it so "the work the agents do" reads identically everywhere.Reviewer notes
openframe-frontend-coreversion.MarqueeWallFadesuses a solid color-overlay fade (not a mask), so a wall only blends on a solid surface — see the hub PR's fix for the gradient-background case.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Changes