@stealthscale/component-messaging 0.1.0 → 0.2.2

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.
@@ -0,0 +1,1237 @@
1
+ import { t as __exportAll } from "../../rolldown-runtime-D7D4PA-g.js";
2
+ import { C as Toolbar, E as Input, T as Root$1, _ as Typing, b as JumpTrigger, c as Status$1, d as Footer, f as Content, g as conversation_exports, l as Root, m as Avatar$1, p as Bubble, s as message_exports, t as groupTurns, u as Header, v as Root$2, w as Submit, x as Content$1, y as useConversation } from "../../shared-B0H4dgkB.js";
3
+ import { c } from "react/compiler-runtime";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ import { Fragment as Fragment$1, useEffect, useState } from "react";
6
+ import { Details } from "@stealthscale/component-disclosure";
7
+ import { Room, specimen, useWords } from "@stealthscale/specimen";
8
+ import { ArrowDownIcon, ArrowUpIcon, CheckCheckIcon, CheckIcon, ChevronRightIcon, SparklesIcon, SquareIcon } from "lucide-react";
9
+ import { Avatar } from "@stealthscale/component-media";
10
+ import { Code, Strong, Text } from "@stealthscale/component-typography";
11
+ import { Divider, Stack } from "@stealthscale/component-layout";
12
+ import { JsonTreeView, Markdown } from "@stealthscale/component-content";
13
+ import { Badge, Status, Timestamp } from "@stealthscale/component-data";
14
+ import { Listbox, useListCollection } from "@stealthscale/component-collections";
15
+ //#region src/conversation/examples/assistant.example.tsx
16
+ var assistant_example_exports = /* @__PURE__ */ __exportAll({
17
+ Assistant: () => Assistant,
18
+ source: () => source$4
19
+ });
20
+ const STEP = 12;
21
+ const TOOL = 20;
22
+ const CALL = {
23
+ arguments: {
24
+ metrics: [
25
+ "revenue",
26
+ "churn",
27
+ "accounts"
28
+ ],
29
+ quarter: "2026-Q3"
30
+ },
31
+ result: {
32
+ accounts: 312,
33
+ churn: .018,
34
+ revenue: 42e5
35
+ }
36
+ };
37
+ const PALETTES = {
38
+ cancelled: "neutral",
39
+ done: "success",
40
+ running: "info"
41
+ };
42
+ function thinking(summary, reasoning) {
43
+ return /* @__PURE__ */ jsxs(Details.Root, {
44
+ size: "sm",
45
+ variant: "plain",
46
+ children: [/* @__PURE__ */ jsxs(Details.Summary, { children: [/* @__PURE__ */ jsx(Details.Indicator, { children: /* @__PURE__ */ jsx(ChevronRightIcon, {}) }), summary] }), /* @__PURE__ */ jsx(Details.Content, { children: /* @__PURE__ */ jsx(Text, {
47
+ size: "sm",
48
+ tone: "muted",
49
+ children: reasoning
50
+ }) })]
51
+ });
52
+ }
53
+ function called(call, name, state, label) {
54
+ return /* @__PURE__ */ jsxs(Details.Root, {
55
+ size: "sm",
56
+ children: [/* @__PURE__ */ jsxs(Details.Summary, { children: [/* @__PURE__ */ jsx(Details.Indicator, { children: /* @__PURE__ */ jsx(ChevronRightIcon, {}) }), /* @__PURE__ */ jsxs(Stack, {
57
+ as: "span",
58
+ direction: "row",
59
+ gap: "sm",
60
+ wrap: true,
61
+ children: [/* @__PURE__ */ jsx(Code, { children: name }), /* @__PURE__ */ jsxs(Status.Root, {
62
+ palette: PALETTES[call],
63
+ children: [/* @__PURE__ */ jsx(Status.Indicator, {}), state]
64
+ })]
65
+ })] }), /* @__PURE__ */ jsx(Details.Content, { children: /* @__PURE__ */ jsx(JsonTreeView.Root, {
66
+ data: CALL,
67
+ defaultExpandedDepth: 2,
68
+ children: /* @__PURE__ */ jsx(JsonTreeView.Tree, {
69
+ "aria-label": label,
70
+ arrow: /* @__PURE__ */ jsx(ChevronRightIcon, {})
71
+ })
72
+ }) })]
73
+ });
74
+ }
75
+ function Assistant() {
76
+ const $ = c(90);
77
+ const { t } = useWords("conversation");
78
+ const conversation = useConversation();
79
+ const answer = t("answer");
80
+ const [question, setQuestion] = useState();
81
+ const [tick, setTick] = useState(Number.POSITIVE_INFINITY);
82
+ const [stopped, setStopped] = useState(false);
83
+ const busy = !stopped && tick < TOOL + Math.ceil(answer.length / STEP);
84
+ const length = Math.min(answer.length, Math.max(0, tick - TOOL) * STEP);
85
+ const call = tick >= TOOL ? "done" : stopped ? "cancelled" : "running";
86
+ let t0;
87
+ if ($[0] !== busy || $[1] !== setTick) {
88
+ t0 = () => {
89
+ const timer = busy ? setInterval(() => {
90
+ setTick(_temp$3);
91
+ }, 40) : void 0;
92
+ return () => {
93
+ clearInterval(timer);
94
+ };
95
+ };
96
+ $[0] = busy;
97
+ $[1] = setTick;
98
+ $[2] = t0;
99
+ } else t0 = $[2];
100
+ let t1;
101
+ if ($[3] !== busy) {
102
+ t1 = [busy];
103
+ $[3] = busy;
104
+ $[4] = t1;
105
+ } else t1 = $[4];
106
+ useEffect(t0, t1);
107
+ const T0 = Stack;
108
+ const t2 = "sm";
109
+ const t3 = conversation_exports;
110
+ const t4 = "sm";
111
+ let t5;
112
+ if ($[5] !== t) {
113
+ t5 = t("assistantLabel");
114
+ $[5] = t;
115
+ $[6] = t5;
116
+ } else t5 = $[6];
117
+ const t6 = "sm";
118
+ const t7 = conversation_exports;
119
+ let t8;
120
+ if ($[7] !== t) {
121
+ t8 = t("you");
122
+ $[7] = t;
123
+ $[8] = t8;
124
+ } else t8 = $[8];
125
+ let t9;
126
+ if ($[9] !== question || $[10] !== t) {
127
+ t9 = question ?? t("question");
128
+ $[9] = question;
129
+ $[10] = t;
130
+ $[11] = t9;
131
+ } else t9 = $[11];
132
+ let t10;
133
+ if ($[12] !== t9) {
134
+ t10 = /* @__PURE__ */ jsx(Content, { children: /* @__PURE__ */ jsx(Bubble, { children: t9 }) });
135
+ $[12] = t9;
136
+ $[13] = t10;
137
+ } else t10 = $[13];
138
+ let t11;
139
+ if ($[14] !== t10 || $[15] !== t8) {
140
+ t11 = /* @__PURE__ */ jsx(Root, {
141
+ align: "end",
142
+ "aria-label": t8,
143
+ look: "solid",
144
+ palette: "primary",
145
+ children: t10
146
+ });
147
+ $[14] = t10;
148
+ $[15] = t8;
149
+ $[16] = t11;
150
+ } else t11 = $[16];
151
+ const t12 = message_exports;
152
+ const t13 = "plain";
153
+ let t14;
154
+ if ($[17] === Symbol.for("react.memo_cache_sentinel")) {
155
+ t14 = /* @__PURE__ */ jsx(Avatar$1, { children: /* @__PURE__ */ jsx(Avatar.Root, {
156
+ "aria-hidden": true,
157
+ size: "sm",
158
+ children: /* @__PURE__ */ jsx(Avatar.Fallback, { children: /* @__PURE__ */ jsx(SparklesIcon, {}) })
159
+ }) });
160
+ $[17] = t14;
161
+ } else t14 = $[17];
162
+ const t15 = message_exports;
163
+ let t16;
164
+ if ($[18] !== t) {
165
+ t16 = t("assistant");
166
+ $[18] = t;
167
+ $[19] = t16;
168
+ } else t16 = $[19];
169
+ let t17;
170
+ if ($[20] !== t16) {
171
+ t17 = /* @__PURE__ */ jsx(Header, { children: /* @__PURE__ */ jsx(Strong, { children: t16 }) });
172
+ $[20] = t16;
173
+ $[21] = t17;
174
+ } else t17 = $[21];
175
+ let t18;
176
+ if ($[22] !== t) {
177
+ t18 = thinking(t("thought"), t("reasoning"));
178
+ $[22] = t;
179
+ $[23] = t18;
180
+ } else t18 = $[23];
181
+ let t19;
182
+ if ($[24] !== call || $[25] !== t) {
183
+ t19 = called(call, t("tool"), t(call), t("call"));
184
+ $[24] = call;
185
+ $[25] = t;
186
+ $[26] = t19;
187
+ } else t19 = $[26];
188
+ const t20 = length > 0 ? /* @__PURE__ */ jsx(Bubble, { children: /* @__PURE__ */ jsx(Markdown, {
189
+ headingLevel: 3,
190
+ size: "sm",
191
+ source: answer.slice(0, length),
192
+ streaming: busy
193
+ }) }) : null;
194
+ let t21;
195
+ if ($[27] !== stopped || $[28] !== t) {
196
+ t21 = stopped ? /* @__PURE__ */ jsx(Footer, { children: t("stopped") }) : null;
197
+ $[27] = stopped;
198
+ $[28] = t;
199
+ $[29] = t21;
200
+ } else t21 = $[29];
201
+ let t22;
202
+ if ($[30] !== t15.Content || $[31] !== t17 || $[32] !== t18 || $[33] !== t19 || $[34] !== t20 || $[35] !== t21) {
203
+ t22 = /* @__PURE__ */ jsxs(t15.Content, { children: [
204
+ t17,
205
+ t18,
206
+ t19,
207
+ t20,
208
+ t21
209
+ ] });
210
+ $[30] = t15.Content;
211
+ $[31] = t17;
212
+ $[32] = t18;
213
+ $[33] = t19;
214
+ $[34] = t20;
215
+ $[35] = t21;
216
+ $[36] = t22;
217
+ } else t22 = $[36];
218
+ let t23;
219
+ if ($[37] !== busy || $[38] !== t12.Root || $[39] !== t14 || $[40] !== t22) {
220
+ t23 = /* @__PURE__ */ jsxs(t12.Root, {
221
+ "aria-busy": busy,
222
+ look: t13,
223
+ children: [t14, t22]
224
+ });
225
+ $[37] = busy;
226
+ $[38] = t12.Root;
227
+ $[39] = t14;
228
+ $[40] = t22;
229
+ $[41] = t23;
230
+ } else t23 = $[41];
231
+ let t24;
232
+ if ($[42] !== t11 || $[43] !== t23 || $[44] !== t7.Content) {
233
+ t24 = /* @__PURE__ */ jsxs(t7.Content, { children: [t11, t23] });
234
+ $[42] = t11;
235
+ $[43] = t23;
236
+ $[44] = t7.Content;
237
+ $[45] = t24;
238
+ } else t24 = $[45];
239
+ let t25;
240
+ if ($[46] !== t) {
241
+ t25 = t("jump");
242
+ $[46] = t;
243
+ $[47] = t25;
244
+ } else t25 = $[47];
245
+ let t26;
246
+ if ($[48] === Symbol.for("react.memo_cache_sentinel")) {
247
+ t26 = /* @__PURE__ */ jsx(ArrowDownIcon, { size: "1em" });
248
+ $[48] = t26;
249
+ } else t26 = $[48];
250
+ let t27;
251
+ if ($[49] !== t25) {
252
+ t27 = /* @__PURE__ */ jsx(JumpTrigger, {
253
+ label: t25,
254
+ children: t26
255
+ });
256
+ $[49] = t25;
257
+ $[50] = t27;
258
+ } else t27 = $[50];
259
+ let t28;
260
+ if ($[51] !== conversation || $[52] !== t24 || $[53] !== t27 || $[54] !== t3.Root || $[55] !== t5) {
261
+ t28 = /* @__PURE__ */ jsxs(t3.Root, {
262
+ conversation,
263
+ inset: t4,
264
+ label: t5,
265
+ maxHeight: t6,
266
+ children: [t24, t27]
267
+ });
268
+ $[51] = conversation;
269
+ $[52] = t24;
270
+ $[53] = t27;
271
+ $[54] = t3.Root;
272
+ $[55] = t5;
273
+ $[56] = t28;
274
+ } else t28 = $[56];
275
+ let t29;
276
+ if ($[57] !== setStopped) {
277
+ t29 = () => {
278
+ setStopped(true);
279
+ };
280
+ $[57] = setStopped;
281
+ $[58] = t29;
282
+ } else t29 = $[58];
283
+ let t30;
284
+ if ($[59] !== conversation || $[60] !== setQuestion || $[61] !== setStopped || $[62] !== setTick) {
285
+ t30 = (asked) => {
286
+ setQuestion(asked);
287
+ setStopped(false);
288
+ setTick(0);
289
+ conversation.scrollToEnd();
290
+ };
291
+ $[59] = conversation;
292
+ $[60] = setQuestion;
293
+ $[61] = setStopped;
294
+ $[62] = setTick;
295
+ $[63] = t30;
296
+ } else t30 = $[63];
297
+ let t31;
298
+ if ($[64] !== t) {
299
+ t31 = t("ask");
300
+ $[64] = t;
301
+ $[65] = t31;
302
+ } else t31 = $[65];
303
+ let t32;
304
+ if ($[66] !== t) {
305
+ t32 = t("askPlaceholder");
306
+ $[66] = t;
307
+ $[67] = t32;
308
+ } else t32 = $[67];
309
+ let t33;
310
+ if ($[68] !== t31 || $[69] !== t32) {
311
+ t33 = /* @__PURE__ */ jsx(Input, {
312
+ label: t31,
313
+ placeholder: t32
314
+ });
315
+ $[68] = t31;
316
+ $[69] = t32;
317
+ $[70] = t33;
318
+ } else t33 = $[70];
319
+ let t34;
320
+ if ($[71] !== t) {
321
+ t34 = t("send");
322
+ $[71] = t;
323
+ $[72] = t34;
324
+ } else t34 = $[72];
325
+ let t35;
326
+ if ($[73] === Symbol.for("react.memo_cache_sentinel")) {
327
+ t35 = /* @__PURE__ */ jsx(SquareIcon, { size: "1em" });
328
+ $[73] = t35;
329
+ } else t35 = $[73];
330
+ let t36;
331
+ if ($[74] !== t) {
332
+ t36 = t("stop");
333
+ $[74] = t;
334
+ $[75] = t36;
335
+ } else t36 = $[75];
336
+ let t37;
337
+ if ($[76] === Symbol.for("react.memo_cache_sentinel")) {
338
+ t37 = /* @__PURE__ */ jsx(ArrowUpIcon, { size: "1em" });
339
+ $[76] = t37;
340
+ } else t37 = $[76];
341
+ let t38;
342
+ if ($[77] !== t34 || $[78] !== t36) {
343
+ t38 = /* @__PURE__ */ jsx(Toolbar, { children: /* @__PURE__ */ jsx(Submit, {
344
+ label: t34,
345
+ stopIcon: t35,
346
+ stopLabel: t36,
347
+ children: t37
348
+ }) });
349
+ $[77] = t34;
350
+ $[78] = t36;
351
+ $[79] = t38;
352
+ } else t38 = $[79];
353
+ let t39;
354
+ if ($[80] !== busy || $[81] !== t29 || $[82] !== t30 || $[83] !== t33 || $[84] !== t38) {
355
+ t39 = /* @__PURE__ */ jsxs(Root$1, {
356
+ busy,
357
+ onStop: t29,
358
+ onSubmit: t30,
359
+ children: [t33, t38]
360
+ });
361
+ $[80] = busy;
362
+ $[81] = t29;
363
+ $[82] = t30;
364
+ $[83] = t33;
365
+ $[84] = t38;
366
+ $[85] = t39;
367
+ } else t39 = $[85];
368
+ let t40;
369
+ if ($[86] !== T0 || $[87] !== t28 || $[88] !== t39) {
370
+ t40 = /* @__PURE__ */ jsxs(T0, {
371
+ gap: t2,
372
+ children: [t28, t39]
373
+ });
374
+ $[86] = T0;
375
+ $[87] = t28;
376
+ $[88] = t39;
377
+ $[89] = t40;
378
+ } else t40 = $[89];
379
+ return t40;
380
+ }
381
+ function _temp$3(at) {
382
+ return at + 1;
383
+ }
384
+ const source$4 = "import { type ReactElement, useEffect, useState } from \"react\";\n\nimport {\n ArrowDownIcon,\n ArrowUpIcon,\n ChevronRightIcon,\n SparklesIcon,\n SquareIcon,\n} from \"lucide-react\";\n\nimport { JsonTreeView, Markdown } from \"@stealthscale/component-content\";\nimport { Status } from \"@stealthscale/component-data\";\nimport { Details } from \"@stealthscale/component-disclosure\";\nimport { Stack } from \"@stealthscale/component-layout\";\nimport { Avatar } from \"@stealthscale/component-media\";\nimport { Code, Strong, Text } from \"@stealthscale/component-typography\";\nimport { useWords } from \"@stealthscale/specimen\";\n\nimport { Composer, Conversation, Message } from \"@stealthscale/component-messaging\";\n\nconst STEP = 12;\n\nconst TOOL = 20;\n\nconst CALL = {\n arguments: { metrics: [\"revenue\", \"churn\", \"accounts\"], quarter: \"2026-Q3\" },\n result: { accounts: 312, churn: 0.018, revenue: 4_200_000 },\n};\n\nconst PALETTES = { cancelled: \"neutral\", done: \"success\", running: \"info\" } as const;\n\ntype Call = keyof typeof PALETTES;\n\nfunction thinking(summary: string, reasoning: string): ReactElement {\n return (\n <Details.Root size=\"sm\" variant=\"plain\">\n <Details.Summary>\n <Details.Indicator>\n <ChevronRightIcon />\n </Details.Indicator>\n {summary}\n </Details.Summary>\n <Details.Content>\n <Text size=\"sm\" tone=\"muted\">\n {reasoning}\n </Text>\n </Details.Content>\n </Details.Root>\n );\n}\n\nfunction called(call: Call, name: string, state: string, label: string): ReactElement {\n return (\n <Details.Root size=\"sm\">\n <Details.Summary>\n <Details.Indicator>\n <ChevronRightIcon />\n </Details.Indicator>\n <Stack as=\"span\" direction=\"row\" gap=\"sm\" wrap>\n <Code>{name}</Code>\n <Status.Root palette={PALETTES[call]}>\n <Status.Indicator />\n {state}\n </Status.Root>\n </Stack>\n </Details.Summary>\n <Details.Content>\n <JsonTreeView.Root data={CALL} defaultExpandedDepth={2}>\n <JsonTreeView.Tree aria-label={label} arrow={<ChevronRightIcon />} />\n </JsonTreeView.Root>\n </Details.Content>\n </Details.Root>\n );\n}\n\nexport function Assistant(): ReactElement {\n const { t } = useWords(\"conversation\");\n const conversation = Conversation.useConversation();\n const answer = t(\"answer\");\n const [question, setQuestion] = useState<string | undefined>();\n const [tick, setTick] = useState(Number.POSITIVE_INFINITY);\n const [stopped, setStopped] = useState(false);\n const busy = !stopped && tick < TOOL + Math.ceil(answer.length / STEP);\n const length = Math.min(answer.length, Math.max(0, tick - TOOL) * STEP);\n const call: Call = tick >= TOOL ? \"done\" : stopped ? \"cancelled\" : \"running\";\n\n useEffect(() => {\n const timer = busy\n ? setInterval(() => {\n setTick((at) => at + 1);\n }, 40)\n : undefined;\n\n return (): void => {\n clearInterval(timer);\n };\n }, [busy]);\n\n return (\n <Stack gap=\"sm\">\n <Conversation.Root\n conversation={conversation}\n inset=\"sm\"\n label={t(\"assistantLabel\")}\n maxHeight=\"sm\"\n >\n <Conversation.Content>\n <Message.Root align=\"end\" aria-label={t(\"you\")} look=\"solid\" palette=\"primary\">\n <Message.Content>\n <Message.Bubble>{question ?? t(\"question\")}</Message.Bubble>\n </Message.Content>\n </Message.Root>\n <Message.Root aria-busy={busy} look=\"plain\">\n <Message.Avatar>\n <Avatar.Root aria-hidden size=\"sm\">\n <Avatar.Fallback>\n <SparklesIcon />\n </Avatar.Fallback>\n </Avatar.Root>\n </Message.Avatar>\n <Message.Content>\n <Message.Header>\n <Strong>{t(\"assistant\")}</Strong>\n </Message.Header>\n {thinking(t(\"thought\"), t(\"reasoning\"))}\n {called(call, t(\"tool\"), t(call), t(\"call\"))}\n {length > 0 ? (\n <Message.Bubble>\n <Markdown\n headingLevel={3}\n size=\"sm\"\n source={answer.slice(0, length)}\n streaming={busy}\n />\n </Message.Bubble>\n ) : null}\n {stopped ? <Message.Footer>{t(\"stopped\")}</Message.Footer> : null}\n </Message.Content>\n </Message.Root>\n </Conversation.Content>\n <Conversation.JumpTrigger label={t(\"jump\")}>\n <ArrowDownIcon size=\"1em\" />\n </Conversation.JumpTrigger>\n </Conversation.Root>\n <Composer.Root\n busy={busy}\n onStop={() => {\n setStopped(true);\n }}\n onSubmit={(asked) => {\n setQuestion(asked);\n setStopped(false);\n setTick(0);\n conversation.scrollToEnd();\n }}\n >\n <Composer.Input label={t(\"ask\")} placeholder={t(\"askPlaceholder\")} />\n <Composer.Toolbar>\n <Composer.Submit\n label={t(\"send\")}\n stopIcon={<SquareIcon size=\"1em\" />}\n stopLabel={t(\"stop\")}\n >\n <ArrowUpIcon size=\"1em\" />\n </Composer.Submit>\n </Composer.Toolbar>\n </Composer.Root>\n </Stack>\n );\n}\n";
385
+ //#endregion
386
+ //#region src/conversation/examples/chat.example.tsx
387
+ var chat_example_exports = /* @__PURE__ */ __exportAll({
388
+ Chat: () => Chat,
389
+ source: () => source$3
390
+ });
391
+ const REPLIES = [
392
+ "scheduled",
393
+ "confirmed",
394
+ "archive",
395
+ "thanks"
396
+ ];
397
+ const OPENING = [
398
+ {
399
+ author: "ada",
400
+ id: "hello",
401
+ key: "hello"
402
+ },
403
+ {
404
+ author: "ada",
405
+ id: "check",
406
+ key: "check"
407
+ },
408
+ {
409
+ author: "me",
410
+ id: "please",
411
+ key: "please"
412
+ },
413
+ {
414
+ author: "ada",
415
+ id: "matches",
416
+ key: "matches"
417
+ },
418
+ {
419
+ author: "ada",
420
+ id: "approve",
421
+ key: "approve"
422
+ },
423
+ {
424
+ author: "me",
425
+ id: "approved",
426
+ key: "approved"
427
+ }
428
+ ];
429
+ function turnOf$1(turn, words, unread) {
430
+ const bubbles = turn.messages.map((one) => /* @__PURE__ */ jsx(Bubble, { children: one.text }, one.id));
431
+ return turn.own ? /* @__PURE__ */ jsx(Root, {
432
+ align: "end",
433
+ "aria-label": words.you,
434
+ look: "solid",
435
+ palette: "primary",
436
+ children: /* @__PURE__ */ jsxs(Content, { children: [bubbles, /* @__PURE__ */ jsx(Footer, { children: /* @__PURE__ */ jsxs(Status$1, {
437
+ status: unread ? "delivered" : "read",
438
+ children: [unread ? /* @__PURE__ */ jsx(CheckIcon, { "aria-hidden": true }) : /* @__PURE__ */ jsx(CheckCheckIcon, { "aria-hidden": true }), unread ? words.delivered : words.read]
439
+ }) })] })
440
+ }, turn.first.id) : /* @__PURE__ */ jsxs(Root, {
441
+ "aria-label": words.ada,
442
+ children: [/* @__PURE__ */ jsx(Avatar$1, { children: /* @__PURE__ */ jsx(Avatar.Root, {
443
+ "aria-hidden": true,
444
+ name: words.ada,
445
+ size: "sm",
446
+ children: /* @__PURE__ */ jsx(Avatar.Fallback, {})
447
+ }) }), /* @__PURE__ */ jsx(Content, { children: bubbles })]
448
+ }, turn.first.id);
449
+ }
450
+ function Chat() {
451
+ const $ = c(47);
452
+ const { t } = useWords("conversation");
453
+ const conversation = useConversation();
454
+ const [sent, setSent] = useState(OPENING);
455
+ const [typing, setTyping] = useState(false);
456
+ const words = {
457
+ ada: t("ada"),
458
+ delivered: t("delivered"),
459
+ read: t("read"),
460
+ you: t("you")
461
+ };
462
+ let t0;
463
+ if ($[0] !== t) {
464
+ t0 = (one) => ({
465
+ author: one.author,
466
+ id: one.id,
467
+ text: one.key === void 0 ? one.typed ?? "" : t(one.key)
468
+ });
469
+ $[0] = t;
470
+ $[1] = t0;
471
+ } else t0 = $[1];
472
+ const shown = sent.map(t0);
473
+ const turns = groupTurns(shown, { self: "me" }).flatMap(_temp$2);
474
+ let t1;
475
+ let t2;
476
+ if ($[2] !== typing) {
477
+ t1 = () => {
478
+ const timer = typing ? setTimeout(() => {
479
+ setTyping(false);
480
+ setSent(_temp2$1);
481
+ }, 1500) : void 0;
482
+ return () => {
483
+ clearTimeout(timer);
484
+ };
485
+ };
486
+ t2 = [typing];
487
+ $[2] = typing;
488
+ $[3] = t1;
489
+ $[4] = t2;
490
+ } else {
491
+ t1 = $[3];
492
+ t2 = $[4];
493
+ }
494
+ useEffect(t1, t2);
495
+ const T0 = Stack;
496
+ const t3 = "sm";
497
+ const t4 = conversation_exports;
498
+ const t5 = "sm";
499
+ let t6;
500
+ if ($[5] !== t) {
501
+ t6 = t("label");
502
+ $[5] = t;
503
+ $[6] = t6;
504
+ } else t6 = $[6];
505
+ const t7 = "xs";
506
+ const t8 = conversation_exports;
507
+ const t9 = turns.map((turn, at) => turnOf$1(turn, words, !typing && at === turns.length - 1));
508
+ let t10;
509
+ if ($[7] !== t || $[8] !== typing) {
510
+ t10 = typing ? /* @__PURE__ */ jsx(Typing, { children: t("typing") }) : null;
511
+ $[7] = t;
512
+ $[8] = typing;
513
+ $[9] = t10;
514
+ } else t10 = $[9];
515
+ let t11;
516
+ if ($[10] !== t10 || $[11] !== t8.Content || $[12] !== t9) {
517
+ t11 = /* @__PURE__ */ jsxs(t8.Content, { children: [t9, t10] });
518
+ $[10] = t10;
519
+ $[11] = t8.Content;
520
+ $[12] = t9;
521
+ $[13] = t11;
522
+ } else t11 = $[13];
523
+ let t12;
524
+ if ($[14] !== t) {
525
+ t12 = t("jump");
526
+ $[14] = t;
527
+ $[15] = t12;
528
+ } else t12 = $[15];
529
+ let t13;
530
+ if ($[16] === Symbol.for("react.memo_cache_sentinel")) {
531
+ t13 = /* @__PURE__ */ jsx(ArrowDownIcon, { size: "1em" });
532
+ $[16] = t13;
533
+ } else t13 = $[16];
534
+ let t14;
535
+ if ($[17] !== t12) {
536
+ t14 = /* @__PURE__ */ jsx(JumpTrigger, {
537
+ label: t12,
538
+ children: t13
539
+ });
540
+ $[17] = t12;
541
+ $[18] = t14;
542
+ } else t14 = $[18];
543
+ let t15;
544
+ if ($[19] !== conversation || $[20] !== t11 || $[21] !== t14 || $[22] !== t4.Root || $[23] !== t6) {
545
+ t15 = /* @__PURE__ */ jsxs(t4.Root, {
546
+ conversation,
547
+ inset: t5,
548
+ label: t6,
549
+ maxHeight: t7,
550
+ children: [t11, t14]
551
+ });
552
+ $[19] = conversation;
553
+ $[20] = t11;
554
+ $[21] = t14;
555
+ $[22] = t4.Root;
556
+ $[23] = t6;
557
+ $[24] = t15;
558
+ } else t15 = $[24];
559
+ let t16;
560
+ if ($[25] !== conversation) {
561
+ t16 = (typed) => {
562
+ setSent((was_0) => [...was_0, {
563
+ author: "me",
564
+ id: `me-${String(was_0.length)}`,
565
+ typed
566
+ }]);
567
+ setTyping(true);
568
+ conversation.scrollToEnd();
569
+ };
570
+ $[25] = conversation;
571
+ $[26] = t16;
572
+ } else t16 = $[26];
573
+ let t17;
574
+ if ($[27] !== t) {
575
+ t17 = t("message");
576
+ $[27] = t;
577
+ $[28] = t17;
578
+ } else t17 = $[28];
579
+ let t18;
580
+ if ($[29] !== t) {
581
+ t18 = t("placeholder");
582
+ $[29] = t;
583
+ $[30] = t18;
584
+ } else t18 = $[30];
585
+ let t19;
586
+ if ($[31] !== t17 || $[32] !== t18) {
587
+ t19 = /* @__PURE__ */ jsx(Input, {
588
+ label: t17,
589
+ placeholder: t18
590
+ });
591
+ $[31] = t17;
592
+ $[32] = t18;
593
+ $[33] = t19;
594
+ } else t19 = $[33];
595
+ let t20;
596
+ if ($[34] !== t) {
597
+ t20 = t("send");
598
+ $[34] = t;
599
+ $[35] = t20;
600
+ } else t20 = $[35];
601
+ let t21;
602
+ if ($[36] === Symbol.for("react.memo_cache_sentinel")) {
603
+ t21 = /* @__PURE__ */ jsx(ArrowUpIcon, { size: "1em" });
604
+ $[36] = t21;
605
+ } else t21 = $[36];
606
+ let t22;
607
+ if ($[37] !== t20) {
608
+ t22 = /* @__PURE__ */ jsx(Toolbar, { children: /* @__PURE__ */ jsx(Submit, {
609
+ label: t20,
610
+ children: t21
611
+ }) });
612
+ $[37] = t20;
613
+ $[38] = t22;
614
+ } else t22 = $[38];
615
+ let t23;
616
+ if ($[39] !== t16 || $[40] !== t19 || $[41] !== t22) {
617
+ t23 = /* @__PURE__ */ jsxs(Root$1, {
618
+ onSubmit: t16,
619
+ children: [t19, t22]
620
+ });
621
+ $[39] = t16;
622
+ $[40] = t19;
623
+ $[41] = t22;
624
+ $[42] = t23;
625
+ } else t23 = $[42];
626
+ let t24;
627
+ if ($[43] !== T0 || $[44] !== t15 || $[45] !== t23) {
628
+ t24 = /* @__PURE__ */ jsxs(T0, {
629
+ gap: t3,
630
+ children: [t15, t23]
631
+ });
632
+ $[43] = T0;
633
+ $[44] = t15;
634
+ $[45] = t23;
635
+ $[46] = t24;
636
+ } else t24 = $[46];
637
+ return t24;
638
+ }
639
+ function _temp2$1(was) {
640
+ return [...was, {
641
+ author: "ada",
642
+ id: `ada-${String(was.length)}`,
643
+ key: REPLIES[was.length % REPLIES.length] ?? "thanks"
644
+ }];
645
+ }
646
+ function _temp$2(day) {
647
+ return day.turns;
648
+ }
649
+ const source$3 = "import { type ReactElement, useEffect, useState } from \"react\";\n\nimport { ArrowDownIcon, ArrowUpIcon, CheckCheckIcon, CheckIcon } from \"lucide-react\";\n\nimport { Stack } from \"@stealthscale/component-layout\";\nimport { Avatar } from \"@stealthscale/component-media\";\nimport { useWords } from \"@stealthscale/specimen\";\n\nimport { Composer, Conversation, Message, Turn, groupTurns } from \"@stealthscale/component-messaging\";\n\nconst REPLIES = [\"scheduled\", \"confirmed\", \"archive\", \"thanks\"] as const;\n\nconst OPENING = [\n { author: \"ada\", id: \"hello\", key: \"hello\" },\n { author: \"ada\", id: \"check\", key: \"check\" },\n { author: \"me\", id: \"please\", key: \"please\" },\n { author: \"ada\", id: \"matches\", key: \"matches\" },\n { author: \"ada\", id: \"approve\", key: \"approve\" },\n { author: \"me\", id: \"approved\", key: \"approved\" },\n] as const;\n\ntype Said = (typeof OPENING)[number][\"key\"] | (typeof REPLIES)[number];\n\ninterface Sent {\n readonly author: string;\n readonly id: string;\n readonly key?: Said;\n readonly typed?: string;\n}\n\ninterface Shown {\n readonly author: string;\n readonly id: string;\n readonly text: string;\n}\n\ninterface Words {\n readonly ada: string;\n readonly delivered: string;\n readonly read: string;\n readonly you: string;\n}\n\nfunction turnOf(turn: Turn<Shown>, words: Words, unread: boolean): ReactElement {\n const bubbles = turn.messages.map((one) => (\n <Message.Bubble key={one.id}>{one.text}</Message.Bubble>\n ));\n\n return turn.own ? (\n <Message.Root\n align=\"end\"\n aria-label={words.you}\n key={turn.first.id}\n look=\"solid\"\n palette=\"primary\"\n >\n <Message.Content>\n {bubbles}\n <Message.Footer>\n <Message.Status status={unread ? \"delivered\" : \"read\"}>\n {unread ? <CheckIcon aria-hidden /> : <CheckCheckIcon aria-hidden />}\n {unread ? words.delivered : words.read}\n </Message.Status>\n </Message.Footer>\n </Message.Content>\n </Message.Root>\n ) : (\n <Message.Root aria-label={words.ada} key={turn.first.id}>\n <Message.Avatar>\n <Avatar.Root aria-hidden name={words.ada} size=\"sm\">\n <Avatar.Fallback />\n </Avatar.Root>\n </Message.Avatar>\n <Message.Content>{bubbles}</Message.Content>\n </Message.Root>\n );\n}\n\nexport function Chat(): ReactElement {\n const { t } = useWords(\"conversation\");\n const conversation = Conversation.useConversation();\n const [sent, setSent] = useState<readonly Sent[]>(OPENING);\n const [typing, setTyping] = useState(false);\n const words = { ada: t(\"ada\"), delivered: t(\"delivered\"), read: t(\"read\"), you: t(\"you\") };\n const shown = sent.map((one) => ({\n author: one.author,\n id: one.id,\n text: one.key === undefined ? (one.typed ?? \"\") : t(one.key),\n }));\n const turns = groupTurns(shown, { self: \"me\" }).flatMap((day) => day.turns);\n\n useEffect(() => {\n const timer = typing\n ? setTimeout(() => {\n setTyping(false);\n setSent((was) => [\n ...was,\n {\n author: \"ada\",\n id: `ada-${String(was.length)}`,\n key: REPLIES[was.length % REPLIES.length] ?? \"thanks\",\n },\n ]);\n }, 1500)\n : undefined;\n\n return (): void => {\n clearTimeout(timer);\n };\n }, [typing]);\n\n return (\n <Stack gap=\"sm\">\n <Conversation.Root conversation={conversation} inset=\"sm\" label={t(\"label\")} maxHeight=\"xs\">\n <Conversation.Content>\n {turns.map((turn, at) => turnOf(turn, words, !typing && at === turns.length - 1))}\n {typing ? <Conversation.Typing>{t(\"typing\")}</Conversation.Typing> : null}\n </Conversation.Content>\n <Conversation.JumpTrigger label={t(\"jump\")}>\n <ArrowDownIcon size=\"1em\" />\n </Conversation.JumpTrigger>\n </Conversation.Root>\n <Composer.Root\n onSubmit={(typed) => {\n setSent((was) => [...was, { author: \"me\", id: `me-${String(was.length)}`, typed }]);\n setTyping(true);\n conversation.scrollToEnd();\n }}\n >\n <Composer.Input label={t(\"message\")} placeholder={t(\"placeholder\")} />\n <Composer.Toolbar>\n <Composer.Submit label={t(\"send\")}>\n <ArrowUpIcon size=\"1em\" />\n </Composer.Submit>\n </Composer.Toolbar>\n </Composer.Root>\n </Stack>\n );\n}\n";
650
+ //#endregion
651
+ //#region src/conversation/examples/history.example.tsx
652
+ var history_example_exports = /* @__PURE__ */ __exportAll({
653
+ History: () => History,
654
+ source: () => source$2
655
+ });
656
+ const PAGES = [[
657
+ "opened",
658
+ "asked",
659
+ "sent",
660
+ "found"
661
+ ], [
662
+ "hello",
663
+ "check",
664
+ "please",
665
+ "matches"
666
+ ]];
667
+ const LATEST = [
668
+ "approve",
669
+ "approved",
670
+ "thanks"
671
+ ];
672
+ function History() {
673
+ const $ = c(38);
674
+ const { t } = useWords("conversation");
675
+ const [pages, setPages] = useState(0);
676
+ const [loading, setLoading] = useState(false);
677
+ let T0;
678
+ let T1;
679
+ let t0;
680
+ let t1;
681
+ let t2;
682
+ let t3;
683
+ let t4;
684
+ let t5;
685
+ let t6;
686
+ if ($[0] !== loading || $[1] !== pages || $[2] !== t) {
687
+ const keys = [...PAGES.slice(PAGES.length - pages).flat(), ...LATEST];
688
+ let t7;
689
+ if ($[12] !== loading || $[13] !== pages) {
690
+ t7 = function reached(event) {
691
+ if (event.currentTarget.scrollTop > 0 || loading || pages === PAGES.length) return;
692
+ setLoading(true);
693
+ setTimeout(() => {
694
+ setPages(_temp$1);
695
+ setLoading(false);
696
+ }, 600);
697
+ };
698
+ $[12] = loading;
699
+ $[13] = pages;
700
+ $[14] = t7;
701
+ } else t7 = $[14];
702
+ const reached = t7;
703
+ T1 = Root$2;
704
+ t4 = "sm";
705
+ if ($[15] !== t) {
706
+ t5 = t("label");
707
+ $[15] = t;
708
+ $[16] = t5;
709
+ } else t5 = $[16];
710
+ t6 = "xs";
711
+ T0 = Content$1;
712
+ t0 = loading;
713
+ t1 = reached;
714
+ if ($[17] !== loading || $[18] !== t) {
715
+ t2 = loading ? /* @__PURE__ */ jsx(Text, {
716
+ size: "sm",
717
+ children: t("loading")
718
+ }) : null;
719
+ $[17] = loading;
720
+ $[18] = t;
721
+ $[19] = t2;
722
+ } else t2 = $[19];
723
+ t3 = keys.map((key, index) => /* @__PURE__ */ jsx(Root, {
724
+ align: index % 2 === 0 ? "start" : "end",
725
+ "aria-label": index % 2 === 0 ? t("ada") : t("you"),
726
+ look: index % 2 === 0 ? "subtle" : "solid",
727
+ palette: index % 2 === 0 ? "neutral" : "primary",
728
+ children: /* @__PURE__ */ jsx(Content, { children: /* @__PURE__ */ jsx(Bubble, { children: t(key) }) })
729
+ }, key));
730
+ $[0] = loading;
731
+ $[1] = pages;
732
+ $[2] = t;
733
+ $[3] = T0;
734
+ $[4] = T1;
735
+ $[5] = t0;
736
+ $[6] = t1;
737
+ $[7] = t2;
738
+ $[8] = t3;
739
+ $[9] = t4;
740
+ $[10] = t5;
741
+ $[11] = t6;
742
+ } else {
743
+ T0 = $[3];
744
+ T1 = $[4];
745
+ t0 = $[5];
746
+ t1 = $[6];
747
+ t2 = $[7];
748
+ t3 = $[8];
749
+ t4 = $[9];
750
+ t5 = $[10];
751
+ t6 = $[11];
752
+ }
753
+ let t7;
754
+ if ($[20] !== T0 || $[21] !== t0 || $[22] !== t1 || $[23] !== t2 || $[24] !== t3) {
755
+ t7 = /* @__PURE__ */ jsxs(T0, {
756
+ "aria-busy": t0,
757
+ onScroll: t1,
758
+ children: [t2, t3]
759
+ });
760
+ $[20] = T0;
761
+ $[21] = t0;
762
+ $[22] = t1;
763
+ $[23] = t2;
764
+ $[24] = t3;
765
+ $[25] = t7;
766
+ } else t7 = $[25];
767
+ let t8;
768
+ if ($[26] !== t) {
769
+ t8 = t("jump");
770
+ $[26] = t;
771
+ $[27] = t8;
772
+ } else t8 = $[27];
773
+ let t9;
774
+ if ($[28] === Symbol.for("react.memo_cache_sentinel")) {
775
+ t9 = /* @__PURE__ */ jsx(ArrowDownIcon, { size: "1em" });
776
+ $[28] = t9;
777
+ } else t9 = $[28];
778
+ let t10;
779
+ if ($[29] !== t8) {
780
+ t10 = /* @__PURE__ */ jsx(JumpTrigger, {
781
+ label: t8,
782
+ children: t9
783
+ });
784
+ $[29] = t8;
785
+ $[30] = t10;
786
+ } else t10 = $[30];
787
+ let t11;
788
+ if ($[31] !== T1 || $[32] !== t10 || $[33] !== t4 || $[34] !== t5 || $[35] !== t6 || $[36] !== t7) {
789
+ t11 = /* @__PURE__ */ jsxs(T1, {
790
+ inset: t4,
791
+ label: t5,
792
+ maxHeight: t6,
793
+ children: [t7, t10]
794
+ });
795
+ $[31] = T1;
796
+ $[32] = t10;
797
+ $[33] = t4;
798
+ $[34] = t5;
799
+ $[35] = t6;
800
+ $[36] = t7;
801
+ $[37] = t11;
802
+ } else t11 = $[37];
803
+ return t11;
804
+ }
805
+ function _temp$1(was) {
806
+ return was + 1;
807
+ }
808
+ const source$2 = "import { type ReactElement, type UIEvent, useState } from \"react\";\n\nimport { ArrowDownIcon } from \"lucide-react\";\n\nimport { Text } from \"@stealthscale/component-typography\";\nimport { useWords } from \"@stealthscale/specimen\";\n\nimport { Conversation, Message } from \"@stealthscale/component-messaging\";\n\nconst PAGES = [\n [\"opened\", \"asked\", \"sent\", \"found\"],\n [\"hello\", \"check\", \"please\", \"matches\"],\n] as const;\n\nconst LATEST = [\"approve\", \"approved\", \"thanks\"] as const;\n\nexport function History(): ReactElement {\n const { t } = useWords(\"conversation\");\n const [pages, setPages] = useState(0);\n const [loading, setLoading] = useState(false);\n const keys = [...PAGES.slice(PAGES.length - pages).flat(), ...LATEST];\n\n function reached(event: UIEvent<HTMLElement>): void {\n if (event.currentTarget.scrollTop > 0 || loading || pages === PAGES.length) return;\n\n setLoading(true);\n setTimeout(() => {\n setPages((was) => was + 1);\n setLoading(false);\n }, 600);\n }\n\n return (\n <Conversation.Root inset=\"sm\" label={t(\"label\")} maxHeight=\"xs\">\n <Conversation.Content aria-busy={loading} onScroll={reached}>\n {loading ? <Text size=\"sm\">{t(\"loading\")}</Text> : null}\n {keys.map((key, index) => (\n <Message.Root\n align={index % 2 === 0 ? \"start\" : \"end\"}\n aria-label={index % 2 === 0 ? t(\"ada\") : t(\"you\")}\n key={key}\n look={index % 2 === 0 ? \"subtle\" : \"solid\"}\n palette={index % 2 === 0 ? \"neutral\" : \"primary\"}\n >\n <Message.Content>\n <Message.Bubble>{t(key)}</Message.Bubble>\n </Message.Content>\n </Message.Root>\n ))}\n </Conversation.Content>\n <Conversation.JumpTrigger label={t(\"jump\")}>\n <ArrowDownIcon size=\"1em\" />\n </Conversation.JumpTrigger>\n </Conversation.Root>\n );\n}\n";
809
+ //#endregion
810
+ //#region src/conversation/examples/inbox.example.tsx
811
+ var inbox_example_exports = /* @__PURE__ */ __exportAll({
812
+ Inbox: () => Inbox,
813
+ source: () => source$1
814
+ });
815
+ const TIME$1 = {
816
+ timeStyle: "short",
817
+ timeZone: "UTC"
818
+ };
819
+ const CHATS = [
820
+ {
821
+ at: "2026-09-30T09:15:00Z",
822
+ id: "ada",
823
+ palette: "accent",
824
+ said: "thanks",
825
+ unread: 2
826
+ },
827
+ {
828
+ at: "2026-09-30T08:52:00Z",
829
+ id: "bram",
830
+ palette: "info",
831
+ said: "confirmed",
832
+ unread: 1
833
+ },
834
+ {
835
+ at: "2026-09-30T08:10:00Z",
836
+ id: "cleo",
837
+ palette: "success",
838
+ said: "archive",
839
+ unread: 0
840
+ },
841
+ {
842
+ at: "2026-09-30T07:30:00Z",
843
+ id: "devi",
844
+ palette: "warning",
845
+ said: "found",
846
+ unread: 0
847
+ }
848
+ ];
849
+ function Inbox() {
850
+ const $ = c(20);
851
+ const { t } = useWords("conversation");
852
+ let t0;
853
+ if ($[0] !== t) {
854
+ t0 = CHATS.map((chat_1) => ({
855
+ at: chat_1.at,
856
+ id: chat_1.id,
857
+ name: t(chat_1.id),
858
+ palette: chat_1.palette,
859
+ preview: t(chat_1.said),
860
+ unread: chat_1.unread
861
+ }));
862
+ $[0] = t;
863
+ $[1] = t0;
864
+ } else t0 = $[1];
865
+ let t1;
866
+ if ($[2] !== t0) {
867
+ t1 = {
868
+ itemToString: _temp,
869
+ itemToValue: _temp2,
870
+ rows: t0
871
+ };
872
+ $[2] = t0;
873
+ $[3] = t1;
874
+ } else t1 = $[3];
875
+ const { collection } = useListCollection(t1);
876
+ let t2;
877
+ if ($[4] === Symbol.for("react.memo_cache_sentinel")) {
878
+ t2 = ["ada"];
879
+ $[4] = t2;
880
+ } else t2 = $[4];
881
+ let t3;
882
+ if ($[5] !== t) {
883
+ t3 = t("conversations");
884
+ $[5] = t;
885
+ $[6] = t3;
886
+ } else t3 = $[6];
887
+ let t4;
888
+ if ($[7] !== t3) {
889
+ t4 = /* @__PURE__ */ jsx(Listbox.Label, { children: t3 });
890
+ $[7] = t3;
891
+ $[8] = t4;
892
+ } else t4 = $[8];
893
+ let t5;
894
+ if ($[9] !== collection.items || $[10] !== t) {
895
+ let t6;
896
+ if ($[12] !== t) {
897
+ t6 = (chat_2) => /* @__PURE__ */ jsxs(Listbox.Item, {
898
+ item: chat_2,
899
+ children: [
900
+ /* @__PURE__ */ jsx(Avatar.Root, {
901
+ "aria-hidden": true,
902
+ name: chat_2.name,
903
+ palette: chat_2.palette,
904
+ size: "sm",
905
+ variant: "solid",
906
+ children: /* @__PURE__ */ jsx(Avatar.Fallback, {})
907
+ }),
908
+ /* @__PURE__ */ jsxs(Listbox.ItemLines, { children: [/* @__PURE__ */ jsx(Listbox.ItemText, {
909
+ item: chat_2,
910
+ children: chat_2.unread > 0 ? /* @__PURE__ */ jsx(Strong, { children: chat_2.name }) : chat_2.name
911
+ }), /* @__PURE__ */ jsx(Listbox.ItemDescription, { children: chat_2.preview })] }),
912
+ /* @__PURE__ */ jsxs(Stack, {
913
+ direction: "row",
914
+ gap: "sm",
915
+ children: [chat_2.unread > 0 ? /* @__PURE__ */ jsx(Badge, {
916
+ size: "sm",
917
+ variant: "solid",
918
+ children: t("unread", { count: chat_2.unread })
919
+ }) : null, /* @__PURE__ */ jsx(Timestamp, {
920
+ options: TIME$1,
921
+ value: chat_2.at
922
+ })]
923
+ })
924
+ ]
925
+ }, chat_2.id);
926
+ $[12] = t;
927
+ $[13] = t6;
928
+ } else t6 = $[13];
929
+ t5 = collection.items.map(t6);
930
+ $[9] = collection.items;
931
+ $[10] = t;
932
+ $[11] = t5;
933
+ } else t5 = $[11];
934
+ let t6;
935
+ if ($[14] !== t5) {
936
+ t6 = /* @__PURE__ */ jsx(Listbox.Frame, { children: /* @__PURE__ */ jsx(Listbox.Content, { children: t5 }) });
937
+ $[14] = t5;
938
+ $[15] = t6;
939
+ } else t6 = $[15];
940
+ let t7;
941
+ if ($[16] !== collection || $[17] !== t4 || $[18] !== t6) {
942
+ t7 = /* @__PURE__ */ jsxs(Listbox.Root, {
943
+ collection,
944
+ defaultValue: t2,
945
+ variant: "surface",
946
+ children: [t4, t6]
947
+ });
948
+ $[16] = collection;
949
+ $[17] = t4;
950
+ $[18] = t6;
951
+ $[19] = t7;
952
+ } else t7 = $[19];
953
+ return t7;
954
+ }
955
+ function _temp2(chat_0) {
956
+ return chat_0.id;
957
+ }
958
+ function _temp(chat) {
959
+ return chat.name;
960
+ }
961
+ const source$1 = "import { type ReactElement } from \"react\";\n\nimport { Listbox, useListCollection } from \"@stealthscale/component-collections\";\nimport { Badge, Timestamp } from \"@stealthscale/component-data\";\nimport { Stack } from \"@stealthscale/component-layout\";\nimport { Avatar } from \"@stealthscale/component-media\";\nimport { Strong } from \"@stealthscale/component-typography\";\nimport { useWords } from \"@stealthscale/specimen\";\n\nconst TIME: Intl.DateTimeFormatOptions = { timeStyle: \"short\", timeZone: \"UTC\" };\n\nconst CHATS = [\n { at: \"2026-09-30T09:15:00Z\", id: \"ada\", palette: \"accent\", said: \"thanks\", unread: 2 },\n { at: \"2026-09-30T08:52:00Z\", id: \"bram\", palette: \"info\", said: \"confirmed\", unread: 1 },\n { at: \"2026-09-30T08:10:00Z\", id: \"cleo\", palette: \"success\", said: \"archive\", unread: 0 },\n { at: \"2026-09-30T07:30:00Z\", id: \"devi\", palette: \"warning\", said: \"found\", unread: 0 },\n] as const;\n\ninterface Chat {\n readonly at: string;\n readonly id: string;\n readonly name: string;\n readonly palette: (typeof CHATS)[number][\"palette\"];\n readonly preview: string;\n readonly unread: number;\n}\n\nexport function Inbox(): ReactElement {\n const { t } = useWords(\"conversation\");\n const { collection } = useListCollection<Chat>({\n itemToString: (chat) => chat.name,\n itemToValue: (chat) => chat.id,\n rows: CHATS.map((chat) => ({\n at: chat.at,\n id: chat.id,\n name: t(chat.id),\n palette: chat.palette,\n preview: t(chat.said),\n unread: chat.unread,\n })),\n });\n\n return (\n <Listbox.Root collection={collection} defaultValue={[\"ada\"]} variant=\"surface\">\n <Listbox.Label>{t(\"conversations\")}</Listbox.Label>\n <Listbox.Frame>\n <Listbox.Content>\n {collection.items.map((chat) => (\n <Listbox.Item item={chat} key={chat.id}>\n <Avatar.Root\n aria-hidden\n name={chat.name}\n palette={chat.palette}\n size=\"sm\"\n variant=\"solid\"\n >\n <Avatar.Fallback />\n </Avatar.Root>\n <Listbox.ItemLines>\n <Listbox.ItemText item={chat}>\n {chat.unread > 0 ? <Strong>{chat.name}</Strong> : chat.name}\n </Listbox.ItemText>\n <Listbox.ItemDescription>{chat.preview}</Listbox.ItemDescription>\n </Listbox.ItemLines>\n <Stack direction=\"row\" gap=\"sm\">\n {chat.unread > 0 ? (\n <Badge size=\"sm\" variant=\"solid\">\n {t(\"unread\", { count: chat.unread })}\n </Badge>\n ) : null}\n <Timestamp options={TIME} value={chat.at} />\n </Stack>\n </Listbox.Item>\n ))}\n </Listbox.Content>\n </Listbox.Frame>\n </Listbox.Root>\n );\n}\n";
962
+ //#endregion
963
+ //#region src/conversation/examples/transcript.example.tsx
964
+ var transcript_example_exports = /* @__PURE__ */ __exportAll({
965
+ Transcript: () => Transcript,
966
+ source: () => source
967
+ });
968
+ const TIME = {
969
+ timeStyle: "short",
970
+ timeZone: "UTC"
971
+ };
972
+ const DAY = {
973
+ dateStyle: "medium",
974
+ timeZone: "UTC"
975
+ };
976
+ const SAID = [
977
+ {
978
+ author: "ada",
979
+ id: "m1",
980
+ key: "hello",
981
+ sentAt: "2026-09-29T08:02:00Z"
982
+ },
983
+ {
984
+ author: "ada",
985
+ id: "m2",
986
+ key: "check",
987
+ sentAt: "2026-09-29T08:03:00Z"
988
+ },
989
+ {
990
+ author: "me",
991
+ id: "m3",
992
+ key: "please",
993
+ sentAt: "2026-09-29T08:20:00Z"
994
+ },
995
+ {
996
+ author: "ada",
997
+ id: "m4",
998
+ key: "matches",
999
+ sentAt: "2026-09-30T09:12:00Z"
1000
+ },
1001
+ {
1002
+ author: "ada",
1003
+ id: "m5",
1004
+ key: "approve",
1005
+ sentAt: "2026-09-30T09:12:30Z"
1006
+ },
1007
+ {
1008
+ author: "me",
1009
+ id: "m6",
1010
+ key: "approved",
1011
+ sentAt: "2026-09-30T09:14:00Z"
1012
+ },
1013
+ {
1014
+ author: "ada",
1015
+ id: "m7",
1016
+ key: "thanks",
1017
+ sentAt: "2026-09-30T09:15:00Z"
1018
+ }
1019
+ ];
1020
+ function turnOf(turn, words) {
1021
+ const bubbles = turn.messages.map((said) => /* @__PURE__ */ jsx(Bubble, { children: said.text }, said.id));
1022
+ return turn.own ? /* @__PURE__ */ jsx(Root, {
1023
+ align: "end",
1024
+ "aria-label": words.you,
1025
+ look: "solid",
1026
+ palette: "primary",
1027
+ children: /* @__PURE__ */ jsxs(Content, { children: [bubbles, /* @__PURE__ */ jsxs(Footer, { children: [/* @__PURE__ */ jsx(Timestamp, {
1028
+ options: TIME,
1029
+ value: turn.last.sentAt
1030
+ }), /* @__PURE__ */ jsxs(Status$1, {
1031
+ status: "read",
1032
+ children: [/* @__PURE__ */ jsx(CheckCheckIcon, { "aria-hidden": true }), words.read]
1033
+ })] })] })
1034
+ }, turn.first.id) : /* @__PURE__ */ jsxs(Root, { children: [/* @__PURE__ */ jsx(Avatar$1, { children: /* @__PURE__ */ jsx(Avatar.Root, {
1035
+ "aria-hidden": true,
1036
+ name: words.ada,
1037
+ size: "sm",
1038
+ children: /* @__PURE__ */ jsx(Avatar.Fallback, {})
1039
+ }) }), /* @__PURE__ */ jsxs(Content, { children: [/* @__PURE__ */ jsxs(Header, { children: [/* @__PURE__ */ jsx(Strong, { children: words.ada }), /* @__PURE__ */ jsx(Timestamp, {
1040
+ options: TIME,
1041
+ value: turn.first.sentAt
1042
+ })] }), bubbles] })] }, turn.first.id);
1043
+ }
1044
+ function Transcript() {
1045
+ const $ = c(27);
1046
+ const { t } = useWords("conversation");
1047
+ let T0;
1048
+ let T1;
1049
+ let t0;
1050
+ let t1;
1051
+ let t2;
1052
+ let t3;
1053
+ if ($[0] !== t) {
1054
+ const words = {
1055
+ ada: t("ada"),
1056
+ read: t("read"),
1057
+ you: t("you")
1058
+ };
1059
+ const said = SAID.map((one) => ({
1060
+ author: one.author,
1061
+ id: one.id,
1062
+ sentAt: one.sentAt,
1063
+ text: t(one.key)
1064
+ }));
1065
+ T1 = Root$2;
1066
+ t1 = "sm";
1067
+ t2 = t("label");
1068
+ t3 = "xs";
1069
+ T0 = Content$1;
1070
+ t0 = groupTurns(said, {
1071
+ self: "me",
1072
+ timeZone: "UTC"
1073
+ }).map((day) => /* @__PURE__ */ jsxs(Fragment$1, { children: [day.day === void 0 ? null : /* @__PURE__ */ jsx(Divider, { label: /* @__PURE__ */ jsx(Timestamp, {
1074
+ options: DAY,
1075
+ value: day.day
1076
+ }) }), day.turns.map((turn) => turnOf(turn, words))] }, day.key));
1077
+ $[0] = t;
1078
+ $[1] = T0;
1079
+ $[2] = T1;
1080
+ $[3] = t0;
1081
+ $[4] = t1;
1082
+ $[5] = t2;
1083
+ $[6] = t3;
1084
+ } else {
1085
+ T0 = $[1];
1086
+ T1 = $[2];
1087
+ t0 = $[3];
1088
+ t1 = $[4];
1089
+ t2 = $[5];
1090
+ t3 = $[6];
1091
+ }
1092
+ let t4;
1093
+ if ($[7] !== t) {
1094
+ t4 = t("typing");
1095
+ $[7] = t;
1096
+ $[8] = t4;
1097
+ } else t4 = $[8];
1098
+ let t5;
1099
+ if ($[9] !== t4) {
1100
+ t5 = /* @__PURE__ */ jsx(Typing, { children: t4 });
1101
+ $[9] = t4;
1102
+ $[10] = t5;
1103
+ } else t5 = $[10];
1104
+ let t6;
1105
+ if ($[11] !== T0 || $[12] !== t0 || $[13] !== t5) {
1106
+ t6 = /* @__PURE__ */ jsxs(T0, { children: [t0, t5] });
1107
+ $[11] = T0;
1108
+ $[12] = t0;
1109
+ $[13] = t5;
1110
+ $[14] = t6;
1111
+ } else t6 = $[14];
1112
+ let t7;
1113
+ if ($[15] !== t) {
1114
+ t7 = t("jump");
1115
+ $[15] = t;
1116
+ $[16] = t7;
1117
+ } else t7 = $[16];
1118
+ let t8;
1119
+ if ($[17] === Symbol.for("react.memo_cache_sentinel")) {
1120
+ t8 = /* @__PURE__ */ jsx(ArrowDownIcon, { size: "1em" });
1121
+ $[17] = t8;
1122
+ } else t8 = $[17];
1123
+ let t9;
1124
+ if ($[18] !== t7) {
1125
+ t9 = /* @__PURE__ */ jsx(JumpTrigger, {
1126
+ label: t7,
1127
+ children: t8
1128
+ });
1129
+ $[18] = t7;
1130
+ $[19] = t9;
1131
+ } else t9 = $[19];
1132
+ let t10;
1133
+ if ($[20] !== T1 || $[21] !== t1 || $[22] !== t2 || $[23] !== t3 || $[24] !== t6 || $[25] !== t9) {
1134
+ t10 = /* @__PURE__ */ jsxs(T1, {
1135
+ inset: t1,
1136
+ label: t2,
1137
+ maxHeight: t3,
1138
+ children: [t6, t9]
1139
+ });
1140
+ $[20] = T1;
1141
+ $[21] = t1;
1142
+ $[22] = t2;
1143
+ $[23] = t3;
1144
+ $[24] = t6;
1145
+ $[25] = t9;
1146
+ $[26] = t10;
1147
+ } else t10 = $[26];
1148
+ return t10;
1149
+ }
1150
+ const source = "import { Fragment, type ReactElement } from \"react\";\n\nimport { ArrowDownIcon, CheckCheckIcon } from \"lucide-react\";\n\nimport { Timestamp } from \"@stealthscale/component-data\";\nimport { Divider } from \"@stealthscale/component-layout\";\nimport { Avatar } from \"@stealthscale/component-media\";\nimport { Strong } from \"@stealthscale/component-typography\";\nimport { useWords } from \"@stealthscale/specimen\";\n\nimport { Conversation, Message, Turn, groupTurns } from \"@stealthscale/component-messaging\";\n\nconst TIME: Intl.DateTimeFormatOptions = { timeStyle: \"short\", timeZone: \"UTC\" };\n\nconst DAY: Intl.DateTimeFormatOptions = { dateStyle: \"medium\", timeZone: \"UTC\" };\n\nconst SAID = [\n { author: \"ada\", id: \"m1\", key: \"hello\", sentAt: \"2026-09-29T08:02:00Z\" },\n { author: \"ada\", id: \"m2\", key: \"check\", sentAt: \"2026-09-29T08:03:00Z\" },\n { author: \"me\", id: \"m3\", key: \"please\", sentAt: \"2026-09-29T08:20:00Z\" },\n { author: \"ada\", id: \"m4\", key: \"matches\", sentAt: \"2026-09-30T09:12:00Z\" },\n { author: \"ada\", id: \"m5\", key: \"approve\", sentAt: \"2026-09-30T09:12:30Z\" },\n { author: \"me\", id: \"m6\", key: \"approved\", sentAt: \"2026-09-30T09:14:00Z\" },\n { author: \"ada\", id: \"m7\", key: \"thanks\", sentAt: \"2026-09-30T09:15:00Z\" },\n] as const;\n\ninterface Said {\n readonly author: string;\n readonly id: string;\n readonly sentAt: string;\n readonly text: string;\n}\n\ninterface Words {\n readonly ada: string;\n readonly read: string;\n readonly you: string;\n}\n\nfunction turnOf(turn: Turn<Said>, words: Words): ReactElement {\n const bubbles = turn.messages.map((said) => (\n <Message.Bubble key={said.id}>{said.text}</Message.Bubble>\n ));\n\n return turn.own ? (\n <Message.Root\n align=\"end\"\n aria-label={words.you}\n key={turn.first.id}\n look=\"solid\"\n palette=\"primary\"\n >\n <Message.Content>\n {bubbles}\n <Message.Footer>\n <Timestamp options={TIME} value={turn.last.sentAt} />\n <Message.Status status=\"read\">\n <CheckCheckIcon aria-hidden />\n {words.read}\n </Message.Status>\n </Message.Footer>\n </Message.Content>\n </Message.Root>\n ) : (\n <Message.Root key={turn.first.id}>\n <Message.Avatar>\n <Avatar.Root aria-hidden name={words.ada} size=\"sm\">\n <Avatar.Fallback />\n </Avatar.Root>\n </Message.Avatar>\n <Message.Content>\n <Message.Header>\n <Strong>{words.ada}</Strong>\n <Timestamp options={TIME} value={turn.first.sentAt} />\n </Message.Header>\n {bubbles}\n </Message.Content>\n </Message.Root>\n );\n}\n\nexport function Transcript(): ReactElement {\n const { t } = useWords(\"conversation\");\n const words = { ada: t(\"ada\"), read: t(\"read\"), you: t(\"you\") };\n const said = SAID.map((one) => ({\n author: one.author,\n id: one.id,\n sentAt: one.sentAt,\n text: t(one.key),\n }));\n\n return (\n <Conversation.Root inset=\"sm\" label={t(\"label\")} maxHeight=\"xs\">\n <Conversation.Content>\n {groupTurns(said, { self: \"me\", timeZone: \"UTC\" }).map((day) => (\n <Fragment key={day.key}>\n {day.day === undefined ? null : (\n <Divider label={<Timestamp options={DAY} value={day.day} />} />\n )}\n {day.turns.map((turn) => turnOf(turn, words))}\n </Fragment>\n ))}\n <Conversation.Typing>{t(\"typing\")}</Conversation.Typing>\n </Conversation.Content>\n <Conversation.JumpTrigger label={t(\"jump\")}>\n <ArrowDownIcon size=\"1em\" />\n </Conversation.JumpTrigger>\n </Conversation.Root>\n );\n}\n";
1151
+ //#endregion
1152
+ //#region src/conversation/conversation.specimen.tsx
1153
+ /**
1154
+ * Catalogue page for the conversation.
1155
+ *
1156
+ * @remarks
1157
+ * The recipe has no axis, so every scene is hand-written, each in a room at the `sm` measure, the
1158
+ * width of a phone. The scenes render a transcript of two days, a chat that answers what the
1159
+ * reader sends, an assistant whose answer streams, older messages that load at the top, and an
1160
+ * inbox of conversations. The words are keys under `conversation` in
1161
+ * `locales/en/specimen/conversation.json`.
1162
+ */
1163
+ /**
1164
+ * Hand-written scene for two days of grouped turns.
1165
+ */
1166
+ const transcribed = {
1167
+ about: "conversation.transcript.about",
1168
+ draw: () => /* @__PURE__ */ jsx(Room, {
1169
+ size: "sm",
1170
+ children: /* @__PURE__ */ jsx(Transcript, {})
1171
+ }),
1172
+ example: transcript_example_exports,
1173
+ title: "conversation.transcript.title"
1174
+ };
1175
+ /**
1176
+ * Hand-written scene for a chat that answers what the reader sends.
1177
+ */
1178
+ const chatted = {
1179
+ about: "conversation.chat.about",
1180
+ draw: () => /* @__PURE__ */ jsx(Room, {
1181
+ size: "sm",
1182
+ children: /* @__PURE__ */ jsx(Chat, {})
1183
+ }),
1184
+ example: chat_example_exports,
1185
+ title: "conversation.chat.title"
1186
+ };
1187
+ /**
1188
+ * Hand-written scene for an assistant's answer that streams after its reasoning and tool call.
1189
+ */
1190
+ const assisted = {
1191
+ about: "conversation.assisting.about",
1192
+ draw: () => /* @__PURE__ */ jsx(Room, {
1193
+ size: "md",
1194
+ children: /* @__PURE__ */ jsx(Assistant, {})
1195
+ }),
1196
+ example: assistant_example_exports,
1197
+ title: "conversation.assisting.title"
1198
+ };
1199
+ /**
1200
+ * Hand-written scene for older messages that load at the top.
1201
+ */
1202
+ const paged = {
1203
+ about: "conversation.history.about",
1204
+ draw: () => /* @__PURE__ */ jsx(Room, {
1205
+ size: "sm",
1206
+ children: /* @__PURE__ */ jsx(History, {})
1207
+ }),
1208
+ example: history_example_exports,
1209
+ title: "conversation.history.title"
1210
+ };
1211
+ /**
1212
+ * Hand-written scene for a list of conversations.
1213
+ */
1214
+ const listed = {
1215
+ about: "conversation.inbox.about",
1216
+ draw: () => /* @__PURE__ */ jsx(Room, {
1217
+ size: "sm",
1218
+ children: /* @__PURE__ */ jsx(Inbox, {})
1219
+ }),
1220
+ example: inbox_example_exports,
1221
+ title: "conversation.inbox.title"
1222
+ };
1223
+ var conversation_specimen_default = specimen({
1224
+ about: "conversation.about",
1225
+ id: "components/messaging/conversation",
1226
+ imports: "import { Conversation, groupTurns } from \"@stealthscale/component-messaging\";",
1227
+ scenes: [
1228
+ transcribed,
1229
+ chatted,
1230
+ assisted,
1231
+ paged,
1232
+ listed
1233
+ ],
1234
+ title: "conversation.title"
1235
+ });
1236
+ //#endregion
1237
+ export { assisted, chatted, conversation_specimen_default as default, listed, paged, transcribed };