@flamingo-stack/openframe-frontend-core 0.0.219 → 0.0.220-snapshot.20260602172647

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 (146) hide show
  1. package/dist/{chunk-F3FO2ZZZ.cjs → chunk-4PBV66HQ.cjs} +7 -7
  2. package/dist/{chunk-F3FO2ZZZ.cjs.map → chunk-4PBV66HQ.cjs.map} +1 -1
  3. package/dist/{chunk-EDW2NVRV.js → chunk-4WZOFD46.js} +37 -37
  4. package/dist/{chunk-EDW2NVRV.js.map → chunk-4WZOFD46.js.map} +1 -1
  5. package/dist/{chunk-YX3YQNC4.cjs → chunk-73YDB6AT.cjs} +13 -13
  6. package/dist/{chunk-YX3YQNC4.cjs.map → chunk-73YDB6AT.cjs.map} +1 -1
  7. package/dist/{chunk-MPHDM2VZ.cjs → chunk-7TQNW2AM.cjs} +30 -30
  8. package/dist/{chunk-MPHDM2VZ.cjs.map → chunk-7TQNW2AM.cjs.map} +1 -1
  9. package/dist/{chunk-65CPJ4SX.cjs → chunk-C6ASEPZL.cjs} +30 -30
  10. package/dist/{chunk-65CPJ4SX.cjs.map → chunk-C6ASEPZL.cjs.map} +1 -1
  11. package/dist/{chunk-DRPECAXO.js → chunk-CPIX5AAR.js} +2 -2
  12. package/dist/{chunk-SRA2QYK6.js → chunk-E2AWBQEU.js} +4 -4
  13. package/dist/{chunk-SZXKKEUH.cjs → chunk-E6B4B7GM.cjs} +46 -30
  14. package/dist/chunk-E6B4B7GM.cjs.map +1 -0
  15. package/dist/{chunk-XG7DFRJL.js → chunk-FOOQFRJR.js} +3 -3
  16. package/dist/{chunk-A3PL6ZCF.js → chunk-IS4IZC7N.js} +6388 -5128
  17. package/dist/chunk-IS4IZC7N.js.map +1 -0
  18. package/dist/{chunk-ZGBXHK26.cjs → chunk-JMGSJHFP.cjs} +12 -12
  19. package/dist/{chunk-ZGBXHK26.cjs.map → chunk-JMGSJHFP.cjs.map} +1 -1
  20. package/dist/{chunk-SL3RGBPX.cjs → chunk-QNYH3WUU.cjs} +9 -9
  21. package/dist/{chunk-SL3RGBPX.cjs.map → chunk-QNYH3WUU.cjs.map} +1 -1
  22. package/dist/{chunk-24Q2WLIU.js → chunk-QYRV6MKX.js} +2 -2
  23. package/dist/{chunk-ZII7TNVA.js → chunk-SRCEVQYA.js} +3 -3
  24. package/dist/{chunk-Y3MXGCOW.js → chunk-YZDUOUMB.js} +46 -30
  25. package/dist/chunk-YZDUOUMB.js.map +1 -0
  26. package/dist/{chunk-7UZLRI7W.cjs → chunk-ZAGQXSAP.cjs} +3292 -2032
  27. package/dist/chunk-ZAGQXSAP.cjs.map +1 -0
  28. package/dist/components/chat/chat-archive-page.d.ts +25 -0
  29. package/dist/components/chat/chat-archive-page.d.ts.map +1 -0
  30. package/dist/components/chat/chat-composer.d.ts +29 -0
  31. package/dist/components/chat/chat-composer.d.ts.map +1 -0
  32. package/dist/components/chat/chat-header-icon-button.d.ts +14 -0
  33. package/dist/components/chat/chat-header-icon-button.d.ts.map +1 -0
  34. package/dist/components/chat/chat-panel-header.d.ts +32 -0
  35. package/dist/components/chat/chat-panel-header.d.ts.map +1 -0
  36. package/dist/components/chat/embeddable-chat.d.ts +18 -0
  37. package/dist/components/chat/embeddable-chat.d.ts.map +1 -1
  38. package/dist/components/chat/guide-mode-banner.d.ts +16 -0
  39. package/dist/components/chat/guide-mode-banner.d.ts.map +1 -0
  40. package/dist/components/chat/guide-welcome.d.ts +42 -0
  41. package/dist/components/chat/guide-welcome.d.ts.map +1 -0
  42. package/dist/components/chat/hooks/use-chat-dialog-manager.d.ts +58 -0
  43. package/dist/components/chat/hooks/use-chat-dialog-manager.d.ts.map +1 -0
  44. package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts +26 -1
  45. package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts.map +1 -1
  46. package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts.map +1 -1
  47. package/dist/components/chat/hooks/use-unified-chat.d.ts.map +1 -1
  48. package/dist/components/chat/index.cjs +29 -5
  49. package/dist/components/chat/index.cjs.map +1 -1
  50. package/dist/components/chat/index.d.ts +9 -0
  51. package/dist/components/chat/index.d.ts.map +1 -1
  52. package/dist/components/chat/index.js +28 -4
  53. package/dist/components/chat/mingo-chat-history.d.ts +37 -0
  54. package/dist/components/chat/mingo-chat-history.d.ts.map +1 -0
  55. package/dist/components/chat/mingo-chat-modals.d.ts +50 -0
  56. package/dist/components/chat/mingo-chat-modals.d.ts.map +1 -0
  57. package/dist/components/chat/mingo-onboarding-card.d.ts.map +1 -1
  58. package/dist/components/chat/mingo-welcome.d.ts +78 -0
  59. package/dist/components/chat/mingo-welcome.d.ts.map +1 -0
  60. package/dist/components/chat/types/unified-chat-state.types.d.ts +6 -0
  61. package/dist/components/chat/types/unified-chat-state.types.d.ts.map +1 -1
  62. package/dist/components/contact/index.cjs +6 -6
  63. package/dist/components/contact/index.js +5 -5
  64. package/dist/components/features/index.cjs +5 -5
  65. package/dist/components/features/index.js +4 -4
  66. package/dist/components/icons-v2-generated/brand-logos/fleet-mdm-logo-grey-icon.d.ts.map +1 -1
  67. package/dist/components/icons-v2-generated/brand-logos/fleet-mdm-logo-icon.d.ts.map +1 -1
  68. package/dist/components/icons-v2-generated/index.cjs +2 -2
  69. package/dist/components/icons-v2-generated/index.js +1 -1
  70. package/dist/components/index.cjs +128 -104
  71. package/dist/components/index.cjs.map +1 -1
  72. package/dist/components/index.js +31 -7
  73. package/dist/components/index.js.map +1 -1
  74. package/dist/components/layout/page-heading.d.ts +7 -6
  75. package/dist/components/layout/page-heading.d.ts.map +1 -1
  76. package/dist/components/navigation/app-layout-drawer.d.ts.map +1 -1
  77. package/dist/components/navigation/header-mingo-button.d.ts +4 -2
  78. package/dist/components/navigation/header-mingo-button.d.ts.map +1 -1
  79. package/dist/components/navigation/index.cjs +5 -5
  80. package/dist/components/navigation/index.js +4 -4
  81. package/dist/components/onboarding-guides/index.cjs +28 -28
  82. package/dist/components/onboarding-guides/index.js +6 -6
  83. package/dist/components/tickets/index.cjs +88 -88
  84. package/dist/components/tickets/index.js +7 -7
  85. package/dist/components/ui/dropdown-menu.d.ts.map +1 -1
  86. package/dist/components/ui/file-manager/index.cjs +50 -50
  87. package/dist/components/ui/file-manager/index.js +1 -1
  88. package/dist/components/ui/index.cjs +29 -5
  89. package/dist/components/ui/index.cjs.map +1 -1
  90. package/dist/components/ui/index.js +28 -4
  91. package/dist/components/ui/modal-v2.d.ts.map +1 -1
  92. package/dist/components/ui/more-actions-menu.d.ts +8 -1
  93. package/dist/components/ui/more-actions-menu.d.ts.map +1 -1
  94. package/dist/components/ui/portal-container.d.ts +21 -0
  95. package/dist/components/ui/portal-container.d.ts.map +1 -0
  96. package/dist/components/ui/tooltip.d.ts.map +1 -1
  97. package/dist/hooks/index.cjs +3 -3
  98. package/dist/hooks/index.js +2 -2
  99. package/dist/index.cjs +29 -5
  100. package/dist/index.cjs.map +1 -1
  101. package/dist/index.js +28 -4
  102. package/package.json +1 -1
  103. package/src/components/chat/chat-archive-page.tsx +93 -0
  104. package/src/components/chat/chat-composer.tsx +99 -0
  105. package/src/components/chat/chat-header-icon-button.tsx +36 -0
  106. package/src/components/chat/chat-panel-header.tsx +114 -0
  107. package/src/components/chat/embeddable-chat.tsx +388 -311
  108. package/src/components/chat/guide-mode-banner.tsx +75 -0
  109. package/src/components/chat/guide-welcome.tsx +199 -0
  110. package/src/components/chat/hooks/use-chat-dialog-manager.ts +227 -0
  111. package/src/components/chat/hooks/use-nats-chat-adapter.ts +85 -0
  112. package/src/components/chat/hooks/use-sse-chat-adapter.ts +8 -0
  113. package/src/components/chat/hooks/use-unified-chat.ts +12 -0
  114. package/src/components/chat/index.ts +9 -0
  115. package/src/components/chat/mingo-chat-history.tsx +308 -0
  116. package/src/components/chat/mingo-chat-modals.tsx +223 -0
  117. package/src/components/chat/mingo-onboarding-card.tsx +5 -8
  118. package/src/components/chat/mingo-welcome.tsx +396 -0
  119. package/src/components/chat/types/unified-chat-state.types.ts +8 -0
  120. package/src/components/icons-v2/brand-logos/fleet-mdm-logo-grey.svg +6 -6
  121. package/src/components/icons-v2/brand-logos/fleet-mdm-logo.svg +6 -6
  122. package/src/components/icons-v2-generated/brand-logos/fleet-mdm-logo-grey-icon.tsx +2 -22
  123. package/src/components/icons-v2-generated/brand-logos/fleet-mdm-logo-icon.tsx +22 -2
  124. package/src/components/layout/page-heading.tsx +13 -7
  125. package/src/components/navigation/app-header.tsx +12 -12
  126. package/src/components/navigation/app-layout-drawer.tsx +25 -15
  127. package/src/components/navigation/header-mingo-button.tsx +22 -7
  128. package/src/components/ui/dropdown-menu.tsx +9 -3
  129. package/src/components/ui/modal-v2.tsx +33 -3
  130. package/src/components/ui/more-actions-menu.tsx +15 -2
  131. package/src/components/ui/portal-container.tsx +28 -0
  132. package/src/components/ui/tooltip.tsx +9 -3
  133. package/src/stories/AppLayoutSidebar.stories.tsx +184 -0
  134. package/src/stories/EmbeddableChat.stories.tsx +114 -0
  135. package/src/stories/GuideWelcome.stories.tsx +124 -0
  136. package/src/stories/MingoChatModals.stories.tsx +82 -0
  137. package/src/stories/MingoWelcome.stories.tsx +119 -0
  138. package/dist/chunk-7UZLRI7W.cjs.map +0 -1
  139. package/dist/chunk-A3PL6ZCF.js.map +0 -1
  140. package/dist/chunk-SZXKKEUH.cjs.map +0 -1
  141. package/dist/chunk-Y3MXGCOW.js.map +0 -1
  142. /package/dist/{chunk-DRPECAXO.js.map → chunk-CPIX5AAR.js.map} +0 -0
  143. /package/dist/{chunk-SRA2QYK6.js.map → chunk-E2AWBQEU.js.map} +0 -0
  144. /package/dist/{chunk-XG7DFRJL.js.map → chunk-FOOQFRJR.js.map} +0 -0
  145. /package/dist/{chunk-24Q2WLIU.js.map → chunk-QYRV6MKX.js.map} +0 -0
  146. /package/dist/{chunk-ZII7TNVA.js.map → chunk-SRCEVQYA.js.map} +0 -0
