@particle-academy/react-fancy 5.64.1 → 5.66.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 (59) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/dist/{PastePills.types-CDyotkUb.d.cts → PastePills.types-CpXzsuwJ.d.cts} +2 -0
  3. package/dist/{PastePills.types-CDyotkUb.d.ts → PastePills.types-CpXzsuwJ.d.ts} +2 -0
  4. package/dist/{PastedFilesInput-CrgNoEBd.d.ts → PastedFilesInput-Bt3KSTVN.d.ts} +1 -1
  5. package/dist/{PastedFilesInput-DQ1eZaHk.d.cts → PastedFilesInput-CUQeOeOA.d.cts} +1 -1
  6. package/dist/{chunk-SZH7TFME.js → chunk-4EUDW2WU.js} +3 -3
  7. package/dist/{chunk-SZH7TFME.js.map → chunk-4EUDW2WU.js.map} +1 -1
  8. package/dist/{chunk-CMC2TOEQ.cjs → chunk-6CAXLP7D.cjs} +6 -6
  9. package/dist/{chunk-CMC2TOEQ.cjs.map → chunk-6CAXLP7D.cjs.map} +1 -1
  10. package/dist/{chunk-TQGRXJ74.cjs → chunk-7FYRFNPX.cjs} +9 -9
  11. package/dist/{chunk-TQGRXJ74.cjs.map → chunk-7FYRFNPX.cjs.map} +1 -1
  12. package/dist/{chunk-4XHG6Q42.cjs → chunk-7HXFEYOV.cjs} +8 -8
  13. package/dist/{chunk-4XHG6Q42.cjs.map → chunk-7HXFEYOV.cjs.map} +1 -1
  14. package/dist/{chunk-MVVHUTXA.js → chunk-B4LVQ4QI.js} +3 -3
  15. package/dist/{chunk-MVVHUTXA.js.map → chunk-B4LVQ4QI.js.map} +1 -1
  16. package/dist/{chunk-QNQNGARW.cjs → chunk-HFK5SBNM.cjs} +27 -9
  17. package/dist/chunk-HFK5SBNM.cjs.map +1 -0
  18. package/dist/{chunk-BTNF3QBR.js → chunk-J44EV6SP.js} +366 -32
  19. package/dist/chunk-J44EV6SP.js.map +1 -0
  20. package/dist/{chunk-Z6LYBL5L.cjs → chunk-MCQWTOTV.cjs} +369 -33
  21. package/dist/chunk-MCQWTOTV.cjs.map +1 -0
  22. package/dist/{chunk-ELFTEJBT.js → chunk-QSGAFKK2.js} +3 -3
  23. package/dist/{chunk-ELFTEJBT.js.map → chunk-QSGAFKK2.js.map} +1 -1
  24. package/dist/{chunk-NILA2LZY.js → chunk-YIKXJWO4.js} +27 -9
  25. package/dist/chunk-YIKXJWO4.js.map +1 -0
  26. package/dist/composer.cjs +4 -4
  27. package/dist/composer.d.cts +2 -2
  28. package/dist/composer.d.ts +2 -2
  29. package/dist/composer.js +2 -2
  30. package/dist/{index-BZON-gXr.d.ts → index-DWtSViJ3.d.cts} +147 -4
  31. package/dist/{index-BF5qsQur.d.cts → index-XR5t8bTq.d.ts} +147 -4
  32. package/dist/index.cjs +36 -28
  33. package/dist/index.d.cts +3 -3
  34. package/dist/index.d.ts +3 -3
  35. package/dist/index.js +5 -5
  36. package/dist/input.cjs +3 -3
  37. package/dist/input.d.cts +1 -1
  38. package/dist/input.d.ts +1 -1
  39. package/dist/input.js +2 -2
  40. package/dist/inputs.cjs +3 -3
  41. package/dist/inputs.d.cts +1 -1
  42. package/dist/inputs.d.ts +1 -1
  43. package/dist/inputs.js +2 -2
  44. package/dist/json-editor.cjs +14 -14
  45. package/dist/json-editor.js +3 -3
  46. package/dist/paste-pills.cjs +14 -14
  47. package/dist/paste-pills.d.cts +3 -3
  48. package/dist/paste-pills.d.ts +3 -3
  49. package/dist/paste-pills.js +1 -1
  50. package/dist/prompt-input.cjs +11 -3
  51. package/dist/prompt-input.d.cts +2 -2
  52. package/dist/prompt-input.d.ts +2 -2
  53. package/dist/prompt-input.js +2 -2
  54. package/docs/PromptInput.md +101 -1
  55. package/package.json +1 -1
  56. package/dist/chunk-BTNF3QBR.js.map +0 -1
  57. package/dist/chunk-NILA2LZY.js.map +0 -1
  58. package/dist/chunk-QNQNGARW.cjs.map +0 -1
  59. package/dist/chunk-Z6LYBL5L.cjs.map +0 -1
