@flamingo-stack/openframe-frontend-core 0.0.222 → 0.0.223
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-AIKPR6BE.js → chunk-4VHWSAQR.js} +6069 -5235
- package/dist/chunk-4VHWSAQR.js.map +1 -0
- package/dist/{chunk-GMNE7P7V.cjs → chunk-4WBVMIKQ.cjs} +26 -26
- package/dist/{chunk-GMNE7P7V.cjs.map → chunk-4WBVMIKQ.cjs.map} +1 -1
- package/dist/{chunk-QYRV6MKX.js → chunk-53EA4QPZ.js} +13 -7
- package/dist/chunk-53EA4QPZ.js.map +1 -0
- package/dist/{chunk-4JOWZLEL.cjs → chunk-7TKYFZ3L.cjs} +7 -7
- package/dist/{chunk-4JOWZLEL.cjs.map → chunk-7TKYFZ3L.cjs.map} +1 -1
- package/dist/{chunk-IZA5WF44.cjs → chunk-ABBU7QOM.cjs} +12 -12
- package/dist/{chunk-IZA5WF44.cjs.map → chunk-ABBU7QOM.cjs.map} +1 -1
- package/dist/{chunk-KM4KK2A6.cjs → chunk-ENDUMC7Z.cjs} +2558 -1724
- package/dist/chunk-ENDUMC7Z.cjs.map +1 -0
- package/dist/{chunk-CCSEXVI4.js → chunk-GYYGA24C.js} +2 -2
- package/dist/{chunk-RMOHKAHA.js → chunk-ISWWP2BH.js} +3 -3
- package/dist/{chunk-QNYH3WUU.cjs → chunk-JUWPBMQM.cjs} +13 -7
- package/dist/chunk-JUWPBMQM.cjs.map +1 -0
- package/dist/{chunk-A2SFHYVF.cjs → chunk-KXLTWANQ.cjs} +10 -10
- package/dist/{chunk-A2SFHYVF.cjs.map → chunk-KXLTWANQ.cjs.map} +1 -1
- package/dist/{chunk-ND24XSVT.js → chunk-TA6Z2AKD.js} +3 -3
- package/dist/chunk-TA6Z2AKD.js.map +1 -0
- package/dist/{chunk-HRIAZQ5D.js → chunk-WOKB6RJ7.js} +3 -3
- package/dist/components/chat/chat-archive-page.d.ts.map +1 -1
- package/dist/components/chat/chat-composer.d.ts.map +1 -1
- package/dist/components/chat/chat-message-enhanced.d.ts.map +1 -1
- package/dist/components/chat/chat-message-skeleton.d.ts +19 -4
- package/dist/components/chat/chat-message-skeleton.d.ts.map +1 -1
- package/dist/components/chat/chat-panel-header-mobile.d.ts +24 -0
- package/dist/components/chat/chat-panel-header-mobile.d.ts.map +1 -0
- package/dist/components/chat/chat-panel-header.d.ts +6 -0
- package/dist/components/chat/chat-panel-header.d.ts.map +1 -1
- package/dist/components/chat/chat-quick-action-row.d.ts +31 -0
- package/dist/components/chat/chat-quick-action-row.d.ts.map +1 -0
- package/dist/components/chat/embeddable-chat.d.ts +27 -0
- package/dist/components/chat/embeddable-chat.d.ts.map +1 -1
- package/dist/components/chat/entity-cards/dispatch.d.ts +14 -0
- package/dist/components/chat/entity-cards/dispatch.d.ts.map +1 -1
- package/dist/components/chat/entity-cards/github-activity-card.d.ts +10 -1
- package/dist/components/chat/entity-cards/github-activity-card.d.ts.map +1 -1
- package/dist/components/chat/guide-welcome.d.ts.map +1 -1
- package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts +9 -0
- package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts.map +1 -1
- package/dist/components/chat/hooks/use-slash-commands.d.ts +26 -0
- package/dist/components/chat/hooks/use-slash-commands.d.ts.map +1 -1
- package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts +12 -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 +12 -0
- package/dist/components/chat/hooks/use-unified-chat.d.ts.map +1 -1
- package/dist/components/chat/index.cjs +9 -3
- package/dist/components/chat/index.cjs.map +1 -1
- package/dist/components/chat/index.js +8 -2
- package/dist/components/chat/mingo-chat-history.d.ts +10 -0
- package/dist/components/chat/mingo-chat-history.d.ts.map +1 -1
- package/dist/components/chat/mingo-info-card.d.ts +63 -0
- package/dist/components/chat/mingo-info-card.d.ts.map +1 -0
- package/dist/components/chat/mingo-welcome.d.ts +14 -1
- package/dist/components/chat/mingo-welcome.d.ts.map +1 -1
- package/dist/components/chat/types/chat.types.d.ts +6 -0
- package/dist/components/chat/types/chat.types.d.ts.map +1 -1
- package/dist/components/chat/types/unified-chat-state.types.d.ts +30 -0
- package/dist/components/chat/types/unified-chat-state.types.d.ts.map +1 -1
- package/dist/components/chat/utils/process-historical-messages.d.ts.map +1 -1
- package/dist/components/contact/index.cjs +4 -4
- package/dist/components/contact/index.js +3 -3
- package/dist/components/features/index.cjs +3 -3
- package/dist/components/features/index.js +2 -2
- package/dist/components/index.cjs +110 -104
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.js +36 -30
- package/dist/components/index.js.map +1 -1
- package/dist/components/navigation/app-layout-drawer.d.ts +7 -0
- package/dist/components/navigation/app-layout-drawer.d.ts.map +1 -1
- package/dist/components/navigation/index.cjs +3 -3
- package/dist/components/navigation/index.js +2 -2
- package/dist/components/onboarding-guides/index.cjs +26 -26
- package/dist/components/onboarding-guides/index.js +4 -4
- package/dist/components/tickets/index.cjs +67 -67
- package/dist/components/tickets/index.js +5 -5
- package/dist/components/ui/actions-menu.d.ts +9 -0
- package/dist/components/ui/actions-menu.d.ts.map +1 -1
- package/dist/components/ui/dropdown-button.d.ts +2 -2
- package/dist/components/ui/dropdown-button.d.ts.map +1 -1
- package/dist/components/ui/index.cjs +9 -3
- package/dist/components/ui/index.cjs.map +1 -1
- package/dist/components/ui/index.js +8 -2
- package/dist/components/ui/modal-v2.d.ts.map +1 -1
- package/dist/components/ui/more-actions-menu.d.ts +13 -0
- package/dist/components/ui/more-actions-menu.d.ts.map +1 -1
- package/dist/components/ui/square-avatar.d.ts +4 -0
- package/dist/components/ui/square-avatar.d.ts.map +1 -1
- package/dist/components/ui/textarea.d.ts.map +1 -1
- package/dist/hooks/index.cjs +2 -2
- package/dist/hooks/index.js +1 -1
- package/dist/hooks/ui/use-auto-limit-tags.d.ts +7 -1
- package/dist/hooks/ui/use-auto-limit-tags.d.ts.map +1 -1
- package/dist/index.cjs +9 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +8 -2
- package/package.json +1 -1
- package/src/components/chat/chat-archive-page.tsx +32 -7
- package/src/components/chat/chat-composer.tsx +6 -2
- package/src/components/chat/chat-message-enhanced.tsx +24 -7
- package/src/components/chat/chat-message-list.tsx +1 -1
- package/src/components/chat/chat-message-skeleton.tsx +60 -60
- package/src/components/chat/chat-panel-header-mobile.tsx +94 -0
- package/src/components/chat/chat-panel-header.tsx +78 -50
- package/src/components/chat/chat-quick-action-row.tsx +146 -0
- package/src/components/chat/embeddable-chat.tsx +140 -45
- package/src/components/chat/entity-cards/dispatch.tsx +770 -232
- package/src/components/chat/entity-cards/github-activity-card.tsx +9 -3
- package/src/components/chat/guide-welcome.tsx +11 -8
- package/src/components/chat/hooks/use-nats-chat-adapter.ts +80 -7
- package/src/components/chat/hooks/use-slash-commands.ts +39 -0
- package/src/components/chat/hooks/use-sse-chat-adapter.ts +49 -45
- package/src/components/chat/hooks/use-unified-chat.ts +31 -8
- package/src/components/chat/mingo-chat-history.tsx +118 -36
- package/src/components/chat/mingo-info-card.tsx +214 -0
- package/src/components/chat/mingo-welcome.tsx +89 -36
- package/src/components/chat/types/chat.types.ts +4 -0
- package/src/components/chat/types/unified-chat-state.types.ts +35 -0
- package/src/components/chat/utils/process-historical-messages.ts +8 -0
- package/src/components/content-loading-container.tsx +1 -1
- package/src/components/empty-state.tsx +3 -3
- package/src/components/faq-accordion.tsx +2 -2
- package/src/components/features/changelog-manager.tsx +2 -2
- package/src/components/features/highlight-config-section.tsx +2 -2
- package/src/components/features/og-editor-preview.tsx +3 -3
- package/src/components/features/release-media-manager.tsx +2 -2
- package/src/components/loading/card-skeleton.tsx +3 -3
- package/src/components/loading/device-card-skeleton.tsx +1 -1
- package/src/components/loading/margin-report-skeleton.tsx +2 -2
- package/src/components/loading/organization-card-skeleton.tsx +1 -1
- package/src/components/loading/organization-icon-skeleton.tsx +1 -1
- package/src/components/loading/page-layout-skeleton.tsx +9 -9
- package/src/components/loading/users-grid-skeleton.tsx +2 -2
- package/src/components/logs-list.tsx +1 -1
- package/src/components/media-carousel.tsx +1 -1
- package/src/components/navigation/app-layout-drawer.tsx +70 -6
- package/src/components/open-source-features.tsx +2 -2
- package/src/components/ui/actions-menu.tsx +36 -13
- package/src/components/ui/allowed-domains-input.tsx +3 -3
- package/src/components/ui/cursor-pagination.tsx +1 -1
- package/src/components/ui/dropdown-button.tsx +6 -6
- package/src/components/ui/info-row.tsx +2 -2
- package/src/components/ui/modal-v2.tsx +8 -1
- package/src/components/ui/more-actions-menu.tsx +13 -0
- package/src/components/ui/square-avatar.tsx +6 -1
- package/src/components/ui/tags-input.tsx +1 -1
- package/src/components/ui/textarea.tsx +18 -4
- package/src/components/vendor-icon.tsx +1 -1
- package/src/components/vendor-tag.tsx +1 -1
- package/src/components/why-it-matters.tsx +1 -1
- package/src/hooks/ui/use-auto-limit-tags.ts +22 -8
- package/src/stories/ActionsMenu.stories.tsx +256 -0
- package/src/stories/AppLayoutSidebar.stories.tsx +6 -0
- package/src/stories/Board.stories.tsx +1 -1
- package/src/stories/ChatMingoInfoCards.stories.tsx +748 -0
- package/src/stories/ChatTicketList.stories.tsx +1 -1
- package/src/stories/DataTable.stories.tsx +17 -13
- package/src/stories/EmbeddableChat.stories.tsx +233 -1
- package/src/stories/MingoInfoCard.stories.tsx +193 -0
- package/src/stories/MingoOnboardingCard.stories.tsx +1 -1
- package/src/stories/Modal.stories.tsx +1 -0
- package/src/stories/NotificationDrawer.stories.tsx +14 -3
- package/src/stories/PageActions.stories.tsx +4 -4
- package/src/stories/PageContainer.stories.tsx +5 -5
- package/src/stories/PageLayout.stories.tsx +1 -1
- package/src/stories/TabSelector.stories.tsx +2 -2
- package/src/stories/Tag.stories.tsx +10 -5
- package/src/stories/TicketInfoSection.stories.tsx +23 -11
- package/src/stories/Toaster.stories.tsx +5 -5
- package/src/stories/__fixtures__/chat-card-decorator.tsx +44 -0
- package/dist/chunk-AIKPR6BE.js.map +0 -1
- package/dist/chunk-KM4KK2A6.cjs.map +0 -1
- package/dist/chunk-ND24XSVT.js.map +0 -1
- package/dist/chunk-QNYH3WUU.cjs.map +0 -1
- package/dist/chunk-QYRV6MKX.js.map +0 -1
- package/src/stories/.TicketDescriptionViewer.stories.md +0 -39
- package/src/stories/.TicketKnowledgeBaseList.stories.md +0 -36
- package/src/stories/TicketDescriptionViewer.stories.tsx +0 -70
- package/src/stories/TicketKnowledgeBaseList.stories.tsx +0 -51
- /package/dist/{chunk-CCSEXVI4.js.map → chunk-GYYGA24C.js.map} +0 -0
- /package/dist/{chunk-RMOHKAHA.js.map → chunk-ISWWP2BH.js.map} +0 -0
- /package/dist/{chunk-HRIAZQ5D.js.map → chunk-WOKB6RJ7.js.map} +0 -0
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import * as React from 'react'
|
|
4
4
|
import { cn } from '../../utils/cn'
|
|
5
|
-
import {
|
|
5
|
+
import { ActionsMenuDropdown, type ActionsMenuItem } from '../ui/actions-menu'
|
|
6
|
+
import { Button } from '../ui/button'
|
|
6
7
|
import { Ellipsis01Icon } from '../icons-v2-generated'
|
|
7
8
|
import type { DialogItem } from './types/component.types'
|
|
8
9
|
|
|
@@ -106,71 +107,91 @@ function MingoChatHistoryRow({
|
|
|
106
107
|
|
|
107
108
|
const menuItems = [
|
|
108
109
|
onRequestRename && {
|
|
110
|
+
id: 'rename',
|
|
109
111
|
label: 'Rename chat',
|
|
110
112
|
onClick: () => onRequestRename(dialog),
|
|
111
113
|
},
|
|
112
114
|
onRequestArchive && {
|
|
115
|
+
id: 'archive',
|
|
113
116
|
label: 'Archive chat',
|
|
114
117
|
onClick: () => onRequestArchive(dialog),
|
|
115
118
|
},
|
|
116
|
-
].filter(Boolean) as
|
|
119
|
+
].filter(Boolean) as ActionsMenuItem[]
|
|
117
120
|
|
|
121
|
+
// The item is the full-width row (inside the group's bordered box). Padding
|
|
122
|
+
// lives on the inner content button (badge + title) only — the `⋯` is a
|
|
123
|
+
// SIBLING of that padded content, so it sits at the item's right edge, inside
|
|
124
|
+
// the item but outside its padded content area.
|
|
118
125
|
return (
|
|
119
126
|
<div
|
|
120
|
-
role="button"
|
|
121
|
-
tabIndex={0}
|
|
122
|
-
onClick={() => onSelect?.(dialog.id)}
|
|
123
|
-
onKeyDown={(e) => {
|
|
124
|
-
if (e.key === 'Enter' || e.key === ' ') {
|
|
125
|
-
e.preventDefault()
|
|
126
|
-
onSelect?.(dialog.id)
|
|
127
|
-
}
|
|
128
|
-
}}
|
|
129
127
|
className={cn(
|
|
130
|
-
'group/row flex h-12 items-center
|
|
131
|
-
'bg-ods-bg
|
|
132
|
-
'cursor-pointer transition-colors hover:bg-ods-bg-hover focus:outline-none focus-visible:bg-ods-bg-hover',
|
|
128
|
+
'group/row flex h-12 items-center bg-ods-bg border-b border-ods-border last:border-b-0',
|
|
129
|
+
'transition-colors hover:bg-ods-bg-hover',
|
|
133
130
|
isActive && 'bg-ods-bg-hover',
|
|
134
131
|
)}
|
|
135
132
|
>
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
133
|
+
<div
|
|
134
|
+
role="button"
|
|
135
|
+
tabIndex={0}
|
|
136
|
+
onClick={() => onSelect?.(dialog.id)}
|
|
137
|
+
onKeyDown={(e) => {
|
|
138
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
139
|
+
e.preventDefault()
|
|
140
|
+
onSelect?.(dialog.id)
|
|
141
|
+
}
|
|
142
|
+
}}
|
|
143
|
+
className="flex min-w-0 flex-1 items-center gap-[var(--spacing-system-xsf)] p-[var(--spacing-system-s)] cursor-pointer focus:outline-none focus-visible:bg-ods-bg-hover"
|
|
144
144
|
>
|
|
145
|
-
{
|
|
146
|
-
|
|
145
|
+
{unread > 0 ? (
|
|
146
|
+
<span className="flex h-8 min-w-8 shrink-0 items-center justify-center rounded-md bg-ods-accent px-[var(--spacing-system-xsf)] text-h5 text-ods-text-on-accent">
|
|
147
|
+
{unread > 99 ? '99+' : unread}
|
|
148
|
+
</span>
|
|
149
|
+
) : null}
|
|
150
|
+
<span
|
|
151
|
+
className="min-w-0 flex-1 truncate text-h4 text-ods-text-primary"
|
|
152
|
+
title={title}
|
|
153
|
+
>
|
|
154
|
+
{title}
|
|
155
|
+
</span>
|
|
156
|
+
</div>
|
|
147
157
|
{hasMenu ? (
|
|
148
|
-
//
|
|
149
|
-
//
|
|
158
|
+
// Sibling of the padded content (not inside it): sits at the item's
|
|
159
|
+
// right edge, free of the content's left padding. Revealed on row
|
|
160
|
+
// hover/focus; kept visible while its menu is open. Hidden on mobile —
|
|
161
|
+
// it's a hover affordance (no hover on touch).
|
|
150
162
|
<span
|
|
151
163
|
onClick={(e) => e.stopPropagation()}
|
|
152
164
|
className={cn(
|
|
153
|
-
|
|
165
|
+
// `self-stretch` so the wrapper fills the row height — gives the
|
|
166
|
+
// button's `h-full` a definite parent to resolve against (under the
|
|
167
|
+
// row's `items-center` the wrapper would otherwise collapse to its
|
|
168
|
+
// content and `h-full` on the button would no-op).
|
|
169
|
+
'self-stretch shrink-0 transition-opacity max-md:hidden',
|
|
154
170
|
menuOpen
|
|
155
171
|
? 'opacity-100'
|
|
156
172
|
: 'opacity-0 group-hover/row:opacity-100 focus-within:opacity-100',
|
|
157
173
|
)}
|
|
158
174
|
>
|
|
159
|
-
<
|
|
160
|
-
|
|
161
|
-
|
|
175
|
+
<ActionsMenuDropdown
|
|
176
|
+
triggerAriaLabel="Chat actions"
|
|
177
|
+
groups={[{ items: menuItems }]}
|
|
162
178
|
open={menuOpen}
|
|
163
179
|
onOpenChange={setMenuOpen}
|
|
164
180
|
// Don't return focus (and its ring) to the `⋯` trigger on close.
|
|
165
181
|
onCloseAutoFocus={(e) => e.preventDefault()}
|
|
166
|
-
|
|
167
|
-
<
|
|
168
|
-
|
|
182
|
+
customTrigger={
|
|
183
|
+
<Button
|
|
184
|
+
variant="transparent"
|
|
185
|
+
size="icon"
|
|
169
186
|
aria-label="Chat actions"
|
|
170
|
-
|
|
187
|
+
// Square, grey icon → white on hover. `h-full` matches the row
|
|
188
|
+
// height exactly (no breakpoint poke from `size="icon"`'s
|
|
189
|
+
// responsive `md:h-12`); `w-9` keeps it compact; `p-0` drops the
|
|
190
|
+
// icon-size padding. tailwind-merge so these win over defaults.
|
|
191
|
+
className="h-full md:h-full rounded-none p-0 text-ods-text-secondary hover:text-ods-text-primary"
|
|
171
192
|
>
|
|
172
|
-
<Ellipsis01Icon
|
|
173
|
-
</
|
|
193
|
+
<Ellipsis01Icon />
|
|
194
|
+
</Button>
|
|
174
195
|
}
|
|
175
196
|
/>
|
|
176
197
|
</span>
|
|
@@ -179,6 +200,67 @@ function MingoChatHistoryRow({
|
|
|
179
200
|
)
|
|
180
201
|
}
|
|
181
202
|
|
|
203
|
+
// =============================================================================
|
|
204
|
+
// Skeleton
|
|
205
|
+
// =============================================================================
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Loading placeholder for the dialog history, shaped like the grouped row list
|
|
209
|
+
* above. Shown while the FIRST page of dialogs is in flight so the Mingo empty
|
|
210
|
+
* state doesn't flash the new-user greeting+grid before the list arrives (the
|
|
211
|
+
* two layouts are mutually exclusive — swapping between them mid-load reads as
|
|
212
|
+
* a flicker). Pure ODS tokens, `animate-pulse`.
|
|
213
|
+
*/
|
|
214
|
+
export function MingoChatHistorySkeleton({ className }: { className?: string }) {
|
|
215
|
+
// Mirror the real grouped list: a group label, then a bordered container of
|
|
216
|
+
// `h-12` rows. `withBadge` adds the leading unread-badge square on the first
|
|
217
|
+
// N rows; `widths` varies the title-bar length so the placeholder reads like
|
|
218
|
+
// a list of differing-length chat titles, not identical bars. `bg-ods-border`
|
|
219
|
+
// (not `bg-ods-bg-secondary`) so the placeholders are actually visible on the
|
|
220
|
+
// dark surface.
|
|
221
|
+
const groups: ReadonlyArray<{ withBadge: number; widths: ReadonlyArray<string> }> = [
|
|
222
|
+
{ withBadge: 2, widths: ['w-3/5', 'w-4/5', 'w-2/5', 'w-3/4'] },
|
|
223
|
+
{ withBadge: 0, widths: ['w-1/2', 'w-3/5', 'w-2/3'] },
|
|
224
|
+
]
|
|
225
|
+
return (
|
|
226
|
+
<div
|
|
227
|
+
aria-hidden
|
|
228
|
+
// `overflow-hidden` clips the placeholder rows to the flex-1 box (the real
|
|
229
|
+
// list scrolls; the skeleton just needs to stay within bounds). Without it
|
|
230
|
+
// the rows bleed past their allotment and render under the pinned composer
|
|
231
|
+
// — which has no surface of its own — so the input appears to overlap them.
|
|
232
|
+
className={cn(
|
|
233
|
+
'flex flex-1 min-h-0 flex-col gap-[var(--spacing-system-m)] overflow-hidden',
|
|
234
|
+
className,
|
|
235
|
+
)}
|
|
236
|
+
>
|
|
237
|
+
{groups.map((group, g) => (
|
|
238
|
+
<div key={g} className="flex shrink-0 flex-col gap-[var(--spacing-system-xxs)]">
|
|
239
|
+
{/* Group label (Today / Yesterday). */}
|
|
240
|
+
<div className="h-3 w-16 animate-pulse rounded bg-ods-border" />
|
|
241
|
+
<div className="overflow-hidden rounded-md border border-ods-border">
|
|
242
|
+
{group.widths.map((w, i) => (
|
|
243
|
+
<div
|
|
244
|
+
key={i}
|
|
245
|
+
// Match the real row surface: `bg-ods-bg` (#161616) sits darker
|
|
246
|
+
// than the `bg-ods-card` (#212121) panel, which is what makes
|
|
247
|
+
// the `bg-ods-border` (#3a3a3a) placeholders read clearly. On
|
|
248
|
+
// the bare card they'd be muddy (too close in value).
|
|
249
|
+
className="flex h-12 items-center gap-[var(--spacing-system-xsf)] bg-ods-bg border-b border-ods-border px-[var(--spacing-system-s)] last:border-b-0"
|
|
250
|
+
>
|
|
251
|
+
{i < group.withBadge && (
|
|
252
|
+
<div className="h-8 w-8 shrink-0 animate-pulse rounded-md bg-ods-border" />
|
|
253
|
+
)}
|
|
254
|
+
<div className={cn('h-4 animate-pulse rounded bg-ods-border', w)} />
|
|
255
|
+
</div>
|
|
256
|
+
))}
|
|
257
|
+
</div>
|
|
258
|
+
</div>
|
|
259
|
+
))}
|
|
260
|
+
</div>
|
|
261
|
+
)
|
|
262
|
+
}
|
|
263
|
+
|
|
182
264
|
// =============================================================================
|
|
183
265
|
// Component
|
|
184
266
|
// =============================================================================
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
import { cn } from '../../utils/cn'
|
|
5
|
+
import { tagVariants, type TagProps } from '../ui/tag'
|
|
6
|
+
import { ActionsMenuDropdown, type ActionsMenuGroup } from '../ui/actions-menu'
|
|
7
|
+
import { Ellipsis01Icon } from '../icons-v2-generated'
|
|
8
|
+
import { getFirstLastInitials } from '../../utils/format'
|
|
9
|
+
|
|
10
|
+
// =============================================================================
|
|
11
|
+
// Types
|
|
12
|
+
// =============================================================================
|
|
13
|
+
|
|
14
|
+
/** A coloured status pill rendered to the right of the title/description. */
|
|
15
|
+
export interface MingoInfoCardStatus {
|
|
16
|
+
/** Pill text — rendered uppercase via the `success`/`warning`/… Tag palette. */
|
|
17
|
+
label: React.ReactNode
|
|
18
|
+
/** Tag colour. Defaults to `success` (green) to match the Figma sample. */
|
|
19
|
+
variant?: TagProps['variant']
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface MingoInfoCardProps {
|
|
23
|
+
/** Card title — DM Sans Medium 18px (`text-h4`), white. Truncates on overflow. */
|
|
24
|
+
title: React.ReactNode
|
|
25
|
+
/** Optional secondary line below the title — 14px (`text-h6`), grey. Truncates. */
|
|
26
|
+
description?: React.ReactNode
|
|
27
|
+
/**
|
|
28
|
+
* Leading 40px avatar image (`img="image"` variant in Figma). Takes
|
|
29
|
+
* precedence over `icon` when both are supplied. Falls back to initials
|
|
30
|
+
* derived from `imageAlt` if the image fails to load.
|
|
31
|
+
*/
|
|
32
|
+
imageSrc?: string
|
|
33
|
+
/** Alt text / initials fallback for `imageSrc`. */
|
|
34
|
+
imageAlt?: string
|
|
35
|
+
/**
|
|
36
|
+
* Leading 24px glyph rendered inside a 40px box (`img="icon"` variant).
|
|
37
|
+
* Ignored when `imageSrc` is set. Pass an `icons-v2-generated` icon — it
|
|
38
|
+
* inherits `currentColor` from the box's `text-ods-text-primary`.
|
|
39
|
+
*/
|
|
40
|
+
icon?: React.ReactNode
|
|
41
|
+
/** Optional status pill (e.g. `{ label: 'Online' }`). */
|
|
42
|
+
status?: MingoInfoCardStatus
|
|
43
|
+
/**
|
|
44
|
+
* Overflow-menu groups. When non-empty, a trailing "⋯" button opens an
|
|
45
|
+
* `ActionsMenuDropdown`. Omit to hide the button entirely.
|
|
46
|
+
*/
|
|
47
|
+
menuGroups?: ReadonlyArray<ActionsMenuGroup>
|
|
48
|
+
/** Aria-label for the "⋯" trigger. Defaults to `"More actions"`. */
|
|
49
|
+
menuAriaLabel?: string
|
|
50
|
+
/**
|
|
51
|
+
* Renders the main content region as an `<a>` (for navigation) instead of a
|
|
52
|
+
* `<button>`. Takes precedence over `onClick`. The "⋯" button stays a sibling
|
|
53
|
+
* of the anchor — not nested inside it — so the markup stays valid.
|
|
54
|
+
*/
|
|
55
|
+
anchorProps?: MingoInfoCardAnchorProps
|
|
56
|
+
/**
|
|
57
|
+
* Click handler for the main content region. When set (and `anchorProps` is
|
|
58
|
+
* not), the title/description region becomes a `<button>` with hover/focus
|
|
59
|
+
* affordances. The "⋯" button keeps its own independent click target.
|
|
60
|
+
*/
|
|
61
|
+
onClick?: () => void
|
|
62
|
+
/** Appended to the root element. */
|
|
63
|
+
className?: string
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Anchor-prop bundle for the content region — mirrors the `*AnchorProps`
|
|
67
|
+
* shape the chat entity-cards pass (`{ href, target?, rel?, onClick? }`). */
|
|
68
|
+
export interface MingoInfoCardAnchorProps {
|
|
69
|
+
href: string
|
|
70
|
+
target?: '_blank'
|
|
71
|
+
rel?: string
|
|
72
|
+
onClick?: (e: React.MouseEvent<HTMLAnchorElement>) => void
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// =============================================================================
|
|
76
|
+
// Span-only markup
|
|
77
|
+
//
|
|
78
|
+
// This card renders inline inside chat message paragraphs (the `[card://]`
|
|
79
|
+
// marker is replaced in-place, landing the card inside a `<p>`). To stay
|
|
80
|
+
// valid there, EVERY element is phrasing content — `<span>` / `<a>` / `<img>`
|
|
81
|
+
// / `<button>` only, never `<div>`. Layout comes from `display` utilities, so
|
|
82
|
+
// the visual is identical to a block card.
|
|
83
|
+
// =============================================================================
|
|
84
|
+
|
|
85
|
+
/** 40px leading media — avatar image (with initials fallback) or boxed glyph. */
|
|
86
|
+
function CardMedia({
|
|
87
|
+
imageSrc,
|
|
88
|
+
imageAlt,
|
|
89
|
+
icon,
|
|
90
|
+
}: Pick<MingoInfoCardProps, 'imageSrc' | 'imageAlt' | 'icon'>) {
|
|
91
|
+
if (imageSrc) {
|
|
92
|
+
return (
|
|
93
|
+
<span className="relative block size-10 shrink-0">
|
|
94
|
+
{/* Initials fallback sits BEHIND the image. The image is the framed
|
|
95
|
+
element itself — `rounded-sm` + `border` live on the `<img>` so the
|
|
96
|
+
picture and its 1px frame share one radius (no corner gap from
|
|
97
|
+
clipping a separate absolutely-positioned child). On a 404 the
|
|
98
|
+
image's `onError` hides it and the initials show through. */}
|
|
99
|
+
<span className="absolute inset-0 flex items-center justify-center rounded-sm border border-ods-border bg-ods-bg text-xs font-medium text-ods-text-primary">
|
|
100
|
+
{getFirstLastInitials(imageAlt) || '?'}
|
|
101
|
+
</span>
|
|
102
|
+
<img
|
|
103
|
+
src={imageSrc}
|
|
104
|
+
alt={imageAlt || ''}
|
|
105
|
+
className="relative block size-10 rounded-sm border border-ods-border object-cover"
|
|
106
|
+
onError={(e) => {
|
|
107
|
+
e.currentTarget.style.display = 'none'
|
|
108
|
+
}}
|
|
109
|
+
/>
|
|
110
|
+
</span>
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
if (icon) {
|
|
114
|
+
return (
|
|
115
|
+
<span className="flex size-10 shrink-0 items-center justify-center overflow-hidden rounded-md border border-ods-border bg-ods-bg text-ods-text-secondary">
|
|
116
|
+
<span className="flex size-6 items-center justify-center">{icon}</span>
|
|
117
|
+
</span>
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
return null
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function MingoInfoCard({
|
|
124
|
+
title,
|
|
125
|
+
description,
|
|
126
|
+
imageSrc,
|
|
127
|
+
imageAlt,
|
|
128
|
+
icon,
|
|
129
|
+
status,
|
|
130
|
+
menuGroups,
|
|
131
|
+
menuAriaLabel = 'More actions',
|
|
132
|
+
anchorProps,
|
|
133
|
+
onClick,
|
|
134
|
+
className,
|
|
135
|
+
}: MingoInfoCardProps) {
|
|
136
|
+
const hasMedia = !!imageSrc || !!icon
|
|
137
|
+
const hasMenu = !!menuGroups && menuGroups.length > 0
|
|
138
|
+
const isAnchor = !!anchorProps
|
|
139
|
+
const isInteractive = isAnchor || !!onClick
|
|
140
|
+
|
|
141
|
+
// The content region: leading media, text column, status pill. Owns the
|
|
142
|
+
// `border-r` divider that sits before the "⋯" button.
|
|
143
|
+
const content = (
|
|
144
|
+
<>
|
|
145
|
+
{hasMedia && (
|
|
146
|
+
<CardMedia imageSrc={imageSrc} imageAlt={imageAlt} icon={icon} />
|
|
147
|
+
)}
|
|
148
|
+
<span className="flex min-w-0 flex-1 flex-col justify-center">
|
|
149
|
+
<span className="truncate text-h4 font-medium text-ods-text-primary">
|
|
150
|
+
{title}
|
|
151
|
+
</span>
|
|
152
|
+
{description ? (
|
|
153
|
+
<span className="truncate text-h6 text-ods-text-secondary">
|
|
154
|
+
{description}
|
|
155
|
+
</span>
|
|
156
|
+
) : null}
|
|
157
|
+
</span>
|
|
158
|
+
{status ? (
|
|
159
|
+
// Reuse the shared Tag palette (single source of truth for colours)
|
|
160
|
+
// without rendering Tag's `<div>` — span-only for `<p>` validity.
|
|
161
|
+
<span className={cn(tagVariants({ variant: status.variant ?? 'success' }), 'shrink-0')}>
|
|
162
|
+
<span className="truncate uppercase tracking-[-0.28px]">{status.label}</span>
|
|
163
|
+
</span>
|
|
164
|
+
) : null}
|
|
165
|
+
</>
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
const contentClass = cn(
|
|
169
|
+
'flex min-w-0 flex-1 items-center gap-[var(--spacing-system-s)] overflow-hidden p-[var(--spacing-system-s)] text-left',
|
|
170
|
+
hasMenu && 'border-r border-ods-border',
|
|
171
|
+
isInteractive &&
|
|
172
|
+
'transition-colors hover:bg-ods-bg-hover active:bg-ods-bg-active focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ods-accent cursor-pointer',
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
return (
|
|
176
|
+
<span
|
|
177
|
+
className={cn(
|
|
178
|
+
'flex w-full items-center overflow-hidden rounded-md border border-ods-border bg-ods-card',
|
|
179
|
+
className,
|
|
180
|
+
)}
|
|
181
|
+
>
|
|
182
|
+
{isAnchor ? (
|
|
183
|
+
<a {...anchorProps} className={cn(contentClass, 'no-underline')}>
|
|
184
|
+
{content}
|
|
185
|
+
</a>
|
|
186
|
+
) : onClick ? (
|
|
187
|
+
<button type="button" onClick={onClick} className={contentClass}>
|
|
188
|
+
{content}
|
|
189
|
+
</button>
|
|
190
|
+
) : (
|
|
191
|
+
<span className={contentClass}>{content}</span>
|
|
192
|
+
)}
|
|
193
|
+
|
|
194
|
+
{hasMenu && (
|
|
195
|
+
<ActionsMenuDropdown
|
|
196
|
+
groups={menuGroups as ActionsMenuGroup[]}
|
|
197
|
+
triggerAriaLabel={menuAriaLabel}
|
|
198
|
+
onCloseAutoFocus={(e) => e.preventDefault()}
|
|
199
|
+
customTrigger={
|
|
200
|
+
<button
|
|
201
|
+
type="button"
|
|
202
|
+
aria-label={menuAriaLabel}
|
|
203
|
+
// Press feedback mirrors `Button` (transparent variant):
|
|
204
|
+
// `hover:bg-ods-bg-hover` → `active:bg-ods-bg-active`.
|
|
205
|
+
className="flex w-[56px] md:w-[68px] shrink-0 items-center justify-center self-stretch text-ods-text-secondary transition-colors hover:bg-ods-bg-hover hover:text-ods-text-primary active:bg-ods-bg-active active:text-ods-text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ods-accent"
|
|
206
|
+
>
|
|
207
|
+
<Ellipsis01Icon size={24} />
|
|
208
|
+
</button>
|
|
209
|
+
}
|
|
210
|
+
/>
|
|
211
|
+
)}
|
|
212
|
+
</span>
|
|
213
|
+
)
|
|
214
|
+
}
|
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
import * as React from 'react'
|
|
4
4
|
import { cn } from '../../utils/cn'
|
|
5
5
|
import { MingoIcon } from '../icons'
|
|
6
|
+
import { MingoChatHistorySkeleton } from './mingo-chat-history'
|
|
7
|
+
import { ChatQuickActionRow } from './chat-quick-action-row'
|
|
6
8
|
import { Tag } from '../ui/tag'
|
|
9
|
+
import { Button } from '../ui/button'
|
|
7
10
|
import { XmarkIcon } from '../icons-v2-generated/signs-and-symbols/xmark-icon'
|
|
8
11
|
import {
|
|
9
12
|
Rocket01Icon,
|
|
@@ -12,6 +15,8 @@ import {
|
|
|
12
15
|
LayersIcon,
|
|
13
16
|
CompassIcon,
|
|
14
17
|
Arrow01DownIcon,
|
|
18
|
+
AlertCircleIcon,
|
|
19
|
+
Refresh01RightIcon,
|
|
15
20
|
} from '../icons-v2-generated'
|
|
16
21
|
|
|
17
22
|
// =============================================================================
|
|
@@ -74,6 +79,19 @@ export interface MingoWelcomeProps {
|
|
|
74
79
|
* `<MingoChatHistory>`), it replaces the greeting + feature grid and owns
|
|
75
80
|
* its own scroll region. The chips below stay pinned. */
|
|
76
81
|
dialogHistory?: React.ReactNode
|
|
82
|
+
/** True while the FIRST page of dialogs is still loading and we don't yet
|
|
83
|
+
* know if the user is new or returning. Renders a history skeleton in place
|
|
84
|
+
* of both the greeting+grid and the history, so the empty state doesn't
|
|
85
|
+
* flash the new-user layout before the list arrives. Ignored once
|
|
86
|
+
* `dialogHistory` is provided. */
|
|
87
|
+
isLoadingHistory?: boolean
|
|
88
|
+
/** The dialog-list load FAILED (e.g. backend down) and there's nothing
|
|
89
|
+
* cached. Renders an error + retry block in place of the new-user empty
|
|
90
|
+
* state (which would otherwise misleadingly advertise Guide). Takes
|
|
91
|
+
* priority over `isLoadingHistory`. Ignored once `dialogHistory` is set. */
|
|
92
|
+
loadError?: boolean
|
|
93
|
+
/** Retry handler for the `loadError` state. */
|
|
94
|
+
onRetry?: () => void
|
|
77
95
|
/** When provided, renders the "Start Guide Chat" chip (the only wired
|
|
78
96
|
* action — switches the host chat to Guide mode) and enables the default
|
|
79
97
|
* promo notification. When omitted, both are suppressed. */
|
|
@@ -149,6 +167,9 @@ export function MingoWelcome({
|
|
|
149
167
|
quickActions,
|
|
150
168
|
hasExistingChats = false,
|
|
151
169
|
dialogHistory,
|
|
170
|
+
isLoadingHistory = false,
|
|
171
|
+
loadError = false,
|
|
172
|
+
onRetry,
|
|
152
173
|
onStartGuideChat,
|
|
153
174
|
className,
|
|
154
175
|
}: MingoWelcomeProps) {
|
|
@@ -222,6 +243,14 @@ export function MingoWelcome({
|
|
|
222
243
|
// Last row's first index — used to drop the bottom divider on the final row.
|
|
223
244
|
const lastRowStart = cellCount - ((cellCount % 2) || 2)
|
|
224
245
|
|
|
246
|
+
// While we don't yet know whether the user is new or returning (first page
|
|
247
|
+
// loading, or it errored with nothing cached), suppress the pinned region —
|
|
248
|
+
// the "New to OpenFrame?" promo + quick-action chips. Showing them over the
|
|
249
|
+
// history skeleton both clutters the loading frame and pre-judges the user as
|
|
250
|
+
// new. They return once a resolved state (history, or the new-user greeting)
|
|
251
|
+
// renders. `dialogHistory` always wins — a returning user keeps the chips.
|
|
252
|
+
const showPinnedRegion = !!dialogHistory || (!isLoadingHistory && !loadError)
|
|
253
|
+
|
|
225
254
|
return (
|
|
226
255
|
<div
|
|
227
256
|
className={cn(
|
|
@@ -233,8 +262,33 @@ export function MingoWelcome({
|
|
|
233
262
|
)}
|
|
234
263
|
>
|
|
235
264
|
{/* Returning users see their dialog history (its own scroll region) in
|
|
236
|
-
place of the greeting + grid.
|
|
237
|
-
|
|
265
|
+
place of the greeting + grid. Precedence: load error (retry) → first-
|
|
266
|
+
page loading (skeleton) → new-user greeting+grid. The error branch
|
|
267
|
+
stops a failed fetch from masquerading as "no chats" (which would
|
|
268
|
+
otherwise show the Guide promo). */}
|
|
269
|
+
{dialogHistory ?? (loadError ? (
|
|
270
|
+
<div className="flex flex-1 min-h-0 flex-col items-center justify-center gap-[var(--spacing-system-m)] text-center">
|
|
271
|
+
<AlertCircleIcon className="h-8 w-8 text-ods-text-secondary shrink-0" />
|
|
272
|
+
<div className="flex flex-col gap-[var(--spacing-system-xxs)]">
|
|
273
|
+
<p className="text-h4 text-ods-text-primary">Couldn’t load your chats</p>
|
|
274
|
+
<p className="text-body-sm text-ods-text-secondary">
|
|
275
|
+
Something went wrong reaching the server. Check your connection and try again.
|
|
276
|
+
</p>
|
|
277
|
+
</div>
|
|
278
|
+
{onRetry && (
|
|
279
|
+
<Button
|
|
280
|
+
variant="outline"
|
|
281
|
+
size="small"
|
|
282
|
+
leftIcon={<Refresh01RightIcon />}
|
|
283
|
+
onClick={onRetry}
|
|
284
|
+
>
|
|
285
|
+
Try again
|
|
286
|
+
</Button>
|
|
287
|
+
)}
|
|
288
|
+
</div>
|
|
289
|
+
) : isLoadingHistory ? (
|
|
290
|
+
<MingoChatHistorySkeleton />
|
|
291
|
+
) : (
|
|
238
292
|
<>
|
|
239
293
|
{/* Scrollable region — only the greeting + grid scroll; the notification
|
|
240
294
|
and chips below stay pinned so they're always visible above the
|
|
@@ -321,9 +375,11 @@ export function MingoWelcome({
|
|
|
321
375
|
/>
|
|
322
376
|
</div>
|
|
323
377
|
</>
|
|
324
|
-
)}
|
|
378
|
+
))}
|
|
325
379
|
|
|
326
|
-
{/* Pinned region —
|
|
380
|
+
{/* Pinned region — visible above the input once the new/returning-user
|
|
381
|
+
state is known (hidden during first-page load / error). */}
|
|
382
|
+
{showPinnedRegion && (
|
|
327
383
|
<div className="flex shrink-0 flex-col gap-[var(--spacing-system-m)]">
|
|
328
384
|
{/* "New to OpenFrame?" — a one-time informational notification (no
|
|
329
385
|
action). The leading down-arrow (Figma node 7532:317130) only shows
|
|
@@ -356,41 +412,38 @@ export function MingoWelcome({
|
|
|
356
412
|
</div>
|
|
357
413
|
)}
|
|
358
414
|
|
|
359
|
-
{/* Quick-action chips.
|
|
360
|
-
|
|
361
|
-
|
|
415
|
+
{/* Quick-action chips. "Start Guide Chat" stays pinned (the `leading`
|
|
416
|
+
slot — never collapses); any caller `quickActions` fit inline and the
|
|
417
|
+
rest collapse under a "⋯" overflow menu, width-measured like the
|
|
418
|
+
Autocomplete tag row. */}
|
|
362
419
|
{(onStartGuideChat || (quickActions && quickActions.length > 0)) && (
|
|
363
|
-
<
|
|
364
|
-
{
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
label={action.label}
|
|
388
|
-
/>
|
|
389
|
-
</button>
|
|
390
|
-
))}
|
|
391
|
-
</div>
|
|
420
|
+
<ChatQuickActionRow
|
|
421
|
+
leading={
|
|
422
|
+
onStartGuideChat && (
|
|
423
|
+
<button
|
|
424
|
+
type="button"
|
|
425
|
+
onClick={onStartGuideChat}
|
|
426
|
+
className="shrink-0 rounded-md focus:outline-none focus-visible:ring-2 focus-visible:ring-ods-accent"
|
|
427
|
+
>
|
|
428
|
+
<Tag
|
|
429
|
+
variant={hasExistingChats ? 'outline' : 'primary'}
|
|
430
|
+
icon={<CompassIcon size={16} />}
|
|
431
|
+
label="Start Guide Chat"
|
|
432
|
+
/>
|
|
433
|
+
</button>
|
|
434
|
+
)
|
|
435
|
+
}
|
|
436
|
+
chips={(quickActions ?? []).map((action) => ({
|
|
437
|
+
id: action.id,
|
|
438
|
+
label: action.label,
|
|
439
|
+
icon: action.icon,
|
|
440
|
+
variant: action.variant,
|
|
441
|
+
onSelect: action.onClick,
|
|
442
|
+
}))}
|
|
443
|
+
/>
|
|
392
444
|
)}
|
|
393
445
|
</div>
|
|
446
|
+
)}
|
|
394
447
|
</div>
|
|
395
448
|
)
|
|
396
449
|
}
|
|
@@ -87,6 +87,10 @@ export interface MessageOwner {
|
|
|
87
87
|
id: string
|
|
88
88
|
firstName?: string
|
|
89
89
|
lastName?: string
|
|
90
|
+
/** Optional author avatar. Host queries it on the message owner (e.g.
|
|
91
|
+
* GraphQL `User.image`); `imageUrl` may be relative — the host is
|
|
92
|
+
* responsible for resolving it to an absolute/proxied URL. */
|
|
93
|
+
image?: { imageUrl?: string | null } | null
|
|
90
94
|
}
|
|
91
95
|
}
|
|
92
96
|
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
import type { MessageSegment } from './message.types'
|
|
20
20
|
import type { ScrollAnchor } from './message.types'
|
|
21
|
+
import type { AuthorType } from './chat.types'
|
|
21
22
|
import type { ChatRef } from '../chat-ref.types'
|
|
22
23
|
import type { ChatSource } from '../hooks/use-sse-chat-adapter'
|
|
23
24
|
import type { ChatAttachment } from '../utils/chat-attachment-markdown'
|
|
@@ -108,6 +109,32 @@ export interface UnifiedChatMessage {
|
|
|
108
109
|
id: string
|
|
109
110
|
role: 'user' | 'assistant'
|
|
110
111
|
|
|
112
|
+
/**
|
|
113
|
+
* Optional host-supplied display name for the message author. When set, the
|
|
114
|
+
* chat renders it verbatim (e.g. the signed-in user's full name for a
|
|
115
|
+
* `user` bubble, or a per-message admin name from history); when omitted the
|
|
116
|
+
* UI falls back to the role default (`'Mingo'` / `'You'`).
|
|
117
|
+
*/
|
|
118
|
+
name?: string
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Optional host-supplied avatar URL for the message author. Used by the
|
|
122
|
+
* panel to render an inline avatar on the author row (proxied via
|
|
123
|
+
* `useProxiedImageUrl`). Omit/`null` to fall back to initials. For `user`
|
|
124
|
+
* bubbles the avatar only renders when this is set, so consumers that don't
|
|
125
|
+
* supply one keep the name-only layout.
|
|
126
|
+
*/
|
|
127
|
+
avatar?: string | null
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Optional host-supplied author type — drives the name-row accent color in
|
|
131
|
+
* `<ChatMessageEnhanced>` (e.g. `'admin'` renders the name in the accent
|
|
132
|
+
* color, matching the standalone `/mingo` page). When omitted the panel
|
|
133
|
+
* falls back to the role default (`'user'` for user bubbles, the assistant
|
|
134
|
+
* type otherwise).
|
|
135
|
+
*/
|
|
136
|
+
authorType?: AuthorType
|
|
137
|
+
|
|
111
138
|
/**
|
|
112
139
|
* Flat string form for legacy/simple callers. The structured `segments`
|
|
113
140
|
* form is preferred — it carries thinking blocks, tool calls, approval
|
|
@@ -296,6 +323,14 @@ export interface UnifiedChatState {
|
|
|
296
323
|
/** True while the dialog list is being fetched for the first time. */
|
|
297
324
|
isDialogsLoading: boolean
|
|
298
325
|
|
|
326
|
+
/** True when the initial dialog-list load FAILED (e.g. backend down).
|
|
327
|
+
* Lets the UI distinguish a load error from a genuinely empty list and
|
|
328
|
+
* show a retry affordance instead of the new-user empty state. */
|
|
329
|
+
dialogsError: boolean
|
|
330
|
+
|
|
331
|
+
/** Re-run the initial dialog-list load — retry after `dialogsError`. */
|
|
332
|
+
reloadDialogs: () => void
|
|
333
|
+
|
|
299
334
|
/** True while message history for the active dialog is being fetched. */
|
|
300
335
|
isMessagesLoading: boolean
|
|
301
336
|
|