@websolutespa/ask-ui 1.0.2 → 1.0.4

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 (133) hide show
  1. package/dist/components/accordion.d.mts +25 -0
  2. package/dist/components/accordion.mjs +55 -0
  3. package/dist/components/ai-elements/attachments.d.mts +94 -0
  4. package/dist/components/ai-elements/attachments.mjs +174 -0
  5. package/dist/components/ai-elements/conversation.d.mts +51 -0
  6. package/dist/components/ai-elements/conversation.mjs +85 -0
  7. package/dist/components/ai-elements/message.d.mts +89 -0
  8. package/dist/components/ai-elements/message.mjs +175 -0
  9. package/dist/components/ai-elements/prompt-input.d.mts +277 -0
  10. package/dist/components/ai-elements/prompt-input.mjs +703 -0
  11. package/dist/components/ai-elements/speech-input.d.mts +64 -0
  12. package/dist/components/ai-elements/speech-input.mjs +153 -0
  13. package/dist/components/alert-dialog.d.mts +60 -0
  14. package/dist/components/alert-dialog.mjs +101 -0
  15. package/dist/components/alert.d.mts +28 -0
  16. package/dist/components/alert.mjs +43 -0
  17. package/dist/components/aspect-ratio.d.mts +9 -0
  18. package/dist/components/aspect-ratio.mjs +12 -0
  19. package/dist/components/avatar.d.mts +34 -0
  20. package/dist/components/avatar.mjs +51 -0
  21. package/dist/components/badge.d.mts +19 -0
  22. package/dist/components/badge.mjs +27 -0
  23. package/dist/components/breadcrumb.d.mts +38 -0
  24. package/dist/components/breadcrumb.mjs +70 -0
  25. package/dist/components/button-group.d.mts +28 -0
  26. package/dist/components/button-group.mjs +38 -0
  27. package/dist/components/button.d.mts +21 -0
  28. package/dist/components/button.mjs +47 -0
  29. package/dist/components/calendar.d.mts +30 -0
  30. package/dist/components/calendar.mjs +109 -0
  31. package/dist/components/card.d.mts +37 -0
  32. package/dist/components/card.mjs +56 -0
  33. package/dist/components/carousel.d.mts +56 -0
  34. package/dist/components/carousel.mjs +132 -0
  35. package/dist/components/chart.d.mts +78 -0
  36. package/dist/components/chart.mjs +146 -0
  37. package/dist/components/checkbox.d.mts +11 -0
  38. package/dist/components/checkbox.mjs +21 -0
  39. package/dist/components/collapsible.d.mts +15 -0
  40. package/dist/components/collapsible.mjs +24 -0
  41. package/dist/components/combobox.d.mts +81 -0
  42. package/dist/components/combobox.mjs +163 -0
  43. package/dist/components/command.d.mts +54 -0
  44. package/dist/components/command.mjs +88 -0
  45. package/dist/components/context-menu.d.mts +85 -0
  46. package/dist/components/context-menu.mjs +125 -0
  47. package/dist/components/dialog.d.mts +51 -0
  48. package/dist/components/dialog.mjs +96 -0
  49. package/dist/components/direction.d.mts +15 -0
  50. package/dist/components/direction.mjs +14 -0
  51. package/dist/components/drawer.d.mts +47 -0
  52. package/dist/components/drawer.mjs +79 -0
  53. package/dist/components/dropdown-menu.d.mts +87 -0
  54. package/dist/components/dropdown-menu.mjs +131 -0
  55. package/dist/components/empty.d.mts +35 -0
  56. package/dist/components/empty.mjs +59 -0
  57. package/dist/components/field.d.mts +64 -0
  58. package/dist/components/field.mjs +110 -0
  59. package/dist/components/hover-card.d.mts +22 -0
  60. package/dist/components/hover-card.mjs +33 -0
  61. package/dist/components/input-group.d.mts +43 -0
  62. package/dist/components/input-group.mjs +79 -0
  63. package/dist/components/input-otp.d.mts +28 -0
  64. package/dist/components/input-otp.mjs +47 -0
  65. package/dist/components/input.d.mts +11 -0
  66. package/dist/components/input.mjs +14 -0
  67. package/dist/components/item.d.mts +62 -0
  68. package/dist/components/item.mjs +117 -0
  69. package/dist/components/kbd.d.mts +13 -0
  70. package/dist/components/kbd.mjs +19 -0
  71. package/dist/components/label.d.mts +11 -0
  72. package/dist/components/label.mjs +15 -0
  73. package/dist/components/menubar.d.mts +90 -0
  74. package/dist/components/menubar.mjs +135 -0
  75. package/dist/components/native-select.d.mts +21 -0
  76. package/dist/components/native-select.mjs +37 -0
  77. package/dist/components/navigation-menu.d.mts +46 -0
  78. package/dist/components/navigation-menu.mjs +80 -0
  79. package/dist/components/pagination.d.mts +45 -0
  80. package/dist/components/pagination.mjs +80 -0
  81. package/dist/components/popover.d.mts +34 -0
  82. package/dist/components/popover.mjs +56 -0
  83. package/dist/components/progress.d.mts +12 -0
  84. package/dist/components/progress.mjs +20 -0
  85. package/dist/components/radio-group.d.mts +15 -0
  86. package/dist/components/radio-group.mjs +27 -0
  87. package/dist/components/resizable.d.mts +20 -0
  88. package/dist/components/resizable.mjs +28 -0
  89. package/dist/components/scroll-area.d.mts +17 -0
  90. package/dist/components/scroll-area.mjs +37 -0
  91. package/dist/components/select.d.mts +53 -0
  92. package/dist/components/select.mjs +100 -0
  93. package/dist/components/separator.d.mts +13 -0
  94. package/dist/components/separator.mjs +17 -0
  95. package/dist/components/sheet.d.mts +42 -0
  96. package/dist/components/sheet.mjs +90 -0
  97. package/dist/components/sidebar.d.mts +167 -0
  98. package/dist/components/sidebar.mjs +374 -0
  99. package/dist/components/skeleton.d.mts +9 -0
  100. package/dist/components/skeleton.mjs +12 -0
  101. package/dist/components/slider.d.mts +15 -0
  102. package/dist/components/slider.mjs +36 -0
  103. package/dist/components/sonner.d.mts +9 -0
  104. package/dist/components/sonner.mjs +30 -0
  105. package/dist/components/spinner.d.mts +9 -0
  106. package/dist/components/spinner.mjs +14 -0
  107. package/dist/components/switch.d.mts +14 -0
  108. package/dist/components/switch.mjs +20 -0
  109. package/dist/components/table.d.mts +38 -0
  110. package/dist/components/table.mjs +67 -0
  111. package/dist/components/tabs.d.mts +30 -0
  112. package/dist/components/tabs.mjs +46 -0
  113. package/dist/components/textarea.d.mts +10 -0
  114. package/dist/components/textarea.mjs +13 -0
  115. package/dist/components/toggle-group.d.mts +28 -0
  116. package/dist/components/toggle-group.mjs +51 -0
  117. package/dist/components/toggle.d.mts +19 -0
  118. package/dist/components/toggle.mjs +37 -0
  119. package/dist/components/tooltip.d.mts +23 -0
  120. package/dist/components/tooltip.mjs +36 -0
  121. package/dist/exports/components.d.mts +61 -2248
  122. package/dist/exports/components.mjs +60 -4481
  123. package/dist/exports/hooks.d.mts +1 -3
  124. package/dist/exports/hooks.mjs +1 -1
  125. package/dist/exports/lib.d.mts +1 -1
  126. package/dist/exports/lib.mjs +1 -1
  127. package/dist/hooks/use-mobile.d.mts +4 -0
  128. package/dist/{use-mobile-SSHtEcOs.mjs → hooks/use-mobile.mjs} +1 -1
  129. package/dist/index.d.mts +1 -1
  130. package/dist/index.mjs +1 -1
  131. package/dist/{utils-CCM7KiiI.d.mts → lib/utils.d.mts} +1 -1
  132. package/dist/{utils-XdqGR1qq.mjs → lib/utils.mjs} +1 -1
  133. package/package.json +7 -7
