@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.
- package/dist/{chunk-F3FO2ZZZ.cjs → chunk-4PBV66HQ.cjs} +7 -7
- package/dist/{chunk-F3FO2ZZZ.cjs.map → chunk-4PBV66HQ.cjs.map} +1 -1
- package/dist/{chunk-EDW2NVRV.js → chunk-4WZOFD46.js} +37 -37
- package/dist/{chunk-EDW2NVRV.js.map → chunk-4WZOFD46.js.map} +1 -1
- package/dist/{chunk-YX3YQNC4.cjs → chunk-73YDB6AT.cjs} +13 -13
- package/dist/{chunk-YX3YQNC4.cjs.map → chunk-73YDB6AT.cjs.map} +1 -1
- package/dist/{chunk-MPHDM2VZ.cjs → chunk-7TQNW2AM.cjs} +30 -30
- package/dist/{chunk-MPHDM2VZ.cjs.map → chunk-7TQNW2AM.cjs.map} +1 -1
- package/dist/{chunk-65CPJ4SX.cjs → chunk-C6ASEPZL.cjs} +30 -30
- package/dist/{chunk-65CPJ4SX.cjs.map → chunk-C6ASEPZL.cjs.map} +1 -1
- package/dist/{chunk-DRPECAXO.js → chunk-CPIX5AAR.js} +2 -2
- package/dist/{chunk-SRA2QYK6.js → chunk-E2AWBQEU.js} +4 -4
- package/dist/{chunk-SZXKKEUH.cjs → chunk-E6B4B7GM.cjs} +46 -30
- package/dist/chunk-E6B4B7GM.cjs.map +1 -0
- package/dist/{chunk-XG7DFRJL.js → chunk-FOOQFRJR.js} +3 -3
- package/dist/{chunk-A3PL6ZCF.js → chunk-IS4IZC7N.js} +6388 -5128
- package/dist/chunk-IS4IZC7N.js.map +1 -0
- package/dist/{chunk-ZGBXHK26.cjs → chunk-JMGSJHFP.cjs} +12 -12
- package/dist/{chunk-ZGBXHK26.cjs.map → chunk-JMGSJHFP.cjs.map} +1 -1
- package/dist/{chunk-SL3RGBPX.cjs → chunk-QNYH3WUU.cjs} +9 -9
- package/dist/{chunk-SL3RGBPX.cjs.map → chunk-QNYH3WUU.cjs.map} +1 -1
- package/dist/{chunk-24Q2WLIU.js → chunk-QYRV6MKX.js} +2 -2
- package/dist/{chunk-ZII7TNVA.js → chunk-SRCEVQYA.js} +3 -3
- package/dist/{chunk-Y3MXGCOW.js → chunk-YZDUOUMB.js} +46 -30
- package/dist/chunk-YZDUOUMB.js.map +1 -0
- package/dist/{chunk-7UZLRI7W.cjs → chunk-ZAGQXSAP.cjs} +3292 -2032
- package/dist/chunk-ZAGQXSAP.cjs.map +1 -0
- package/dist/components/chat/chat-archive-page.d.ts +25 -0
- package/dist/components/chat/chat-archive-page.d.ts.map +1 -0
- package/dist/components/chat/chat-composer.d.ts +29 -0
- package/dist/components/chat/chat-composer.d.ts.map +1 -0
- package/dist/components/chat/chat-header-icon-button.d.ts +14 -0
- package/dist/components/chat/chat-header-icon-button.d.ts.map +1 -0
- package/dist/components/chat/chat-panel-header.d.ts +32 -0
- package/dist/components/chat/chat-panel-header.d.ts.map +1 -0
- package/dist/components/chat/embeddable-chat.d.ts +18 -0
- package/dist/components/chat/embeddable-chat.d.ts.map +1 -1
- package/dist/components/chat/guide-mode-banner.d.ts +16 -0
- package/dist/components/chat/guide-mode-banner.d.ts.map +1 -0
- package/dist/components/chat/guide-welcome.d.ts +42 -0
- package/dist/components/chat/guide-welcome.d.ts.map +1 -0
- package/dist/components/chat/hooks/use-chat-dialog-manager.d.ts +58 -0
- package/dist/components/chat/hooks/use-chat-dialog-manager.d.ts.map +1 -0
- package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts +26 -1
- package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts.map +1 -1
- package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts.map +1 -1
- package/dist/components/chat/hooks/use-unified-chat.d.ts.map +1 -1
- package/dist/components/chat/index.cjs +29 -5
- package/dist/components/chat/index.cjs.map +1 -1
- package/dist/components/chat/index.d.ts +9 -0
- package/dist/components/chat/index.d.ts.map +1 -1
- package/dist/components/chat/index.js +28 -4
- package/dist/components/chat/mingo-chat-history.d.ts +37 -0
- package/dist/components/chat/mingo-chat-history.d.ts.map +1 -0
- package/dist/components/chat/mingo-chat-modals.d.ts +50 -0
- package/dist/components/chat/mingo-chat-modals.d.ts.map +1 -0
- package/dist/components/chat/mingo-onboarding-card.d.ts.map +1 -1
- package/dist/components/chat/mingo-welcome.d.ts +78 -0
- package/dist/components/chat/mingo-welcome.d.ts.map +1 -0
- package/dist/components/chat/types/unified-chat-state.types.d.ts +6 -0
- package/dist/components/chat/types/unified-chat-state.types.d.ts.map +1 -1
- package/dist/components/contact/index.cjs +6 -6
- package/dist/components/contact/index.js +5 -5
- package/dist/components/features/index.cjs +5 -5
- package/dist/components/features/index.js +4 -4
- package/dist/components/icons-v2-generated/brand-logos/fleet-mdm-logo-grey-icon.d.ts.map +1 -1
- package/dist/components/icons-v2-generated/brand-logos/fleet-mdm-logo-icon.d.ts.map +1 -1
- package/dist/components/icons-v2-generated/index.cjs +2 -2
- package/dist/components/icons-v2-generated/index.js +1 -1
- package/dist/components/index.cjs +128 -104
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.js +31 -7
- package/dist/components/index.js.map +1 -1
- package/dist/components/layout/page-heading.d.ts +7 -6
- package/dist/components/layout/page-heading.d.ts.map +1 -1
- package/dist/components/navigation/app-layout-drawer.d.ts.map +1 -1
- package/dist/components/navigation/header-mingo-button.d.ts +4 -2
- package/dist/components/navigation/header-mingo-button.d.ts.map +1 -1
- package/dist/components/navigation/index.cjs +5 -5
- package/dist/components/navigation/index.js +4 -4
- package/dist/components/onboarding-guides/index.cjs +28 -28
- package/dist/components/onboarding-guides/index.js +6 -6
- package/dist/components/tickets/index.cjs +88 -88
- package/dist/components/tickets/index.js +7 -7
- package/dist/components/ui/dropdown-menu.d.ts.map +1 -1
- package/dist/components/ui/file-manager/index.cjs +50 -50
- package/dist/components/ui/file-manager/index.js +1 -1
- package/dist/components/ui/index.cjs +29 -5
- package/dist/components/ui/index.cjs.map +1 -1
- package/dist/components/ui/index.js +28 -4
- package/dist/components/ui/modal-v2.d.ts.map +1 -1
- package/dist/components/ui/more-actions-menu.d.ts +8 -1
- package/dist/components/ui/more-actions-menu.d.ts.map +1 -1
- package/dist/components/ui/portal-container.d.ts +21 -0
- package/dist/components/ui/portal-container.d.ts.map +1 -0
- package/dist/components/ui/tooltip.d.ts.map +1 -1
- package/dist/hooks/index.cjs +3 -3
- package/dist/hooks/index.js +2 -2
- package/dist/index.cjs +29 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +28 -4
- package/package.json +1 -1
- package/src/components/chat/chat-archive-page.tsx +93 -0
- package/src/components/chat/chat-composer.tsx +99 -0
- package/src/components/chat/chat-header-icon-button.tsx +36 -0
- package/src/components/chat/chat-panel-header.tsx +114 -0
- package/src/components/chat/embeddable-chat.tsx +388 -311
- package/src/components/chat/guide-mode-banner.tsx +75 -0
- package/src/components/chat/guide-welcome.tsx +199 -0
- package/src/components/chat/hooks/use-chat-dialog-manager.ts +227 -0
- package/src/components/chat/hooks/use-nats-chat-adapter.ts +85 -0
- package/src/components/chat/hooks/use-sse-chat-adapter.ts +8 -0
- package/src/components/chat/hooks/use-unified-chat.ts +12 -0
- package/src/components/chat/index.ts +9 -0
- package/src/components/chat/mingo-chat-history.tsx +308 -0
- package/src/components/chat/mingo-chat-modals.tsx +223 -0
- package/src/components/chat/mingo-onboarding-card.tsx +5 -8
- package/src/components/chat/mingo-welcome.tsx +396 -0
- package/src/components/chat/types/unified-chat-state.types.ts +8 -0
- package/src/components/icons-v2/brand-logos/fleet-mdm-logo-grey.svg +6 -6
- package/src/components/icons-v2/brand-logos/fleet-mdm-logo.svg +6 -6
- package/src/components/icons-v2-generated/brand-logos/fleet-mdm-logo-grey-icon.tsx +2 -22
- package/src/components/icons-v2-generated/brand-logos/fleet-mdm-logo-icon.tsx +22 -2
- package/src/components/layout/page-heading.tsx +13 -7
- package/src/components/navigation/app-header.tsx +12 -12
- package/src/components/navigation/app-layout-drawer.tsx +25 -15
- package/src/components/navigation/header-mingo-button.tsx +22 -7
- package/src/components/ui/dropdown-menu.tsx +9 -3
- package/src/components/ui/modal-v2.tsx +33 -3
- package/src/components/ui/more-actions-menu.tsx +15 -2
- package/src/components/ui/portal-container.tsx +28 -0
- package/src/components/ui/tooltip.tsx +9 -3
- package/src/stories/AppLayoutSidebar.stories.tsx +184 -0
- package/src/stories/EmbeddableChat.stories.tsx +114 -0
- package/src/stories/GuideWelcome.stories.tsx +124 -0
- package/src/stories/MingoChatModals.stories.tsx +82 -0
- package/src/stories/MingoWelcome.stories.tsx +119 -0
- package/dist/chunk-7UZLRI7W.cjs.map +0 -1
- package/dist/chunk-A3PL6ZCF.js.map +0 -1
- package/dist/chunk-SZXKKEUH.cjs.map +0 -1
- package/dist/chunk-Y3MXGCOW.js.map +0 -1
- /package/dist/{chunk-DRPECAXO.js.map → chunk-CPIX5AAR.js.map} +0 -0
- /package/dist/{chunk-SRA2QYK6.js.map → chunk-E2AWBQEU.js.map} +0 -0
- /package/dist/{chunk-XG7DFRJL.js.map → chunk-FOOQFRJR.js.map} +0 -0
- /package/dist/{chunk-24Q2WLIU.js.map → chunk-QYRV6MKX.js.map} +0 -0
- /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="#
|
|
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="#
|
|
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="#
|
|
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="#
|
|
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="#
|
|
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="#
|
|
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="#
|
|
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="#
|
|
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="#
|
|
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="#
|
|
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="#
|
|
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="#
|
|
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=
|
|
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=
|
|
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
|
|
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)
|
|
6
|
-
* Mirrors the global `h1 { @apply text-h1 }` base, so
|
|
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
|
|
10
|
-
* `<PageHeading>` (or, for the rare non-h1 caller, apply
|
|
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
|
|
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
|
-
{
|
|
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
|
})
|