@mcp-b/react-components 0.1.1 → 0.1.3

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 (203) hide show
  1. package/dist/Button-QCM8ZKgR.d.ts +98 -0
  2. package/dist/CodeBlock-CttHhWcb.js +356 -0
  3. package/dist/CodeBlock-V6pgafSS.d.ts +174 -0
  4. package/dist/Select-CRsKCJ9x.d.ts +200 -0
  5. package/dist/class-names-9UtZwb0J.js +12 -0
  6. package/dist/components/Accordion.d.ts +81 -0
  7. package/dist/components/Accordion.js +97 -0
  8. package/dist/components/AgentConnection.d.ts +69 -0
  9. package/dist/components/AgentConnection.js +176 -0
  10. package/dist/components/AlertDialog.d.ts +148 -0
  11. package/dist/components/AlertDialog.js +141 -0
  12. package/dist/components/Artifact.d.ts +128 -0
  13. package/dist/components/Artifact.js +115 -0
  14. package/dist/components/Autocomplete.d.ts +186 -0
  15. package/dist/components/Autocomplete.js +186 -0
  16. package/dist/components/Avatar.d.ts +134 -0
  17. package/dist/components/Avatar.js +118 -0
  18. package/dist/components/Badge.d.ts +68 -0
  19. package/dist/components/Badge.js +108 -0
  20. package/dist/components/Button.d.ts +2 -0
  21. package/dist/components/Button.js +73 -0
  22. package/dist/components/Card.d.ts +87 -0
  23. package/dist/components/Card.js +81 -0
  24. package/dist/components/ChainOfThought.d.ts +153 -0
  25. package/dist/components/ChainOfThought.js +157 -0
  26. package/dist/components/Checkbox.d.ts +35 -0
  27. package/dist/components/Checkbox.js +47 -0
  28. package/dist/components/CheckboxGroup.d.ts +76 -0
  29. package/dist/components/CheckboxGroup.js +69 -0
  30. package/dist/components/CodeBlock.d.ts +2 -0
  31. package/dist/components/CodeBlock.js +2 -0
  32. package/dist/components/Collapsible.d.ts +127 -0
  33. package/dist/components/Collapsible.js +103 -0
  34. package/dist/components/Combobox.d.ts +177 -0
  35. package/dist/components/Combobox.js +179 -0
  36. package/dist/components/Commit.d.ts +265 -0
  37. package/dist/components/Commit.js +282 -0
  38. package/dist/components/Context.d.ts +129 -0
  39. package/dist/components/Context.js +309 -0
  40. package/dist/components/ContextMenu.d.ts +282 -0
  41. package/dist/components/ContextMenu.js +270 -0
  42. package/dist/components/Conversation.d.ts +166 -0
  43. package/dist/components/Conversation.js +202 -0
  44. package/dist/components/Dialog.d.ts +120 -0
  45. package/dist/components/Dialog.js +114 -0
  46. package/dist/components/EnvironmentVariables.d.ts +176 -0
  47. package/dist/components/EnvironmentVariables.js +221 -0
  48. package/dist/components/Field.d.ts +281 -0
  49. package/dist/components/Field.js +274 -0
  50. package/dist/components/Fieldset.d.ts +93 -0
  51. package/dist/components/Fieldset.js +81 -0
  52. package/dist/components/FileTree.d.ts +116 -0
  53. package/dist/components/FileTree.js +310 -0
  54. package/dist/components/Form.d.ts +94 -0
  55. package/dist/components/Form.js +88 -0
  56. package/dist/components/GithubMotionMark.d.ts +13 -0
  57. package/dist/components/GithubMotionMark.js +23 -0
  58. package/dist/components/Input.d.ts +33 -0
  59. package/dist/components/Input.js +27 -0
  60. package/dist/components/Label.d.ts +44 -0
  61. package/dist/components/Label.js +41 -0
  62. package/dist/components/Menu.d.ts +395 -0
  63. package/dist/components/Menu.js +338 -0
  64. package/dist/components/Menubar.d.ts +331 -0
  65. package/dist/components/Menubar.js +299 -0
  66. package/dist/components/Message.d.ts +216 -0
  67. package/dist/components/Message.js +245 -0
  68. package/dist/components/Meter.d.ts +151 -0
  69. package/dist/components/Meter.js +121 -0
  70. package/dist/components/ModelSelector.d.ts +198 -0
  71. package/dist/components/ModelSelector.js +306 -0
  72. package/dist/components/NaniteScene.d.ts +33 -0
  73. package/dist/components/NaniteScene.js +405 -0
  74. package/dist/components/NavigationMenu.d.ts +256 -0
  75. package/dist/components/NavigationMenu.js +228 -0
  76. package/dist/components/NumberField.d.ts +159 -0
  77. package/dist/components/NumberField.js +123 -0
  78. package/dist/components/Popover.d.ts +278 -0
  79. package/dist/components/Popover.js +245 -0
  80. package/dist/components/PreviewCard.d.ts +156 -0
  81. package/dist/components/PreviewCard.js +156 -0
  82. package/dist/components/PricingPage.d.ts +209 -0
  83. package/dist/components/PricingPage.js +506 -0
  84. package/dist/components/Progress.d.ts +58 -0
  85. package/dist/components/Progress.js +51 -0
  86. package/dist/components/PromptInput.d.ts +399 -0
  87. package/dist/components/PromptInput.js +737 -0
  88. package/dist/components/Queue.d.ts +241 -0
  89. package/dist/components/Queue.js +225 -0
  90. package/dist/components/RadioGroup.d.ts +78 -0
  91. package/dist/components/RadioGroup.js +72 -0
  92. package/dist/components/Reasoning.d.ts +88 -0
  93. package/dist/components/Reasoning.js +187 -0
  94. package/dist/components/ScrollArea.d.ts +160 -0
  95. package/dist/components/ScrollArea.js +145 -0
  96. package/dist/components/Select.d.ts +2 -0
  97. package/dist/components/Select.js +260 -0
  98. package/dist/components/Separator.d.ts +45 -0
  99. package/dist/components/Separator.js +40 -0
  100. package/dist/components/Shell.d.ts +163 -0
  101. package/dist/components/Shell.js +137 -0
  102. package/dist/components/Slider.d.ts +120 -0
  103. package/dist/components/Slider.js +115 -0
  104. package/dist/components/Sources.d.ts +91 -0
  105. package/dist/components/Sources.js +121 -0
  106. package/dist/components/Stepper.d.ts +190 -0
  107. package/dist/components/Stepper.js +315 -0
  108. package/dist/components/Switch.d.ts +57 -0
  109. package/dist/components/Switch.js +55 -0
  110. package/dist/components/Tabs.d.ts +84 -0
  111. package/dist/components/Tabs.js +78 -0
  112. package/dist/components/Task.d.ts +100 -0
  113. package/dist/components/Task.js +120 -0
  114. package/dist/components/Terminal.d.ts +118 -0
  115. package/dist/components/Terminal.js +234 -0
  116. package/dist/components/TestResults.d.ts +236 -0
  117. package/dist/components/TestResults.js +346 -0
  118. package/dist/components/Toast.d.ts +192 -0
  119. package/dist/components/Toast.js +163 -0
  120. package/dist/components/Toggle.d.ts +69 -0
  121. package/dist/components/Toggle.js +66 -0
  122. package/dist/components/ToggleGroup.d.ts +69 -0
  123. package/dist/components/ToggleGroup.js +99 -0
  124. package/dist/components/Tool.d.ts +119 -0
  125. package/dist/components/Tool.js +192 -0
  126. package/dist/components/Toolbar.d.ts +162 -0
  127. package/dist/components/Toolbar.js +126 -0
  128. package/dist/components/Tooltip.d.ts +81 -0
  129. package/dist/components/Tooltip.js +74 -0
  130. package/dist/components/WebPreview.d.ts +143 -0
  131. package/dist/components/WebPreview.js +280 -0
  132. package/dist/components/useStickToBottom.d.ts +30 -0
  133. package/dist/components/useStickToBottom.js +95 -0
  134. package/dist/copy-button-BIATcZWZ.d.ts +16 -0
  135. package/dist/copy-button-BNgclDrO.js +43 -0
  136. package/dist/icons-BsWc8g4E.js +221 -0
  137. package/dist/styles/accordion.css +10 -10
  138. package/dist/styles/agent-connection.css +18 -17
  139. package/dist/styles/alert-dialog.css +48 -29
  140. package/dist/styles/artifact.css +10 -10
  141. package/dist/styles/autocomplete.css +25 -24
  142. package/dist/styles/avatar.css +96 -6
  143. package/dist/styles/badge.css +78 -14
  144. package/dist/styles/base.css +9 -9
  145. package/dist/styles/button.css +57 -38
  146. package/dist/styles/card.css +57 -10
  147. package/dist/styles/chain-of-thought.css +47 -33
  148. package/dist/styles/checkbox.css +24 -10
  149. package/dist/styles/code-block.css +22 -15
  150. package/dist/styles/collapsible.css +14 -12
  151. package/dist/styles/combobox.css +26 -25
  152. package/dist/styles/commit.css +49 -24
  153. package/dist/styles/context-menu.css +70 -16
  154. package/dist/styles/context.css +15 -15
  155. package/dist/styles/conversation.css +12 -12
  156. package/dist/styles/dialog.css +52 -16
  157. package/dist/styles/environment-variables.css +19 -19
  158. package/dist/styles/field.css +184 -22
  159. package/dist/styles/fieldset.css +5 -4
  160. package/dist/styles/file-tree.css +17 -17
  161. package/dist/styles/index.css.d.ts +1 -0
  162. package/dist/styles/input.css +45 -11
  163. package/dist/styles/label.css +21 -4
  164. package/dist/styles/menu.css +55 -32
  165. package/dist/styles/menubar.css +80 -28
  166. package/dist/styles/message.css +22 -13
  167. package/dist/styles/meter.css +8 -8
  168. package/dist/styles/model-selector.css +33 -21
  169. package/dist/styles/navigation-menu.css +26 -24
  170. package/dist/styles/number-field.css +20 -18
  171. package/dist/styles/popover.css +38 -28
  172. package/dist/styles/preview-card.css +15 -13
  173. package/dist/styles/pricing-page.css +53 -53
  174. package/dist/styles/progress.css +7 -7
  175. package/dist/styles/prompt-input.css +73 -24
  176. package/dist/styles/queue.css +58 -32
  177. package/dist/styles/radio.css +26 -11
  178. package/dist/styles/reasoning.css +15 -11
  179. package/dist/styles/scroll-area.css +6 -6
  180. package/dist/styles/scrollbars.css +4 -4
  181. package/dist/styles/select.css +84 -25
  182. package/dist/styles/separator.css +1 -1
  183. package/dist/styles/shell.css +21 -20
  184. package/dist/styles/slider.css +35 -11
  185. package/dist/styles/sources.css +20 -20
  186. package/dist/styles/stepper.css +22 -22
  187. package/dist/styles/switch.css +29 -13
  188. package/dist/styles/tabs.css +23 -16
  189. package/dist/styles/task.css +27 -23
  190. package/dist/styles/terminal.css +16 -13
  191. package/dist/styles/test-results.css +79 -32
  192. package/dist/styles/toast.css +26 -25
  193. package/dist/styles/toggle-group.css +12 -12
  194. package/dist/styles/toggle.css +29 -11
  195. package/dist/styles/tool.css +54 -32
  196. package/dist/styles/toolbar.css +30 -28
  197. package/dist/styles/tooltip.css +10 -8
  198. package/dist/styles/web-preview.css +37 -21
  199. package/dist/utils/structured-code.d.ts +10 -0
  200. package/dist/utils/structured-code.js +81 -0
  201. package/package.json +17 -8
  202. package/dist/index.d.ts +0 -7236
  203. package/dist/index.js +0 -8945