@@ -0,0 +1,396 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import { cn } from '../../utils/cn'
5
+ import { MingoIcon } from '../icons'
6
+ import { Tag } from '../ui/tag'
7
+ import { XmarkIcon } from '../icons-v2-generated/signs-and-symbols/xmark-icon'
8
+ import {
9
+ Rocket01Icon,
10
+ BracketCurlyIcon,
11
+ SearchIcon,
12
+ LayersIcon,
13
+ CompassIcon,
14
+ Arrow01DownIcon,
15
+ } from '../icons-v2-generated'
16
+
17
+ // =============================================================================
18
+ // Types
19
+ // =============================================================================
20
+
21
+ /** A single capability cell in the 2-up feature grid. */
22
+ export interface MingoFeatureCard {
23
+ /** Stable React key. */
24
+ id: string
25
+ /** Leading 16×16 icon (monochrome `ods-text-secondary`). */
26
+ icon?: React.ReactNode
27
+ /** Caption text. Line breaks (`\n`) are honoured (`whitespace-pre-line`)
28
+ * so the two-line wrap stays fixed regardless of panel width. */
29
+ text: React.ReactNode
30
+ }
31
+
32
+ /** "New to OpenFrame?" one-time notification config. `null` hides the card. */
33
+ export interface MingoWelcomePromo {
34
+ title: React.ReactNode
35
+ description: React.ReactNode
36
+ }
37
+
38
+ /** Extra quick-action chip rendered after the built-in "Start Guide Chat". */
39
+ export interface MingoQuickAction {
40
+ /** Stable React key. */
41
+ id: string
42
+ label: string
43
+ icon?: React.ReactNode
44
+ /** `'primary'` = accent (yellow) chip, `'outline'` = bordered chip. */
45
+ variant?: 'primary' | 'outline'
46
+ onClick?: () => void
47
+ }
48
+
49
+ export interface MingoWelcomeProps {
50
+ /** First name woven into the greeting. Empty/undefined → no-name variant. */
51
+ userName?: string
52
+ /** Greeting heading. Defaults to `Hey{, name}, I'm Mingo`. */
53
+ title?: React.ReactNode
54
+ /** Greeting sub-line under the heading. */
55
+ subtitle?: React.ReactNode
56
+ /** 2-up capability grid. Defaults to the four OpenFrame highlights. */
57
+ featureCards?: ReadonlyArray<MingoFeatureCard>
58
+ /** One-time "New to OpenFrame?" notification below the grid. `null` hides
59
+ * it; omitting falls back to the OpenFrame default (only rendered when
60
+ * `onStartGuideChat` is wired, i.e. Guide mode exists to advertise). */
61
+ promo?: MingoWelcomePromo | null
62
+ /** Storage key used to remember the promo dismissal. */
63
+ promoStorageKey?: string
64
+ /** Where the dismissal is persisted. `'local'` (default) survives across
65
+ * sessions; `'session'` clears when the tab closes. */
66
+ promoStorage?: 'local' | 'session'
67
+ /** Extra quick-action chips appended after the "Start Guide Chat" chip. */
68
+ quickActions?: ReadonlyArray<MingoQuickAction>
69
+ /** Returning-user variation: the user already has chats. Hides the
70
+ * "New to OpenFrame?" notification entirely and renders the "Start Guide
71
+ * Chat" chip in the muted `outline` style instead of the accent yellow. */
72
+ hasExistingChats?: boolean
73
+ /** Returning-user main content — when provided (typically a
74
+ * `<MingoChatHistory>`), it replaces the greeting + feature grid and owns
75
+ * its own scroll region. The chips below stay pinned. */
76
+ dialogHistory?: React.ReactNode
77
+ /** When provided, renders the "Start Guide Chat" chip (the only wired
78
+ * action — switches the host chat to Guide mode) and enables the default
79
+ * promo notification. When omitted, both are suppressed. */
80
+ onStartGuideChat?: () => void
81
+ /** Appended to the root element. */
82
+ className?: string
83
+ }
84
+
85
+ // =============================================================================
86
+ // Defaults (OpenFrame copy — overridable per Core Rule: platform-agnostic kit)
87
+ // =============================================================================
88
+
89
+ const DEFAULT_SUBTITLE =
90
+ 'Ready to help with your technical tasks. What can I do for you?'
91
+
92
+ const DEFAULT_FEATURE_CARDS: ReadonlyArray<MingoFeatureCard> = [
93
+ {
94
+ id: 'answers',
95
+ icon: <Rocket01Icon size={16} />,
96
+ text: 'Get instant answers about\ndevices, tickets, and clients',
97
+ },
98
+ {
99
+ id: 'scripts',
100
+ icon: <BracketCurlyIcon size={16} />,
101
+ text: 'Run scripts and queries\nthrough natural language',
102
+ },
103
+ {
104
+ id: 'summarize',
105
+ icon: <SearchIcon size={16} />,
106
+ text: 'Summarize long ticket\nthreads or activity history',
107
+ },
108
+ {
109
+ id: 'delegate',
110
+ icon: <LayersIcon size={16} />,
111
+ text: 'Delegate tasks, let Mingo\nwork in the background',
112
+ },
113
+ ]
114
+
115
+ const DEFAULT_PROMO: MingoWelcomePromo = {
116
+ title: 'New to OpenFrame?',
117
+ description: 'Start a Guide Chat to learn how it works and how to set it up.',
118
+ }
119
+
120
+ const DEFAULT_PROMO_STORAGE_KEY = 'mingo-welcome:promo-dismissed'
121
+
122
+ // =============================================================================
123
+ // Component
124
+ // =============================================================================
125
+
126
+ /**
127
+ * MingoWelcome — Figma node `7532:222444`.
128
+ *
129
+ * Default (Mingo-mode) chat empty state: a vertically-centred greeting that
130
+ * grows to fill available height, then a pinned stack of a 2-up capability
131
+ * grid, an optional one-time "New to OpenFrame?" notification, and a
132
+ * quick-action chip row. The Guide-mode empty state keeps the slash-command
133
+ * onboarding list.
134
+ *
135
+ * Content is configurable (props) with OpenFrame defaults so the kit stays
136
+ * platform-agnostic. The "Start Guide Chat" chip is the only wired action —
137
+ * it switches the host chat to Guide mode via `onStartGuideChat`. The
138
+ * notification carries no action; its dismiss "X" persists to local/session
139
+ * storage so it shows only until the user closes it once.
140
+ */
141
+ export function MingoWelcome({
142
+ userName,
143
+ title,
144
+ subtitle = DEFAULT_SUBTITLE,
145
+ featureCards = DEFAULT_FEATURE_CARDS,
146
+ promo,
147
+ promoStorageKey = DEFAULT_PROMO_STORAGE_KEY,
148
+ promoStorage = 'local',
149
+ quickActions,
150
+ hasExistingChats = false,
151
+ dialogHistory,
152
+ onStartGuideChat,
153
+ className,
154
+ }: MingoWelcomeProps) {
155
+ const heading =
156
+ title ?? (userName ? `Hey ${userName}, I'm Mingo` : "Hey, I'm Mingo")
157
+
158
+ // `promo` omitted → fall back to the OpenFrame default, but only when guide
159
+ // mode exists to advertise (otherwise the notification points nowhere).
160
+ // Returning users (`hasExistingChats`) never see the onboarding notification.
161
+ const resolvedPromo = hasExistingChats
162
+ ? null
163
+ : promo === undefined
164
+ ? onStartGuideChat
165
+ ? DEFAULT_PROMO
166
+ : null
167
+ : promo
168
+
169
+ // One-time notification: hydrate the dismissal from storage after mount
170
+ // (SSR-safe — `window` is untouched on the server). Default to "not
171
+ // dismissed" so the first paint shows it, then hide if storage says so.
172
+ const [promoDismissed, setPromoDismissed] = React.useState(false)
173
+ React.useEffect(() => {
174
+ if (!resolvedPromo) return
175
+ try {
176
+ const store =
177
+ promoStorage === 'session' ? window.sessionStorage : window.localStorage
178
+ if (store.getItem(promoStorageKey) === '1') setPromoDismissed(true)
179
+ } catch {
180
+ // Storage can throw (private mode, blocked cookies) — treat as
181
+ // "not dismissed" and simply keep showing the notification.
182
+ }
183
+ }, [resolvedPromo, promoStorage, promoStorageKey])
184
+
185
+ const dismissPromo = React.useCallback(() => {
186
+ setPromoDismissed(true)
187
+ try {
188
+ const store =
189
+ promoStorage === 'session' ? window.sessionStorage : window.localStorage
190
+ store.setItem(promoStorageKey, '1')
191
+ } catch {
192
+ // Best-effort persistence; the in-memory state still hides it for
193
+ // the rest of this session.
194
+ }
195
+ }, [promoStorage, promoStorageKey])
196
+
197
+ // Scroll-fade affordances: show a 48px gradient at the top/bottom edge of the
198
+ // scroll region only while content is actually hidden in that direction.
199
+ const scrollRef = React.useRef<HTMLDivElement>(null)
200
+ const [scrollFade, setScrollFade] = React.useState({ top: false, bottom: false })
201
+ const updateScrollFade = React.useCallback(() => {
202
+ const el = scrollRef.current
203
+ if (!el) return
204
+ const top = el.scrollTop > 1
205
+ const bottom = el.scrollTop + el.clientHeight < el.scrollHeight - 1
206
+ setScrollFade((prev) =>
207
+ prev.top === top && prev.bottom === bottom ? prev : { top, bottom },
208
+ )
209
+ }, [])
210
+ React.useEffect(() => {
211
+ updateScrollFade()
212
+ const el = scrollRef.current
213
+ if (!el || typeof ResizeObserver === 'undefined') return
214
+ // Recompute when the panel resizes — a taller panel may remove the need
215
+ // to scroll entirely, a shorter one introduces it.
216
+ const ro = new ResizeObserver(updateScrollFade)
217
+ ro.observe(el)
218
+ return () => ro.disconnect()
219
+ }, [updateScrollFade])
220
+
221
+ const cellCount = featureCards.length
222
+ // Last row's first index — used to drop the bottom divider on the final row.
223
+ const lastRowStart = cellCount - ((cellCount % 2) || 2)
224
+
225
+ return (
226
+ <div
227
+ className={cn(
228
+ // `@container` makes the panel itself the responsive context (the
229
+ // drawer width ≠ viewport width) so the notification's down-arrow can
230
+ // appear only once the panel is genuinely wide.
231
+ '@container flex flex-1 min-h-0 flex-col gap-[var(--spacing-system-m)]',
232
+ className,
233
+ )}
234
+ >
235
+ {/* Returning users see their dialog history (its own scroll region) in
236
+ place of the greeting + grid. */}
237
+ {dialogHistory ?? (
238
+ <>
239
+ {/* Scrollable region — only the greeting + grid scroll; the notification
240
+ and chips below stay pinned so they're always visible above the
241
+ input. The wrapper is `relative` so the scroll-fade gradients can
242
+ overlay the top/bottom edges. */}
243
+ <div className="relative flex flex-1 min-h-0 flex-col">
244
+ <div
245
+ ref={scrollRef}
246
+ onScroll={updateScrollFade}
247
+ className="flex flex-1 min-h-0 flex-col gap-[var(--spacing-system-m)] overflow-y-auto"
248
+ >
249
+ {/* Greeting — grows to fill (`flex-1`) so it centres vertically,
250
+ keeping the grid anchored at the bottom of the scroll area. Default
251
+ `min-height: auto` (no `min-h-0`) stops it shrinking below its own
252
+ content, so the region scrolls instead of the greeting overlapping
253
+ the grid. Padding is modest so it never dominates the narrow panel. */}
254
+ <div className="flex flex-1 flex-col items-center justify-center gap-[var(--spacing-system-l)] px-[var(--spacing-system-l)] py-[var(--spacing-system-xxl)] text-center">
255
+ <MingoIcon
256
+ className="h-12 w-12"
257
+ color="white"
258
+ eyesColor="var(--ods-flamingo-cyan-base)"
259
+ cornerColor="var(--ods-flamingo-cyan-base)"
260
+ />
261
+ <div className="flex w-full flex-col gap-1">
262
+ <p className="text-h4 text-ods-text-primary">{heading}</p>
263
+ <p className="text-h6 text-ods-text-secondary">{subtitle}</p>
264
+ </div>
265
+ </div>
266
+
267
+ {/* 2-up capability grid. `shrink-0` keeps every cell at its natural
268
+ height so the bottom row is never clipped (the root scrolls instead).
269
+ Cells share the lighter `ods-card` surface; 1px `ods-border` dividers
270
+ (right on the left column, bottom on every row but the last) draw the
271
+ inner cross. Captions use explicit `\n` breaks (`whitespace-pre-line`)
272
+ so the two-line wrap is fixed and never reflows with panel width. */}
273
+ {cellCount > 0 && (
274
+ <div className="grid shrink-0 grid-cols-2 overflow-hidden rounded-md border border-ods-border bg-ods-card">
275
+ {featureCards.map((card, i) => (
276
+ <div
277
+ key={card.id}
278
+ className={cn(
279
+ 'flex flex-col items-center justify-center gap-[var(--spacing-system-m)] p-[var(--spacing-system-m)] text-center',
280
+ i % 2 === 0 && 'border-r border-ods-border',
281
+ i < lastRowStart && 'border-b border-ods-border',
282
+ )}
283
+ >
284
+ {card.icon ? (
285
+ <span className="flex size-4 shrink-0 items-center justify-center text-ods-text-primary">
286
+ {card.icon}
287
+ </span>
288
+ ) : null}
289
+ <p className="text-h4 text-ods-text-secondary whitespace-pre-line">
290
+ {card.text}
291
+ </p>
292
+ </div>
293
+ ))}
294
+ </div>
295
+ )}
296
+ </div>
297
+
298
+ {/* Top scroll-fade — visible only when content is hidden above. */}
299
+ <div
300
+ aria-hidden
301
+ className={cn(
302
+ 'pointer-events-none absolute inset-x-0 top-0 h-12 transition-opacity duration-150',
303
+ scrollFade.top ? 'opacity-100' : 'opacity-0',
304
+ )}
305
+ style={{
306
+ background:
307
+ 'linear-gradient(0deg, transparent 0%, var(--color-bg-card) 100%)',
308
+ }}
309
+ />
310
+ {/* Bottom scroll-fade — visible only when content is hidden below. */}
311
+ <div
312
+ aria-hidden
313
+ className={cn(
314
+ 'pointer-events-none absolute inset-x-0 bottom-0 h-12 transition-opacity duration-150',
315
+ scrollFade.bottom ? 'opacity-100' : 'opacity-0',
316
+ )}
317
+ style={{
318
+ background:
319
+ 'linear-gradient(180deg, transparent 0%, var(--color-bg-card) 100%)',
320
+ }}
321
+ />
322
+ </div>
323
+ </>
324
+ )}
325
+
326
+ {/* Pinned region — always visible above the input. */}
327
+ <div className="flex shrink-0 flex-col gap-[var(--spacing-system-m)]">
328
+ {/* "New to OpenFrame?" — a one-time informational notification (no
329
+ action). The leading down-arrow (Figma node 7532:317130) only shows
330
+ once the panel is wide (`@2xl`), mirroring the responsive design. The
331
+ dismiss "X" persists to local/session storage via `dismissPromo`, so
332
+ once closed it stays hidden. The actual Guide entry point is the
333
+ "Start Guide Chat" chip below. */}
334
+ {resolvedPromo && !promoDismissed && (
335
+ <div className="relative shrink-0 flex items-center gap-[var(--spacing-system-m)] rounded-md border border-ods-border bg-ods-bg p-[var(--spacing-system-m)] pr-[var(--spacing-system-xl)]">
336
+ <span className="hidden @2xl:flex size-4 shrink-0 items-center justify-center text-ods-text-primary">
337
+ <Arrow01DownIcon size={16} />
338
+ </span>
339
+ <div className="flex min-w-0 flex-col items-start gap-1">
340
+ <span className="text-h3 text-ods-text-primary">
341
+ {resolvedPromo.title}
342
+ </span>
343
+ <span className="text-h6 text-ods-text-secondary">
344
+ {resolvedPromo.description}
345
+ </span>
346
+ </div>
347
+ {/* Plain cross — no button chrome, just a clickable icon. */}
348
+ <button
349
+ type="button"
350
+ onClick={dismissPromo}
351
+ aria-label="Dismiss"
352
+ className="absolute right-2 top-2 text-ods-text-secondary transition-colors hover:text-ods-text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-ods-accent rounded-sm"
353
+ >
354
+ <XmarkIcon size={16} />
355
+ </button>
356
+ </div>
357
+ )}
358
+
359
+ {/* Quick-action chips. Only "Start Guide Chat" is wired by default; the
360
+ chip and any `quickActions` use the design-system <Tag>, wrapped in a
361
+ real <button> so each chip gets keyboard focus + a focus ring. */}
362
+ {(onStartGuideChat || (quickActions && quickActions.length > 0)) && (
363
+ <div className="flex shrink-0 flex-wrap items-center gap-1">
364
+ {onStartGuideChat && (
365
+ <button
366
+ type="button"
367
+ onClick={onStartGuideChat}
368
+ className="rounded-md focus:outline-none focus-visible:ring-2 focus-visible:ring-ods-accent"
369
+ >
370
+ <Tag
371
+ variant={hasExistingChats ? 'outline' : 'primary'}
372
+ icon={<CompassIcon size={16} />}
373
+ label="Start Guide Chat"
374
+ />
375
+ </button>
376
+ )}
377
+ {quickActions?.map((action) => (
378
+ <button
379
+ key={action.id}
380
+ type="button"
381
+ onClick={action.onClick}
382
+ className="rounded-md focus:outline-none focus-visible:ring-2 focus-visible:ring-ods-accent"
383
+ >
384
+ <Tag
385
+ variant={action.variant ?? 'outline'}
386
+ icon={action.icon}
387
+ label={action.label}
388
+ />
389
+ </button>
390
+ ))}
391
+ </div>
392
+ )}
393
+ </div>
394
+ </div>
395
+ )
396
+ }
@@ -285,6 +285,14 @@ export interface UnifiedChatState {
285
285
  * Mingo gates on the host-provided callback). */
