@anvia/react-ui 0.3.0 → 0.4.0

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.
Files changed (38) hide show
  1. package/README.md +14 -1
  2. package/dist/attachment/index.js +2 -2
  3. package/dist/chat/index.d.ts +29 -4
  4. package/dist/chat/index.js +4 -4
  5. package/dist/{chunk-QHP4GSRG.js → chunk-32QFNRXR.js} +2 -2
  6. package/dist/{chunk-MXYHYD3O.js → chunk-657E5BPN.js} +2 -2
  7. package/dist/{chunk-5IHC7D4U.js → chunk-BCCU3VT5.js} +1 -1
  8. package/dist/chunk-BCCU3VT5.js.map +1 -0
  9. package/dist/{chunk-5Z2IWZ5O.js → chunk-FZANAMWH.js} +3 -3
  10. package/dist/{chunk-OKTDYMSA.js → chunk-GXB4VINL.js} +2 -2
  11. package/dist/{chunk-YDMDLFU4.js → chunk-JUNV3MUR.js} +2 -2
  12. package/dist/{chunk-LRAQW3ZK.js → chunk-KO36TK2O.js} +669 -47
  13. package/dist/chunk-KO36TK2O.js.map +1 -0
  14. package/dist/{chunk-IHRCKGRS.js → chunk-SE7I6ZXJ.js} +2 -2
  15. package/dist/{chunk-4Z7LULAM.js → chunk-TOBAQM2G.js} +2 -2
  16. package/dist/completion/index.js +2 -2
  17. package/dist/human-input/index.js +2 -2
  18. package/dist/image/index.js +2 -2
  19. package/dist/index.d.ts +1 -1
  20. package/dist/index.js +9 -9
  21. package/dist/message/index.js +3 -3
  22. package/dist/selection-toolbar/index.js +2 -2
  23. package/dist/shared.d.ts +1 -1
  24. package/dist/shared.js +1 -1
  25. package/dist/styles.css +20 -0
  26. package/dist/thread-C2-Xo08o.d.ts +102 -0
  27. package/dist/thread-list/index.js +2 -2
  28. package/package.json +11 -1
  29. package/dist/chunk-5IHC7D4U.js.map +0 -1
  30. package/dist/chunk-LRAQW3ZK.js.map +0 -1
  31. package/dist/thread-DYPvlsJa.d.ts +0 -38
  32. /package/dist/{chunk-QHP4GSRG.js.map → chunk-32QFNRXR.js.map} +0 -0
  33. /package/dist/{chunk-MXYHYD3O.js.map → chunk-657E5BPN.js.map} +0 -0
  34. /package/dist/{chunk-5Z2IWZ5O.js.map → chunk-FZANAMWH.js.map} +0 -0
  35. /package/dist/{chunk-OKTDYMSA.js.map → chunk-GXB4VINL.js.map} +0 -0
  36. /package/dist/{chunk-YDMDLFU4.js.map → chunk-JUNV3MUR.js.map} +0 -0
  37. /package/dist/{chunk-IHRCKGRS.js.map → chunk-SE7I6ZXJ.js.map} +0 -0
  38. /package/dist/{chunk-4Z7LULAM.js.map → chunk-TOBAQM2G.js.map} +0 -0
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  Message
3
- } from "./chunk-5Z2IWZ5O.js";
3
+ } from "./chunk-FZANAMWH.js";
4
4
  import {
5
5
  Attachment
6
- } from "./chunk-QHP4GSRG.js";
6
+ } from "./chunk-32QFNRXR.js";
7
7
  import {
8
8
  composeRefs,
9
9
  renderPrimitive
@@ -16,9 +16,18 @@ import {
16
16
  useChatContext,
17
17
  useComposer,
18
18
  useThread
19
- } from "./chunk-5IHC7D4U.js";
19
+ } from "./chunk-BCCU3VT5.js";
20
20
 
21
21
  // src/chat/composer.tsx