@@ -0,0 +1,737 @@
1
+ import { C as SpinnerIcon, E as WarningIcon, _ as ImageIcon, b as PlusIcon, i as ArrowUpIcon, w as SquareIcon, y as MonitorIcon } from "../icons-BsWc8g4E.js";
2
+ import { Select, SelectContent, SelectItem, SelectPortal, SelectPositioner, SelectTrigger, SelectValue } from "./Select.js";
3
+ import { t as cx } from "../class-names-9UtZwb0J.js";
4
+ import { Button } from "./Button.js";
5
+ import { Tooltip, TooltipPopup, TooltipPortal, TooltipPositioner, TooltipTrigger } from "./Tooltip.js";
6
+ import { Combobox, ComboboxEmpty, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxList, ComboboxSeparator } from "./Combobox.js";
7
+ import { MenuItem, MenuPopup, MenuPortal, MenuPositioner, MenuRoot, MenuTrigger } from "./Menu.js";
8
+ import { Tab, TabPanel, TabsList } from "./Tabs.js";
9
+ import * as React from "react";
10
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
11
+ //#region src/components/PromptInput.tsx
12
+ const PromptInputContext = React.createContext(null);
13
+ const PromptInputController = React.createContext(null);
14
+ const ProviderAttachmentsContext = React.createContext(null);
15
+ const LocalAttachmentsContext = React.createContext(null);
16
+ const LocalReferencedSourcesContext = React.createContext(null);
17
+ function usePromptInputContext(component) {
18
+ const ctx = React.useContext(PromptInputContext);
19
+ if (!ctx) throw new Error(`${component} must be used inside a <PromptInput>.`);
20
+ return ctx;
21
+ }
22
+ function usePromptInputController() {
23
+ const ctx = React.useContext(PromptInputController);
24
+ if (!ctx) throw new Error("Wrap your component inside <PromptInputProvider> to use usePromptInputController().");
25
+ return ctx;
26
+ }
27
+ function useOptionalPromptInputController() {
28
+ return React.useContext(PromptInputController);
29
+ }
30
+ function useProviderAttachments() {
31
+ const ctx = React.useContext(ProviderAttachmentsContext);
32
+ if (!ctx) throw new Error("Wrap your component inside <PromptInputProvider> to use useProviderAttachments().");
33
+ return ctx;
34
+ }
35
+ function useOptionalProviderAttachments() {
36
+ return React.useContext(ProviderAttachmentsContext);
37
+ }
38
+ function usePromptInputAttachments() {
39
+ const provider = useOptionalProviderAttachments();
40
+ const context = React.useContext(LocalAttachmentsContext) ?? provider;
41
+ if (!context) throw new Error("usePromptInputAttachments must be used within a PromptInput or PromptInputProvider.");
42
+ return context;
43
+ }
44
+ function usePromptInputReferencedSources() {
45
+ const ctx = React.useContext(LocalReferencedSourcesContext);
46
+ if (!ctx) throw new Error("usePromptInputReferencedSources must be used within a PromptInput.");
47
+ return ctx;
48
+ }
49
+ function PromptInputProvider({ initialInput = "", children }) {
50
+ const [textInput, setTextInput] = React.useState(initialInput);
51
+ const [attachmentFiles, setAttachmentFiles] = React.useState([]);
52
+ const fileInputRef = React.useRef(null);
53
+ const openRef = React.useRef(() => {});
54
+ const attachmentsRef = React.useRef(attachmentFiles);
55
+ attachmentsRef.current = attachmentFiles;
56
+ React.useEffect(() => () => {
57
+ revokeFileUrls(attachmentsRef.current);
58
+ }, []);
59
+ const add = React.useCallback((files) => {
60
+ const incoming = Array.from(files);
61
+ setAttachmentFiles((prev) => [...prev, ...incoming.map(fileToPart)]);
62
+ }, []);
63
+ const remove = React.useCallback((id) => {
64
+ setAttachmentFiles((prev) => {
65
+ const found = prev.find((file) => file.id === id);
66
+ if (found?.url) URL.revokeObjectURL(found.url);
67
+ return prev.filter((file) => file.id !== id);
68
+ });
69
+ }, []);
70
+ const clear = React.useCallback(() => {
71
+ setAttachmentFiles((prev) => {
72
+ revokeFileUrls(prev);
73
+ return [];
74
+ });
75
+ }, []);
76
+ const attachments = React.useMemo(() => ({
77
+ add,
78
+ clear,
79
+ fileInputRef,
80
+ files: attachmentFiles,
81
+ openFileDialog: () => openRef.current(),
82
+ remove
83
+ }), [
84
+ add,
85
+ attachmentFiles,
86
+ clear,
87
+ remove
88
+ ]);
89
+ const controller = React.useMemo(() => ({
90
+ __registerFileInput: (ref, open) => {
91
+ fileInputRef.current = ref.current;
92
+ openRef.current = open;
93
+ },
94
+ attachments,
95
+ textInput: {
96
+ value: textInput,
97
+ setInput: setTextInput,
98
+ clear: () => setTextInput("")
99
+ }
100
+ }), [attachments, textInput]);
101
+ return /* @__PURE__ */ jsx(PromptInputController.Provider, {
102
+ value: controller,
103
+ children: /* @__PURE__ */ jsx(ProviderAttachmentsContext.Provider, {
104
+ value: attachments,
105
+ children
106
+ })
107
+ });
108
+ }
109
+ /**
110
+ * Chat-style composer form. Wraps a textarea and footer controls, with
111
+ * Enter-to-submit semantics, auto-resizing behavior, and optional attachments.
112
+ * Adapted from Vercel AI Elements and restyled with Sigvelo CSS tokens.
113
+ *
114
+ * Use for assistant/chat submit forms. Do not use it as a generic textarea or
115
+ * non-chat form. Sigvelo changes: uses shared Menu, Select, Combobox, Tabs,
116
+ * Button, and Tooltip components; maps styles to CSS tokens; adds local
117
+ * attachment and referenced-source helpers.
118
+ *
119
+ * The form handles Enter-to-submit while multiline editing remains in the
120
+ * textarea. Attachment controls need accessible labels through their buttons
121
+ * and menus. Add new shared tools here when multiple apps need them.
122
+ *
123
+ * @example
124
+ * ```tsx
125
+ * <PromptInput onSubmit={({ text, files }) => sendMessage(text, files)}>
126
+ * <PromptInputBody>
127
+ * <PromptInputTextarea placeholder="Ask Sigvelo..." />
128
+ * </PromptInputBody>
129
+ * <PromptInputFooter>
130
+ * <PromptInputTools>
131
+ * <PromptInputActionAddAttachments />
132
+ * </PromptInputTools>
133
+ * <PromptInputSubmit status="ready" />
134
+ * </PromptInputFooter>
135
+ * </PromptInput>
136
+ * ```
137
+ *
138
+ * @see {@link https://elements.ai-sdk.dev/components/prompt-input | AI Elements Prompt Input}
139
+ */
140
+ function PromptInput({ className, accept, multiple, globalDrop, syncHiddenInput, maxFiles, maxFileSize, onError, onSubmit, children, id, ref, ...props }) {
141
+ const generatedId = React.useId();
142
+ const formId = id ?? generatedId;
143
+ const controller = useOptionalPromptInputController();
144
+ const usingProvider = Boolean(controller);
145
+ const formRef = React.useRef(null);
146
+ const inputRef = React.useRef(null);
147
+ const [localFiles, setLocalFiles] = React.useState([]);
148
+ const [referencedSources, setReferencedSources] = React.useState([]);
149
+ const files = controller?.attachments.files ?? localFiles;
150
+ const filesRef = React.useRef(files);
151
+ filesRef.current = files;
152
+ React.useImperativeHandle(ref, () => formRef.current, []);
153
+ React.useEffect(() => () => {
154
+ if (!usingProvider) revokeFileUrls(filesRef.current);
155
+ }, [usingProvider]);
156
+ const matchesAccept = React.useCallback((file) => {
157
+ if (!accept?.trim()) return true;
158
+ return accept.split(",").map((pattern) => pattern.trim()).filter(Boolean).some((pattern) => pattern.endsWith("/*") ? file.type.startsWith(pattern.slice(0, -1)) : file.type === pattern);
159
+ }, [accept]);
160
+ const add = React.useCallback((fileList) => {
161
+ const incoming = Array.from(fileList);
162
+ const accepted = incoming.filter(matchesAccept);
163
+ if (incoming.length && accepted.length === 0) {
164
+ onError?.({
165
+ code: "accept",
166
+ message: "No files match the accepted types."
167
+ });
168
+ return;
169
+ }
170
+ const sized = accepted.filter((file) => !maxFileSize || file.size <= maxFileSize);
171
+ if (accepted.length && sized.length === 0) {
172
+ onError?.({
173
+ code: "max_file_size",
174
+ message: "All files exceed the maximum size."
175
+ });
176
+ return;
177
+ }
178
+ const capacity = typeof maxFiles === "number" ? Math.max(0, maxFiles - filesRef.current.length) : void 0;
179
+ const capped = typeof capacity === "number" ? sized.slice(0, capacity) : sized;
180
+ if (typeof capacity === "number" && sized.length > capacity) onError?.({
181
+ code: "max_files",
182
+ message: "Too many files. Some were not added."
183
+ });
184
+ if (!capped.length) return;
185
+ if (controller) controller.attachments.add(capped);
186
+ else setLocalFiles((prev) => [...prev, ...capped.map(fileToPart)]);
187
+ }, [
188
+ controller,
189
+ matchesAccept,
190
+ maxFileSize,
191
+ maxFiles,
192
+ onError
193
+ ]);
194
+ const remove = React.useCallback((attachmentId) => {
195
+ if (controller) {
196
+ controller.attachments.remove(attachmentId);
197
+ return;
198
+ }
199
+ setLocalFiles((prev) => {
200
+ const found = prev.find((file) => file.id === attachmentId);
201
+ if (found?.url) URL.revokeObjectURL(found.url);
202
+ return prev.filter((file) => file.id !== attachmentId);
203
+ });
204
+ }, [controller]);
205
+ const clearAttachments = React.useCallback(() => {
206
+ if (controller) {
207
+ controller.attachments.clear();
208
+ return;
209
+ }
210
+ setLocalFiles((prev) => {
211
+ revokeFileUrls(prev);
212
+ return [];
213
+ });
214
+ }, [controller]);
215
+ const clearReferencedSources = React.useCallback(() => setReferencedSources([]), []);
216
+ const clear = React.useCallback(() => {
217
+ clearAttachments();
218
+ clearReferencedSources();
219
+ }, [clearAttachments, clearReferencedSources]);
220
+ const openFileDialog = React.useCallback(() => inputRef.current?.click(), []);
221
+ React.useEffect(() => {
222
+ if (!controller) return;
223
+ controller.__registerFileInput(inputRef, openFileDialog);
224
+ }, [controller, openFileDialog]);
225
+ React.useEffect(() => {
226
+ if (syncHiddenInput && inputRef.current && files.length === 0) inputRef.current.value = "";
227
+ }, [files.length, syncHiddenInput]);
228
+ React.useEffect(() => {
229
+ const target = globalDrop ? document : formRef.current;
230
+ if (!target) return;
231
+ const onDragOver = (event) => {
232
+ const dragEvent = event;
233
+ if (dragEvent.dataTransfer?.types.includes("Files")) dragEvent.preventDefault();
234
+ };
235
+ const onDrop = (event) => {
236
+ const dragEvent = event;
237
+ if (dragEvent.dataTransfer?.types.includes("Files")) dragEvent.preventDefault();
238
+ if (dragEvent.dataTransfer?.files?.length) add(dragEvent.dataTransfer.files);
239
+ };
240
+ target.addEventListener("dragover", onDragOver);
241
+ target.addEventListener("drop", onDrop);
242
+ return () => {
243
+ target.removeEventListener("dragover", onDragOver);
244
+ target.removeEventListener("drop", onDrop);
245
+ };
246
+ }, [add, globalDrop]);
247
+ const attachmentsCtx = React.useMemo(() => ({
248
+ add,
249
+ clear: clearAttachments,
250
+ fileInputRef: inputRef,
251
+ files,
252
+ openFileDialog,
253
+ remove
254
+ }), [
255
+ add,
256
+ clearAttachments,
257
+ files,
258
+ openFileDialog,
259
+ remove
260
+ ]);
261
+ const referencedSourcesCtx = React.useMemo(() => ({
262
+ add: (incoming) => {
263
+ const array = Array.isArray(incoming) ? incoming : [incoming];
264
+ setReferencedSources((prev) => [...prev, ...array.map((source) => ({
265
+ ...source,
266
+ id: source.id ?? createId()
267
+ }))]);
268
+ },
269
+ clear: clearReferencedSources,
270
+ remove: (sourceId) => setReferencedSources((prev) => prev.filter((source) => source.id !== sourceId)),
271
+ sources: referencedSources
272
+ }), [clearReferencedSources, referencedSources]);
273
+ const submit = React.useCallback(() => {
274
+ formRef.current?.requestSubmit();
275
+ }, []);
276
+ const ctx = React.useMemo(() => ({
277
+ formId,
278
+ submit
279
+ }), [formId, submit]);
280
+ const handleSubmit = React.useCallback(async (event) => {
281
+ event.preventDefault();
282
+ const textarea = event.currentTarget.querySelector("textarea[data-prompt-input-textarea=\"true\"]");
283
+ const text = controller?.textInput.value ?? textarea?.value.trim() ?? "";
284
+ const submittedFiles = await Promise.all(files.map(resolveSubmittedFile));
285
+ try {
286
+ await onSubmit?.({
287
+ text,
288
+ files: submittedFiles
289
+ }, event);
290
+ if (!controller && textarea) {
291
+ textarea.value = "";
292
+ textarea.dispatchEvent(new Event("input", { bubbles: true }));
293
+ }
294
+ controller?.textInput.clear();
295
+ clear();
296
+ } catch {}
297
+ }, [
298
+ clear,
299
+ controller,
300
+ files,
301
+ onSubmit
302
+ ]);
303
+ return /* @__PURE__ */ jsx(PromptInputContext.Provider, {
304
+ value: ctx,
305
+ children: /* @__PURE__ */ jsx(LocalAttachmentsContext.Provider, {
306
+ value: attachmentsCtx,
307
+ children: /* @__PURE__ */ jsxs(LocalReferencedSourcesContext.Provider, {
308
+ value: referencedSourcesCtx,
309
+ children: [/* @__PURE__ */ jsx("input", {
310
+ ref: inputRef,
311
+ accept,
312
+ "aria-label": "Upload files",
313
+ className: "prompt-input__file-input",
314
+ multiple,
315
+ onChange: (event) => {
316
+ if (event.currentTarget.files?.length) add(event.currentTarget.files);
317
+ event.currentTarget.value = "";
318
+ },
319
+ tabIndex: -1,
320
+ type: "file"
321
+ }), /* @__PURE__ */ jsx("form", {
322
+ ref: formRef,
323
+ id: formId,
324
+ className: cx("prompt-input", className),
325
+ onSubmit: handleSubmit,
326
+ ...props,
327
+ children
328
+ })]
329
+ })
330
+ })
331
+ });
332
+ }
333
+ function PromptInputHeader({ className, children, ref, ...props }) {
334
+ return /* @__PURE__ */ jsx("div", {
335
+ ref,
336
+ className: cx("prompt-input__header", className),
337
+ ...props,
338
+ children
339
+ });
340
+ }
341
+ function PromptInputBody({ className, children, ref, ...props }) {
342
+ return /* @__PURE__ */ jsx("div", {
343
+ ref,
344
+ className: cx("prompt-input__body", className),
345
+ ...props,
346
+ children
347
+ });
348
+ }
349
+ function PromptInputFooter({ className, children, ref, ...props }) {
350
+ return /* @__PURE__ */ jsx("div", {
351
+ ref,
352
+ className: cx("prompt-input__footer", className),
353
+ ...props,
354
+ children
355
+ });
356
+ }
357
+ function PromptInputTools({ className, children, ref, ...props }) {
358
+ return /* @__PURE__ */ jsx("div", {
359
+ ref,
360
+ className: cx("prompt-input__tools", className),
361
+ ...props,
362
+ children
363
+ });
364
+ }
365
+ const MIN_TEXTAREA_HEIGHT_PX = 48;
366
+ const MAX_TEXTAREA_HEIGHT_PX = 240;
367
+ function PromptInputTextarea({ className, placeholder = "How can I help?", rows = 1, minHeight = MIN_TEXTAREA_HEIGHT_PX, maxHeight = MAX_TEXTAREA_HEIGHT_PX, onChange, onCompositionEnd, onCompositionStart, onKeyDown, onInput, onPaste, ref, ...props }) {
368
+ const { submit } = usePromptInputContext("PromptInputTextarea");
369
+ const controller = useOptionalPromptInputController();
370
+ const attachments = usePromptInputAttachments();
371
+ const innerRef = React.useRef(null);
372
+ const [isComposing, setIsComposing] = React.useState(false);
373
+ React.useImperativeHandle(ref, () => innerRef.current, []);
374
+ const resize = React.useCallback(() => {
375
+ const el = innerRef.current;
376
+ if (!el) return;
377
+ el.style.height = "auto";
378
+ const next = Math.min(Math.max(el.scrollHeight, minHeight), maxHeight);
379
+ el.style.height = `${next}px`;
380
+ el.style.overflowY = el.scrollHeight > maxHeight ? "auto" : "hidden";
381
+ }, [maxHeight, minHeight]);
382
+ React.useLayoutEffect(() => {
383
+ resize();
384
+ }, [
385
+ resize,
386
+ props.value,
387
+ props.defaultValue,
388
+ controller?.textInput.value
389
+ ]);
390
+ const controlledProps = controller ? {
391
+ value: controller.textInput.value,
392
+ onChange: (event) => {
393
+ controller.textInput.setInput(event.currentTarget.value);
394
+ onChange?.(event);
395
+ }
396
+ } : { onChange };
397
+ return /* @__PURE__ */ jsx("textarea", {
398
+ ref: innerRef,
399
+ "data-prompt-input-textarea": "true",
400
+ className: cx("prompt-input__textarea", className),
401
+ name: "message",
402
+ placeholder,
403
+ rows,
404
+ onCompositionEnd: (event) => {
405
+ setIsComposing(false);
406
+ onCompositionEnd?.(event);
407
+ },
408
+ onCompositionStart: (event) => {
409
+ setIsComposing(true);
410
+ onCompositionStart?.(event);
411
+ },
412
+ onKeyDown: (event) => {
413
+ onKeyDown?.(event);
414
+ if (event.defaultPrevented) return;
415
+ if (event.key === "Enter" && !event.shiftKey && !isComposing && !event.nativeEvent.isComposing) {
416
+ event.preventDefault();
417
+ submit();
418
+ }
419
+ if (event.key === "Backspace" && event.currentTarget.value === "" && attachments.files.length > 0) {
420
+ event.preventDefault();
421
+ const last = attachments.files.at(-1);
422
+ if (last) attachments.remove(last.id);
423
+ }
424
+ },
425
+ onInput: (event) => {
426
+ onInput?.(event);
427
+ resize();
428
+ },
429
+ onPaste: (event) => {
430
+ onPaste?.(event);
431
+ if (event.defaultPrevented) return;
432
+ const files = Array.from(event.clipboardData.items).filter((item) => item.kind === "file").map((item) => item.getAsFile()).filter((file) => Boolean(file));
433
+ if (files.length) {
434
+ event.preventDefault();
435
+ attachments.add(files);
436
+ }
437
+ },
438
+ style: {
439
+ minHeight,
440
+ maxHeight,
441
+ ...props.style
442
+ },
443
+ ...props,
444
+ ...controlledProps
445
+ });
446
+ }
447
+ function PromptInputButton({ className, tooltip, variant = "ghost", color = "neutral", size, children, "aria-label": ariaLabel, ref, ...props }) {
448
+ const resolvedSize = size ?? (React.Children.count(children) > 1 ? "sm" : "icon");
449
+ const tooltipConfig = typeof tooltip === "string" ? { content: tooltip } : tooltip;
450
+ const fallbackLabel = ariaLabel ?? (typeof tooltipConfig?.content === "string" ? tooltipConfig.content : void 0);
451
+ const button = /* @__PURE__ */ jsx(Button, {
452
+ ref,
453
+ type: "button",
454
+ variant,
455
+ color,
456
+ size: resolvedSize,
457
+ className: cx("prompt-input__button", className),
458
+ "aria-label": fallbackLabel,
459
+ ...props,
460
+ children
461
+ });
462
+ if (!tooltipConfig) return button;
463
+ return /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, { render: button }), /* @__PURE__ */ jsx(TooltipPortal, { children: /* @__PURE__ */ jsx(TooltipPositioner, {
464
+ side: tooltipConfig.side,
465
+ sideOffset: 6,
466
+ children: /* @__PURE__ */ jsxs(TooltipPopup, { children: [/* @__PURE__ */ jsx("span", {
467
+ className: "prompt-input__tooltip-content",
468
+ children: tooltipConfig.content
469
+ }), tooltipConfig.shortcut ? /* @__PURE__ */ jsx("kbd", {
470
+ className: "prompt-input__tooltip-shortcut",
471
+ children: tooltipConfig.shortcut
472
+ }) : null] })
473
+ }) })] });
474
+ }
475
+ function PromptInputActionMenu(props) {
476
+ return /* @__PURE__ */ jsx(MenuRoot, { ...props });
477
+ }
478
+ function PromptInputActionMenuTrigger({ className, children, ...props }) {
479
+ return /* @__PURE__ */ jsx(MenuTrigger, { render: /* @__PURE__ */ jsx(PromptInputButton, {
480
+ className,
481
+ ...props,
482
+ children: children ?? /* @__PURE__ */ jsx(PlusIcon, {})
483
+ }) });
484
+ }
485
+ function PromptInputActionMenuContent({ className, children, ...props }) {
486
+ return /* @__PURE__ */ jsx(MenuPortal, { children: /* @__PURE__ */ jsx(MenuPositioner, { children: /* @__PURE__ */ jsx(MenuPopup, {
487
+ className: cx("prompt-input__action-menu", className),
488
+ ...props,
489
+ children
490
+ }) }) });
491
+ }
492
+ function PromptInputActionMenuItem({ className, ...props }) {
493
+ return /* @__PURE__ */ jsx(MenuItem, {
494
+ className: cx("prompt-input__action-menu-item", className),
495
+ ...props
496
+ });
497
+ }
498
+ function PromptInputActionAddAttachments({ label = "Add photos or files", onClick, children, ...props }) {
499
+ const attachments = usePromptInputAttachments();
500
+ return /* @__PURE__ */ jsx(PromptInputActionMenuItem, {
501
+ ...props,
502
+ onClick: (event) => {
503
+ onClick?.(event);
504
+ if (!event.defaultPrevented) attachments.openFileDialog();
505
+ },
506
+ children: children ?? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(ImageIcon, { className: "prompt-input__action-menu-icon" }), label] })
507
+ });
508
+ }
509
+ function PromptInputActionAddScreenshot({ label = "Take screenshot", onClick, children, ...props }) {
510
+ const attachments = usePromptInputAttachments();
511
+ return /* @__PURE__ */ jsx(PromptInputActionMenuItem, {
512
+ ...props,
513
+ onClick: async (event) => {
514
+ onClick?.(event);
515
+ if (event.defaultPrevented) return;
516
+ try {
517
+ const screenshot = await captureScreenshot();
518
+ if (screenshot) attachments.add([screenshot]);
519
+ } catch (error) {
520
+ if (error instanceof DOMException && (error.name === "NotAllowedError" || error.name === "AbortError")) return;
521
+ throw error;
522
+ }
523
+ },
524
+ children: children ?? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(MonitorIcon, { className: "prompt-input__action-menu-icon" }), label] })
525
+ });
526
+ }
527
+ function PromptInputSubmit({ className, status = "ready", onStop, variant = "normal", color = "primary", size = "icon", onClick, children, label, ref, ...props }) {
528
+ const isStopping = status === "submitted" || status === "streaming";
529
+ const resolvedLabel = label ?? defaultSubmitLabel(status);
530
+ return /* @__PURE__ */ jsx(Button, {
531
+ ref,
532
+ type: isStopping ? "button" : "submit",
533
+ variant,
534
+ color,
535
+ size,
536
+ className: cx("prompt-input__submit", className),
537
+ "data-status": status,
538
+ "aria-label": resolvedLabel,
539
+ onClick: (event) => {
540
+ onClick?.(event);
541
+ if (event.defaultPrevented) return;
542
+ if (isStopping) {
543
+ event.preventDefault();
544
+ onStop?.();
545
+ }
546
+ },
547
+ ...props,
548
+ children: children ?? defaultSubmitIcon(status)
549
+ });
550
+ }
551
+ const PromptInputSelect = Select;
552
+ function PromptInputSelectTrigger(props) {
553
+ return /* @__PURE__ */ jsx(SelectTrigger, { ...props });
554
+ }
555
+ function PromptInputSelectContent(props) {
556
+ return /* @__PURE__ */ jsx(SelectPortal, { children: /* @__PURE__ */ jsx(SelectPositioner, { children: /* @__PURE__ */ jsx(SelectContent, { ...props }) }) });
557
+ }
558
+ const PromptInputSelectItem = SelectItem;
559
+ const PromptInputSelectValue = SelectValue;
560
+ const PromptInputHoverCardContext = React.createContext(null);
561
+ function PromptInputHoverCard({ openDelay = 0, closeDelay = 0, className, children, ref, ...props }) {
562
+ const [open, setOpen] = React.useState(false);
563
+ const timer = React.useRef(void 0);
564
+ const clearTimer = React.useCallback(() => window.clearTimeout(timer.current), []);
565
+ const openNow = React.useCallback(() => {
566
+ clearTimer();
567
+ timer.current = window.setTimeout(() => setOpen(true), openDelay);
568
+ }, [clearTimer, openDelay]);
569
+ const closeLater = React.useCallback(() => {
570
+ clearTimer();
571
+ timer.current = window.setTimeout(() => setOpen(false), closeDelay);
572
+ }, [clearTimer, closeDelay]);
573
+ React.useEffect(() => clearTimer, [clearTimer]);
574
+ const value = React.useMemo(() => ({
575
+ open,
576
+ openNow,
577
+ closeLater
578
+ }), [
579
+ closeLater,
580
+ open,
581
+ openNow
582
+ ]);
583
+ return /* @__PURE__ */ jsx(PromptInputHoverCardContext.Provider, {
584
+ value,
585
+ children: /* @__PURE__ */ jsx("div", {
586
+ ref,
587
+ className: cx("prompt-input__hover-card", className),
588
+ ...props,
589
+ children
590
+ })
591
+ });
592
+ }
593
+ function PromptInputHoverCardTrigger({ className, ref, ...props }) {
594
+ const ctx = React.useContext(PromptInputHoverCardContext);
595
+ return /* @__PURE__ */ jsx("span", {
596
+ ref,
597
+ className: cx("prompt-input__hover-card-trigger", className),
598
+ onFocus: ctx?.openNow,
599
+ onBlur: ctx?.closeLater,
600
+ onMouseEnter: ctx?.openNow,
601
+ onMouseLeave: ctx?.closeLater,
602
+ ...props
603
+ });
604
+ }
605
+ function PromptInputHoverCardContent({ className, ref, ...props }) {
606
+ const ctx = React.useContext(PromptInputHoverCardContext);
607
+ if (!ctx?.open) return null;
608
+ return /* @__PURE__ */ jsx("div", {
609
+ ref,
610
+ className: cx("prompt-input__hover-card-content", className),
611
+ onMouseEnter: ctx.openNow,
612
+ onMouseLeave: ctx.closeLater,
613
+ ...props
614
+ });
615
+ }
616
+ const PromptInputTabsList = TabsList;
617
+ const PromptInputTab = Tab;
618
+ const PromptInputTabBody = TabPanel;
619
+ function PromptInputTabLabel({ className, ref, ...props }) {
620
+ return /* @__PURE__ */ jsx("h3", {
621
+ ref,
622
+ className: cx("prompt-input__tab-label", className),
623
+ ...props
624
+ });
625
+ }
626
+ function PromptInputTabItem({ className, ref, ...props }) {
627
+ return /* @__PURE__ */ jsx("div", {
628
+ ref,
629
+ className: cx("prompt-input__tab-item", className),
630
+ ...props
631
+ });
632
+ }
633
+ const PromptInputCommand = Combobox;
634
+ const PromptInputCommandInput = ComboboxInput;
635
+ function PromptInputCommandList({ className, ref, ...props }) {
636
+ return /* @__PURE__ */ jsx(ComboboxList, {
637
+ ref,
638
+ className: cx("prompt-input__command-list", className),
639
+ ...props
640
+ });
641
+ }
642
+ const PromptInputCommandEmpty = ComboboxEmpty;
643
+ const PromptInputCommandGroup = ComboboxGroup;
644
+ const PromptInputCommandItem = ComboboxItem;
645
+ const PromptInputCommandSeparator = ComboboxSeparator;
646
+ function defaultSubmitIcon(status) {
647
+ switch (status) {
648
+ case "submitted": return /* @__PURE__ */ jsx(SpinnerIcon, { className: "prompt-input__submit-spinner" });
649
+ case "streaming": return /* @__PURE__ */ jsx(SquareIcon, {});
650
+ case "error": return /* @__PURE__ */ jsx(WarningIcon, {});
651
+ default: return /* @__PURE__ */ jsx(ArrowUpIcon, {});
652
+ }
653
+ }
654
+ function defaultSubmitLabel(status) {
655
+ switch (status) {
656
+ case "submitted": return "Sending";
657
+ case "streaming": return "Stop response";
658
+ case "error": return "Retry";
659
+ default: return "Send message";
660
+ }
661
+ }
662
+ function fileToPart(file) {
663
+ return {
664
+ id: createId(),
665
+ type: "file",
666
+ filename: file.name,
667
+ mediaType: file.type,
668
+ url: URL.createObjectURL(file)
669
+ };
670
+ }
671
+ function revokeFileUrls(files) {
672
+ for (const file of files) if (file.url?.startsWith("blob:")) URL.revokeObjectURL(file.url);
673
+ }
674
+ async function resolveSubmittedFile(file) {
675
+ const { id: _id, ...part } = file;
676
+ if (!part.url?.startsWith("blob:")) return part;
677
+ const dataUrl = await blobUrlToDataUrl(part.url);
678
+ return {
679
+ ...part,
680
+ url: dataUrl ?? part.url
681
+ };
682
+ }
683
+ async function blobUrlToDataUrl(url) {
684
+ try {
685
+ const blob = await (await fetch(url)).blob();
686
+ return await new Promise((resolve) => {
687
+ const reader = new FileReader();
688
+ reader.onloadend = () => resolve(typeof reader.result === "string" ? reader.result : null);
689
+ reader.onerror = () => resolve(null);
690
+ reader.readAsDataURL(blob);
691
+ });
692
+ } catch {
693
+ return null;
694
+ }
695
+ }
696
+ async function captureScreenshot() {
697
+ if (typeof navigator === "undefined" || !navigator.mediaDevices?.getDisplayMedia) return null;
698
+ let stream = null;
699
+ const video = document.createElement("video");
700
+ video.muted = true;
701
+ video.playsInline = true;
702
+ try {
703
+ stream = await navigator.mediaDevices.getDisplayMedia({
704
+ audio: false,
705
+ video: true
706
+ });
707
+ video.srcObject = stream;
708
+ await new Promise((resolve, reject) => {
709
+ video.onloadedmetadata = () => resolve();
710
+ video.onerror = () => reject(/* @__PURE__ */ new Error("Failed to load screen stream"));
711
+ });
712
+ await video.play();
713
+ const canvas = document.createElement("canvas");
714
+ canvas.width = video.videoWidth;
715
+ canvas.height = video.videoHeight;
716
+ const context = canvas.getContext("2d");
717
+ if (!context || !canvas.width || !canvas.height) return null;
718
+ context.drawImage(video, 0, 0, canvas.width, canvas.height);
719
+ const blob = await new Promise((resolve) => canvas.toBlob(resolve, "image/png"));
720
+ if (!blob) return null;
721
+ return new File([blob], `screenshot-${(/* @__PURE__ */ new Date()).toISOString().replaceAll(/[:.]/g, "-")}.png`, {
722
+ type: "image/png",
723
+ lastModified: Date.now()
724
+ });
725
+ } catch {
726
+ return null;
727
+ } finally {
728
+ for (const track of stream?.getTracks() ?? []) track.stop();
729
+ video.pause();
730
+ video.srcObject = null;
731
+ }
732
+ }
733
+ function createId() {
734
+ return globalThis.crypto?.randomUUID?.() ?? `prompt-input-${Date.now()}-${Math.random()}`;
735
+ }
736
+ //#endregion
737
+ export { 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 };