@@ -1,11 +1,100 @@
1
1
  import { Tooltip } from './chunk-GR2WKBER.js';
2
- import { useInlinePaste, InlinePasteEditor, PastedFilesInput } from './chunk-NILA2LZY.js';
2
+ import { useInlinePaste, InlinePasteEditor, PastedFilesInput, markedIds, expandMarkers } from './chunk-YIKXJWO4.js';
3
3
  import { Button } from './chunk-TLRRMYNV.js';
4
4
  import { Icon } from './chunk-J3A4Z2P7.js';
5
5
  import { cn } from './chunk-MMW2JGKZ.js';
6
- import { useState, useRef, useEffect, useMemo } from 'react';
6
+ import { forwardRef, useState, useRef, useCallback, useReducer, useMemo, useEffect, useLayoutEffect, useImperativeHandle } from 'react';
7
7
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
8
8
 
9
+ // src/components/PromptInput/prompt-draft.ts
10
+ var EMPTY_DRAFT = { v: 1, text: "", pills: [], attachments: [] };
11
+ function readFile(raw, id) {
12
+ if (!raw || typeof raw !== "object") return null;
13
+ const f = raw;
14
+ const fileId = id ?? f.id;
15
+ if (typeof fileId !== "string" || typeof f.name !== "string") return null;
16
+ const out = {
17
+ id: fileId,
18
+ name: f.name,
19
+ type: typeof f.type === "string" ? f.type : "",
20
+ bytes: typeof f.bytes === "number" && Number.isFinite(f.bytes) ? f.bytes : 0
21
+ };
22
+ if (typeof f.ref === "string") out.ref = f.ref;
23
+ return out;
24
+ }
25
+ function readPill(raw) {
26
+ if (!raw || typeof raw !== "object") return null;
27
+ const p = raw;
28
+ if (typeof p.id !== "string") return null;
29
+ if (p.kind === "text" && typeof p.text === "string") {
30
+ return typeof p.label === "string" ? { kind: "text", id: p.id, text: p.text, label: p.label } : { kind: "text", id: p.id, text: p.text };
31
+ }
32
+ if (p.kind === "image" && typeof p.label === "string") {
33
+ const file = readFile(p.file, p.id);
34
+ return file ? { kind: "image", id: p.id, label: p.label, file } : null;
35
+ }
36
+ return null;
37
+ }
38
+ function readPromptDraft(raw, inline) {
39
+ if (!raw || typeof raw !== "object" || raw.v !== 1) return null;
40
+ const d = raw;
41
+ let text = typeof d.text === "string" ? d.text : "";
42
+ let pills = (Array.isArray(d.pills) ? d.pills : []).map(readPill).filter((p) => p !== null);
43
+ let attachments = (Array.isArray(d.attachments) ? d.attachments : []).map((a) => readFile(a)).filter((a) => a !== null);
44
+ const marked = new Set(markedIds(text));
45
+ pills = pills.filter((p) => marked.has(p.id));
46
+ if (!inline && pills.length > 0) {
47
+ const texts = pills.flatMap((p) => p.kind === "text" ? [{ id: p.id, text: p.text }] : []);
48
+ const images = pills.flatMap((p) => p.kind === "image" ? [p] : []);
49
+ text = expandMarkers(
50
+ text,
51
+ texts,
52
+ images.map((p) => ({ id: p.id, label: p.label }))
53
+ );
54
+ attachments = [...attachments, ...images.map((p) => p.file)];
55
+ pills = [];
56
+ }
57
+ return { v: 1, text, pills, attachments };
58
+ }
59
+ function clonePromptDraft(draft) {
60
+ return JSON.parse(JSON.stringify(draft));
61
+ }
62
+ function promptDraftFiles(draft) {
63
+ return [
64
+ ...draft.attachments,
65
+ ...draft.pills.flatMap((p) => p.kind === "image" ? [p.file] : [])
66
+ ];
67
+ }
68
+ function promptFileToDataUrl(file) {
69
+ return new Promise((resolve, reject) => {
70
+ const reader = new FileReader();
71
+ reader.onload = () => resolve(String(reader.result));
72
+ reader.onerror = () => reject(reader.error);
73
+ reader.readAsDataURL(file);
74
+ });
75
+ }
76
+ function promptDataUrlToFile(dataUrl, name, type) {
77
+ if (!dataUrl.startsWith("data:")) return void 0;
78
+ const comma = dataUrl.indexOf(",");
79
+ if (comma < 0) return void 0;
80
+ const meta = dataUrl.slice(5, comma);
81
+ const body = dataUrl.slice(comma + 1);
82
+ const mime = type || meta.split(";")[0] || "application/octet-stream";
83
+ try {
84
+ let bytes;
85
+ if (meta.endsWith(";base64")) {
86
+ const bin = atob(body);
87
+ bytes = new Uint8Array(bin.length);
88
+ for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
89
+ } else {
90
+ bytes = new TextEncoder().encode(decodeURIComponent(body));
91
+ }
92
+ return new File([bytes], name, { type: mime });
93
+ } catch {
94
+ return void 0;
95
+ }
96
+ }
97
+
9
98
  // src/utils/platform.ts