286
286
  deleteDialog: (id: string) => Promise<void>
287
287
 
288
+ /** Rename a dialog. Optimistically updates the title in the local list.
289
+ * No-op when the adapter doesn't expose a rename callback. */
290
+ renameDialog: (id: string, title: string) => Promise<void>
291
+
292
+ /** Archive a dialog (removes it from the active list). No-op when the
293
+ * adapter doesn't expose an archive callback. */
294
+ archiveDialog: (id: string) => Promise<void>
295
+
288
296
  /** True while the dialog list is being fetched for the first time. */
289
297
  isDialogsLoading: boolean
290
298
 
@@ -1,8 +1,8 @@
1
1
  <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
- <path d="M4.51209 6.99996C5.89948 6.99996 7.02419 5.88068 7.02419 4.49998C7.02419 3.11928 5.89948 2 4.51209 2C3.1247 2 2 3.11928 2 4.49998C2 5.88068 3.1247 6.99996 4.51209 6.99996Z" fill="#2EC318"/>
3
- <path d="M12.0483 6.99996C13.4356 6.99996 14.5603 5.88068 14.5603 4.49998C14.5603 3.11928 13.4356 2 12.0483 2C10.6608 2 9.53613 3.11928 9.53613 4.49998C9.53613 5.88068 10.6608 6.99996 12.0483 6.99996Z" fill="#36A8DE"/>
4
- <path d="M19.5844 6.99996C20.9718 6.99996 22.0964 5.88068 22.0964 4.49998C22.0964 3.11928 20.9718 2 19.5844 2C18.197 2 17.0723 3.11928 17.0723 4.49998C17.0723 5.88068 18.197 6.99996 19.5844 6.99996Z" fill="#F9667D"/>
5
- <path d="M4.51209 14.5C5.89948 14.5 7.02419 13.3807 7.02419 12C7.02419 10.6193 5.89948 9.5 4.51209 9.5C3.1247 9.5 2 10.6193 2 12C2 13.3807 3.1247 14.5 4.51209 14.5Z" fill="#C561D7"/>
6
- <path d="M12.0483 14.5C13.4356 14.5 14.5603 13.3807 14.5603 12C14.5603 10.6193 13.4356 9.5 12.0483 9.5C10.6608 9.5 9.53613 10.6193 9.53613 12C9.53613 13.3807 10.6608 14.5 12.0483 14.5Z" fill="#FF9B57"/>
7
- <path d="M4.51209 22C5.89948 22 7.02419 20.8808 7.02419 19.5C7.02419 18.1194 5.89948 17.0001 4.51209 17.0001C3.1247 17.0001 2 18.1194 2 19.5C2 20.8808 3.1247 22 4.51209 22Z" fill="#00EBBC"/>
2
+ <path d="M4.51209 6.99996C5.89948 6.99996 7.02419 5.88068 7.02419 4.49998C7.02419 3.11928 5.89948 2 4.51209 2C3.1247 2 2 3.11928 2 4.49998C2 5.88068 3.1247 6.99996 4.51209 6.99996Z" fill="#888888"/>
3
+ <path d="M12.0483 6.99996C13.4356 6.99996 14.5603 5.88068 14.5603 4.49998C14.5603 3.11928 13.4356 2 12.0483 2C10.6608 2 9.53613 3.11928 9.53613 4.49998C9.53613 5.88068 10.6608 6.99996 12.0483 6.99996Z" fill="#888888"/>
4
+ <path d="M19.5844 6.99996C20.9718 6.99996 22.0964 5.88068 22.0964 4.49998C22.0964 3.11928 20.9718 2 19.5844 2C18.197 2 17.0723 3.11928 17.0723 4.49998C17.0723 5.88068 18.197 6.99996 19.5844 6.99996Z" fill="#888888"/>
5
+ <path d="M4.51209 14.5C5.89948 14.5 7.02419 13.3807 7.02419 12C7.02419 10.6193 5.89948 9.5 4.51209 9.5C3.1247 9.5 2 10.6193 2 12C2 13.3807 3.1247 14.5 4.51209 14.5Z" fill="#888888"/>
6
+ <path d="M12.0483 14.5C13.4356 14.5 14.5603 13.3807 14.5603 12C14.5603 10.6193 13.4356 9.5 12.0483 9.5C10.6608 9.5 9.53613 10.6193 9.53613 12C9.53613 13.3807 10.6608 14.5 12.0483 14.5Z" fill="#888888"/>
7
+ <path d="M4.51209 22C5.89948 22 7.02419 20.8808 7.02419 19.5C7.02419 18.1194 5.89948 17.0001 4.51209 17.0001C3.1247 17.0001 2 18.1194 2 19.5C2 20.8808 3.1247 22 4.51209 22Z" fill="#888888"/>
8
8
  </svg>