22
+ import { mergeAttributes } from "@tiptap/core";
23
+ import Document from "@tiptap/extension-document";
24
+ import HardBreak from "@tiptap/extension-hard-break";
25
+ import Mention from "@tiptap/extension-mention";
26
+ import Paragraph from "@tiptap/extension-paragraph";
27
+ import Placeholder from "@tiptap/extension-placeholder";
28
+ import Text from "@tiptap/extension-text";
29
+ import { PluginKey } from "@tiptap/pm/state";
30
+ import { EditorContent, useEditor } from "@tiptap/react";
22
31
  import {
23
32
  forwardRef,
24
33
  useCallback,
@@ -31,14 +40,18 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
31
40
  var ComposerRoot = forwardRef(function ComposerRoot2({
32
41
  attachments: attachmentsProp,
33
42
  defaultAttachments,
43
+ defaultEntities,
34
44
  defaultInput = "",
35
45
  defaultQuote,
46
+ entities: entitiesProp,
36
47
  input: inputProp,
37
48
  onAttachmentsChange,
49
+ onEntitiesChange,
38
50
  onInputChange,
39
51
  onQuoteChange,
40
52
  quote: quoteProp,
41
53
  submitMessage,
54
+ triggers: triggersProp,
42
55
  onSubmit,
43
56
  ...props
44
57
  }, ref) {
@@ -47,11 +60,17 @@ var ComposerRoot = forwardRef(function ComposerRoot2({
47
60
  const [uncontrolledAttachments, setUncontrolledAttachments] = useState(() => [
48
61
  ...defaultAttachments ?? []
49
62
  ]);
63
+ const [uncontrolledEntities, setUncontrolledEntities] = useState(() => [
64
+ ...defaultEntities ?? []
65
+ ]);
50
66
  const [uncontrolledQuote, setUncontrolledQuote] = useState(
51
67
  defaultQuote
52
68
  );
69
+ const [activeTrigger, setActiveTriggerState] = useState();
53
70
  const input = inputProp ?? uncontrolledInput;
54
71
  const attachments = attachmentsProp ?? uncontrolledAttachments;
72
+ const entities = entitiesProp ?? uncontrolledEntities;
73
+ const triggers = triggersProp ?? EMPTY_COMPOSER_TRIGGERS;
55
74
  const rawQuote = quoteProp ?? uncontrolledQuote;
56
75
  const rawQuoteMessageId = rawQuote?.messageId;
57
76
  const rawQuoteText = rawQuote?.text;
@@ -61,10 +80,13 @@ var ComposerRoot = forwardRef(function ComposerRoot2({
61
80
  );
62
81
  const attachmentsRef = useRef(attachments);
63
82
  attachmentsRef.current = attachments;
83
+ const entitiesRef = useRef(entities);
84
+ entitiesRef.current = entities;
64
85
  const inputControlled = inputProp !== void 0;
65
86
  const attachmentsControlled = attachmentsProp !== void 0;
87
+ const entitiesControlled = entitiesProp !== void 0;
66
88
  const quoteControlled = quoteProp !== void 0;
67
- const hasMessageContent = input.trim().length > 0 || attachments.length > 0 || quote !== void 0;
89
+ const hasMessageContent = input.trim().length > 0 || attachments.length > 0 || entities.length > 0 || quote !== void 0;
68
90
  const canSubmit = hasMessageContent && chat.status !== "streaming";
69
91
  const canStop = chat.status === "streaming";
70
92
  const setInput = useCallback(
@@ -87,6 +109,17 @@ var ComposerRoot = forwardRef(function ComposerRoot2({
87
109
  },
88
110
  [attachmentsControlled, onAttachmentsChange]
89
111
  );
112
+ const setEntities = useCallback(
113
+ (update) => {
114
+ const nextEntities = typeof update === "function" ? update(entitiesRef.current) : update;
115
+ entitiesRef.current = nextEntities;
116
+ if (!entitiesControlled) {
117
+ setUncontrolledEntities(nextEntities);
118
+ }
119
+ onEntitiesChange?.(nextEntities);
120
+ },
121
+ [entitiesControlled, onEntitiesChange]
122
+ );
90
123
  const setQuote = useCallback(
91
124
  (nextQuote) => {
92
125
  const normalizedQuote = normalizeQuote(nextQuote);
@@ -119,17 +152,20 @@ var ComposerRoot = forwardRef(function ComposerRoot2({
119
152
  const clear = useCallback(() => {
120
153
  setInput("");
121
154
  setAttachments([]);
155
+ setEntities([]);
156
+ setActiveTriggerState(void 0);
122
157
  clearQuote();
123
- }, [clearQuote, setAttachments, setInput]);
158
+ }, [clearQuote, setAttachments, setEntities, setInput]);
124
159
  const submit = useCallback(async () => {
125
160
  const prompt = input;
126
- if (prompt.trim().length === 0 && attachments.length === 0 && quote === void 0 || chat.status === "streaming") {
161
+ if (prompt.trim().length === 0 && attachments.length === 0 && entities.length === 0 && quote === void 0 || chat.status === "streaming") {
127
162
  return;
128
163
  }
129
164
  if (submitMessage !== void 0) {
130
165
  await submitMessage({
131
166
  input: prompt,
132
167
  attachments,
168
+ entities,
133
169
  chat,
134
170
  quote,
135
171
  clear
@@ -137,24 +173,28 @@ var ComposerRoot = forwardRef(function ComposerRoot2({
137
173
  return;
138
174
  }
139
175
  const submittedText = quote === void 0 ? prompt : promptWithQuote(prompt, quote);
176
+ const metadata = composerSubmitMetadata(quote, entities);
140
177
  if (attachments.length === 0) {
141
- if (quote === void 0) {
178
+ if (metadata === void 0) {
142
179
  await chat.sendMessage(prompt);
143
180
  } else {
144
181
  await chat.sendMessage({
145
182
  text: submittedText,
146
- metadata: { quote }
183
+ metadata
147
184
  });
148
185
  }
149
186
  } else {
150
187
  await chat.sendMessage({
151
188
  text: submittedText,
152
189
  attachments,
153
- ...quote === void 0 ? {} : { metadata: { quote } }
190
+ ...metadata === void 0 ? {} : { metadata }
154
191
  });
155
192
  }
156
193
  clear();
157
- }, [attachments, chat, clear, input, quote, submitMessage]);
194
+ }, [attachments, chat, clear, entities, input, quote, submitMessage]);
195
+ const setActiveTrigger = useCallback((update) => {
196
+ setActiveTriggerState(update);
197
+ }, []);
158
198
  const value = useMemo(
159
199
  () => ({
160
200
  input,
@@ -164,9 +204,14 @@ var ComposerRoot = forwardRef(function ComposerRoot2({
164
204
  addAttachment,
165
205
  removeAttachment,
166
206
  clearAttachments,
207
+ entities,
208
+ setEntities,
167
209
  quote,
168
210
  setQuote,
169
211
  clearQuote,
212
+ triggers,
213
+ activeTrigger,
214
+ setActiveTrigger,
170
215
  submit,
171
216
  stop: chat.stop,
172
217
  status: chat.status,
@@ -176,19 +221,24 @@ var ComposerRoot = forwardRef(function ComposerRoot2({
176
221
  [
177
222
  addAttachment,
178
223
  attachments,
224
+ activeTrigger,
179
225
  canStop,
180
226
  canSubmit,
181
227
  chat.status,
182
228
  chat.stop,
183
229
  clearAttachments,
184
230
  clearQuote,
231
+ entities,
185
232
  input,
186
233
  quote,
187
234
  removeAttachment,
188
235
  setAttachments,
236
+ setActiveTrigger,
237
+ setEntities,
189
238
  setInput,
190
239
  setQuote,
191
- submit
240
+ submit,
241
+ triggers
192
242
  ]
193
243
  );
194
244
  const handleSubmit = useCallback(
@@ -264,42 +314,71 @@ var ComposerClearQuote = forwardRef(
264
314
  );
265
315
  var ComposerInput = forwardRef(function ComposerInput2({
266
316
  autoResize = true,
317
+ disabled,
267
318
  maxRows: maxRowsProp,
268
319
  minRows: minRowsProp,
269
- onChange,
270
320
  onKeyDown,
271
- rows,
321
+ placeholder,
322
+ style,
272
323
  ...props
273
324
  }, ref) {
274
325
  const composer = useComposer();
275
- const inputRef = useRef(null);
276
- const wasAutoResizeRef = useRef(autoResize);
277
- const composedInputRef = useMemo(() => composeRefs(inputRef, ref), [ref]);
278
- const minRows = normalizeRows(minRowsProp ?? rows ?? 1, 1);
326
+ const composerRef = useRef(composer);
327
+ composerRef.current = composer;
328
+ const editorReadyRef = useRef(false);
329
+ const syncingFromEditorRef = useRef(false);
330
+ const triggers = composer.triggers;
331
+ const minRows = normalizeRows(minRowsProp ?? 1, 1);
279
332
  const maxRows = normalizeRows(maxRowsProp ?? 6, minRows);
280
- useEffect(() => {
281
- const wasAutoResize = wasAutoResizeRef.current;
282
- wasAutoResizeRef.current = autoResize;
283
- if (!autoResize) {
284
- if (wasAutoResize) {
285
- resetComposerInputSize(inputRef.current);
286
- }
287
- return;
288
- }
289
- resizeComposerInput(inputRef.current, { maxRows, minRows });
290
- });
291
- const handleChange = useCallback(
292
- (event) => {
293
- onChange?.(event);
294
- if (!event.defaultPrevented) {
295
- composer.setInput(event.currentTarget.value);
296
- if (autoResize) {
297
- resizeComposerInput(event.currentTarget, { maxRows, minRows });
333
+ const extensions = useMemo(
334
+ () => composerInputExtensions({
335
+ composerRef,
336
+ placeholder,
337
+ triggers
338
+ }),
339
+ [placeholder, triggers]
340
+ );
341
+ const editor = useEditor(
342
+ {
343
+ extensions,
344
+ content: plainTextToComposerContent(composer.input),
345
+ editable: composer.status !== "streaming",
346
+ editorProps: {
347
+ attributes: {
348
+ "aria-label": props["aria-label"] ?? "Message",
349
+ "aria-multiline": "true",
350
+ "data-anvia-composer-editor": "",
351
+ role: "textbox"
352
+ }
353
+ },
354
+ onUpdate: ({ editor: updatedEditor }) => {
355
+ if (!editorReadyRef.current) {
356
+ return;
298
357
  }
358
+ const snapshot = composerSnapshotFromEditor(
359
+ updatedEditor,
360
+ composerRef.current.entities,
361
+ composerRef.current.triggers
362
+ );
363
+ syncingFromEditorRef.current = true;
364
+ composerRef.current.setInput(snapshot.input);
365
+ composerRef.current.setEntities(snapshot.entities);
299
366
  }
300
367
  },
301
- [autoResize, composer, maxRows, minRows, onChange]
368
+ [extensions]
302
369
  );
370
+ const ariaDisabled = props["aria-disabled"] === true || props["aria-disabled"] === "true";
371
+ const composerDisabled = disabled === true || ariaDisabled || composer.status === "streaming";
372
+ const inputStyle = useMemo(() => {
373
+ if (!autoResize) {
374
+ return style;
375
+ }
376
+ const rowsStyle = {
377
+ ["--anvia-composer-min-rows"]: minRows,
378
+ ["--anvia-composer-max-rows"]: maxRows
379
+ };
380
+ return style === void 0 ? rowsStyle : { ...rowsStyle, ...style };
381
+ }, [autoResize, maxRows, minRows, style]);
303
382
  const handleKeyDown = useCallback(
304
383
  (event) => {
305
384
  onKeyDown?.(event);
@@ -307,25 +386,474 @@ var ComposerInput = forwardRef(function ComposerInput2({
307
386
  return;
308
387
  }
309
388
  event.preventDefault();
310
- void composer.submit();
389
+ void composerRef.current.submit();
311
390
  },
312
- [composer, onKeyDown]
391
+ [onKeyDown]
313
392
  );
393
+ useEffect(() => {
394
+ if (editor === null) {
395
+ return;
396
+ }
397
+ editor.setEditable(!composerDisabled, false);
398
+ }, [composerDisabled, editor]);
399
+ useEffect(() => {
400
+ if (editor === null) {
401
+ return;
402
+ }
403
+ const handleKeyDown2 = (event) => {
404
+ if (submitComposerFromEditorKeyDown(event, composerRef)) {
405
+ event.stopPropagation();
406
+ }
407
+ };
408
+ editor.view.dom.addEventListener("keydown", handleKeyDown2, true);
409
+ return () => {
410
+ editor.view.dom.removeEventListener("keydown", handleKeyDown2, true);
411
+ };
412
+ }, [editor]);
413
+ useEffect(() => {
414
+ if (editor === null) {
415
+ return;
416
+ }
417
+ editor.commands.setContent(plainTextToComposerContent(composerRef.current.input), {
418
+ emitUpdate: false
419
+ });
420
+ editorReadyRef.current = true;
421
+ return () => {
422
+ editorReadyRef.current = false;
423
+ };
424
+ }, [editor]);
425
+ useEffect(() => {
426
+ if (editor === null || !editorReadyRef.current) {
427
+ return;
428
+ }
429
+ if (syncingFromEditorRef.current) {
430
+ syncingFromEditorRef.current = false;
431
+ return;
432
+ }
433
+ const snapshot = composerSnapshotFromEditor(editor, composer.entities, composer.triggers);
434
+ if (snapshot.input === composer.input) {
435
+ return;
436
+ }
437
+ editor.commands.setContent(plainTextToComposerContent(composer.input), {
438
+ emitUpdate: false
439
+ });
440
+ }, [composer, editor]);
314
441
  return renderPrimitive(
315
- "textarea",
442
+ "div",
316
443
  {
317
444
  ...props,
318
- "aria-label": props["aria-label"] ?? "Message",
319
- disabled: props.disabled ?? composer.status === "streaming",
320
- onChange: handleChange,
445
+ children: /* @__PURE__ */ jsx(EditorContent, { editor }),
446
+ "aria-disabled": composerDisabled ? true : props["aria-disabled"],
447
+ "data-anvia-composer-input": "",
448
+ "data-state": composerDisabled ? "disabled" : "enabled",
449
+ "data-auto-resize": autoResize ? "" : void 0,
321
450
  onKeyDown: handleKeyDown,
322
- rows: autoResize ? minRows : rows,
323
- value: composer.input,
324
- "data-anvia-composer-input": ""
451
+ style: inputStyle
325
452
  },
326
- composedInputRef
453
+ ref
327
454
  );
328
455
  });
456
+ var ComposerTextareaInput = forwardRef(
457
+ function ComposerTextareaInput2({
458
+ autoResize = true,
459
+ maxRows: maxRowsProp,
460
+ minRows: minRowsProp,
461
+ onChange,
462
+ onKeyDown,
463
+ rows,
464
+ ...props
465
+ }, ref) {
466
+ const composer = useComposer();
467
+ const inputRef = useRef(null);
468
+ const wasAutoResizeRef = useRef(autoResize);
469
+ const composedInputRef = useMemo(() => composeRefs(inputRef, ref), [ref]);
470
+ const minRows = normalizeRows(minRowsProp ?? rows ?? 1, 1);
471
+ const maxRows = normalizeRows(maxRowsProp ?? 6, minRows);
472
+ useEffect(() => {
473
+ const wasAutoResize = wasAutoResizeRef.current;
474
+ wasAutoResizeRef.current = autoResize;
475
+ if (!autoResize) {
476
+ if (wasAutoResize) {
477
+ resetComposerInputSize(inputRef.current);
478
+ }
479
+ return;
480
+ }
481
+ resizeComposerInput(inputRef.current, { maxRows, minRows });
482
+ });
483
+ const handleChange = useCallback(
484
+ (event) => {
485
+ onChange?.(event);
486
+ if (!event.defaultPrevented) {
487
+ composer.setInput(event.currentTarget.value);
488
+ composer.setEntities([]);
489
+ if (autoResize) {
490
+ resizeComposerInput(event.currentTarget, { maxRows, minRows });
491
+ }
492
+ }
493
+ },
494
+ [autoResize, composer, maxRows, minRows, onChange]
495
+ );
496
+ const handleKeyDown = useCallback(
497
+ (event) => {
498
+ onKeyDown?.(event);
499
+ if (event.defaultPrevented || event.key !== "Enter" || event.shiftKey || event.nativeEvent.isComposing) {
500
+ return;
501
+ }
502
+ event.preventDefault();
503
+ void composer.submit();
504
+ },
505
+ [composer, onKeyDown]
506
+ );
507
+ return renderPrimitive(
508
+ "textarea",
509
+ {
510
+ ...props,
511
+ "aria-label": props["aria-label"] ?? "Message",
512
+ disabled: props.disabled ?? composer.status === "streaming",
513
+ onChange: handleChange,
514
+ onKeyDown: handleKeyDown,
515
+ rows: autoResize ? minRows : rows,
516
+ value: composer.input,
517
+ "data-anvia-composer-input": "",
518
+ "data-anvia-composer-textarea-input": ""
519
+ },
520
+ composedInputRef
521
+ );
522
+ }
523
+ );
524
+ var COMPOSER_ENTITY_NODE = "composerEntity";
525
+ var EMPTY_COMPOSER_TRIGGERS = [];
526
+ var ComposerEntityExtension = Mention.extend({
527
+ name: COMPOSER_ENTITY_NODE,
528
+ addAttributes() {
529
+ return {
530
+ id: {
531
+ default: null,
532
+ parseHTML: (element) => element.getAttribute("data-id"),
533
+ renderHTML: (attributes) => attributes.id === void 0 || attributes.id === null ? {} : { "data-id": String(attributes.id) }
534
+ },
535
+ label: {
536
+ default: null,
537
+ parseHTML: (element) => element.getAttribute("data-label"),
538
+ renderHTML: (attributes) => attributes.label === void 0 || attributes.label === null ? {} : { "data-label": String(attributes.label) }
539
+ },
540
+ mentionSuggestionChar: {
541
+ default: "@",
542
+ parseHTML: (element) => element.getAttribute("data-mention-suggestion-char"),
543
+ renderHTML: (attributes) => ({
544
+ "data-mention-suggestion-char": attributes.mentionSuggestionChar
545
+ })
546
+ },
547
+ triggerId: {
548
+ default: null,
549
+ parseHTML: (element) => element.getAttribute("data-trigger-id"),
550
+ renderHTML: (attributes) => attributes.triggerId === void 0 || attributes.triggerId === null ? {} : { "data-trigger-id": String(attributes.triggerId) }
551
+ },
552
+ text: {
553
+ default: null,
554
+ parseHTML: (element) => element.getAttribute("data-text"),
555
+ renderHTML: (attributes) => attributes.text === void 0 || attributes.text === null ? {} : { "data-text": String(attributes.text) }
556
+ },
557
+ data: {
558
+ default: null,
559
+ parseHTML: (element) => dataFromAttribute(element.getAttribute("data-entity-data")),
560
+ renderHTML: (attributes) => attributes.data === void 0 || attributes.data === null ? {} : { "data-entity-data": JSON.stringify(attributes.data) }
561
+ }
562
+ };
563
+ }
564
+ });
565
+ function composerInputExtensions({
566
+ composerRef,
567
+ placeholder,
568
+ triggers
569
+ }) {
570
+ return [
571
+ Document,
572
+ Paragraph,
573
+ Text,
574
+ HardBreak,
575
+ Placeholder.configure(placeholder === void 0 ? {} : { placeholder }),
576
+ ComposerEntityExtension.configure({
577
+ HTMLAttributes: {
578
+ "data-anvia-composer-entity": ""
579
+ },
580
+ deleteTriggerWithBackspace: true,
581
+ renderText: ({ node }) => composerEntityText(node.attrs),
582
+ renderHTML: ({ node, options }) => [
583
+ "span",
584
+ mergeAttributes(options.HTMLAttributes, {
585
+ "data-anvia-composer-entity": ""
586
+ }),
587
+ composerEntityText(node.attrs)
588
+ ],
589
+ suggestions: triggers.map((trigger) => composerSuggestion(trigger, composerRef))
590
+ })
591
+ ];
592
+ }
593
+ function submitComposerFromEditorKeyDown(event, composerRef) {
594
+ if (event.defaultPrevented || event.key !== "Enter" || event.shiftKey || event.isComposing || composerRef.current.activeTrigger !== void 0) {
595
+ return false;
596
+ }
597
+ event.preventDefault();
598
+ void composerRef.current.submit();
599
+ return true;
600
+ }
601
+ function composerSuggestion(trigger, composerRef) {
602
+ const pluginKey = new PluginKey(`anvia-composer-trigger-${trigger.id}`);
603
+ return {
604
+ pluginKey,
605
+ char: trigger.char,
606
+ ...trigger.allowSpaces === void 0 ? {} : { allowSpaces: trigger.allowSpaces },
607
+ ...trigger.allowedPrefixes === void 0 ? {} : { allowedPrefixes: trigger.allowedPrefixes },
608
+ ...trigger.startOfLine === void 0 ? {} : { startOfLine: trigger.startOfLine },
609
+ ...trigger.minQueryLength === void 0 ? {} : { minQueryLength: trigger.minQueryLength },
610
+ items: ({ query, signal }) => resolveTriggerItems(trigger, query, composerRef.current, signal),
611
+ command: ({
612
+ editor,
613
+ range,
614
+ props
615
+ }) => {
616
+ const nodeAfter = editor.view.state.selection.$to.nodeAfter;
617
+ const overrideSpace = nodeAfter?.text?.startsWith(" ");
618
+ const insertRange = {
619
+ from: range.from,
620
+ to: overrideSpace ? range.to + 1 : range.to
621
+ };
622
+ editor.chain().focus().insertContentAt(insertRange, [
623
+ {
624
+ type: COMPOSER_ENTITY_NODE,
625
+ attrs: props
626
+ },
627
+ {
628
+ type: "text",
629
+ text: " "
630
+ }
631
+ ]).run();
632
+ editor.view.dom.ownerDocument.defaultView?.getSelection()?.collapseToEnd();
633
+ composerRef.current.setActiveTrigger(void 0);
634
+ },
635
+ render: () => ({
636
+ onStart: (props) => {
637
+ updateComposerTriggerState(trigger, props, composerRef);
638
+ },
639
+ onUpdate: (props) => {
640
+ updateComposerTriggerState(trigger, props, composerRef);
641
+ },
642
+ onExit: () => {
643
+ composerRef.current.setActiveTrigger(
644
+ (current) => current?.trigger.id === trigger.id ? void 0 : current
645
+ );
646
+ },
647
+ onKeyDown: (props) => handleComposerTriggerKeyDown(trigger, props, composerRef)
648
+ })
649
+ };
650
+ }
651
+ function resolveTriggerItems(trigger, query, composer, signal) {
652
+ if (Array.isArray(trigger.items)) {
653
+ const normalizedQuery = query.toLowerCase();
654
+ return trigger.items.filter((item) => item.label.toLowerCase().includes(normalizedQuery));
655
+ }
656
+ return trigger.items({
657
+ trigger,
658
+ query,
659
+ input: composer.input,
660
+ entities: composer.entities,
661
+ signal
662
+ });
663
+ }
664
+ function updateComposerTriggerState(trigger, props, composerRef) {
665
+ const current = composerRef.current.activeTrigger;
666
+ const selectedIndex = current?.trigger.id === trigger.id ? clampIndex(current.selectedIndex, props.items.length) : 0;
667
+ const rect = rectFromSuggestion(props);
668
+ composerRef.current.setActiveTrigger({
669
+ trigger,
670
+ query: props.query,
671
+ items: props.items,
672
+ loading: props.loading,
673
+ selectedIndex,
674
+ ...rect === void 0 ? {} : { rect },
675
+ selectItem: (item) => {
676
+ if (item.disabled) {
677
+ return;
678
+ }
679
+ props.command(triggerItemToEntityAttrs(trigger, item));
680
+ },
681
+ setSelectedIndex: (index) => {
682
+ composerRef.current.setActiveTrigger(
683
+ (active) => active?.trigger.id === trigger.id ? { ...active, selectedIndex: clampIndex(index, active.items.length) } : active
684
+ );
685
+ }
686
+ });
687
+ }
688
+ function handleComposerTriggerKeyDown(trigger, { event }, composerRef) {
689
+ const activeTrigger = composerRef.current.activeTrigger;
690
+ if (activeTrigger?.trigger.id !== trigger.id) {
691
+ return false;
692
+ }
693
+ if (event.key === "ArrowDown") {
694
+ event.preventDefault();
695
+ activeTrigger.setSelectedIndex(activeTrigger.selectedIndex + 1);
696
+ return true;
697
+ }
698
+ if (event.key === "ArrowUp") {
699
+ event.preventDefault();
700
+ activeTrigger.setSelectedIndex(activeTrigger.selectedIndex - 1);
701
+ return true;
702
+ }
703
+ if (event.key === "Enter" || event.key === "Tab") {
704
+ event.preventDefault();
705
+ const item = activeTrigger.items[activeTrigger.selectedIndex];
706
+ if (item !== void 0) {
707
+ activeTrigger.selectItem(item);
708
+ }
709
+ return true;
710
+ }
711
+ if (event.key === "Escape") {
712
+ composerRef.current.setActiveTrigger(void 0);
713
+ return false;
714
+ }
715
+ return false;
716
+ }
717
+ function triggerItemToEntityAttrs(trigger, item) {
718
+ return {
719
+ id: item.id,
720
+ label: item.label,
721
+ mentionSuggestionChar: trigger.char,
722
+ triggerId: trigger.id,
723
+ text: triggerItemText(trigger, item),
724
+ data: item.data
725
+ };
726
+ }
727
+ function triggerItemText(trigger, item) {
728
+ return item.text ?? `${trigger.char}${item.label}`;
729
+ }
730
+ function rectFromSuggestion(props) {
731
+ const rect = props.clientRect?.();
732
+ if (rect === void 0 || rect === null) {
733
+ return void 0;
734
+ }
735
+ return {
736
+ top: rect.top,
737
+ right: rect.right,
738
+ bottom: rect.bottom,
739
+ left: rect.left,
740
+ width: rect.width,
741
+ height: rect.height
742
+ };
743
+ }
744
+ function clampIndex(index, length) {
745
+ if (length <= 0) {
746
+ return 0;
747
+ }
748
+ return (index + length) % length;
749
+ }
750
+ function plainTextToComposerContent(input) {
751
+ const lines = input.length === 0 ? [""] : input.split(/\r?\n/);
752
+ return {
753
+ type: "doc",
754
+ content: lines.map((line) => ({
755
+ type: "paragraph",
756
+ ...line.length === 0 ? {} : {
757
+ content: [
758
+ {
759
+ type: "text",
760
+ text: line
761
+ }
762
+ ]
763
+ }
764
+ }))
765
+ };
766
+ }
767
+ function composerSnapshotFromEditor(editor, previousEntities, triggers) {
768
+ return composerSnapshotFromContent(editor.getJSON(), previousEntities, triggers);
769
+ }
770
+ function composerSnapshotFromContent(content, previousEntities, triggers) {
771
+ const entities = [];
772
+ let input = "";
773
+ let blockIndex = 0;
774
+ const appendNode = (node) => {
775
+ if (node.type === "doc") {
776
+ for (const child of node.content ?? []) {
777
+ appendNode(child);
778
+ }
779
+ return;
780
+ }
781
+ if (node.type === "paragraph") {
782
+ if (blockIndex > 0) {
783
+ input += "\n";
784
+ }
785
+ blockIndex += 1;
786
+ for (const child of node.content ?? []) {
787
+ appendNode(child);
788
+ }
789
+ return;
790
+ }
791
+ if (node.type === "text") {
792
+ input += node.text ?? "";
793
+ return;
794
+ }
795
+ if (node.type === "hardBreak") {
796
+ input += "\n";
797
+ return;
798
+ }
799
+ if (node.type === COMPOSER_ENTITY_NODE) {
800
+ const attrs = node.attrs;
801
+ const entity = composerEntityFromAttrs(attrs, input.length, previousEntities, triggers);
802
+ input += entity.text;
803
+ entities.push(entity);
804
+ return;
805
+ }
806
+ for (const child of node.content ?? []) {
807
+ appendNode(child);
808
+ }
809
+ };
810
+ appendNode(content);
811
+ return { input, entities };
812
+ }
813
+ function composerEntityFromAttrs(attrs, from, previousEntities, triggers) {
814
+ const id = stringAttr(attrs?.id) ?? "";
815
+ const trigger = stringAttr(attrs?.mentionSuggestionChar) ?? "@";
816
+ const triggerId = stringAttr(attrs?.triggerId) ?? triggers.find((candidate) => candidate.char === trigger)?.id ?? trigger;
817
+ const label = stringAttr(attrs?.label) ?? id;
818
+ const text = composerEntityText(attrs);
819
+ const previous = previousEntities.find(
820
+ (entity) => entity.id === id && entity.triggerId === triggerId && entity.trigger === trigger
821
+ );
822
+ const data = attrs?.data ?? previous?.data;
823
+ return {
824
+ id,
825
+ triggerId,
826
+ trigger,
827
+ label,
828
+ text,
829
+ range: {
830
+ from,
831
+ to: from + text.length
832
+ },
833
+ ...data === void 0 ? {} : { data }
834
+ };
835
+ }
836
+ function composerEntityText(attrs) {
837
+ const text = stringAttr(attrs?.text);
838
+ if (text !== void 0) {
839
+ return text;
840
+ }
841
+ const trigger = stringAttr(attrs?.mentionSuggestionChar) ?? "@";
842
+ return `${trigger}${stringAttr(attrs?.label) ?? stringAttr(attrs?.id) ?? ""}`;
843
+ }
844
+ function stringAttr(value) {
845
+ return typeof value === "string" ? value : void 0;
846
+ }
847
+ function dataFromAttribute(value) {
848
+ if (value === null) {
849
+ return null;
850
+ }
851
+ try {
852
+ return JSON.parse(value);
853
+ } catch {
854
+ return null;
855
+ }
856
+ }
329
857
  function resizeComposerInput(input, { maxRows, minRows }) {
330
858
  if (input === null) {
331
859
  return;
@@ -384,6 +912,19 @@ function normalizeQuote(quote) {
384
912
  messageId: quote.messageId
385
913
  };
386
914
  }
915
+ function composerSubmitMetadata(quote, entities) {
916
+ if (quote === void 0 && entities.length === 0) {
917
+ return void 0;
918
+ }
919
+ return {
920
+ ...quote === void 0 ? {} : { quote },
921
+ ...entities.length === 0 ? {} : {
922
+ composer: {
923
+ entities
924
+ }
925
+ }
926
+ };
927
+ }
387
928
  var ComposerSubmit = forwardRef(
388
929
  function ComposerSubmit2(props, ref) {
389
930
  const composer = useComposer();
@@ -429,6 +970,84 @@ var ComposerStop = forwardRef(function ComposerStop2({ onClick, ...props }, ref)
429
970
  ref
430
971
  );
431
972
  });
973
+ var ComposerTriggerMenu = forwardRef(
974
+ function ComposerTriggerMenu2({ children, keepMounted = false, style, ...props }, ref) {
975
+ const composer = useComposer();
976
+ const activeTrigger = composer.activeTrigger;
977
+ const empty = activeTrigger === void 0;
978
+ if (empty && !keepMounted) {
979
+ return null;
980
+ }
981
+ const menuStyle = activeTrigger?.rect === void 0 ? style : {
982
+ position: "fixed",
983
+ top: activeTrigger.rect.bottom,
984
+ left: activeTrigger.rect.left,
985
+ ...style
986
+ };
987
+ const renderedChildren = activeTrigger === void 0 ? typeof children === "function" ? null : children : typeof children === "function" ? children(activeTrigger) : children ?? activeTrigger.items.map((item, index) => /* @__PURE__ */ jsx(ComposerTriggerItem, { index }, `${item.id}:${index.toString()}`));
988
+ return renderPrimitive(
989
+ "div",
990
+ {
991
+ ...props,
992
+ children: renderedChildren,
993
+ role: props.role ?? "listbox",
994
+ style: menuStyle,
995
+ "data-anvia-composer-trigger-menu": "",
996
+ "data-empty": empty ? "" : void 0,
997
+ "data-loading": activeTrigger?.loading ? "" : void 0,
998
+ "data-trigger": activeTrigger?.trigger.char,
999
+ "data-trigger-id": activeTrigger?.trigger.id
1000
+ },
1001
+ ref
1002
+ );
1003
+ }
1004
+ );
1005
+ var ComposerTriggerItem = forwardRef(
1006
+ function ComposerTriggerItem2({ children, index, item: itemProp, onClick, onMouseEnter, ...props }, ref) {
1007
+ const composer = useComposer();
1008
+ const activeTrigger = composer.activeTrigger;
1009
+ const itemIndex = index ?? 0;
1010
+ const item = itemProp ?? activeTrigger?.items[itemIndex];
1011
+ if (activeTrigger === void 0 || item === void 0) {
1012
+ return null;
1013
+ }
1014
+ const selected = activeTrigger.selectedIndex === itemIndex;
1015
+ const disabled = props.disabled ?? item.disabled ?? false;
1016
+ const renderedChildren = typeof children === "function" ? children(item, activeTrigger) : children ?? item.label;
1017
+ const handleMouseEnter = (event) => {
1018
+ onMouseEnter?.(event);
1019
+ if (!event.defaultPrevented && !disabled) {
1020
+ activeTrigger.setSelectedIndex(itemIndex);
1021
+ }
1022
+ };
1023
+ const handleClick = (event) => {
1024
+ onClick?.(event);
1025
+ if (!event.defaultPrevented && !disabled) {
1026
+ activeTrigger.selectItem(item);
1027
+ }
1028
+ };
1029
+ return renderPrimitive(
1030
+ "button",
1031
+ {
1032
+ ...props,
1033
+ children: renderedChildren,
1034
+ disabled,
1035
+ onClick: handleClick,
1036
+ onMouseEnter: handleMouseEnter,
1037
+ role: props.role ?? "option",
1038
+ type: props.type ?? "button",
1039
+ "aria-selected": selected,
1040
+ "data-anvia-composer-trigger-item": "",
1041
+ "data-disabled": disabled ? "" : void 0,
1042
+ "data-item-id": item.id,
1043
+ "data-selected": selected ? "" : void 0,
1044
+ "data-trigger": activeTrigger.trigger.char,
1045
+ "data-trigger-id": activeTrigger.trigger.id
1046
+ },
1047
+ ref
1048
+ );
1049
+ }
1050
+ );
432
1051
  var ComposerAttachments = forwardRef(
433
1052
  function ComposerAttachments2({ children, keepMounted = false, ...props }, ref) {
434
1053
  const composer = useComposer();
@@ -635,6 +1254,9 @@ var Composer = {
635
1254
  Quote: ComposerQuotePreview,
636
1255
  ClearQuote: ComposerClearQuote,
637
1256
  Input: ComposerInput,
1257
+ TextareaInput: ComposerTextareaInput,
1258
+ TriggerMenu: ComposerTriggerMenu,
1259
+ TriggerItem: ComposerTriggerItem,
638
1260
  Attachments: ComposerAttachments,
639
1261
  AddAttachment: ComposerAddAttachment,
640
1262
  AttachmentInput: ComposerAttachmentInput,
@@ -924,4 +1546,4 @@ export {
924
1546
  Composer,
925
1547
  Thread
926
1548
  };
927
- //# sourceMappingURL=chunk-LRAQW3ZK.js.map
1549
+ //# sourceMappingURL=chunk-KO36TK2O.js.map