@@ -0,0 +1,703 @@
1
+ "use client";
2
+ import { cn } from "../../lib/utils.mjs";
3
+ import { HoverCard, HoverCardContent, HoverCardTrigger } from "../hover-card.mjs";
4
+ import { Tooltip, TooltipContent, TooltipTrigger } from "../tooltip.mjs";
5
+ import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupTextarea } from "../input-group.mjs";
6
+ import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator } from "../command.mjs";
7
+ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "../dropdown-menu.mjs";
8
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../select.mjs";
9
+ import { Spinner } from "../spinner.mjs";
10
+ import { ArrowUpIcon, ImageIcon, Monitor, PlusIcon, SquareIcon, XIcon } from "lucide-react";
11
+ import { Children, createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
12
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
13
+ import { nanoid } from "nanoid";
14
+ //#region src/components/ai-elements/prompt-input.tsx
15
+ const convertBlobUrlToDataUrl = async (url) => {
16
+ try {
17
+ const blob = await (await fetch(url)).blob();
18
+ return new Promise((resolve) => {
19
+ const reader = new FileReader();
20
+ reader.onloadend = () => resolve(reader.result);
21
+ reader.onerror = () => resolve(null);
22
+ reader.readAsDataURL(blob);
23
+ });
24
+ } catch {
25
+ return null;
26
+ }
27
+ };
28
+ const captureScreenshot = async () => {
29
+ if (typeof navigator === "undefined" || !navigator.mediaDevices?.getDisplayMedia) return null;
30
+ let stream = null;
31
+ const video = document.createElement("video");
32
+ video.muted = true;
33
+ video.playsInline = true;
34
+ try {
35
+ stream = await navigator.mediaDevices.getDisplayMedia({
36
+ audio: false,
37
+ video: true
38
+ });
39
+ video.srcObject = stream;
40
+ await new Promise((resolve, reject) => {
41
+ video.onloadedmetadata = () => resolve();
42
+ video.onerror = () => reject(/* @__PURE__ */ new Error("Failed to load screen stream"));
43
+ });
44
+ await video.play();
45
+ const width = video.videoWidth;
46
+ const height = video.videoHeight;
47
+ if (!width || !height) return null;
48
+ const canvas = document.createElement("canvas");
49
+ canvas.width = width;
50
+ canvas.height = height;
51
+ const context = canvas.getContext("2d");
52
+ if (!context) return null;
53
+ context.drawImage(video, 0, 0, width, height);
54
+ const blob = await new Promise((resolve) => {
55
+ canvas.toBlob(resolve, "image/png");
56
+ });
57
+ if (!blob) return null;
58
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replaceAll(/[:.]/g, "-").replace("T", "_").replace("Z", "");
59
+ return new File([blob], `screenshot-${timestamp}.png`, {
60
+ lastModified: Date.now(),
61
+ type: "image/png"
62
+ });
63
+ } finally {
64
+ if (stream) for (const track of stream.getTracks()) track.stop();
65
+ video.pause();
66
+ video.srcObject = null;
67
+ }
68
+ };
69
+ const PromptInputController = createContext(null);
70
+ const ProviderAttachmentsContext = createContext(null);
71
+ const usePromptInputController = () => {
72
+ const ctx = useContext(PromptInputController);
73
+ if (!ctx) throw new Error("Wrap your component inside <PromptInputProvider> to use usePromptInputController().");
74
+ return ctx;
75
+ };
76
+ const useOptionalPromptInputController = () => useContext(PromptInputController);
77
+ const useProviderAttachments = () => {
78
+ const ctx = useContext(ProviderAttachmentsContext);
79
+ if (!ctx) throw new Error("Wrap your component inside <PromptInputProvider> to use useProviderAttachments().");
80
+ return ctx;
81
+ };
82
+ const useOptionalProviderAttachments = () => useContext(ProviderAttachmentsContext);
83
+ /**
84
+ * Optional global provider that lifts PromptInput state outside of PromptInput.
85
+ * If you don't use it, PromptInput stays fully self-managed.
86
+ */
87
+ const PromptInputProvider = ({ initialInput: initialTextInput = "", children }) => {
88
+ const [textInput, setTextInput] = useState(initialTextInput);
89
+ const clearInput = useCallback(() => setTextInput(""), []);
90
+ const [attachmentFiles, setAttachmentFiles] = useState([]);
91
+ const fileInputRef = useRef(null);
92
+ const openRef = useRef(() => {});
93
+ const add = useCallback((files) => {
94
+ const incoming = [...files];
95
+ if (incoming.length === 0) return;
96
+ setAttachmentFiles((prev) => [...prev, ...incoming.map((file) => ({
97
+ filename: file.name,
98
+ id: nanoid(),
99
+ mediaType: file.type,
100
+ type: "file",
101
+ url: URL.createObjectURL(file)
102
+ }))]);
103
+ }, []);
104
+ const remove = useCallback((id) => {
105
+ setAttachmentFiles((prev) => {
106
+ const found = prev.find((f) => f.id === id);
107
+ if (found?.url) URL.revokeObjectURL(found.url);
108
+ return prev.filter((f) => f.id !== id);
109
+ });
110
+ }, []);
111
+ const clear = useCallback(() => {
112
+ setAttachmentFiles((prev) => {
113
+ for (const f of prev) if (f.url) URL.revokeObjectURL(f.url);
114
+ return [];
115
+ });
116
+ }, []);
117
+ const attachmentsRef = useRef(attachmentFiles);
118
+ useEffect(() => {
119
+ attachmentsRef.current = attachmentFiles;
120
+ }, [attachmentFiles]);
121
+ useEffect(() => () => {
122
+ for (const f of attachmentsRef.current) if (f.url) URL.revokeObjectURL(f.url);
123
+ }, []);
124
+ const openFileDialog = useCallback(() => {
125
+ openRef.current?.();
126
+ }, []);
127
+ const attachments = useMemo(() => ({
128
+ add,
129
+ clear,
130
+ fileInputRef,
131
+ files: attachmentFiles,
132
+ openFileDialog,
133
+ remove
134
+ }), [
135
+ attachmentFiles,
136
+ add,
137
+ remove,
138
+ clear,
139
+ openFileDialog
140
+ ]);
141
+ const __registerFileInput = useCallback((ref, open) => {
142
+ fileInputRef.current = ref.current;
143
+ openRef.current = open;
144
+ }, []);
145
+ const controller = useMemo(() => ({
146
+ __registerFileInput,
147
+ attachments,
148
+ textInput: {
149
+ clear: clearInput,
150
+ setInput: setTextInput,
151
+ value: textInput
152
+ }
153
+ }), [
154
+ textInput,
155
+ clearInput,
156
+ attachments,
157
+ __registerFileInput
158
+ ]);
159
+ return /* @__PURE__ */ jsx(PromptInputController.Provider, {
160
+ value: controller,
161
+ children: /* @__PURE__ */ jsx(ProviderAttachmentsContext.Provider, {
162
+ value: attachments,
163
+ children
164
+ })
165
+ });
166
+ };
167
+ const LocalAttachmentsContext = createContext(null);
168
+ const usePromptInputAttachments = () => {
169
+ const provider = useOptionalProviderAttachments();
170
+ const context = useContext(LocalAttachmentsContext) ?? provider;
171
+ if (!context) throw new Error("usePromptInputAttachments must be used within a PromptInput or PromptInputProvider");
172
+ return context;
173
+ };
174
+ const LocalReferencedSourcesContext = createContext(null);
175
+ const usePromptInputReferencedSources = () => {
176
+ const ctx = useContext(LocalReferencedSourcesContext);
177
+ if (!ctx) throw new Error("usePromptInputReferencedSources must be used within a LocalReferencedSourcesContext.Provider");
178
+ return ctx;
179
+ };
180
+ const PromptInputActionAddAttachments = ({ label = "Add photos or files", ...props }) => {
181
+ const attachments = usePromptInputAttachments();
182
+ const handleSelect = useCallback((e) => {
183
+ e.preventDefault();
184
+ attachments.openFileDialog();
185
+ }, [attachments]);
186
+ return /* @__PURE__ */ jsxs(DropdownMenuItem, {
187
+ ...props,
188
+ onSelect: handleSelect,
189
+ children: [
190
+ /* @__PURE__ */ jsx(ImageIcon, { className: "mr-2 size-4" }),
191
+ " ",
192
+ label
193
+ ]
194
+ });
195
+ };
196
+ const PromptInputActionAddScreenshot = ({ label = "Take screenshot", onSelect, ...props }) => {
197
+ const attachments = usePromptInputAttachments();
198
+ const handleSelect = useCallback(async (event) => {
199
+ onSelect?.(event);
200
+ if (event.defaultPrevented) return;
201
+ try {
202
+ const screenshot = await captureScreenshot();
203
+ if (screenshot) attachments.add([screenshot]);
204
+ } catch (error) {
205
+ if (error instanceof DOMException && (error.name === "NotAllowedError" || error.name === "AbortError")) return;
206
+ throw error;
207
+ }
208
+ }, [onSelect, attachments]);
209
+ return /* @__PURE__ */ jsxs(DropdownMenuItem, {
210
+ ...props,
211
+ onSelect: handleSelect,
212
+ children: [/* @__PURE__ */ jsx(Monitor, { className: "mr-2 size-4" }), label]
213
+ });
214
+ };
215
+ const PromptInput = ({ className, groupClassName, accept, multiple, globalDrop, syncHiddenInput, maxFiles, maxFileSize, onError, onSubmit, children, ...props }) => {
216
+ const controller = useOptionalPromptInputController();
217
+ const usingProvider = !!controller;
218
+ const inputRef = useRef(null);
219
+ const formRef = useRef(null);
220
+ const [items, setItems] = useState([]);
221
+ const files = usingProvider ? controller.attachments.files : items;
222
+ const [referencedSources, setReferencedSources] = useState([]);
223
+ const filesRef = useRef(files);
224
+ useEffect(() => {
225
+ filesRef.current = files;
226
+ }, [files]);
227
+ const openFileDialogLocal = useCallback(() => {
228
+ inputRef.current?.click();
229
+ }, []);
230
+ const matchesAccept = useCallback((f) => {
231
+ if (!accept || accept.trim() === "") return true;
232
+ return accept.split(",").map((s) => s.trim()).filter(Boolean).some((pattern) => {
233
+ if (pattern.endsWith("/*")) {
234
+ const prefix = pattern.slice(0, -1);
235
+ return f.type.startsWith(prefix);
236
+ }
237
+ return f.type === pattern;
238
+ });
239
+ }, [accept]);
240
+ const addLocal = useCallback((fileList) => {
241
+ const incoming = [...fileList];
242
+ const accepted = incoming.filter((f) => matchesAccept(f));
243
+ if (incoming.length && accepted.length === 0) {
244
+ onError?.({
245
+ code: "accept",
246
+ message: "No files match the accepted types."
247
+ });
248
+ return;
249
+ }
250
+ const withinSize = (f) => maxFileSize ? f.size <= maxFileSize : true;
251
+ const sized = accepted.filter(withinSize);
252
+ if (accepted.length > 0 && sized.length === 0) {
253
+ onError?.({
254
+ code: "max_file_size",
255
+ message: "All files exceed the maximum size."
256
+ });
257
+ return;
258
+ }
259
+ setItems((prev) => {
260
+ const capacity = typeof maxFiles === "number" ? Math.max(0, maxFiles - prev.length) : void 0;
261
+ const capped = typeof capacity === "number" ? sized.slice(0, capacity) : sized;
262
+ if (typeof capacity === "number" && sized.length > capacity) onError?.({
263
+ code: "max_files",
264
+ message: "Too many files. Some were not added."
265
+ });
266
+ const next = [];
267
+ for (const file of capped) next.push({
268
+ filename: file.name,
269
+ id: nanoid(),
270
+ mediaType: file.type,
271
+ type: "file",
272
+ url: URL.createObjectURL(file)
273
+ });
274
+ return [...prev, ...next];
275
+ });
276
+ }, [
277
+ matchesAccept,
278
+ maxFiles,
279
+ maxFileSize,
280
+ onError
281
+ ]);
282
+ const removeLocal = useCallback((id) => setItems((prev) => {
283
+ const found = prev.find((file) => file.id === id);
284
+ if (found?.url) URL.revokeObjectURL(found.url);
285
+ return prev.filter((file) => file.id !== id);
286
+ }), []);
287
+ const addWithProviderValidation = useCallback((fileList) => {
288
+ const incoming = [...fileList];
289
+ const accepted = incoming.filter((f) => matchesAccept(f));
290
+ if (incoming.length && accepted.length === 0) {
291
+ onError?.({
292
+ code: "accept",
293
+ message: "No files match the accepted types."
294
+ });
295
+ return;
296
+ }
297
+ const withinSize = (f) => maxFileSize ? f.size <= maxFileSize : true;
298
+ const sized = accepted.filter(withinSize);
299
+ if (accepted.length > 0 && sized.length === 0) {
300
+ onError?.({
301
+ code: "max_file_size",
302
+ message: "All files exceed the maximum size."
303
+ });
304
+ return;
305
+ }
306
+ const currentCount = files.length;
307
+ const capacity = typeof maxFiles === "number" ? Math.max(0, maxFiles - currentCount) : void 0;
308
+ const capped = typeof capacity === "number" ? sized.slice(0, capacity) : sized;
309
+ if (typeof capacity === "number" && sized.length > capacity) onError?.({
310
+ code: "max_files",
311
+ message: "Too many files. Some were not added."
312
+ });
313
+ if (capped.length > 0) controller?.attachments.add(capped);
314
+ }, [
315
+ matchesAccept,
316
+ maxFileSize,
317
+ maxFiles,
318
+ onError,
319
+ files.length,
320
+ controller
321
+ ]);
322
+ const clearAttachments = useCallback(() => usingProvider ? controller?.attachments.clear() : setItems((prev) => {
323
+ for (const file of prev) if (file.url) URL.revokeObjectURL(file.url);
324
+ return [];
325
+ }), [usingProvider, controller]);
326
+ const clearReferencedSources = useCallback(() => setReferencedSources([]), []);
327
+ const add = usingProvider ? addWithProviderValidation : addLocal;
328
+ const remove = usingProvider ? controller.attachments.remove : removeLocal;
329
+ const openFileDialog = usingProvider ? controller.attachments.openFileDialog : openFileDialogLocal;
330
+ const clear = useCallback(() => {
331
+ clearAttachments();
332
+ clearReferencedSources();
333
+ }, [clearAttachments, clearReferencedSources]);
334
+ useEffect(() => {
335
+ if (!usingProvider) return;
336
+ controller.__registerFileInput(inputRef, () => inputRef.current?.click());
337
+ }, [usingProvider, controller]);
338
+ useEffect(() => {
339
+ if (syncHiddenInput && inputRef.current && files.length === 0) inputRef.current.value = "";
340
+ }, [files, syncHiddenInput]);
341
+ useEffect(() => {
342
+ const form = formRef.current;
343
+ if (!form) return;
344
+ if (globalDrop) return;
345
+ const onDragOver = (e) => {
346
+ if (e.dataTransfer?.types?.includes("Files")) e.preventDefault();
347
+ };
348
+ const onDrop = (e) => {
349
+ if (e.dataTransfer?.types?.includes("Files")) e.preventDefault();
350
+ if (e.dataTransfer?.files && e.dataTransfer.files.length > 0) add(e.dataTransfer.files);
351
+ };
352
+ form.addEventListener("dragover", onDragOver);
353
+ form.addEventListener("drop", onDrop);
354
+ return () => {
355
+ form.removeEventListener("dragover", onDragOver);
356
+ form.removeEventListener("drop", onDrop);
357
+ };
358
+ }, [add, globalDrop]);
359
+ useEffect(() => {
360
+ if (!globalDrop) return;
361
+ const onDragOver = (e) => {
362
+ if (e.dataTransfer?.types?.includes("Files")) e.preventDefault();
363
+ };
364
+ const onDrop = (e) => {
365
+ if (e.dataTransfer?.types?.includes("Files")) e.preventDefault();
366
+ if (e.dataTransfer?.files && e.dataTransfer.files.length > 0) add(e.dataTransfer.files);
367
+ };
368
+ document.addEventListener("dragover", onDragOver);
369
+ document.addEventListener("drop", onDrop);
370
+ return () => {
371
+ document.removeEventListener("dragover", onDragOver);
372
+ document.removeEventListener("drop", onDrop);
373
+ };
374
+ }, [add, globalDrop]);
375
+ useEffect(() => () => {
376
+ if (!usingProvider) {
377
+ for (const f of filesRef.current) if (f.url) URL.revokeObjectURL(f.url);
378
+ }
379
+ }, [usingProvider]);
380
+ const handleChange = useCallback((event) => {
381
+ if (event.currentTarget.files) add(event.currentTarget.files);
382
+ event.currentTarget.value = "";
383
+ }, [add]);
384
+ const attachmentsCtx = useMemo(() => ({
385
+ add,
386
+ clear: clearAttachments,
387
+ fileInputRef: inputRef,
388
+ files: files.map((item) => ({
389
+ ...item,
390
+ id: item.id
391
+ })),
392
+ openFileDialog,
393
+ remove
394
+ }), [
395
+ files,
396
+ add,
397
+ remove,
398
+ clearAttachments,
399
+ openFileDialog
400
+ ]);
401
+ const refsCtx = useMemo(() => ({
402
+ add: (incoming) => {
403
+ const array = Array.isArray(incoming) ? incoming : [incoming];
404
+ setReferencedSources((prev) => [...prev, ...array.map((s) => ({
405
+ ...s,
406
+ id: nanoid()
407
+ }))]);
408
+ },
409
+ clear: clearReferencedSources,
410
+ remove: (id) => {
411
+ setReferencedSources((prev) => prev.filter((s) => s.id !== id));
412
+ },
413
+ sources: referencedSources
414
+ }), [referencedSources, clearReferencedSources]);
415
+ const handleSubmit = useCallback(async (event) => {
416
+ event.preventDefault();
417
+ const form = event.currentTarget;
418
+ const text = usingProvider ? controller.textInput.value : new FormData(form).get("message") || "";
419
+ if (!usingProvider) form.reset();
420
+ try {
421
+ const result = onSubmit({
422
+ files: await Promise.all(files.map(async ({ id: _id, ...item }) => {
423
+ if (item.url?.startsWith("blob:")) {
424
+ const dataUrl = await convertBlobUrlToDataUrl(item.url);
425
+ return {
426
+ ...item,
427
+ url: dataUrl ?? item.url
428
+ };
429
+ }
430
+ return item;
431
+ })),
432
+ text
433
+ }, event);
434
+ if (result instanceof Promise) try {
435
+ await result;
436
+ clear();
437
+ if (usingProvider) controller.textInput.clear();
438
+ } catch {}
439
+ else {
440
+ clear();
441
+ if (usingProvider) controller.textInput.clear();
442
+ }
443
+ } catch {}
444
+ }, [
445
+ usingProvider,
446
+ controller,
447
+ files,
448
+ onSubmit,
449
+ clear
450
+ ]);
451
+ const inner = /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("input", {
452
+ accept,
453
+ "aria-label": "Upload files",
454
+ className: "hidden",
455
+ multiple,
456
+ onChange: handleChange,
457
+ ref: inputRef,
458
+ title: "Upload files",
459
+ type: "file"
460
+ }), /* @__PURE__ */ jsx("form", {
461
+ className: cn("w-full", className),
462
+ onSubmit: handleSubmit,
463
+ ref: formRef,
464
+ ...props,
465
+ children: /* @__PURE__ */ jsx(InputGroup, {
466
+ className: cn("overflow-hidden bg-white dark:bg-white", groupClassName),
467
+ children
468
+ })
469
+ })] });
470
+ const withReferencedSources = /* @__PURE__ */ jsx(LocalReferencedSourcesContext.Provider, {
471
+ value: refsCtx,
472
+ children: inner
473
+ });
474
+ return /* @__PURE__ */ jsx(LocalAttachmentsContext.Provider, {
475
+ value: attachmentsCtx,
476
+ children: withReferencedSources
477
+ });
478
+ };
479
+ const PromptInputBody = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
480
+ className: cn("contents", className),
481
+ ...props
482
+ });
483
+ const PromptInputTextarea = ({ onChange, onKeyDown, className, placeholder = "What would you like to know?", ...props }) => {
484
+ const controller = useOptionalPromptInputController();
485
+ const attachments = usePromptInputAttachments();
486
+ const [isComposing, setIsComposing] = useState(false);
487
+ const handleKeyDown = useCallback((e) => {
488
+ onKeyDown?.(e);
489
+ if (e.defaultPrevented) return;
490
+ if (e.key === "Enter") {
491
+ if (isComposing || e.nativeEvent.isComposing) return;
492
+ if (e.shiftKey) return;
493
+ e.preventDefault();
494
+ const { form } = e.currentTarget;
495
+ if ((form?.querySelector("button[type=\"submit\"]"))?.disabled) return;
496
+ form?.requestSubmit();
497
+ }
498
+ if (e.key === "Backspace" && e.currentTarget.value === "" && attachments.files.length > 0) {
499
+ e.preventDefault();
500
+ const lastAttachment = attachments.files.at(-1);
501
+ if (lastAttachment) attachments.remove(lastAttachment.id);
502
+ }
503
+ }, [
504
+ onKeyDown,
505
+ isComposing,
506
+ attachments
507
+ ]);
508
+ const handlePaste = useCallback((event) => {
509
+ const items = event.clipboardData?.items;
510
+ if (!items) return;
511
+ const files = [];
512
+ for (const item of items) if (item.kind === "file") {
513
+ const file = item.getAsFile();
514
+ if (file) files.push(file);
515
+ }
516
+ if (files.length > 0) {
517
+ event.preventDefault();
518
+ attachments.add(files);
519
+ }
520
+ }, [attachments]);
521
+ const handleCompositionEnd = useCallback(() => setIsComposing(false), []);
522
+ const handleCompositionStart = useCallback(() => setIsComposing(true), []);
523
+ const controlledProps = controller ? {
524
+ onChange: (e) => {
525
+ controller.textInput.setInput(e.currentTarget.value);
526
+ onChange?.(e);
527
+ },
528
+ value: controller.textInput.value
529
+ } : { onChange };
530
+ return /* @__PURE__ */ jsx(InputGroupTextarea, {
531
+ className: cn("field-sizing-content max-h-48 min-h-16", className),
532
+ name: "message",
533
+ onCompositionEnd: handleCompositionEnd,
534
+ onCompositionStart: handleCompositionStart,
535
+ onKeyDown: handleKeyDown,
536
+ onPaste: handlePaste,
537
+ placeholder,
538
+ ...props,
539
+ ...controlledProps
540
+ });
541
+ };
542
+ const PromptInputHeader = ({ className, ...props }) => /* @__PURE__ */ jsx(InputGroupAddon, {
543
+ align: "block-end",
544
+ className: cn("order-first flex-wrap gap-1", className),
545
+ ...props
546
+ });
547
+ const PromptInputFooter = ({ className, ...props }) => /* @__PURE__ */ jsx(InputGroupAddon, {
548
+ align: "block-end",
549
+ className: cn("justify-between gap-1", className),
550
+ ...props
551
+ });
552
+ const PromptInputTools = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
553
+ className: cn("flex min-w-0 items-center gap-1", className),
554
+ ...props
555
+ });
556
+ const PromptInputButton = ({ variant = "ghost", className, size, tooltip, ...props }) => {
557
+ const newSize = size ?? (Children.count(props.children) > 1 ? "sm" : "icon-sm");
558
+ const button = /* @__PURE__ */ jsx(InputGroupButton, {
559
+ className: cn(className),
560
+ size: newSize,
561
+ type: "button",
562
+ variant,
563
+ ...props
564
+ });
565
+ if (!tooltip) return button;
566
+ const tooltipContent = typeof tooltip === "string" ? tooltip : tooltip.content;
567
+ const shortcut = typeof tooltip === "string" ? void 0 : tooltip.shortcut;
568
+ const side = typeof tooltip === "string" ? "top" : tooltip.side ?? "top";
569
+ return /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
570
+ asChild: true,
571
+ children: button
572
+ }), /* @__PURE__ */ jsxs(TooltipContent, {
573
+ side,
574
+ children: [tooltipContent, shortcut && /* @__PURE__ */ jsx("span", {
575
+ className: "ml-2 text-muted-foreground",
576
+ children: shortcut
577
+ })]
578
+ })] });
579
+ };
580
+ const PromptInputActionMenu = (props) => /* @__PURE__ */ jsx(DropdownMenu, { ...props });
581
+ const PromptInputActionMenuTrigger = ({ className, children, ...props }) => /* @__PURE__ */ jsx(DropdownMenuTrigger, {
582
+ asChild: true,
583
+ children: /* @__PURE__ */ jsx(PromptInputButton, {
584
+ className,
585
+ ...props,
586
+ children: children ?? /* @__PURE__ */ jsx(PlusIcon, { className: "size-4" })
587
+ })
588
+ });
589
+ const PromptInputActionMenuContent = ({ className, ...props }) => /* @__PURE__ */ jsx(DropdownMenuContent, {
590
+ align: "start",
591
+ className: cn(className),
592
+ ...props
593
+ });
594
+ const PromptInputActionMenuItem = ({ className, ...props }) => /* @__PURE__ */ jsx(DropdownMenuItem, {
595
+ className: cn(className),
596
+ ...props
597
+ });
598
+ const PromptInputSubmit = ({ className, variant = "default", size = "icon-sm", status, onStop, onClick, children, ...props }) => {
599
+ const isGenerating = status === "submitted" || status === "streaming";
600
+ let Icon = /* @__PURE__ */ jsx(ArrowUpIcon, { className: "size-4" });
601
+ if (status === "submitted") Icon = /* @__PURE__ */ jsx(Spinner, {});
602
+ else if (status === "streaming") Icon = /* @__PURE__ */ jsx(SquareIcon, { className: "size-4" });
603
+ else if (status === "error") Icon = /* @__PURE__ */ jsx(XIcon, { className: "size-4" });
604
+ const handleClick = useCallback((e) => {
605
+ if (isGenerating && onStop) {
606
+ e.preventDefault();
607
+ onStop();
608
+ return;
609
+ }
610
+ onClick?.(e);
611
+ }, [
612
+ isGenerating,
613
+ onStop,
614
+ onClick
615
+ ]);
616
+ return /* @__PURE__ */ jsx(InputGroupButton, {
617
+ "aria-label": isGenerating ? "Stop" : "Submit",
618
+ className: cn(className),
619
+ onClick: handleClick,
620
+ size,
621
+ type: isGenerating && onStop ? "button" : "submit",
622
+ variant,
623
+ ...props,
624
+ children: children ?? Icon
625
+ });
626
+ };
627
+ const PromptInputSelect = (props) => /* @__PURE__ */ jsx(Select, { ...props });
628
+ const PromptInputSelectTrigger = ({ className, ...props }) => /* @__PURE__ */ jsx(SelectTrigger, {
629
+ className: cn("border-none bg-transparent font-medium text-muted-foreground shadow-none transition-colors", "hover:bg-accent hover:text-foreground aria-expanded:bg-accent aria-expanded:text-foreground", className),
630
+ ...props
631
+ });
632
+ const PromptInputSelectContent = ({ className, ...props }) => /* @__PURE__ */ jsx(SelectContent, {
633
+ className: cn(className),
634
+ ...props
635
+ });
636
+ const PromptInputSelectItem = ({ className, ...props }) => /* @__PURE__ */ jsx(SelectItem, {
637
+ className: cn(className),
638
+ ...props
639
+ });
640
+ const PromptInputSelectValue = ({ className, ...props }) => /* @__PURE__ */ jsx(SelectValue, {
641
+ className: cn(className),
642
+ ...props
643
+ });
644
+ const PromptInputHoverCard = ({ openDelay = 0, closeDelay = 0, ...props }) => /* @__PURE__ */ jsx(HoverCard, {
645
+ closeDelay,
646
+ openDelay,
647
+ ...props
648
+ });
649
+ const PromptInputHoverCardTrigger = (props) => /* @__PURE__ */ jsx(HoverCardTrigger, { ...props });
650
+ const PromptInputHoverCardContent = ({ align = "start", ...props }) => /* @__PURE__ */ jsx(HoverCardContent, {
651
+ align,
652
+ ...props
653
+ });
654
+ const PromptInputTabsList = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
655
+ className: cn(className),
656
+ ...props
657
+ });
658
+ const PromptInputTab = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
659
+ className: cn(className),
660
+ ...props
661
+ });
662
+ const PromptInputTabLabel = ({ className, ...props }) => /* @__PURE__ */ jsx("h3", {
663
+ className: cn("mb-2 px-3 font-medium text-muted-foreground text-xs", className),
664
+ ...props
665
+ });
666
+ const PromptInputTabBody = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
667
+ className: cn("space-y-1", className),
668
+ ...props
669
+ });
670
+ const PromptInputTabItem = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
671
+ className: cn("flex items-center gap-2 px-3 py-2 text-xs hover:bg-accent", className),
672
+ ...props
673
+ });
674
+ const PromptInputCommand = ({ className, ...props }) => /* @__PURE__ */ jsx(Command, {
675
+ className: cn(className),
676
+ ...props
677
+ });
678
+ const PromptInputCommandInput = ({ className, ...props }) => /* @__PURE__ */ jsx(CommandInput, {
679
+ className: cn(className),
680
+ ...props
681
+ });
682
+ const PromptInputCommandList = ({ className, ...props }) => /* @__PURE__ */ jsx(CommandList, {
683
+ className: cn(className),
684
+ ...props
685
+ });
686
+ const PromptInputCommandEmpty = ({ className, ...props }) => /* @__PURE__ */ jsx(CommandEmpty, {
687
+ className: cn(className),
688
+ ...props
689
+ });
690
+ const PromptInputCommandGroup = ({ className, ...props }) => /* @__PURE__ */ jsx(CommandGroup, {
691
+ className: cn(className),
692
+ ...props
693
+ });
694
+ const PromptInputCommandItem = ({ className, ...props }) => /* @__PURE__ */ jsx(CommandItem, {
695
+ className: cn(className),
696
+ ...props
697
+ });
698
+ const PromptInputCommandSeparator = ({ className, ...props }) => /* @__PURE__ */ jsx(CommandSeparator, {
699
+ className: cn(className),
700
+ ...props
701
+ });
702
+ //#endregion
703
+ export { LocalReferencedSourcesContext, PromptInput, PromptInputActionAddAttachments, PromptInputActionAddScreenshot, PromptInputActionMenu, PromptInputActionMenuContent, PromptInputActionMenuItem, PromptInputActionMenuTrigger, PromptInputBody, PromptInputButton, PromptInputCommand, PromptInputCommandEmpty, PromptInputCommandGroup, PromptInputCommandInput, PromptInputCommandItem, PromptInputCommandList, PromptInputCommandSeparator, PromptInputFooter, PromptInputHeader, PromptInputHoverCard, PromptInputHoverCardContent, PromptInputHoverCardTrigger, PromptInputProvider, PromptInputSelect, PromptInputSelectContent, PromptInputSelectItem, PromptInputSelectTrigger, PromptInputSelectValue, PromptInputSubmit, PromptInputTab, PromptInputTabBody, PromptInputTabItem, PromptInputTabLabel, PromptInputTabsList, PromptInputTextarea, PromptInputTools, usePromptInputAttachments, usePromptInputController, usePromptInputReferencedSources, useProviderAttachments };