@particle-academy/react-fancy 5.37.0 → 5.39.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 (64) hide show
  1. package/CHANGELOG.md +133 -0
  2. package/dist/{index-C8PKWA_5.d.cts → Composer.types-CuSPnaOt.d.cts} +103 -7
  3. package/dist/{index-C8PKWA_5.d.ts → Composer.types-CuSPnaOt.d.ts} +103 -7
  4. package/dist/autocomplete.cjs +3 -3
  5. package/dist/autocomplete.js +2 -2
  6. package/dist/chunk-2JOWGYAJ.js +609 -0
  7. package/dist/chunk-2JOWGYAJ.js.map +1 -0
  8. package/dist/chunk-6TZPM43Q.cjs +616 -0
  9. package/dist/chunk-6TZPM43Q.cjs.map +1 -0
  10. package/dist/{chunk-FXP4JLGU.cjs → chunk-AR3JOPGT.cjs} +3 -3
  11. package/dist/{chunk-FXP4JLGU.cjs.map → chunk-AR3JOPGT.cjs.map} +1 -1
  12. package/dist/{chunk-23FTDEGR.cjs → chunk-BZOU7VJQ.cjs} +97 -84
  13. package/dist/chunk-BZOU7VJQ.cjs.map +1 -0
  14. package/dist/{chunk-35EN5AYE.js → chunk-CF5KRDN4.js} +6 -3
  15. package/dist/chunk-CF5KRDN4.js.map +1 -0
  16. package/dist/{chunk-DNV7MUEE.cjs → chunk-FJ2GMQLC.cjs} +117 -27
  17. package/dist/chunk-FJ2GMQLC.cjs.map +1 -0
  18. package/dist/{chunk-6ASZUE4T.js → chunk-FQLL2JI7.js} +3 -3
  19. package/dist/{chunk-6ASZUE4T.js.map → chunk-FQLL2JI7.js.map} +1 -1
  20. package/dist/{chunk-ZH3SPBVB.js → chunk-IECPLOSZ.js} +3 -3
  21. package/dist/{chunk-ZH3SPBVB.js.map → chunk-IECPLOSZ.js.map} +1 -1
  22. package/dist/{chunk-E2SZBIAV.cjs → chunk-IWQ3ZFLZ.cjs} +5 -5
  23. package/dist/{chunk-E2SZBIAV.cjs.map → chunk-IWQ3ZFLZ.cjs.map} +1 -1
  24. package/dist/{chunk-QBNR5IVM.cjs → chunk-MFBUK4RV.cjs} +3 -3
  25. package/dist/{chunk-QBNR5IVM.cjs.map → chunk-MFBUK4RV.cjs.map} +1 -1
  26. package/dist/{chunk-2XAVAPDA.js → chunk-MXAVGOHW.js} +99 -85
  27. package/dist/chunk-MXAVGOHW.js.map +1 -0
  28. package/dist/{chunk-ISPCXIJZ.cjs → chunk-X7H75WVP.cjs} +6 -3
  29. package/dist/chunk-X7H75WVP.cjs.map +1 -0
  30. package/dist/{chunk-O3WEV562.js → chunk-YLPVTEUH.js} +118 -28
  31. package/dist/chunk-YLPVTEUH.js.map +1 -0
  32. package/dist/{chunk-O2ZHORSC.js → chunk-Z6RVBZQ4.js} +3 -3
  33. package/dist/{chunk-O2ZHORSC.js.map → chunk-Z6RVBZQ4.js.map} +1 -1
  34. package/dist/composer.cjs +10 -2
  35. package/dist/composer.d.cts +7 -2
  36. package/dist/composer.d.ts +7 -2
  37. package/dist/composer.js +5 -1
  38. package/dist/editor.cjs +1 -1
  39. package/dist/editor.js +1 -1
  40. package/dist/{index-DXUAAZzE.d.ts → index-BsIBOwaK.d.cts} +46 -1
  41. package/dist/{index-DXUAAZzE.d.cts → index-S4FC1yFK.d.ts} +46 -1
  42. package/dist/index.cjs +40 -35
  43. package/dist/index.d.cts +3 -2
  44. package/dist/index.d.ts +3 -2
  45. package/dist/index.js +8 -7
  46. package/dist/inputs.cjs +3 -3
  47. package/dist/inputs.js +2 -2
  48. package/dist/json-editor.cjs +14 -14
  49. package/dist/json-editor.js +3 -3
  50. package/dist/prompt-input.cjs +5 -3
  51. package/dist/prompt-input.d.cts +2 -1
  52. package/dist/prompt-input.d.ts +2 -1
  53. package/dist/prompt-input.js +4 -2
  54. package/dist/select.cjs +3 -3
  55. package/dist/select.js +2 -2
  56. package/dist/tooltip.cjs +2 -2
  57. package/dist/tooltip.js +1 -1
  58. package/package.json +1 -1
  59. package/dist/chunk-23FTDEGR.cjs.map +0 -1
  60. package/dist/chunk-2XAVAPDA.js.map +0 -1
  61. package/dist/chunk-35EN5AYE.js.map +0 -1
  62. package/dist/chunk-DNV7MUEE.cjs.map +0 -1
  63. package/dist/chunk-ISPCXIJZ.cjs.map +0 -1
  64. package/dist/chunk-O3WEV562.js.map +0 -1
