@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.
- package/dist/Button-QCM8ZKgR.d.ts +98 -0
- package/dist/CodeBlock-CttHhWcb.js +356 -0
- package/dist/CodeBlock-V6pgafSS.d.ts +174 -0
- package/dist/Select-CRsKCJ9x.d.ts +200 -0
- package/dist/class-names-9UtZwb0J.js +12 -0
- package/dist/components/Accordion.d.ts +81 -0
- package/dist/components/Accordion.js +97 -0
- package/dist/components/AgentConnection.d.ts +69 -0
- package/dist/components/AgentConnection.js +176 -0
- package/dist/components/AlertDialog.d.ts +148 -0
- package/dist/components/AlertDialog.js +141 -0
- package/dist/components/Artifact.d.ts +128 -0
- package/dist/components/Artifact.js +115 -0
- package/dist/components/Autocomplete.d.ts +186 -0
- package/dist/components/Autocomplete.js +186 -0
- package/dist/components/Avatar.d.ts +134 -0
- package/dist/components/Avatar.js +118 -0
- package/dist/components/Badge.d.ts +68 -0
- package/dist/components/Badge.js +108 -0
- package/dist/components/Button.d.ts +2 -0
- package/dist/components/Button.js +73 -0
- package/dist/components/Card.d.ts +87 -0
- package/dist/components/Card.js +81 -0
- package/dist/components/ChainOfThought.d.ts +153 -0
- package/dist/components/ChainOfThought.js +157 -0
- package/dist/components/Checkbox.d.ts +35 -0
- package/dist/components/Checkbox.js +47 -0
- package/dist/components/CheckboxGroup.d.ts +76 -0
- package/dist/components/CheckboxGroup.js +69 -0
- package/dist/components/CodeBlock.d.ts +2 -0
- package/dist/components/CodeBlock.js +2 -0
- package/dist/components/Collapsible.d.ts +127 -0
- package/dist/components/Collapsible.js +103 -0
- package/dist/components/Combobox.d.ts +177 -0
- package/dist/components/Combobox.js +179 -0
- package/dist/components/Commit.d.ts +265 -0
- package/dist/components/Commit.js +282 -0
- package/dist/components/Context.d.ts +129 -0
- package/dist/components/Context.js +309 -0
- package/dist/components/ContextMenu.d.ts +282 -0
- package/dist/components/ContextMenu.js +270 -0
- package/dist/components/Conversation.d.ts +166 -0
- package/dist/components/Conversation.js +202 -0
- package/dist/components/Dialog.d.ts +120 -0
- package/dist/components/Dialog.js +114 -0
- package/dist/components/EnvironmentVariables.d.ts +176 -0
- package/dist/components/EnvironmentVariables.js +221 -0
- package/dist/components/Field.d.ts +281 -0
- package/dist/components/Field.js +274 -0
- package/dist/components/Fieldset.d.ts +93 -0
- package/dist/components/Fieldset.js +81 -0
- package/dist/components/FileTree.d.ts +116 -0
- package/dist/components/FileTree.js +310 -0
- package/dist/components/Form.d.ts +94 -0
- package/dist/components/Form.js +88 -0
- package/dist/components/GithubMotionMark.d.ts +13 -0
- package/dist/components/GithubMotionMark.js +23 -0
- package/dist/components/Input.d.ts +33 -0
- package/dist/components/Input.js +27 -0
- package/dist/components/Label.d.ts +44 -0
- package/dist/components/Label.js +41 -0
- package/dist/components/Menu.d.ts +395 -0
- package/dist/components/Menu.js +338 -0
- package/dist/components/Menubar.d.ts +331 -0
- package/dist/components/Menubar.js +299 -0
- package/dist/components/Message.d.ts +216 -0
- package/dist/components/Message.js +245 -0
- package/dist/components/Meter.d.ts +151 -0
- package/dist/components/Meter.js +121 -0
- package/dist/components/ModelSelector.d.ts +198 -0
- package/dist/components/ModelSelector.js +306 -0
- package/dist/components/NaniteScene.d.ts +33 -0
- package/dist/components/NaniteScene.js +405 -0
- package/dist/components/NavigationMenu.d.ts +256 -0
- package/dist/components/NavigationMenu.js +228 -0
- package/dist/components/NumberField.d.ts +159 -0
- package/dist/components/NumberField.js +123 -0
- package/dist/components/Popover.d.ts +278 -0
- package/dist/components/Popover.js +245 -0
- package/dist/components/PreviewCard.d.ts +156 -0
- package/dist/components/PreviewCard.js +156 -0
- package/dist/components/PricingPage.d.ts +209 -0
- package/dist/components/PricingPage.js +506 -0
- package/dist/components/Progress.d.ts +58 -0
- package/dist/components/Progress.js +51 -0
- package/dist/components/PromptInput.d.ts +399 -0
- package/dist/components/PromptInput.js +737 -0
- package/dist/components/Queue.d.ts +241 -0
- package/dist/components/Queue.js +225 -0
- package/dist/components/RadioGroup.d.ts +78 -0
- package/dist/components/RadioGroup.js +72 -0
- package/dist/components/Reasoning.d.ts +88 -0
- package/dist/components/Reasoning.js +187 -0
- package/dist/components/ScrollArea.d.ts +160 -0
- package/dist/components/ScrollArea.js +145 -0
- package/dist/components/Select.d.ts +2 -0
- package/dist/components/Select.js +260 -0
- package/dist/components/Separator.d.ts +45 -0
- package/dist/components/Separator.js +40 -0
- package/dist/components/Shell.d.ts +163 -0
- package/dist/components/Shell.js +137 -0
- package/dist/components/Slider.d.ts +120 -0
- package/dist/components/Slider.js +115 -0
- package/dist/components/Sources.d.ts +91 -0
- package/dist/components/Sources.js +121 -0
- package/dist/components/Stepper.d.ts +190 -0
- package/dist/components/Stepper.js +315 -0
- package/dist/components/Switch.d.ts +57 -0
- package/dist/components/Switch.js +55 -0
- package/dist/components/Tabs.d.ts +84 -0
- package/dist/components/Tabs.js +78 -0
- package/dist/components/Task.d.ts +100 -0
- package/dist/components/Task.js +120 -0
- package/dist/components/Terminal.d.ts +118 -0
- package/dist/components/Terminal.js +234 -0
- package/dist/components/TestResults.d.ts +236 -0
- package/dist/components/TestResults.js +346 -0
- package/dist/components/Toast.d.ts +192 -0
- package/dist/components/Toast.js +163 -0
- package/dist/components/Toggle.d.ts +69 -0
- package/dist/components/Toggle.js +66 -0
- package/dist/components/ToggleGroup.d.ts +69 -0
- package/dist/components/ToggleGroup.js +99 -0
- package/dist/components/Tool.d.ts +119 -0
- package/dist/components/Tool.js +192 -0
- package/dist/components/Toolbar.d.ts +162 -0
- package/dist/components/Toolbar.js +126 -0
- package/dist/components/Tooltip.d.ts +81 -0
- package/dist/components/Tooltip.js +74 -0
- package/dist/components/WebPreview.d.ts +143 -0
- package/dist/components/WebPreview.js +280 -0
- package/dist/components/useStickToBottom.d.ts +30 -0
- package/dist/components/useStickToBottom.js +95 -0
- package/dist/copy-button-BIATcZWZ.d.ts +16 -0
- package/dist/copy-button-BNgclDrO.js +43 -0
- package/dist/icons-BsWc8g4E.js +221 -0
- package/dist/styles/accordion.css +10 -10
- package/dist/styles/agent-connection.css +18 -17
- package/dist/styles/alert-dialog.css +48 -29
- package/dist/styles/artifact.css +10 -10
- package/dist/styles/autocomplete.css +25 -24
- package/dist/styles/avatar.css +96 -6
- package/dist/styles/badge.css +78 -14
- package/dist/styles/base.css +9 -9
- package/dist/styles/button.css +57 -38
- package/dist/styles/card.css +57 -10
- package/dist/styles/chain-of-thought.css +47 -33
- package/dist/styles/checkbox.css +24 -10
- package/dist/styles/code-block.css +22 -15
- package/dist/styles/collapsible.css +14 -12
- package/dist/styles/combobox.css +26 -25
- package/dist/styles/commit.css +49 -24
- package/dist/styles/context-menu.css +70 -16
- package/dist/styles/context.css +15 -15
- package/dist/styles/conversation.css +12 -12
- package/dist/styles/dialog.css +52 -16
- package/dist/styles/environment-variables.css +19 -19
- package/dist/styles/field.css +184 -22
- package/dist/styles/fieldset.css +5 -4
- package/dist/styles/file-tree.css +17 -17
- package/dist/styles/index.css.d.ts +1 -0
- package/dist/styles/input.css +45 -11
- package/dist/styles/label.css +21 -4
- package/dist/styles/menu.css +55 -32
- package/dist/styles/menubar.css +80 -28
- package/dist/styles/message.css +22 -13
- package/dist/styles/meter.css +8 -8
- package/dist/styles/model-selector.css +33 -21
- package/dist/styles/navigation-menu.css +26 -24
- package/dist/styles/number-field.css +20 -18
- package/dist/styles/popover.css +38 -28
- package/dist/styles/preview-card.css +15 -13
- package/dist/styles/pricing-page.css +53 -53
- package/dist/styles/progress.css +7 -7
- package/dist/styles/prompt-input.css +73 -24
- package/dist/styles/queue.css +58 -32
- package/dist/styles/radio.css +26 -11
- package/dist/styles/reasoning.css +15 -11
- package/dist/styles/scroll-area.css +6 -6
- package/dist/styles/scrollbars.css +4 -4
- package/dist/styles/select.css +84 -25
- package/dist/styles/separator.css +1 -1
- package/dist/styles/shell.css +21 -20
- package/dist/styles/slider.css +35 -11
- package/dist/styles/sources.css +20 -20
- package/dist/styles/stepper.css +22 -22
- package/dist/styles/switch.css +29 -13
- package/dist/styles/tabs.css +23 -16
- package/dist/styles/task.css +27 -23
- package/dist/styles/terminal.css +16 -13
- package/dist/styles/test-results.css +79 -32
- package/dist/styles/toast.css +26 -25
- package/dist/styles/toggle-group.css +12 -12
- package/dist/styles/toggle.css +29 -11
- package/dist/styles/tool.css +54 -32
- package/dist/styles/toolbar.css +30 -28
- package/dist/styles/tooltip.css +10 -8
- package/dist/styles/web-preview.css +37 -21
- package/dist/utils/structured-code.d.ts +10 -0
- package/dist/utils/structured-code.js +81 -0
- package/package.json +17 -8
- package/dist/index.d.ts +0 -7236
- 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 };
|