@particle-academy/react-fancy 5.64.0 → 5.65.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.
- package/CHANGELOG.md +75 -0
- package/dist/chunk-BXPLERIJ.js +293 -0
- package/dist/chunk-BXPLERIJ.js.map +1 -0
- package/dist/{chunk-Z6LYBL5L.cjs → chunk-C463C4MM.cjs} +359 -29
- package/dist/chunk-C463C4MM.cjs.map +1 -0
- package/dist/{chunk-BTNF3QBR.js → chunk-COXHFZR6.js} +360 -32
- package/dist/chunk-COXHFZR6.js.map +1 -0
- package/dist/chunk-XZOBGKZL.cjs +296 -0
- package/dist/chunk-XZOBGKZL.cjs.map +1 -0
- package/dist/dropdown.cjs +3 -3
- package/dist/dropdown.d.cts +10 -0
- package/dist/dropdown.d.ts +10 -0
- package/dist/dropdown.js +1 -1
- package/dist/{index-BF5qsQur.d.cts → index-CCJVT9Hu.d.cts} +140 -3
- package/dist/{index-BZON-gXr.d.ts → index-DRlltbdV.d.ts} +140 -3
- package/dist/index.cjs +16 -8
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/prompt-input.cjs +10 -2
- package/dist/prompt-input.d.cts +1 -1
- package/dist/prompt-input.d.ts +1 -1
- package/dist/prompt-input.js +1 -1
- package/docs/PromptInput.md +78 -1
- package/docs/ReorderableGrid.md +2 -0
- package/package.json +1 -1
- package/dist/chunk-A6HWKQ4D.cjs +0 -172
- package/dist/chunk-A6HWKQ4D.cjs.map +0 -1
- package/dist/chunk-BTNF3QBR.js.map +0 -1
- package/dist/chunk-JG2DJK5A.js +0 -169
- package/dist/chunk-JG2DJK5A.js.map +0 -1
- 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-NILA2LZY.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,
|
|
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 { 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
|
-
|
|
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,146 @@ 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((p) => p.kind === "text" ? [{ id: p.id, text: p.text }] : []),
|
|
249
|
+
[model.pills]
|
|
250
|
+
);
|
|
251
|
+
const heldImages = useMemo(
|
|
252
|
+
() => model.pills.flatMap(
|
|
253
|
+
(p) => p.kind === "image" ? [{ id: p.id, label: p.label, file: fileFor(p.file) }] : []
|
|
254
|
+
),
|
|
255
|
+
// filesVersion: a restored File arrived, so the stand-in can be swapped out.
|
|
256
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
257
|
+
[model.pills, fileFor, filesVersion]
|
|
258
|
+
);
|
|
259
|
+
const onHeldPastesChange = useCallback(
|
|
260
|
+
(next) => commit((m) => {
|
|
261
|
+
const current = m.pills.filter((p) => p.kind === "text");
|
|
262
|
+
if (current.length === next.length && current.every((p, i) => p.id === next[i].id && p.kind === "text" && p.text === next[i].text)) {
|
|
263
|
+
return m;
|
|
264
|
+
}
|
|
265
|
+
return {
|
|
266
|
+
...m,
|
|
267
|
+
pills: [
|
|
268
|
+
...next.map((p) => ({ kind: "text", id: p.id, text: p.text })),
|
|
269
|
+
...m.pills.filter((p) => p.kind === "image")
|
|
270
|
+
]
|
|
271
|
+
};
|
|
272
|
+
}),
|
|
273
|
+
[commit]
|
|
274
|
+
);
|
|
275
|
+
const onHeldImagesChange = useCallback(
|
|
276
|
+
(next) => commit((m) => {
|
|
277
|
+
const before = new Map(
|
|
278
|
+
m.pills.flatMap((p) => p.kind === "image" ? [[p.id, p]] : [])
|
|
279
|
+
);
|
|
280
|
+
if (before.size === next.length && next.every((i) => before.has(i.id))) return m;
|
|
281
|
+
const images = next.map((image) => {
|
|
282
|
+
const known = before.get(image.id);
|
|
283
|
+
if (known && known.kind === "image") return known;
|
|
284
|
+
filesRef.current.set(image.id, image.file);
|
|
285
|
+
const entry = fileMeta(image.id, image.file);
|
|
286
|
+
const fileRef2 = persist(image.file, entry);
|
|
287
|
+
return {
|
|
288
|
+
kind: "image",
|
|
289
|
+
id: image.id,
|
|
290
|
+
label: image.label,
|
|
291
|
+
file: fileRef2 === void 0 ? entry : { ...entry, ref: fileRef2 }
|
|
292
|
+
};
|
|
293
|
+
});
|
|
294
|
+
return { ...m, pills: [...m.pills.filter((p) => p.kind === "text"), ...images] };
|
|
295
|
+
}),
|
|
296
|
+
[commit, persist]
|
|
297
|
+
);
|
|
64
298
|
const editorRef = useRef(null);
|
|
65
299
|
const paste = useInlinePaste({
|
|
66
300
|
pasteThreshold,
|
|
@@ -69,9 +303,55 @@ function PromptInput({
|
|
|
69
303
|
renderPastePreview,
|
|
70
304
|
classNames: pasteClassNames,
|
|
71
305
|
disabled: false,
|
|
306
|
+
heldPastes,
|
|
307
|
+
onHeldPastesChange,
|
|
308
|
+
heldImages,
|
|
309
|
+
onHeldImagesChange,
|
|
72
310
|
editorRef
|
|
73
311
|
});
|
|
74
|
-
const
|
|
312
|
+
const attachments = useMemo(
|
|
313
|
+
() => model.attachments.map((a) => ({
|
|
314
|
+
id: a.id,
|
|
315
|
+
name: a.name,
|
|
316
|
+
bytes: a.bytes,
|
|
317
|
+
file: filesRef.current.get(a.id),
|
|
318
|
+
type: a.type,
|
|
319
|
+
...a.ref !== void 0 ? { ref: a.ref } : {}
|
|
320
|
+
})),
|
|
321
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
322
|
+
[model.attachments, filesVersion]
|
|
323
|
+
);
|
|
324
|
+
const restoreRef = useRef(restoreAttachment);
|
|
325
|
+
restoreRef.current = restoreAttachment;
|
|
326
|
+
useEffect(() => {
|
|
327
|
+
const restore = restoreRef.current;
|
|
328
|
+
if (!restore) return;
|
|
329
|
+
for (const entry of promptDraftFiles(model)) {
|
|
330
|
+
if (filesRef.current.has(entry.id) || requestedRef.current.has(entry.id)) continue;
|
|
331
|
+
requestedRef.current.add(entry.id);
|
|
332
|
+
const accept = (file) => {
|
|
333
|
+
if (!(file instanceof Blob)) return;
|
|
334
|
+
filesRef.current.set(entry.id, file);
|
|
335
|
+
bumpFiles();
|
|
336
|
+
};
|
|
337
|
+
let out;
|
|
338
|
+
try {
|
|
339
|
+
out = restore(entry);
|
|
340
|
+
} catch (error) {
|
|
341
|
+
console.error("PromptInput: restoreAttachment threw", error);
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
if (out && typeof out.then === "function") {
|
|
345
|
+
out.then(
|
|
346
|
+
accept,
|
|
347
|
+
(error) => console.error("PromptInput: restoreAttachment rejected", error)
|
|
348
|
+
);
|
|
349
|
+
} else {
|
|
350
|
+
accept(out);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}, [model]);
|
|
354
|
+
const setText = (next) => commit((m) => m.text === next ? m : { ...m, text: next });
|
|
75
355
|
const [picker, setPicker] = useState(null);
|
|
76
356
|
const taRef = useRef(null);
|
|
77
357
|
const fileRef = useRef(null);
|
|
@@ -160,22 +440,68 @@ function PromptInput({
|
|
|
160
440
|
const submit = () => {
|
|
161
441
|
if (!text.trim() && attachments.length === 0) return;
|
|
162
442
|
if (inline) {
|
|
163
|
-
const pastedImages = paste.imagesIn(text).map((image) =>
|
|
164
|
-
id
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
443
|
+
const pastedImages = paste.imagesIn(text).map((image) => {
|
|
444
|
+
const entry = model.pills.find((p) => p.id === image.id);
|
|
445
|
+
const meta = entry?.kind === "image" ? entry.file : fileMeta(image.id, image.file);
|
|
446
|
+
return {
|
|
447
|
+
id: image.id,
|
|
448
|
+
name: meta.name,
|
|
449
|
+
bytes: meta.bytes,
|
|
450
|
+
file: filesRef.current.get(image.id),
|
|
451
|
+
type: meta.type,
|
|
452
|
+
...meta.ref !== void 0 ? { ref: meta.ref } : {}
|
|
453
|
+
};
|
|
454
|
+
});
|
|
170
455
|
onSubmit(paste.expand(text), [...attachments, ...pastedImages]);
|
|
171
|
-
paste.clear();
|
|
172
456
|
} else {
|
|
173
457
|
onSubmit(text, attachments);
|
|
174
458
|
}
|
|
175
|
-
|
|
176
|
-
|
|
459
|
+
commit(() => EMPTY_DRAFT);
|
|
460
|
+
filesRef.current.clear();
|
|
461
|
+
placeholdersRef.current.clear();
|
|
462
|
+
requestedRef.current.clear();
|
|
177
463
|
setPicker(null);
|
|
178
464
|
};
|
|
465
|
+
const pendingFocus = useRef(false);
|
|
466
|
+
const focusInput = useCallback(() => {
|
|
467
|
+
if (inline) {
|
|
468
|
+
editorRef.current?.focus();
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
const ta = taRef.current;
|
|
472
|
+
if (!ta) return;
|
|
473
|
+
ta.focus();
|
|
474
|
+
ta.setSelectionRange(ta.value.length, ta.value.length);
|
|
475
|
+
}, [inline]);
|
|
476
|
+
useLayoutEffect(() => {
|
|
477
|
+
if (!pendingFocus.current) return;
|
|
478
|
+
pendingFocus.current = false;
|
|
479
|
+
focusInput();
|
|
480
|
+
});
|
|
481
|
+
useImperativeHandle(
|
|
482
|
+
ref,
|
|
483
|
+
() => ({
|
|
484
|
+
getDraft: () => clonePromptDraft(modelRef.current),
|
|
485
|
+
setDraft(next, options) {
|
|
486
|
+
const read = readPromptDraft(next, inline);
|
|
487
|
+
if (!read) {
|
|
488
|
+
console.warn(
|
|
489
|
+
"PromptInput: setDraft ignored a draft it does not recognise (expected { v: 1, ... }).",
|
|
490
|
+
next
|
|
491
|
+
);
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
for (const entry of promptDraftFiles(read)) {
|
|
495
|
+
if (!filesRef.current.has(entry.id)) requestedRef.current.delete(entry.id);
|
|
496
|
+
}
|
|
497
|
+
setPicker(null);
|
|
498
|
+
commit(() => read, false);
|
|
499
|
+
if (options?.focus) pendingFocus.current = true;
|
|
500
|
+
},
|
|
501
|
+
focus: focusInput
|
|
502
|
+
}),
|
|
503
|
+
[inline, commit, focusInput]
|
|
504
|
+
);
|
|
179
505
|
const onKeyDown = (e) => {
|
|
180
506
|
if (picker) {
|
|
181
507
|
if (e.key === "ArrowDown") {
|
|
@@ -209,16 +535,14 @@ function PromptInput({
|
|
|
209
535
|
const attach = (list) => {
|
|
210
536
|
const files = Array.from(list ?? []);
|
|
211
537
|
if (files.length === 0) return;
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
}))
|
|
221
|
-
]);
|
|
538
|
+
const added = files.map((f) => {
|
|
539
|
+
const id = crypto.randomUUID();
|
|
540
|
+
filesRef.current.set(id, f);
|
|
541
|
+
const entry = fileMeta(id, f);
|
|
542
|
+
const fileRef2 = persist(f, entry);
|
|
543
|
+
return fileRef2 === void 0 ? entry : { ...entry, ref: fileRef2 };
|
|
544
|
+
});
|
|
545
|
+
commit((m) => ({ ...m, attachments: [...m.attachments, ...added] }));
|
|
222
546
|
};
|
|
223
547
|
const onDrop = (e) => {
|
|
224
548
|
e.preventDefault();
|
|
@@ -254,6 +578,7 @@ function PromptInput({
|
|
|
254
578
|
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
579
|
"span",
|
|
256
580
|
{
|
|
581
|
+
"data-react-fancy-prompt-input-attachment": a.id,
|
|
257
582
|
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
583
|
children: [
|
|
259
584
|
/* @__PURE__ */ jsx(Icon, { name: "paperclip", size: "xs", className: "text-zinc-400" }),
|
|
@@ -262,7 +587,10 @@ function PromptInput({
|
|
|
262
587
|
/* @__PURE__ */ jsx(
|
|
263
588
|
"button",
|
|
264
589
|
{
|
|
265
|
-
onClick: () =>
|
|
590
|
+
onClick: () => commit((m) => ({
|
|
591
|
+
...m,
|
|
592
|
+
attachments: m.attachments.filter((x) => x.id !== a.id)
|
|
593
|
+
})),
|
|
266
594
|
className: "opacity-50 hover:opacity-100",
|
|
267
595
|
"aria-label": "Remove attachment",
|
|
268
596
|
children: "\xD7"
|
|
@@ -304,7 +632,7 @@ function PromptInput({
|
|
|
304
632
|
PastedFilesInput,
|
|
305
633
|
{
|
|
306
634
|
name: pasteFilesName,
|
|
307
|
-
files: paste.imagesIn(text).
|
|
635
|
+
files: paste.imagesIn(text).flatMap((i) => filesRef.current.has(i.id) ? [i.file] : [])
|
|
308
636
|
}
|
|
309
637
|
)
|
|
310
638
|
] }) : /* @__PURE__ */ jsx(
|
|
@@ -469,7 +797,7 @@ function PromptInput({
|
|
|
469
797
|
]
|
|
470
798
|
}
|
|
471
799
|
);
|
|
472
|
-
}
|
|
800
|
+
});
|
|
473
801
|
function fmtTokens(n) {
|
|
474
802
|
if (n < 1e3) return `${n}`;
|
|
475
803
|
return `${(n / 1e3).toFixed(1)}k`;
|
|
@@ -480,6 +808,6 @@ function fmtSize(b) {
|
|
|
480
808
|
return `${(b / 1024 / 1024).toFixed(1)} MB`;
|
|
481
809
|
}
|
|
482
810
|
|
|
483
|
-
export { PromptInput, browserPlatformHints, detectPlatform, modifierKeyLabel };
|
|
484
|
-
//# sourceMappingURL=chunk-
|
|
485
|
-
//# sourceMappingURL=chunk-
|
|
811
|
+
export { PromptInput, browserPlatformHints, detectPlatform, modifierKeyLabel, promptDataUrlToFile, promptFileToDataUrl };
|
|
812
|
+
//# sourceMappingURL=chunk-COXHFZR6.js.map
|
|
813
|
+
//# sourceMappingURL=chunk-COXHFZR6.js.map
|