@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
|
@@ -6,54 +6,40 @@ interface ChatMessageSkeletonProps {
|
|
|
6
6
|
className?: string
|
|
7
7
|
showAvatar?: boolean
|
|
8
8
|
isUser?: boolean
|
|
9
|
-
assistantType?: 'mingo' | 'fae'
|
|
10
9
|
}
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
/**
|
|
12
|
+
* One message-row skeleton. Mirrors `ChatMessageEnhanced`'s real layout:
|
|
13
|
+
* full panel width, no card background, and an INLINE avatar in the name row
|
|
14
|
+
* (the 2025 chat pattern — Claude.ai / ChatGPT / Gemini), NOT the legacy
|
|
15
|
+
* `absolute -left-16` hanging avatar. Body lines use percentage widths so they
|
|
16
|
+
* reflow cleanly as the panel resizes.
|
|
17
|
+
*/
|
|
18
|
+
export function ChatMessageSkeleton({
|
|
19
|
+
className,
|
|
20
|
+
showAvatar = true,
|
|
21
|
+
isUser = false,
|
|
17
22
|
}: ChatMessageSkeletonProps) {
|
|
18
|
-
const isMingo = assistantType === 'mingo'
|
|
19
|
-
|
|
20
23
|
return (
|
|
21
|
-
<div
|
|
22
|
-
className=
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
"
|
|
33
|
-
isMingo
|
|
34
|
-
? "bg-gradient-to-br from-cyan-400/30 to-cyan-600/30"
|
|
35
|
-
: "bg-gradient-to-br from-pink-400/30 to-pink-600/30"
|
|
36
|
-
)} />
|
|
37
|
-
)}
|
|
38
|
-
|
|
39
|
-
{/* Message Content Skeleton */}
|
|
40
|
-
<div className="flex flex-col gap-1 min-w-0">
|
|
41
|
-
{/* Name and Timestamp Row Skeleton */}
|
|
42
|
-
<div className="flex items-center justify-between pr-2">
|
|
43
|
-
<div className="h-5 w-16 bg-ods-border rounded animate-pulse" />
|
|
44
|
-
<div className="h-4 w-12 bg-ods-border rounded animate-pulse" />
|
|
24
|
+
<div className={cn("relative py-[var(--spacing-system-s)]", className)}>
|
|
25
|
+
<div className="flex min-w-0 flex-col gap-[var(--spacing-system-xxs)]">
|
|
26
|
+
{/* Name row — inline avatar (assistant only) + name + timestamp,
|
|
27
|
+
matching ChatMessageEnhanced's inline-avatar header. Neutral
|
|
28
|
+
`bg-ods-border` so the placeholders are actually visible (a tinted
|
|
29
|
+
low-alpha fill reads as blank on the dark surface). */}
|
|
30
|
+
<div className="flex items-center gap-[var(--spacing-system-xs)]">
|
|
31
|
+
{showAvatar && !isUser && (
|
|
32
|
+
<div className="h-8 w-8 flex-shrink-0 rounded-full bg-ods-border animate-pulse" />
|
|
33
|
+
)}
|
|
34
|
+
<div className="h-5 w-32 rounded bg-ods-border animate-pulse" />
|
|
35
|
+
<div className="ml-auto h-4 w-14 rounded bg-ods-border animate-pulse" />
|
|
45
36
|
</div>
|
|
46
|
-
|
|
47
|
-
{/*
|
|
48
|
-
<div className="flex flex-col gap-2">
|
|
49
|
-
<div className="
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
{/* Second line - 80% width */}
|
|
53
|
-
<div className="h-4 w-4/5 bg-ods-border rounded animate-pulse" />
|
|
54
|
-
{/* Third line - 60% width (optional for variation) */}
|
|
55
|
-
<div className="h-4 w-3/5 bg-ods-border rounded animate-pulse" />
|
|
56
|
-
</div>
|
|
37
|
+
|
|
38
|
+
{/* Body lines — percentage widths so they reflow on panel resize. */}
|
|
39
|
+
<div className="flex flex-col gap-2 pt-[var(--spacing-system-xxs)]">
|
|
40
|
+
<div className="h-4 w-full rounded bg-ods-border animate-pulse" />
|
|
41
|
+
<div className="h-4 w-11/12 rounded bg-ods-border animate-pulse" />
|
|
42
|
+
<div className="h-4 w-3/5 rounded bg-ods-border animate-pulse" />
|
|
57
43
|
</div>
|
|
58
44
|
</div>
|
|
59
45
|
</div>
|
|
@@ -64,7 +50,16 @@ interface ChatMessageListSkeletonProps {
|
|
|
64
50
|
className?: string
|
|
65
51
|
messageCount?: number
|
|
66
52
|
showAvatars?: boolean
|
|
67
|
-
|
|
53
|
+
/**
|
|
54
|
+
* Match `ChatMessageList`'s `fullWidth`: drop the centered
|
|
55
|
+
* `max-w-ods-content-narrow` column so the skeleton fills the panel and
|
|
56
|
+
* reflows on resize (instead of staying a fixed narrow column with large
|
|
57
|
+
* side gutters). Same semantics as the real list.
|
|
58
|
+
*/
|
|
59
|
+
fullWidth?: boolean
|
|
60
|
+
/** Extra classes on the content column (e.g. host-provided padding). The
|
|
61
|
+
* host wrapper is expected to own the panel padding; this stays empty by
|
|
62
|
+
* default so the skeleton sits flush. */
|
|
68
63
|
contentClassName?: string
|
|
69
64
|
}
|
|
70
65
|
|
|
@@ -72,13 +67,12 @@ export function ChatMessageListSkeleton({
|
|
|
72
67
|
className,
|
|
73
68
|
messageCount = 6,
|
|
74
69
|
showAvatars = true,
|
|
75
|
-
|
|
76
|
-
contentClassName
|
|
70
|
+
fullWidth = false,
|
|
71
|
+
contentClassName,
|
|
77
72
|
}: ChatMessageListSkeletonProps) {
|
|
78
73
|
const messages = Array.from({ length: messageCount }, (_, index) => ({
|
|
79
74
|
id: index,
|
|
80
75
|
isUser: index % 3 === 0,
|
|
81
|
-
assistantType: assistantType
|
|
82
76
|
}))
|
|
83
77
|
|
|
84
78
|
return (
|
|
@@ -88,23 +82,29 @@ export function ChatMessageListSkeleton({
|
|
|
88
82
|
"flex h-full w-full flex-col overflow-y-auto overflow-x-hidden flex-1",
|
|
89
83
|
"[scroll-behavior:smooth]",
|
|
90
84
|
"scrollbar-thin scrollbar-track-transparent scrollbar-thumb-ods-border/30 hover:scrollbar-thumb-ods-text-secondary/30",
|
|
91
|
-
className
|
|
85
|
+
className,
|
|
92
86
|
)}
|
|
93
87
|
>
|
|
94
|
-
<div
|
|
88
|
+
<div
|
|
89
|
+
className={cn(
|
|
90
|
+
fullWidth
|
|
91
|
+
? "flex w-full flex-col min-w-0"
|
|
92
|
+
: "mx-auto flex w-full max-w-ods-content-narrow flex-col min-w-0",
|
|
93
|
+
contentClassName,
|
|
94
|
+
)}
|
|
95
|
+
style={{ minHeight: '100%' }}
|
|
96
|
+
>
|
|
97
|
+
{/* Bottom-anchor the rows like the real list. */}
|
|
95
98
|
<div className="flex-1" />
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
/>
|
|
104
|
-
))}
|
|
105
|
-
</div>
|
|
99
|
+
{messages.map((message) => (
|
|
100
|
+
<ChatMessageSkeleton
|
|
101
|
+
key={message.id}
|
|
102
|
+
showAvatar={showAvatars}
|
|
103
|
+
isUser={message.isUser}
|
|
104
|
+
/>
|
|
105
|
+
))}
|
|
106
106
|
</div>
|
|
107
107
|
</div>
|
|
108
108
|
</div>
|
|
109
109
|
)
|
|
110
|
-
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { Chevron02LeftIcon, ClockHistoryIcon } from '../icons-v2-generated'
|
|
4
|
+
import { Button } from '../ui/button'
|
|
5
|
+
import { ActionsMenuDropdown, type ActionsMenuItem } from '../ui/actions-menu'
|
|
6
|
+
import { cn } from '../../utils/cn'
|
|
7
|
+
import type { ChatPanelHeaderProps } from './chat-panel-header'
|
|
8
|
+
|
|
9
|
+
export interface ChatPanelHeaderMobileProps extends ChatPanelHeaderProps {
|
|
10
|
+
className?: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Mobile chat panel header — Figma node `7363:85532`. A distinct layout from
|
|
15
|
+
* the desktop bar (which packs full-height divider cells into a fixed `h-14`):
|
|
16
|
+
*
|
|
17
|
+
* - Surface: `ods-card` with a bottom border; generous `l` side padding,
|
|
18
|
+
* `l` bottom padding, and `xl` top padding on the title block (so the row
|
|
19
|
+
* bottom-aligns under a tall header).
|
|
20
|
+
* - Title: `text-h2` (vs the desktop `h3`/`h4`), truncated.
|
|
21
|
+
* - Actions: 44px outlined icon buttons (`Button variant="outline"
|
|
22
|
+
* size="icon"`) — a back chevron (when applicable) and a single trailing
|
|
23
|
+
* `⋯` menu carrying the view-dependent actions. There is no Close action
|
|
24
|
+
* (the drawer is dismissed via its backdrop / the back chevron); when no
|
|
25
|
+
* action applies the `⋯` trigger is hidden entirely.
|
|
26
|
+
*
|
|
27
|
+
* Same prop contract as `ChatPanelHeader` (`onClose` is accepted for parity but
|
|
28
|
+
* unused here); the orchestrator renders this on mobile and the desktop bar on
|
|
29
|
+
* `md+`.
|
|
30
|
+
*/
|
|
31
|
+
export function ChatPanelHeaderMobile({
|
|
32
|
+
showBack = false,
|
|
33
|
+
title,
|
|
34
|
+
backAriaLabel = 'Back',
|
|
35
|
+
isArchivedView = false,
|
|
36
|
+
onBack,
|
|
37
|
+
onRestore,
|
|
38
|
+
onRename,
|
|
39
|
+
onArchive,
|
|
40
|
+
onOpenArchive,
|
|
41
|
+
className,
|
|
42
|
+
}: ChatPanelHeaderMobileProps) {
|
|
43
|
+
// The right-hand ⋯ menu carries the view-dependent actions. Close is not
|
|
44
|
+
// listed (the panel is dismissed elsewhere); the trigger is hidden entirely
|
|
45
|
+
// when no action applies (e.g. the archive list view).
|
|
46
|
+
const menuItems = [
|
|
47
|
+
!showBack && onOpenArchive && { id: 'open-archive', label: 'Chat archive', icon: <ClockHistoryIcon className="w-full h-full" />, onClick: onOpenArchive },
|
|
48
|
+
isArchivedView && onRestore && { id: 'unarchive', label: 'Unarchive chat', onClick: onRestore },
|
|
49
|
+
!isArchivedView && onRename && { id: 'rename', label: 'Rename chat', onClick: onRename },
|
|
50
|
+
!isArchivedView && onArchive && { id: 'archive', label: 'Archive chat', onClick: onArchive },
|
|
51
|
+
].filter(Boolean) as ActionsMenuItem[]
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<div
|
|
55
|
+
className={cn(
|
|
56
|
+
'flex-shrink-0 flex flex-col border-b border-ods-border bg-ods-card',
|
|
57
|
+
'px-[var(--spacing-system-l)] pb-[var(--spacing-system-l)]',
|
|
58
|
+
className,
|
|
59
|
+
)}
|
|
60
|
+
>
|
|
61
|
+
<div className="flex w-full items-end gap-[var(--spacing-system-sf)]">
|
|
62
|
+
{showBack && (
|
|
63
|
+
<Button
|
|
64
|
+
variant="outline"
|
|
65
|
+
size="icon"
|
|
66
|
+
aria-label={backAriaLabel}
|
|
67
|
+
onClick={onBack}
|
|
68
|
+
className="shrink-0"
|
|
69
|
+
>
|
|
70
|
+
<Chevron02LeftIcon />
|
|
71
|
+
</Button>
|
|
72
|
+
)}
|
|
73
|
+
|
|
74
|
+
{/* Title block — `xl` top padding pushes the title down so the row
|
|
75
|
+
bottom-aligns the title, back chevron, and ⋯ menu. */}
|
|
76
|
+
<div className="flex flex-1 min-w-0 items-end gap-[var(--spacing-system-m)] pt-[var(--spacing-system-xl)]">
|
|
77
|
+
<div className="flex flex-1 min-w-0 flex-col gap-[var(--spacing-system-xs)]">
|
|
78
|
+
<p className="min-w-0 truncate text-h2 text-ods-text-primary" title={title}>
|
|
79
|
+
{title}
|
|
80
|
+
</p>
|
|
81
|
+
</div>
|
|
82
|
+
|
|
83
|
+
{menuItems.length > 0 && (
|
|
84
|
+
<ActionsMenuDropdown
|
|
85
|
+
groups={[{ items: menuItems }]}
|
|
86
|
+
triggerAriaLabel="Chat actions"
|
|
87
|
+
triggerClassName="shrink-0 bg-ods-card border-ods-border hover:bg-ods-bg-hover flex items-center justify-center focus-visible:ring-0"
|
|
88
|
+
/>
|
|
89
|
+
)}
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
)
|
|
94
|
+
}
|
|
@@ -9,7 +9,16 @@ import {
|
|
|
9
9
|
} from '../icons-v2-generated'
|
|
10
10
|
import { XmarkIcon } from '../icons-v2-generated/signs-and-symbols/xmark-icon'
|
|
11
11
|
import { ChatHeaderIconButton } from './chat-header-icon-button'
|
|
12
|
-
import {
|
|
12
|
+
import { ActionsMenuDropdown, type ActionsMenuItem } from '../ui/actions-menu'
|
|
13
|
+
import { ChatPanelHeaderMobile } from './chat-panel-header-mobile'
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Compact rounded header button used for the desktop back chevron. Borderless
|
|
17
|
+
* square with a hover background. (Mobile uses its own header component —
|
|
18
|
+
* `ChatPanelHeaderMobile`.)
|
|
19
|
+
*/
|
|
20
|
+
export const COMPACT_HEADER_BUTTON =
|
|
21
|
+
'inline-flex shrink-0 items-center justify-center size-8 rounded-md text-ods-text-secondary transition-colors hover:bg-ods-bg-hover hover:text-ods-text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-ods-accent'
|
|
13
22
|
|
|
14
23
|
export interface ChatPanelHeaderProps {
|
|
15
24
|
/** Show the back-chevron + bold (h3) title. When false, the static list
|
|
@@ -53,62 +62,81 @@ export function ChatPanelHeader({
|
|
|
53
62
|
onArchive,
|
|
54
63
|
onOpenArchive,
|
|
55
64
|
}: ChatPanelHeaderProps) {
|
|
65
|
+
// Desktop ⋯ menu (active, non-archived conversation only) — rename / archive.
|
|
56
66
|
const menuItems = [
|
|
57
|
-
onRename && { label: 'Rename chat', onClick: onRename },
|
|
58
|
-
onArchive && { label: 'Archive chat', onClick: onArchive },
|
|
59
|
-
].filter(Boolean) as
|
|
67
|
+
onRename && { id: 'rename', label: 'Rename chat', onClick: onRename },
|
|
68
|
+
onArchive && { id: 'archive', label: 'Archive chat', onClick: onArchive },
|
|
69
|
+
].filter(Boolean) as ActionsMenuItem[]
|
|
60
70
|
|
|
61
71
|
return (
|
|
62
|
-
|
|
63
|
-
<
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
72
|
+
<>
|
|
73
|
+
{/* Mobile (<md): a distinct layout (Figma node 7363:85532). */}
|
|
74
|
+
<ChatPanelHeaderMobile
|
|
75
|
+
className="flex md:hidden"
|
|
76
|
+
showBack={showBack}
|
|
77
|
+
title={title}
|
|
78
|
+
backAriaLabel={backAriaLabel}
|
|
79
|
+
isArchivedView={isArchivedView}
|
|
80
|
+
onBack={onBack}
|
|
81
|
+
onClose={onClose}
|
|
82
|
+
onRestore={onRestore}
|
|
83
|
+
onRename={onRename}
|
|
84
|
+
onArchive={onArchive}
|
|
85
|
+
onOpenArchive={onOpenArchive}
|
|
86
|
+
/>
|
|
87
|
+
|
|
88
|
+
{/* Desktop (md+): fixed-height bar with full-height divider action cells. */}
|
|
89
|
+
<div className="hidden md:flex flex-shrink-0 h-14 w-full overflow-hidden border-b border-ods-border bg-ods-card">
|
|
90
|
+
<div className="flex flex-1 min-w-0 items-center gap-2 px-4 py-3">
|
|
91
|
+
{showBack ? (
|
|
92
|
+
<>
|
|
93
|
+
<button
|
|
94
|
+
type="button"
|
|
95
|
+
onClick={onBack}
|
|
96
|
+
aria-label={backAriaLabel}
|
|
97
|
+
className={`${COMPACT_HEADER_BUTTON} -ml-1`}
|
|
98
|
+
>
|
|
99
|
+
<Chevron01LeftIcon size={20} />
|
|
100
|
+
</button>
|
|
101
|
+
<span className="truncate text-h3 text-ods-text-primary">{title}</span>
|
|
102
|
+
</>
|
|
103
|
+
) : (
|
|
104
|
+
<p className="truncate text-h3 text-ods-text-primary">{title}</p>
|
|
105
|
+
)}
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
{/* Restore (refresh) — archived chat header (Figma node 7361:425441). */}
|
|
109
|
+
{isArchivedView && onRestore && (
|
|
110
|
+
<ChatHeaderIconButton onClick={onRestore} aria-label="Unarchive chat">
|
|
111
|
+
<Refresh01LeftIcon size={24} />
|
|
112
|
+
</ChatHeaderIconButton>
|
|
78
113
|
)}
|
|
79
|
-
</div>
|
|
80
114
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
115
|
+
{/* Rename / Archive menu — active (non-archived) conversation only. */}
|
|
116
|
+
{showBack && !isArchivedView && menuItems.length > 0 && (
|
|
117
|
+
<ActionsMenuDropdown
|
|
118
|
+
triggerAriaLabel="Chat actions"
|
|
119
|
+
onCloseAutoFocus={(e) => e.preventDefault()}
|
|
120
|
+
groups={[{ items: menuItems }]}
|
|
121
|
+
customTrigger={
|
|
122
|
+
<ChatHeaderIconButton aria-label="Chat actions">
|
|
123
|
+
<Ellipsis01Icon size={24} />
|
|
124
|
+
</ChatHeaderIconButton>
|
|
125
|
+
}
|
|
126
|
+
/>
|
|
127
|
+
)}
|
|
87
128
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
trigger={
|
|
95
|
-
<ChatHeaderIconButton aria-label="Chat actions">
|
|
96
|
-
<Ellipsis01Icon size={24} />
|
|
97
|
-
</ChatHeaderIconButton>
|
|
98
|
-
}
|
|
99
|
-
/>
|
|
100
|
-
)}
|
|
129
|
+
{/* Chat Archive entry (Figma node 7532:225034) — list view only. */}
|
|
130
|
+
{!showBack && onOpenArchive && (
|
|
131
|
+
<ChatHeaderIconButton onClick={onOpenArchive} aria-label="Chat archive">
|
|
132
|
+
<ClockHistoryIcon size={24} />
|
|
133
|
+
</ChatHeaderIconButton>
|
|
134
|
+
)}
|
|
101
135
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
<ChatHeaderIconButton onClick={onOpenArchive} aria-label="Chat archive">
|
|
105
|
-
<ClockHistoryIcon size={24} />
|
|
136
|
+
<ChatHeaderIconButton onClick={onClose} aria-label="Close">
|
|
137
|
+
<XmarkIcon size={24} />
|
|
106
138
|
</ChatHeaderIconButton>
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
<ChatHeaderIconButton onClick={onClose} aria-label="Close">
|
|
110
|
-
<XmarkIcon size={24} />
|
|
111
|
-
</ChatHeaderIconButton>
|
|
112
|
-
</div>
|
|
139
|
+
</div>
|
|
140
|
+
</>
|
|
113
141
|
)
|
|
114
142
|
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
import { cn } from '../../utils/cn'
|
|
5
|
+
import { Tag } from '../ui/tag'
|
|
6
|
+
import { ActionsMenuDropdown } from '../ui/actions-menu'
|
|
7
|
+
import { Ellipsis01Icon } from '../icons-v2-generated'
|
|
8
|
+
import { useAutoLimitTags } from '../../hooks/ui/use-auto-limit-tags'
|
|
9
|
+
|
|
10
|
+
// =============================================================================
|
|
11
|
+
// Types
|
|
12
|
+
// =============================================================================
|
|
13
|
+
|
|
14
|
+
/** A single collapsible quick-action chip. */
|
|
15
|
+
export interface QuickActionChip {
|
|
16
|
+
/** Stable React key + menu-item id. */
|
|
17
|
+
id: string
|
|
18
|
+
label: string
|
|
19
|
+
icon?: React.ReactNode
|
|
20
|
+
/** `'primary'` = accent (yellow) chip, `'outline'` = bordered chip (default). */
|
|
21
|
+
variant?: 'primary' | 'outline'
|
|
22
|
+
onSelect?: () => void
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface ChatQuickActionRowProps {
|
|
26
|
+
/** Always-visible leading chip(s) (e.g. Mingo's "Start Guide Chat"). Never
|
|
27
|
+
* collapses into the overflow menu — rendered before the measured zone. */
|
|
28
|
+
leading?: React.ReactNode
|
|
29
|
+
/** Collapsible chips — as many as fit on one line render inline; the rest
|
|
30
|
+
* collapse under a trailing "⋯" overflow menu (small icon button). */
|
|
31
|
+
chips: ReadonlyArray<QuickActionChip>
|
|
32
|
+
className?: string
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// =============================================================================
|
|
36
|
+
// Chip button
|
|
37
|
+
// =============================================================================
|
|
38
|
+
|
|
39
|
+
function ChipButton({ chip }: { chip: QuickActionChip }) {
|
|
40
|
+
return (
|
|
41
|
+
<button
|
|
42
|
+
type="button"
|
|
43
|
+
onClick={chip.onSelect}
|
|
44
|
+
className="shrink-0 rounded-md focus:outline-none focus-visible:ring-2 focus-visible:ring-ods-accent"
|
|
45
|
+
>
|
|
46
|
+
<Tag variant={chip.variant ?? 'outline'} icon={chip.icon} label={chip.label} />
|
|
47
|
+
</button>
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// =============================================================================
|
|
52
|
+
// Component
|
|
53
|
+
// =============================================================================
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* A single-line quick-action chip row that collapses overflow into a "⋯" menu.
|
|
57
|
+
*
|
|
58
|
+
* Same dynamic, width-measured overflow used by the multi-select Autocomplete
|
|
59
|
+
* (`useAutoLimitTags`): off-screen copies of every chip are measured, as many
|
|
60
|
+
* as fit render inline, and the remainder move into an `ActionsMenuDropdown`
|
|
61
|
+
* behind a small "⋯" icon button. An optional `leading` slot stays pinned and
|
|
62
|
+
* never collapses, so the row's primary action is always reachable.
|
|
63
|
+
*/
|
|
64
|
+
export function ChatQuickActionRow({
|
|
65
|
+
leading,
|
|
66
|
+
chips,
|
|
67
|
+
className,
|
|
68
|
+
}: ChatQuickActionRowProps) {
|
|
69
|
+
const auto = useAutoLimitTags({
|
|
70
|
+
count: chips.length,
|
|
71
|
+
reserveInputWidth: false,
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
const visibleChips = chips.slice(0, auto.visibleCount)
|
|
75
|
+
const hiddenChips = chips.slice(auto.visibleCount)
|
|
76
|
+
|
|
77
|
+
if (!leading && chips.length === 0) return null
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<div
|
|
81
|
+
className={cn('relative flex shrink-0 items-center gap-1', className)}
|
|
82
|
+
>
|
|
83
|
+
{leading}
|
|
84
|
+
|
|
85
|
+
{chips.length > 0 && (
|
|
86
|
+
// Measured zone: `flex-1 min-w-0 overflow-hidden` so it claims the width
|
|
87
|
+
// left by the pinned `leading` slot and clips anything past the fit.
|
|
88
|
+
<div
|
|
89
|
+
ref={auto.middleRef}
|
|
90
|
+
className="flex flex-1 min-w-0 items-center gap-1 overflow-hidden"
|
|
91
|
+
>
|
|
92
|
+
{visibleChips.map((chip) => (
|
|
93
|
+
<ChipButton key={chip.id} chip={chip} />
|
|
94
|
+
))}
|
|
95
|
+
|
|
96
|
+
{hiddenChips.length > 0 && (
|
|
97
|
+
<ActionsMenuDropdown
|
|
98
|
+
triggerAriaLabel="More quick actions"
|
|
99
|
+
// Don't return focus (and its ring) to the "⋯" trigger on close.
|
|
100
|
+
onCloseAutoFocus={(e) => e.preventDefault()}
|
|
101
|
+
groups={[
|
|
102
|
+
{
|
|
103
|
+
items: hiddenChips.map((chip) => ({
|
|
104
|
+
id: chip.id,
|
|
105
|
+
label: chip.label,
|
|
106
|
+
icon: chip.icon,
|
|
107
|
+
onClick: chip.onSelect,
|
|
108
|
+
})),
|
|
109
|
+
},
|
|
110
|
+
]}
|
|
111
|
+
customTrigger={
|
|
112
|
+
<button
|
|
113
|
+
ref={auto.badgeRef}
|
|
114
|
+
type="button"
|
|
115
|
+
aria-label="More quick actions"
|
|
116
|
+
className="shrink-0 rounded-md focus:outline-none focus-visible:ring-2 focus-visible:ring-ods-accent"
|
|
117
|
+
>
|
|
118
|
+
<Tag variant="outline" label={<Ellipsis01Icon size={16} />} />
|
|
119
|
+
</button>
|
|
120
|
+
}
|
|
121
|
+
/>
|
|
122
|
+
)}
|
|
123
|
+
</div>
|
|
124
|
+
)}
|
|
125
|
+
|
|
126
|
+
{/* Off-screen measurement copies of every chip — the hook reads their
|
|
127
|
+
widths to decide how many fit. Mirror the visible chip markup (same
|
|
128
|
+
variant/icon/label and the same `gap-1`) so the measured widths match
|
|
129
|
+
what actually renders. */}
|
|
130
|
+
<div
|
|
131
|
+
ref={auto.measureRef}
|
|
132
|
+
aria-hidden
|
|
133
|
+
className="pointer-events-none invisible absolute left-0 top-0 -z-10 flex gap-1"
|
|
134
|
+
>
|
|
135
|
+
{chips.map((chip) => (
|
|
136
|
+
<Tag
|
|
137
|
+
key={`measure-${chip.id}`}
|
|
138
|
+
variant={chip.variant ?? 'outline'}
|
|
139
|
+
icon={chip.icon}
|
|
140
|
+
label={chip.label}
|
|
141
|
+
/>
|
|
142
|
+
))}
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
)
|
|
146
|
+
}
|