Skip to content

Unify quick actions into the general AI chat + delete legacy ChatQuickAction path#1500

Merged
michaelassraf merged 1 commit into
mainfrom
claude/unify-quick-actions-chat-135adc
Jul 19, 2026
Merged

Unify quick actions into the general AI chat + delete legacy ChatQuickAction path#1500
michaelassraf merged 1 commit into
mainfrom
claude/unify-quick-actions-chat-135adc

Conversation

@michaelassraf

@michaelassraf michaelassraf commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

What

Makes the general AI chat empty state render the same shared QuickActionWall the website hero + deck use, and removes the deprecated bespoke chat quick-action components.

Chat empty state → shared wall

  • GuideWelcome and MingoWelcome now render QuickActionWall (brick mode, up to 4 rows, left/right edge fades, pauseOnHover) instead of the bespoke ChatQuickActionRow. Hover→composer-preview and click→send are preserved (the chip already supports onHoverStart/End + onSelect).
  • MingoWelcome keeps its pinned "Start Guide Chat" chip above the wall.

Shared chip mapper relocated

  • Moved the QuickActionChip type + QuickActionChipFromData mapper from chat-quick-action-row.tsx into quick-action-chip.tsx (the SSOT the wall + marquee import). Repointed quick-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 persisted storageKey to mingo-chat-width-v3 so the new default applies over stale saved widths.

Hard deletions (dead code)

  • chat-quick-action.tsx (deprecated ChatQuickAction), chat-quick-action-row.tsx (ChatQuickActionRow), use-quick-action-hint.ts, ChatQuickActionProps, their .md doc 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

  • Net −460 lines (176 insertions / 636 deletions).
  • Paired hub PR: unifies the OpenFrame AI co-pilot tabs + swaps the mock hero demo onto real agent data. After this lib PR merges + releases, the hub pins the new openframe-frontend-core version.
  • MarqueeWallFades uses 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

    • Added a unified quick-action chip model and rendering support.
    • Updated welcome screens and chat examples to use the Quick Action Wall, supporting multi-row layouts, hover behavior, and loading states.
    • Chat drawer width now adapts responsively to the viewport.
  • Changes

    • Removed legacy chat quick-action components and the rotating quick-action hint hook.
    • Existing saved drawer widths are reset to accommodate the updated sizing behavior.

…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]>
@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR replaces legacy chat quick-action components with QuickActionWall and a shared chip contract, removes related public exports and hook documentation, and updates consumers and stories. Embeddable chat drawer sizing now derives from viewport width with a versioned persistence key.

Changes

Quick-action consolidation

Layer / File(s) Summary
Shared quick-action contract
openframe-frontend-core/src/components/chat/quick-action-chip.tsx, openframe-frontend-core/src/components/chat/quick-action-marquee.tsx, openframe-frontend-core/src/components/chat/quick-action-wall.tsx
Adds the shared QuickActionChip data shape and mapper, and updates wall and marquee imports to use it.
Quick-action consumer migration
openframe-frontend-core/src/components/chat/guide-welcome.tsx, openframe-frontend-core/src/components/chat/mingo-welcome.tsx, openframe-frontend-core/src/stories/*
Replaces legacy row rendering with QuickActionWall, including pinned actions, hover callbacks, loading behavior, and updated layout configuration.
Legacy quick-action surface removal
openframe-frontend-core/src/components/chat/index.ts, openframe-frontend-core/src/components/chat/types/component.types.ts, openframe-frontend-core/src/hooks/index.ts
Removes legacy chat quick-action exports, the old prop contract, and the quick-action hint hook export and documentation.

Responsive drawer sizing

Layer / File(s) Summary
Responsive drawer width
openframe-frontend-core/src/components/chat/embeddable-chat.tsx
Computes the drawer default width from viewport size with an SSR fallback and changes persistence to a versioned storage key.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

Suggested reviewers: romanivan-flamingo, pavlo-flamingo, aliaska-varieva

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: consolidating quick actions and removing the legacy ChatQuickAction path.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/unify-quick-actions-chat-135adc

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.

@michaelassraf
michaelassraf merged commit 68c53d1 into main Jul 19, 2026
6 of 7 checks passed
@michaelassraf
michaelassraf deleted the claude/unify-quick-actions-chat-135adc branch July 19, 2026 12:02

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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)
openframe-frontend-core/src/components/chat/mingo-welcome.tsx (1)

196-198: 🚀 Performance & Scalability | 🟠 Major | 🏗️ Heavy lift

Memoize quick actions to prevent marquee track re-renders on scroll.

MingoWelcome updates its state frequently on scroll due to useScrollFade. Because the quickActions.map() is inline, it creates a new array reference on every single scroll frame. This defeats QuickActionWall's internal useMemo and forces the marquee to continuously re-measure and re-render all chips while the user scrolls.

Memoize the mapped array so the QuickActionWall maintains a stable reference across scroll-driven renders (similar to GuideWelcome). I also updated the inline comment here which mentions "2 stacked row marquees" to correctly reflect rows={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}
               pauseOnHover

Also 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

📥 Commits

Reviewing files that changed from the base of the PR and between a876657 and cac5f33.

📒 Files selected for processing (17)
  • openframe-frontend-core/src/components/chat/.chat-quick-action.md
  • openframe-frontend-core/src/components/chat/chat-quick-action-row.tsx
  • openframe-frontend-core/src/components/chat/chat-quick-action.tsx
  • openframe-frontend-core/src/components/chat/embeddable-chat.tsx
  • openframe-frontend-core/src/components/chat/guide-welcome.tsx
  • openframe-frontend-core/src/components/chat/index.ts
  • openframe-frontend-core/src/components/chat/mingo-welcome.tsx
  • openframe-frontend-core/src/components/chat/quick-action-chip.tsx
  • openframe-frontend-core/src/components/chat/quick-action-marquee.tsx
  • openframe-frontend-core/src/components/chat/quick-action-wall.tsx
  • openframe-frontend-core/src/components/chat/types/component.types.ts
  • openframe-frontend-core/src/hooks/.index.md
  • openframe-frontend-core/src/hooks/.use-quick-action-hint.md
  • openframe-frontend-core/src/hooks/index.ts
  • openframe-frontend-core/src/hooks/use-quick-action-hint.ts
  • openframe-frontend-core/src/stories/ChatContainer.stories.tsx
  • openframe-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

Comment on lines +115 to +125
// 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)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 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.tsx

Repository: 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.tsx

Repository: 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant