@parallelworks/ui 0.40.1 → 0.42.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -0
- package/dist/ai/adapter/memoryStore.js +7 -0
- package/dist/ai/adapter/mock/mockAdapter.js +52 -17
- package/dist/ai/adapter/openai/createOpenAIStream.js +1 -1
- package/dist/ai/adapter/openai/index.d.ts +1 -1
- package/dist/ai/adapter/openai/sse.d.ts +8 -2
- package/dist/ai/adapter/openai/sse.js +40 -4
- package/dist/ai/adapter/openai/wire.d.ts +6 -0
- package/dist/ai/components/AllocationSelector.d.ts +3 -4
- package/dist/ai/components/AllocationSelector.js +37 -33
- package/dist/ai/components/AttachmentManager.js +7 -7
- package/dist/ai/components/AttachmentUpload.js +5 -5
- package/dist/ai/components/BranchNavigator.js +1 -1
- package/dist/ai/components/ChatEmptyState.js +2 -2
- package/dist/ai/components/ChatInput.js +10 -8
- package/dist/ai/components/ChatLayout.js +4 -5
- package/dist/ai/components/ChatMessage.js +12 -12
- package/dist/ai/components/ChatMessageList.js +9 -9
- package/dist/ai/components/ChatSidebar.js +8 -9
- package/dist/ai/components/ChatThread.js +4 -4
- package/dist/ai/components/ComposerChrome.d.ts +2 -2
- package/dist/ai/components/ComposerChrome.js +3 -6
- package/dist/ai/components/DragOverlay.js +2 -2
- package/dist/ai/components/ModelSelector.d.ts +5 -3
- package/dist/ai/components/ModelSelector.js +102 -339
- package/dist/ai/components/PastedText.js +11 -11
- package/dist/ai/components/ProviderIssueBanner.d.ts +2 -0
- package/dist/ai/components/ProviderIssueBanner.js +34 -16
- package/dist/ai/components/Reasoning.js +4 -4
- package/dist/ai/components/ShareDialog.js +43 -35
- package/dist/ai/components/SlashMenu.d.ts +12 -2
- package/dist/ai/components/SlashMenu.js +29 -13
- package/dist/ai/components/agent/ActivityLine.js +3 -3
- package/dist/ai/components/agent/AgentMessageParts.js +20 -20
- package/dist/ai/components/agent/PermissionPicker.js +13 -11
- package/dist/ai/components/conversationActions.d.ts +4 -6
- package/dist/ai/components/conversationActions.js +9 -13
- package/dist/ai/core/ChatProvider.js +4 -4
- package/dist/ai/core/config.js +1 -1
- package/dist/ai/core/parts.d.ts +5 -0
- package/dist/ai/core/parts.js +28 -1
- package/dist/ai/core/providerIssues.d.ts +7 -0
- package/dist/ai/core/providerIssues.js +9 -1
- package/dist/ai/core/useKeyboardShortcuts.d.ts +4 -0
- package/dist/ai/core/useKeyboardShortcuts.js +8 -2
- package/dist/ai/index.d.ts +0 -1
- package/dist/ai/index.js +1 -2
- package/dist/ai/strings.d.ts +19 -0
- package/dist/ai/strings.js +18 -1
- package/dist/ai/ui/Avatar.js +5 -5
- package/dist/ai/ui/ConversationSidebar.d.ts +0 -1
- package/dist/ai/ui/ConversationSidebar.js +6 -7
- package/dist/ai/ui/Markdown.js +3 -3
- package/dist/ai/ui/Notice.js +6 -6
- package/dist/ai/ui/chip.d.ts +4 -0
- package/dist/ai/ui/chip.js +8 -0
- package/dist/ai/ui/sidebar.d.ts +6 -5
- package/dist/ai/ui/sidebar.js +39 -43
- package/dist/components/ActionMenu.d.ts +13 -0
- package/dist/components/ActionMenu.js +30 -0
- package/dist/components/Avatar.js +6 -6
- package/dist/components/Breadcrumbs.js +1 -1
- package/dist/components/Button.js +10 -10
- package/dist/components/Callout.js +2 -2
- package/dist/components/Card.js +3 -3
- package/dist/components/Combobox.d.ts +66 -0
- package/dist/components/Combobox.js +393 -0
- package/dist/components/ConfirmModal.js +11 -11
- package/dist/components/ContextMenu.d.ts +23 -0
- package/dist/components/ContextMenu.js +89 -0
- package/dist/components/CopyToClipboard.js +2 -2
- package/dist/components/CreateModal.d.ts +17 -36
- package/dist/components/CreateModal.js +97 -259
- package/dist/components/DescriptionList.js +8 -8
- package/dist/components/Filter.js +2 -2
- package/dist/components/FilterPill.js +2 -2
- package/dist/components/HoverCard.d.ts +14 -0
- package/dist/components/HoverCard.js +33 -0
- package/dist/components/IconButton.d.ts +5 -5
- package/dist/components/IconButton.js +18 -22
- package/dist/components/InfoTip.d.ts +15 -0
- package/dist/components/InfoTip.js +60 -0
- package/dist/components/Input.js +5 -5
- package/dist/components/LetterBadge.js +2 -2
- package/dist/components/Loader.js +3 -3
- package/dist/components/Markdown.js +1 -1
- package/dist/components/MarkdownImpl.js +2 -2
- package/dist/components/Menu.d.ts +33 -0
- package/dist/components/Menu.js +86 -0
- package/dist/components/MenuEntries.d.ts +56 -0
- package/dist/components/MenuEntries.js +123 -0
- package/dist/components/Modal.d.ts +29 -0
- package/dist/components/Modal.js +47 -0
- package/dist/components/Overview.js +11 -10
- package/dist/components/PageHeader.d.ts +10 -16
- package/dist/components/PageHeader.js +69 -95
- package/dist/components/Pagination.js +5 -5
- package/dist/components/Provider.d.ts +5 -11
- package/dist/components/Provider.js +11 -14
- package/dist/components/Radio.d.ts +8 -0
- package/dist/components/Radio.js +16 -0
- package/dist/components/SectionHeader.js +2 -2
- package/dist/components/SegmentedControl.d.ts +6 -5
- package/dist/components/SegmentedControl.js +34 -30
- package/dist/components/Select.d.ts +27 -0
- package/dist/components/Select.js +78 -0
- package/dist/components/SettingModeOption.d.ts +5 -8
- package/dist/components/SettingModeOption.js +10 -12
- package/dist/components/SettingRow.js +4 -4
- package/dist/components/SettingsCard.js +4 -4
- package/dist/components/SettingsGroup.js +2 -2
- package/dist/components/Sort.js +1 -1
- package/dist/components/StatusBadge.js +7 -7
- package/dist/components/StatusIcons.js +4 -4
- package/dist/components/Switch.d.ts +22 -0
- package/dist/components/Switch.js +28 -0
- package/dist/components/Table.js +11 -11
- package/dist/components/Tooltip.d.ts +24 -20
- package/dist/components/Tooltip.js +58 -76
- package/dist/components/floating.d.ts +14 -0
- package/dist/components/floating.js +21 -0
- package/dist/components/search.d.ts +2 -0
- package/dist/components/search.js +14 -0
- package/dist/editor/Editor.js +1 -1
- package/dist/editor/EditorField.js +16 -21
- package/dist/editor/FormCodePanel.js +19 -19
- package/dist/editor/Monaco.js +3 -3
- package/dist/file-explorer/FileExplorer.js +77 -81
- package/dist/file-explorer/FileExplorerContext.js +2 -2
- package/dist/file-explorer/FilePreview/CodePreviewEditor.js +1 -1
- package/dist/file-explorer/FilePreview/Inline.js +1 -1
- package/dist/file-explorer/FilePreview/StreamedBadge.js +5 -6
- package/dist/file-explorer/FilePreview/Viewer.js +8 -8
- package/dist/file-explorer/FilePreview/index.d.ts +1 -1
- package/dist/file-explorer/FilePreview/index.js +49 -83
- package/dist/file-explorer/FileRow.d.ts +4 -3
- package/dist/file-explorer/FileRow.js +32 -38
- package/dist/file-explorer/FileRowsWindow.d.ts +3 -2
- package/dist/file-explorer/FileRowsWindow.js +4 -4
- package/dist/file-explorer/FileTable.d.ts +3 -2
- package/dist/file-explorer/FileTable.js +3 -3
- package/dist/file-explorer/LoadMoreRow.js +1 -1
- package/dist/file-explorer/TreeView.js +11 -16
- package/dist/file-explorer/helpers.js +11 -12
- package/dist/file-explorer/toasts.js +11 -11
- package/dist/form/FieldGroup.d.ts +11 -0
- package/dist/form/FieldGroup.js +36 -0
- package/dist/form/FieldWrapper.js +4 -4
- package/dist/form/Fields/BooleanField.js +23 -33
- package/dist/form/Fields/CheckboxGroupField.d.ts +1 -1
- package/dist/form/Fields/CheckboxGroupField.js +9 -13
- package/dist/form/Fields/ColorField.d.ts +1 -2
- package/dist/form/Fields/ColorField.js +6 -11
- package/dist/form/Fields/DropdownField.d.ts +3 -13
- package/dist/form/Fields/DropdownField.js +40 -27
- package/dist/form/Fields/DurationField.d.ts +1 -2
- package/dist/form/Fields/DurationField.js +25 -32
- package/dist/form/Fields/LabelField.js +2 -2
- package/dist/form/Fields/MultiDropdownField.d.ts +6 -12
- package/dist/form/Fields/MultiDropdownField.js +31 -22
- package/dist/form/Fields/NumberField.d.ts +1 -1
- package/dist/form/Fields/NumberField.js +65 -35
- package/dist/form/Fields/ObjectField.d.ts +1 -1
- package/dist/form/Fields/ObjectField.js +12 -43
- package/dist/form/Fields/RadioField.d.ts +1 -1
- package/dist/form/Fields/RadioField.js +30 -41
- package/dist/form/Fields/RangeField.d.ts +1 -2
- package/dist/form/Fields/RangeField.js +23 -16
- package/dist/form/Fields/StringField.d.ts +1 -2
- package/dist/form/Fields/StringField.js +58 -26
- package/dist/form/Fields/TextAreaField.js +12 -10
- package/dist/form/Form.d.ts +30 -48
- package/dist/form/Form.js +306 -412
- package/dist/form/FormLayout.js +9 -9
- package/dist/form/Wizard/WizardContainer.d.ts +1 -4
- package/dist/form/Wizard/WizardContainer.js +40 -56
- package/dist/form/Wizard/WizardNavigation.js +1 -1
- package/dist/form/Wizard/WizardStepContent.d.ts +1 -5
- package/dist/form/Wizard/WizardStepContent.js +11 -16
- package/dist/form/Wizard/WizardStepIndicator.js +6 -6
- package/dist/form/Wizard/types.d.ts +0 -14
- package/dist/form/fieldRegistry.d.ts +1 -1
- package/dist/form/fieldRegistry.js +6 -6
- package/dist/form/formEditing.js +1 -1
- package/dist/form/index.d.ts +5 -4
- package/dist/form/index.js +6 -7
- package/dist/form/types/fieldComponentTypes.d.ts +8 -10
- package/dist/form/useChoiceField.d.ts +20 -0
- package/dist/form/useChoiceField.js +49 -0
- package/dist/form/useFieldValue.d.ts +7 -0
- package/dist/form/useFieldValue.js +20 -0
- package/dist/form/useParsedOpts.d.ts +3 -1
- package/dist/form/useParsedOpts.js +12 -17
- package/dist/form/utils/dependentOptions.d.ts +8 -4
- package/dist/form/utils/dependentOptions.js +25 -17
- package/dist/form/utils/fieldItems.d.ts +10 -0
- package/dist/form/utils/fieldItems.js +11 -0
- package/dist/form/utils/fieldPath.d.ts +10 -0
- package/dist/form/utils/fieldPath.js +20 -0
- package/dist/form/utils/shareEqual.d.ts +6 -0
- package/dist/form/utils/shareEqual.js +38 -0
- package/dist/graph/AnnotationBanner.js +8 -8
- package/dist/graph/Collapse.d.ts +1 -0
- package/dist/graph/Collapse.js +6 -27
- package/dist/graph/Connectors.js +5 -5
- package/dist/graph/DependencyGraph.js +20 -19
- package/dist/graph/GraphEditor.js +30 -41
- package/dist/graph/GraphEditorDialogs.js +17 -17
- package/dist/graph/GraphNode.js +1 -1
- package/dist/graph/InputDialog.js +19 -22
- package/dist/graph/InputsEditor.js +58 -66
- package/dist/graph/JobSummary.js +42 -39
- package/dist/graph/MatrixGroup.js +1 -1
- package/dist/graph/ProblemsButton.js +18 -19
- package/dist/graph/Reveal.js +1 -1
- package/dist/graph/ShortcutsButton.js +15 -18
- package/dist/graph/SuggestionInput.d.ts +5 -10
- package/dist/graph/SuggestionInput.js +14 -18
- package/dist/graph/ToolbarPopover.d.ts +11 -6
- package/dist/graph/ToolbarPopover.js +35 -67
- package/dist/graph/TreeView.js +24 -37
- package/dist/graph/WorkflowSettingsDialog.js +4 -4
- package/dist/graph/editorChrome.js +13 -12
- package/dist/graph/editorFields.js +29 -29
- package/dist/graph/editorPrimitives.d.ts +1 -1
- package/dist/graph/editorPrimitives.js +10 -7
- package/dist/graph/editorRows.js +103 -97
- package/dist/graph/fieldProblems.js +2 -2
- package/dist/graph/graphEditorState.d.ts +4 -3
- package/dist/graph/inputRefs.js +9 -9
- package/dist/graph/settingsViews.js +6 -7
- package/dist/graph/stepWith.js +6 -6
- package/dist/index.d.ts +14 -15
- package/dist/index.js +15 -14
- package/dist/list/ActionToolbar.d.ts +6 -14
- package/dist/list/ActionToolbar.js +48 -96
- package/dist/list/ListPage.d.ts +6 -12
- package/dist/list/ListPage.js +46 -29
- package/dist/list/ListPager.js +4 -4
- package/dist/list/ListTable.js +5 -5
- package/dist/list/ListViewControls.d.ts +5 -4
- package/dist/list/ListViewControls.js +177 -267
- package/dist/list/UserHoverCard.d.ts +3 -17
- package/dist/list/UserHoverCard.js +21 -45
- package/dist/list/index.d.ts +3 -4
- package/dist/list/index.js +3 -4
- package/dist/list/listSearch.js +12 -11
- package/dist/list/rowMenu.d.ts +63 -0
- package/dist/list/rowMenu.js +134 -0
- package/dist/logviewer/LogViewer.js +26 -26
- package/dist/styles.css +1 -1
- package/package.json +7 -13
- package/dist/ai/ui/Dropdown.d.ts +0 -31
- package/dist/ai/ui/Dropdown.js +0 -110
- package/dist/components/AddMenu.d.ts +0 -18
- package/dist/components/AddMenu.js +0 -46
- package/dist/components/BareModal.d.ts +0 -17
- package/dist/components/BareModal.js +0 -77
- package/dist/components/CollapsiblePanel.d.ts +0 -27
- package/dist/components/CollapsiblePanel.js +0 -77
- package/dist/components/Dropdown.d.ts +0 -28
- package/dist/components/Dropdown.js +0 -355
- package/dist/components/Listbox.d.ts +0 -21
- package/dist/components/Listbox.js +0 -119
- package/dist/components/MultiSelect.d.ts +0 -22
- package/dist/components/MultiSelect.js +0 -168
- package/dist/components/SwitchToggle.d.ts +0 -15
- package/dist/components/SwitchToggle.js +0 -57
- package/dist/components/SwitchToggleSmall.d.ts +0 -6
- package/dist/components/SwitchToggleSmall.js +0 -14
- package/dist/components/Toggle.d.ts +0 -6
- package/dist/components/Toggle.js +0 -20
- package/dist/components/dropdownUtils.d.ts +0 -37
- package/dist/components/dropdownUtils.js +0 -99
- package/dist/form/SpecialTypes/DropdownWrapper.d.ts +0 -25
- package/dist/form/SpecialTypes/DropdownWrapper.js +0 -28
- package/dist/form/SpecialTypes/FormikCustomDropdown.d.ts +0 -51
- package/dist/form/SpecialTypes/FormikCustomDropdown.js +0 -119
- package/dist/form/SpecialTypes/FormikCustomInput.d.ts +0 -19
- package/dist/form/SpecialTypes/FormikCustomInput.js +0 -121
- package/dist/form/SpecialTypes/FormikMultiSelectDropdown.d.ts +0 -13
- package/dist/form/SpecialTypes/FormikMultiSelectDropdown.js +0 -31
- package/dist/form/SpecialTypes/FormikNumericInput.d.ts +0 -9
- package/dist/form/SpecialTypes/FormikNumericInput.js +0 -60
- package/dist/form/SpecialTypes/index.d.ts +0 -3
- package/dist/list/HoverCard.d.ts +0 -24
- package/dist/list/HoverCard.js +0 -73
- package/dist/list/RowContextMenu.d.ts +0 -127
- package/dist/list/RowContextMenu.js +0 -515
package/README.md
CHANGED
|
@@ -176,6 +176,31 @@ animations) and streamdown's classes next to the theme contract:
|
|
|
176
176
|
|
|
177
177
|
The prebuilt `@parallelworks/ui/styles.css` already includes both.
|
|
178
178
|
|
|
179
|
+
## Form state
|
|
180
|
+
|
|
181
|
+
`DynamicForm` keeps its values in react-hook-form, a peer dependency, so the
|
|
182
|
+
host and the library share one instance. A host that reads or drives the form
|
|
183
|
+
creates it and passes it in:
|
|
184
|
+
|
|
185
|
+
```tsx
|
|
186
|
+
const form = useForm()
|
|
187
|
+
const { isDirty } = useFormState({ control: form.control })
|
|
188
|
+
|
|
189
|
+
<DynamicForm form={form} formJSONs={inputs} initialValues={saved} />
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
`form.getValues()`, `form.reset()` and `formState.isDirty` then work as
|
|
193
|
+
react-hook-form documents them. Without `form`, the form keeps its own;
|
|
194
|
+
`onValuesChange` reports each change either way. `missingFields` marks fields
|
|
195
|
+
as `required` errors.
|
|
196
|
+
|
|
197
|
+
Each field subscribes only to what it reads, so typing renders the edited
|
|
198
|
+
field and whatever reads it, not the whole form. A host field component,
|
|
199
|
+
passed through `fields` or `slots.formFields`, binds the same way: its own
|
|
200
|
+
value with `useFieldValue(field.name)` (or `useController`), another input's
|
|
201
|
+
with `useFieldAt(path)` or `useWatch`, and a pick from a list with
|
|
202
|
+
`useChoiceField`, which applies `autoselect` and `secondaryField`.
|
|
203
|
+
|
|
179
204
|
## Form layouts
|
|
180
205
|
|
|
181
206
|
`DynamicForm` accepts an optional `$meta.layout` in `formJSONs`. A layout arranges
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ChatAdapterError } from "./types.js";
|
|
2
|
+
import { applyPartDelta, finalizeParts } from "../core/parts.js";
|
|
2
3
|
//#region src/ai/adapter/memoryStore.ts
|
|
3
4
|
function newId() {
|
|
4
5
|
return crypto.randomUUID();
|
|
@@ -103,6 +104,7 @@ function createMemoryConversationStore(options) {
|
|
|
103
104
|
return async (req, handlers, signal) => {
|
|
104
105
|
let partialContent = "";
|
|
105
106
|
let partialReasoning = "";
|
|
107
|
+
let parts = [];
|
|
106
108
|
const wrappedHandlers = {
|
|
107
109
|
...handlers,
|
|
108
110
|
onContent: (chunk) => {
|
|
@@ -112,6 +114,10 @@ function createMemoryConversationStore(options) {
|
|
|
112
114
|
onReasoning: (chunk) => {
|
|
113
115
|
partialReasoning += chunk;
|
|
114
116
|
handlers.onReasoning?.(chunk);
|
|
117
|
+
},
|
|
118
|
+
onPart: (delta) => {
|
|
119
|
+
parts = applyPartDelta(parts, delta);
|
|
120
|
+
handlers.onPart?.(delta);
|
|
115
121
|
}
|
|
116
122
|
};
|
|
117
123
|
recordUser(req);
|
|
@@ -126,6 +132,7 @@ function createMemoryConversationStore(options) {
|
|
|
126
132
|
...result.reasoning ? { reasoning: result.reasoning } : {},
|
|
127
133
|
...result.responsesOutput.length ? { responsesOutput: result.responsesOutput } : {},
|
|
128
134
|
...result.toolCalls.length ? { toolCalls: result.toolCalls } : {},
|
|
135
|
+
...parts.length ? { parts: finalizeParts(parts) } : {},
|
|
129
136
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
130
137
|
});
|
|
131
138
|
return {
|
|
@@ -5,6 +5,7 @@ var CANNED_REPLIES = [
|
|
|
5
5
|
"Everything you see is generated locally. The mock adapter implements the same `ChatAdapter` interface as the OpenAI-compatible adapter and any backend's own, so the UI can't tell the difference.\n\n```ts\nconst adapter = createMockChatAdapter()\n```\n\nMarkdown, `inline code`, and code blocks render through the same pipeline.",
|
|
6
6
|
"Branching works too: edit one of your earlier messages or hit regenerate on a reply, then use the arrows to flip between branches."
|
|
7
7
|
];
|
|
8
|
+
var TOOL_REPLY = "Two clusters are running: **gpu-a** with 4 nodes, and **cpu-batch**, idle and scaled to zero.";
|
|
8
9
|
var THINKING_PREAMBLE = "Thinking about the question, weighing a few canned options, and picking the next reply in the rotation...";
|
|
9
10
|
function sleep(ms, signal) {
|
|
10
11
|
return new Promise((resolve, reject) => {
|
|
@@ -30,21 +31,32 @@ function createMockChatAdapter(options) {
|
|
|
30
31
|
conversations: store.conversations,
|
|
31
32
|
models: { async list() {
|
|
32
33
|
return {
|
|
33
|
-
models: [
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
34
|
+
models: [
|
|
35
|
+
{
|
|
36
|
+
id: "mock:local/canned-small",
|
|
37
|
+
object: "model",
|
|
38
|
+
created: 0,
|
|
39
|
+
owned_by: "mock",
|
|
40
|
+
provider: "Mock Provider",
|
|
41
|
+
tool_calling_mode: "none"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
id: "mock:local/canned-thinking",
|
|
45
|
+
object: "model",
|
|
46
|
+
created: 0,
|
|
47
|
+
owned_by: "mock",
|
|
48
|
+
provider: "Mock Provider",
|
|
49
|
+
tool_calling_mode: "none"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
id: "mock:local/canned-tools",
|
|
53
|
+
object: "model",
|
|
54
|
+
created: 0,
|
|
55
|
+
owned_by: "mock",
|
|
56
|
+
provider: "Mock Provider",
|
|
57
|
+
tool_calling_mode: "native"
|
|
58
|
+
}
|
|
59
|
+
],
|
|
48
60
|
unreachableSessions: [],
|
|
49
61
|
providerIssues: []
|
|
50
62
|
};
|
|
@@ -57,8 +69,31 @@ function createMockChatAdapter(options) {
|
|
|
57
69
|
reasoning += word;
|
|
58
70
|
handlers.onReasoning?.(word);
|
|
59
71
|
}
|
|
60
|
-
const
|
|
61
|
-
|
|
72
|
+
const usesTools = req.model.includes("tools");
|
|
73
|
+
if (usesTools) {
|
|
74
|
+
const id = `call-${crypto.randomUUID()}`;
|
|
75
|
+
handlers.onPart?.({
|
|
76
|
+
type: "tool_start",
|
|
77
|
+
id,
|
|
78
|
+
name: "list_clusters",
|
|
79
|
+
args: "{}"
|
|
80
|
+
});
|
|
81
|
+
await sleep(900, signal);
|
|
82
|
+
handlers.onPart?.({
|
|
83
|
+
type: "tool_end",
|
|
84
|
+
id,
|
|
85
|
+
result: JSON.stringify([{
|
|
86
|
+
name: "gpu-a",
|
|
87
|
+
status: "on",
|
|
88
|
+
nodes: 4
|
|
89
|
+
}, {
|
|
90
|
+
name: "cpu-batch",
|
|
91
|
+
status: "on",
|
|
92
|
+
nodes: 0
|
|
93
|
+
}])
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
const reply = usesTools ? TOOL_REPLY : CANNED_REPLIES[replyIndex++ % CANNED_REPLIES.length] ?? "";
|
|
62
97
|
await sleep(250, signal);
|
|
63
98
|
let content = "";
|
|
64
99
|
for (const word of reply.split(/(?<= )/)) {
|
|
@@ -27,7 +27,7 @@ function createOpenAICompatibleStream(config) {
|
|
|
27
27
|
if (!res.ok) throw new ChatAdapterError(await errorMessageFromResponse(res), { status: res.status });
|
|
28
28
|
const reader = res.body?.getReader();
|
|
29
29
|
if (!reader) throw new ChatAdapterError("No response body");
|
|
30
|
-
return processSSEStream(reader, handlers.onContent, void 0, void 0, handlers.onReasoning, handlers.onMessageId, useResponses ? parseResponsesSSELine : void 0);
|
|
30
|
+
return processSSEStream(reader, handlers.onContent, void 0, void 0, handlers.onReasoning, handlers.onMessageId, useResponses ? parseResponsesSSELine : void 0, handlers.onPart);
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
33
|
//#endregion
|
|
@@ -3,4 +3,4 @@ export { apiErrorMessage, errorMessageFromResponse, statusFallbackMessage, } fro
|
|
|
3
3
|
export { createOpenAIChatAdapter, type OpenAIChatAdapterOptions, } from './openaiAdapter.js';
|
|
4
4
|
export { buildResponsesRequestBody, parseResponsesSSELine, } from './responses.js';
|
|
5
5
|
export { type ParsedSSELine, parseSSELine, processSSEStream, type SSELineParser, } from './sse.js';
|
|
6
|
-
export type { CompletionChunk, CompletionChunkChoice, CompletionMessage, CompletionRequest, ModelsWireResponse, } from './wire.js';
|
|
6
|
+
export type { CompletionChunk, CompletionChunkChoice, CompletionMessage, CompletionRequest, ModelsWireResponse, ToolResultChunk, } from './wire.js';
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import type { ToolCall, ToolCallDelta } from '../../types.js';
|
|
2
|
+
import type { PartDelta } from '../types.js';
|
|
3
|
+
import type { ToolResultChunk } from './wire.js';
|
|
2
4
|
export type ParsedSSELine = {
|
|
3
5
|
id?: string | undefined;
|
|
4
6
|
messageId?: string | undefined;
|
|
5
7
|
content?: string | undefined;
|
|
6
8
|
reasoningContent?: string | undefined;
|
|
7
9
|
toolCalls?: ToolCallDelta[] | undefined;
|
|
10
|
+
toolResult?: ToolResultChunk | undefined;
|
|
8
11
|
finishReason?: string | null | undefined;
|
|
9
12
|
model?: string | undefined;
|
|
10
13
|
error?: string | undefined;
|
|
@@ -12,8 +15,11 @@ export type ParsedSSELine = {
|
|
|
12
15
|
} | null;
|
|
13
16
|
export type SSELineParser = (line: string) => ParsedSSELine;
|
|
14
17
|
export declare function parseSSELine(line: string): ParsedSSELine;
|
|
15
|
-
/** Process an SSE stream from a ReadableStream reader
|
|
16
|
-
|
|
18
|
+
/** Process an SSE stream from a ReadableStream reader. With `onPart`, tool
|
|
19
|
+
* calls stream as parts; a call the gateway answered with a result is left
|
|
20
|
+
* off the returned `toolCalls`, which then hold only calls the caller must
|
|
21
|
+
* run. */
|
|
22
|
+
export declare function processSSEStream(reader: ReadableStreamDefaultReader<Uint8Array>, onContent: (content: string) => void, onToolCall?: (toolCalls: ToolCall[]) => void, onError?: (error: string) => void, onReasoning?: (reasoning: string) => void, onMessageId?: (messageId: string) => void, parseLine?: SSELineParser, onPart?: (delta: PartDelta) => void): Promise<{
|
|
17
23
|
content: string;
|
|
18
24
|
messageId: string;
|
|
19
25
|
toolCalls: ToolCall[];
|
|
@@ -14,6 +14,7 @@ function parseSSELine(line) {
|
|
|
14
14
|
content: choice?.delta?.content,
|
|
15
15
|
reasoningContent: choice?.delta?.reasoning_content,
|
|
16
16
|
toolCalls: choice?.delta?.tool_calls ?? void 0,
|
|
17
|
+
toolResult: chunk.toolResult,
|
|
17
18
|
finishReason: choice?.finish_reason,
|
|
18
19
|
model: chunk.model
|
|
19
20
|
};
|
|
@@ -21,8 +22,11 @@ function parseSSELine(line) {
|
|
|
21
22
|
return null;
|
|
22
23
|
}
|
|
23
24
|
}
|
|
24
|
-
/** Process an SSE stream from a ReadableStream reader
|
|
25
|
-
|
|
25
|
+
/** Process an SSE stream from a ReadableStream reader. With `onPart`, tool
|
|
26
|
+
* calls stream as parts; a call the gateway answered with a result is left
|
|
27
|
+
* off the returned `toolCalls`, which then hold only calls the caller must
|
|
28
|
+
* run. */
|
|
29
|
+
async function processSSEStream(reader, onContent, onToolCall, onError, onReasoning, onMessageId, parseLine = parseSSELine, onPart) {
|
|
26
30
|
const decoder = new TextDecoder();
|
|
27
31
|
let assistantContent = "";
|
|
28
32
|
let assistantReasoning = "";
|
|
@@ -31,6 +35,19 @@ async function processSSEStream(reader, onContent, onToolCall, onError, onReason
|
|
|
31
35
|
let resolvedModel = null;
|
|
32
36
|
let responsesOutput = [];
|
|
33
37
|
const toolCallsMap = /* @__PURE__ */ new Map();
|
|
38
|
+
const startedToolCalls = /* @__PURE__ */ new Set();
|
|
39
|
+
const answeredToolCalls = /* @__PURE__ */ new Set();
|
|
40
|
+
const startToolCall = (index) => {
|
|
41
|
+
const tc = toolCallsMap.get(index);
|
|
42
|
+
if (!onPart || !tc?.id || !tc.name || startedToolCalls.has(index)) return;
|
|
43
|
+
startedToolCalls.add(index);
|
|
44
|
+
onPart({
|
|
45
|
+
type: "tool_start",
|
|
46
|
+
id: tc.id,
|
|
47
|
+
name: tc.name,
|
|
48
|
+
args: tc.arguments
|
|
49
|
+
});
|
|
50
|
+
};
|
|
34
51
|
let lineBuffer = "";
|
|
35
52
|
while (true) {
|
|
36
53
|
const result = await reader.read();
|
|
@@ -64,13 +81,32 @@ async function processSSEStream(reader, onContent, onToolCall, onError, onReason
|
|
|
64
81
|
if (tc.index === void 0) continue;
|
|
65
82
|
const existing = toolCallsMap.get(tc.index);
|
|
66
83
|
if (existing) {
|
|
67
|
-
|
|
84
|
+
existing.id ||= tc.id || "";
|
|
85
|
+
existing.name ||= tc.function?.name || "";
|
|
86
|
+
if (tc.function?.arguments) {
|
|
87
|
+
existing.arguments += tc.function.arguments;
|
|
88
|
+
if (startedToolCalls.has(tc.index)) onPart?.({
|
|
89
|
+
type: "tool_update",
|
|
90
|
+
id: existing.id,
|
|
91
|
+
args: tc.function.arguments
|
|
92
|
+
});
|
|
93
|
+
}
|
|
68
94
|
} else toolCallsMap.set(tc.index, {
|
|
69
95
|
id: tc.id || "",
|
|
70
96
|
type: tc.type || "function",
|
|
71
97
|
name: tc.function?.name || "",
|
|
72
98
|
arguments: tc.function?.arguments || ""
|
|
73
99
|
});
|
|
100
|
+
startToolCall(tc.index);
|
|
101
|
+
}
|
|
102
|
+
if (parsed.toolResult) {
|
|
103
|
+
answeredToolCalls.add(parsed.toolResult.toolCallId);
|
|
104
|
+
onPart?.({
|
|
105
|
+
type: "tool_end",
|
|
106
|
+
id: parsed.toolResult.toolCallId,
|
|
107
|
+
result: parsed.toolResult.content,
|
|
108
|
+
...parsed.toolResult.isError ? { isError: true } : {}
|
|
109
|
+
});
|
|
74
110
|
}
|
|
75
111
|
}
|
|
76
112
|
}
|
|
@@ -89,7 +125,7 @@ async function processSSEStream(reader, onContent, onToolCall, onError, onReason
|
|
|
89
125
|
if (parsed.finishReason) finishReason = parsed.finishReason;
|
|
90
126
|
}
|
|
91
127
|
}
|
|
92
|
-
const toolCalls = Array.from(toolCallsMap.values()).map((tc) => ({
|
|
128
|
+
const toolCalls = Array.from(toolCallsMap.values()).filter((tc) => !answeredToolCalls.has(tc.id)).map((tc) => ({
|
|
93
129
|
id: tc.id,
|
|
94
130
|
type: tc.type,
|
|
95
131
|
function: {
|
|
@@ -30,9 +30,15 @@ export interface CompletionChunk {
|
|
|
30
30
|
object?: string;
|
|
31
31
|
created?: number;
|
|
32
32
|
messageId?: string;
|
|
33
|
+
toolResult?: ToolResultChunk;
|
|
33
34
|
choices?: CompletionChunkChoice[] | null;
|
|
34
35
|
usage?: TokenUsage;
|
|
35
36
|
}
|
|
37
|
+
export interface ToolResultChunk {
|
|
38
|
+
toolCallId: string;
|
|
39
|
+
content: string;
|
|
40
|
+
isError?: boolean;
|
|
41
|
+
}
|
|
36
42
|
export interface ModelsWireResponse {
|
|
37
43
|
object: string;
|
|
38
44
|
data: ChatModel[] | null;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
}): import("react").JSX.Element | null;
|
|
1
|
+
/** Which allocation an organization model bills, with its spend so far. Shows
|
|
2
|
+
* only for an organization model, and picks the first allocation for it. */
|
|
3
|
+
export default function AllocationSelector(): import("react").JSX.Element | null;
|
|
@@ -1,55 +1,59 @@
|
|
|
1
|
+
import { FaAngleDown } from "../../icons/fa/FaAngleDown.js";
|
|
2
|
+
import { Select } from "../../components/Select.js";
|
|
3
|
+
import { useChatConfig } from "../core/config.js";
|
|
1
4
|
import { useChat } from "../core/ChatProvider.js";
|
|
2
|
-
import
|
|
5
|
+
import { chipClassName } from "../ui/chip.js";
|
|
3
6
|
import { isOrgModel } from "../utils.js";
|
|
4
|
-
import { useEffect } from "react";
|
|
5
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
import { useEffect } from "react";
|
|
6
9
|
import useSWR from "swr";
|
|
7
10
|
//#region src/ai/components/AllocationSelector.tsx
|
|
8
|
-
|
|
9
|
-
|
|
11
|
+
var money = (amount) => `$${amount.toFixed(2)}`;
|
|
12
|
+
/** Which allocation an organization model bills, with its spend so far. Shows
|
|
13
|
+
* only for an organization model, and picks the first allocation for it. */
|
|
14
|
+
function AllocationSelector() {
|
|
10
15
|
const { adapter, selectedProvider, selectedAllocation, setSelectedAllocation } = useChat();
|
|
16
|
+
const t = useChatConfig().strings.allocationPicker;
|
|
11
17
|
const isOrgProvider = isOrgModel(selectedProvider);
|
|
12
18
|
const allocationsAdapter = adapter.allocations;
|
|
13
19
|
const { data: allocations } = useSWR(isOrgProvider && allocationsAdapter ? ["ai-chat-allocations"] : null, () => allocationsAdapter?.list());
|
|
20
|
+
const first = allocations?.[0];
|
|
14
21
|
useEffect(() => {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
isOrgProvider,
|
|
19
|
-
allocations,
|
|
20
|
-
selectedAllocation,
|
|
21
|
-
setSelectedAllocation
|
|
22
|
-
]);
|
|
23
|
-
useEffect(() => {
|
|
24
|
-
if (!isOrgProvider && selectedAllocation) setSelectedAllocation(null);
|
|
22
|
+
if (!isOrgProvider) {
|
|
23
|
+
if (selectedAllocation) setSelectedAllocation(null);
|
|
24
|
+
} else if (first && !selectedAllocation) setSelectedAllocation(first.name);
|
|
25
25
|
}, [
|
|
26
26
|
isOrgProvider,
|
|
27
|
+
first,
|
|
27
28
|
selectedAllocation,
|
|
28
29
|
setSelectedAllocation
|
|
29
30
|
]);
|
|
30
31
|
if (!isOrgProvider || !allocations?.length) return null;
|
|
31
|
-
const options = allocations.map((alloc) => ({
|
|
32
|
-
label: alloc.name,
|
|
33
|
-
value: alloc.name
|
|
34
|
-
}));
|
|
35
32
|
const selected = allocations.find((a) => a.name === selectedAllocation);
|
|
36
33
|
return /* @__PURE__ */ jsxs("div", {
|
|
37
|
-
className: "flex items-center gap-2",
|
|
38
|
-
children: [/* @__PURE__ */ jsx(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
34
|
+
className: "flex min-w-0 items-center gap-2",
|
|
35
|
+
children: [/* @__PURE__ */ jsx(Select, {
|
|
36
|
+
"aria-label": t.choose,
|
|
37
|
+
items: allocations.map((a) => ({
|
|
38
|
+
value: a.name,
|
|
39
|
+
label: a.name
|
|
40
|
+
})),
|
|
41
|
+
value: selectedAllocation,
|
|
42
|
+
onValueChange: setSelectedAllocation,
|
|
43
|
+
trigger: /* @__PURE__ */ jsxs("button", {
|
|
44
|
+
type: "button",
|
|
45
|
+
className: chipClassName,
|
|
46
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
47
|
+
className: "truncate",
|
|
48
|
+
children: selected?.name ?? t.placeholder
|
|
49
|
+
}), /* @__PURE__ */ jsx(FaAngleDown, {
|
|
50
|
+
className: "h-3 w-3 shrink-0",
|
|
51
|
+
"aria-hidden": "true"
|
|
52
|
+
})]
|
|
53
|
+
})
|
|
54
|
+
}), selected && /* @__PURE__ */ jsx("span", {
|
|
46
55
|
className: "text-xs tabular-nums theme-muted-text whitespace-nowrap",
|
|
47
|
-
children:
|
|
48
|
-
"$",
|
|
49
|
-
(selected.used ?? 0).toFixed(2),
|
|
50
|
-
" / $",
|
|
51
|
-
selected.total.toFixed(2)
|
|
52
|
-
]
|
|
56
|
+
children: t.usage(money(selected.used ?? 0), money(selected.total))
|
|
53
57
|
})]
|
|
54
58
|
});
|
|
55
59
|
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { safeUrl } from "../../safeUrl.js";
|
|
2
1
|
import { BiSolidTrashAlt } from "../../icons/bi/BiSolidTrashAlt.js";
|
|
3
2
|
import { FiDownload } from "../../icons/fi/FiDownload.js";
|
|
4
3
|
import { FiFile } from "../../icons/fi/FiFile.js";
|
|
4
|
+
import { safeUrl } from "../../safeUrl.js";
|
|
5
5
|
import { useChatConfig } from "../core/config.js";
|
|
6
6
|
import { useChat } from "../core/ChatProvider.js";
|
|
7
7
|
import { formatFileSize } from "../utils.js";
|
|
8
8
|
import { attachmentsPageKey } from "./attachmentKeys.js";
|
|
9
|
-
import
|
|
9
|
+
import { clsx } from "clsx";
|
|
10
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
10
11
|
import { useCallback, useMemo, useState } from "react";
|
|
11
|
-
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
12
12
|
import { DateTime } from "luxon";
|
|
13
13
|
import useSWRInfinite from "swr/infinite";
|
|
14
14
|
//#region src/ai/components/AttachmentManager.tsx
|
|
@@ -74,7 +74,7 @@ function AttachmentManager() {
|
|
|
74
74
|
}) : attachments.length === 0 ? /* @__PURE__ */ jsx("p", {
|
|
75
75
|
className: "text-center theme-muted-text py-12 text-sm",
|
|
76
76
|
children: t.empty
|
|
77
|
-
}) : /* @__PURE__ */ jsxs(Fragment
|
|
77
|
+
}) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
|
|
78
78
|
className: "grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4",
|
|
79
79
|
children: attachments.map((att) => {
|
|
80
80
|
const downloadUrl = attachmentsAdapter.downloadUrl(att.id);
|
|
@@ -87,14 +87,14 @@ function AttachmentManager() {
|
|
|
87
87
|
style: { cursor: "pointer" }
|
|
88
88
|
} : {};
|
|
89
89
|
return /* @__PURE__ */ jsxs("div", {
|
|
90
|
-
className:
|
|
90
|
+
className: clsx("rounded-lg border theme-border overflow-hidden", isDeleting && "opacity-50 pointer-events-none"),
|
|
91
91
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
92
92
|
className: "relative aspect-square bg-(--theme-muted-panel-bg) flex items-center justify-center",
|
|
93
93
|
...openProps,
|
|
94
94
|
children: [/* @__PURE__ */ jsx(FiFile, { className: "w-8 h-8 theme-muted-text" }), showThumbnail && /* @__PURE__ */ jsx("img", {
|
|
95
95
|
src: downloadUrl,
|
|
96
96
|
alt: att.filename || "Image",
|
|
97
|
-
className:
|
|
97
|
+
className: clsx("absolute inset-0 w-full h-full object-cover", thumbnail !== "loaded" && "opacity-0"),
|
|
98
98
|
loading: "lazy",
|
|
99
99
|
onLoad: () => markThumbnail(att.id, "loaded"),
|
|
100
100
|
onError: () => markThumbnail(att.id, "broken")
|
|
@@ -108,7 +108,7 @@ function AttachmentManager() {
|
|
|
108
108
|
children: att.filename || "File"
|
|
109
109
|
}), /* @__PURE__ */ jsxs("p", {
|
|
110
110
|
className: "truncate theme-muted-text",
|
|
111
|
-
children: [att.size ? formatFileSize(att.size) : "", att.uploadedAt && /* @__PURE__ */ jsxs(Fragment
|
|
111
|
+
children: [att.size ? formatFileSize(att.size) : "", att.uploadedAt && /* @__PURE__ */ jsxs(Fragment, { children: [att.size ? " · " : "", DateTime.fromISO(att.uploadedAt).toRelative()] })]
|
|
112
112
|
})]
|
|
113
113
|
}), /* @__PURE__ */ jsxs("div", {
|
|
114
114
|
className: "flex items-center gap-0.5 flex-shrink-0",
|
|
@@ -7,9 +7,9 @@ import { useChatConfig } from "../core/config.js";
|
|
|
7
7
|
import { useChat } from "../core/ChatProvider.js";
|
|
8
8
|
import { formatFileSize } from "../utils.js";
|
|
9
9
|
import { ATTACHMENTS_INFINITE_KEY } from "./attachmentKeys.js";
|
|
10
|
-
import
|
|
10
|
+
import { clsx } from "clsx";
|
|
11
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
11
12
|
import { forwardRef, useCallback, useImperativeHandle, useRef, useState } from "react";
|
|
12
|
-
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
13
13
|
import { mutate } from "swr";
|
|
14
14
|
//#region src/ai/components/AttachmentUpload.tsx
|
|
15
15
|
var DEFAULT_MAX_FILE_SIZE = 26214400;
|
|
@@ -112,7 +112,7 @@ var AttachmentUpload = forwardRef(function AttachmentUpload({ conversationId, at
|
|
|
112
112
|
const handleClick = () => {
|
|
113
113
|
fileInputRef.current?.click();
|
|
114
114
|
};
|
|
115
|
-
return /* @__PURE__ */ jsxs(Fragment
|
|
115
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("input", {
|
|
116
116
|
ref: fileInputRef,
|
|
117
117
|
type: "file",
|
|
118
118
|
multiple: true,
|
|
@@ -127,7 +127,7 @@ var AttachmentUpload = forwardRef(function AttachmentUpload({ conversationId, at
|
|
|
127
127
|
children: attachments.map((attachment, index) => {
|
|
128
128
|
const Icon = getFileIcon(attachment.mimeType);
|
|
129
129
|
return /* @__PURE__ */ jsxs("div", {
|
|
130
|
-
className:
|
|
130
|
+
className: clsx("flex items-center gap-2 px-2 py-1 rounded border theme-border theme-muted-panel", attachment.error && "border-red-500"),
|
|
131
131
|
children: [
|
|
132
132
|
/* @__PURE__ */ jsx(Icon, { className: "w-4 h-4 theme-muted-text" }),
|
|
133
133
|
/* @__PURE__ */ jsx("span", {
|
|
@@ -163,7 +163,7 @@ var AttachmentUpload = forwardRef(function AttachmentUpload({ conversationId, at
|
|
|
163
163
|
onDragOver: handleDragOver,
|
|
164
164
|
onDragLeave: handleDragLeave,
|
|
165
165
|
onDrop: handleDrop,
|
|
166
|
-
className:
|
|
166
|
+
className: clsx("relative block w-full border-2 border-dashed rounded-lg p-3 text-center transition-colors", isDragging ? "border-blue-500 bg-blue-500/10" : "theme-border hover:theme-border", disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer"),
|
|
167
167
|
onClick: disabled ? void 0 : handleClick,
|
|
168
168
|
children: [/* @__PURE__ */ jsxs("span", {
|
|
169
169
|
className: "flex items-center justify-center gap-2 text-sm theme-muted-text",
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { FaAngleLeft } from "../../icons/fa/FaAngleLeft.js";
|
|
2
2
|
import { FaAngleRight } from "../../icons/fa/FaAngleRight.js";
|
|
3
3
|
import { useChatConfig } from "../core/config.js";
|
|
4
|
-
import { useMemo } from "react";
|
|
5
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { useMemo } from "react";
|
|
6
6
|
//#region src/ai/components/BranchNavigator.tsx
|
|
7
7
|
function findSiblings(messages, messageId) {
|
|
8
8
|
const message = messages.find((m) => m.id === messageId);
|
|
@@ -12,8 +12,8 @@ import DragOverlay from "./DragOverlay.js";
|
|
|
12
12
|
import ChatInput from "./ChatInput.js";
|
|
13
13
|
import ChatStage from "./ChatStage.js";
|
|
14
14
|
import ProviderIssueBanner from "./ProviderIssueBanner.js";
|
|
15
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
15
16
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
16
|
-
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
17
17
|
//#region src/ai/components/ChatEmptyState.tsx
|
|
18
18
|
function pickRandom(arr, count) {
|
|
19
19
|
return [...arr].sort(() => Math.random() - .5).slice(0, count);
|
|
@@ -138,7 +138,7 @@ function ChatEmptyState({ targetSession }) {
|
|
|
138
138
|
disabled: isLoading || isLoadingModels || models.length === 0,
|
|
139
139
|
placeholder: isLoadingModels ? tInput.placeholderLoading : hasLoadedModels && models.length === 0 ? tInput.placeholderNoProviders : tInput.placeholder,
|
|
140
140
|
settingsLeft: /* @__PURE__ */ jsx(ConnectToolsLink, {}),
|
|
141
|
-
settingsRight: /* @__PURE__ */ jsxs(Fragment
|
|
141
|
+
settingsRight: /* @__PURE__ */ jsxs(Fragment, { children: [slots.composerUsage, /* @__PURE__ */ jsx(ComposerControls, { targetSession })] })
|
|
142
142
|
}),
|
|
143
143
|
prompts.length > 0 && !(hasLoadedModels && models.length === 0) && /* @__PURE__ */ jsx("ul", {
|
|
144
144
|
className: "mx-auto max-w-[var(--chat-column,50rem)] px-8",
|
|
@@ -11,9 +11,9 @@ import DragOverlay from "./DragOverlay.js";
|
|
|
11
11
|
import { PasteFileCard } from "./PastedText.js";
|
|
12
12
|
import { SlashMenu, useSlashMenu } from "./SlashMenu.js";
|
|
13
13
|
import { usePasteCards } from "./usePasteCards.js";
|
|
14
|
-
import
|
|
14
|
+
import { clsx } from "clsx";
|
|
15
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
15
16
|
import { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from "react";
|
|
16
|
-
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
17
17
|
//#region src/ai/components/ChatInput.tsx
|
|
18
18
|
/** An optional box that is still empty should not wear the filled look of
|
|
19
19
|
* one waiting to send something. */
|
|
@@ -35,10 +35,10 @@ function AttachButton({ open, disabled, label, onClick }) {
|
|
|
35
35
|
onClick,
|
|
36
36
|
disabled,
|
|
37
37
|
"aria-expanded": open,
|
|
38
|
-
className:
|
|
38
|
+
className: clsx("flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full transition-colors", open ? "theme-text chat-tint-strong" : "theme-muted-text hover:theme-text hover:chat-tint", "disabled:opacity-50 disabled:cursor-not-allowed"),
|
|
39
39
|
"aria-label": label,
|
|
40
40
|
title: label,
|
|
41
|
-
children: /* @__PURE__ */ jsx(GoPlus, { className:
|
|
41
|
+
children: /* @__PURE__ */ jsx(GoPlus, { className: clsx("h-5 w-5 transition-transform duration-200", open && "rotate-45") })
|
|
42
42
|
});
|
|
43
43
|
}
|
|
44
44
|
function SendButton({ onClick, enabled, emphasised, label, testId }) {
|
|
@@ -47,7 +47,7 @@ function SendButton({ onClick, enabled, emphasised, label, testId }) {
|
|
|
47
47
|
onClick,
|
|
48
48
|
disabled: !enabled,
|
|
49
49
|
"data-testid": testId,
|
|
50
|
-
className:
|
|
50
|
+
className: clsx("flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full transition-[background-color,color,transform] duration-150 active:scale-95", emphasised ? "theme-element hover:opacity-90" : "chat-send-idle", !enabled && "cursor-not-allowed active:scale-100"),
|
|
51
51
|
"aria-label": label,
|
|
52
52
|
title: label,
|
|
53
53
|
children: /* @__PURE__ */ jsx(FaArrowUp, { className: "h-4 w-4" })
|
|
@@ -228,6 +228,7 @@ var ChatInput = forwardRef(function ChatInput({ onSend, disabled = false, placeh
|
|
|
228
228
|
className: "relative",
|
|
229
229
|
children: [slash && slashMenu.open && /* @__PURE__ */ jsx(SlashMenu, {
|
|
230
230
|
label: slash.label,
|
|
231
|
+
listId: slashMenu.listId,
|
|
231
232
|
matches: slashMenu.matches,
|
|
232
233
|
active: slashMenu.active,
|
|
233
234
|
onChoose: (name) => {
|
|
@@ -248,7 +249,7 @@ var ChatInput = forwardRef(function ChatInput({ onSend, disabled = false, placeh
|
|
|
248
249
|
})
|
|
249
250
|
}),
|
|
250
251
|
/* @__PURE__ */ jsxs("div", {
|
|
251
|
-
className:
|
|
252
|
+
className: clsx(!toolbar && "flex items-end gap-2 pr-2 pb-2"),
|
|
252
253
|
children: [/* @__PURE__ */ jsx("div", {
|
|
253
254
|
className: "max-h-[200px] min-w-0 flex-1 overflow-y-auto",
|
|
254
255
|
children: /* @__PURE__ */ jsx("textarea", {
|
|
@@ -257,11 +258,12 @@ var ChatInput = forwardRef(function ChatInput({ onSend, disabled = false, placeh
|
|
|
257
258
|
onChange: (e) => setInputAndPersist(e.target.value),
|
|
258
259
|
onKeyDown: handleKeyDown,
|
|
259
260
|
onPaste: handlePaste,
|
|
261
|
+
...slashMenu.inputProps,
|
|
260
262
|
placeholder: effectivePlaceholder,
|
|
261
263
|
"aria-label": inputLabel ?? effectivePlaceholder,
|
|
262
264
|
disabled,
|
|
263
265
|
rows: 1,
|
|
264
|
-
className:
|
|
266
|
+
className: clsx("theme-text block w-full resize-none overflow-hidden", "bg-transparent border-0 px-4", toolbar ? "pt-3.5 pb-1" : "pt-3 pb-1", "placeholder:theme-muted-text", "focus:outline-none focus:ring-0", "disabled:opacity-50 disabled:cursor-not-allowed", "text-[1rem] leading-relaxed")
|
|
265
267
|
})
|
|
266
268
|
}), !toolbar && action]
|
|
267
269
|
}),
|
|
@@ -286,7 +288,7 @@ var ChatInput = forwardRef(function ChatInput({ onSend, disabled = false, placeh
|
|
|
286
288
|
]
|
|
287
289
|
})]
|
|
288
290
|
}),
|
|
289
|
-
(settingsLeft || uploadedCount > 0) && /* @__PURE__ */ jsx(ComposerSettings, { left: /* @__PURE__ */ jsxs(Fragment
|
|
291
|
+
(settingsLeft || uploadedCount > 0) && /* @__PURE__ */ jsx(ComposerSettings, { left: /* @__PURE__ */ jsxs(Fragment, { children: [settingsLeft, uploadedCount > 0 && /* @__PURE__ */ jsx("span", {
|
|
290
292
|
className: "text-[11px] theme-muted-text",
|
|
291
293
|
children: t.filesAttached(uploadedCount)
|
|
292
294
|
})] }) }),
|
|
@@ -4,9 +4,9 @@ import { useChat } from "../core/ChatProvider.js";
|
|
|
4
4
|
import { KeyboardShortcutsProvider } from "../core/KeyboardShortcutsProvider.js";
|
|
5
5
|
import { SidebarToggle } from "../ui/sidebar.js";
|
|
6
6
|
import ChatSidebar from "./ChatSidebar.js";
|
|
7
|
-
import
|
|
8
|
-
import { useLayoutEffect, useRef, useState } from "react";
|
|
7
|
+
import { clsx } from "clsx";
|
|
9
8
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
import { useLayoutEffect, useRef, useState } from "react";
|
|
10
10
|
//#region src/ai/components/ChatLayout.tsx
|
|
11
11
|
/** The list at its default width beside the narrowest thread that still
|
|
12
12
|
* reads comfortably; narrower than that, 'auto' moves the list to a drawer. */
|
|
@@ -51,10 +51,9 @@ function ConversationLayout({ sidebar, children, sidebarMode = "inline", drawerB
|
|
|
51
51
|
const { strings } = useChatConfig();
|
|
52
52
|
return /* @__PURE__ */ jsxs("div", {
|
|
53
53
|
ref,
|
|
54
|
-
className:
|
|
54
|
+
className: clsx("flex h-full", drawer && "relative overflow-hidden"),
|
|
55
55
|
children: [sidebar, /* @__PURE__ */ jsxs("main", {
|
|
56
|
-
|
|
57
|
-
className: cx("flex min-w-0 flex-1 flex-col", CHAT_COLUMN_SCOPE),
|
|
56
|
+
className: clsx("flex min-w-0 flex-1 flex-col", CHAT_COLUMN_SCOPE),
|
|
58
57
|
children: [drawer && (drawerToggle || drawerBar) && /* @__PURE__ */ jsxs("div", {
|
|
59
58
|
className: "flex shrink-0 items-center gap-2 bg-(--theme-panel-bg) px-2 pt-2",
|
|
60
59
|
children: [drawerToggle && /* @__PURE__ */ jsx(SidebarToggle, {
|