@@ -0,0 +1,616 @@
1
+ 'use strict';
2
+
3
+ var chunkX7H75WVP_cjs = require('./chunk-X7H75WVP.cjs');
4
+ var chunkSNHU2AI4_cjs = require('./chunk-SNHU2AI4.cjs');
5
+ var chunkZU5Q77OT_cjs = require('./chunk-ZU5Q77OT.cjs');
6
+ var react = require('react');
7
+ var jsxRuntime = require('react/jsx-runtime');
8
+ var reactDom = require('react-dom');
9
+
10
+ // src/components/Composer/paste-markers.ts
11
+ var PILL_MARK = "\uFFFC";
12
+ var MARKER = /\uFFFC([^\uFFFC\n]+)\uFFFC/g;
13
+ function pasteMarker(id) {
14
+ return `${PILL_MARK}${id}${PILL_MARK}`;
15
+ }
16
+ function segmentValue(value, known) {
17
+ const out = [];
18
+ let last = 0;
19
+ for (const match of value.matchAll(MARKER)) {
20
+ const id = match[1];
21
+ if (!known.has(id)) continue;
22
+ const start = match.index ?? 0;
23
+ if (start > last) out.push({ type: "text", text: value.slice(last, start) });
24
+ out.push({ type: "pill", id });
25
+ last = start + match[0].length;
26
+ }
27
+ if (last < value.length) out.push({ type: "text", text: value.slice(last) });
28
+ return out;
29
+ }
30
+ function markedIds(value) {
31
+ return Array.from(value.matchAll(MARKER), (m) => m[1]);
32
+ }
33
+ function expandMarkers(value, pastes, images) {
34
+ const text = new Map(pastes.map((p) => [p.id, p.text]));
35
+ const image = new Map(images.map((i) => [i.id, `[${i.label}]`]));
36
+ return value.replace(MARKER, (whole, id) => text.get(id) ?? image.get(id) ?? whole);
37
+ }
38
+ function usePastePills({
39
+ threshold,
40
+ heldPastes: controlledHeldPastes,
41
+ onHeldPastesChange
42
+ } = {}) {
43
+ const [heldPastes, setHeldPastes] = chunkSNHU2AI4_cjs.useControllableState(
44
+ controlledHeldPastes,
45
+ [],
46
+ onHeldPastesChange
47
+ );
48
+ const nextId = react.useRef(0);
49
+ const hold = react.useCallback(
50
+ (text) => {
51
+ let id;
52
+ do {
53
+ nextId.current += 1;
54
+ id = `paste-${nextId.current}`;
55
+ } while (heldPastes.some((p) => p.id === id));
56
+ setHeldPastes((current) => [...current, { id, text }]);
57
+ return id;
58
+ },
59
+ [heldPastes, setHeldPastes]
60
+ );
61
+ const handlePaste = react.useCallback(
62
+ (event) => {
63
+ if (!threshold || threshold <= 0) return false;
64
+ const text = event.clipboardData?.getData("text/plain") ?? "";
65
+ if (text.length < threshold) return false;
66
+ event.preventDefault();
67
+ hold(text);
68
+ return true;
69
+ },
70
+ [threshold, hold]
71
+ );
72
+ const expand = react.useCallback(
73
+ (id) => {
74
+ const found = heldPastes.find((p) => p.id === id);
75
+ if (found === void 0) return void 0;
76
+ setHeldPastes((current) => current.filter((p) => p.id !== id));
77
+ return found.text;
78
+ },
79
+ [heldPastes, setHeldPastes]
80
+ );
81
+ const remove = react.useCallback(
82
+ (id) => setHeldPastes((current) => current.filter((p) => p.id !== id)),
83
+ [setHeldPastes]
84
+ );
85
+ const clear = react.useCallback(() => setHeldPastes([]), [setHeldPastes]);
86
+ return { heldPastes, handlePaste, hold, expand, remove, clear };
87
+ }
88
+ function withSeparators(n) {
89
+ return String(n).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
90
+ }
91
+ function endsOf(text, chars) {
92
+ if (text.length <= chars * 2) return text;
93
+ return `${text.slice(0, chars)} \u2026 ${text.slice(-chars)}`;
94
+ }
95
+ var PILL_BASE = "inline-flex select-none items-center rounded-md px-2 py-0.5 text-xs leading-5 transition-colors";
96
+ var TEXT_PILL = "cursor-pointer bg-zinc-100 text-zinc-700 hover:bg-zinc-200 dark:bg-zinc-800 dark:text-zinc-200 dark:hover:bg-zinc-700";
97
+ var IMAGE_PILL = "bg-rose-100 font-medium text-rose-600 dark:bg-rose-500/20 dark:text-rose-300";
98
+ var PREVIEW_BASE = "max-w-xs whitespace-pre-wrap break-all text-xs";
99
+ function ImageThumb({ file }) {
100
+ const [url, setUrl] = react.useState(null);
101
+ react.useEffect(() => {
102
+ if (typeof URL.createObjectURL !== "function") return;
103
+ const made = URL.createObjectURL(file);
104
+ setUrl(made);
105
+ return () => URL.revokeObjectURL(made);
106
+ }, [file]);
107
+ return url ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: url, alt: "", className: "max-h-40 max-w-[16rem] rounded" }) : /* @__PURE__ */ jsxRuntime.jsx("span", { children: file.name });
108
+ }
109
+ function InlinePill({
110
+ preview,
111
+ previewChars,
112
+ renderPastePreview,
113
+ classNames,
114
+ disabled,
115
+ onExpand
116
+ }) {
117
+ const id = preview.kind === "text" ? preview.paste.id : preview.image.id;
118
+ const content = /* @__PURE__ */ jsxRuntime.jsx("span", { "data-react-fancy-composer-paste-pill-preview": id, children: renderPastePreview?.(preview) ?? (preview.kind === "text" ? endsOf(preview.paste.text, previewChars) : /* @__PURE__ */ jsxRuntime.jsx(ImageThumb, { file: preview.image.file })) });
119
+ const tooltipClass = chunkZU5Q77OT_cjs.cn(PREVIEW_BASE, classNames?.preview);
120
+ if (preview.kind === "image") {
121
+ return /* @__PURE__ */ jsxRuntime.jsx(chunkX7H75WVP_cjs.Tooltip, { content, className: tooltipClass, children: /* @__PURE__ */ jsxRuntime.jsx(
122
+ "span",
123
+ {
124
+ "data-react-fancy-composer-image-pill": id,
125
+ role: "img",
126
+ "aria-label": preview.image.label,
127
+ className: chunkZU5Q77OT_cjs.cn(PILL_BASE, IMAGE_PILL, classNames?.pill, classNames?.imagePill),
128
+ children: preview.image.label
129
+ }
130
+ ) });
131
+ }
132
+ const count = withSeparators(preview.paste.text.length);
133
+ return /* @__PURE__ */ jsxRuntime.jsx(chunkX7H75WVP_cjs.Tooltip, { content, className: tooltipClass, children: /* @__PURE__ */ jsxRuntime.jsxs(
134
+ "button",
135
+ {
136
+ type: "button",
137
+ "data-react-fancy-composer-paste-pill": id,
138
+ onClick: () => onExpand(id),
139
+ disabled,
140
+ "aria-label": `Insert this pasted text, ${count} characters`,
141
+ className: chunkZU5Q77OT_cjs.cn(PILL_BASE, TEXT_PILL, classNames?.pill, classNames?.textPill),
142
+ children: [
143
+ "Pasted text \xB7 ",
144
+ count,
145
+ " characters"
146
+ ]
147
+ }
148
+ ) });
149
+ }
150
+ var HOST_ATTR = "data-react-fancy-composer-paste-host";
151
+ function serialize(parent, keep) {
152
+ let out = "";
153
+ const children = Array.from(parent.childNodes);
154
+ children.forEach((node, i) => {
155
+ if (node.nodeType === Node.TEXT_NODE) {
156
+ out += (node.textContent ?? "").replace(/\u00A0/g, " ");
157
+ return;
158
+ }
159
+ if (!(node instanceof HTMLElement)) return;
160
+ const id = node.getAttribute(HOST_ATTR);
161
+ if (id !== null) {
162
+ if (keep(id)) out += pasteMarker(id);
163
+ return;
164
+ }
165
+ if (node.tagName === "BR") {
166
+ if (i !== children.length - 1) out += "\n";
167
+ return;
168
+ }
169
+ if (/^(DIV|P)$/.test(node.tagName) && out.length > 0 && !out.endsWith("\n")) {
170
+ out += "\n";
171
+ }
172
+ out += serialize(node, keep);
173
+ });
174
+ return out;
175
+ }
176
+ function makeHost(id) {
177
+ const host = document.createElement("span");
178
+ host.setAttribute("contenteditable", "false");
179
+ host.setAttribute(HOST_ATTR, id);
180
+ host.className = "inline-block align-baseline";
181
+ return host;
182
+ }
183
+ function ensureTrailingBreak(root, value) {
184
+ const last = root.lastChild;
185
+ const hasBreak = last instanceof HTMLElement && last.tagName === "BR";
186
+ if (value.endsWith("\n") && !hasBreak) root.append(document.createElement("br"));
187
+ if (!value.endsWith("\n") && hasBreak && value.length > 0) last.remove();
188
+ }
189
+ function placeCaretAfter(node) {
190
+ const selection = window.getSelection();
191
+ if (!selection) return;
192
+ const range = document.createRange();
193
+ range.setStartAfter(node);
194
+ range.collapse(true);
195
+ selection.removeAllRanges();
196
+ selection.addRange(range);
197
+ }
198
+ function placeCaretAtOffset(root, offset, keep) {
199
+ const selection = window.getSelection();
200
+ if (!selection) return;
201
+ const range = document.createRange();
202
+ let remaining = offset;
203
+ let placed = false;
204
+ for (const node of Array.from(root.childNodes)) {
205
+ if (node.nodeType === Node.TEXT_NODE) {
206
+ const length = node.textContent?.length ?? 0;
207
+ if (remaining <= length) {
208
+ range.setStart(node, remaining);
209
+ placed = true;
210
+ break;
211
+ }
212
+ remaining -= length;
213
+ continue;
214
+ }
215
+ const id = node instanceof HTMLElement ? node.getAttribute(HOST_ATTR) : null;
216
+ if (id !== null && keep(id)) {
217
+ const length = pasteMarker(id).length;
218
+ if (remaining < length) {
219
+ range.setStartAfter(node);
220
+ placed = true;
221
+ break;
222
+ }
223
+ remaining -= length;
224
+ }
225
+ }
226
+ if (!placed) {
227
+ range.selectNodeContents(root);
228
+ range.collapse(false);
229
+ const last = root.lastChild;
230
+ if (last instanceof HTMLElement && last.tagName === "BR") range.setStartBefore(last);
231
+ }
232
+ range.collapse(true);
233
+ selection.removeAllRanges();
234
+ selection.addRange(range);
235
+ }
236
+ var InlinePasteEditor = react.forwardRef(
237
+ function InlinePasteEditor2({
238
+ value,
239
+ known,
240
+ onChange,
241
+ onSubmit,
242
+ onPaste,
243
+ onHostsChange,
244
+ placeholder,
245
+ disabled,
246
+ className,
247
+ submitKey = "enter",
248
+ onKeyDown,
249
+ placeholderClassName,
250
+ maxHeight
251
+ }, ref) {
252
+ const rootRef = react.useRef(null);
253
+ const lastValue = react.useRef(null);
254
+ const pending = react.useRef(/* @__PURE__ */ new Set());
255
+ const knownRef = react.useRef(known);
256
+ knownRef.current = known;
257
+ const keep = react.useCallback(
258
+ (id) => knownRef.current.has(id) || pending.current.has(id),
259
+ []
260
+ );
261
+ const reportHosts = react.useCallback(() => {
262
+ const root = rootRef.current;
263
+ if (!root) return;
264
+ const hosts = /* @__PURE__ */ new Map();
265
+ root.querySelectorAll(`[${HOST_ATTR}]`).forEach((el) => {
266
+ const id = el.getAttribute(HOST_ATTR);
267
+ if (keep(id)) hosts.set(id, el);
268
+ });
269
+ onHostsChange(hosts);
270
+ }, [keep, onHostsChange]);
271
+ const caretOffset = react.useCallback(
272
+ (whole) => {
273
+ const root = rootRef.current;
274
+ const selection = window.getSelection();
275
+ if (!root || !selection || selection.rangeCount === 0) return whole.length;
276
+ const range = selection.getRangeAt(0);
277
+ if (!root.contains(range.endContainer)) return whole.length;
278
+ const head = document.createRange();
279
+ head.selectNodeContents(root);
280
+ head.setEnd(range.endContainer, range.endOffset);
281
+ return serialize(head.cloneContents(), keep).length;
282
+ },
283
+ [keep]
284
+ );
285
+ const emit = react.useCallback(() => {
286
+ const root = rootRef.current;
287
+ if (!root) return;
288
+ const next = serialize(root, keep);
289
+ ensureTrailingBreak(root, next);
290
+ reportHosts();
291
+ lastValue.current = next;
292
+ onChange(next, caretOffset(next));
293
+ }, [keep, onChange, reportHosts, caretOffset]);
294
+ const render = react.useCallback(
295
+ (next) => {
296
+ const root = rootRef.current;
297
+ if (!root) return;
298
+ root.replaceChildren();
299
+ for (const segment of segmentValue(next, knownRef.current)) {
300
+ if (segment.type === "text") {
301
+ root.append(document.createTextNode(segment.text));
302
+ } else {
303
+ root.append(makeHost(segment.id));
304
+ }
305
+ }
306
+ ensureTrailingBreak(root, next);
307
+ lastValue.current = next;
308
+ reportHosts();
309
+ },
310
+ [reportHosts]
311
+ );
312
+ const caret = react.useCallback(() => {
313
+ const root = rootRef.current;
314
+ const selection = window.getSelection();
315
+ if (selection && selection.rangeCount > 0) {
316
+ const range2 = selection.getRangeAt(0);
317
+ if (root.contains(range2.commonAncestorContainer)) return range2;
318
+ }
319
+ const range = document.createRange();
320
+ range.selectNodeContents(root);
321
+ range.collapse(false);
322
+ const last = root.lastChild;
323
+ if (last instanceof HTMLElement && last.tagName === "BR") {
324
+ range.setStartBefore(last);
325
+ range.collapse(true);
326
+ }
327
+ return range;
328
+ }, []);
329
+ const insert = react.useCallback(
330
+ (build) => {
331
+ const root = rootRef.current;
332
+ const range = caret();
333
+ range.deleteContents();
334
+ const head = document.createRange();
335
+ head.selectNodeContents(root);
336
+ head.setEnd(range.startContainer, range.startOffset);
337
+ const tail = document.createRange();
338
+ tail.selectNodeContents(root);
339
+ tail.setStart(range.endContainer, range.endOffset);
340
+ const nodes = build(
341
+ serialize(head.cloneContents(), () => true),
342
+ serialize(tail.cloneContents(), () => true)
343
+ );
344
+ if (nodes.length === 0) return;
345
+ const fragment = document.createDocumentFragment();
346
+ nodes.forEach((n) => fragment.append(n));
347
+ range.insertNode(fragment);
348
+ placeCaretAfter(nodes[nodes.length - 1]);
349
+ emit();
350
+ },
351
+ [caret, emit]
352
+ );
353
+ const insertText = react.useCallback(
354
+ (text) => insert(() => text ? [document.createTextNode(text)] : []),
355
+ [insert]
356
+ );
357
+ react.useLayoutEffect(() => {
358
+ const root = rootRef.current;
359
+ if (!root) return;
360
+ for (const id of pending.current) if (known.has(id)) pending.current.delete(id);
361
+ const wanted = segmentValue(value, known).flatMap((s) => s.type === "pill" ? [s.id] : []).join(PILL_MARK);
362
+ const onScreen = Array.from(root.querySelectorAll(`[${HOST_ATTR}]`)).map((el) => el.getAttribute(HOST_ATTR)).filter((id) => id !== null && keep(id)).join(PILL_MARK);
363
+ if (value === lastValue.current && wanted === onScreen) return;
364
+ render(value);
365
+ }, [value, known, keep, render]);
366
+ react.useImperativeHandle(
367
+ ref,
368
+ () => ({
369
+ focus() {
370
+ const root = rootRef.current;
371
+ if (!root) return;
372
+ root.focus();
373
+ const range = caret();
374
+ const selection = window.getSelection();
375
+ selection?.removeAllRanges();
376
+ selection?.addRange(range);
377
+ },
378
+ replacePill(id, text) {
379
+ const root = rootRef.current;
380
+ const host = Array.from(
381
+ root?.querySelectorAll(`[${HOST_ATTR}]`) ?? []
382
+ ).find((el) => el.getAttribute(HOST_ATTR) === id);
383
+ if (!root || !host) return;
384
+ const node = document.createTextNode(text);
385
+ host.replaceWith(node);
386
+ root.focus();
387
+ placeCaretAfter(node);
388
+ emit();
389
+ },
390
+ setValue(next, offset) {
391
+ const root = rootRef.current;
392
+ if (!root) return;
393
+ render(next);
394
+ root.focus();
395
+ placeCaretAtOffset(root, offset, keep);
396
+ emit();
397
+ }
398
+ }),
399
+ [caret, emit, render, keep]
400
+ );
401
+ const handlePaste = (e) => {
402
+ e.preventDefault();
403
+ if (disabled) return;
404
+ const pills = onPaste(e.clipboardData);
405
+ if (pills === null) {
406
+ insertText((e.clipboardData?.getData("text/plain") ?? "").replace(/\r\n?/g, "\n"));
407
+ return;
408
+ }
409
+ for (const { id } of pills) pending.current.add(id);
410
+ insert((before, after) => {
411
+ const nodes = [];
412
+ for (const { id, block } of pills) {
413
+ if (block) {
414
+ const precededByBreak = nodes.length === 0 ? before === "" || before.endsWith("\n") : nodes[nodes.length - 1].textContent === "\n";
415
+ if (!precededByBreak) nodes.push(document.createTextNode("\n"));
416
+ nodes.push(makeHost(id));
417
+ nodes.push(document.createTextNode("\n"));
418
+ } else {
419
+ nodes.push(makeHost(id));
420
+ }
421
+ }
422
+ const last = nodes[nodes.length - 1];
423
+ if (last.textContent === "\n" && after.startsWith("\n")) nodes.pop();
424
+ return nodes;
425
+ });
426
+ };
427
+ const handleKeyDown = (e) => {
428
+ onKeyDown?.(e);
429
+ if (e.defaultPrevented) return;
430
+ if (e.key !== "Enter" || e.nativeEvent.isComposing) return;
431
+ e.preventDefault();
432
+ const sends = submitKey === "mod-enter" ? e.ctrlKey || e.metaKey : !e.shiftKey;
433
+ if (sends) {
434
+ onSubmit();
435
+ } else {
436
+ insertText("\n");
437
+ }
438
+ };
439
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
440
+ value === "" && /* @__PURE__ */ jsxRuntime.jsx(
441
+ "span",
442
+ {
443
+ "aria-hidden": "true",
444
+ className: chunkZU5Q77OT_cjs.cn(
445
+ "pointer-events-none absolute left-4 top-3 text-sm text-zinc-400",
446
+ placeholderClassName
447
+ ),
448
+ children: placeholder
449
+ }
450
+ ),
451
+ /* @__PURE__ */ jsxRuntime.jsx(
452
+ "div",
453
+ {
454
+ ref: rootRef,
455
+ "data-react-fancy-composer-input": "",
456
+ role: "textbox",
457
+ "aria-multiline": "true",
458
+ "aria-placeholder": placeholder,
459
+ "aria-disabled": disabled || void 0,
460
+ contentEditable: !disabled,
461
+ suppressContentEditableWarning: true,
462
+ onInput: emit,
463
+ onPaste: handlePaste,
464
+ onKeyDown: handleKeyDown,
465
+ style: maxHeight === void 0 ? void 0 : { maxHeight },
466
+ className: chunkZU5Q77OT_cjs.cn(
467
+ "max-h-72 min-h-[4.5rem] w-full overflow-y-auto whitespace-pre-wrap break-words bg-transparent px-4 pt-3 text-sm outline-none",
468
+ className
469
+ )
470
+ }
471
+ )
472
+ ] });
473
+ }
474
+ );
475
+ function useInlinePaste({
476
+ pasteThreshold,
477
+ pasteImages = false,
478
+ previewChars,
479
+ renderPastePreview,
480
+ classNames,
481
+ disabled,
482
+ heldPastes: controlledHeldPastes,
483
+ onHeldPastesChange,
484
+ heldImages: controlledHeldImages,
485
+ onHeldImagesChange,
486
+ editorRef
487
+ }) {
488
+ const pills = usePastePills({
489
+ threshold: pasteThreshold,
490
+ heldPastes: controlledHeldPastes,
491
+ onHeldPastesChange
492
+ });
493
+ const heldPastes = pills.heldPastes;
494
+ const [heldImages, setHeldImages] = chunkSNHU2AI4_cjs.useControllableState(
495
+ controlledHeldImages,
496
+ [],
497
+ onHeldImagesChange
498
+ );
499
+ const imageCount = react.useRef(0);
500
+ const known = react.useMemo(
501
+ () => /* @__PURE__ */ new Set([...heldPastes.map((p) => p.id), ...heldImages.map((i) => i.id)]),
502
+ [heldPastes, heldImages]
503
+ );
504
+ const holdImage = react.useCallback(
505
+ (file) => {
506
+ let id;
507
+ do {
508
+ imageCount.current += 1;
509
+ id = `image-${imageCount.current}`;
510
+ } while (heldImages.some((i) => i.id === id));
511
+ const image = { id, file, label: `Pasted image ${imageCount.current}` };
512
+ setHeldImages((current) => [...current, image]);
513
+ return id;
514
+ },
515
+ [heldImages, setHeldImages]
516
+ );
517
+ const decidePaste = react.useCallback(
518
+ (data) => {
519
+ const text = data?.getData("text/plain") ?? "";
520
+ if (pasteImages && text === "") {
521
+ const files = Array.from(data?.files ?? []).filter(
522
+ (f) => f.type.startsWith("image/")
523
+ );
524
+ if (files.length > 0) {
525
+ return files.map((file) => ({ id: holdImage(file), block: false }));
526
+ }
527
+ }
528
+ if (pasteThreshold && pasteThreshold > 0 && text.length >= pasteThreshold) {
529
+ return [{ id: pills.hold(text), block: true }];
530
+ }
531
+ return null;
532
+ },
533
+ [pasteImages, pasteThreshold, holdImage, pills]
534
+ );
535
+ const prune = react.useCallback(
536
+ (next) => {
537
+ const present = new Set(markedIds(next));
538
+ for (const paste of heldPastes) if (!present.has(paste.id)) pills.remove(paste.id);
539
+ if (heldImages.some((i) => !present.has(i.id))) {
540
+ setHeldImages((current) => current.filter((i) => present.has(i.id)));
541
+ }
542
+ },
543
+ [heldPastes, heldImages, pills, setHeldImages]
544
+ );
545
+ const [hosts, setHosts] = react.useState(/* @__PURE__ */ new Map());
546
+ const onHostsChange = react.useCallback((next) => {
547
+ setHosts((prev) => {
548
+ if (prev.size === next.size && [...next].every(([id, el]) => prev.get(id) === el)) {
549
+ return prev;
550
+ }
551
+ return next;
552
+ });
553
+ }, []);
554
+ const expandPill = react.useCallback(
555
+ (id) => {
556
+ const text = pills.expand(id);
557
+ if (text === void 0) return;
558
+ editorRef.current?.replacePill(id, text);
559
+ },
560
+ [pills, editorRef]
561
+ );
562
+ const expand = react.useCallback(
563
+ (value) => expandMarkers(value, heldPastes, heldImages),
564
+ [heldPastes, heldImages]
565
+ );
566
+ const imagesIn = react.useCallback(
567
+ (value) => markedIds(value).flatMap((id) => heldImages.filter((i) => i.id === id)),
568
+ [heldImages]
569
+ );
570
+ const clear = react.useCallback(() => {
571
+ pills.clear();
572
+ setHeldImages([]);
573
+ }, [pills, setHeldImages]);
574
+ const portals = Array.from(hosts, ([id, host]) => {
575
+ const paste = heldPastes.find((p) => p.id === id);
576
+ const image = paste ? void 0 : heldImages.find((i) => i.id === id);
577
+ const preview = paste ? { kind: "text", paste } : image ? { kind: "image", image } : null;
578
+ return preview ? reactDom.createPortal(
579
+ /* @__PURE__ */ jsxRuntime.jsx(
580
+ InlinePill,
581
+ {
582
+ preview,
583
+ previewChars,
584
+ renderPastePreview,
585
+ classNames,
586
+ disabled,
587
+ onExpand: expandPill
588
+ }
589
+ ),
590
+ host,
591
+ id
592
+ ) : null;
593
+ });
594
+ return {
595
+ pills,
596
+ heldPastes,
597
+ heldImages,
598
+ known,
599
+ decidePaste,
600
+ prune,
601
+ onHostsChange,
602
+ expand,
603
+ imagesIn,
604
+ clear,
605
+ portals
606
+ };
607
+ }
608
+
609
+ exports.InlinePasteEditor = InlinePasteEditor;
610
+ exports.endsOf = endsOf;
611
+ exports.pasteMarker = pasteMarker;
612
+ exports.useInlinePaste = useInlinePaste;
613
+ exports.usePastePills = usePastePills;
614
+ exports.withSeparators = withSeparators;
615
+ //# sourceMappingURL=chunk-6TZPM43Q.cjs.map
616
+ //# sourceMappingURL=chunk-6TZPM43Q.cjs.map