@flamingo-stack/openframe-frontend-core 0.0.454 → 0.0.455

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/dist/{chunk-AJAF456I.js → chunk-2KUIA2AR.js} +3 -3
  2. package/dist/{chunk-55ISAJFV.js → chunk-5YPKZM6Y.js} +6 -6
  3. package/dist/{chunk-72VDY6MM.cjs → chunk-62AXZKYN.cjs} +539 -674
  4. package/dist/chunk-62AXZKYN.cjs.map +1 -0
  5. package/dist/{chunk-TMST2WFZ.js → chunk-6W4ZEVIC.js} +3 -3
  6. package/dist/{chunk-MLFFBQ5B.js → chunk-BD3JBEUC.js} +65 -135
  7. package/dist/chunk-BD3JBEUC.js.map +1 -0
  8. package/dist/{chunk-ACUD4XVZ.cjs → chunk-BE77G7GW.cjs} +40 -40
  9. package/dist/{chunk-ACUD4XVZ.cjs.map → chunk-BE77G7GW.cjs.map} +1 -1
  10. package/dist/{chunk-OZZ4VQ52.cjs → chunk-D5PBUVGB.cjs} +5 -5
  11. package/dist/{chunk-OZZ4VQ52.cjs.map → chunk-D5PBUVGB.cjs.map} +1 -1
  12. package/dist/{chunk-UGFB5EAW.js → chunk-DGPKGESY.js} +4 -4
  13. package/dist/{chunk-OGKDOG5D.js → chunk-EGTGRG7N.js} +2 -2
  14. package/dist/{chunk-XH33VG5A.cjs → chunk-FCVF4YAB.cjs} +40 -40
  15. package/dist/{chunk-XH33VG5A.cjs.map → chunk-FCVF4YAB.cjs.map} +1 -1
  16. package/dist/{chunk-WLUIXASU.cjs → chunk-GNYV6HYO.cjs} +15 -15
  17. package/dist/{chunk-WLUIXASU.cjs.map → chunk-GNYV6HYO.cjs.map} +1 -1
  18. package/dist/{chunk-PB24PKG2.cjs → chunk-IDCPYG7N.cjs} +29 -29
  19. package/dist/{chunk-PB24PKG2.cjs.map → chunk-IDCPYG7N.cjs.map} +1 -1
  20. package/dist/{chunk-YED55PQY.js → chunk-JUUPMMKI.js} +3 -3
  21. package/dist/{chunk-QF3NQW7C.cjs → chunk-KTGGM5WA.cjs} +2 -72
  22. package/dist/chunk-KTGGM5WA.cjs.map +1 -0
  23. package/dist/{chunk-GOWE4WSU.js → chunk-LB3OZE4B.js} +6 -6
  24. package/dist/{chunk-IM5A5F2E.cjs → chunk-M5RT26WQ.cjs} +73 -73
  25. package/dist/{chunk-IM5A5F2E.cjs.map → chunk-M5RT26WQ.cjs.map} +1 -1
  26. package/dist/{chunk-UOTHXOCS.js → chunk-N4CHA7T7.js} +5416 -5551
  27. package/dist/chunk-N4CHA7T7.js.map +1 -0
  28. package/dist/{chunk-44AD5D3C.cjs → chunk-NNDNJCJ3.cjs} +18 -18
  29. package/dist/{chunk-44AD5D3C.cjs.map → chunk-NNDNJCJ3.cjs.map} +1 -1
  30. package/dist/{chunk-PT233TJV.js → chunk-QSIZW6EX.js} +2 -2
  31. package/dist/{chunk-UPRY4I63.cjs → chunk-RT7DIGTN.cjs} +3 -3
  32. package/dist/{chunk-UPRY4I63.cjs.map → chunk-RT7DIGTN.cjs.map} +1 -1
  33. package/dist/{chunk-N2UFIQAD.cjs → chunk-RZL7I6YN.cjs} +7 -7
  34. package/dist/{chunk-N2UFIQAD.cjs.map → chunk-RZL7I6YN.cjs.map} +1 -1
  35. package/dist/{chunk-5HQV2F5K.cjs → chunk-UJZ6O2ON.cjs} +32 -32
  36. package/dist/{chunk-5HQV2F5K.cjs.map → chunk-UJZ6O2ON.cjs.map} +1 -1
  37. package/dist/{chunk-JUNKHNWE.js → chunk-VNE5R4LQ.js} +5 -5
  38. package/dist/{chunk-EQWE64RJ.js → chunk-X62ICFPJ.js} +2 -2
  39. package/dist/{chunk-YJ7SPS7I.js → chunk-XAQLBUCP.js} +2 -2
  40. package/dist/{chunk-NUT3HRKS.cjs → chunk-XT6S6DYS.cjs} +12 -12
  41. package/dist/{chunk-NUT3HRKS.cjs.map → chunk-XT6S6DYS.cjs.map} +1 -1
  42. package/dist/{chunk-YAZIVPUF.cjs → chunk-YSKSBLCG.cjs} +73 -73
  43. package/dist/{chunk-YAZIVPUF.cjs.map → chunk-YSKSBLCG.cjs.map} +1 -1
  44. package/dist/{chunk-MX2ITMKK.js → chunk-YXCYQKAX.js} +5 -5
  45. package/dist/components/case-studies/index.cjs +9 -9
  46. package/dist/components/case-studies/index.js +3 -3
  47. package/dist/components/chat/chat-message-list.d.ts.map +1 -1
  48. package/dist/components/chat/embeddable-chat.d.ts.map +1 -1
  49. package/dist/components/chat/guide-welcome.d.ts.map +1 -1
  50. package/dist/components/chat/index.cjs +3 -9
  51. package/dist/components/chat/index.cjs.map +1 -1
  52. package/dist/components/chat/index.d.ts +0 -2
  53. package/dist/components/chat/index.d.ts.map +1 -1
  54. package/dist/components/chat/index.js +2 -8
  55. package/dist/components/chat/mingo-welcome.d.ts.map +1 -1
  56. package/dist/components/chat/quick-action-chip.d.ts +48 -0
  57. package/dist/components/chat/quick-action-chip.d.ts.map +1 -1
  58. package/dist/components/chat/quick-action-marquee.d.ts.map +1 -1
  59. package/dist/components/chat/quick-action-wall.d.ts +1 -2
  60. package/dist/components/chat/quick-action-wall.d.ts.map +1 -1
  61. package/dist/components/chat/types/component.types.d.ts +0 -12
  62. package/dist/components/chat/types/component.types.d.ts.map +1 -1
  63. package/dist/components/contact/index.cjs +4 -4
  64. package/dist/components/contact/index.js +3 -3
  65. package/dist/components/docs/index.cjs +6 -6
  66. package/dist/components/docs/index.js +5 -5
  67. package/dist/components/embeds/index.cjs +4 -4
  68. package/dist/components/embeds/index.js +3 -3
  69. package/dist/components/faq/index.cjs +5 -5
  70. package/dist/components/faq/index.js +4 -4
  71. package/dist/components/features/index.cjs +3 -3
  72. package/dist/components/features/index.js +2 -2
  73. package/dist/components/help-center-pages/index.cjs +22 -22
  74. package/dist/components/help-center-pages/index.js +13 -13
  75. package/dist/components/index.cjs +135 -141
  76. package/dist/components/index.cjs.map +1 -1
  77. package/dist/components/index.js +11 -17
  78. package/dist/components/index.js.map +1 -1
  79. package/dist/components/navigation/index.cjs +3 -3
  80. package/dist/components/navigation/index.js +2 -2
  81. package/dist/components/onboarding-guides/index.cjs +7 -7
  82. package/dist/components/onboarding-guides/index.js +6 -6
  83. package/dist/components/related-content/index.cjs +5 -5
  84. package/dist/components/related-content/index.js +4 -4
  85. package/dist/components/tickets/index.cjs +6 -6
  86. package/dist/components/tickets/index.js +5 -5
  87. package/dist/components/ui/index.cjs +3 -9
  88. package/dist/components/ui/index.cjs.map +1 -1
  89. package/dist/components/ui/index.js +2 -8
  90. package/dist/hooks/index.cjs +2 -4
  91. package/dist/hooks/index.cjs.map +1 -1
  92. package/dist/hooks/index.d.ts +0 -1
  93. package/dist/hooks/index.d.ts.map +1 -1
  94. package/dist/hooks/index.js +1 -3
  95. package/dist/index.cjs +3 -11
  96. package/dist/index.cjs.map +1 -1
  97. package/dist/index.js +2 -10
  98. package/package.json +1 -1
  99. package/src/components/chat/approval-request-message.tsx +1 -1
  100. package/src/components/chat/chat-context-picker.tsx +1 -1
  101. package/src/components/chat/chat-input.tsx +1 -1
  102. package/src/components/chat/chat-message-list.tsx +5 -1
  103. package/src/components/chat/chat-message-skeleton.tsx +1 -1
  104. package/src/components/chat/chat-sidebar-skeleton.tsx +1 -1
  105. package/src/components/chat/chat-sidebar.tsx +2 -2
  106. package/src/components/chat/chat-ticket-list.tsx +1 -1
  107. package/src/components/chat/context-items-list.tsx +1 -1
  108. package/src/components/chat/embeddable-chat.tsx +15 -3
  109. package/src/components/chat/guide-welcome.tsx +23 -14
  110. package/src/components/chat/index.ts +0 -2
  111. package/src/components/chat/mingo-chat-history.tsx +1 -1
  112. package/src/components/chat/mingo-welcome.tsx +41 -27
  113. package/src/components/chat/quick-action-chip.tsx +77 -0
  114. package/src/components/chat/quick-action-marquee.tsx +1 -2
  115. package/src/components/chat/quick-action-wall.tsx +6 -4
  116. package/src/components/chat/slash-command-suggestions.tsx +1 -1
  117. package/src/components/chat/tool-call-blocks.tsx +1 -1
  118. package/src/components/chat/types/component.types.ts +0 -15
  119. package/src/hooks/.index.md +0 -1
  120. package/src/hooks/index.ts +0 -1
  121. package/src/stories/ChatContainer.stories.tsx +15 -12
  122. package/src/stories/QuickActionWall.stories.tsx +1 -2
  123. package/dist/chunk-72VDY6MM.cjs.map +0 -1
  124. package/dist/chunk-MLFFBQ5B.js.map +0 -1
  125. package/dist/chunk-QF3NQW7C.cjs.map +0 -1
  126. package/dist/chunk-UOTHXOCS.js.map +0 -1
  127. package/dist/components/chat/chat-quick-action-row.d.ts +0 -84
  128. package/dist/components/chat/chat-quick-action-row.d.ts.map +0 -1
  129. package/dist/components/chat/chat-quick-action.d.ts +0 -11
  130. package/dist/components/chat/chat-quick-action.d.ts.map +0 -1
  131. package/dist/hooks/use-quick-action-hint.d.ts +0 -25
  132. package/dist/hooks/use-quick-action-hint.d.ts.map +0 -1
  133. package/src/components/chat/.chat-quick-action.md +0 -46
  134. package/src/components/chat/chat-quick-action-row.tsx +0 -282
  135. package/src/components/chat/chat-quick-action.tsx +0 -59
  136. package/src/hooks/.use-quick-action-hint.md +0 -43
  137. package/src/hooks/use-quick-action-hint.ts +0 -126
  138. /package/dist/{chunk-AJAF456I.js.map → chunk-2KUIA2AR.js.map} +0 -0
  139. /package/dist/{chunk-55ISAJFV.js.map → chunk-5YPKZM6Y.js.map} +0 -0
  140. /package/dist/{chunk-TMST2WFZ.js.map → chunk-6W4ZEVIC.js.map} +0 -0
  141. /package/dist/{chunk-UGFB5EAW.js.map → chunk-DGPKGESY.js.map} +0 -0
  142. /package/dist/{chunk-OGKDOG5D.js.map → chunk-EGTGRG7N.js.map} +0 -0
  143. /package/dist/{chunk-YED55PQY.js.map → chunk-JUUPMMKI.js.map} +0 -0
  144. /package/dist/{chunk-GOWE4WSU.js.map → chunk-LB3OZE4B.js.map} +0 -0
  145. /package/dist/{chunk-PT233TJV.js.map → chunk-QSIZW6EX.js.map} +0 -0
  146. /package/dist/{chunk-JUNKHNWE.js.map → chunk-VNE5R4LQ.js.map} +0 -0
  147. /package/dist/{chunk-EQWE64RJ.js.map → chunk-X62ICFPJ.js.map} +0 -0
  148. /package/dist/{chunk-YJ7SPS7I.js.map → chunk-XAQLBUCP.js.map} +0 -0
  149. /package/dist/{chunk-MX2ITMKK.js.map → chunk-YXCYQKAX.js.map} +0 -0
