@stealthscale/component-messaging 0.2.1 → 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.
- package/dist/cyclonedx/bom.json +1 -1
- package/dist/index.js +1 -2247
- package/dist/pages/attachment/attachment.js +488 -0
- package/dist/pages/attachment/attachment.props.js +3 -0
- package/dist/pages/composer/composer.js +959 -0
- package/dist/pages/composer/composer.props.js +3 -0
- package/dist/pages/conversation/conversation.js +1237 -0
- package/dist/pages/conversation/conversation.props.js +3 -0
- package/dist/pages/message/message.js +1055 -0
- package/dist/pages/message/message.props.js +3 -0
- package/dist/pages/pages.json +66 -0
- package/dist/pages/reactions/reactions.js +246 -0
- package/dist/pages/reactions/reactions.props.js +3 -0
- package/dist/shared-B0H4dgkB.js +2248 -0
- package/dist/theme.js +1 -1
- package/locales/en/specimen/attachment.json +66 -0
- package/locales/en/specimen/composer.json +56 -0
- package/locales/en/specimen/conversation.json +70 -0
- package/locales/en/specimen/message.json +88 -0
- package/locales/en/specimen/reactions.json +23 -0
- package/package.json +10 -9
- /package/dist/{recipe-D1oVFnRU.js → recipes-D1oVFnRU.js} +0 -0
|
@@ -0,0 +1,2248 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
+
import { a as recipe$1, i as ROWS, n as recipe$3, o as recipe, r as recipe$2, t as recipe$4 } from "./recipes-D1oVFnRU.js";
|
|
3
|
+
import { createSlotRecipeContext } from "@stealthscale/theme";
|
|
4
|
+
import { c } from "react/compiler-runtime";
|
|
5
|
+
import { createRequiredContext, omitUndefined, useCallbackRef, useControllableState, useMediaQuery } from "@stealthscale/hooks";
|
|
6
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
import { useId, useRef, useState } from "react";
|
|
8
|
+
import { Button, ButtonPropsProvider, IconButton } from "@stealthscale/component-actions";
|
|
9
|
+
import { flushSync } from "react-dom";
|
|
10
|
+
import { Portal, ScrollArea } from "@stealthscale/component-primitives";
|
|
11
|
+
import { useStickToBottom } from "use-stick-to-bottom";
|
|
12
|
+
import { Popover } from "@stealthscale/component-disclosure";
|
|
13
|
+
//#region src/attachment/context.ts
|
|
14
|
+
/**
|
|
15
|
+
* Binds the attachment recipe to the elements that render it.
|
|
16
|
+
*
|
|
17
|
+
* @remarks
|
|
18
|
+
* The binding is kept out of `recipe.ts` because an application's style compiler imports the
|
|
19
|
+
* recipe at build time, and a binding there would pull the React runtime into that import.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Supplies the element factories the attachment parts are built from.
|
|
23
|
+
*/
|
|
24
|
+
const { withContext: withContext$4, withProvider: withProvider$4 } = createSlotRecipeContext(recipe);
|
|
25
|
+
//#endregion
|
|
26
|
+
//#region src/attachment/actions.ts
|
|
27
|
+
/**
|
|
28
|
+
* Renders the actions' `div`.
|
|
29
|
+
*/
|
|
30
|
+
const Actions$1 = withContext$4("div", "actions");
|
|
31
|
+
//#endregion
|
|
32
|
+
//#region src/attachment/content.ts
|
|
33
|
+
/**
|
|
34
|
+
* Renders the column's `div`.
|
|
35
|
+
*/
|
|
36
|
+
const Content$2 = withContext$4("div", "content");
|
|
37
|
+
//#endregion
|
|
38
|
+
//#region src/attachment/description.ts
|
|
39
|
+
/**
|
|
40
|
+
* Renders the description's `span`.
|
|
41
|
+
*/
|
|
42
|
+
const Description = withContext$4("span", "description");
|
|
43
|
+
//#endregion
|
|
44
|
+
//#region src/attachment/state.ts
|
|
45
|
+
/**
|
|
46
|
+
* Provides a group's size and orientation to the attachments inside it, and tells an attachment
|
|
47
|
+
* that it is an item of a list.
|
|
48
|
+
*/
|
|
49
|
+
/**
|
|
50
|
+
* Provides a group's defaults, and reads them where an attachment renders, undefined outside a
|
|
51
|
+
* group.
|
|
52
|
+
*/
|
|
53
|
+
const [GroupProvider, , useGroupDefaults] = createRequiredContext("Attachment.Group");
|
|
54
|
+
//#endregion
|
|
55
|
+
//#region src/attachment/group.tsx
|
|
56
|
+
/**
|
|
57
|
+
* Renders the `ul` with the attachment's group class.
|
|
58
|
+
*/
|
|
59
|
+
const Listed$1 = withProvider$4("ul", "group");
|
|
60
|
+
/**
|
|
61
|
+
* Renders the list and provides its size and orientation to the attachments inside.
|
|
62
|
+
*
|
|
63
|
+
* @param props - The size and orientation of the attachments, and the props of a `ul`.
|
|
64
|
+
* @returns The `ul` element inside the provider.
|
|
65
|
+
*/
|
|
66
|
+
function Group(props) {
|
|
67
|
+
const $ = c(8);
|
|
68
|
+
const { orientation, size } = props;
|
|
69
|
+
let t0;
|
|
70
|
+
if ($[0] !== orientation || $[1] !== size) {
|
|
71
|
+
t0 = omitUndefined({
|
|
72
|
+
orientation,
|
|
73
|
+
size
|
|
74
|
+
});
|
|
75
|
+
$[0] = orientation;
|
|
76
|
+
$[1] = size;
|
|
77
|
+
$[2] = t0;
|
|
78
|
+
} else t0 = $[2];
|
|
79
|
+
let t1;
|
|
80
|
+
if ($[3] !== props) {
|
|
81
|
+
t1 = /* @__PURE__ */ jsx(Listed$1, { ...props });
|
|
82
|
+
$[3] = props;
|
|
83
|
+
$[4] = t1;
|
|
84
|
+
} else t1 = $[4];
|
|
85
|
+
let t2;
|
|
86
|
+
if ($[5] !== t0 || $[6] !== t1) {
|
|
87
|
+
t2 = /* @__PURE__ */ jsx(GroupProvider, {
|
|
88
|
+
value: t0,
|
|
89
|
+
children: t1
|
|
90
|
+
});
|
|
91
|
+
$[5] = t0;
|
|
92
|
+
$[6] = t1;
|
|
93
|
+
$[7] = t2;
|
|
94
|
+
} else t2 = $[7];
|
|
95
|
+
return t2;
|
|
96
|
+
}
|
|
97
|
+
//#endregion
|
|
98
|
+
//#region src/attachment/media.ts
|
|
99
|
+
/**
|
|
100
|
+
* Renders the media square's `div`.
|
|
101
|
+
*/
|
|
102
|
+
const Media = withContext$4("div", "media");
|
|
103
|
+
//#endregion
|
|
104
|
+
//#region src/attachment/root.tsx
|
|
105
|
+
/**
|
|
106
|
+
* Renders the `div` with the attachment's variants.
|
|
107
|
+
*/
|
|
108
|
+
const Framed$1 = withProvider$4("div", "root");
|
|
109
|
+
/**
|
|
110
|
+
* Renders the attachment, an `li` inside a group.
|
|
111
|
+
*
|
|
112
|
+
* @param props - The state, the recipe's variants and the props of a `div`.
|
|
113
|
+
* @returns The `div` or `li` element.
|
|
114
|
+
*/
|
|
115
|
+
function Root$4(t0) {
|
|
116
|
+
const $ = c(15);
|
|
117
|
+
let orientation;
|
|
118
|
+
let props;
|
|
119
|
+
let size;
|
|
120
|
+
let t1;
|
|
121
|
+
if ($[0] !== t0) {
|
|
122
|
+
({orientation, size, state: t1, ...props} = t0);
|
|
123
|
+
$[0] = t0;
|
|
124
|
+
$[1] = orientation;
|
|
125
|
+
$[2] = props;
|
|
126
|
+
$[3] = size;
|
|
127
|
+
$[4] = t1;
|
|
128
|
+
} else {
|
|
129
|
+
orientation = $[1];
|
|
130
|
+
props = $[2];
|
|
131
|
+
size = $[3];
|
|
132
|
+
t1 = $[4];
|
|
133
|
+
}
|
|
134
|
+
const state = t1 === void 0 ? "done" : t1;
|
|
135
|
+
const group = useGroupDefaults();
|
|
136
|
+
let t2;
|
|
137
|
+
if ($[5] !== group) {
|
|
138
|
+
t2 = group === void 0 ? {} : { as: "li" };
|
|
139
|
+
$[5] = group;
|
|
140
|
+
$[6] = t2;
|
|
141
|
+
} else t2 = $[6];
|
|
142
|
+
const t3 = orientation ?? group?.orientation;
|
|
143
|
+
const t4 = size ?? group?.size;
|
|
144
|
+
let t5;
|
|
145
|
+
if ($[7] !== t3 || $[8] !== t4) {
|
|
146
|
+
t5 = omitUndefined({
|
|
147
|
+
orientation: t3,
|
|
148
|
+
size: t4
|
|
149
|
+
});
|
|
150
|
+
$[7] = t3;
|
|
151
|
+
$[8] = t4;
|
|
152
|
+
$[9] = t5;
|
|
153
|
+
} else t5 = $[9];
|
|
154
|
+
let t6;
|
|
155
|
+
if ($[10] !== props || $[11] !== state || $[12] !== t2 || $[13] !== t5) {
|
|
156
|
+
t6 = /* @__PURE__ */ jsx(Framed$1, {
|
|
157
|
+
"data-state": state,
|
|
158
|
+
...t2,
|
|
159
|
+
...t5,
|
|
160
|
+
...props
|
|
161
|
+
});
|
|
162
|
+
$[10] = props;
|
|
163
|
+
$[11] = state;
|
|
164
|
+
$[12] = t2;
|
|
165
|
+
$[13] = t5;
|
|
166
|
+
$[14] = t6;
|
|
167
|
+
} else t6 = $[14];
|
|
168
|
+
return t6;
|
|
169
|
+
}
|
|
170
|
+
//#endregion
|
|
171
|
+
//#region src/attachment/title.ts
|
|
172
|
+
/**
|
|
173
|
+
* Renders the title's `span`.
|
|
174
|
+
*/
|
|
175
|
+
const Title = withContext$4("span", "title");
|
|
176
|
+
//#endregion
|
|
177
|
+
//#region src/attachment/index.ts
|
|
178
|
+
var attachment_exports = /* @__PURE__ */ __exportAll({
|
|
179
|
+
Actions: () => Actions$1,
|
|
180
|
+
Content: () => Content$2,
|
|
181
|
+
Description: () => Description,
|
|
182
|
+
Group: () => Group,
|
|
183
|
+
Media: () => Media,
|
|
184
|
+
Root: () => Root$4,
|
|
185
|
+
Title: () => Title
|
|
186
|
+
});
|
|
187
|
+
//#endregion
|
|
188
|
+
//#region src/composer/state.ts
|
|
189
|
+
/**
|
|
190
|
+
* Provides what the composer's root knows to its parts: the text, whether it can be sent, and the
|
|
191
|
+
* handlers the parts call.
|
|
192
|
+
*/
|
|
193
|
+
/**
|
|
194
|
+
* Provides the state to the parts, and reads it where a part renders.
|
|
195
|
+
*/
|
|
196
|
+
const [StateProvider$1, useComposerState] = createRequiredContext("Composer");
|
|
197
|
+
//#endregion
|
|
198
|
+
//#region src/composer/attach-trigger.tsx
|
|
199
|
+
/**
|
|
200
|
+
* Renders the control that opens the file picker, and the hidden file input behind it.
|
|
201
|
+
*
|
|
202
|
+
* @remarks
|
|
203
|
+
* The control is the actions `Button`, square, small, in the ghost look, named by `label`,
|
|
204
|
+
* "Attach files" unless stated, with the caller's glyph. A press opens the browser's file picker,
|
|
205
|
+
* and the files picked reach the root's `onAttach`. The file input is `hidden`, so neither a
|
|
206
|
+
* screen reader nor the Tab key finds a second, unnamed control. It is emptied after each pick,
|
|
207
|
+
* so picking the same file twice attaches it twice. `accept` limits the files the picker offers.
|
|
208
|
+
*/
|
|
209
|
+
/**
|
|
210
|
+
* Renders the control and the hidden file input.
|
|
211
|
+
*
|
|
212
|
+
* @param props - The name, the accepted types and the props of the actions `Button`, the glyph
|
|
213
|
+
* among its children.
|
|
214
|
+
* @returns The `button` element and the `input` element.
|
|
215
|
+
*/
|
|
216
|
+
function AttachTrigger(t0) {
|
|
217
|
+
const $ = c(20);
|
|
218
|
+
let accept;
|
|
219
|
+
let onClick;
|
|
220
|
+
let props;
|
|
221
|
+
let t1;
|
|
222
|
+
if ($[0] !== t0) {
|
|
223
|
+
({accept, label: t1, onClick, ...props} = t0);
|
|
224
|
+
$[0] = t0;
|
|
225
|
+
$[1] = accept;
|
|
226
|
+
$[2] = onClick;
|
|
227
|
+
$[3] = props;
|
|
228
|
+
$[4] = t1;
|
|
229
|
+
} else {
|
|
230
|
+
accept = $[1];
|
|
231
|
+
onClick = $[2];
|
|
232
|
+
props = $[3];
|
|
233
|
+
t1 = $[4];
|
|
234
|
+
}
|
|
235
|
+
const label = t1 === void 0 ? "Attach files" : t1;
|
|
236
|
+
const { disabled, onAttach } = useComposerState();
|
|
237
|
+
const picker = useRef(null);
|
|
238
|
+
let t2;
|
|
239
|
+
if ($[5] !== onClick) {
|
|
240
|
+
t2 = (event) => {
|
|
241
|
+
onClick?.(event);
|
|
242
|
+
picker.current?.click();
|
|
243
|
+
};
|
|
244
|
+
$[5] = onClick;
|
|
245
|
+
$[6] = t2;
|
|
246
|
+
} else t2 = $[6];
|
|
247
|
+
let t3;
|
|
248
|
+
if ($[7] !== disabled || $[8] !== label || $[9] !== props || $[10] !== t2) {
|
|
249
|
+
t3 = /* @__PURE__ */ jsx(Button, {
|
|
250
|
+
"aria-label": label,
|
|
251
|
+
disabled,
|
|
252
|
+
shape: "square",
|
|
253
|
+
size: "sm",
|
|
254
|
+
variant: "ghost",
|
|
255
|
+
...props,
|
|
256
|
+
onClick: t2
|
|
257
|
+
});
|
|
258
|
+
$[7] = disabled;
|
|
259
|
+
$[8] = label;
|
|
260
|
+
$[9] = props;
|
|
261
|
+
$[10] = t2;
|
|
262
|
+
$[11] = t3;
|
|
263
|
+
} else t3 = $[11];
|
|
264
|
+
let t4;
|
|
265
|
+
if ($[12] !== onAttach) {
|
|
266
|
+
t4 = (event_0) => {
|
|
267
|
+
const files = [...event_0.currentTarget.files];
|
|
268
|
+
event_0.currentTarget.value = "";
|
|
269
|
+
if (files.length > 0) onAttach?.(files);
|
|
270
|
+
};
|
|
271
|
+
$[12] = onAttach;
|
|
272
|
+
$[13] = t4;
|
|
273
|
+
} else t4 = $[13];
|
|
274
|
+
let t5;
|
|
275
|
+
if ($[14] !== accept || $[15] !== t4) {
|
|
276
|
+
t5 = /* @__PURE__ */ jsx("input", {
|
|
277
|
+
accept,
|
|
278
|
+
hidden: true,
|
|
279
|
+
multiple: true,
|
|
280
|
+
onChange: t4,
|
|
281
|
+
ref: picker,
|
|
282
|
+
type: "file"
|
|
283
|
+
});
|
|
284
|
+
$[14] = accept;
|
|
285
|
+
$[15] = t4;
|
|
286
|
+
$[16] = t5;
|
|
287
|
+
} else t5 = $[16];
|
|
288
|
+
let t6;
|
|
289
|
+
if ($[17] !== t3 || $[18] !== t5) {
|
|
290
|
+
t6 = /* @__PURE__ */ jsxs(Fragment, { children: [t3, t5] });
|
|
291
|
+
$[17] = t3;
|
|
292
|
+
$[18] = t5;
|
|
293
|
+
$[19] = t6;
|
|
294
|
+
} else t6 = $[19];
|
|
295
|
+
return t6;
|
|
296
|
+
}
|
|
297
|
+
//#endregion
|
|
298
|
+
//#region src/composer/context.ts
|
|
299
|
+
/**
|
|
300
|
+
* Binds the composer recipe to the elements that render it.
|
|
301
|
+
*
|
|
302
|
+
* @remarks
|
|
303
|
+
* The binding is kept out of `recipe.ts` because an application's style compiler imports the
|
|
304
|
+
* recipe at build time, and a binding there would pull the React runtime into that import.
|
|
305
|
+
*/
|
|
306
|
+
/**
|
|
307
|
+
* Supplies the element factories the composer parts are built from.
|
|
308
|
+
*/
|
|
309
|
+
const { withContext: withContext$3, withProvider: withProvider$3 } = createSlotRecipeContext(recipe$1);
|
|
310
|
+
//#endregion
|
|
311
|
+
//#region src/composer/attachments.ts
|
|
312
|
+
/**
|
|
313
|
+
* Renders the row's `div`.
|
|
314
|
+
*/
|
|
315
|
+
const Attachments = withContext$3("div", "attachments");
|
|
316
|
+
//#endregion
|
|
317
|
+
//#region src/composer/context-strip.ts
|
|
318
|
+
/**
|
|
319
|
+
* Renders the strip's `div`.
|
|
320
|
+
*/
|
|
321
|
+
const Context = withContext$3("div", "context");
|
|
322
|
+
//#endregion
|
|
323
|
+
//#region src/composer/keys.ts
|
|
324
|
+
/**
|
|
325
|
+
* Returns whether a press holds any modifier.
|
|
326
|
+
*/
|
|
327
|
+
function modified(pressed) {
|
|
328
|
+
return pressed.altKey || pressed.ctrlKey || pressed.metaKey || pressed.shiftKey;
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Returns whether a press of Enter sends under the keys `submitOn` names.
|
|
332
|
+
*/
|
|
333
|
+
function sends(pressed, submitOn) {
|
|
334
|
+
return submitOn === "enter" ? !pressed.shiftKey : pressed.ctrlKey || pressed.metaKey;
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Returns what a key pressed in the textarea does.
|
|
338
|
+
*
|
|
339
|
+
* @param pressed - The key and its modifiers.
|
|
340
|
+
* @param offered - The reply, the edit and the send keys the composer offers.
|
|
341
|
+
* @returns The action, or `none` for a key the composer leaves to the textarea.
|
|
342
|
+
*/
|
|
343
|
+
function actionOf(pressed, offered) {
|
|
344
|
+
if (pressed.isComposing) return "none";
|
|
345
|
+
if (pressed.key === "Escape" && offered.cancels) return "cancel";
|
|
346
|
+
if (pressed.key === "ArrowUp" && offered.edits && offered.empty && !modified(pressed)) return "edit";
|
|
347
|
+
if (pressed.key === "Enter" && sends(pressed, offered.submitOn)) return "submit";
|
|
348
|
+
return "none";
|
|
349
|
+
}
|
|
350
|
+
//#endregion
|
|
351
|
+
//#region src/composer/suggestions.tsx
|
|
352
|
+
/**
|
|
353
|
+
* Renders the list's `div`.
|
|
354
|
+
*/
|
|
355
|
+
const Listed = withContext$3("div", "suggestions");
|
|
356
|
+
/**
|
|
357
|
+
* Renders one suggestion's `div`.
|
|
358
|
+
*/
|
|
359
|
+
const Option = withContext$3("div", "suggestion");
|
|
360
|
+
/**
|
|
361
|
+
* Renders the line under a suggestion's name.
|
|
362
|
+
*/
|
|
363
|
+
const Detail = withContext$3("span", "detail");
|
|
364
|
+
/**
|
|
365
|
+
* Renders the open list, or nothing.
|
|
366
|
+
*
|
|
367
|
+
* @param props - The list's name and the mention state.
|
|
368
|
+
* @returns The `listbox`, or nothing while the list is closed.
|
|
369
|
+
*/
|
|
370
|
+
function Suggestions(t0) {
|
|
371
|
+
const $ = c(6);
|
|
372
|
+
const { label, mentions } = t0;
|
|
373
|
+
if (!mentions.open) return null;
|
|
374
|
+
const t1 = mentions.listId;
|
|
375
|
+
let t2;
|
|
376
|
+
if ($[0] !== mentions) {
|
|
377
|
+
t2 = mentions.suggestions.map((suggestion, index) => /* @__PURE__ */ jsxs(Option, {
|
|
378
|
+
"aria-selected": index === mentions.highlight,
|
|
379
|
+
id: mentions.optionId(index),
|
|
380
|
+
onClick: () => {
|
|
381
|
+
mentions.choose(index);
|
|
382
|
+
},
|
|
383
|
+
onMouseDown: _temp,
|
|
384
|
+
onPointerMove: () => {
|
|
385
|
+
mentions.setHighlight(index);
|
|
386
|
+
},
|
|
387
|
+
role: "option",
|
|
388
|
+
children: [suggestion.label, suggestion.description === void 0 ? null : /* @__PURE__ */ jsx(Detail, { children: suggestion.description })]
|
|
389
|
+
}, suggestion.id));
|
|
390
|
+
$[0] = mentions;
|
|
391
|
+
$[1] = t2;
|
|
392
|
+
} else t2 = $[1];
|
|
393
|
+
let t3;
|
|
394
|
+
if ($[2] !== label || $[3] !== mentions.listId || $[4] !== t2) {
|
|
395
|
+
t3 = /* @__PURE__ */ jsx(Listed, {
|
|
396
|
+
"aria-label": label,
|
|
397
|
+
id: t1,
|
|
398
|
+
role: "listbox",
|
|
399
|
+
children: t2
|
|
400
|
+
});
|
|
401
|
+
$[2] = label;
|
|
402
|
+
$[3] = mentions.listId;
|
|
403
|
+
$[4] = t2;
|
|
404
|
+
$[5] = t3;
|
|
405
|
+
} else t3 = $[5];
|
|
406
|
+
return t3;
|
|
407
|
+
}
|
|
408
|
+
function _temp(event) {
|
|
409
|
+
event.preventDefault();
|
|
410
|
+
}
|
|
411
|
+
//#endregion
|
|
412
|
+
//#region src/composer/mentions.ts
|
|
413
|
+
/**
|
|
414
|
+
* Returns the query of one trigger at the caret, or nothing where the trigger opens none there.
|
|
415
|
+
*/
|
|
416
|
+
function queryOf(text, caret, trigger) {
|
|
417
|
+
const from = text.lastIndexOf(trigger, caret - 1);
|
|
418
|
+
if (from === -1 || /\S/u.test(text.charAt(from - 1))) return void 0;
|
|
419
|
+
const term = text.slice(from + trigger.length, caret);
|
|
420
|
+
return /\s/u.test(term) ? void 0 : {
|
|
421
|
+
from,
|
|
422
|
+
term,
|
|
423
|
+
trigger
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Returns the mention the caret is in.
|
|
428
|
+
*
|
|
429
|
+
* @param text - The text so far.
|
|
430
|
+
* @param caret - The caret's index in the text.
|
|
431
|
+
* @param triggers - The characters that open a mention, such as `@` and `#`.
|
|
432
|
+
* @returns The query at the caret, or undefined outside every mention.
|
|
433
|
+
*/
|
|
434
|
+
function queryAt(text, caret, triggers) {
|
|
435
|
+
return triggers.map((trigger) => queryOf(text, caret, trigger)).find((query) => query !== void 0);
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Returns the text with the query replaced by the trigger, the chosen name and a space, and the
|
|
439
|
+
* caret after them.
|
|
440
|
+
*
|
|
441
|
+
* @param text - The text so far.
|
|
442
|
+
* @param query - The mention being typed.
|
|
443
|
+
* @param caret - The caret's index in the text.
|
|
444
|
+
* @param name - The chosen name.
|
|
445
|
+
* @returns The new text and the caret's index in it.
|
|
446
|
+
*/
|
|
447
|
+
function mentioned(text, query, caret, name) {
|
|
448
|
+
const written = `${query.trigger}${name} `;
|
|
449
|
+
return {
|
|
450
|
+
caret: query.from + written.length,
|
|
451
|
+
text: text.slice(0, query.from) + written + text.slice(caret)
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
//#endregion
|
|
455
|
+
//#region src/composer/use-mentions.ts
|
|
456
|
+
/**
|
|
457
|
+
* Keeps the mention a reader types in the composer's textarea: the query at the caret, the
|
|
458
|
+
* suggestion the keys highlight, and the insertion of the one chosen.
|
|
459
|
+
*
|
|
460
|
+
* @remarks
|
|
461
|
+
* Focus stays in the textarea. The arrows move the highlight, which the textarea reports through
|
|
462
|
+
* `aria-activedescendant`, Enter and Tab insert the highlighted suggestion, and Escape closes the
|
|
463
|
+
* list without changing the text and without reaching a dialog around the composer. The list is
|
|
464
|
+
* open while the caret is in a query and there are suggestions. A new query moves the highlight
|
|
465
|
+
* back to the first suggestion, and a list that shrinks keeps it on its last suggestion. A key
|
|
466
|
+
* pressed while an input method composes text is left to the input method.
|
|
467
|
+
*/
|
|
468
|
+
/**
|
|
469
|
+
* Lists no suggestions.
|
|
470
|
+
*/
|
|
471
|
+
const NONE = [];
|
|
472
|
+
/**
|
|
473
|
+
* Lists no triggers.
|
|
474
|
+
*/
|
|
475
|
+
const UNTRIGGERED = [];
|
|
476
|
+
/**
|
|
477
|
+
* Maps each key the open list uses to what it does.
|
|
478
|
+
*/
|
|
479
|
+
const KEYS = {
|
|
480
|
+
ArrowDown: 1,
|
|
481
|
+
ArrowUp: -1,
|
|
482
|
+
Enter: "choose",
|
|
483
|
+
Escape: "close",
|
|
484
|
+
Tab: "choose"
|
|
485
|
+
};
|
|
486
|
+
/**
|
|
487
|
+
* Returns whether two queries name the same mention.
|
|
488
|
+
*/
|
|
489
|
+
function same(one, other) {
|
|
490
|
+
return one?.from === other?.from && one?.term === other?.term && one?.trigger === other?.trigger;
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* Returns the mention state of the composer's textarea.
|
|
494
|
+
*
|
|
495
|
+
* @param options - The triggers, the suggestions and the handlers.
|
|
496
|
+
* @param input - The textarea, once it renders.
|
|
497
|
+
* @param setText - Replaces the composer's text.
|
|
498
|
+
* @returns The list's state and the handlers the textarea and the list call.
|
|
499
|
+
*/
|
|
500
|
+
function useMentions(options, input, setText) {
|
|
501
|
+
const $ = c(33);
|
|
502
|
+
const { onMention, onQueryChange, suggestions: t0, triggers: t1 } = options;
|
|
503
|
+
const suggestions = t0 === void 0 ? NONE : t0;
|
|
504
|
+
const triggers = t1 === void 0 ? UNTRIGGERED : t1;
|
|
505
|
+
const listId = useId();
|
|
506
|
+
const [query, setQuery] = useState();
|
|
507
|
+
const [stored, setHighlight] = useState(0);
|
|
508
|
+
const open = query !== void 0 && suggestions.length > 0;
|
|
509
|
+
const highlight = Math.min(stored, suggestions.length - 1);
|
|
510
|
+
let t2;
|
|
511
|
+
if ($[0] !== onQueryChange || $[1] !== query) {
|
|
512
|
+
t2 = function queried(next) {
|
|
513
|
+
if (same(next, query)) return;
|
|
514
|
+
setHighlight(0);
|
|
515
|
+
setQuery(next);
|
|
516
|
+
onQueryChange?.(next);
|
|
517
|
+
};
|
|
518
|
+
$[0] = onQueryChange;
|
|
519
|
+
$[1] = query;
|
|
520
|
+
$[2] = t2;
|
|
521
|
+
} else t2 = $[2];
|
|
522
|
+
const queried = t2;
|
|
523
|
+
let t3;
|
|
524
|
+
if ($[3] !== input || $[4] !== onMention || $[5] !== queried || $[6] !== query || $[7] !== setText || $[8] !== suggestions) {
|
|
525
|
+
t3 = function choose(index) {
|
|
526
|
+
const [at, element, suggestion] = [
|
|
527
|
+
query,
|
|
528
|
+
input,
|
|
529
|
+
suggestions[index]
|
|
530
|
+
];
|
|
531
|
+
const next_0 = mentioned(element.value, at, element.selectionStart, suggestion.label);
|
|
532
|
+
flushSync(() => {
|
|
533
|
+
setText(next_0.text);
|
|
534
|
+
});
|
|
535
|
+
element.setSelectionRange(next_0.caret, next_0.caret);
|
|
536
|
+
queried();
|
|
537
|
+
onMention?.(suggestion, at);
|
|
538
|
+
};
|
|
539
|
+
$[3] = input;
|
|
540
|
+
$[4] = onMention;
|
|
541
|
+
$[5] = queried;
|
|
542
|
+
$[6] = query;
|
|
543
|
+
$[7] = setText;
|
|
544
|
+
$[8] = suggestions;
|
|
545
|
+
$[9] = t3;
|
|
546
|
+
} else t3 = $[9];
|
|
547
|
+
const choose = t3;
|
|
548
|
+
let t4;
|
|
549
|
+
if ($[10] !== choose || $[11] !== highlight || $[12] !== open || $[13] !== queried || $[14] !== suggestions.length) {
|
|
550
|
+
t4 = function keyed(event) {
|
|
551
|
+
const action = open && !event.nativeEvent.isComposing ? KEYS[event.key] : void 0;
|
|
552
|
+
if (action === void 0) return false;
|
|
553
|
+
event.preventDefault();
|
|
554
|
+
if (typeof action === "number") setHighlight((highlight + action + suggestions.length) % suggestions.length);
|
|
555
|
+
else if (action === "choose") choose(highlight);
|
|
556
|
+
else {
|
|
557
|
+
event.stopPropagation();
|
|
558
|
+
queried();
|
|
559
|
+
}
|
|
560
|
+
return true;
|
|
561
|
+
};
|
|
562
|
+
$[10] = choose;
|
|
563
|
+
$[11] = highlight;
|
|
564
|
+
$[12] = open;
|
|
565
|
+
$[13] = queried;
|
|
566
|
+
$[14] = suggestions.length;
|
|
567
|
+
$[15] = t4;
|
|
568
|
+
} else t4 = $[15];
|
|
569
|
+
const keyed = t4;
|
|
570
|
+
let t5;
|
|
571
|
+
if ($[16] !== queried) {
|
|
572
|
+
t5 = () => {
|
|
573
|
+
queried();
|
|
574
|
+
};
|
|
575
|
+
$[16] = queried;
|
|
576
|
+
$[17] = t5;
|
|
577
|
+
} else t5 = $[17];
|
|
578
|
+
let t6;
|
|
579
|
+
if ($[18] !== queried || $[19] !== triggers) {
|
|
580
|
+
t6 = (element_0) => {
|
|
581
|
+
queried(queryAt(element_0.value, element_0.selectionStart, triggers));
|
|
582
|
+
};
|
|
583
|
+
$[18] = queried;
|
|
584
|
+
$[19] = triggers;
|
|
585
|
+
$[20] = t6;
|
|
586
|
+
} else t6 = $[20];
|
|
587
|
+
let t7;
|
|
588
|
+
if ($[21] !== listId) {
|
|
589
|
+
t7 = (index_0) => `${listId}-${String(index_0)}`;
|
|
590
|
+
$[21] = listId;
|
|
591
|
+
$[22] = t7;
|
|
592
|
+
} else t7 = $[22];
|
|
593
|
+
let t8;
|
|
594
|
+
if ($[23] !== choose || $[24] !== highlight || $[25] !== keyed || $[26] !== listId || $[27] !== open || $[28] !== suggestions || $[29] !== t5 || $[30] !== t6 || $[31] !== t7) {
|
|
595
|
+
t8 = {
|
|
596
|
+
choose,
|
|
597
|
+
dismiss: t5,
|
|
598
|
+
highlight,
|
|
599
|
+
keyed,
|
|
600
|
+
listId,
|
|
601
|
+
moved: t6,
|
|
602
|
+
open,
|
|
603
|
+
optionId: t7,
|
|
604
|
+
setHighlight,
|
|
605
|
+
suggestions
|
|
606
|
+
};
|
|
607
|
+
$[23] = choose;
|
|
608
|
+
$[24] = highlight;
|
|
609
|
+
$[25] = keyed;
|
|
610
|
+
$[26] = listId;
|
|
611
|
+
$[27] = open;
|
|
612
|
+
$[28] = suggestions;
|
|
613
|
+
$[29] = t5;
|
|
614
|
+
$[30] = t6;
|
|
615
|
+
$[31] = t7;
|
|
616
|
+
$[32] = t8;
|
|
617
|
+
} else t8 = $[32];
|
|
618
|
+
return t8;
|
|
619
|
+
}
|
|
620
|
+
//#endregion
|
|
621
|
+
//#region src/composer/input.tsx
|
|
622
|
+
/**
|
|
623
|
+
* Renders the `textarea` with the composer's input class.
|
|
624
|
+
*/
|
|
625
|
+
const Typed = withContext$3("textarea", "input");
|
|
626
|
+
/**
|
|
627
|
+
* Runs what a key does: dismisses the reply, edits the last message, or sends.
|
|
628
|
+
*
|
|
629
|
+
* @param event - The key press, which the composer cancels when it acts on it.
|
|
630
|
+
* @param offered - The reply, the edit and the send keys the composer offers.
|
|
631
|
+
* @param handlers - The handlers the root takes.
|
|
632
|
+
*/
|
|
633
|
+
function keyed(event, offered, handlers) {
|
|
634
|
+
const action = actionOf({
|
|
635
|
+
altKey: event.altKey,
|
|
636
|
+
ctrlKey: event.ctrlKey,
|
|
637
|
+
isComposing: event.nativeEvent.isComposing,
|
|
638
|
+
key: event.key,
|
|
639
|
+
metaKey: event.metaKey,
|
|
640
|
+
shiftKey: event.shiftKey
|
|
641
|
+
}, offered);
|
|
642
|
+
if (action === "none") return;
|
|
643
|
+
event.preventDefault();
|
|
644
|
+
if (action === "cancel") handlers.onCancelContext?.();
|
|
645
|
+
else if (action === "edit") handlers.onEditLast?.();
|
|
646
|
+
else event.currentTarget.form?.requestSubmit();
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* Attaches the files a paste carries, in place of pasting text.
|
|
650
|
+
*
|
|
651
|
+
* @param event - The paste event, which the composer cancels when it attaches.
|
|
652
|
+
* @param onAttach - The root's attach handler.
|
|
653
|
+
*/
|
|
654
|
+
function pasted(event, onAttach) {
|
|
655
|
+
const files = [...event.clipboardData.files];
|
|
656
|
+
if (onAttach === void 0 || files.length === 0) return;
|
|
657
|
+
event.preventDefault();
|
|
658
|
+
onAttach(files);
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* Returns what the composer offers a key press, read from the root's state.
|
|
662
|
+
*/
|
|
663
|
+
function offeredBy(state) {
|
|
664
|
+
return {
|
|
665
|
+
cancels: state.onCancelContext !== void 0,
|
|
666
|
+
edits: state.onEditLast !== void 0,
|
|
667
|
+
empty: state.text === "",
|
|
668
|
+
submitOn: state.submitOn
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Passes the textarea to the caller's ref, a callback or an object.
|
|
673
|
+
*
|
|
674
|
+
* @param handed - The caller's ref, if any.
|
|
675
|
+
* @param element - The textarea, or `null` once it unmounts.
|
|
676
|
+
*/
|
|
677
|
+
function forwarded(handed, element) {
|
|
678
|
+
if (typeof handed === "function") handed(element);
|
|
679
|
+
else if (handed !== null && handed !== void 0) handed.current = element;
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* Returns the textarea's handlers, each running the caller's first.
|
|
683
|
+
*
|
|
684
|
+
* @param wiring - The state and the handlers the textarea's handlers read.
|
|
685
|
+
* @returns The five handlers.
|
|
686
|
+
*/
|
|
687
|
+
function wired(wiring) {
|
|
688
|
+
const { handlers, mentions, offered, onAttach, own, setText } = wiring;
|
|
689
|
+
return {
|
|
690
|
+
onBlur: (event) => {
|
|
691
|
+
own.onBlur?.(event);
|
|
692
|
+
mentions.dismiss();
|
|
693
|
+
},
|
|
694
|
+
onChange: (event) => {
|
|
695
|
+
own.onChange?.(event);
|
|
696
|
+
setText(event.target.value);
|
|
697
|
+
mentions.moved(event.currentTarget);
|
|
698
|
+
},
|
|
699
|
+
onKeyDown: (event) => {
|
|
700
|
+
own.onKeyDown?.(event);
|
|
701
|
+
if (event.defaultPrevented || mentions.keyed(event)) return;
|
|
702
|
+
keyed(event, offered, handlers);
|
|
703
|
+
},
|
|
704
|
+
onPaste: (event) => {
|
|
705
|
+
own.onPaste?.(event);
|
|
706
|
+
if (!event.defaultPrevented) pasted(event, onAttach);
|
|
707
|
+
},
|
|
708
|
+
onSelect: (event) => {
|
|
709
|
+
own.onSelect?.(event);
|
|
710
|
+
mentions.moved(event.currentTarget);
|
|
711
|
+
}
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
/**
|
|
715
|
+
* Renders the textarea with the composer's text and keys, and the list of suggestions.
|
|
716
|
+
*
|
|
717
|
+
* @param props - The name, the row limit, the mention options and the props of a `textarea`.
|
|
718
|
+
* @returns The `textarea` element and the list.
|
|
719
|
+
*/
|
|
720
|
+
function Input(t0) {
|
|
721
|
+
const $ = c(56);
|
|
722
|
+
let handed;
|
|
723
|
+
let onBlur;
|
|
724
|
+
let onChange;
|
|
725
|
+
let onKeyDown;
|
|
726
|
+
let onMention;
|
|
727
|
+
let onPaste;
|
|
728
|
+
let onQueryChange;
|
|
729
|
+
let onSelect;
|
|
730
|
+
let props;
|
|
731
|
+
let suggestions;
|
|
732
|
+
let t1;
|
|
733
|
+
let t2;
|
|
734
|
+
let t3;
|
|
735
|
+
let triggers;
|
|
736
|
+
if ($[0] !== t0) {
|
|
737
|
+
({label: t1, maxRows: t2, onBlur, onChange, onKeyDown, onMention, onPaste, onQueryChange, onSelect, ref: handed, suggestions, suggestionsLabel: t3, triggers, ...props} = t0);
|
|
738
|
+
$[0] = t0;
|
|
739
|
+
$[1] = handed;
|
|
740
|
+
$[2] = onBlur;
|
|
741
|
+
$[3] = onChange;
|
|
742
|
+
$[4] = onKeyDown;
|
|
743
|
+
$[5] = onMention;
|
|
744
|
+
$[6] = onPaste;
|
|
745
|
+
$[7] = onQueryChange;
|
|
746
|
+
$[8] = onSelect;
|
|
747
|
+
$[9] = props;
|
|
748
|
+
$[10] = suggestions;
|
|
749
|
+
$[11] = t1;
|
|
750
|
+
$[12] = t2;
|
|
751
|
+
$[13] = t3;
|
|
752
|
+
$[14] = triggers;
|
|
753
|
+
} else {
|
|
754
|
+
handed = $[1];
|
|
755
|
+
onBlur = $[2];
|
|
756
|
+
onChange = $[3];
|
|
757
|
+
onKeyDown = $[4];
|
|
758
|
+
onMention = $[5];
|
|
759
|
+
onPaste = $[6];
|
|
760
|
+
onQueryChange = $[7];
|
|
761
|
+
onSelect = $[8];
|
|
762
|
+
props = $[9];
|
|
763
|
+
suggestions = $[10];
|
|
764
|
+
t1 = $[11];
|
|
765
|
+
t2 = $[12];
|
|
766
|
+
t3 = $[13];
|
|
767
|
+
triggers = $[14];
|
|
768
|
+
}
|
|
769
|
+
const label = t1 === void 0 ? "Message" : t1;
|
|
770
|
+
const maxRows = t2 === void 0 ? 8 : t2;
|
|
771
|
+
const suggestionsLabel = t3 === void 0 ? "Suggestions" : t3;
|
|
772
|
+
const state = useComposerState();
|
|
773
|
+
const { disabled, input, onAttach, onCancelContext, onEditLast, setInput, setText, text } = state;
|
|
774
|
+
let t4;
|
|
775
|
+
if ($[15] !== onMention || $[16] !== onQueryChange || $[17] !== suggestions || $[18] !== triggers) {
|
|
776
|
+
t4 = {
|
|
777
|
+
onMention,
|
|
778
|
+
onQueryChange,
|
|
779
|
+
suggestions,
|
|
780
|
+
triggers
|
|
781
|
+
};
|
|
782
|
+
$[15] = onMention;
|
|
783
|
+
$[16] = onQueryChange;
|
|
784
|
+
$[17] = suggestions;
|
|
785
|
+
$[18] = triggers;
|
|
786
|
+
$[19] = t4;
|
|
787
|
+
} else t4 = $[19];
|
|
788
|
+
const mentions = useMentions(t4, input, setText);
|
|
789
|
+
const t5 = String(maxRows);
|
|
790
|
+
let t6;
|
|
791
|
+
if ($[20] !== t5) {
|
|
792
|
+
t6 = { [ROWS]: t5 };
|
|
793
|
+
$[20] = t5;
|
|
794
|
+
$[21] = t6;
|
|
795
|
+
} else t6 = $[21];
|
|
796
|
+
const sized = t6;
|
|
797
|
+
let t7;
|
|
798
|
+
if ($[22] !== handed || $[23] !== setInput) {
|
|
799
|
+
t7 = (element) => {
|
|
800
|
+
setInput(element);
|
|
801
|
+
forwarded(handed, element);
|
|
802
|
+
};
|
|
803
|
+
$[22] = handed;
|
|
804
|
+
$[23] = setInput;
|
|
805
|
+
$[24] = t7;
|
|
806
|
+
} else t7 = $[24];
|
|
807
|
+
const attach = useCallbackRef(t7);
|
|
808
|
+
let t8;
|
|
809
|
+
if ($[25] !== mentions || $[26] !== onAttach || $[27] !== onBlur || $[28] !== onCancelContext || $[29] !== onChange || $[30] !== onEditLast || $[31] !== onKeyDown || $[32] !== onPaste || $[33] !== onSelect || $[34] !== setText || $[35] !== state) {
|
|
810
|
+
t8 = wired({
|
|
811
|
+
handlers: {
|
|
812
|
+
onCancelContext,
|
|
813
|
+
onEditLast
|
|
814
|
+
},
|
|
815
|
+
mentions,
|
|
816
|
+
offered: offeredBy(state),
|
|
817
|
+
onAttach,
|
|
818
|
+
own: {
|
|
819
|
+
onBlur,
|
|
820
|
+
onChange,
|
|
821
|
+
onKeyDown,
|
|
822
|
+
onPaste,
|
|
823
|
+
onSelect
|
|
824
|
+
},
|
|
825
|
+
setText
|
|
826
|
+
});
|
|
827
|
+
$[25] = mentions;
|
|
828
|
+
$[26] = onAttach;
|
|
829
|
+
$[27] = onBlur;
|
|
830
|
+
$[28] = onCancelContext;
|
|
831
|
+
$[29] = onChange;
|
|
832
|
+
$[30] = onEditLast;
|
|
833
|
+
$[31] = onKeyDown;
|
|
834
|
+
$[32] = onPaste;
|
|
835
|
+
$[33] = onSelect;
|
|
836
|
+
$[34] = setText;
|
|
837
|
+
$[35] = state;
|
|
838
|
+
$[36] = t8;
|
|
839
|
+
} else t8 = $[36];
|
|
840
|
+
const events = t8;
|
|
841
|
+
let t9;
|
|
842
|
+
if ($[37] !== mentions) {
|
|
843
|
+
t9 = mentions.open ? mentions.optionId(mentions.highlight) : void 0;
|
|
844
|
+
$[37] = mentions;
|
|
845
|
+
$[38] = t9;
|
|
846
|
+
} else t9 = $[38];
|
|
847
|
+
const t10 = triggers === void 0 ? void 0 : "list";
|
|
848
|
+
const t11 = mentions.open ? mentions.listId : void 0;
|
|
849
|
+
let t12;
|
|
850
|
+
if ($[39] !== attach || $[40] !== disabled || $[41] !== events || $[42] !== label || $[43] !== props || $[44] !== sized || $[45] !== t10 || $[46] !== t11 || $[47] !== t9 || $[48] !== text) {
|
|
851
|
+
t12 = /* @__PURE__ */ jsx(Typed, {
|
|
852
|
+
"aria-activedescendant": t9,
|
|
853
|
+
"aria-autocomplete": t10,
|
|
854
|
+
"aria-controls": t11,
|
|
855
|
+
"aria-label": label,
|
|
856
|
+
disabled,
|
|
857
|
+
ref: attach,
|
|
858
|
+
rows: 1,
|
|
859
|
+
...props,
|
|
860
|
+
...events,
|
|
861
|
+
style: sized,
|
|
862
|
+
value: text
|
|
863
|
+
});
|
|
864
|
+
$[39] = attach;
|
|
865
|
+
$[40] = disabled;
|
|
866
|
+
$[41] = events;
|
|
867
|
+
$[42] = label;
|
|
868
|
+
$[43] = props;
|
|
869
|
+
$[44] = sized;
|
|
870
|
+
$[45] = t10;
|
|
871
|
+
$[46] = t11;
|
|
872
|
+
$[47] = t9;
|
|
873
|
+
$[48] = text;
|
|
874
|
+
$[49] = t12;
|
|
875
|
+
} else t12 = $[49];
|
|
876
|
+
let t13;
|
|
877
|
+
if ($[50] !== mentions || $[51] !== suggestionsLabel) {
|
|
878
|
+
t13 = /* @__PURE__ */ jsx(Suggestions, {
|
|
879
|
+
label: suggestionsLabel,
|
|
880
|
+
mentions
|
|
881
|
+
});
|
|
882
|
+
$[50] = mentions;
|
|
883
|
+
$[51] = suggestionsLabel;
|
|
884
|
+
$[52] = t13;
|
|
885
|
+
} else t13 = $[52];
|
|
886
|
+
let t14;
|
|
887
|
+
if ($[53] !== t12 || $[54] !== t13) {
|
|
888
|
+
t14 = /* @__PURE__ */ jsxs(Fragment, { children: [t12, t13] });
|
|
889
|
+
$[53] = t12;
|
|
890
|
+
$[54] = t13;
|
|
891
|
+
$[55] = t14;
|
|
892
|
+
} else t14 = $[55];
|
|
893
|
+
return t14;
|
|
894
|
+
}
|
|
895
|
+
//#endregion
|
|
896
|
+
//#region src/composer/drop.ts
|
|
897
|
+
/**
|
|
898
|
+
* Tracks files dragged over the composer's box, and attaches the files dropped on it.
|
|
899
|
+
*
|
|
900
|
+
* @remarks
|
|
901
|
+
* The box accepts a drag only while `onAttach` is set and the composer is enabled, so a browser
|
|
902
|
+
* shows no drop cursor over a composer that attaches nothing. A drag that moves between the box's
|
|
903
|
+
* own children keeps the mark, and one that leaves the box drops it.
|
|
904
|
+
*/
|
|
905
|
+
/**
|
|
906
|
+
* Returns whether files are over the box, and the handlers that attach the files dropped on it.
|
|
907
|
+
*
|
|
908
|
+
* @param onAttach - Called with the files dropped, the composer's attach handler.
|
|
909
|
+
* @param disabled - Whether the composer takes no input.
|
|
910
|
+
* @returns The mark and the three handlers.
|
|
911
|
+
*/
|
|
912
|
+
function useDrop(onAttach, disabled) {
|
|
913
|
+
const $ = c(10);
|
|
914
|
+
const [dragging, setDragging] = useState(false);
|
|
915
|
+
const drops = onAttach !== void 0 && !disabled;
|
|
916
|
+
let t0;
|
|
917
|
+
if ($[0] !== drops) {
|
|
918
|
+
t0 = function onDragOver(event) {
|
|
919
|
+
if (!drops) return;
|
|
920
|
+
event.preventDefault();
|
|
921
|
+
setDragging(true);
|
|
922
|
+
};
|
|
923
|
+
$[0] = drops;
|
|
924
|
+
$[1] = t0;
|
|
925
|
+
} else t0 = $[1];
|
|
926
|
+
const onDragOver = t0;
|
|
927
|
+
let t1;
|
|
928
|
+
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
|
|
929
|
+
t1 = function onDragLeave(event_0) {
|
|
930
|
+
const next = event_0.relatedTarget;
|
|
931
|
+
if (!(next instanceof Node) || !event_0.currentTarget.contains(next)) setDragging(false);
|
|
932
|
+
};
|
|
933
|
+
$[2] = t1;
|
|
934
|
+
} else t1 = $[2];
|
|
935
|
+
const onDragLeave = t1;
|
|
936
|
+
let t2;
|
|
937
|
+
if ($[3] !== drops || $[4] !== onAttach) {
|
|
938
|
+
t2 = function onDrop(event_1) {
|
|
939
|
+
const files = [...event_1.dataTransfer.files];
|
|
940
|
+
setDragging(false);
|
|
941
|
+
if (!drops || files.length === 0) return;
|
|
942
|
+
event_1.preventDefault();
|
|
943
|
+
onAttach(files);
|
|
944
|
+
};
|
|
945
|
+
$[3] = drops;
|
|
946
|
+
$[4] = onAttach;
|
|
947
|
+
$[5] = t2;
|
|
948
|
+
} else t2 = $[5];
|
|
949
|
+
const onDrop = t2;
|
|
950
|
+
let t3;
|
|
951
|
+
if ($[6] !== dragging || $[7] !== onDragOver || $[8] !== onDrop) {
|
|
952
|
+
t3 = {
|
|
953
|
+
dragging,
|
|
954
|
+
onDragLeave,
|
|
955
|
+
onDragOver,
|
|
956
|
+
onDrop
|
|
957
|
+
};
|
|
958
|
+
$[6] = dragging;
|
|
959
|
+
$[7] = onDragOver;
|
|
960
|
+
$[8] = onDrop;
|
|
961
|
+
$[9] = t3;
|
|
962
|
+
} else t3 = $[9];
|
|
963
|
+
return t3;
|
|
964
|
+
}
|
|
965
|
+
//#endregion
|
|
966
|
+
//#region src/composer/root.tsx
|
|
967
|
+
/**
|
|
968
|
+
* Renders the composer's root: the `form` a reader writes and sends a message in.
|
|
969
|
+
*
|
|
970
|
+
* @remarks
|
|
971
|
+
* The root keeps the text, controlled through `value` or held from `defaultValue`. A send, from
|
|
972
|
+
* the submit control or from the key `submitOn` names, calls `onSubmit` with the text trimmed,
|
|
973
|
+
* clears the text unless the caller controls it, and moves focus back to the textarea. Text of
|
|
974
|
+
* whitespace alone is not a message, and attached files alone are, so the root sends while the
|
|
975
|
+
* trimmed text is not empty or `attached` is true, and never while `busy` or `disabled`. With
|
|
976
|
+
* `onAttach`, files dropped on the box are attached, and the box takes the focus edge while they
|
|
977
|
+
* are dragged over it.
|
|
978
|
+
*/
|
|
979
|
+
/**
|
|
980
|
+
* Renders the `form` with the composer's variants.
|
|
981
|
+
*/
|
|
982
|
+
const Form = withProvider$3("form", "root");
|
|
983
|
+
/**
|
|
984
|
+
* Renders the form and provides the text and the handlers to its parts.
|
|
985
|
+
*
|
|
986
|
+
* @param props - The text, the states, the handlers and the props of a `form`.
|
|
987
|
+
* @returns The `form` element inside the state's provider.
|
|
988
|
+
*/
|
|
989
|
+
function Root$3(t0) {
|
|
990
|
+
const $ = c(53);
|
|
991
|
+
let onAttach;
|
|
992
|
+
let onCancelContext;
|
|
993
|
+
let onEditLast;
|
|
994
|
+
let onStop;
|
|
995
|
+
let onSubmit;
|
|
996
|
+
let onValueChange;
|
|
997
|
+
let props;
|
|
998
|
+
let t1;
|
|
999
|
+
let t2;
|
|
1000
|
+
let t3;
|
|
1001
|
+
let t4;
|
|
1002
|
+
let t5;
|
|
1003
|
+
let value;
|
|
1004
|
+
if ($[0] !== t0) {
|
|
1005
|
+
({attached: t1, busy: t2, defaultValue: t3, disabled: t4, onAttach, onCancelContext, onEditLast, onStop, onSubmit, onValueChange, submitOn: t5, value, ...props} = t0);
|
|
1006
|
+
$[0] = t0;
|
|
1007
|
+
$[1] = onAttach;
|
|
1008
|
+
$[2] = onCancelContext;
|
|
1009
|
+
$[3] = onEditLast;
|
|
1010
|
+
$[4] = onStop;
|
|
1011
|
+
$[5] = onSubmit;
|
|
1012
|
+
$[6] = onValueChange;
|
|
1013
|
+
$[7] = props;
|
|
1014
|
+
$[8] = t1;
|
|
1015
|
+
$[9] = t2;
|
|
1016
|
+
$[10] = t3;
|
|
1017
|
+
$[11] = t4;
|
|
1018
|
+
$[12] = t5;
|
|
1019
|
+
$[13] = value;
|
|
1020
|
+
} else {
|
|
1021
|
+
onAttach = $[1];
|
|
1022
|
+
onCancelContext = $[2];
|
|
1023
|
+
onEditLast = $[3];
|
|
1024
|
+
onStop = $[4];
|
|
1025
|
+
onSubmit = $[5];
|
|
1026
|
+
onValueChange = $[6];
|
|
1027
|
+
props = $[7];
|
|
1028
|
+
t1 = $[8];
|
|
1029
|
+
t2 = $[9];
|
|
1030
|
+
t3 = $[10];
|
|
1031
|
+
t4 = $[11];
|
|
1032
|
+
t5 = $[12];
|
|
1033
|
+
value = $[13];
|
|
1034
|
+
}
|
|
1035
|
+
const attached = t1 === void 0 ? false : t1;
|
|
1036
|
+
const busy = t2 === void 0 ? false : t2;
|
|
1037
|
+
const defaultValue = t3 === void 0 ? "" : t3;
|
|
1038
|
+
const disabled = t4 === void 0 ? false : t4;
|
|
1039
|
+
const submitOn = t5 === void 0 ? "enter" : t5;
|
|
1040
|
+
let t6;
|
|
1041
|
+
if ($[14] !== defaultValue || $[15] !== onValueChange || $[16] !== value) {
|
|
1042
|
+
t6 = {
|
|
1043
|
+
defaultValue,
|
|
1044
|
+
onChange: onValueChange,
|
|
1045
|
+
value
|
|
1046
|
+
};
|
|
1047
|
+
$[14] = defaultValue;
|
|
1048
|
+
$[15] = onValueChange;
|
|
1049
|
+
$[16] = value;
|
|
1050
|
+
$[17] = t6;
|
|
1051
|
+
} else t6 = $[17];
|
|
1052
|
+
const [text, setText] = useControllableState(t6);
|
|
1053
|
+
const [input, setInput] = useState(null);
|
|
1054
|
+
const t7 = useDrop(onAttach, disabled);
|
|
1055
|
+
let dragging;
|
|
1056
|
+
let drop;
|
|
1057
|
+
if ($[18] !== t7) {
|
|
1058
|
+
({dragging, ...drop} = t7);
|
|
1059
|
+
$[18] = t7;
|
|
1060
|
+
$[19] = dragging;
|
|
1061
|
+
$[20] = drop;
|
|
1062
|
+
} else {
|
|
1063
|
+
dragging = $[19];
|
|
1064
|
+
drop = $[20];
|
|
1065
|
+
}
|
|
1066
|
+
let t8;
|
|
1067
|
+
if ($[21] !== attached || $[22] !== busy || $[23] !== disabled || $[24] !== text) {
|
|
1068
|
+
t8 = !disabled && !busy && (text.trim() !== "" || attached);
|
|
1069
|
+
$[21] = attached;
|
|
1070
|
+
$[22] = busy;
|
|
1071
|
+
$[23] = disabled;
|
|
1072
|
+
$[24] = text;
|
|
1073
|
+
$[25] = t8;
|
|
1074
|
+
} else t8 = $[25];
|
|
1075
|
+
const canSubmit = t8;
|
|
1076
|
+
let t9;
|
|
1077
|
+
if ($[26] !== canSubmit || $[27] !== input || $[28] !== onSubmit || $[29] !== setText || $[30] !== text || $[31] !== value) {
|
|
1078
|
+
t9 = function submitted(event) {
|
|
1079
|
+
event.preventDefault();
|
|
1080
|
+
if (!canSubmit) return;
|
|
1081
|
+
onSubmit?.(text.trim());
|
|
1082
|
+
if (value === void 0) setText("");
|
|
1083
|
+
input?.focus();
|
|
1084
|
+
};
|
|
1085
|
+
$[26] = canSubmit;
|
|
1086
|
+
$[27] = input;
|
|
1087
|
+
$[28] = onSubmit;
|
|
1088
|
+
$[29] = setText;
|
|
1089
|
+
$[30] = text;
|
|
1090
|
+
$[31] = value;
|
|
1091
|
+
$[32] = t9;
|
|
1092
|
+
} else t9 = $[32];
|
|
1093
|
+
const submitted = t9;
|
|
1094
|
+
let t10;
|
|
1095
|
+
if ($[33] !== busy || $[34] !== canSubmit || $[35] !== disabled || $[36] !== input || $[37] !== onAttach || $[38] !== onCancelContext || $[39] !== onEditLast || $[40] !== onStop || $[41] !== setText || $[42] !== submitOn || $[43] !== text) {
|
|
1096
|
+
t10 = {
|
|
1097
|
+
busy,
|
|
1098
|
+
canSubmit,
|
|
1099
|
+
disabled,
|
|
1100
|
+
input,
|
|
1101
|
+
onAttach,
|
|
1102
|
+
onCancelContext,
|
|
1103
|
+
onEditLast,
|
|
1104
|
+
onStop,
|
|
1105
|
+
setInput,
|
|
1106
|
+
setText,
|
|
1107
|
+
submitOn,
|
|
1108
|
+
text
|
|
1109
|
+
};
|
|
1110
|
+
$[33] = busy;
|
|
1111
|
+
$[34] = canSubmit;
|
|
1112
|
+
$[35] = disabled;
|
|
1113
|
+
$[36] = input;
|
|
1114
|
+
$[37] = onAttach;
|
|
1115
|
+
$[38] = onCancelContext;
|
|
1116
|
+
$[39] = onEditLast;
|
|
1117
|
+
$[40] = onStop;
|
|
1118
|
+
$[41] = setText;
|
|
1119
|
+
$[42] = submitOn;
|
|
1120
|
+
$[43] = text;
|
|
1121
|
+
$[44] = t10;
|
|
1122
|
+
} else t10 = $[44];
|
|
1123
|
+
const t11 = dragging ? "" : void 0;
|
|
1124
|
+
let t12;
|
|
1125
|
+
if ($[45] !== drop || $[46] !== props || $[47] !== submitted || $[48] !== t11) {
|
|
1126
|
+
t12 = /* @__PURE__ */ jsx(Form, {
|
|
1127
|
+
...props,
|
|
1128
|
+
...drop,
|
|
1129
|
+
"data-dragging": t11,
|
|
1130
|
+
onSubmit: submitted
|
|
1131
|
+
});
|
|
1132
|
+
$[45] = drop;
|
|
1133
|
+
$[46] = props;
|
|
1134
|
+
$[47] = submitted;
|
|
1135
|
+
$[48] = t11;
|
|
1136
|
+
$[49] = t12;
|
|
1137
|
+
} else t12 = $[49];
|
|
1138
|
+
let t13;
|
|
1139
|
+
if ($[50] !== t10 || $[51] !== t12) {
|
|
1140
|
+
t13 = /* @__PURE__ */ jsx(StateProvider$1, {
|
|
1141
|
+
value: t10,
|
|
1142
|
+
children: t12
|
|
1143
|
+
});
|
|
1144
|
+
$[50] = t10;
|
|
1145
|
+
$[51] = t12;
|
|
1146
|
+
$[52] = t13;
|
|
1147
|
+
} else t13 = $[52];
|
|
1148
|
+
return t13;
|
|
1149
|
+
}
|
|
1150
|
+
//#endregion
|
|
1151
|
+
//#region src/composer/submit.tsx
|
|
1152
|
+
/**
|
|
1153
|
+
* Renders the actions `Button` with the composer's submit class.
|
|
1154
|
+
*/
|
|
1155
|
+
const Sent = withContext$3(Button, "submit");
|
|
1156
|
+
/**
|
|
1157
|
+
* Renders the control that sends, or stops while a response runs.
|
|
1158
|
+
*
|
|
1159
|
+
* @param props - The names, the stop glyph and the props of the actions `Button`, the send glyph
|
|
1160
|
+
* among its children.
|
|
1161
|
+
* @returns The `button` element.
|
|
1162
|
+
*/
|
|
1163
|
+
function Submit(t0) {
|
|
1164
|
+
const $ = c(18);
|
|
1165
|
+
let children;
|
|
1166
|
+
let onClick;
|
|
1167
|
+
let props;
|
|
1168
|
+
let stopIcon;
|
|
1169
|
+
let t1;
|
|
1170
|
+
let t2;
|
|
1171
|
+
if ($[0] !== t0) {
|
|
1172
|
+
({children, label: t1, onClick, stopIcon, stopLabel: t2, ...props} = t0);
|
|
1173
|
+
$[0] = t0;
|
|
1174
|
+
$[1] = children;
|
|
1175
|
+
$[2] = onClick;
|
|
1176
|
+
$[3] = props;
|
|
1177
|
+
$[4] = stopIcon;
|
|
1178
|
+
$[5] = t1;
|
|
1179
|
+
$[6] = t2;
|
|
1180
|
+
} else {
|
|
1181
|
+
children = $[1];
|
|
1182
|
+
onClick = $[2];
|
|
1183
|
+
props = $[3];
|
|
1184
|
+
stopIcon = $[4];
|
|
1185
|
+
t1 = $[5];
|
|
1186
|
+
t2 = $[6];
|
|
1187
|
+
}
|
|
1188
|
+
const label = t1 === void 0 ? "Send" : t1;
|
|
1189
|
+
const stopLabel = t2 === void 0 ? "Stop" : t2;
|
|
1190
|
+
const { busy, canSubmit, onStop } = useComposerState();
|
|
1191
|
+
const stop = busy ? onStop : void 0;
|
|
1192
|
+
const t3 = stop === void 0 && !canSubmit ? true : void 0;
|
|
1193
|
+
const t4 = stop === void 0 ? label : stopLabel;
|
|
1194
|
+
const t5 = stop === void 0 ? "submit" : "button";
|
|
1195
|
+
let t6;
|
|
1196
|
+
if ($[7] !== canSubmit || $[8] !== onClick || $[9] !== stop) {
|
|
1197
|
+
t6 = (event) => {
|
|
1198
|
+
onClick?.(event);
|
|
1199
|
+
if (stop !== void 0) stop();
|
|
1200
|
+
else if (!canSubmit) event.preventDefault();
|
|
1201
|
+
};
|
|
1202
|
+
$[7] = canSubmit;
|
|
1203
|
+
$[8] = onClick;
|
|
1204
|
+
$[9] = stop;
|
|
1205
|
+
$[10] = t6;
|
|
1206
|
+
} else t6 = $[10];
|
|
1207
|
+
const t7 = stop === void 0 ? children : stopIcon;
|
|
1208
|
+
let t8;
|
|
1209
|
+
if ($[11] !== props || $[12] !== t3 || $[13] !== t4 || $[14] !== t5 || $[15] !== t6 || $[16] !== t7) {
|
|
1210
|
+
t8 = /* @__PURE__ */ jsx(Sent, {
|
|
1211
|
+
"aria-disabled": t3,
|
|
1212
|
+
"aria-label": t4,
|
|
1213
|
+
shape: "square",
|
|
1214
|
+
size: "sm",
|
|
1215
|
+
type: t5,
|
|
1216
|
+
variant: "solid",
|
|
1217
|
+
...props,
|
|
1218
|
+
onClick: t6,
|
|
1219
|
+
children: t7
|
|
1220
|
+
});
|
|
1221
|
+
$[11] = props;
|
|
1222
|
+
$[12] = t3;
|
|
1223
|
+
$[13] = t4;
|
|
1224
|
+
$[14] = t5;
|
|
1225
|
+
$[15] = t6;
|
|
1226
|
+
$[16] = t7;
|
|
1227
|
+
$[17] = t8;
|
|
1228
|
+
} else t8 = $[17];
|
|
1229
|
+
return t8;
|
|
1230
|
+
}
|
|
1231
|
+
//#endregion
|
|
1232
|
+
//#region src/composer/toolbar.ts
|
|
1233
|
+
/**
|
|
1234
|
+
* Renders the row's `div`.
|
|
1235
|
+
*/
|
|
1236
|
+
const Toolbar = withContext$3("div", "toolbar");
|
|
1237
|
+
//#endregion
|
|
1238
|
+
//#region src/composer/index.ts
|
|
1239
|
+
var composer_exports = /* @__PURE__ */ __exportAll({
|
|
1240
|
+
AttachTrigger: () => AttachTrigger,
|
|
1241
|
+
Attachments: () => Attachments,
|
|
1242
|
+
Context: () => Context,
|
|
1243
|
+
Input: () => Input,
|
|
1244
|
+
Root: () => Root$3,
|
|
1245
|
+
Submit: () => Submit,
|
|
1246
|
+
Toolbar: () => Toolbar
|
|
1247
|
+
});
|
|
1248
|
+
//#endregion
|
|
1249
|
+
//#region src/conversation/context.ts
|
|
1250
|
+
/**
|
|
1251
|
+
* Binds the conversation recipe to the elements that render it.
|
|
1252
|
+
*
|
|
1253
|
+
* @remarks
|
|
1254
|
+
* The binding is kept out of `recipe.ts` because an application's style compiler imports the
|
|
1255
|
+
* recipe at build time, and a binding there would pull the React runtime into that import.
|
|
1256
|
+
*/
|
|
1257
|
+
/**
|
|
1258
|
+
* Supplies the element factories the conversation parts are built from.
|
|
1259
|
+
*/
|
|
1260
|
+
const { withContext: withContext$2, withProvider: withProvider$2 } = createSlotRecipeContext(recipe$2);
|
|
1261
|
+
//#endregion
|
|
1262
|
+
//#region src/conversation/state.ts
|
|
1263
|
+
/**
|
|
1264
|
+
* Provides the state to the parts, and reads it where a part renders.
|
|
1265
|
+
*/
|
|
1266
|
+
const [StateProvider, useConversationState] = createRequiredContext("Conversation");
|
|
1267
|
+
//#endregion
|
|
1268
|
+
//#region src/conversation/content.tsx
|
|
1269
|
+
/**
|
|
1270
|
+
* Renders the scroll area's viewport with the conversation's viewport class.
|
|
1271
|
+
*/
|
|
1272
|
+
const Viewport = withContext$2(ScrollArea.Viewport, "viewport");
|
|
1273
|
+
/**
|
|
1274
|
+
* Renders the scroll area's content with the conversation's content class.
|
|
1275
|
+
*/
|
|
1276
|
+
const Stacked = withContext$2(ScrollArea.Content, "content");
|
|
1277
|
+
/**
|
|
1278
|
+
* Renders the log around the content, with the engine's refs on both.
|
|
1279
|
+
*
|
|
1280
|
+
* @param props - The props of the scroll area's viewport, the turns among its children.
|
|
1281
|
+
* @returns The viewport element.
|
|
1282
|
+
*/
|
|
1283
|
+
function Content$1(t0) {
|
|
1284
|
+
const $ = c(11);
|
|
1285
|
+
let children;
|
|
1286
|
+
let props;
|
|
1287
|
+
if ($[0] !== t0) {
|
|
1288
|
+
({children, ...props} = t0);
|
|
1289
|
+
$[0] = t0;
|
|
1290
|
+
$[1] = children;
|
|
1291
|
+
$[2] = props;
|
|
1292
|
+
} else {
|
|
1293
|
+
children = $[1];
|
|
1294
|
+
props = $[2];
|
|
1295
|
+
}
|
|
1296
|
+
const { contentRef, label, scrollRef } = useConversationState();
|
|
1297
|
+
let t1;
|
|
1298
|
+
if ($[3] !== children || $[4] !== contentRef) {
|
|
1299
|
+
t1 = /* @__PURE__ */ jsx(Stacked, {
|
|
1300
|
+
ref: contentRef,
|
|
1301
|
+
children
|
|
1302
|
+
});
|
|
1303
|
+
$[3] = children;
|
|
1304
|
+
$[4] = contentRef;
|
|
1305
|
+
$[5] = t1;
|
|
1306
|
+
} else t1 = $[5];
|
|
1307
|
+
let t2;
|
|
1308
|
+
if ($[6] !== label || $[7] !== props || $[8] !== scrollRef || $[9] !== t1) {
|
|
1309
|
+
t2 = /* @__PURE__ */ jsx(Viewport, {
|
|
1310
|
+
"aria-label": label,
|
|
1311
|
+
"aria-live": "polite",
|
|
1312
|
+
"aria-relevant": "additions",
|
|
1313
|
+
role: "log",
|
|
1314
|
+
...props,
|
|
1315
|
+
ref: scrollRef,
|
|
1316
|
+
children: t1
|
|
1317
|
+
});
|
|
1318
|
+
$[6] = label;
|
|
1319
|
+
$[7] = props;
|
|
1320
|
+
$[8] = scrollRef;
|
|
1321
|
+
$[9] = t1;
|
|
1322
|
+
$[10] = t2;
|
|
1323
|
+
} else t2 = $[10];
|
|
1324
|
+
return t2;
|
|
1325
|
+
}
|
|
1326
|
+
//#endregion
|
|
1327
|
+
//#region src/conversation/jump-trigger.tsx
|
|
1328
|
+
/**
|
|
1329
|
+
* Renders the `div` that places the control over the transcript.
|
|
1330
|
+
*/
|
|
1331
|
+
const Placed = withContext$2("div", "jumpTrigger");
|
|
1332
|
+
/**
|
|
1333
|
+
* Renders the control while the view is away from the end.
|
|
1334
|
+
*
|
|
1335
|
+
* @param props - The control's name and the props of the actions `Button`, the glyph among its
|
|
1336
|
+
* children.
|
|
1337
|
+
* @returns The placed control, or nothing at the end of the transcript.
|
|
1338
|
+
*/
|
|
1339
|
+
function JumpTrigger(t0) {
|
|
1340
|
+
const $ = c(12);
|
|
1341
|
+
let onClick;
|
|
1342
|
+
let props;
|
|
1343
|
+
let t1;
|
|
1344
|
+
if ($[0] !== t0) {
|
|
1345
|
+
({label: t1, onClick, ...props} = t0);
|
|
1346
|
+
$[0] = t0;
|
|
1347
|
+
$[1] = onClick;
|
|
1348
|
+
$[2] = props;
|
|
1349
|
+
$[3] = t1;
|
|
1350
|
+
} else {
|
|
1351
|
+
onClick = $[1];
|
|
1352
|
+
props = $[2];
|
|
1353
|
+
t1 = $[3];
|
|
1354
|
+
}
|
|
1355
|
+
const label = t1 === void 0 ? "Jump to the latest message" : t1;
|
|
1356
|
+
const { atEnd, scrollRef, scrollToEnd } = useConversationState();
|
|
1357
|
+
if (atEnd) return null;
|
|
1358
|
+
let t2;
|
|
1359
|
+
if ($[4] !== onClick || $[5] !== scrollRef || $[6] !== scrollToEnd) {
|
|
1360
|
+
t2 = (event) => {
|
|
1361
|
+
onClick?.(event);
|
|
1362
|
+
scrollRef.current.focus({ preventScroll: true });
|
|
1363
|
+
scrollToEnd();
|
|
1364
|
+
};
|
|
1365
|
+
$[4] = onClick;
|
|
1366
|
+
$[5] = scrollRef;
|
|
1367
|
+
$[6] = scrollToEnd;
|
|
1368
|
+
$[7] = t2;
|
|
1369
|
+
} else t2 = $[7];
|
|
1370
|
+
let t3;
|
|
1371
|
+
if ($[8] !== label || $[9] !== props || $[10] !== t2) {
|
|
1372
|
+
t3 = /* @__PURE__ */ jsx(Placed, { children: /* @__PURE__ */ jsx(Button, {
|
|
1373
|
+
"aria-label": label,
|
|
1374
|
+
elevation: "floating",
|
|
1375
|
+
palette: "neutral",
|
|
1376
|
+
shape: "square",
|
|
1377
|
+
size: "sm",
|
|
1378
|
+
variant: "surface",
|
|
1379
|
+
...props,
|
|
1380
|
+
onClick: t2
|
|
1381
|
+
}) });
|
|
1382
|
+
$[8] = label;
|
|
1383
|
+
$[9] = props;
|
|
1384
|
+
$[10] = t2;
|
|
1385
|
+
$[11] = t3;
|
|
1386
|
+
} else t3 = $[11];
|
|
1387
|
+
return t3;
|
|
1388
|
+
}
|
|
1389
|
+
//#endregion
|
|
1390
|
+
//#region src/conversation/use-conversation.ts
|
|
1391
|
+
/**
|
|
1392
|
+
* Runs a conversation's scroll engine: whether the view is at the end, and the two scrolls.
|
|
1393
|
+
*
|
|
1394
|
+
* @remarks
|
|
1395
|
+
* The hook runs use-stick-to-bottom. While the view is at the end of the transcript, a message
|
|
1396
|
+
* that arrives or grows scrolls the view down with it. A wheel, a key or a drag that scrolls up
|
|
1397
|
+
* stops the follow, and scrolling back to within 70px of the end starts it again. The view opens
|
|
1398
|
+
* at the end, and under reduced motion every scroll is instant. An application calls the hook
|
|
1399
|
+
* and passes the result to `Conversation.Root` as `conversation`, so a control outside the
|
|
1400
|
+
* transcript, such as the composer's send, can scroll it. A root without `conversation` runs its
|
|
1401
|
+
* own.
|
|
1402
|
+
*/
|
|
1403
|
+
/**
|
|
1404
|
+
* Media query a reader matches by asking the system for reduced motion.
|
|
1405
|
+
*/
|
|
1406
|
+
const REDUCED_MOTION = "(prefers-reduced-motion: reduce)";
|
|
1407
|
+
/**
|
|
1408
|
+
* Engine options that open at the end at once and follow new content with a spring.
|
|
1409
|
+
*/
|
|
1410
|
+
const FOLLOWING = { initial: "instant" };
|
|
1411
|
+
/**
|
|
1412
|
+
* Engine options that open at the end and follow new content, both at once.
|
|
1413
|
+
*/
|
|
1414
|
+
const STILL = {
|
|
1415
|
+
initial: "instant",
|
|
1416
|
+
resize: "instant"
|
|
1417
|
+
};
|
|
1418
|
+
/**
|
|
1419
|
+
* Runs the scroll engine of one conversation.
|
|
1420
|
+
*
|
|
1421
|
+
* @returns Whether the view is at the end, the two scrolls, and the refs the root passes on.
|
|
1422
|
+
*/
|
|
1423
|
+
function useConversation() {
|
|
1424
|
+
const $ = c(16);
|
|
1425
|
+
let t0;
|
|
1426
|
+
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
|
1427
|
+
t0 = [REDUCED_MOTION];
|
|
1428
|
+
$[0] = t0;
|
|
1429
|
+
} else t0 = $[0];
|
|
1430
|
+
const t1 = useMediaQuery(t0);
|
|
1431
|
+
let t2;
|
|
1432
|
+
if ($[1] !== t1) {
|
|
1433
|
+
t2 = t1.includes(true);
|
|
1434
|
+
$[1] = t1;
|
|
1435
|
+
$[2] = t2;
|
|
1436
|
+
} else t2 = $[2];
|
|
1437
|
+
const reduced = t2;
|
|
1438
|
+
const { contentRef, isAtBottom, scrollRef, scrollToBottom, stopScroll } = useStickToBottom(reduced ? STILL : FOLLOWING);
|
|
1439
|
+
let t3;
|
|
1440
|
+
if ($[3] !== reduced || $[4] !== scrollToBottom) {
|
|
1441
|
+
t3 = function scrollToEnd() {
|
|
1442
|
+
scrollToBottom(reduced ? "instant" : "smooth");
|
|
1443
|
+
};
|
|
1444
|
+
$[3] = reduced;
|
|
1445
|
+
$[4] = scrollToBottom;
|
|
1446
|
+
$[5] = t3;
|
|
1447
|
+
} else t3 = $[5];
|
|
1448
|
+
const scrollToEnd = t3;
|
|
1449
|
+
let t4;
|
|
1450
|
+
if ($[6] !== reduced || $[7] !== scrollRef || $[8] !== stopScroll) {
|
|
1451
|
+
t4 = function scrollToMessage(id) {
|
|
1452
|
+
const viewport = scrollRef.current;
|
|
1453
|
+
const target = viewport?.querySelector(`[id="${CSS.escape(id)}"]`);
|
|
1454
|
+
if (!viewport || !target) return;
|
|
1455
|
+
stopScroll();
|
|
1456
|
+
viewport.scrollTo({
|
|
1457
|
+
behavior: reduced ? "instant" : "smooth",
|
|
1458
|
+
top: viewport.scrollTop + target.getBoundingClientRect().top - viewport.getBoundingClientRect().top
|
|
1459
|
+
});
|
|
1460
|
+
};
|
|
1461
|
+
$[6] = reduced;
|
|
1462
|
+
$[7] = scrollRef;
|
|
1463
|
+
$[8] = stopScroll;
|
|
1464
|
+
$[9] = t4;
|
|
1465
|
+
} else t4 = $[9];
|
|
1466
|
+
const scrollToMessage = t4;
|
|
1467
|
+
let t5;
|
|
1468
|
+
if ($[10] !== contentRef || $[11] !== isAtBottom || $[12] !== scrollRef || $[13] !== scrollToEnd || $[14] !== scrollToMessage) {
|
|
1469
|
+
t5 = {
|
|
1470
|
+
atEnd: isAtBottom,
|
|
1471
|
+
contentRef,
|
|
1472
|
+
scrollRef,
|
|
1473
|
+
scrollToEnd,
|
|
1474
|
+
scrollToMessage
|
|
1475
|
+
};
|
|
1476
|
+
$[10] = contentRef;
|
|
1477
|
+
$[11] = isAtBottom;
|
|
1478
|
+
$[12] = scrollRef;
|
|
1479
|
+
$[13] = scrollToEnd;
|
|
1480
|
+
$[14] = scrollToMessage;
|
|
1481
|
+
$[15] = t5;
|
|
1482
|
+
} else t5 = $[15];
|
|
1483
|
+
return t5;
|
|
1484
|
+
}
|
|
1485
|
+
//#endregion
|
|
1486
|
+
//#region src/conversation/root.tsx
|
|
1487
|
+
/**
|
|
1488
|
+
* Renders the scroll area's root with the conversation's root class.
|
|
1489
|
+
*/
|
|
1490
|
+
const Framed = withProvider$2(ScrollArea.Root, "root");
|
|
1491
|
+
/**
|
|
1492
|
+
* Renders the root and provides the conversation's state to its parts.
|
|
1493
|
+
*
|
|
1494
|
+
* @param props - The engine, the log's name and the props of the scroll area's root.
|
|
1495
|
+
* @returns The scroll area's root inside the state's provider.
|
|
1496
|
+
*/
|
|
1497
|
+
function Root$2(t0) {
|
|
1498
|
+
const $ = c(19);
|
|
1499
|
+
let children;
|
|
1500
|
+
let conversation;
|
|
1501
|
+
let props;
|
|
1502
|
+
let t1;
|
|
1503
|
+
if ($[0] !== t0) {
|
|
1504
|
+
({children, conversation, label: t1, ...props} = t0);
|
|
1505
|
+
$[0] = t0;
|
|
1506
|
+
$[1] = children;
|
|
1507
|
+
$[2] = conversation;
|
|
1508
|
+
$[3] = props;
|
|
1509
|
+
$[4] = t1;
|
|
1510
|
+
} else {
|
|
1511
|
+
children = $[1];
|
|
1512
|
+
conversation = $[2];
|
|
1513
|
+
props = $[3];
|
|
1514
|
+
t1 = $[4];
|
|
1515
|
+
}
|
|
1516
|
+
const label = t1 === void 0 ? "Messages" : t1;
|
|
1517
|
+
const own = useConversation();
|
|
1518
|
+
const { atEnd, contentRef, scrollRef, scrollToEnd, scrollToMessage } = conversation ?? own;
|
|
1519
|
+
let t2;
|
|
1520
|
+
if ($[5] !== atEnd || $[6] !== contentRef || $[7] !== label || $[8] !== scrollRef || $[9] !== scrollToEnd || $[10] !== scrollToMessage) {
|
|
1521
|
+
t2 = {
|
|
1522
|
+
atEnd,
|
|
1523
|
+
contentRef,
|
|
1524
|
+
label,
|
|
1525
|
+
scrollRef,
|
|
1526
|
+
scrollToEnd,
|
|
1527
|
+
scrollToMessage
|
|
1528
|
+
};
|
|
1529
|
+
$[5] = atEnd;
|
|
1530
|
+
$[6] = contentRef;
|
|
1531
|
+
$[7] = label;
|
|
1532
|
+
$[8] = scrollRef;
|
|
1533
|
+
$[9] = scrollToEnd;
|
|
1534
|
+
$[10] = scrollToMessage;
|
|
1535
|
+
$[11] = t2;
|
|
1536
|
+
} else t2 = $[11];
|
|
1537
|
+
let t3;
|
|
1538
|
+
if ($[12] === Symbol.for("react.memo_cache_sentinel")) {
|
|
1539
|
+
t3 = /* @__PURE__ */ jsx(ScrollArea.Scrollbar, {});
|
|
1540
|
+
$[12] = t3;
|
|
1541
|
+
} else t3 = $[12];
|
|
1542
|
+
let t4;
|
|
1543
|
+
if ($[13] !== children || $[14] !== props) {
|
|
1544
|
+
t4 = /* @__PURE__ */ jsxs(Framed, {
|
|
1545
|
+
...props,
|
|
1546
|
+
children: [children, t3]
|
|
1547
|
+
});
|
|
1548
|
+
$[13] = children;
|
|
1549
|
+
$[14] = props;
|
|
1550
|
+
$[15] = t4;
|
|
1551
|
+
} else t4 = $[15];
|
|
1552
|
+
let t5;
|
|
1553
|
+
if ($[16] !== t2 || $[17] !== t4) {
|
|
1554
|
+
t5 = /* @__PURE__ */ jsx(StateProvider, {
|
|
1555
|
+
value: t2,
|
|
1556
|
+
children: t4
|
|
1557
|
+
});
|
|
1558
|
+
$[16] = t2;
|
|
1559
|
+
$[17] = t4;
|
|
1560
|
+
$[18] = t5;
|
|
1561
|
+
} else t5 = $[18];
|
|
1562
|
+
return t5;
|
|
1563
|
+
}
|
|
1564
|
+
//#endregion
|
|
1565
|
+
//#region src/conversation/typing.tsx
|
|
1566
|
+
/**
|
|
1567
|
+
* Renders the row's `div`.
|
|
1568
|
+
*/
|
|
1569
|
+
const Row = withContext$2("div", "typing");
|
|
1570
|
+
/**
|
|
1571
|
+
* Renders the `span` around the three dots.
|
|
1572
|
+
*/
|
|
1573
|
+
const Dots = withContext$2("span", "dots");
|
|
1574
|
+
/**
|
|
1575
|
+
* Renders the dots before the caller's words.
|
|
1576
|
+
*
|
|
1577
|
+
* @param props - The props of a `div`, the words among its children.
|
|
1578
|
+
* @returns The `div` element.
|
|
1579
|
+
*/
|
|
1580
|
+
function Typing(t0) {
|
|
1581
|
+
const $ = c(7);
|
|
1582
|
+
let children;
|
|
1583
|
+
let props;
|
|
1584
|
+
if ($[0] !== t0) {
|
|
1585
|
+
({children, ...props} = t0);
|
|
1586
|
+
$[0] = t0;
|
|
1587
|
+
$[1] = children;
|
|
1588
|
+
$[2] = props;
|
|
1589
|
+
} else {
|
|
1590
|
+
children = $[1];
|
|
1591
|
+
props = $[2];
|
|
1592
|
+
}
|
|
1593
|
+
let t1;
|
|
1594
|
+
if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
|
|
1595
|
+
t1 = /* @__PURE__ */ jsxs(Dots, {
|
|
1596
|
+
"aria-hidden": "true",
|
|
1597
|
+
children: [
|
|
1598
|
+
/* @__PURE__ */ jsx("span", {}),
|
|
1599
|
+
/* @__PURE__ */ jsx("span", {}),
|
|
1600
|
+
/* @__PURE__ */ jsx("span", {})
|
|
1601
|
+
]
|
|
1602
|
+
});
|
|
1603
|
+
$[3] = t1;
|
|
1604
|
+
} else t1 = $[3];
|
|
1605
|
+
let t2;
|
|
1606
|
+
if ($[4] !== children || $[5] !== props) {
|
|
1607
|
+
t2 = /* @__PURE__ */ jsxs(Row, {
|
|
1608
|
+
...props,
|
|
1609
|
+
children: [t1, children]
|
|
1610
|
+
});
|
|
1611
|
+
$[4] = children;
|
|
1612
|
+
$[5] = props;
|
|
1613
|
+
$[6] = t2;
|
|
1614
|
+
} else t2 = $[6];
|
|
1615
|
+
return t2;
|
|
1616
|
+
}
|
|
1617
|
+
//#endregion
|
|
1618
|
+
//#region src/conversation/index.ts
|
|
1619
|
+
var conversation_exports = /* @__PURE__ */ __exportAll({
|
|
1620
|
+
Content: () => Content$1,
|
|
1621
|
+
JumpTrigger: () => JumpTrigger,
|
|
1622
|
+
Root: () => Root$2,
|
|
1623
|
+
Typing: () => Typing,
|
|
1624
|
+
useConversation: () => useConversation
|
|
1625
|
+
});
|
|
1626
|
+
//#endregion
|
|
1627
|
+
//#region src/message/context.ts
|
|
1628
|
+
/**
|
|
1629
|
+
* Binds the message recipe to the elements that render it.
|
|
1630
|
+
*
|
|
1631
|
+
* @remarks
|
|
1632
|
+
* The binding is kept out of `recipe.ts` because an application's style compiler imports the
|
|
1633
|
+
* recipe at build time, and a binding there would pull the React runtime into that import.
|
|
1634
|
+
*/
|
|
1635
|
+
/**
|
|
1636
|
+
* Supplies the element factories the message parts are built from.
|
|
1637
|
+
*/
|
|
1638
|
+
const { withContext: withContext$1, withProvider: withProvider$1 } = createSlotRecipeContext(recipe$3);
|
|
1639
|
+
//#endregion
|
|
1640
|
+
//#region src/message/actions.ts
|
|
1641
|
+
/**
|
|
1642
|
+
* Renders the actions' `div`.
|
|
1643
|
+
*/
|
|
1644
|
+
const Actions = withContext$1("div", "actions");
|
|
1645
|
+
//#endregion
|
|
1646
|
+
//#region src/message/avatar.ts
|
|
1647
|
+
/**
|
|
1648
|
+
* Renders the avatar's `div`.
|
|
1649
|
+
*/
|
|
1650
|
+
const Avatar = withContext$1("div", "avatar");
|
|
1651
|
+
//#endregion
|
|
1652
|
+
//#region src/message/bubble.tsx
|
|
1653
|
+
/**
|
|
1654
|
+
* Renders the bubble's `div`.
|
|
1655
|
+
*/
|
|
1656
|
+
const Surface = withContext$1("div", "bubble");
|
|
1657
|
+
/**
|
|
1658
|
+
* Renders the bubble, with `data-failed` while `failed`.
|
|
1659
|
+
*
|
|
1660
|
+
* @param props - Whether the message failed, and the props of a `div`.
|
|
1661
|
+
* @returns The `div` element.
|
|
1662
|
+
*/
|
|
1663
|
+
function Bubble(t0) {
|
|
1664
|
+
const $ = c(6);
|
|
1665
|
+
let props;
|
|
1666
|
+
let t1;
|
|
1667
|
+
if ($[0] !== t0) {
|
|
1668
|
+
({failed: t1, ...props} = t0);
|
|
1669
|
+
$[0] = t0;
|
|
1670
|
+
$[1] = props;
|
|
1671
|
+
$[2] = t1;
|
|
1672
|
+
} else {
|
|
1673
|
+
props = $[1];
|
|
1674
|
+
t1 = $[2];
|
|
1675
|
+
}
|
|
1676
|
+
const t2 = (t1 === void 0 ? false : t1) ? "" : void 0;
|
|
1677
|
+
let t3;
|
|
1678
|
+
if ($[3] !== props || $[4] !== t2) {
|
|
1679
|
+
t3 = /* @__PURE__ */ jsx(Surface, {
|
|
1680
|
+
"data-failed": t2,
|
|
1681
|
+
...props
|
|
1682
|
+
});
|
|
1683
|
+
$[3] = props;
|
|
1684
|
+
$[4] = t2;
|
|
1685
|
+
$[5] = t3;
|
|
1686
|
+
} else t3 = $[5];
|
|
1687
|
+
return t3;
|
|
1688
|
+
}
|
|
1689
|
+
//#endregion
|
|
1690
|
+
//#region src/message/content.ts
|
|
1691
|
+
/**
|
|
1692
|
+
* Renders the column's `div`.
|
|
1693
|
+
*/
|
|
1694
|
+
const Content = withContext$1("div", "content");
|
|
1695
|
+
//#endregion
|
|
1696
|
+
//#region src/message/footer.ts
|
|
1697
|
+
/**
|
|
1698
|
+
* Renders the footer's `div`.
|
|
1699
|
+
*/
|
|
1700
|
+
const Footer = withContext$1("div", "footer");
|
|
1701
|
+
//#endregion
|
|
1702
|
+
//#region src/message/header.ts
|
|
1703
|
+
/**
|
|
1704
|
+
* Renders the header's `div`.
|
|
1705
|
+
*/
|
|
1706
|
+
const Header = withContext$1("div", "header");
|
|
1707
|
+
//#endregion
|
|
1708
|
+
//#region src/message/root.ts
|
|
1709
|
+
/**
|
|
1710
|
+
* Renders the root `article` with the message's variants.
|
|
1711
|
+
*/
|
|
1712
|
+
const Root$1 = withProvider$1("article", "root");
|
|
1713
|
+
//#endregion
|
|
1714
|
+
//#region src/message/status.tsx
|
|
1715
|
+
/**
|
|
1716
|
+
* Renders the status `span`.
|
|
1717
|
+
*/
|
|
1718
|
+
const Marked = withContext$1("span", "status");
|
|
1719
|
+
/**
|
|
1720
|
+
* Renders the status with its state written as `data-status`.
|
|
1721
|
+
*
|
|
1722
|
+
* @param props - How far the message has got, and the props of a `span`.
|
|
1723
|
+
* @returns The `span` element.
|
|
1724
|
+
*/
|
|
1725
|
+
function Status(t0) {
|
|
1726
|
+
const $ = c(6);
|
|
1727
|
+
let props;
|
|
1728
|
+
let status;
|
|
1729
|
+
if ($[0] !== t0) {
|
|
1730
|
+
({status, ...props} = t0);
|
|
1731
|
+
$[0] = t0;
|
|
1732
|
+
$[1] = props;
|
|
1733
|
+
$[2] = status;
|
|
1734
|
+
} else {
|
|
1735
|
+
props = $[1];
|
|
1736
|
+
status = $[2];
|
|
1737
|
+
}
|
|
1738
|
+
let t1;
|
|
1739
|
+
if ($[3] !== props || $[4] !== status) {
|
|
1740
|
+
t1 = /* @__PURE__ */ jsx(Marked, {
|
|
1741
|
+
"data-status": status,
|
|
1742
|
+
...props
|
|
1743
|
+
});
|
|
1744
|
+
$[3] = props;
|
|
1745
|
+
$[4] = status;
|
|
1746
|
+
$[5] = t1;
|
|
1747
|
+
} else t1 = $[5];
|
|
1748
|
+
return t1;
|
|
1749
|
+
}
|
|
1750
|
+
//#endregion
|
|
1751
|
+
//#region src/message/index.ts
|
|
1752
|
+
var message_exports = /* @__PURE__ */ __exportAll({
|
|
1753
|
+
Actions: () => Actions,
|
|
1754
|
+
Avatar: () => Avatar,
|
|
1755
|
+
Bubble: () => Bubble,
|
|
1756
|
+
Content: () => Content,
|
|
1757
|
+
Footer: () => Footer,
|
|
1758
|
+
Header: () => Header,
|
|
1759
|
+
Root: () => Root$1,
|
|
1760
|
+
Status: () => Status
|
|
1761
|
+
});
|
|
1762
|
+
//#endregion
|
|
1763
|
+
//#region src/reactions/context.ts
|
|
1764
|
+
/**
|
|
1765
|
+
* Binds the reactions recipe to the elements that render it.
|
|
1766
|
+
*
|
|
1767
|
+
* @remarks
|
|
1768
|
+
* The binding is kept out of `recipe.ts` because an application's style compiler imports the
|
|
1769
|
+
* recipe at build time, and a binding there would pull the React runtime into that import.
|
|
1770
|
+
*/
|
|
1771
|
+
/**
|
|
1772
|
+
* Supplies the element factories the reactions parts are built from.
|
|
1773
|
+
*/
|
|
1774
|
+
const { withContext, withProvider } = createSlotRecipeContext(recipe$4);
|
|
1775
|
+
//#endregion
|
|
1776
|
+
//#region src/reactions/state.ts
|
|
1777
|
+
/**
|
|
1778
|
+
* Provides the picker's choice handler to the choices inside it.
|
|
1779
|
+
*/
|
|
1780
|
+
/**
|
|
1781
|
+
* Provides the picker's state to the choices, and reads it where a choice renders.
|
|
1782
|
+
*/
|
|
1783
|
+
const [PickerProvider, usePickerState] = createRequiredContext("Reactions.Picker");
|
|
1784
|
+
//#endregion
|
|
1785
|
+
//#region src/reactions/choice.tsx
|
|
1786
|
+
/**
|
|
1787
|
+
* Renders the `span` around the glyph.
|
|
1788
|
+
*/
|
|
1789
|
+
const Glyph$1 = withContext("span", "glyph");
|
|
1790
|
+
/**
|
|
1791
|
+
* Renders the choice's button.
|
|
1792
|
+
*
|
|
1793
|
+
* @param props - The name, the value, and the props of the actions `Button`, the glyph among its
|
|
1794
|
+
* children.
|
|
1795
|
+
* @returns The `button` element.
|
|
1796
|
+
*/
|
|
1797
|
+
function Choice(t0) {
|
|
1798
|
+
const $ = c(17);
|
|
1799
|
+
let children;
|
|
1800
|
+
let label;
|
|
1801
|
+
let onClick;
|
|
1802
|
+
let props;
|
|
1803
|
+
let value;
|
|
1804
|
+
if ($[0] !== t0) {
|
|
1805
|
+
({children, label, onClick, value, ...props} = t0);
|
|
1806
|
+
$[0] = t0;
|
|
1807
|
+
$[1] = children;
|
|
1808
|
+
$[2] = label;
|
|
1809
|
+
$[3] = onClick;
|
|
1810
|
+
$[4] = props;
|
|
1811
|
+
$[5] = value;
|
|
1812
|
+
} else {
|
|
1813
|
+
children = $[1];
|
|
1814
|
+
label = $[2];
|
|
1815
|
+
onClick = $[3];
|
|
1816
|
+
props = $[4];
|
|
1817
|
+
value = $[5];
|
|
1818
|
+
}
|
|
1819
|
+
const { choose } = usePickerState();
|
|
1820
|
+
let t1;
|
|
1821
|
+
if ($[6] !== choose || $[7] !== onClick || $[8] !== value) {
|
|
1822
|
+
t1 = (event) => {
|
|
1823
|
+
onClick?.(event);
|
|
1824
|
+
choose(value);
|
|
1825
|
+
};
|
|
1826
|
+
$[6] = choose;
|
|
1827
|
+
$[7] = onClick;
|
|
1828
|
+
$[8] = value;
|
|
1829
|
+
$[9] = t1;
|
|
1830
|
+
} else t1 = $[9];
|
|
1831
|
+
let t2;
|
|
1832
|
+
if ($[10] !== children) {
|
|
1833
|
+
t2 = /* @__PURE__ */ jsx(Glyph$1, {
|
|
1834
|
+
"aria-hidden": "true",
|
|
1835
|
+
children
|
|
1836
|
+
});
|
|
1837
|
+
$[10] = children;
|
|
1838
|
+
$[11] = t2;
|
|
1839
|
+
} else t2 = $[11];
|
|
1840
|
+
let t3;
|
|
1841
|
+
if ($[12] !== label || $[13] !== props || $[14] !== t1 || $[15] !== t2) {
|
|
1842
|
+
t3 = /* @__PURE__ */ jsx(Button, {
|
|
1843
|
+
"aria-label": label,
|
|
1844
|
+
shape: "square",
|
|
1845
|
+
size: "sm",
|
|
1846
|
+
variant: "ghost",
|
|
1847
|
+
...props,
|
|
1848
|
+
onClick: t1,
|
|
1849
|
+
children: t2
|
|
1850
|
+
});
|
|
1851
|
+
$[12] = label;
|
|
1852
|
+
$[13] = props;
|
|
1853
|
+
$[14] = t1;
|
|
1854
|
+
$[15] = t2;
|
|
1855
|
+
$[16] = t3;
|
|
1856
|
+
} else t3 = $[16];
|
|
1857
|
+
return t3;
|
|
1858
|
+
}
|
|
1859
|
+
//#endregion
|
|
1860
|
+
//#region src/reactions/item.tsx
|
|
1861
|
+
/**
|
|
1862
|
+
* Renders the `span` around the glyph.
|
|
1863
|
+
*/
|
|
1864
|
+
const Glyph = withContext("span", "glyph");
|
|
1865
|
+
/**
|
|
1866
|
+
* Renders the `span` around the count.
|
|
1867
|
+
*/
|
|
1868
|
+
const Count = withContext("span", "count");
|
|
1869
|
+
/**
|
|
1870
|
+
* Renders the reaction's button.
|
|
1871
|
+
*
|
|
1872
|
+
* @param props - The count, the name, whether the reader reacted, and the props of the actions
|
|
1873
|
+
* `Button`, the glyph among its children.
|
|
1874
|
+
* @returns The `button` element.
|
|
1875
|
+
*/
|
|
1876
|
+
function Item(t0) {
|
|
1877
|
+
const $ = c(17);
|
|
1878
|
+
let children;
|
|
1879
|
+
let count;
|
|
1880
|
+
let label;
|
|
1881
|
+
let props;
|
|
1882
|
+
let t1;
|
|
1883
|
+
if ($[0] !== t0) {
|
|
1884
|
+
({children, count, label, pressed: t1, ...props} = t0);
|
|
1885
|
+
$[0] = t0;
|
|
1886
|
+
$[1] = children;
|
|
1887
|
+
$[2] = count;
|
|
1888
|
+
$[3] = label;
|
|
1889
|
+
$[4] = props;
|
|
1890
|
+
$[5] = t1;
|
|
1891
|
+
} else {
|
|
1892
|
+
children = $[1];
|
|
1893
|
+
count = $[2];
|
|
1894
|
+
label = $[3];
|
|
1895
|
+
props = $[4];
|
|
1896
|
+
t1 = $[5];
|
|
1897
|
+
}
|
|
1898
|
+
const pressed = t1 === void 0 ? false : t1;
|
|
1899
|
+
const t2 = pressed ? "primary" : "neutral";
|
|
1900
|
+
let t3;
|
|
1901
|
+
if ($[6] !== children) {
|
|
1902
|
+
t3 = /* @__PURE__ */ jsx(Glyph, {
|
|
1903
|
+
"aria-hidden": "true",
|
|
1904
|
+
children
|
|
1905
|
+
});
|
|
1906
|
+
$[6] = children;
|
|
1907
|
+
$[7] = t3;
|
|
1908
|
+
} else t3 = $[7];
|
|
1909
|
+
let t4;
|
|
1910
|
+
if ($[8] !== count) {
|
|
1911
|
+
t4 = count === void 0 ? null : /* @__PURE__ */ jsx(Count, {
|
|
1912
|
+
"aria-hidden": "true",
|
|
1913
|
+
children: count
|
|
1914
|
+
});
|
|
1915
|
+
$[8] = count;
|
|
1916
|
+
$[9] = t4;
|
|
1917
|
+
} else t4 = $[9];
|
|
1918
|
+
let t5;
|
|
1919
|
+
if ($[10] !== label || $[11] !== pressed || $[12] !== props || $[13] !== t2 || $[14] !== t3 || $[15] !== t4) {
|
|
1920
|
+
t5 = /* @__PURE__ */ jsxs(Button, {
|
|
1921
|
+
"aria-label": label,
|
|
1922
|
+
"aria-pressed": pressed,
|
|
1923
|
+
palette: t2,
|
|
1924
|
+
size: "xs",
|
|
1925
|
+
variant: "outline",
|
|
1926
|
+
...props,
|
|
1927
|
+
children: [t3, t4]
|
|
1928
|
+
});
|
|
1929
|
+
$[10] = label;
|
|
1930
|
+
$[11] = pressed;
|
|
1931
|
+
$[12] = props;
|
|
1932
|
+
$[13] = t2;
|
|
1933
|
+
$[14] = t3;
|
|
1934
|
+
$[15] = t4;
|
|
1935
|
+
$[16] = t5;
|
|
1936
|
+
} else t5 = $[16];
|
|
1937
|
+
return t5;
|
|
1938
|
+
}
|
|
1939
|
+
//#endregion
|
|
1940
|
+
//#region src/reactions/picker.tsx
|
|
1941
|
+
/**
|
|
1942
|
+
* Renders the control that adds a reaction: a button that opens the disclosure `Popover` with the
|
|
1943
|
+
* caller's choices.
|
|
1944
|
+
*
|
|
1945
|
+
* @remarks
|
|
1946
|
+
* The trigger is the actions `IconButton`, extra small in the ghost look, named by `label`, with
|
|
1947
|
+
* the caller's glyph. The panel is portalled, named by `choicesLabel`, and lays the choices out
|
|
1948
|
+
* in a row that wraps. A choice reports its value through `onSelect` and closes the panel, and
|
|
1949
|
+
* the popover returns focus to the trigger. A reaction is one act, so a panel left open would
|
|
1950
|
+
* invite a second press that removes it again.
|
|
1951
|
+
*/
|
|
1952
|
+
/**
|
|
1953
|
+
* Renders the `div` that lays the choices out.
|
|
1954
|
+
*/
|
|
1955
|
+
const Choices = withContext("div", "choices");
|
|
1956
|
+
/**
|
|
1957
|
+
* Renders the trigger and the panel, and closes the panel on a choice.
|
|
1958
|
+
*
|
|
1959
|
+
* @param props - The trigger's glyph and name, the panel's name, the handler of a choice and the
|
|
1960
|
+
* popover root's props.
|
|
1961
|
+
* @returns The popover's root.
|
|
1962
|
+
*/
|
|
1963
|
+
function Picker(t0) {
|
|
1964
|
+
const $ = c(29);
|
|
1965
|
+
let children;
|
|
1966
|
+
let icon;
|
|
1967
|
+
let onSelect;
|
|
1968
|
+
let props;
|
|
1969
|
+
let t1;
|
|
1970
|
+
let t2;
|
|
1971
|
+
if ($[0] !== t0) {
|
|
1972
|
+
({children, choicesLabel: t1, icon, label: t2, onSelect, ...props} = t0);
|
|
1973
|
+
$[0] = t0;
|
|
1974
|
+
$[1] = children;
|
|
1975
|
+
$[2] = icon;
|
|
1976
|
+
$[3] = onSelect;
|
|
1977
|
+
$[4] = props;
|
|
1978
|
+
$[5] = t1;
|
|
1979
|
+
$[6] = t2;
|
|
1980
|
+
} else {
|
|
1981
|
+
children = $[1];
|
|
1982
|
+
icon = $[2];
|
|
1983
|
+
onSelect = $[3];
|
|
1984
|
+
props = $[4];
|
|
1985
|
+
t1 = $[5];
|
|
1986
|
+
t2 = $[6];
|
|
1987
|
+
}
|
|
1988
|
+
const choicesLabel = t1 === void 0 ? "Choose a reaction" : t1;
|
|
1989
|
+
const label = t2 === void 0 ? "Add a reaction" : t2;
|
|
1990
|
+
const [open, setOpen] = useState(false);
|
|
1991
|
+
let t3;
|
|
1992
|
+
if ($[7] !== onSelect) {
|
|
1993
|
+
t3 = function choose(value) {
|
|
1994
|
+
onSelect?.(value);
|
|
1995
|
+
setOpen(false);
|
|
1996
|
+
};
|
|
1997
|
+
$[7] = onSelect;
|
|
1998
|
+
$[8] = t3;
|
|
1999
|
+
} else t3 = $[8];
|
|
2000
|
+
const choose = t3;
|
|
2001
|
+
let t4;
|
|
2002
|
+
if ($[9] === Symbol.for("react.memo_cache_sentinel")) {
|
|
2003
|
+
t4 = (details) => {
|
|
2004
|
+
setOpen(details.open);
|
|
2005
|
+
};
|
|
2006
|
+
$[9] = t4;
|
|
2007
|
+
} else t4 = $[9];
|
|
2008
|
+
let t5;
|
|
2009
|
+
if ($[10] === Symbol.for("react.memo_cache_sentinel")) {
|
|
2010
|
+
t5 = {
|
|
2011
|
+
size: "xs",
|
|
2012
|
+
variant: "ghost"
|
|
2013
|
+
};
|
|
2014
|
+
$[10] = t5;
|
|
2015
|
+
} else t5 = $[10];
|
|
2016
|
+
let t6;
|
|
2017
|
+
if ($[11] !== icon || $[12] !== label) {
|
|
2018
|
+
t6 = /* @__PURE__ */ jsx(ButtonPropsProvider, {
|
|
2019
|
+
value: t5,
|
|
2020
|
+
children: /* @__PURE__ */ jsx(Popover.Trigger, {
|
|
2021
|
+
"aria-label": label,
|
|
2022
|
+
as: IconButton,
|
|
2023
|
+
children: icon
|
|
2024
|
+
})
|
|
2025
|
+
});
|
|
2026
|
+
$[11] = icon;
|
|
2027
|
+
$[12] = label;
|
|
2028
|
+
$[13] = t6;
|
|
2029
|
+
} else t6 = $[13];
|
|
2030
|
+
let t7;
|
|
2031
|
+
if ($[14] !== choose) {
|
|
2032
|
+
t7 = { choose };
|
|
2033
|
+
$[14] = choose;
|
|
2034
|
+
$[15] = t7;
|
|
2035
|
+
} else t7 = $[15];
|
|
2036
|
+
let t8;
|
|
2037
|
+
if ($[16] !== children) {
|
|
2038
|
+
t8 = /* @__PURE__ */ jsx(Choices, { children });
|
|
2039
|
+
$[16] = children;
|
|
2040
|
+
$[17] = t8;
|
|
2041
|
+
} else t8 = $[17];
|
|
2042
|
+
let t9;
|
|
2043
|
+
if ($[18] !== t7 || $[19] !== t8) {
|
|
2044
|
+
t9 = /* @__PURE__ */ jsx(PickerProvider, {
|
|
2045
|
+
value: t7,
|
|
2046
|
+
children: t8
|
|
2047
|
+
});
|
|
2048
|
+
$[18] = t7;
|
|
2049
|
+
$[19] = t8;
|
|
2050
|
+
$[20] = t9;
|
|
2051
|
+
} else t9 = $[20];
|
|
2052
|
+
let t10;
|
|
2053
|
+
if ($[21] !== choicesLabel || $[22] !== t9) {
|
|
2054
|
+
t10 = /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(Popover.Positioner, { children: /* @__PURE__ */ jsx(Popover.Content, {
|
|
2055
|
+
"aria-label": choicesLabel,
|
|
2056
|
+
children: t9
|
|
2057
|
+
}) }) });
|
|
2058
|
+
$[21] = choicesLabel;
|
|
2059
|
+
$[22] = t9;
|
|
2060
|
+
$[23] = t10;
|
|
2061
|
+
} else t10 = $[23];
|
|
2062
|
+
let t11;
|
|
2063
|
+
if ($[24] !== open || $[25] !== props || $[26] !== t10 || $[27] !== t6) {
|
|
2064
|
+
t11 = /* @__PURE__ */ jsxs(Popover.Root, {
|
|
2065
|
+
...props,
|
|
2066
|
+
onOpenChange: t4,
|
|
2067
|
+
open,
|
|
2068
|
+
children: [t6, t10]
|
|
2069
|
+
});
|
|
2070
|
+
$[24] = open;
|
|
2071
|
+
$[25] = props;
|
|
2072
|
+
$[26] = t10;
|
|
2073
|
+
$[27] = t6;
|
|
2074
|
+
$[28] = t11;
|
|
2075
|
+
} else t11 = $[28];
|
|
2076
|
+
return t11;
|
|
2077
|
+
}
|
|
2078
|
+
//#endregion
|
|
2079
|
+
//#region src/reactions/root.tsx
|
|
2080
|
+
/**
|
|
2081
|
+
* Renders the `fieldset` with the reactions' root class.
|
|
2082
|
+
*/
|
|
2083
|
+
const Grouped = withProvider("fieldset", "root");
|
|
2084
|
+
/**
|
|
2085
|
+
* Renders the row, named by `label`.
|
|
2086
|
+
*
|
|
2087
|
+
* @param props - The row's name and the props of a `fieldset`.
|
|
2088
|
+
* @returns The `fieldset` element.
|
|
2089
|
+
*/
|
|
2090
|
+
function Root(t0) {
|
|
2091
|
+
const $ = c(6);
|
|
2092
|
+
let props;
|
|
2093
|
+
let t1;
|
|
2094
|
+
if ($[0] !== t0) {
|
|
2095
|
+
({label: t1, ...props} = t0);
|
|
2096
|
+
$[0] = t0;
|
|
2097
|
+
$[1] = props;
|
|
2098
|
+
$[2] = t1;
|
|
2099
|
+
} else {
|
|
2100
|
+
props = $[1];
|
|
2101
|
+
t1 = $[2];
|
|
2102
|
+
}
|
|
2103
|
+
const label = t1 === void 0 ? "Reactions" : t1;
|
|
2104
|
+
let t2;
|
|
2105
|
+
if ($[3] !== label || $[4] !== props) {
|
|
2106
|
+
t2 = /* @__PURE__ */ jsx(Grouped, {
|
|
2107
|
+
"aria-label": label,
|
|
2108
|
+
...props
|
|
2109
|
+
});
|
|
2110
|
+
$[3] = label;
|
|
2111
|
+
$[4] = props;
|
|
2112
|
+
$[5] = t2;
|
|
2113
|
+
} else t2 = $[5];
|
|
2114
|
+
return t2;
|
|
2115
|
+
}
|
|
2116
|
+
//#endregion
|
|
2117
|
+
//#region src/reactions/index.ts
|
|
2118
|
+
var reactions_exports = /* @__PURE__ */ __exportAll({
|
|
2119
|
+
Choice: () => Choice,
|
|
2120
|
+
Item: () => Item,
|
|
2121
|
+
Picker: () => Picker,
|
|
2122
|
+
Root: () => Root
|
|
2123
|
+
});
|
|
2124
|
+
//#endregion
|
|
2125
|
+
//#region src/turns/group-turns.ts
|
|
2126
|
+
/**
|
|
2127
|
+
* Longest gap between two messages of one turn when the caller states none: 5 minutes.
|
|
2128
|
+
*/
|
|
2129
|
+
const WINDOW = 3e5;
|
|
2130
|
+
/**
|
|
2131
|
+
* Returns the milliseconds since the epoch of an instant, or undefined for none or one `Date`
|
|
2132
|
+
* cannot read.
|
|
2133
|
+
*/
|
|
2134
|
+
function instantOf(sentAt) {
|
|
2135
|
+
const time = sentAt === void 0 ? NaN : new Date(sentAt).getTime();
|
|
2136
|
+
return Number.isNaN(time) ? void 0 : time;
|
|
2137
|
+
}
|
|
2138
|
+
/**
|
|
2139
|
+
* Returns the wall-clock time an instant reads in a zone, as milliseconds of a UTC clock.
|
|
2140
|
+
*/
|
|
2141
|
+
function wallOf(time, timeZone) {
|
|
2142
|
+
const parts = new Intl.DateTimeFormat("en-US", {
|
|
2143
|
+
day: "numeric",
|
|
2144
|
+
hour: "numeric",
|
|
2145
|
+
hourCycle: "h23",
|
|
2146
|
+
minute: "numeric",
|
|
2147
|
+
month: "numeric",
|
|
2148
|
+
second: "numeric",
|
|
2149
|
+
timeZone,
|
|
2150
|
+
year: "numeric"
|
|
2151
|
+
}).formatToParts(time).map((part) => [part.type, Number(part.value)]);
|
|
2152
|
+
const fields = Object.fromEntries(parts);
|
|
2153
|
+
return Date.UTC(fields.year, fields.month - 1, fields.day, fields.hour, fields.minute, fields.second);
|
|
2154
|
+
}
|
|
2155
|
+
/**
|
|
2156
|
+
* Returns the first instant of the instant's day in a zone: midnight, or the first instant after
|
|
2157
|
+
* it where a daylight saving change skips midnight.
|
|
2158
|
+
*
|
|
2159
|
+
* @remarks
|
|
2160
|
+
* The first estimate applies the zone's offset at midnight read as UTC, and the second the offset
|
|
2161
|
+
* at the first estimate. Where a change skips midnight, as in Havana in March, the second falls
|
|
2162
|
+
* on the evening before, and the first is the day's start.
|
|
2163
|
+
*/
|
|
2164
|
+
function zonedMidnightOf(time, timeZone) {
|
|
2165
|
+
const wall = wallOf(time, timeZone);
|
|
2166
|
+
const day = wall - wall % 864e5;
|
|
2167
|
+
const early = day - (wallOf(day, timeZone) - day);
|
|
2168
|
+
const late = day - (wallOf(early, timeZone) - early);
|
|
2169
|
+
return wallOf(late, timeZone) >= day ? late : early;
|
|
2170
|
+
}
|
|
2171
|
+
/**
|
|
2172
|
+
* Returns midnight at the start of the instant's day, in the zone stated or the runtime's.
|
|
2173
|
+
*/
|
|
2174
|
+
function midnightOf(time, timeZone) {
|
|
2175
|
+
if (timeZone !== void 0) return zonedMidnightOf(time, timeZone);
|
|
2176
|
+
const date = new Date(time);
|
|
2177
|
+
return new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime();
|
|
2178
|
+
}
|
|
2179
|
+
/**
|
|
2180
|
+
* Returns the last day while the message belongs to it, and otherwise opens a day for the message.
|
|
2181
|
+
*
|
|
2182
|
+
* @param days - The days so far, which a new day joins.
|
|
2183
|
+
* @param day - Midnight at the start of the message's day.
|
|
2184
|
+
* @param same - Whether the message's day is the last day's.
|
|
2185
|
+
* @param id - Identifier of the message, which keys a new day.
|
|
2186
|
+
* @returns The day the message joins.
|
|
2187
|
+
*/
|
|
2188
|
+
function dayFor(days, day, same, id) {
|
|
2189
|
+
const last = days.at(-1);
|
|
2190
|
+
if (last !== void 0 && same) return last;
|
|
2191
|
+
const opened = {
|
|
2192
|
+
day: day === void 0 ? void 0 : new Date(day),
|
|
2193
|
+
key: id,
|
|
2194
|
+
turns: []
|
|
2195
|
+
};
|
|
2196
|
+
days.push(opened);
|
|
2197
|
+
return opened;
|
|
2198
|
+
}
|
|
2199
|
+
/**
|
|
2200
|
+
* Adds a message to the last turn of its day while the author and the gap allow, and otherwise
|
|
2201
|
+
* opens a turn with it.
|
|
2202
|
+
*
|
|
2203
|
+
* @param turns - The turns of the message's day.
|
|
2204
|
+
* @param message - The next message of the transcript.
|
|
2205
|
+
* @param near - Whether the gap since the message before is inside the window.
|
|
2206
|
+
* @param self - Identifier of the reader.
|
|
2207
|
+
*/
|
|
2208
|
+
function join(turns, message, near, self) {
|
|
2209
|
+
const turn = turns.at(-1);
|
|
2210
|
+
if (turn?.author === message.author && near) {
|
|
2211
|
+
turn.messages.push(message);
|
|
2212
|
+
turn.last = message;
|
|
2213
|
+
return;
|
|
2214
|
+
}
|
|
2215
|
+
turns.push({
|
|
2216
|
+
author: message.author,
|
|
2217
|
+
first: message,
|
|
2218
|
+
last: message,
|
|
2219
|
+
messages: [message],
|
|
2220
|
+
own: message.author === self
|
|
2221
|
+
});
|
|
2222
|
+
}
|
|
2223
|
+
/**
|
|
2224
|
+
* Groups messages into days and turns.
|
|
2225
|
+
*
|
|
2226
|
+
* @typeParam Message - The caller's message type, which each turn returns as it was given.
|
|
2227
|
+
* @param messages - The transcript, oldest first.
|
|
2228
|
+
* @param options - The reader's identifier, the zone a day starts in and the longest gap inside
|
|
2229
|
+
* one turn.
|
|
2230
|
+
* @returns The days in order, each with its turns in order.
|
|
2231
|
+
*/
|
|
2232
|
+
function groupTurns(messages, options = {}) {
|
|
2233
|
+
const { self, timeZone, window = WINDOW } = options;
|
|
2234
|
+
const days = [];
|
|
2235
|
+
let midnight;
|
|
2236
|
+
let previous;
|
|
2237
|
+
for (const message of messages) {
|
|
2238
|
+
const time = instantOf(message.sentAt);
|
|
2239
|
+
const day = time === void 0 ? midnight : midnightOf(time, timeZone);
|
|
2240
|
+
const near = previous === void 0 || time === void 0 || time - previous <= window;
|
|
2241
|
+
join(dayFor(days, day, day === midnight, message.id).turns, message, near, self);
|
|
2242
|
+
midnight = day;
|
|
2243
|
+
previous = time ?? previous;
|
|
2244
|
+
}
|
|
2245
|
+
return days;
|
|
2246
|
+
}
|
|
2247
|
+
//#endregion
|
|
2248
|
+
export { attachment_exports as A, Toolbar as C, Context as D, Input as E, Description as F, Content$2 as I, Actions$1 as L, Root$4 as M, Media as N, Attachments as O, Group as P, composer_exports as S, Root$3 as T, Typing as _, Item as a, JumpTrigger as b, Status as c, Footer as d, Content as f, conversation_exports as g, Actions as h, Picker as i, Title as j, AttachTrigger as k, Root$1 as l, Avatar as m, reactions_exports as n, Choice as o, Bubble as p, Root as r, message_exports as s, groupTurns as t, Header as u, Root$2 as v, Submit as w, Content$1 as x, useConversation as y };
|