@@ -1,8 +1,8 @@
1
1
  <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
- <path d="M4.51209 6.99996C5.89948 6.99996 7.02419 5.88068 7.02419 4.49998C7.02419 3.11928 5.89948 2 4.51209 2C3.1247 2 2 3.11928 2 4.49998C2 5.88068 3.1247 6.99996 4.51209 6.99996Z" fill="#888888"/>
3
- <path d="M12.0483 6.99996C13.4356 6.99996 14.5603 5.88068 14.5603 4.49998C14.5603 3.11928 13.4356 2 12.0483 2C10.6608 2 9.53613 3.11928 9.53613 4.49998C9.53613 5.88068 10.6608 6.99996 12.0483 6.99996Z" fill="#888888"/>
4
- <path d="M19.5844 6.99996C20.9718 6.99996 22.0964 5.88068 22.0964 4.49998C22.0964 3.11928 20.9718 2 19.5844 2C18.197 2 17.0723 3.11928 17.0723 4.49998C17.0723 5.88068 18.197 6.99996 19.5844 6.99996Z" fill="#888888"/>
5
- <path d="M4.51209 14.5C5.89948 14.5 7.02419 13.3807 7.02419 12C7.02419 10.6193 5.89948 9.5 4.51209 9.5C3.1247 9.5 2 10.6193 2 12C2 13.3807 3.1247 14.5 4.51209 14.5Z" fill="#888888"/>
6
- <path d="M12.0483 14.5C13.4356 14.5 14.5603 13.3807 14.5603 12C14.5603 10.6193 13.4356 9.5 12.0483 9.5C10.6608 9.5 9.53613 10.6193 9.53613 12C9.53613 13.3807 10.6608 14.5 12.0483 14.5Z" fill="#888888"/>
7
- <path d="M4.51209 22C5.89948 22 7.02419 20.8808 7.02419 19.5C7.02419 18.1194 5.89948 17.0001 4.51209 17.0001C3.1247 17.0001 2 18.1194 2 19.5C2 20.8808 3.1247 22 4.51209 22Z" fill="#888888"/>
2
+ <path d="M4.51209 6.99996C5.89948 6.99996 7.02419 5.88068 7.02419 4.49998C7.02419 3.11928 5.89948 2 4.51209 2C3.1247 2 2 3.11928 2 4.49998C2 5.88068 3.1247 6.99996 4.51209 6.99996Z" fill="#2EC318"/>
3
+ <path d="M12.0483 6.99996C13.4356 6.99996 14.5603 5.88068 14.5603 4.49998C14.5603 3.11928 13.4356 2 12.0483 2C10.6608 2 9.53613 3.11928 9.53613 4.49998C9.53613 5.88068 10.6608 6.99996 12.0483 6.99996Z" fill="#36A8DE"/>
4
+ <path d="M19.5844 6.99996C20.9718 6.99996 22.0964 5.88068 22.0964 4.49998C22.0964 3.11928 20.9718 2 19.5844 2C18.197 2 17.0723 3.11928 17.0723 4.49998C17.0723 5.88068 18.197 6.99996 19.5844 6.99996Z" fill="#F9667D"/>
5
+ <path d="M4.51209 14.5C5.89948 14.5 7.02419 13.3807 7.02419 12C7.02419 10.6193 5.89948 9.5 4.51209 9.5C3.1247 9.5 2 10.6193 2 12C2 13.3807 3.1247 14.5 4.51209 14.5Z" fill="#C561D7"/>
6
+ <path d="M12.0483 14.5C13.4356 14.5 14.5603 13.3807 14.5603 12C14.5603 10.6193 13.4356 9.5 12.0483 9.5C10.6608 9.5 9.53613 10.6193 9.53613 12C9.53613 13.3807 10.6608 14.5 12.0483 14.5Z" fill="#FF9B57"/>
7
+ <path d="M4.51209 22C5.89948 22 7.02419 20.8808 7.02419 19.5C7.02419 18.1194 5.89948 17.0001 4.51209 17.0001C3.1247 17.0001 2 18.1194 2 19.5C2 20.8808 3.1247 22 4.51209 22Z" fill="#00EBBC"/>
8
8
  </svg>