@@ -46,7 +46,7 @@ const ChatTicketList = React.forwardRef<HTMLDivElement, ChatTicketListProps>(
46
46
  !fadeBottom && "border-b rounded-b-md",
47
47
  )}
48
48
  >
49
- <div ref={scrollRef} className="overflow-y-auto h-full" onScroll={updateFade}>
49
+ <div ref={scrollRef} className="overflow-y-auto overscroll-contain h-full" onScroll={updateFade}>
50
50
  {tickets.map((ticket) => (
51
51
  <ChatTicketItem
52
52
  key={ticket.id}
@@ -148,7 +148,7 @@ export function ContextItemsList({
148
148
  // Cap to 340px but never exceed ~half the viewport, so on short screens the
149
149
  // results list (inside the bottom-anchored picker popover) still fits and
150
150
  // scrolls instead of overflowing past the top edge.
151
- className={cn('max-h-[min(340px,45vh)] overflow-y-auto', className)}
151
+ className={cn('max-h-[min(340px,45vh)] overflow-y-auto overscroll-contain', className)}
152
152
  >
153
153
  {items.map(item => {
154
154
  const selected = selectedKeys.has(itemKey(item))
@@ -112,6 +112,18 @@ import { resolveIcon } from './utils/icon-library'
112
112
  import { getSourceIconName } from './utils/source-icons'
113
113
  import { formatSingularLookupInvocation } from './utils/slash-dispatch-utils'
114
114
 
115
+ // Desktop drawer opens at this fraction of the viewport width (clamped to the
116
+ // Drawer's own min/max). The user can still resize (persisted per DRAWER_WIDTH_KEY).
117
+ const DRAWER_DEFAULT_WIDTH_RATIO = 0.5
118
+ // Bump the suffix whenever the default policy changes so previously-persisted
119
+ // widths (e.g. the old fixed 750px / earlier 30% default) reset on next open.
120
+ const DRAWER_WIDTH_KEY = 'mingo-chat-width-v3'
121
+ const DRAWER_DEFAULT_WIDTH_PX = 750 // SSR fallback before the viewport is known
122
+ function drawerDefaultWidth(): number {
123
+ if (typeof window === 'undefined') return DRAWER_DEFAULT_WIDTH_PX
124
+ return Math.round(window.innerWidth * DRAWER_DEFAULT_WIDTH_RATIO)
125
+ }
126
+
115
127
 
116
128
  // =============================================================================
117
129
  // Types
@@ -806,7 +818,7 @@ function SourceChips({
806
818
  Sources
807
819
  </span>
808
820
  <div
809
- className={`flex flex-wrap gap-1.5 ${expanded ? 'max-h-[200px] overflow-y-auto' : ''}`}
821
+ className={`flex flex-wrap gap-1.5 ${expanded ? 'max-h-[200px] overflow-y-auto overscroll-contain' : ''}`}
810
822
  >
811
823
  {cited.map((src) => (
812
824
  <SourceChip
@@ -2515,8 +2527,8 @@ function EmbeddableChatInner({
2515
2527
  resizable
2516
2528
  minSize={480}
2517
2529
  maxSize={1600}
2518
- defaultSize={750}
2519
- storageKey="mingo-chat-width"
2530
+ defaultSize={drawerDefaultWidth()}
2531
+ storageKey={DRAWER_WIDTH_KEY}
2520
2532
  resizeAriaLabel="Resize chat panel"
2521
2533
  overlayClassName="mingo-chat-overlay"
2522
2534
  aria-describedby={undefined}
@@ -5,7 +5,7 @@ import { cn } from '../../utils/cn'
5
5
  import { MingoIcon } from '../icons'
6
6
  import { ScrollFadeOverlay, useScrollFade } from '../ui/scroll-fade'
7
7
  import { Skeleton } from '../ui/skeleton'
8
- import { ChatQuickActionRow } from './chat-quick-action-row'
8
+ import { QuickActionWall } from './quick-action-wall'
9
9
  import { EntityIcon } from '../icon-display'
10
10
  import { accentFromIdentityIcon, type QuickActionAccent } from './quick-action-chip'
11
11
 
@@ -103,9 +103,11 @@ export function GuideWelcome({
103
103
  // direction. (Same behaviour as MingoWelcome.)
104
104
  const { scrollRef, fadeTop, fadeBottom, update: updateScrollFade } = useScrollFade<HTMLDivElement>()
105
105
 
106
- // Map to the shared `ChatQuickActionRow` chip shape. In `wrap` mode every chip
107
- // renders (no overflow), and `onHoverStart`/`onHoverEnd` drive the composer
108
- // prompt preview.
106
+ // Map to the shared {@link QuickActionChip} shape consumed by the unified
107
+ // `QuickActionWall` the SAME chip wall the website hero + deck render, so
108
+ // the chat's quick actions read as "the work the agent does". Every chip
109
+ // stays interactive (clone copies included); `onHoverStart`/`onHoverEnd` drive
110
+ // the composer prompt preview and `onSelect` sends.
109
111
  const chipItems = React.useMemo(
110
112
  () =>
111
113
  quickActions.map((action) => ({
@@ -142,7 +144,7 @@ export function GuideWelcome({
142
144
  <div
143
145
  ref={scrollRef}
144
146
  onScroll={updateScrollFade}
145
- className="flex flex-1 min-h-0 flex-col gap-[var(--spacing-system-m)] overflow-y-auto"
147
+ className="flex flex-1 min-h-0 flex-col gap-[var(--spacing-system-m)] overflow-y-auto overscroll-contain"
146
148
  >
147
149
  {/* Greeting grows to fill (`flex-1`) so the slash-command list stays
148
150
  anchored below it — but its content is anchored at the TOP of the
@@ -192,17 +194,24 @@ export function GuideWelcome({
192
194
  <ScrollFadeOverlay edge="bottom" visible={fadeBottom} className="h-12" />
193
195
  </div>
194
196
 
195
- {/* Pinned quick-action chips above the composer — the shared
196
- `ChatQuickActionRow` in `wrap` mode: ALL chips render (no "⋯" overflow
197
- collapse) so every action is directly hoverable; hover/focus previews
198
- the action's full prompt in the composer, click sends it. Capped height
199
- + internal scroll (scrollbar-hide: scrollable, bar never shows) so a
200
- long list can't squeeze the composer on short screens. */}
197
+ {/* Pinned quick-action wall above the composer — the shared
198
+ {@link QuickActionWall} in BRICK mode (the SAME "wall of the work
199
+ agents do" the website hero renders): up to 4 stacked row marquees,
200
+ chips packed edge-to-edge, drifting under left/right edge fades so a
201
+ long agent action set gets "reach" without squeezing the composer.
202
+ Chips stay directly hoverable hover/focus previews the action's full
203
+ prompt in the composer, click sends it — and `pauseOnHover` freezes
204
+ the hovered row so a moving chip never dodges a click. */}
201
205
  {quickActions.length > 0 && (
202
- <ChatQuickActionRow
203
- wrap
206
+ <QuickActionWall
204
207
  chips={chipItems}
205
- className="max-h-28 overflow-y-auto scrollbar-hide"
208
+ rows={4}
209
+ pauseOnHover
210
+ fade={['left', 'right']}
211
+ fadeSize={{ left: 32 }}
212
+ fadeColor="var(--color-bg)"
213
+ copyGap="var(--spacing-system-xxs)"
214
+ className="max-h-44 shrink-0"
206
215
  />
207
216
  )}
208
217
  </div>
@@ -15,8 +15,6 @@ export * from './chat-message-list'
15
15
  export * from './chat-message-row'
16
16
  export * from './chat-message-skeleton'
17
17
 
18
- export * from './chat-quick-action'
19
- export * from './chat-quick-action-row'
20
18
  export * from './quick-action-chip'
21
19
  export * from './quick-action-marquee'
22
20
  export * from './quick-action-wall'
@@ -341,7 +341,7 @@ export function MingoChatHistory({
341
341
  <div
342
342
  ref={scrollRef}
343
343
  onScroll={updateFade}
344
- className="flex flex-1 min-h-0 flex-col gap-[var(--spacing-system-m)] overflow-y-auto"
344
+ className="flex flex-1 min-h-0 flex-col gap-[var(--spacing-system-m)] overflow-y-auto overscroll-contain"
345
345
  >
346
346
  {noSearchResults ? (
347
347
  // No search matches — same centred glyph + title + caption layout as
@@ -4,8 +4,8 @@ import * as React from 'react'
4
4
  import { cn } from '../../utils/cn'
5
5
  import { MingoIcon } from '../icons'
6
6
  import { MingoChatHistorySkeleton } from './mingo-chat-history'
7
- import { ChatQuickActionRow } from './chat-quick-action-row'
8
7
  import { QuickActionChipButton } from './quick-action-chip'
8
+ import { QuickActionWall } from './quick-action-wall'
9
9
  import { Button } from '../ui/button'
10
10
  import { ScrollFadeOverlay, useScrollFade } from '../ui/scroll-fade'
11
11
  import { XmarkIcon } from '../icons-v2-generated/signs-and-symbols/xmark-icon'
@@ -242,7 +242,7 @@ export function MingoWelcome({
242
242
  <div
243
243
  ref={scrollRef}
244
244
  onScroll={updateScrollFade}
245
- className="flex flex-1 min-h-0 flex-col gap-[var(--spacing-system-m)] overflow-y-auto"
245
+ className="flex flex-1 min-h-0 flex-col gap-[var(--spacing-system-m)] overflow-y-auto overscroll-contain"
246
246
  >
247
247
  {/* Greeting — grows to fill (`flex-1`) so it centres vertically,
248
248
  keeping the grid anchored at the bottom of the scroll area. Default
@@ -307,32 +307,46 @@ export function MingoWelcome({
307
307
  </div>
308
308
  )}
309
309
 
310
- {/* Quick-action chips. "Start Guide Chat" stays pinned (the `leading`
311
- slot never collapses); any caller `quickActions` fit inline and the
312
- rest collapse under a "⋯" overflow menu, width-measured like the
313
- Autocomplete tag row. */}
310
+ {/* Quick actions. "Start Guide Chat" stays pinned above the wall — it's
311
+ the primary mode switch and must never scroll out of reach. The rest
312
+ render in the shared {@link QuickActionWall} in BRICK mode (the SAME
313
+ chip wall the website hero uses): 2 stacked row marquees under left/
314
+ right edge fades, so a long agent action set gets "reach" without
315
+ squeezing the composer. `pauseOnHover` freezes the hovered row so a
316
+ moving chip never dodges a click; hover/focus previews the action's
317
+ full prompt in the composer. */}
314
318
  {(onStartGuideChat || (quickActions && quickActions.length > 0)) && (
315
- <ChatQuickActionRow
316
- leading={
317
- onStartGuideChat && (
318
- <QuickActionChipButton
319
- label="Start Guide Chat"
320
- icon={<CompassIcon size={16} />}
321
- variant={hasExistingChats ? 'outline' : 'primary'}
322
- onSelect={onStartGuideChat}
323
- />
324
- )
325
- }
326
- chips={(quickActions ?? []).map((action) => ({
327
- id: action.id,
328
- label: action.label,
329
- icon: action.icon,
330
- variant: action.variant,
331
- onSelect: action.onClick,
332
- onHoverStart: () => onQuickActionHover?.(action),
333
- onHoverEnd: () => onQuickActionHoverEnd?.(),
334
- }))}
335
- />
319
+ <div className="flex shrink-0 flex-col gap-[var(--spacing-system-xsf)]">
320
+ {onStartGuideChat && (
321
+ <QuickActionChipButton
322
+ label="Start Guide Chat"
323
+ icon={<CompassIcon size={16} />}
324
+ variant={hasExistingChats ? 'outline' : 'primary'}
325
+ onSelect={onStartGuideChat}
326
+ className="self-start"
327
+ />
328
+ )}
329
+ {quickActions && quickActions.length > 0 && (
330
+ <QuickActionWall
331
+ chips={quickActions.map((action) => ({
332
+ id: action.id,
333
+ label: action.label,
334
+ icon: action.icon,
335
+ variant: action.variant,
336
+ onSelect: action.onClick,
337
+ onHoverStart: () => onQuickActionHover?.(action),
338
+ onHoverEnd: () => onQuickActionHoverEnd?.(),
339
+ }))}
340
+ rows={4}
341
+ pauseOnHover
342
+ fade={['left', 'right']}
343
+ fadeSize={{ left: 32 }}
344
+ fadeColor="var(--color-bg)"
345
+ copyGap="var(--spacing-system-xxs)"
346
+ className="max-h-44 shrink-0"
347
+ />
348
+ )}
349
+ </div>
336
350
  )}
337
351
  </div>
338
352
  )}
@@ -299,3 +299,80 @@ export function QuickActionChipButton({
299
299
  </button>
300
300
  )
301
301
  }
302
+
303
+ // =============================================================================
304
+ // Chip data shape + data→button mapper
305
+ // =============================================================================
306
+
307
+ /**
308
+ * THE quick-action chip DATA shape — one declarative description of a chip that
309
+ * every quick-action surface builds (chat empty states, {@link QuickActionWall},
310
+ * {@link QuickActionMarquee}). Maps 1:1 to {@link QuickActionChipButton} props
311
+ * via {@link QuickActionChipFromData}, so adding a chip capability means editing
312
+ * exactly these two.
313
+ */
314
+ export interface QuickActionChip {
315
+ /** Stable React key + menu-item id. */
316
+ id: string
317
+ label: string
318
+ /** Pre-rendered node OR a declarative {@link QuickActionIconSpec} (resolved
319
+ * via the unified `<EntityIcon>` path). */
320
+ icon?: React.ReactNode | QuickActionIconSpec
321
+ /** Caller-supplied {@link QuickActionThemeSpec} (accent + optional
322
+ * lozenge) — per-chip so mixed walls (interleaved IT/SEC streams) work.
323
+ * The lib ships no theme registry; consumers define their own specs. */
324
+ theme?: QuickActionThemeSpec
325
+ /** Classification affix at the label's leading edge; `true` = the theme's. */
326
+ lozenge?: QuickActionChipLozenge | boolean
327
+ /** `'primary'` = accent (yellow) chip, `'outline'` = bordered chip (default). */
328
+ variant?: 'primary' | 'outline'
329
+ /** Active single-select state — renders the accented `selected` skin
330
+ * (overrides `variant`). */
331
+ selected?: boolean
332
+ /** Accent for the `selected` skin (`'cyan'` = cyan twin, else pink). */
333
+ selectedAccent?: QuickActionAccent
334
+ onSelect?: () => void
335
+ /** Pointer/keyboard focus enters the chip — e.g. preview the full prompt in
336
+ * the composer. */
337
+ onHoverStart?: () => void
338
+ /** Pointer/keyboard focus leaves the chip — e.g. restore the composer. */
339
+ onHoverEnd?: () => void
340
+ }
341
+
342
+ /**
343
+ * THE {@link QuickActionChip}-data → {@link QuickActionChipButton} mapper —
344
+ * one prop-plumbing spelling shared by every chat empty state, `QuickActionWall`,
345
+ * and `QuickActionMarquee` (adding a chip field means editing exactly here).
346
+ * `defaultTheme`/`defaultLozenge` fill gaps for wall-level theming;
347
+ * `interactive={false}` renders the decorative Tag form (loop-clone copies).
348
+ */
349
+ export function QuickActionChipFromData({
350
+ chip,
351
+ defaultTheme,
352
+ defaultLozenge,
353
+ interactive = true,
354
+ className,
355
+ }: {
356
+ chip: QuickActionChip
357
+ defaultTheme?: QuickActionThemeSpec
358
+ defaultLozenge?: boolean
359
+ interactive?: boolean
360
+ className?: string
361
+ }) {
362
+ return (
363
+ <QuickActionChipButton
364
+ label={chip.label}
365
+ icon={chip.icon}
366
+ theme={chip.theme ?? defaultTheme}
367
+ lozenge={chip.lozenge ?? (defaultLozenge || undefined)}
368
+ variant={chip.variant ?? 'outline'}
369
+ selected={chip.selected}
370
+ selectedAccent={chip.selectedAccent}
371
+ onSelect={chip.onSelect}
372
+ onHoverStart={chip.onHoverStart}
373
+ onHoverEnd={chip.onHoverEnd}
374
+ interactive={interactive}
375
+ className={className}
376
+ />
377
+ )
378
+ }
@@ -2,8 +2,7 @@
2
2
 
3
3
  import * as React from 'react'
4
4
  import type { MarqueeSyncController } from '../ui/marquee-wall'
5
- import type { QuickActionIconSpec } from './quick-action-chip'
6
- import type { QuickActionChip } from './chat-quick-action-row'
5
+ import type { QuickActionIconSpec, QuickActionChip } from './quick-action-chip'
7
6
  import { QuickActionWall } from './quick-action-wall'
8
7
 
9
8
  // =============================================================================
@@ -9,10 +9,12 @@ import {
9
9
  type MarqueeWallFadeEdge,
10
10
  type MarqueeWallProps,
11
11
  } from '../ui/marquee-wall'
12
- import { QuickActionChipSkeleton } from './quick-action-chip'
13
- import type { QuickActionThemeSpec } from './quick-action-chip'
14
- import { QuickActionChipFromData } from './chat-quick-action-row'
15
- import type { QuickActionChip } from './chat-quick-action-row'
12
+ import {
13
+ QuickActionChipSkeleton,
14
+ QuickActionChipFromData,
15
+ type QuickActionThemeSpec,
16
+ type QuickActionChip,
17
+ } from './quick-action-chip'
16
18
 
17
19
  // =============================================================================
18
20
  // Shared wall constants
@@ -79,7 +79,7 @@ export function SlashCommandSuggestions({
79
79
  role="menu"
80
80
  aria-label="Slash command suggestions"
81
81
  className={cn(
82
- 'absolute bottom-full mb-2 left-0 right-0 z-50 max-h-96 overflow-y-auto',
82
+ 'absolute bottom-full mb-2 left-0 right-0 z-50 max-h-96 overflow-y-auto overscroll-contain',
83
83
  'rounded-md border border-ods-border bg-ods-card shadow-lg',
84
84
  className,
85
85
  )}
@@ -10,7 +10,7 @@ import { formatToolArgValue, formatToolResult } from "./utils/tool-call-helpers"
10
10
  * scale, no transform) — the token this file was originally flagged for.
11
11
  */
12
12
  const CODE_PRE_CLASS =
13
- "bg-ods-bg border border-ods-border rounded-md p-[var(--spacing-system-sf)] w-full max-h-64 overflow-auto text-code text-ods-text-primary whitespace-pre"
13
+ "bg-ods-bg border border-ods-border rounded-md p-[var(--spacing-system-sf)] w-full max-h-64 overflow-auto overscroll-contain text-code text-ods-text-primary whitespace-pre"
14
14
 
15
15
  /**
16
16
  * Single arg row: inline `key: value` or labeled `<pre>` block.
@@ -582,21 +582,6 @@ export interface ModelUsageBreakdown {
582
582
  haikuSummarizer?: { input: number; output: number }
583
583
  }
584
584
 
585
- // ========== Chat Quick Action Props ==========
586
-
587
- export interface ChatQuickActionProps extends HTMLAttributes<HTMLButtonElement> {
588
- text: string
589
- icon?: React.ReactNode
590
- onAction?: (text: string) => void
591
- onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void
592
- /** Whether this action should show the hint animation */
593
- isHintActive?: boolean
594
- /** Callback when user clicks the action - stops hint */
595
- onHintInteraction?: () => void
596
- disabled?: boolean
597
- loading?: boolean
598
- }
599
-
600
585
  // ========== Dialog Item Props ==========
601
586
 
602
587
  export interface DialogItem {
@@ -9,7 +9,6 @@ Central barrel export file for the hooks package, re-exporting all custom React
9
9
  | `./platform` | Platform-specific hooks |
10
10
  | `./use-toast` | Toast notification hook |
11
11
  | `./use-contact-submission` | Contact form submission hook |
12
- | `./use-quick-action-hint` | Quick action hint display hook |
13
12
  | `./use-copy-to-clipboard` | Clipboard write hook |
14
13
  | `./use-batch-images` | Authenticated batch image loading |
15
14
  | `./use-authenticated-image` | Single authenticated image hook |
@@ -3,7 +3,6 @@ export * from './ui'
3
3
  export * from './platform'
4
4
  export * from './use-toast'
5
5
  export * from './use-contact-submission'
6
- export * from './use-quick-action-hint'
7
6
  export * from './use-copy-to-clipboard'
8
7
 
9
8
  // Image authentication hooks
@@ -9,11 +9,8 @@ import {
9
9
  } from '../components/chat/chat-container'
10
10
  import { ChatInput } from '../components/chat/chat-input'
11
11
  import { ChatMessageList } from '../components/chat/chat-message-list'
12
- import {
13
- ChatQuickActionRow,
14
- ChatQuickActionRowSkeleton,
15
- type QuickActionChip,
16
- } from '../components/chat/chat-quick-action-row'
12
+ import { QuickActionWall } from '../components/chat/quick-action-wall'
13
+ import type { QuickActionChip } from '../components/chat/quick-action-chip'
17
14
  import { ModelDisplay, ModelDisplaySkeleton } from '../components/chat/model-display'
18
15
  import type { Message } from '../components/chat/types/message.types'
19
16
  import { cn } from '../utils/cn'
@@ -145,13 +142,19 @@ function FaeChatShell({
145
142
  />
146
143
  </ChatContent>
147
144
  <ChatFooter fullWidth={fullWidth}>
148
- {/* Quick-action chips above the composer — real row by default, skeleton
149
- placeholder in the loading variant (same pattern as ModelDisplay). */}
150
- {modelLoading ? (
151
- <ChatQuickActionRowSkeleton className="mb-[var(--spacing-system-s)]" />
152
- ) : (
153
- <ChatQuickActionRow className="mb-[var(--spacing-system-s)]" chips={SAMPLE_QUICK_ACTIONS} />
154
- )}
145
+ {/* Quick actions above the composer — the shared `QuickActionWall`
146
+ (its own 1:1 skeleton drives the loading variant, same pattern as
147
+ ModelDisplay). */}
148
+ <QuickActionWall
149
+ className="mb-[var(--spacing-system-s)] max-h-28"
150
+ chips={SAMPLE_QUICK_ACTIONS}
151
+ loading={modelLoading}
152
+ rows={2}
153
+ fade={['left', 'right']}
154
+ fadeSize={{ left: 32 }}
155
+ fadeColor="var(--color-bg)"
156
+ copyGap="var(--spacing-system-xxs)"
157
+ />
155
158
  <ChatInput
156
159
  onSend={handleSend}
157
160
  placeholder="Message Fae..."
@@ -1,7 +1,6 @@
1
1
  import type { Meta, StoryObj } from '@storybook/nextjs-vite'
2
2
  import { QuickActionWall, interleave } from '../components/chat/quick-action-wall'
3
- import type { QuickActionChip } from '../components/chat/chat-quick-action-row'
4
- import type { QuickActionThemeSpec } from '../components/chat/quick-action-chip'
3
+ import type { QuickActionChip, QuickActionThemeSpec } from '../components/chat/quick-action-chip'
5
4
 
6
5
  // Theme specs are CALLER-supplied (the lib ships no registry, no fallbacks):
7
6
  // these are this story's own demo specs — real consumers resolve agent