@flamingo-stack/openframe-frontend-core 0.0.420 → 0.0.421-snapshot.20260713170448
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-WBLTGO7G.cjs → chunk-2JNFQEVX.cjs} +26 -26
- package/dist/{chunk-WBLTGO7G.cjs.map → chunk-2JNFQEVX.cjs.map} +1 -1
- package/dist/{chunk-T55A3BQD.js → chunk-3BGWK5QI.js} +4 -4
- package/dist/{chunk-ZZFLE5LG.js → chunk-4BWQYGIY.js} +2 -2
- package/dist/{chunk-KEWTMPHB.js → chunk-4TMMYZTY.js} +2 -2
- package/dist/{chunk-U7EUN5FZ.js → chunk-6F5OKJTW.js} +4 -4
- package/dist/{chunk-KLMLQ35Y.cjs → chunk-A2IRUIUJ.cjs} +37 -37
- package/dist/{chunk-KLMLQ35Y.cjs.map → chunk-A2IRUIUJ.cjs.map} +1 -1
- package/dist/{chunk-KPLRIPEH.cjs → chunk-B62DI2SC.cjs} +15 -15
- package/dist/{chunk-KPLRIPEH.cjs.map → chunk-B62DI2SC.cjs.map} +1 -1
- package/dist/{chunk-PUXTMLKC.cjs → chunk-BF5BXKM7.cjs} +62 -62
- package/dist/{chunk-PUXTMLKC.cjs.map → chunk-BF5BXKM7.cjs.map} +1 -1
- package/dist/{chunk-PAF4EFRR.js → chunk-DUHM44CQ.js} +2 -2
- package/dist/{chunk-YYF2AYM3.js → chunk-HFK33T6A.js} +4 -4
- package/dist/{chunk-J7WFE7WL.js → chunk-JSL3VXGJ.js} +2 -2
- package/dist/{chunk-BXXXBZYY.cjs → chunk-KUDLV25M.cjs} +190 -154
- package/dist/chunk-KUDLV25M.cjs.map +1 -0
- package/dist/{chunk-OBMYKFBO.js → chunk-LHM2VUTO.js} +2 -2
- package/dist/{chunk-AMUTZJZX.cjs → chunk-LWC2U5OC.cjs} +9 -9
- package/dist/{chunk-AMUTZJZX.cjs.map → chunk-LWC2U5OC.cjs.map} +1 -1
- package/dist/{chunk-2AN4EHN6.cjs → chunk-O3FSSWV4.cjs} +7 -7
- package/dist/{chunk-2AN4EHN6.cjs.map → chunk-O3FSSWV4.cjs.map} +1 -1
- package/dist/{chunk-PHJ6335O.cjs → chunk-PO4PXWZN.cjs} +5 -5
- package/dist/{chunk-PHJ6335O.cjs.map → chunk-PO4PXWZN.cjs.map} +1 -1
- package/dist/{chunk-ORDDVXDS.cjs → chunk-RGKGRS2T.cjs} +59 -59
- package/dist/{chunk-ORDDVXDS.cjs.map → chunk-RGKGRS2T.cjs.map} +1 -1
- package/dist/{chunk-5DTE27A4.cjs → chunk-ULUH2X53.cjs} +11 -11
- package/dist/{chunk-5DTE27A4.cjs.map → chunk-ULUH2X53.cjs.map} +1 -1
- package/dist/{chunk-NIS5VXZJ.cjs → chunk-WAQH35VG.cjs} +26 -26
- package/dist/{chunk-NIS5VXZJ.cjs.map → chunk-WAQH35VG.cjs.map} +1 -1
- package/dist/{chunk-G4W5D7L4.js → chunk-WQEEGFDM.js} +2 -2
- package/dist/{chunk-GC4355MW.js → chunk-XFYQV2K2.js} +2 -2
- package/dist/{chunk-3AJOKFJ4.js → chunk-YJDVAT7J.js} +66 -30
- package/dist/chunk-YJDVAT7J.js.map +1 -0
- package/dist/{chunk-4OZLRBOH.js → chunk-YUMFRC3D.js} +4 -4
- package/dist/{chunk-NDYJ37LY.cjs → chunk-YZQNKMFQ.cjs} +31 -31
- package/dist/{chunk-NDYJ37LY.cjs.map → chunk-YZQNKMFQ.cjs.map} +1 -1
- package/dist/components/case-studies/index.cjs +8 -8
- package/dist/components/case-studies/index.js +2 -2
- package/dist/components/chat/chat-input.d.ts.map +1 -1
- package/dist/components/chat/embeddable-chat.d.ts +9 -0
- package/dist/components/chat/embeddable-chat.d.ts.map +1 -1
- package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts.map +1 -1
- package/dist/components/chat/hooks/use-realtime-chunk-processor.d.ts.map +1 -1
- package/dist/components/chat/index.cjs +2 -2
- package/dist/components/chat/index.js +1 -1
- package/dist/components/chat/types/api.types.d.ts +10 -0
- package/dist/components/chat/types/api.types.d.ts.map +1 -1
- package/dist/components/contact/index.cjs +3 -3
- package/dist/components/contact/index.js +2 -2
- package/dist/components/docs/index.cjs +5 -5
- package/dist/components/docs/index.js +4 -4
- package/dist/components/embeds/index.cjs +3 -3
- package/dist/components/embeds/index.js +2 -2
- package/dist/components/faq/index.cjs +3 -3
- package/dist/components/faq/index.js +2 -2
- package/dist/components/features/index.cjs +2 -2
- package/dist/components/features/index.js +1 -1
- package/dist/components/help-center-pages/index.cjs +20 -20
- package/dist/components/help-center-pages/index.js +11 -11
- package/dist/components/index.cjs +128 -128
- package/dist/components/index.js +9 -9
- package/dist/components/navigation/index.cjs +2 -2
- package/dist/components/navigation/index.js +1 -1
- package/dist/components/onboarding-guides/index.cjs +5 -5
- package/dist/components/onboarding-guides/index.js +4 -4
- package/dist/components/related-content/index.cjs +3 -3
- package/dist/components/related-content/index.js +2 -2
- package/dist/components/tabs.d.ts +10 -0
- package/dist/components/tabs.d.ts.map +1 -1
- package/dist/components/tickets/index.cjs +5 -5
- package/dist/components/tickets/index.js +4 -4
- package/dist/components/ui/dashboard-info-card.d.ts +26 -12
- package/dist/components/ui/dashboard-info-card.d.ts.map +1 -1
- package/dist/components/ui/index.cjs +2 -2
- package/dist/components/ui/index.js +1 -1
- package/dist/components/ui/tab-navigation.d.ts.map +1 -1
- package/dist/components/ui/tabs.d.ts +11 -0
- package/dist/components/ui/tabs.d.ts.map +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/components/chat/chat-input.tsx +5 -1
- package/src/components/chat/embeddable-chat.tsx +17 -3
- package/src/components/chat/hooks/use-nats-chat-adapter.ts +52 -3
- package/src/components/chat/hooks/use-realtime-chunk-processor.ts +25 -3
- package/src/components/chat/types/api.types.ts +10 -0
- package/src/components/tabs.tsx +10 -0
- package/src/components/ui/dashboard-info-card.tsx +70 -45
- package/src/components/ui/tab-navigation.tsx +5 -2
- package/src/components/ui/tabs.tsx +11 -0
- package/src/stories/ApprovalRequestMessageClient.stories.tsx +89 -0
- package/src/stories/ChatMessageEnhanced.stories.tsx +219 -0
- package/src/stories/ChatTypingIndicator.stories.tsx +51 -0
- package/src/stories/ContextCompactionDisplay.stories.tsx +44 -0
- package/src/stories/DashboardInfoCard.stories.tsx +94 -9
- package/src/stories/EmbeddableChat.stories.tsx +159 -0
- package/src/stories/ErrorMessageDisplay.stories.tsx +66 -0
- package/src/stories/ThinkingDisplay.stories.tsx +63 -0
- package/src/stories/TicketCard.stories.tsx +245 -0
- package/dist/chunk-3AJOKFJ4.js.map +0 -1
- package/dist/chunk-BXXXBZYY.cjs.map +0 -1
- /package/dist/{chunk-T55A3BQD.js.map → chunk-3BGWK5QI.js.map} +0 -0
- /package/dist/{chunk-ZZFLE5LG.js.map → chunk-4BWQYGIY.js.map} +0 -0
- /package/dist/{chunk-KEWTMPHB.js.map → chunk-4TMMYZTY.js.map} +0 -0
- /package/dist/{chunk-U7EUN5FZ.js.map → chunk-6F5OKJTW.js.map} +0 -0
- /package/dist/{chunk-PAF4EFRR.js.map → chunk-DUHM44CQ.js.map} +0 -0
- /package/dist/{chunk-YYF2AYM3.js.map → chunk-HFK33T6A.js.map} +0 -0
- /package/dist/{chunk-J7WFE7WL.js.map → chunk-JSL3VXGJ.js.map} +0 -0
- /package/dist/{chunk-OBMYKFBO.js.map → chunk-LHM2VUTO.js.map} +0 -0
- /package/dist/{chunk-G4W5D7L4.js.map → chunk-WQEEGFDM.js.map} +0 -0
- /package/dist/{chunk-GC4355MW.js.map → chunk-XFYQV2K2.js.map} +0 -0
- /package/dist/{chunk-4OZLRBOH.js.map → chunk-YUMFRC3D.js.map} +0 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/nextjs-vite";
|
|
2
|
+
import type React from "react";
|
|
3
|
+
|
|
4
|
+
import { ChatTypingIndicator } from "../components/chat/chat-typing-indicator";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Three pulsing dots shown while the assistant reply streams in — the
|
|
8
|
+
* "Fae is typing" affordance under the message header. Also rendered
|
|
9
|
+
* inside `<ChatInput>` (size="sm") while a send is in flight.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const plainDecorator = (Story: React.ComponentType) => (
|
|
13
|
+
<div style={{ maxWidth: 400, background: "var(--color-bg)" }} className="p-4">
|
|
14
|
+
<Story />
|
|
15
|
+
</div>
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
const meta = {
|
|
19
|
+
title: "Chat/ChatTypingIndicator",
|
|
20
|
+
component: ChatTypingIndicator,
|
|
21
|
+
tags: ["autodocs"],
|
|
22
|
+
parameters: {
|
|
23
|
+
docs: {
|
|
24
|
+
description: {
|
|
25
|
+
component:
|
|
26
|
+
"Pulsing three-dot typing indicator. Sizes sm/md/lg; optional 'Assistant is typing' label; dot color overridable via dotClassName.",
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
decorators: [plainDecorator],
|
|
31
|
+
} satisfies Meta<typeof ChatTypingIndicator>;
|
|
32
|
+
|
|
33
|
+
export default meta;
|
|
34
|
+
type Story = StoryObj<typeof meta>;
|
|
35
|
+
|
|
36
|
+
/** Default — medium dots, no label. */
|
|
37
|
+
export const Default: Story = {};
|
|
38
|
+
|
|
39
|
+
/** Small — as rendered inside ChatInput while a send is in flight. */
|
|
40
|
+
export const Small: Story = { args: { size: "sm" } };
|
|
41
|
+
|
|
42
|
+
/** Large dots. */
|
|
43
|
+
export const Large: Story = { args: { size: "lg" } };
|
|
44
|
+
|
|
45
|
+
/** With the "Assistant is typing" label. */
|
|
46
|
+
export const WithText: Story = { args: { showText: true } };
|
|
47
|
+
|
|
48
|
+
/** Muted dots via dotClassName override. */
|
|
49
|
+
export const MutedDots: Story = {
|
|
50
|
+
args: { dotClassName: "bg-ods-text-secondary" },
|
|
51
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/nextjs-vite";
|
|
2
|
+
import type React from "react";
|
|
3
|
+
|
|
4
|
+
import { ContextCompactionDisplay } from "../components/chat/context-compaction-display";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Inline status pill for `context_compaction` message segments — shown when
|
|
8
|
+
* the conversation hits the context limit and earlier messages get
|
|
9
|
+
* summarized. `started` shows a dots loader, `completed` a green check.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const plainDecorator = (Story: React.ComponentType) => (
|
|
13
|
+
<div style={{ maxWidth: 640, background: "var(--color-bg)" }} className="p-4">
|
|
14
|
+
<Story />
|
|
15
|
+
</div>
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
const meta = {
|
|
19
|
+
title: "Chat/ContextCompactionDisplay",
|
|
20
|
+
component: ContextCompactionDisplay,
|
|
21
|
+
tags: ["autodocs"],
|
|
22
|
+
parameters: {
|
|
23
|
+
docs: {
|
|
24
|
+
description: {
|
|
25
|
+
component:
|
|
26
|
+
"Renders a `context_compaction` message segment — 'Context limit reached…' with a dots loader while running, 'Earlier context summarized.' with a check once done.",
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
decorators: [plainDecorator],
|
|
31
|
+
} satisfies Meta<typeof ContextCompactionDisplay>;
|
|
32
|
+
|
|
33
|
+
export default meta;
|
|
34
|
+
type Story = StoryObj<typeof meta>;
|
|
35
|
+
|
|
36
|
+
/** In progress — summarizing earlier messages. */
|
|
37
|
+
export const Started: Story = {
|
|
38
|
+
args: { status: "started" },
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/** Finished — earlier context summarized. */
|
|
42
|
+
export const Completed: Story = {
|
|
43
|
+
args: { status: "completed" },
|
|
44
|
+
};
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/nextjs-vite'
|
|
2
2
|
import { DashboardInfoCard } from '../components/ui/dashboard-info-card'
|
|
3
|
+
import { Tag } from '../components/ui/tag'
|
|
4
|
+
import { MonitorIcon } from '../components/icons-v2-generated/devices/monitor-icon'
|
|
5
|
+
import { FaceSmile01Icon } from '../components/icons-v2-generated/users/face-smile-01-icon'
|
|
3
6
|
|
|
4
7
|
const meta = {
|
|
5
8
|
title: 'UI/DashboardInfoCard',
|
|
@@ -30,11 +33,13 @@ const meta = {
|
|
|
30
33
|
},
|
|
31
34
|
progressSize: {
|
|
32
35
|
control: { type: 'number' },
|
|
33
|
-
description: '
|
|
36
|
+
description: 'Tablet/desktop ring diameter in px (mobile always shrinks to 24px), or `{ base, md?, lg? }` for full control. Default `{ base: 24, md: 56 }` (Figma spec).',
|
|
34
37
|
},
|
|
35
38
|
href: { control: 'text', description: 'Navigation URL — renders as a Next.js Link with pointer cursor' },
|
|
36
39
|
tooltip: { control: 'text', description: 'Tooltip content for the question-mark icon' },
|
|
37
40
|
valueClassName: { control: 'text', description: 'Override className for the value text' },
|
|
41
|
+
titleTag: { control: false, description: 'Tag rendered in the title row after the title (Figma "status" variant)' },
|
|
42
|
+
icon: { control: false, description: 'Icon or image in a bordered square slot on the left (Figma "icon" variant)' },
|
|
38
43
|
},
|
|
39
44
|
} satisfies Meta<typeof DashboardInfoCard>
|
|
40
45
|
|
|
@@ -276,7 +281,6 @@ export const DeviceStatusesOverview: Story = {
|
|
|
276
281
|
showProgress
|
|
277
282
|
progressVariant="success"
|
|
278
283
|
percentageDisplay="plain"
|
|
279
|
-
progressSize={{ base: 24, md: 56 }}
|
|
280
284
|
/>
|
|
281
285
|
<DashboardInfoCard
|
|
282
286
|
title="Offline Devices"
|
|
@@ -285,7 +289,6 @@ export const DeviceStatusesOverview: Story = {
|
|
|
285
289
|
showProgress
|
|
286
290
|
progressVariant="error"
|
|
287
291
|
percentageDisplay="plain"
|
|
288
|
-
progressSize={{ base: 24, md: 56 }}
|
|
289
292
|
/>
|
|
290
293
|
<DashboardInfoCard
|
|
291
294
|
title="Pending Devices"
|
|
@@ -294,7 +297,6 @@ export const DeviceStatusesOverview: Story = {
|
|
|
294
297
|
showProgress
|
|
295
298
|
progressVariant="warning"
|
|
296
299
|
percentageDisplay="plain"
|
|
297
|
-
progressSize={{ base: 24, md: 56 }}
|
|
298
300
|
/>
|
|
299
301
|
<DashboardInfoCard
|
|
300
302
|
title="Archived Devices"
|
|
@@ -303,7 +305,6 @@ export const DeviceStatusesOverview: Story = {
|
|
|
303
305
|
showProgress
|
|
304
306
|
progressVariant="info"
|
|
305
307
|
percentageDisplay="plain"
|
|
306
|
-
progressSize={{ base: 24, md: 56 }}
|
|
307
308
|
/>
|
|
308
309
|
</div>
|
|
309
310
|
),
|
|
@@ -311,16 +312,17 @@ export const DeviceStatusesOverview: Story = {
|
|
|
311
312
|
docs: {
|
|
312
313
|
description: {
|
|
313
314
|
story:
|
|
314
|
-
'Matches the Figma "Devices Overview" — green/red/amber/grey rings with neutral percentages via `percentageDisplay="plain"
|
|
315
|
+
'Matches the Figma "Devices Overview" — green/red/amber/grey rings with neutral percentages via `percentageDisplay="plain"`. The responsive ring (24px on mobile, 56px from the `md` breakpoint up) is now the default — no `progressSize` needed.',
|
|
315
316
|
},
|
|
316
317
|
},
|
|
317
318
|
},
|
|
318
319
|
}
|
|
319
320
|
|
|
320
321
|
/**
|
|
321
|
-
* `progressSize` controls the ring diameter (stroke scales with
|
|
322
|
-
*
|
|
323
|
-
* `
|
|
322
|
+
* `progressSize` controls the tablet/desktop ring diameter (stroke scales with
|
|
323
|
+
* it); on mobile the ring always shrinks to the 24px Figma spec. Left: 24px.
|
|
324
|
+
* Right: the 56px default. Pass `{ base: n }` to pin one size at every
|
|
325
|
+
* breakpoint.
|
|
324
326
|
*/
|
|
325
327
|
export const ProgressSizeComparison: Story = {
|
|
326
328
|
render: () => (
|
|
@@ -347,6 +349,89 @@ export const ProgressSizeComparison: Story = {
|
|
|
347
349
|
),
|
|
348
350
|
}
|
|
349
351
|
|
|
352
|
+
/**
|
|
353
|
+
* Tag in the title row instead of a plain title — matches the Figma
|
|
354
|
+
* dashboard-card "status" variant (e.g. tickets by status).
|
|
355
|
+
*/
|
|
356
|
+
export const WithTitleTag: Story = {
|
|
357
|
+
args: {
|
|
358
|
+
value: 75,
|
|
359
|
+
titleTag: <Tag variant="outline" label="AI-Assistance" />,
|
|
360
|
+
},
|
|
361
|
+
parameters: {
|
|
362
|
+
docs: {
|
|
363
|
+
description: {
|
|
364
|
+
story:
|
|
365
|
+
'The Figma "status" variant: `titleTag` renders a `Tag` in the title row. Here without a `title`; combine with one for "TITLE + tag" (they sit in a 4px-gap row).',
|
|
366
|
+
},
|
|
367
|
+
},
|
|
368
|
+
},
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Title and tag together in the title row.
|
|
373
|
+
*/
|
|
374
|
+
export const WithTitleAndTag: Story = {
|
|
375
|
+
args: {
|
|
376
|
+
title: 'Tickets',
|
|
377
|
+
value: 75,
|
|
378
|
+
titleTag: <Tag variant="outline" label="Resolved" />,
|
|
379
|
+
},
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Icon in a bordered square slot on the left — matches the Figma
|
|
384
|
+
* dashboard-card "icon" variant (32px box on mobile, 56px from `md` up).
|
|
385
|
+
*/
|
|
386
|
+
export const WithIcon: Story = {
|
|
387
|
+
args: {
|
|
388
|
+
title: 'Endpoints',
|
|
389
|
+
value: 4046,
|
|
390
|
+
icon: <MonitorIcon />,
|
|
391
|
+
},
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* An `<img>` in the icon slot — the node is stretched to fill the slot's
|
|
396
|
+
* content area, so images work the same as icon components.
|
|
397
|
+
*/
|
|
398
|
+
export const WithImage: Story = {
|
|
399
|
+
args: {
|
|
400
|
+
title: 'Organization',
|
|
401
|
+
value: 'Flamingo',
|
|
402
|
+
icon: <img src="https://github.com/flamingo-stack.png" alt="Flamingo" className="rounded-[2px] object-cover" />,
|
|
403
|
+
},
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Every prop at once: icon slot, title + tag, sub-value, percentage,
|
|
408
|
+
* colored progress ring with wrap overflow, and navigation href.
|
|
409
|
+
*/
|
|
410
|
+
export const KitchenSink: Story = {
|
|
411
|
+
args: {
|
|
412
|
+
title: 'Devices',
|
|
413
|
+
titleTag: <Tag variant="outline" label="Live" />,
|
|
414
|
+
icon: <FaceSmile01Icon />,
|
|
415
|
+
value: 4046,
|
|
416
|
+
subValue: '12 sites',
|
|
417
|
+
percentage: 104,
|
|
418
|
+
showProgress: true,
|
|
419
|
+
progressVariant: 'warning',
|
|
420
|
+
percentageDisplay: 'tag',
|
|
421
|
+
progressOverflow: 'wrap',
|
|
422
|
+
progressSize: { base: 24, md: 56 },
|
|
423
|
+
href: '/devices',
|
|
424
|
+
},
|
|
425
|
+
parameters: {
|
|
426
|
+
docs: {
|
|
427
|
+
description: {
|
|
428
|
+
story:
|
|
429
|
+
'All props together: `icon`, `title` + `titleTag`, `value` + `subValue`, `percentage` as a warning `Tag` (`percentageDisplay="tag"`), a wrap-overflow warning ring (`progressOverflow="wrap"`), explicit responsive `progressSize`, and `href`. The `tooltip` prop (question-mark trigger) is shown separately in the WithTooltip story.',
|
|
430
|
+
},
|
|
431
|
+
},
|
|
432
|
+
},
|
|
433
|
+
}
|
|
434
|
+
|
|
350
435
|
/**
|
|
351
436
|
* Multiple cards in a dashboard grid layout.
|
|
352
437
|
*/
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/nextjs-vite'
|
|
2
2
|
import { within, userEvent } from 'storybook/test'
|
|
3
3
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
|
4
|
+
import * as DialogPrimitive from '@radix-ui/react-dialog'
|
|
4
5
|
import React, { useMemo } from 'react'
|
|
5
6
|
import {
|
|
6
7
|
ChatRuntimeContext,
|
|
@@ -14,6 +15,10 @@ import type {
|
|
|
14
15
|
} from '../components/chat/hooks/use-nats-chat-adapter'
|
|
15
16
|
import type { SlashCommandSummary } from '../components/chat/hooks/use-slash-commands'
|
|
16
17
|
import type { DialogItem } from '../components/chat/types/component.types'
|
|
18
|
+
import type {
|
|
19
|
+
UnifiedChatState,
|
|
20
|
+
UnifiedChatMessage,
|
|
21
|
+
} from '../components/chat/types/unified-chat-state.types'
|
|
17
22
|
import type { MingoQuickAction } from '../components/chat/mingo-welcome'
|
|
18
23
|
import {
|
|
19
24
|
BracketCurlyIcon,
|
|
@@ -775,3 +780,157 @@ export const AgentChipsWinOverHost: Story = {
|
|
|
775
780
|
},
|
|
776
781
|
},
|
|
777
782
|
}
|
|
783
|
+
|
|
784
|
+
// =============================================================================
|
|
785
|
+
// 10. Scripted Mingo conversation — Figma "Flamingo Website" node 4050:23837
|
|
786
|
+
// =============================================================================
|
|
787
|
+
|
|
788
|
+
// Simple gradient user-avatar as a self-contained data URI — no external asset
|
|
789
|
+
// dependency in the story. Roman's `user` bubble shows it via `message.avatar`.
|
|
790
|
+
const ROMAN_AVATAR =
|
|
791
|
+
'data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPSc0MCcgaGVpZ2h0PSc0MCc+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSdnJyB4MT0nMCcgeTE9JzAnIHgyPScxJyB5Mj0nMSc+PHN0b3Agb2Zmc2V0PScwJyBzdG9wLWNvbG9yPScjNWVmYWYwJy8+PHN0b3Agb2Zmc2V0PScxJyBzdG9wLWNvbG9yPScjZmY2YjlkJy8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PGNpcmNsZSBjeD0nMjAnIGN5PScyMCcgcj0nMjAnIGZpbGw9J3VybCgjZyknLz48Y2lyY2xlIGN4PScyMCcgY3k9JzE2JyByPSc2LjUnIGZpbGw9JyMxNjE2MTYnIG9wYWNpdHk9JzAuODUnLz48cGF0aCBkPSdNOCAzNGMxLjUtNiA2LTkgMTItOXMxMC41IDMgMTIgOXonIGZpbGw9JyMxNjE2MTYnIG9wYWNpdHk9JzAuODUnLz48L3N2Zz4='
|
|
792
|
+
|
|
793
|
+
// Fixed timestamp so the bubbles render a stable "2:47 PM" (matches the Figma
|
|
794
|
+
// mock) instead of the current clock — 14:47 local → 2:47 PM.
|
|
795
|
+
const TS_247 = new Date(2024, 0, 1, 14, 47, 0)
|
|
796
|
+
|
|
797
|
+
// The exact PowerShell command from the Figma command block. Passed as
|
|
798
|
+
// `toolTitle` (not a `command`/`script` parameter) so `getToolCallTitle`
|
|
799
|
+
// renders this whole string verbatim — including the backticks — as the
|
|
800
|
+
// collapsed command-block preview line.
|
|
801
|
+
const RUN_SCRIPT_TITLE =
|
|
802
|
+
'`run_script` -- PowerShell: `Get-LocalUser | Where-Object { $_.PasswordRequired -eq $false -or $_.PasswordLastSet -lt (Get-Date).AddDays(-90) }`'
|
|
803
|
+
|
|
804
|
+
const SCRIPTED_MESSAGES: UnifiedChatMessage[] = [
|
|
805
|
+
{
|
|
806
|
+
id: 'm1',
|
|
807
|
+
role: 'assistant',
|
|
808
|
+
name: 'Mingo',
|
|
809
|
+
authorType: 'mingo', // cyan name (Figma flamingo-colors/cyan)
|
|
810
|
+
content: 'How can I help?',
|
|
811
|
+
timestamp: TS_247,
|
|
812
|
+
},
|
|
813
|
+
{
|
|
814
|
+
id: 'm2',
|
|
815
|
+
role: 'user',
|
|
816
|
+
name: 'Roman Smith',
|
|
817
|
+
authorType: 'admin', // yellow name (Figma open-colors/yellow)
|
|
818
|
+
avatar: ROMAN_AVATAR,
|
|
819
|
+
content: 'Check machines with weak password',
|
|
820
|
+
timestamp: TS_247,
|
|
821
|
+
},
|
|
822
|
+
{
|
|
823
|
+
id: 'm3',
|
|
824
|
+
role: 'assistant',
|
|
825
|
+
name: 'Mingo',
|
|
826
|
+
authorType: 'mingo',
|
|
827
|
+
content: '', // body lives entirely in `segments`
|
|
828
|
+
timestamp: TS_247,
|
|
829
|
+
segments: [
|
|
830
|
+
{
|
|
831
|
+
type: 'tool_execution',
|
|
832
|
+
data: {
|
|
833
|
+
type: 'EXECUTING_TOOL', // → DotsLoader status + collapsed command block
|
|
834
|
+
integratedToolType: 'OPENFRAME',
|
|
835
|
+
toolFunction: 'run_script',
|
|
836
|
+
toolTitle: RUN_SCRIPT_TITLE,
|
|
837
|
+
parameters: {},
|
|
838
|
+
},
|
|
839
|
+
},
|
|
840
|
+
],
|
|
841
|
+
},
|
|
842
|
+
]
|
|
843
|
+
|
|
844
|
+
/**
|
|
845
|
+
* Fully-stubbed `UnifiedChatState` factory. This is the "inject a static Mingo
|
|
846
|
+
* thread" path (`EmbeddableChat`'s `mingoState` prop): the panel renders this
|
|
847
|
+
* object directly and opens NO NATS subscription. Every action is a no-op so
|
|
848
|
+
* the scene stays read-only. `isLoading: true` + `streamingPhase: 'thinking'`
|
|
849
|
+
* drives the trailing "Mingo is working" dots loader under the command block.
|
|
850
|
+
*/
|
|
851
|
+
function createScriptedMingoState(): UnifiedChatState {
|
|
852
|
+
const noop = () => {}
|
|
853
|
+
const asyncNoop = async () => {}
|
|
854
|
+
return {
|
|
855
|
+
messages: SCRIPTED_MESSAGES,
|
|
856
|
+
isLoading: true,
|
|
857
|
+
streamingPhase: 'thinking',
|
|
858
|
+
sendMessage: asyncNoop,
|
|
859
|
+
stopMessage: noop,
|
|
860
|
+
clearMessages: noop,
|
|
861
|
+
discussRef: noop,
|
|
862
|
+
displayRef: noop,
|
|
863
|
+
// Footer model chip — anthropic mark + "Claude Opus 4.1" (Figma footer).
|
|
864
|
+
currentProvider: 'anthropic',
|
|
865
|
+
currentModelLabel: 'Claude Opus 4.1',
|
|
866
|
+
currentContextWindowMaxTokens: null,
|
|
867
|
+
currentInputTokens: null,
|
|
868
|
+
currentOutputTokens: null,
|
|
869
|
+
currentCacheHitRatePct: null,
|
|
870
|
+
currentUsageBreakdown: null,
|
|
871
|
+
// A single active dialog titled "New Chat" so the header shows that title
|
|
872
|
+
// and the ⋯ menu (rename/archive) renders like the mock.
|
|
873
|
+
dialogs: [{ id: 'd-scripted', title: 'New Chat' }],
|
|
874
|
+
activeDialogId: 'd-scripted',
|
|
875
|
+
selectDialog: noop,
|
|
876
|
+
startNewDialog: async () => null,
|
|
877
|
+
deleteDialog: asyncNoop,
|
|
878
|
+
renameDialog: asyncNoop,
|
|
879
|
+
archiveDialog: asyncNoop,
|
|
880
|
+
isDialogsLoading: false,
|
|
881
|
+
dialogsError: false,
|
|
882
|
+
reloadDialogs: noop,
|
|
883
|
+
isMessagesLoading: false,
|
|
884
|
+
hasMoreDialogs: false,
|
|
885
|
+
loadMoreDialogs: asyncNoop,
|
|
886
|
+
hasMoreMessages: false,
|
|
887
|
+
loadMoreMessages: asyncNoop,
|
|
888
|
+
approveRequest: asyncNoop,
|
|
889
|
+
rejectRequest: asyncNoop,
|
|
890
|
+
dialogTokenUsage: null,
|
|
891
|
+
connectionState: 'connected',
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
/**
|
|
896
|
+
* Scripted Mingo conversation reproducing Figma "Flamingo Website" node
|
|
897
|
+
* 4050:23837 (the marketing hero chat panel): Mingo greeting → Roman Smith's
|
|
898
|
+
* "Check machines with weak password" → a PowerShell `run_script` command block
|
|
899
|
+
* (executing) → Mingo working (dots).
|
|
900
|
+
*
|
|
901
|
+
* Rendered via the `mingoState` injection path with `shell="none"`, so the chat
|
|
902
|
+
* body sits inline inside our own 440-wide framed box (matching the Figma
|
|
903
|
+
* frame) with zero backend — no NATS, no fetch, fully static + read-only.
|
|
904
|
+
*
|
|
905
|
+
* Known deviations from the mock (the shipped component is the source of
|
|
906
|
+
* truth): author names render with a trailing ":" ; the command-block icon is
|
|
907
|
+
* the OpenFrame mark (the `ToolIcon` registry has no PowerShell glyph); the
|
|
908
|
+
* composer placeholder reads "Ask a question…"; and the trailing loader is the
|
|
909
|
+
* component's dots+phrase streaming indicator.
|
|
910
|
+
*/
|
|
911
|
+
export const ScriptedConversation: Story = {
|
|
912
|
+
parameters: { layout: 'fullscreen' },
|
|
913
|
+
render: () => (
|
|
914
|
+
// `shell="none"` expects the HOST to own the Radix Dialog context (in the
|
|
915
|
+
// app, AppLayoutDrawer does). Storybook is that host here, so we supply a
|
|
916
|
+
// headless `Dialog.Root` — it renders no DOM and does not portal, so the
|
|
917
|
+
// panel's visually-hidden `Dialog.Title` gets its context without altering
|
|
918
|
+
// the inline layout.
|
|
919
|
+
<DialogPrimitive.Root open>
|
|
920
|
+
<div className="fixed inset-0 z-[10] flex items-center justify-center bg-ods-bg p-6">
|
|
921
|
+
<div className="w-[440px] h-[560px] overflow-hidden rounded-[6px] border border-ods-border shadow-2xl">
|
|
922
|
+
<EmbeddableChat
|
|
923
|
+
shell="none"
|
|
924
|
+
defaultOpen
|
|
925
|
+
defaultActiveMode="mingo"
|
|
926
|
+
showInternalTrigger={false}
|
|
927
|
+
previewMode
|
|
928
|
+
mingoState={createScriptedMingoState()}
|
|
929
|
+
mingoDialogCapabilities={{ canRename: true, canArchive: true }}
|
|
930
|
+
/>
|
|
931
|
+
</div>
|
|
932
|
+
</div>
|
|
933
|
+
</DialogPrimitive.Root>
|
|
934
|
+
),
|
|
935
|
+
args: {},
|
|
936
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/nextjs-vite";
|
|
2
|
+
import type React from "react";
|
|
3
|
+
|
|
4
|
+
import { ErrorMessageDisplay } from "../components/chat/error-message-display";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Inline error card for `error` message segments — mono uppercase title
|
|
8
|
+
* with an alert icon; optional details expand under the chevron.
|
|
9
|
+
* `type` tints the icon: error (red) / warning (yellow) / info (grey).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const plainDecorator = (Story: React.ComponentType) => (
|
|
13
|
+
<div style={{ maxWidth: 640, background: "var(--color-bg)" }} className="p-4">
|
|
14
|
+
<Story />
|
|
15
|
+
</div>
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
const meta = {
|
|
19
|
+
title: "Chat/ErrorMessageDisplay",
|
|
20
|
+
component: ErrorMessageDisplay,
|
|
21
|
+
tags: ["autodocs"],
|
|
22
|
+
parameters: {
|
|
23
|
+
docs: {
|
|
24
|
+
description: {
|
|
25
|
+
component:
|
|
26
|
+
"Renders an `error` message segment — collapsed mono title row, expandable details. Icon tint follows type: error / warning / info.",
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
decorators: [plainDecorator],
|
|
31
|
+
} satisfies Meta<typeof ErrorMessageDisplay>;
|
|
32
|
+
|
|
33
|
+
export default meta;
|
|
34
|
+
type Story = StoryObj<typeof meta>;
|
|
35
|
+
|
|
36
|
+
/** Error with expandable details. */
|
|
37
|
+
export const WithDetails: Story = {
|
|
38
|
+
args: {
|
|
39
|
+
title: "Tool execution failed",
|
|
40
|
+
details:
|
|
41
|
+
"run_script exited with code 1: brew not found on this endpoint. Install Homebrew first or use the direct .dmg fallback.",
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/** Title only — no chevron, row is not clickable. */
|
|
46
|
+
export const TitleOnly: Story = {
|
|
47
|
+
args: { title: "Stream disconnected — retrying" },
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/** Warning tint. */
|
|
51
|
+
export const Warning: Story = {
|
|
52
|
+
args: {
|
|
53
|
+
type: "warning",
|
|
54
|
+
title: "Approval expired",
|
|
55
|
+
details: "The approval request timed out after 15 minutes and was cancelled automatically.",
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/** Info tint. */
|
|
60
|
+
export const Info: Story = {
|
|
61
|
+
args: {
|
|
62
|
+
type: "info",
|
|
63
|
+
title: "Session resumed",
|
|
64
|
+
details: "Reconnected to the stream; missed chunks were replayed from history.",
|
|
65
|
+
},
|
|
66
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/nextjs-vite";
|
|
2
|
+
import type React from "react";
|
|
3
|
+
|
|
4
|
+
import { ThinkingDisplay } from "../components/chat/thinking-display";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Collapsible "Thought" block for `thinking` message segments — muted
|
|
8
|
+
* markdown inside a bordered card, one line collapsed, expandable via the
|
|
9
|
+
* chevron. While the segment is still streaming the label reads "Thinking"
|
|
10
|
+
* and the collapse transition is disabled.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const SHORT_THOUGHT =
|
|
14
|
+
"The user wants Slack installed. Homebrew is present, so `brew install --cask slack` is the cleanest path.";
|
|
15
|
+
|
|
16
|
+
const LONG_THOUGHT = `The user asked to install Slack on their Mac.
|
|
17
|
+
|
|
18
|
+
**Options considered:**
|
|
19
|
+
|
|
20
|
+
1. Direct \`.dmg\` download — needs manual mounting and copying, hard to automate.
|
|
21
|
+
2. Homebrew Cask — \`brew install --cask slack\`, idempotent and scriptable.
|
|
22
|
+
3. Mac App Store via \`mas\` — requires the user to be signed in to the store.
|
|
23
|
+
|
|
24
|
+
Homebrew is already present on this endpoint (verified in the device inventory), so option 2 is the cleanest path. The install needs client approval because it modifies installed software.`;
|
|
25
|
+
|
|
26
|
+
const plainDecorator = (Story: React.ComponentType) => (
|
|
27
|
+
<div style={{ maxWidth: 640, background: "var(--color-bg)" }} className="p-4">
|
|
28
|
+
<Story />
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const meta = {
|
|
33
|
+
title: "Chat/ThinkingDisplay",
|
|
34
|
+
component: ThinkingDisplay,
|
|
35
|
+
tags: ["autodocs"],
|
|
36
|
+
parameters: {
|
|
37
|
+
docs: {
|
|
38
|
+
description: {
|
|
39
|
+
component:
|
|
40
|
+
"Renders a `thinking` message segment — collapsed single-line 'Thought' card that expands to the full muted-markdown reasoning. Streaming state shows 'Thinking' with a dots loader.",
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
decorators: [plainDecorator],
|
|
45
|
+
} satisfies Meta<typeof ThinkingDisplay>;
|
|
46
|
+
|
|
47
|
+
export default meta;
|
|
48
|
+
type Story = StoryObj<typeof meta>;
|
|
49
|
+
|
|
50
|
+
/** Finished thought — collapsed to one line, expandable. */
|
|
51
|
+
export const Thought: Story = {
|
|
52
|
+
args: { text: SHORT_THOUGHT },
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/** Streaming — "Thinking" label, transition disabled while tokens arrive. */
|
|
56
|
+
export const Streaming: Story = {
|
|
57
|
+
args: { text: SHORT_THOUGHT, isStreaming: true },
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/** Long markdown thought — headings, list, inline code; expand to read. */
|
|
61
|
+
export const LongMarkdown: Story = {
|
|
62
|
+
args: { text: LONG_THOUGHT },
|
|
63
|
+
};
|