10
99
  var APPLE_UA_DATA_PLATFORMS = /* @__PURE__ */ new Set(["macos", "ios", "ipados"]);
11
100
  var APPLE_FREE_TEXT = /\b(mac|macintosh|iphone|ipad|ipod|darwin)/i;
@@ -32,7 +121,15 @@ var DEFAULT_MENTION_COLOR = {
32
121
  file: "#10b981",
33
122
  person: "#3b82f6"
34
123
  };
35
- function PromptInput({
124
+ var PromptInput = forwardRef(function PromptInput2({
125
+ value: controlledValue,
126
+ defaultValue,
127
+ onChange,
128
+ draft: controlledDraft,
129
+ defaultDraft,
130
+ onDraftChange,
131
+ persistAttachment,
132
+ restoreAttachment,
36
133
  budgetTokens,
37
134
  commands = [],
38
135
  mentions = [],
@@ -58,9 +155,152 @@ function PromptInput({
58
155
  meterShowAt = 0.75,
59
156
  meterWarnAt = 0.9,
60
157
  ...rest
61
- }) {
62
- const [text, setText] = useState("");
158
+ }, ref) {
63
159
  const inline = pasteThreshold !== void 0 || pasteImages;
160
+ const draftControlled = controlledDraft !== void 0;
161
+ const [innerDraft, setInnerDraft] = useState(
162
+ () => defaultDraft && readPromptDraft(defaultDraft, inline) || {
163
+ ...EMPTY_DRAFT,
164
+ text: defaultValue ?? ""
165
+ }
166
+ );
167
+ const baseDraft = draftControlled ? controlledDraft : innerDraft;
168
+ const model = !draftControlled && controlledValue !== void 0 && controlledValue !== baseDraft.text ? { ...baseDraft, text: controlledValue } : baseDraft;
169
+ const modelRef = useRef(model);
170
+ modelRef.current = model;
171
+ const text = model.text;
172
+ const onChangeRef = useRef(onChange);
173
+ onChangeRef.current = onChange;
174
+ const onDraftChangeRef = useRef(onDraftChange);
175
+ onDraftChangeRef.current = onDraftChange;
176
+ const persistRef = useRef(persistAttachment);
177
+ persistRef.current = persistAttachment;
178
+ const commit = useCallback(
179
+ (update, notify = true) => {
180
+ const prev = modelRef.current;
181
+ const next = update(prev);
182
+ if (next === prev) return;
183
+ modelRef.current = next;
184
+ setInnerDraft(next);
185
+ if (!notify) return;
186
+ if (next.text !== prev.text) onChangeRef.current?.(next.text);
187
+ onDraftChangeRef.current?.(next);
188
+ },
189
+ []
190
+ );
191
+ const filesRef = useRef(/* @__PURE__ */ new Map());
192
+ const placeholdersRef = useRef(/* @__PURE__ */ new Map());
193
+ const requestedRef = useRef(/* @__PURE__ */ new Set());
194
+ const [filesVersion, bumpFiles] = useReducer((n) => n + 1, 0);
195
+ const setFileRef = useCallback(
196
+ (id, fileRef2) => commit((m) => {
197
+ const withRef = (f) => f.id === id ? { ...f, ref: fileRef2 } : f;
198
+ if (!promptDraftFiles(m).some((f) => f.id === id)) return m;
199
+ return {
200
+ ...m,
201
+ attachments: m.attachments.map(withRef),
202
+ pills: m.pills.map((p) => p.kind === "image" ? { ...p, file: withRef(p.file) } : p)
203
+ };
204
+ }),
205
+ [commit]
206
+ );
207
+ const persist = useCallback(
208
+ (file, entry) => {
209
+ const store = persistRef.current;
210
+ if (!store) return void 0;
211
+ let out;
212
+ try {
213
+ out = store(file, entry);
214
+ } catch (error) {
215
+ console.error("PromptInput: persistAttachment threw", error);
216
+ return void 0;
217
+ }
218
+ if (typeof out === "string") return out;
219
+ if (out && typeof out.then === "function") {
220
+ out.then(
221
+ (fileRef2) => {
222
+ if (typeof fileRef2 === "string") setFileRef(entry.id, fileRef2);
223
+ },
224
+ (error) => console.error("PromptInput: persistAttachment rejected", error)
225
+ );
226
+ }
227
+ return void 0;
228
+ },
229
+ [setFileRef]
230
+ );
231
+ const fileMeta = (id, file) => ({
232
+ id,
233
+ name: file.name,
234
+ type: file.type,
235
+ bytes: file.size
236
+ });
237
+ const fileFor = useCallback((entry) => {
238
+ const real = filesRef.current.get(entry.id);
239
+ if (real) return real;
240
+ let stand = placeholdersRef.current.get(entry.id);
241
+ if (!stand) {
242
+ stand = new File([], entry.name, { type: entry.type });
243
+ placeholdersRef.current.set(entry.id, stand);
244
+ }
245
+ return stand;
246
+ }, []);
247
+ const heldPastes = useMemo(
248
+ () => model.pills.flatMap(
249
+ (p) => p.kind === "text" ? [p.label !== void 0 ? { id: p.id, text: p.text, label: p.label } : { id: p.id, text: p.text }] : []
250
+ ),
251
+ [model.pills]
252
+ );
253
+ const heldImages = useMemo(
254
+ () => model.pills.flatMap(
255
+ (p) => p.kind === "image" ? [{ id: p.id, label: p.label, file: fileFor(p.file) }] : []
256
+ ),
257
+ // filesVersion: a restored File arrived, so the stand-in can be swapped out.
258
+ // eslint-disable-next-line react-hooks/exhaustive-deps
259
+ [model.pills, fileFor, filesVersion]
260
+ );
261
+ const onHeldPastesChange = useCallback(
262
+ (next) => commit((m) => {
263
+ const current = m.pills.filter((p) => p.kind === "text");
264
+ if (current.length === next.length && current.every(
265
+ (p, i) => p.id === next[i].id && p.kind === "text" && p.text === next[i].text && p.label === next[i].label
266
+ )) {
267
+ return m;
268
+ }
269
+ return {
270
+ ...m,
271
+ pills: [
272
+ ...next.map(
273
+ (p) => p.label !== void 0 ? { kind: "text", id: p.id, text: p.text, label: p.label } : { kind: "text", id: p.id, text: p.text }
274
+ ),
275
+ ...m.pills.filter((p) => p.kind === "image")
276
+ ]
277
+ };
278
+ }),
279
+ [commit]
280
+ );
281
+ const onHeldImagesChange = useCallback(
282
+ (next) => commit((m) => {
283
+ const before = new Map(
284
+ m.pills.flatMap((p) => p.kind === "image" ? [[p.id, p]] : [])
285
+ );
286
+ if (before.size === next.length && next.every((i) => before.has(i.id))) return m;
287
+ const images = next.map((image) => {
288
+ const known = before.get(image.id);
289
+ if (known && known.kind === "image") return known;
290
+ filesRef.current.set(image.id, image.file);
291
+ const entry = fileMeta(image.id, image.file);
292
+ const fileRef2 = persist(image.file, entry);
293
+ return {
294
+ kind: "image",
295
+ id: image.id,
296
+ label: image.label,
297
+ file: fileRef2 === void 0 ? entry : { ...entry, ref: fileRef2 }
298
+ };
299
+ });
300
+ return { ...m, pills: [...m.pills.filter((p) => p.kind === "text"), ...images] };
301
+ }),
302
+ [commit, persist]
303
+ );
64
304
  const editorRef = useRef(null);
65
305
  const paste = useInlinePaste({
66
306
  pasteThreshold,
@@ -69,9 +309,55 @@ function PromptInput({
69
309
  renderPastePreview,
70
310
  classNames: pasteClassNames,
71
311
  disabled: false,
312
+ heldPastes,
313
+ onHeldPastesChange,
314
+ heldImages,
315
+ onHeldImagesChange,
72
316
  editorRef
73
317
  });
74
- const [attachments, setAttachments] = useState([]);
318
+ const attachments = useMemo(
319
+ () => model.attachments.map((a) => ({
320
+ id: a.id,
321
+ name: a.name,
322
+ bytes: a.bytes,
323
+ file: filesRef.current.get(a.id),
324
+ type: a.type,
325
+ ...a.ref !== void 0 ? { ref: a.ref } : {}
326
+ })),
327
+ // eslint-disable-next-line react-hooks/exhaustive-deps
328
+ [model.attachments, filesVersion]
329
+ );
330
+ const restoreRef = useRef(restoreAttachment);
331
+ restoreRef.current = restoreAttachment;
332
+ useEffect(() => {
333
+ const restore = restoreRef.current;
334
+ if (!restore) return;
335
+ for (const entry of promptDraftFiles(model)) {
336
+ if (filesRef.current.has(entry.id) || requestedRef.current.has(entry.id)) continue;
337
+ requestedRef.current.add(entry.id);
338
+ const accept = (file) => {
339
+ if (!(file instanceof Blob)) return;
340
+ filesRef.current.set(entry.id, file);
341
+ bumpFiles();
342
+ };
343
+ let out;
344
+ try {
345
+ out = restore(entry);
346
+ } catch (error) {
347
+ console.error("PromptInput: restoreAttachment threw", error);
348
+ continue;
349
+ }
350
+ if (out && typeof out.then === "function") {
351
+ out.then(
352
+ accept,
353
+ (error) => console.error("PromptInput: restoreAttachment rejected", error)
354
+ );
355
+ } else {
356
+ accept(out);
357
+ }
358
+ }
359
+ }, [model]);
360
+ const setText = (next) => commit((m) => m.text === next ? m : { ...m, text: next });
75
361
  const [picker, setPicker] = useState(null);
76
362
  const taRef = useRef(null);
77
363
  const fileRef = useRef(null);
@@ -160,22 +446,68 @@ function PromptInput({
160
446
  const submit = () => {
161
447
  if (!text.trim() && attachments.length === 0) return;
162
448
  if (inline) {
163
- const pastedImages = paste.imagesIn(text).map((image) => ({
164
- id: image.id,
165
- name: image.file.name,
166
- bytes: image.file.size,
167
- file: image.file,
168
- type: image.file.type
169
- }));
449
+ const pastedImages = paste.imagesIn(text).map((image) => {
450
+ const entry = model.pills.find((p) => p.id === image.id);
451
+ const meta = entry?.kind === "image" ? entry.file : fileMeta(image.id, image.file);
452
+ return {
453
+ id: image.id,
454
+ name: meta.name,
455
+ bytes: meta.bytes,
456
+ file: filesRef.current.get(image.id),
457
+ type: meta.type,
458
+ ...meta.ref !== void 0 ? { ref: meta.ref } : {}
459
+ };
460
+ });
170
461
  onSubmit(paste.expand(text), [...attachments, ...pastedImages]);
171
- paste.clear();
172
462
  } else {
173
463
  onSubmit(text, attachments);
174
464
  }
175
- setText("");
176
- setAttachments([]);
465
+ commit(() => EMPTY_DRAFT);
466
+ filesRef.current.clear();
467
+ placeholdersRef.current.clear();
468
+ requestedRef.current.clear();
177
469
  setPicker(null);
178
470
  };
471
+ const pendingFocus = useRef(false);
472
+ const focusInput = useCallback(() => {
473
+ if (inline) {
474
+ editorRef.current?.focus();
475
+ return;
476
+ }
477
+ const ta = taRef.current;
478
+ if (!ta) return;
479
+ ta.focus();
480
+ ta.setSelectionRange(ta.value.length, ta.value.length);
481
+ }, [inline]);
482
+ useLayoutEffect(() => {
483
+ if (!pendingFocus.current) return;
484
+ pendingFocus.current = false;
485
+ focusInput();
486
+ });
487
+ useImperativeHandle(
488
+ ref,
489
+ () => ({
490
+ getDraft: () => clonePromptDraft(modelRef.current),
491
+ setDraft(next, options) {
492
+ const read = readPromptDraft(next, inline);
493
+ if (!read) {
494
+ console.warn(
495
+ "PromptInput: setDraft ignored a draft it does not recognise (expected { v: 1, ... }).",
496
+ next
497
+ );
498
+ return;
499
+ }
500
+ for (const entry of promptDraftFiles(read)) {
501
+ if (!filesRef.current.has(entry.id)) requestedRef.current.delete(entry.id);
502
+ }
503
+ setPicker(null);
504
+ commit(() => read, false);
505
+ if (options?.focus) pendingFocus.current = true;
506
+ },
507
+ focus: focusInput
508
+ }),
509
+ [inline, commit, focusInput]
510
+ );
179
511
  const onKeyDown = (e) => {
180
512
  if (picker) {
181
513
  if (e.key === "ArrowDown") {
@@ -209,16 +541,14 @@ function PromptInput({
209
541
  const attach = (list) => {
210
542
  const files = Array.from(list ?? []);
211
543
  if (files.length === 0) return;
212
- setAttachments((cur) => [
213
- ...cur,
214
- ...files.map((f) => ({
215
- id: crypto.randomUUID(),
216
- name: f.name,
217
- bytes: f.size,
218
- file: f,
219
- type: f.type
220
- }))
221
- ]);
544
+ const added = files.map((f) => {
545
+ const id = crypto.randomUUID();
546
+ filesRef.current.set(id, f);
547
+ const entry = fileMeta(id, f);
548
+ const fileRef2 = persist(f, entry);
549
+ return fileRef2 === void 0 ? entry : { ...entry, ref: fileRef2 };
550
+ });
551
+ commit((m) => ({ ...m, attachments: [...m.attachments, ...added] }));
222
552
  };
223
553
  const onDrop = (e) => {
224
554
  e.preventDefault();
@@ -254,6 +584,7 @@ function PromptInput({
254
584
  attachments.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5 border-b border-zinc-200 px-3 py-2 dark:border-zinc-800", children: attachments.map((a) => /* @__PURE__ */ jsxs(
255
585
  "span",
256
586
  {
587
+ "data-react-fancy-prompt-input-attachment": a.id,
257
588
  className: "inline-flex items-center gap-1.5 rounded-md bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] px-2 py-0.5 text-[11px] dark:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))]",
258
589
  children: [
259
590
  /* @__PURE__ */ jsx(Icon, { name: "paperclip", size: "xs", className: "text-zinc-400" }),
@@ -262,7 +593,10 @@ function PromptInput({
262
593
  /* @__PURE__ */ jsx(
263
594
  "button",
264
595
  {
265
- onClick: () => setAttachments((cur) => cur.filter((x) => x.id !== a.id)),
596
+ onClick: () => commit((m) => ({
597
+ ...m,
598
+ attachments: m.attachments.filter((x) => x.id !== a.id)
599
+ })),
266
600
  className: "opacity-50 hover:opacity-100",
267
601
  "aria-label": "Remove attachment",
268
602
  children: "\xD7"
@@ -304,7 +638,7 @@ function PromptInput({
304
638
  PastedFilesInput,
305
639
  {
306
640
  name: pasteFilesName,
307
- files: paste.imagesIn(text).map((i) => i.file)
641
+ files: paste.imagesIn(text).flatMap((i) => filesRef.current.has(i.id) ? [i.file] : [])
308
642
  }
309
643
  )
310
644
  ] }) : /* @__PURE__ */ jsx(
@@ -469,7 +803,7 @@ function PromptInput({
469
803
  ]
470
804
  }
471
805
  );
472
- }
806
+ });
473
807
  function fmtTokens(n) {
474
808
  if (n < 1e3) return `${n}`;
475
809
  return `${(n / 1e3).toFixed(1)}k`;
@@ -480,6 +814,6 @@ function fmtSize(b) {
480
814
  return `${(b / 1024 / 1024).toFixed(1)} MB`;
481
815
  }
482
816
 
483
- export { PromptInput, browserPlatformHints, detectPlatform, modifierKeyLabel };
484
- //# sourceMappingURL=chunk-BTNF3QBR.js.map
485
- //# sourceMappingURL=chunk-BTNF3QBR.js.map
817
+ export { PromptInput, browserPlatformHints, detectPlatform, modifierKeyLabel, promptDataUrlToFile, promptFileToDataUrl };
818
+ //# sourceMappingURL=chunk-J44EV6SP.js.map
819
+ //# sourceMappingURL=chunk-J44EV6SP.js.map