@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.
- package/CHANGELOG.md +67 -0
- package/dist/{PastePills.types-CDyotkUb.d.cts → PastePills.types-CpXzsuwJ.d.cts} +2 -0
- package/dist/{PastePills.types-CDyotkUb.d.ts → PastePills.types-CpXzsuwJ.d.ts} +2 -0
- package/dist/{PastedFilesInput-CrgNoEBd.d.ts → PastedFilesInput-Bt3KSTVN.d.ts} +1 -1
- package/dist/{PastedFilesInput-DQ1eZaHk.d.cts → PastedFilesInput-CUQeOeOA.d.cts} +1 -1
- package/dist/{chunk-SZH7TFME.js → chunk-4EUDW2WU.js} +3 -3
- package/dist/{chunk-SZH7TFME.js.map → chunk-4EUDW2WU.js.map} +1 -1
- package/dist/{chunk-CMC2TOEQ.cjs → chunk-6CAXLP7D.cjs} +6 -6
- package/dist/{chunk-CMC2TOEQ.cjs.map → chunk-6CAXLP7D.cjs.map} +1 -1
- package/dist/{chunk-TQGRXJ74.cjs → chunk-7FYRFNPX.cjs} +9 -9
- package/dist/{chunk-TQGRXJ74.cjs.map → chunk-7FYRFNPX.cjs.map} +1 -1
- package/dist/{chunk-4XHG6Q42.cjs → chunk-7HXFEYOV.cjs} +8 -8
- package/dist/{chunk-4XHG6Q42.cjs.map → chunk-7HXFEYOV.cjs.map} +1 -1
- package/dist/{chunk-MVVHUTXA.js → chunk-B4LVQ4QI.js} +3 -3
- package/dist/{chunk-MVVHUTXA.js.map → chunk-B4LVQ4QI.js.map} +1 -1
- package/dist/{chunk-QNQNGARW.cjs → chunk-HFK5SBNM.cjs} +27 -9
- package/dist/chunk-HFK5SBNM.cjs.map +1 -0
- package/dist/{chunk-BTNF3QBR.js → chunk-J44EV6SP.js} +366 -32
- package/dist/chunk-J44EV6SP.js.map +1 -0
- package/dist/{chunk-Z6LYBL5L.cjs → chunk-MCQWTOTV.cjs} +369 -33
- package/dist/chunk-MCQWTOTV.cjs.map +1 -0
- package/dist/{chunk-ELFTEJBT.js → chunk-QSGAFKK2.js} +3 -3
- package/dist/{chunk-ELFTEJBT.js.map → chunk-QSGAFKK2.js.map} +1 -1
- package/dist/{chunk-NILA2LZY.js → chunk-YIKXJWO4.js} +27 -9
- package/dist/chunk-YIKXJWO4.js.map +1 -0
- package/dist/composer.cjs +4 -4
- package/dist/composer.d.cts +2 -2
- package/dist/composer.d.ts +2 -2
- package/dist/composer.js +2 -2
- package/dist/{index-BZON-gXr.d.ts → index-DWtSViJ3.d.cts} +147 -4
- package/dist/{index-BF5qsQur.d.cts → index-XR5t8bTq.d.ts} +147 -4
- package/dist/index.cjs +36 -28
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +5 -5
- package/dist/input.cjs +3 -3
- package/dist/input.d.cts +1 -1
- package/dist/input.d.ts +1 -1
- package/dist/input.js +2 -2
- package/dist/inputs.cjs +3 -3
- package/dist/inputs.d.cts +1 -1
- package/dist/inputs.d.ts +1 -1
- package/dist/inputs.js +2 -2
- package/dist/json-editor.cjs +14 -14
- package/dist/json-editor.js +3 -3
- package/dist/paste-pills.cjs +14 -14
- package/dist/paste-pills.d.cts +3 -3
- package/dist/paste-pills.d.ts +3 -3
- package/dist/paste-pills.js +1 -1
- package/dist/prompt-input.cjs +11 -3
- package/dist/prompt-input.d.cts +2 -2
- package/dist/prompt-input.d.ts +2 -2
- package/dist/prompt-input.js +2 -2
- package/docs/PromptInput.md +101 -1
- package/package.json +1 -1
- package/dist/chunk-BTNF3QBR.js.map +0 -1
- package/dist/chunk-NILA2LZY.js.map +0 -1
- package/dist/chunk-QNQNGARW.cjs.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-
|
|
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,
|
|
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
|
-
|
|
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
|
|
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
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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
|
-
|
|
176
|
-
|
|
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
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
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: () =>
|
|
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).
|
|
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-
|
|
485
|
-
//# sourceMappingURL=chunk-
|
|
817
|
+
export { PromptInput, browserPlatformHints, detectPlatform, modifierKeyLabel, promptDataUrlToFile, promptFileToDataUrl };
|
|
818
|
+
//# sourceMappingURL=chunk-J44EV6SP.js.map
|
|
819
|
+
//# sourceMappingURL=chunk-J44EV6SP.js.map
|