@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,959 @@
1
+ import { t as __exportAll } from "../../rolldown-runtime-D7D4PA-g.js";
2
+ import { a as recipe } from "../../recipes-D1oVFnRU.js";
3
+ import { C as Toolbar, D as Context, E as Input, I as Content$1, L as Actions, M as Root$2, N as Media, O as Attachments, P as Group, T as Root$1, f as Content, j as Title, k as AttachTrigger, l as Root, p as Bubble, w as Submit } from "../../shared-B0H4dgkB.js";
4
+ import { c } from "react/compiler-runtime";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ import { useEffect, useId, useState } from "react";
7
+ import { IconButton } from "@stealthscale/component-actions";
8
+ import { Room, scenesOf, specimen, useWords } from "@stealthscale/specimen";
9
+ import { ArrowUpIcon, FileIcon, PaperclipIcon, PencilIcon, ReplyIcon, SquareIcon, XIcon } from "lucide-react";
10
+ import { Mark, Span, Text } from "@stealthscale/component-typography";
11
+ import { Stack } from "@stealthscale/component-layout";
12
+ //#region src/composer/examples/edit.example.tsx
13
+ var edit_example_exports = /* @__PURE__ */ __exportAll({
14
+ EditLast: () => EditLast,
15
+ source: () => source$4
16
+ });
17
+ function strip$1(editing, cancel, cancelled) {
18
+ return /* @__PURE__ */ jsxs(Context, { children: [
19
+ /* @__PURE__ */ jsx(PencilIcon, { "aria-hidden": true }),
20
+ /* @__PURE__ */ jsx(Span, { children: editing }),
21
+ /* @__PURE__ */ jsx(IconButton, {
22
+ "aria-label": cancel,
23
+ onClick: cancelled,
24
+ size: "xs",
25
+ variant: "ghost",
26
+ children: /* @__PURE__ */ jsx(XIcon, { size: "1em" })
27
+ })
28
+ ] });
29
+ }
30
+ function EditLast() {
31
+ const $ = c(41);
32
+ const { t } = useWords("composer");
33
+ const input = useId();
34
+ let t0;
35
+ if ($[0] !== t) {
36
+ t0 = t("first");
37
+ $[0] = t;
38
+ $[1] = t0;
39
+ } else t0 = $[1];
40
+ let t1;
41
+ if ($[2] !== t0) {
42
+ t1 = [t0];
43
+ $[2] = t0;
44
+ $[3] = t1;
45
+ } else t1 = $[3];
46
+ const [sent, setSent] = useState(t1);
47
+ const [text, setText] = useState("");
48
+ const [editing, setEditing] = useState(false);
49
+ let t2;
50
+ if ($[4] === Symbol.for("react.memo_cache_sentinel")) {
51
+ t2 = () => {
52
+ setEditing(false);
53
+ setText("");
54
+ };
55
+ $[4] = t2;
56
+ } else t2 = $[4];
57
+ const left = t2;
58
+ let t3;
59
+ if ($[5] !== sent || $[6] !== t) {
60
+ let t4;
61
+ if ($[8] !== t) {
62
+ t4 = (one, index) => /* @__PURE__ */ jsx(Root, {
63
+ align: "end",
64
+ "aria-label": t("you"),
65
+ look: "solid",
66
+ palette: "primary",
67
+ children: /* @__PURE__ */ jsx(Content, { children: /* @__PURE__ */ jsx(Bubble, { children: one }) })
68
+ }, `${String(index)}-${one}`);
69
+ $[8] = t;
70
+ $[9] = t4;
71
+ } else t4 = $[9];
72
+ t3 = sent.map(t4);
73
+ $[5] = sent;
74
+ $[6] = t;
75
+ $[7] = t3;
76
+ } else t3 = $[7];
77
+ let t4;
78
+ if ($[10] !== sent) {
79
+ t4 = () => {
80
+ setText(sent.at(-1) ?? "");
81
+ setEditing(true);
82
+ };
83
+ $[10] = sent;
84
+ $[11] = t4;
85
+ } else t4 = $[11];
86
+ let t5;
87
+ if ($[12] !== editing) {
88
+ t5 = (value) => {
89
+ setSent((was) => editing ? [...was.slice(0, -1), value] : [...was, value]);
90
+ left();
91
+ };
92
+ $[12] = editing;
93
+ $[13] = t5;
94
+ } else t5 = $[13];
95
+ let t6;
96
+ if ($[14] !== editing || $[15] !== input || $[16] !== t) {
97
+ t6 = editing ? strip$1(t("editing"), t("cancelEdit"), () => {
98
+ left();
99
+ document.querySelector(`[id="${input}"]`)?.focus();
100
+ }) : null;
101
+ $[14] = editing;
102
+ $[15] = input;
103
+ $[16] = t;
104
+ $[17] = t6;
105
+ } else t6 = $[17];
106
+ let t7;
107
+ if ($[18] !== t) {
108
+ t7 = t("label");
109
+ $[18] = t;
110
+ $[19] = t7;
111
+ } else t7 = $[19];
112
+ let t8;
113
+ if ($[20] !== t) {
114
+ t8 = t("editPlaceholder");
115
+ $[20] = t;
116
+ $[21] = t8;
117
+ } else t8 = $[21];
118
+ let t9;
119
+ if ($[22] !== input || $[23] !== t7 || $[24] !== t8) {
120
+ t9 = /* @__PURE__ */ jsx(Input, {
121
+ id: input,
122
+ label: t7,
123
+ placeholder: t8
124
+ });
125
+ $[22] = input;
126
+ $[23] = t7;
127
+ $[24] = t8;
128
+ $[25] = t9;
129
+ } else t9 = $[25];
130
+ let t10;
131
+ if ($[26] !== t) {
132
+ t10 = t("send");
133
+ $[26] = t;
134
+ $[27] = t10;
135
+ } else t10 = $[27];
136
+ let t11;
137
+ if ($[28] === Symbol.for("react.memo_cache_sentinel")) {
138
+ t11 = /* @__PURE__ */ jsx(ArrowUpIcon, { size: "1em" });
139
+ $[28] = t11;
140
+ } else t11 = $[28];
141
+ let t12;
142
+ if ($[29] !== t10) {
143
+ t12 = /* @__PURE__ */ jsx(Toolbar, { children: /* @__PURE__ */ jsx(Submit, {
144
+ label: t10,
145
+ children: t11
146
+ }) });
147
+ $[29] = t10;
148
+ $[30] = t12;
149
+ } else t12 = $[30];
150
+ let t13;
151
+ if ($[31] !== t12 || $[32] !== t4 || $[33] !== t5 || $[34] !== t6 || $[35] !== t9 || $[36] !== text) {
152
+ t13 = /* @__PURE__ */ jsxs(Root$1, {
153
+ onCancelContext: left,
154
+ onEditLast: t4,
155
+ onSubmit: t5,
156
+ onValueChange: setText,
157
+ value: text,
158
+ children: [
159
+ t6,
160
+ t9,
161
+ t12
162
+ ]
163
+ });
164
+ $[31] = t12;
165
+ $[32] = t4;
166
+ $[33] = t5;
167
+ $[34] = t6;
168
+ $[35] = t9;
169
+ $[36] = text;
170
+ $[37] = t13;
171
+ } else t13 = $[37];
172
+ let t14;
173
+ if ($[38] !== t13 || $[39] !== t3) {
174
+ t14 = /* @__PURE__ */ jsxs(Stack, {
175
+ gap: "md",
176
+ children: [t3, t13]
177
+ });
178
+ $[38] = t13;
179
+ $[39] = t3;
180
+ $[40] = t14;
181
+ } else t14 = $[40];
182
+ return t14;
183
+ }
184
+ const source$4 = "import { type ReactElement, useId, useState } from \"react\";\n\nimport { ArrowUpIcon, PencilIcon, XIcon } from \"lucide-react\";\n\nimport { IconButton } from \"@stealthscale/component-actions\";\nimport { Stack } from \"@stealthscale/component-layout\";\nimport { Span } from \"@stealthscale/component-typography\";\nimport { useWords } from \"@stealthscale/specimen\";\n\nimport { Composer, Message } from \"@stealthscale/component-messaging\";\n\nfunction strip(editing: string, cancel: string, cancelled: () => void): ReactElement {\n return (\n <Composer.Context>\n <PencilIcon aria-hidden />\n <Span>{editing}</Span>\n <IconButton aria-label={cancel} onClick={cancelled} size=\"xs\" variant=\"ghost\">\n <XIcon size=\"1em\" />\n </IconButton>\n </Composer.Context>\n );\n}\n\nexport function EditLast(): ReactElement {\n const { t } = useWords(\"composer\");\n const input = useId();\n const [sent, setSent] = useState<readonly string[]>([t(\"first\")]);\n const [text, setText] = useState(\"\");\n const [editing, setEditing] = useState(false);\n const left = (): void => {\n setEditing(false);\n setText(\"\");\n };\n\n return (\n <Stack gap=\"md\">\n {sent.map((one, index) => (\n <Message.Root\n align=\"end\"\n aria-label={t(\"you\")}\n key={`${String(index)}-${one}`}\n look=\"solid\"\n palette=\"primary\"\n >\n <Message.Content>\n <Message.Bubble>{one}</Message.Bubble>\n </Message.Content>\n </Message.Root>\n ))}\n <Composer.Root\n onCancelContext={left}\n onEditLast={() => {\n setText(sent.at(-1) ?? \"\");\n setEditing(true);\n }}\n onSubmit={(value) => {\n setSent((was) => (editing ? [...was.slice(0, -1), value] : [...was, value]));\n left();\n }}\n onValueChange={setText}\n value={text}\n >\n {editing\n ? strip(t(\"editing\"), t(\"cancelEdit\"), () => {\n left();\n document.querySelector<HTMLElement>(`[id=\"${input}\"]`)?.focus();\n })\n : null}\n <Composer.Input id={input} label={t(\"label\")} placeholder={t(\"editPlaceholder\")} />\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";
185
+ //#endregion
186
+ //#region src/composer/examples/mentions.example.tsx
187
+ var mentions_example_exports = /* @__PURE__ */ __exportAll({
188
+ Mentions: () => Mentions,
189
+ source: () => source$3
190
+ });
191
+ const PEOPLE = [
192
+ {
193
+ id: "ada",
194
+ label: "Ada Okafor",
195
+ team: "finance"
196
+ },
197
+ {
198
+ id: "adil",
199
+ label: "Adil Rahman",
200
+ team: "engineering"
201
+ },
202
+ {
203
+ id: "ben",
204
+ label: "Ben Carter",
205
+ team: "sales"
206
+ },
207
+ {
208
+ id: "chioma",
209
+ label: "Chioma Eze",
210
+ team: "design"
211
+ }
212
+ ];
213
+ function marked(text, names) {
214
+ const pattern = new RegExp(`(${names.map((name) => `@${name}`).join("|")})`, "u");
215
+ return names.length === 0 ? [text] : text.split(pattern).map((part, index) => names.includes(part.slice(1)) ? /* @__PURE__ */ jsx(Mark, {
216
+ as: "span",
217
+ palette: "primary",
218
+ children: part
219
+ }, `${String(index)}-${part}`) : part);
220
+ }
221
+ function Mentions() {
222
+ const $ = c(58);
223
+ const { t } = useWords("composer");
224
+ const [query, setQuery] = useState();
225
+ let t0;
226
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
227
+ t0 = [];
228
+ $[0] = t0;
229
+ } else t0 = $[0];
230
+ const [names, setNames] = useState(t0);
231
+ let t1;
232
+ if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
233
+ t1 = [];
234
+ $[1] = t1;
235
+ } else t1 = $[1];
236
+ const [sent, setSent] = useState(t1);
237
+ let T0;
238
+ let T1;
239
+ let T2;
240
+ let t2;
241
+ let t3;
242
+ let t4;
243
+ let t5;
244
+ let t6;
245
+ let t7;
246
+ let t8;
247
+ let t9;
248
+ if ($[2] !== names || $[3] !== query || $[4] !== sent || $[5] !== t) {
249
+ const term = query?.term.toLowerCase() ?? "";
250
+ let t10;
251
+ if ($[17] !== t) {
252
+ t10 = (person) => ({
253
+ description: t(person.team),
254
+ id: person.id,
255
+ label: person.label
256
+ });
257
+ $[17] = t;
258
+ $[18] = t10;
259
+ } else t10 = $[18];
260
+ const people = PEOPLE.map(t10);
261
+ T2 = Stack;
262
+ t8 = "md";
263
+ if ($[19] !== names || $[20] !== sent || $[21] !== t) {
264
+ let t11;
265
+ if ($[23] !== names || $[24] !== t) {
266
+ t11 = (text, index) => /* @__PURE__ */ jsx(Root, {
267
+ align: "end",
268
+ "aria-label": t("you"),
269
+ children: /* @__PURE__ */ jsx(Content, { children: /* @__PURE__ */ jsx(Bubble, { children: marked(text, names) }) })
270
+ }, `${String(index)}-${text}`);
271
+ $[23] = names;
272
+ $[24] = t;
273
+ $[25] = t11;
274
+ } else t11 = $[25];
275
+ t9 = sent.map(t11);
276
+ $[19] = names;
277
+ $[20] = sent;
278
+ $[21] = t;
279
+ $[22] = t9;
280
+ } else t9 = $[22];
281
+ T1 = Root$1;
282
+ if ($[26] === Symbol.for("react.memo_cache_sentinel")) {
283
+ t7 = (text_0) => {
284
+ setSent((was) => [...was, text_0]);
285
+ };
286
+ $[26] = t7;
287
+ } else t7 = $[26];
288
+ T0 = Input;
289
+ if ($[27] !== t) {
290
+ t2 = t("label");
291
+ $[27] = t;
292
+ $[28] = t2;
293
+ } else t2 = $[28];
294
+ if ($[29] === Symbol.for("react.memo_cache_sentinel")) {
295
+ t3 = (person_0) => {
296
+ setNames((was_0) => [...was_0, person_0.label]);
297
+ };
298
+ $[29] = t3;
299
+ } else t3 = $[29];
300
+ t4 = setQuery;
301
+ if ($[30] !== t) {
302
+ t5 = t("mentionPlaceholder");
303
+ $[30] = t;
304
+ $[31] = t5;
305
+ } else t5 = $[31];
306
+ t6 = query === void 0 ? [] : people.filter((person_1) => person_1.label.toLowerCase().includes(term));
307
+ $[2] = names;
308
+ $[3] = query;
309
+ $[4] = sent;
310
+ $[5] = t;
311
+ $[6] = T0;
312
+ $[7] = T1;
313
+ $[8] = T2;
314
+ $[9] = t2;
315
+ $[10] = t3;
316
+ $[11] = t4;
317
+ $[12] = t5;
318
+ $[13] = t6;
319
+ $[14] = t7;
320
+ $[15] = t8;
321
+ $[16] = t9;
322
+ } else {
323
+ T0 = $[6];
324
+ T1 = $[7];
325
+ T2 = $[8];
326
+ t2 = $[9];
327
+ t3 = $[10];
328
+ t4 = $[11];
329
+ t5 = $[12];
330
+ t6 = $[13];
331
+ t7 = $[14];
332
+ t8 = $[15];
333
+ t9 = $[16];
334
+ }
335
+ let t10;
336
+ if ($[32] !== t) {
337
+ t10 = t("people");
338
+ $[32] = t;
339
+ $[33] = t10;
340
+ } else t10 = $[33];
341
+ let t11;
342
+ if ($[34] === Symbol.for("react.memo_cache_sentinel")) {
343
+ t11 = ["@"];
344
+ $[34] = t11;
345
+ } else t11 = $[34];
346
+ let t12;
347
+ if ($[35] !== T0 || $[36] !== t10 || $[37] !== t2 || $[38] !== t3 || $[39] !== t4 || $[40] !== t5 || $[41] !== t6) {
348
+ t12 = /* @__PURE__ */ jsx(T0, {
349
+ label: t2,
350
+ onMention: t3,
351
+ onQueryChange: t4,
352
+ placeholder: t5,
353
+ suggestions: t6,
354
+ suggestionsLabel: t10,
355
+ triggers: t11
356
+ });
357
+ $[35] = T0;
358
+ $[36] = t10;
359
+ $[37] = t2;
360
+ $[38] = t3;
361
+ $[39] = t4;
362
+ $[40] = t5;
363
+ $[41] = t6;
364
+ $[42] = t12;
365
+ } else t12 = $[42];
366
+ let t13;
367
+ if ($[43] !== t) {
368
+ t13 = t("send");
369
+ $[43] = t;
370
+ $[44] = t13;
371
+ } else t13 = $[44];
372
+ let t14;
373
+ if ($[45] === Symbol.for("react.memo_cache_sentinel")) {
374
+ t14 = /* @__PURE__ */ jsx(ArrowUpIcon, { size: "1em" });
375
+ $[45] = t14;
376
+ } else t14 = $[45];
377
+ let t15;
378
+ if ($[46] !== t13) {
379
+ t15 = /* @__PURE__ */ jsx(Toolbar, { children: /* @__PURE__ */ jsx(Submit, {
380
+ label: t13,
381
+ children: t14
382
+ }) });
383
+ $[46] = t13;
384
+ $[47] = t15;
385
+ } else t15 = $[47];
386
+ let t16;
387
+ if ($[48] !== T1 || $[49] !== t12 || $[50] !== t15 || $[51] !== t7) {
388
+ t16 = /* @__PURE__ */ jsxs(T1, {
389
+ onSubmit: t7,
390
+ children: [t12, t15]
391
+ });
392
+ $[48] = T1;
393
+ $[49] = t12;
394
+ $[50] = t15;
395
+ $[51] = t7;
396
+ $[52] = t16;
397
+ } else t16 = $[52];
398
+ let t17;
399
+ if ($[53] !== T2 || $[54] !== t16 || $[55] !== t8 || $[56] !== t9) {
400
+ t17 = /* @__PURE__ */ jsxs(T2, {
401
+ gap: t8,
402
+ children: [t9, t16]
403
+ });
404
+ $[53] = T2;
405
+ $[54] = t16;
406
+ $[55] = t8;
407
+ $[56] = t9;
408
+ $[57] = t17;
409
+ } else t17 = $[57];
410
+ return t17;
411
+ }
412
+ const source$3 = "import { type ReactElement, type ReactNode, useState } from \"react\";\n\nimport { ArrowUpIcon } from \"lucide-react\";\n\nimport { Stack } from \"@stealthscale/component-layout\";\nimport { Mark } from \"@stealthscale/component-typography\";\nimport { useWords } from \"@stealthscale/specimen\";\n\nimport { Composer, Message } from \"@stealthscale/component-messaging\";\n\nconst PEOPLE = [\n { id: \"ada\", label: \"Ada Okafor\", team: \"finance\" },\n { id: \"adil\", label: \"Adil Rahman\", team: \"engineering\" },\n { id: \"ben\", label: \"Ben Carter\", team: \"sales\" },\n { id: \"chioma\", label: \"Chioma Eze\", team: \"design\" },\n] as const;\n\nfunction marked(text: string, names: readonly string[]): ReactNode[] {\n const pattern = new RegExp(`(${names.map((name) => `@${name}`).join(\"|\")})`, \"u\");\n\n return names.length === 0\n ? [text]\n : text.split(pattern).map((part, index) =>\n names.includes(part.slice(1)) ? (\n <Mark as=\"span\" key={`${String(index)}-${part}`} palette=\"primary\">\n {part}\n </Mark>\n ) : (\n part\n ),\n );\n}\n\nexport function Mentions(): ReactElement {\n const { t } = useWords(\"composer\");\n const [query, setQuery] = useState<Composer.Query | undefined>();\n const [names, setNames] = useState<readonly string[]>([]);\n const [sent, setSent] = useState<readonly string[]>([]);\n const term = query?.term.toLowerCase() ?? \"\";\n const people: readonly Composer.Suggestion[] = PEOPLE.map((person) => ({\n description: t(person.team),\n id: person.id,\n label: person.label,\n }));\n\n return (\n <Stack gap=\"md\">\n {sent.map((text, index) => (\n <Message.Root align=\"end\" aria-label={t(\"you\")} key={`${String(index)}-${text}`}>\n <Message.Content>\n <Message.Bubble>{marked(text, names)}</Message.Bubble>\n </Message.Content>\n </Message.Root>\n ))}\n <Composer.Root\n onSubmit={(text) => {\n setSent((was) => [...was, text]);\n }}\n >\n <Composer.Input\n label={t(\"label\")}\n onMention={(person) => {\n setNames((was) => [...was, person.label]);\n }}\n onQueryChange={setQuery}\n placeholder={t(\"mentionPlaceholder\")}\n suggestions={\n query === undefined\n ? []\n : people.filter((person) => person.label.toLowerCase().includes(term))\n }\n suggestionsLabel={t(\"people\")}\n triggers={[\"@\"]}\n />\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";
413
+ //#endregion
414
+ //#region src/composer/examples/reply.example.tsx
415
+ var reply_example_exports = /* @__PURE__ */ __exportAll({
416
+ Reply: () => Reply,
417
+ source: () => source$2
418
+ });
419
+ function filed(files, label, named, removed) {
420
+ return /* @__PURE__ */ jsx(Attachments, { children: /* @__PURE__ */ jsx(Group, {
421
+ "aria-label": label,
422
+ orientation: "vertical",
423
+ size: "xs",
424
+ children: files.map((file) => /* @__PURE__ */ jsxs(Root$2, { children: [
425
+ /* @__PURE__ */ jsx(Media, { children: /* @__PURE__ */ jsx(FileIcon, { "aria-hidden": true }) }),
426
+ /* @__PURE__ */ jsx(Content$1, { children: /* @__PURE__ */ jsx(Title, { children: file.name }) }),
427
+ /* @__PURE__ */ jsx(Actions, { children: /* @__PURE__ */ jsx(IconButton, {
428
+ "aria-label": named(file.name),
429
+ onClick: () => {
430
+ removed(file);
431
+ },
432
+ size: "xs",
433
+ variant: "ghost",
434
+ children: /* @__PURE__ */ jsx(XIcon, { size: "1em" })
435
+ }) })
436
+ ] }, `${file.name}-${String(file.lastModified)}`))
437
+ }) });
438
+ }
439
+ function strip(replying, cancel, cancelled) {
440
+ return /* @__PURE__ */ jsxs(Context, { children: [
441
+ /* @__PURE__ */ jsx(ReplyIcon, { "aria-hidden": true }),
442
+ /* @__PURE__ */ jsx(Span, { children: replying }),
443
+ /* @__PURE__ */ jsx(IconButton, {
444
+ "aria-label": cancel,
445
+ onClick: cancelled,
446
+ size: "xs",
447
+ variant: "ghost",
448
+ children: /* @__PURE__ */ jsx(XIcon, { size: "1em" })
449
+ })
450
+ ] });
451
+ }
452
+ function Reply() {
453
+ const $ = c(49);
454
+ const { t } = useWords("composer");
455
+ const text = useId();
456
+ const [replying, setReplying] = useState(true);
457
+ let t0;
458
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
459
+ t0 = [];
460
+ $[0] = t0;
461
+ } else t0 = $[0];
462
+ const [files, setFiles] = useState(t0);
463
+ const [sent, setSent] = useState("");
464
+ let t1;
465
+ if ($[1] !== text) {
466
+ t1 = () => {
467
+ document.querySelector(`[id="${text}"]`)?.focus();
468
+ };
469
+ $[1] = text;
470
+ $[2] = t1;
471
+ } else t1 = $[2];
472
+ const focused = t1;
473
+ const t2 = files.length > 0;
474
+ let t3;
475
+ let t4;
476
+ if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
477
+ t3 = (picked) => {
478
+ setFiles((was) => [...was, ...picked]);
479
+ };
480
+ t4 = () => {
481
+ setReplying(false);
482
+ };
483
+ $[3] = t3;
484
+ $[4] = t4;
485
+ } else {
486
+ t3 = $[3];
487
+ t4 = $[4];
488
+ }
489
+ let t5;
490
+ if ($[5] !== files.length || $[6] !== t) {
491
+ t5 = (typed) => {
492
+ setSent(t("sent", {
493
+ count: files.length,
494
+ text: typed
495
+ }));
496
+ setFiles([]);
497
+ setReplying(false);
498
+ };
499
+ $[5] = files.length;
500
+ $[6] = t;
501
+ $[7] = t5;
502
+ } else t5 = $[7];
503
+ let t6;
504
+ if ($[8] !== focused || $[9] !== replying || $[10] !== t) {
505
+ t6 = replying ? strip(t("replying"), t("cancelReply"), () => {
506
+ setReplying(false);
507
+ focused();
508
+ }) : null;
509
+ $[8] = focused;
510
+ $[9] = replying;
511
+ $[10] = t;
512
+ $[11] = t6;
513
+ } else t6 = $[11];
514
+ let t7;
515
+ if ($[12] !== files || $[13] !== focused || $[14] !== t) {
516
+ t7 = files.length === 0 ? null : filed(files, t("files"), (name) => t("remove", { name }), (file) => {
517
+ setFiles((was_0) => was_0.filter((one) => one !== file));
518
+ focused();
519
+ });
520
+ $[12] = files;
521
+ $[13] = focused;
522
+ $[14] = t;
523
+ $[15] = t7;
524
+ } else t7 = $[15];
525
+ let t8;
526
+ if ($[16] !== t) {
527
+ t8 = t("label");
528
+ $[16] = t;
529
+ $[17] = t8;
530
+ } else t8 = $[17];
531
+ let t9;
532
+ if ($[18] !== t) {
533
+ t9 = t("replyPlaceholder");
534
+ $[18] = t;
535
+ $[19] = t9;
536
+ } else t9 = $[19];
537
+ let t10;
538
+ if ($[20] !== t8 || $[21] !== t9 || $[22] !== text) {
539
+ t10 = /* @__PURE__ */ jsx(Input, {
540
+ id: text,
541
+ label: t8,
542
+ placeholder: t9
543
+ });
544
+ $[20] = t8;
545
+ $[21] = t9;
546
+ $[22] = text;
547
+ $[23] = t10;
548
+ } else t10 = $[23];
549
+ let t11;
550
+ if ($[24] !== t) {
551
+ t11 = t("attach");
552
+ $[24] = t;
553
+ $[25] = t11;
554
+ } else t11 = $[25];
555
+ let t12;
556
+ if ($[26] === Symbol.for("react.memo_cache_sentinel")) {
557
+ t12 = /* @__PURE__ */ jsx(PaperclipIcon, { size: "1em" });
558
+ $[26] = t12;
559
+ } else t12 = $[26];
560
+ let t13;
561
+ if ($[27] !== t11) {
562
+ t13 = /* @__PURE__ */ jsx(AttachTrigger, {
563
+ label: t11,
564
+ children: t12
565
+ });
566
+ $[27] = t11;
567
+ $[28] = t13;
568
+ } else t13 = $[28];
569
+ let t14;
570
+ if ($[29] !== t) {
571
+ t14 = t("send");
572
+ $[29] = t;
573
+ $[30] = t14;
574
+ } else t14 = $[30];
575
+ let t15;
576
+ if ($[31] === Symbol.for("react.memo_cache_sentinel")) {
577
+ t15 = /* @__PURE__ */ jsx(ArrowUpIcon, { size: "1em" });
578
+ $[31] = t15;
579
+ } else t15 = $[31];
580
+ let t16;
581
+ if ($[32] !== t14) {
582
+ t16 = /* @__PURE__ */ jsx(Submit, {
583
+ label: t14,
584
+ children: t15
585
+ });
586
+ $[32] = t14;
587
+ $[33] = t16;
588
+ } else t16 = $[33];
589
+ let t17;
590
+ if ($[34] !== t13 || $[35] !== t16) {
591
+ t17 = /* @__PURE__ */ jsxs(Toolbar, { children: [t13, t16] });
592
+ $[34] = t13;
593
+ $[35] = t16;
594
+ $[36] = t17;
595
+ } else t17 = $[36];
596
+ let t18;
597
+ if ($[37] !== t10 || $[38] !== t17 || $[39] !== t2 || $[40] !== t5 || $[41] !== t6 || $[42] !== t7) {
598
+ t18 = /* @__PURE__ */ jsxs(Root$1, {
599
+ attached: t2,
600
+ onAttach: t3,
601
+ onCancelContext: t4,
602
+ onSubmit: t5,
603
+ children: [
604
+ t6,
605
+ t7,
606
+ t10,
607
+ t17
608
+ ]
609
+ });
610
+ $[37] = t10;
611
+ $[38] = t17;
612
+ $[39] = t2;
613
+ $[40] = t5;
614
+ $[41] = t6;
615
+ $[42] = t7;
616
+ $[43] = t18;
617
+ } else t18 = $[43];
618
+ let t19;
619
+ if ($[44] !== sent) {
620
+ t19 = /* @__PURE__ */ jsx(Text, {
621
+ as: "output",
622
+ size: "sm",
623
+ children: sent
624
+ });
625
+ $[44] = sent;
626
+ $[45] = t19;
627
+ } else t19 = $[45];
628
+ let t20;
629
+ if ($[46] !== t18 || $[47] !== t19) {
630
+ t20 = /* @__PURE__ */ jsxs(Stack, {
631
+ gap: "sm",
632
+ children: [t18, t19]
633
+ });
634
+ $[46] = t18;
635
+ $[47] = t19;
636
+ $[48] = t20;
637
+ } else t20 = $[48];
638
+ return t20;
639
+ }
640
+ const source$2 = "import { type ReactElement, useId, useState } from \"react\";\n\nimport { ArrowUpIcon, FileIcon, PaperclipIcon, ReplyIcon, XIcon } from \"lucide-react\";\n\nimport { IconButton } from \"@stealthscale/component-actions\";\nimport { Stack } from \"@stealthscale/component-layout\";\nimport { Span, Text } from \"@stealthscale/component-typography\";\nimport { useWords } from \"@stealthscale/specimen\";\n\nimport { Attachment, Composer } from \"@stealthscale/component-messaging\";\n\nfunction filed(\n files: readonly File[],\n label: string,\n named: (file: string) => string,\n removed: (file: File) => void,\n): ReactElement {\n return (\n <Composer.Attachments>\n <Attachment.Group aria-label={label} orientation=\"vertical\" size=\"xs\">\n {files.map((file) => (\n <Attachment.Root key={`${file.name}-${String(file.lastModified)}`}>\n <Attachment.Media>\n <FileIcon aria-hidden />\n </Attachment.Media>\n <Attachment.Content>\n <Attachment.Title>{file.name}</Attachment.Title>\n </Attachment.Content>\n <Attachment.Actions>\n <IconButton\n aria-label={named(file.name)}\n onClick={() => {\n removed(file);\n }}\n size=\"xs\"\n variant=\"ghost\"\n >\n <XIcon size=\"1em\" />\n </IconButton>\n </Attachment.Actions>\n </Attachment.Root>\n ))}\n </Attachment.Group>\n </Composer.Attachments>\n );\n}\n\nfunction strip(replying: string, cancel: string, cancelled: () => void): ReactElement {\n return (\n <Composer.Context>\n <ReplyIcon aria-hidden />\n <Span>{replying}</Span>\n <IconButton aria-label={cancel} onClick={cancelled} size=\"xs\" variant=\"ghost\">\n <XIcon size=\"1em\" />\n </IconButton>\n </Composer.Context>\n );\n}\n\nexport function Reply(): ReactElement {\n const { t } = useWords(\"composer\");\n const text = useId();\n const [replying, setReplying] = useState(true);\n const [files, setFiles] = useState<readonly File[]>([]);\n const [sent, setSent] = useState(\"\");\n const focused = (): void => {\n document.querySelector<HTMLElement>(`[id=\"${text}\"]`)?.focus();\n };\n\n return (\n <Stack gap=\"sm\">\n <Composer.Root\n attached={files.length > 0}\n onAttach={(picked) => {\n setFiles((was) => [...was, ...picked]);\n }}\n onCancelContext={() => {\n setReplying(false);\n }}\n onSubmit={(typed) => {\n setSent(t(\"sent\", { count: files.length, text: typed }));\n setFiles([]);\n setReplying(false);\n }}\n >\n {replying\n ? strip(t(\"replying\"), t(\"cancelReply\"), () => {\n setReplying(false);\n focused();\n })\n : null}\n {files.length === 0\n ? null\n : filed(\n files,\n t(\"files\"),\n (name) => t(\"remove\", { name }),\n (file) => {\n setFiles((was) => was.filter((one) => one !== file));\n focused();\n },\n )}\n <Composer.Input id={text} label={t(\"label\")} placeholder={t(\"replyPlaceholder\")} />\n <Composer.Toolbar>\n <Composer.AttachTrigger label={t(\"attach\")}>\n <PaperclipIcon size=\"1em\" />\n </Composer.AttachTrigger>\n <Composer.Submit label={t(\"send\")}>\n <ArrowUpIcon size=\"1em\" />\n </Composer.Submit>\n </Composer.Toolbar>\n </Composer.Root>\n <Text as=\"output\" size=\"sm\">\n {sent}\n </Text>\n </Stack>\n );\n}\n";
641
+ //#endregion
642
+ //#region src/composer/examples/stopping.example.tsx
643
+ var stopping_example_exports = /* @__PURE__ */ __exportAll({
644
+ Stopping: () => Stopping,
645
+ source: () => source$1
646
+ });
647
+ function Stopping() {
648
+ const $ = c(33);
649
+ const { t } = useWords("composer");
650
+ const [busy, setBusy] = useState(false);
651
+ let t0;
652
+ let t1;
653
+ if ($[0] !== busy) {
654
+ t0 = () => {
655
+ const timer = busy ? setTimeout(() => {
656
+ setBusy(false);
657
+ }, 4e3) : void 0;
658
+ return () => {
659
+ clearTimeout(timer);
660
+ };
661
+ };
662
+ t1 = [busy];
663
+ $[0] = busy;
664
+ $[1] = t0;
665
+ $[2] = t1;
666
+ } else {
667
+ t0 = $[1];
668
+ t1 = $[2];
669
+ }
670
+ useEffect(t0, t1);
671
+ let t2;
672
+ let t3;
673
+ if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
674
+ t2 = () => {
675
+ setBusy(false);
676
+ };
677
+ t3 = () => {
678
+ setBusy(true);
679
+ };
680
+ $[3] = t2;
681
+ $[4] = t3;
682
+ } else {
683
+ t2 = $[3];
684
+ t3 = $[4];
685
+ }
686
+ let t4;
687
+ if ($[5] !== t) {
688
+ t4 = t("askLabel");
689
+ $[5] = t;
690
+ $[6] = t4;
691
+ } else t4 = $[6];
692
+ let t5;
693
+ if ($[7] !== t) {
694
+ t5 = t("askPlaceholder");
695
+ $[7] = t;
696
+ $[8] = t5;
697
+ } else t5 = $[8];
698
+ let t6;
699
+ if ($[9] !== t4 || $[10] !== t5) {
700
+ t6 = /* @__PURE__ */ jsx(Input, {
701
+ label: t4,
702
+ placeholder: t5
703
+ });
704
+ $[9] = t4;
705
+ $[10] = t5;
706
+ $[11] = t6;
707
+ } else t6 = $[11];
708
+ let t7;
709
+ if ($[12] !== busy || $[13] !== t) {
710
+ t7 = busy ? t("answering") : t("hint");
711
+ $[12] = busy;
712
+ $[13] = t;
713
+ $[14] = t7;
714
+ } else t7 = $[14];
715
+ let t8;
716
+ if ($[15] !== t7) {
717
+ t8 = /* @__PURE__ */ jsx(Span, {
718
+ tone: "muted",
719
+ children: t7
720
+ });
721
+ $[15] = t7;
722
+ $[16] = t8;
723
+ } else t8 = $[16];
724
+ let t9;
725
+ if ($[17] !== t) {
726
+ t9 = t("send");
727
+ $[17] = t;
728
+ $[18] = t9;
729
+ } else t9 = $[18];
730
+ let t10;
731
+ if ($[19] === Symbol.for("react.memo_cache_sentinel")) {
732
+ t10 = /* @__PURE__ */ jsx(SquareIcon, { size: "1em" });
733
+ $[19] = t10;
734
+ } else t10 = $[19];
735
+ let t11;
736
+ if ($[20] !== t) {
737
+ t11 = t("stop");
738
+ $[20] = t;
739
+ $[21] = t11;
740
+ } else t11 = $[21];
741
+ let t12;
742
+ if ($[22] === Symbol.for("react.memo_cache_sentinel")) {
743
+ t12 = /* @__PURE__ */ jsx(ArrowUpIcon, { size: "1em" });
744
+ $[22] = t12;
745
+ } else t12 = $[22];
746
+ let t13;
747
+ if ($[23] !== t11 || $[24] !== t9) {
748
+ t13 = /* @__PURE__ */ jsx(Submit, {
749
+ label: t9,
750
+ stopIcon: t10,
751
+ stopLabel: t11,
752
+ children: t12
753
+ });
754
+ $[23] = t11;
755
+ $[24] = t9;
756
+ $[25] = t13;
757
+ } else t13 = $[25];
758
+ let t14;
759
+ if ($[26] !== t13 || $[27] !== t8) {
760
+ t14 = /* @__PURE__ */ jsxs(Toolbar, { children: [t8, t13] });
761
+ $[26] = t13;
762
+ $[27] = t8;
763
+ $[28] = t14;
764
+ } else t14 = $[28];
765
+ let t15;
766
+ if ($[29] !== busy || $[30] !== t14 || $[31] !== t6) {
767
+ t15 = /* @__PURE__ */ jsxs(Root$1, {
768
+ busy,
769
+ onStop: t2,
770
+ onSubmit: t3,
771
+ submitOn: "modEnter",
772
+ children: [t6, t14]
773
+ });
774
+ $[29] = busy;
775
+ $[30] = t14;
776
+ $[31] = t6;
777
+ $[32] = t15;
778
+ } else t15 = $[32];
779
+ return t15;
780
+ }
781
+ const source$1 = "import { type ReactElement, useEffect, useState } from \"react\";\n\nimport { ArrowUpIcon, SquareIcon } from \"lucide-react\";\n\nimport { Span } from \"@stealthscale/component-typography\";\nimport { useWords } from \"@stealthscale/specimen\";\n\nimport { Composer } from \"@stealthscale/component-messaging\";\n\nexport function Stopping(): ReactElement {\n const { t } = useWords(\"composer\");\n const [busy, setBusy] = useState(false);\n\n useEffect(() => {\n const timer = busy\n ? setTimeout(() => {\n setBusy(false);\n }, 4000)\n : undefined;\n\n return (): void => {\n clearTimeout(timer);\n };\n }, [busy]);\n\n return (\n <Composer.Root\n busy={busy}\n onStop={() => {\n setBusy(false);\n }}\n onSubmit={() => {\n setBusy(true);\n }}\n submitOn=\"modEnter\"\n >\n <Composer.Input label={t(\"askLabel\")} placeholder={t(\"askPlaceholder\")} />\n <Composer.Toolbar>\n <Span tone=\"muted\">{busy ? t(\"answering\") : t(\"hint\")}</Span>\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 );\n}\n";
782
+ //#endregion
783
+ //#region src/composer/examples/writing.example.tsx
784
+ var writing_example_exports = /* @__PURE__ */ __exportAll({
785
+ Writing: () => Writing,
786
+ source: () => source
787
+ });
788
+ function Writing(props) {
789
+ const $ = c(24);
790
+ const { t } = useWords("composer");
791
+ let t0;
792
+ if ($[0] !== t) {
793
+ t0 = t("label");
794
+ $[0] = t;
795
+ $[1] = t0;
796
+ } else t0 = $[1];
797
+ let t1;
798
+ if ($[2] !== t) {
799
+ t1 = t("placeholder");
800
+ $[2] = t;
801
+ $[3] = t1;
802
+ } else t1 = $[3];
803
+ let t2;
804
+ if ($[4] !== t0 || $[5] !== t1) {
805
+ t2 = /* @__PURE__ */ jsx(Input, {
806
+ label: t0,
807
+ placeholder: t1
808
+ });
809
+ $[4] = t0;
810
+ $[5] = t1;
811
+ $[6] = t2;
812
+ } else t2 = $[6];
813
+ let t3;
814
+ if ($[7] !== t) {
815
+ t3 = t("attach");
816
+ $[7] = t;
817
+ $[8] = t3;
818
+ } else t3 = $[8];
819
+ let t4;
820
+ if ($[9] === Symbol.for("react.memo_cache_sentinel")) {
821
+ t4 = /* @__PURE__ */ jsx(PaperclipIcon, { size: "1em" });
822
+ $[9] = t4;
823
+ } else t4 = $[9];
824
+ let t5;
825
+ if ($[10] !== t3) {
826
+ t5 = /* @__PURE__ */ jsx(AttachTrigger, {
827
+ label: t3,
828
+ children: t4
829
+ });
830
+ $[10] = t3;
831
+ $[11] = t5;
832
+ } else t5 = $[11];
833
+ let t6;
834
+ if ($[12] !== t) {
835
+ t6 = t("send");
836
+ $[12] = t;
837
+ $[13] = t6;
838
+ } else t6 = $[13];
839
+ let t7;
840
+ if ($[14] === Symbol.for("react.memo_cache_sentinel")) {
841
+ t7 = /* @__PURE__ */ jsx(ArrowUpIcon, { size: "1em" });
842
+ $[14] = t7;
843
+ } else t7 = $[14];
844
+ let t8;
845
+ if ($[15] !== t6) {
846
+ t8 = /* @__PURE__ */ jsx(Submit, {
847
+ label: t6,
848
+ children: t7
849
+ });
850
+ $[15] = t6;
851
+ $[16] = t8;
852
+ } else t8 = $[16];
853
+ let t9;
854
+ if ($[17] !== t5 || $[18] !== t8) {
855
+ t9 = /* @__PURE__ */ jsxs(Toolbar, { children: [t5, t8] });
856
+ $[17] = t5;
857
+ $[18] = t8;
858
+ $[19] = t9;
859
+ } else t9 = $[19];
860
+ let t10;
861
+ if ($[20] !== props || $[21] !== t2 || $[22] !== t9) {
862
+ t10 = /* @__PURE__ */ jsxs(Root$1, {
863
+ ...props,
864
+ children: [t2, t9]
865
+ });
866
+ $[20] = props;
867
+ $[21] = t2;
868
+ $[22] = t9;
869
+ $[23] = t10;
870
+ } else t10 = $[23];
871
+ return t10;
872
+ }
873
+ const source = "import { type ReactElement } from \"react\";\n\nimport { ArrowUpIcon, PaperclipIcon } from \"lucide-react\";\n\nimport { useWords } from \"@stealthscale/specimen\";\n\nimport { Composer } from \"@stealthscale/component-messaging\";\n\nexport function Writing(props: Composer.RootProps): ReactElement {\n const { t } = useWords(\"composer\");\n\n return (\n <Composer.Root {...props}>\n <Composer.Input label={t(\"label\")} placeholder={t(\"placeholder\")} />\n <Composer.Toolbar>\n <Composer.AttachTrigger label={t(\"attach\")}>\n <PaperclipIcon size=\"1em\" />\n </Composer.AttachTrigger>\n <Composer.Submit label={t(\"send\")}>\n <ArrowUpIcon size=\"1em\" />\n </Composer.Submit>\n </Composer.Toolbar>\n </Composer.Root>\n );\n}\n";
874
+ //#endregion
875
+ //#region src/composer/composer.specimen.tsx
876
+ /**
877
+ * Catalogue page for the composer.
878
+ *
879
+ * @remarks
880
+ * `scenesOf` generates the look and size scenes from a composer with a text and the attach and
881
+ * send controls, each in a room at the `sm` measure, the width of a phone. Hand-written scenes
882
+ * render a reply with files, an answer the send control stops, and the edit of the last message.
883
+ * The words are keys under `composer` in `locales/en/specimen/composer.json`.
884
+ */
885
+ /**
886
+ * Hand-written scene for a reply with files.
887
+ */
888
+ const replied = {
889
+ about: "composer.reply.about",
890
+ draw: () => /* @__PURE__ */ jsx(Room, {
891
+ size: "sm",
892
+ children: /* @__PURE__ */ jsx(Reply, {})
893
+ }),
894
+ example: reply_example_exports,
895
+ title: "composer.reply.title"
896
+ };
897
+ /**
898
+ * Hand-written scene for an answer the send control stops.
899
+ */
900
+ const stopped = {
901
+ about: "composer.stopping.about",
902
+ draw: () => /* @__PURE__ */ jsx(Room, {
903
+ size: "sm",
904
+ children: /* @__PURE__ */ jsx(Stopping, {})
905
+ }),
906
+ example: stopping_example_exports,
907
+ title: "composer.stopping.title"
908
+ };
909
+ /**
910
+ * Hand-written scene for mentions typed with `@`.
911
+ */
912
+ const mentioned = {
913
+ about: "composer.mentions.about",
914
+ draw: () => /* @__PURE__ */ jsx(Room, {
915
+ size: "sm",
916
+ children: /* @__PURE__ */ jsx(Mentions, {})
917
+ }),
918
+ example: mentions_example_exports,
919
+ title: "composer.mentions.title"
920
+ };
921
+ /**
922
+ * Hand-written scene for the edit of the last message.
923
+ */
924
+ const edited = {
925
+ about: "composer.edit.about",
926
+ draw: () => /* @__PURE__ */ jsx(Room, {
927
+ size: "sm",
928
+ children: /* @__PURE__ */ jsx(EditLast, {})
929
+ }),
930
+ example: edit_example_exports,
931
+ title: "composer.edit.title"
932
+ };
933
+ var composer_specimen_default = specimen({
934
+ about: "composer.about",
935
+ id: "components/messaging/composer",
936
+ imports: "import { Composer } from \"@stealthscale/component-messaging\";",
937
+ scenes: [
938
+ ...scenesOf(recipe, {
939
+ axes: {
940
+ size: { direction: "column" },
941
+ variant: { direction: "column" }
942
+ },
943
+ draw: (props) => /* @__PURE__ */ jsx(Room, {
944
+ size: "sm",
945
+ children: /* @__PURE__ */ jsx(Writing, { ...props })
946
+ }),
947
+ example: writing_example_exports,
948
+ namespace: "composer",
949
+ order: ["variant", "size"]
950
+ }),
951
+ replied,
952
+ mentioned,
953
+ stopped,
954
+ edited
955
+ ],
956
+ title: "composer.title"
957
+ });
958
+ //#endregion
959
+ export { composer_specimen_default as default, edited, mentioned, replied, stopped };