@@ -22,28 +22,8 @@ export function FleetMdmLogoGreyIcon({
22
22
  {...props}
23
23
  >
24
24
  <path
25
- fill="#2EC318"
26
- d="M4.512 7a2.506 2.506 0 0 0 2.512-2.5C7.024 3.12 5.9 2 4.512 2A2.506 2.506 0 0 0 2 4.5C2 5.88 3.125 7 4.512 7"
27
- />
28
- <path
29
- fill="#36A8DE"
30
- d="M12.048 7a2.506 2.506 0 0 0 2.512-2.5c0-1.38-1.124-2.5-2.512-2.5a2.506 2.506 0 0 0-2.512 2.5c0 1.38 1.125 2.5 2.512 2.5"
31
- />
32
- <path
33
- fill="#F9667D"
34
- d="M19.584 7a2.506 2.506 0 0 0 2.512-2.5c0-1.38-1.124-2.5-2.512-2.5a2.506 2.506 0 0 0-2.512 2.5c0 1.38 1.125 2.5 2.512 2.5"
35
- />
36
- <path
37
- fill="#C561D7"
38
- d="M4.512 14.5A2.506 2.506 0 0 0 7.024 12c0-1.38-1.125-2.5-2.512-2.5A2.506 2.506 0 0 0 2 12c0 1.38 1.125 2.5 2.512 2.5"
39
- />
40
- <path
41
- fill="#FF9B57"
42
- d="M12.048 14.5A2.506 2.506 0 0 0 14.56 12c0-1.38-1.124-2.5-2.512-2.5A2.506 2.506 0 0 0 9.536 12c0 1.38 1.125 2.5 2.512 2.5"
43
- />
44
- <path
45
- fill="#00EBBC"
46
- d="M4.512 22a2.506 2.506 0 0 0 2.512-2.5c0-1.38-1.125-2.5-2.512-2.5A2.506 2.506 0 0 0 2 19.5C2 20.88 3.125 22 4.512 22"
25
+ fill={color}
26
+ d="M4.512 7a2.506 2.506 0 0 0 2.512-2.5C7.024 3.12 5.9 2 4.512 2A2.506 2.506 0 0 0 2 4.5C2 5.88 3.125 7 4.512 7M12.048 7a2.506 2.506 0 0 0 2.512-2.5c0-1.38-1.124-2.5-2.512-2.5a2.506 2.506 0 0 0-2.512 2.5c0 1.38 1.125 2.5 2.512 2.5M19.584 7a2.506 2.506 0 0 0 2.512-2.5c0-1.38-1.124-2.5-2.512-2.5a2.506 2.506 0 0 0-2.512 2.5c0 1.38 1.125 2.5 2.512 2.5M4.512 14.5A2.506 2.506 0 0 0 7.024 12c0-1.38-1.125-2.5-2.512-2.5A2.506 2.506 0 0 0 2 12c0 1.38 1.125 2.5 2.512 2.5M12.048 14.5A2.506 2.506 0 0 0 14.56 12c0-1.38-1.124-2.5-2.512-2.5A2.506 2.506 0 0 0 9.536 12c0 1.38 1.125 2.5 2.512 2.5M4.512 22a2.506 2.506 0 0 0 2.512-2.5c0-1.38-1.125-2.5-2.512-2.5A2.506 2.506 0 0 0 2 19.5C2 20.88 3.125 22 4.512 22"
47
27
  />
48
28
  </svg>
49
29
  );
@@ -22,8 +22,28 @@ export function FleetMdmLogoIcon({
22
22
  {...props}
23
23
  >
24
24
  <path
25
- fill={color}
26
- d="M4.512 7a2.506 2.506 0 0 0 2.512-2.5C7.024 3.12 5.9 2 4.512 2A2.506 2.506 0 0 0 2 4.5C2 5.88 3.125 7 4.512 7M12.048 7a2.506 2.506 0 0 0 2.512-2.5c0-1.38-1.124-2.5-2.512-2.5a2.506 2.506 0 0 0-2.512 2.5c0 1.38 1.125 2.5 2.512 2.5M19.584 7a2.506 2.506 0 0 0 2.512-2.5c0-1.38-1.124-2.5-2.512-2.5a2.506 2.506 0 0 0-2.512 2.5c0 1.38 1.125 2.5 2.512 2.5M4.512 14.5A2.506 2.506 0 0 0 7.024 12c0-1.38-1.125-2.5-2.512-2.5A2.506 2.506 0 0 0 2 12c0 1.38 1.125 2.5 2.512 2.5M12.048 14.5A2.506 2.506 0 0 0 14.56 12c0-1.38-1.124-2.5-2.512-2.5A2.506 2.506 0 0 0 9.536 12c0 1.38 1.125 2.5 2.512 2.5M4.512 22a2.506 2.506 0 0 0 2.512-2.5c0-1.38-1.125-2.5-2.512-2.5A2.506 2.506 0 0 0 2 19.5C2 20.88 3.125 22 4.512 22"
25
+ fill="#2EC318"
26
+ d="M4.512 7a2.506 2.506 0 0 0 2.512-2.5C7.024 3.12 5.9 2 4.512 2A2.506 2.506 0 0 0 2 4.5C2 5.88 3.125 7 4.512 7"
27
+ />
28
+ <path
29
+ fill="#36A8DE"
30
+ d="M12.048 7a2.506 2.506 0 0 0 2.512-2.5c0-1.38-1.124-2.5-2.512-2.5a2.506 2.506 0 0 0-2.512 2.5c0 1.38 1.125 2.5 2.512 2.5"
31
+ />
32
+ <path
33
+ fill="#F9667D"
34
+ d="M19.584 7a2.506 2.506 0 0 0 2.512-2.5c0-1.38-1.124-2.5-2.512-2.5a2.506 2.506 0 0 0-2.512 2.5c0 1.38 1.125 2.5 2.512 2.5"
35
+ />
36
+ <path
37
+ fill="#C561D7"
38
+ d="M4.512 14.5A2.506 2.506 0 0 0 7.024 12c0-1.38-1.125-2.5-2.512-2.5A2.506 2.506 0 0 0 2 12c0 1.38 1.125 2.5 2.512 2.5"
39
+ />
40
+ <path
41
+ fill="#FF9B57"
42
+ d="M12.048 14.5A2.506 2.506 0 0 0 14.56 12c0-1.38-1.124-2.5-2.512-2.5A2.506 2.506 0 0 0 9.536 12c0 1.38 1.125 2.5 2.512 2.5"
43
+ />
44
+ <path
45
+ fill="#00EBBC"
46
+ d="M4.512 22a2.506 2.506 0 0 0 2.512-2.5c0-1.38-1.125-2.5-2.512-2.5A2.506 2.506 0 0 0 2 19.5C2 20.88 3.125 22 4.512 22"
27
47
  />
28
48
  </svg>
29
49
  );
@@ -2,14 +2,15 @@ import type { ReactNode } from 'react'
2
2
 
3
3
  /**
4
4
  * THE page-title style — the ODS `text-h1` token (`--font-size-h1-title` =
5
- * 40 / 48 / 56px, Azeret Mono semibold) plus the standard heading tracking.
6
- * Mirrors the global `h1 { @apply text-h1 }` base, so every page heading is
7
- * the SAME size across the lib and every consuming app.
5
+ * 40 / 48 / 56px, Azeret Mono semibold), which already owns the responsive
6
+ * letter-spacing (-0.02em). Mirrors the global `h1 { @apply text-h1 }` base, so
7
+ * every page heading is the SAME size across the lib and every consuming app.
8
8
  *
9
- * Single source of truth — do NOT hardcode a px size for a page title; render
10
- * `<PageHeading>` (or, for the rare non-h1 caller, apply `PAGE_HEADING_CLASS`).
9
+ * Single source of truth — do NOT hardcode a px size or re-assert the token's
10
+ * tracking; render `<PageHeading>` (or, for the rare non-h1 caller, apply
11
+ * `PAGE_HEADING_CLASS`).
11
12
  */
12
- export const PAGE_HEADING_CLASS = 'text-h1 text-ods-text-primary tracking-[-1.12px]'
13
+ export const PAGE_HEADING_CLASS = 'text-h1 text-ods-text-primary'
13
14
 
14
15
  const DESCRIPTION_CLASS =
15
16
  "mt-6 max-w-[640px] font-['DM_Sans'] text-[16px] md:text-[18px] leading-[24px] md:leading-[28px] text-ods-text-secondary"
@@ -45,10 +46,15 @@ export function PageHeading({
45
46
  }: PageHeadingProps) {
46
47
  const headingClass = className ? `${PAGE_HEADING_CLASS} ${className}` : PAGE_HEADING_CLASS
47
48
  const descClass = descriptionClassName ? `${DESCRIPTION_CLASS} ${descriptionClassName}` : DESCRIPTION_CLASS
49
+ // `description` is a ReactNode, so `description={cond && '...'}` can pass a
50
+ // boolean `false` — exclude it (and empty string) so we never render an empty
51
+ // <p> that adds phantom vertical gap beneath the heading.
52
+ const hasDescription =
53
+ description != null && description !== '' && typeof description !== 'boolean'
48
54
  return (
49
55
  <>
50
56
  <Tag className={headingClass}>{children}</Tag>
51
- {description != null && description !== '' && <p className={descClass}>{description}</p>}
57
+ {hasDescription && <p className={descClass}>{description}</p>}
52
58
  </>
53
59
  )
54
60
  }
@@ -129,18 +129,6 @@ export const AppHeader = React.memo(function AppHeader({
129
129
  />
130
130
  )}
131
131
 
132
- {/* Mingo AI launcher — placed to the LEFT of notifications so the user
133
- menu cluster (notifications → avatar) stays anchored to the right. */}
134
- {showMingoAI && (
135
- <HeaderMingoButton
136
- onClick={onMingoAI}
137
- isActive={isMingoAIActive}
138
- iconOnly={!isMdUp}
139
- disabled={disabled || !onMingoAI}
140
- className={dimmedClass}
141
- />
142
- )}
143
-
144
132
  {/* Notifications button */}
145
133
  {showNotifications && (
146
134
  <NotificationsHeaderButton
@@ -216,6 +204,18 @@ export const AppHeader = React.memo(function AppHeader({
216
204
  </DropdownMenuContent>
217
205
  </DropdownMenu>
218
206
  )}
207
+
208
+ {/* Mingo AI launcher — anchored at the very end of the header. On mobile
209
+ it collapses to an icon-only affordance (no "Mingo AI" wordmark). */}
210
+ {showMingoAI && (
211
+ <HeaderMingoButton
212
+ onClick={onMingoAI}
213
+ isActive={isMingoAIActive}
214
+ iconOnly={!isMdUp}
215
+ disabled={disabled || !onMingoAI}
216
+ className={dimmedClass}
217
+ />
218
+ )}
219
219
  </header>
220
220
  )
221
221
  })