@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
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
import { FaCheck } from "../../icons/fa/FaCheck.js";
|
|
2
1
|
import { FaChevronDown } from "../../icons/fa/FaChevronDown.js";
|
|
3
2
|
import { FaCloud } from "../../icons/fa/FaCloud.js";
|
|
4
|
-
import { FaCog } from "../../icons/fa/FaCog.js";
|
|
5
3
|
import { FaExclamationTriangle } from "../../icons/fa/FaExclamationTriangle.js";
|
|
6
|
-
import { FaRobot } from "../../icons/fa/FaRobot.js";
|
|
7
4
|
import { FaUndoAlt } from "../../icons/fa/FaUndoAlt.js";
|
|
5
|
+
import { Combobox } from "../../components/Combobox.js";
|
|
8
6
|
import { useChatConfig } from "../core/config.js";
|
|
9
|
-
import { providerIssueFor } from "../core/providerIssues.js";
|
|
7
|
+
import { issueDetail, issueLabel, providerIssueFor } from "../core/providerIssues.js";
|
|
10
8
|
import { useChat } from "../core/ChatProvider.js";
|
|
11
|
-
import
|
|
12
|
-
import {
|
|
13
|
-
import { Fragment
|
|
14
|
-
import {
|
|
9
|
+
import { chipClassName } from "../ui/chip.js";
|
|
10
|
+
import { clsx } from "clsx";
|
|
11
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
12
|
+
import { useMemo } from "react";
|
|
15
13
|
//#region src/ai/components/ModelSelector.tsx
|
|
16
14
|
var ORG_OWNER = "org";
|
|
17
15
|
/** Who registered a provider: the provider list's owner when the adapter
|
|
@@ -21,14 +19,11 @@ function providerOwner(info, model) {
|
|
|
21
19
|
if (info) return info.user === ORG_OWNER ? "" : info.user;
|
|
22
20
|
return model?.provider_owner ?? "";
|
|
23
21
|
}
|
|
24
|
-
/** The search field plus the list's max-h-80, so the flip decision matches
|
|
25
|
-
* what renders. */
|
|
26
|
-
var MAX_PANEL_HEIGHT_PX = 380;
|
|
27
22
|
function CspIcon({ cspKind, className }) {
|
|
28
23
|
const { slots } = useChatConfig();
|
|
29
24
|
const custom = slots.providerIcon?.(cspKind, className);
|
|
30
|
-
if (custom) return /* @__PURE__ */ jsx(Fragment
|
|
31
|
-
return /* @__PURE__ */ jsx(FaCloud, { className:
|
|
25
|
+
if (custom) return /* @__PURE__ */ jsx(Fragment, { children: custom });
|
|
26
|
+
return /* @__PURE__ */ jsx(FaCloud, { className: clsx("text-purple-500", className) });
|
|
32
27
|
}
|
|
33
28
|
function getProviderKeyFromModelId(modelId) {
|
|
34
29
|
const idx = modelId.indexOf("/");
|
|
@@ -54,339 +49,107 @@ function groupModelsByProvider(models) {
|
|
|
54
49
|
}
|
|
55
50
|
return groups;
|
|
56
51
|
}
|
|
57
|
-
|
|
58
|
-
|
|
52
|
+
/** Picks the model a conversation runs on, grouped by provider. Sessions that
|
|
53
|
+
* are still connecting or not responding show as headings with their status,
|
|
54
|
+
* so the reader sees why their models are missing. */
|
|
55
|
+
function ModelSelector({ targetSession }) {
|
|
59
56
|
const { providers, models, unreachableSessions, providerIssues, selectedProvider, setSelectedProvider, isLoadingModels, hasLoadedModels, modelsError, refreshModels, currentUser } = useChat();
|
|
60
|
-
const {
|
|
61
|
-
const
|
|
62
|
-
const
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const containerRef = useRef(null);
|
|
71
|
-
const dropdownRef = useRef(null);
|
|
72
|
-
const buttonRef = useRef(null);
|
|
73
|
-
const inputRef = useRef(null);
|
|
74
|
-
const [mounted, setMounted] = useState(false);
|
|
75
|
-
useEffect(() => {
|
|
76
|
-
setMounted(true);
|
|
77
|
-
}, []);
|
|
78
|
-
const updateDropdownPosition = useCallback(() => {
|
|
79
|
-
if (!buttonRef.current) return;
|
|
80
|
-
const rect = buttonRef.current.getBoundingClientRect();
|
|
81
|
-
const below = window.innerHeight - rect.bottom;
|
|
82
|
-
setDropdownPosition({
|
|
83
|
-
top: rect.bottom + 4,
|
|
84
|
-
bottom: window.innerHeight - rect.top + 4,
|
|
85
|
-
left: rect.left,
|
|
86
|
-
width: Math.max(rect.width, 320),
|
|
87
|
-
flipped: below < MAX_PANEL_HEIGHT_PX && rect.top > below
|
|
57
|
+
const { strings } = useChatConfig();
|
|
58
|
+
const t = strings.modelPicker;
|
|
59
|
+
const targetSessionFound = !!targetSession && models.some((m) => m.id.startsWith(`session:${targetSession}/`));
|
|
60
|
+
const targetSessionStatus = !targetSession ? null : isLoadingModels ? "connecting" : hasLoadedModels && !targetSessionFound ? "offline" : null;
|
|
61
|
+
const groups = useMemo(() => {
|
|
62
|
+
const status = (label, description) => ({
|
|
63
|
+
label,
|
|
64
|
+
icon: /* @__PURE__ */ jsx(FaCloud, { className: "h-3 w-3 text-amber-500" }),
|
|
65
|
+
description,
|
|
66
|
+
items: []
|
|
88
67
|
});
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const
|
|
94
|
-
const
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
updateDropdownPosition();
|
|
109
|
-
window.addEventListener("scroll", updateDropdownPosition, true);
|
|
110
|
-
window.addEventListener("resize", updateDropdownPosition);
|
|
111
|
-
return () => {
|
|
112
|
-
window.removeEventListener("scroll", updateDropdownPosition, true);
|
|
113
|
-
window.removeEventListener("resize", updateDropdownPosition);
|
|
114
|
-
};
|
|
115
|
-
}, [isOpen, updateDropdownPosition]);
|
|
116
|
-
useEffect(() => {
|
|
117
|
-
if (isOpen && inputRef.current) inputRef.current.focus();
|
|
118
|
-
}, [isOpen]);
|
|
119
|
-
const filteredModels = useMemo(() => {
|
|
120
|
-
if (!searchQuery.trim()) return models;
|
|
121
|
-
const query = searchQuery.toLowerCase();
|
|
122
|
-
return models.filter((model) => getModelNameFromId(model.id).toLowerCase().includes(query) || (model.provider || "").toLowerCase().includes(query) || (model.name || "").toLowerCase().includes(query));
|
|
123
|
-
}, [models, searchQuery]);
|
|
124
|
-
const groupedModels = useMemo(() => groupModelsByProvider(filteredModels), [filteredModels]);
|
|
125
|
-
const selectedModelInfo = models.find((m) => m.id === selectedProvider);
|
|
126
|
-
const selectedModelName = selectedModelInfo ? getModelLabel(selectedModelInfo) : null;
|
|
127
|
-
const selectedProviderName = selectedModelInfo?.provider || null;
|
|
128
|
-
const issueLabel = (issue) => issue.status === "unauthorized" ? strings.providerIssue.keyRejected : strings.providerIssue.unreachable;
|
|
129
|
-
const issueDetail = (issue) => issue.message || (issue.status === "unauthorized" ? strings.providerIssue.keyRejectedHint : strings.providerIssue.unreachableHint);
|
|
130
|
-
const issueSummary = (issue) => `${issueLabel(issue)} \u2014 ${issueDetail(issue)}`;
|
|
131
|
-
const selectedModelIssue = providerIssueFor(providerIssues, selectedModelInfo);
|
|
132
|
-
const handleSelect = (modelId) => {
|
|
133
|
-
setSelectedProvider(modelId);
|
|
134
|
-
setIsOpen(false);
|
|
135
|
-
setSearchQuery("");
|
|
136
|
-
};
|
|
137
|
-
const targetSessionDisplayName = targetSession?.split(":").pop();
|
|
138
|
-
const targetSessionFound = targetSession && models.length > 0 ? models.some((m) => m.id.startsWith(`session:${targetSession}/`)) : false;
|
|
139
|
-
const targetSessionStatus = targetSession ? isLoadingModels ? "connecting" : hasLoadedModels && !targetSessionFound ? "offline" : null : null;
|
|
140
|
-
const boxClass = bare ? "flex items-center gap-1.5 rounded-full h-8 px-2.5 min-w-0 max-w-[280px]" : "flex items-center gap-2 px-3 h-8 rounded-lg min-w-[180px] max-w-[280px]";
|
|
141
|
-
const textClass = bare ? "text-[13px]" : "text-sm";
|
|
142
|
-
if (isLoadingModels && models.length === 0 && !targetSession) return /* @__PURE__ */ jsx("div", {
|
|
143
|
-
ref: containerRef,
|
|
144
|
-
className: "relative min-w-0",
|
|
145
|
-
children: /* @__PURE__ */ jsxs("div", {
|
|
146
|
-
className: boxClass,
|
|
147
|
-
children: [!bare && /* @__PURE__ */ jsx(FaRobot, { className: "h-4 w-4 text-purple-500 flex-shrink-0 animate-pulse" }), /* @__PURE__ */ jsx("span", {
|
|
148
|
-
className: cx(textClass, "theme-muted-text"),
|
|
149
|
-
children: "Loading models..."
|
|
150
|
-
})]
|
|
151
|
-
})
|
|
152
|
-
});
|
|
153
|
-
if (modelsError && models.length === 0) return /* @__PURE__ */ jsx("div", {
|
|
154
|
-
ref: containerRef,
|
|
155
|
-
className: "relative min-w-0",
|
|
156
|
-
children: /* @__PURE__ */ jsxs("div", {
|
|
157
|
-
className: boxClass,
|
|
158
|
-
children: [
|
|
159
|
-
!bare && /* @__PURE__ */ jsx(FaRobot, { className: "h-4 w-4 text-red-500 flex-shrink-0" }),
|
|
160
|
-
/* @__PURE__ */ jsx("span", {
|
|
161
|
-
className: cx(textClass, "text-red-600"),
|
|
162
|
-
children: "Failed to load models"
|
|
68
|
+
const sessions = unreachableSessions.filter((s) => `${s.owner}:${s.name}` !== targetSession).map((s) => status(s.name, t.notResponding));
|
|
69
|
+
if (targetSession && targetSessionStatus) sessions.unshift(status(targetSession.split(":").pop() ?? targetSession, targetSessionStatus === "connecting" ? t.connecting : t.notResponding));
|
|
70
|
+
const byProvider = [...groupModelsByProvider(models)].map(([providerKey, providerModels]) => {
|
|
71
|
+
const info = providers.find((p) => `${p.user}:${p.name}` === providerKey);
|
|
72
|
+
const first = providerModels[0];
|
|
73
|
+
const name = first?.provider || info?.name || providerKey;
|
|
74
|
+
const platform = info?.platformLabel;
|
|
75
|
+
const owner = providerOwner(info, first);
|
|
76
|
+
const issue = providerIssueFor(providerIssues, first);
|
|
77
|
+
const description = [
|
|
78
|
+
platform && platform.toLowerCase() !== name.toLowerCase() ? platform : null,
|
|
79
|
+
owner && owner !== currentUser.username ? t.sharedBy(owner) : null,
|
|
80
|
+
issue ? issueLabel(strings.providerIssue, issue) : null
|
|
81
|
+
].filter(Boolean).join(" · ");
|
|
82
|
+
return {
|
|
83
|
+
label: name,
|
|
84
|
+
icon: /* @__PURE__ */ jsx(CspIcon, {
|
|
85
|
+
cspKind: info?.cspKind ?? "other",
|
|
86
|
+
className: "h-3 w-3"
|
|
163
87
|
}),
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
})
|
|
170
|
-
|
|
171
|
-
})
|
|
88
|
+
description: description || void 0,
|
|
89
|
+
items: providerModels.map((model) => ({
|
|
90
|
+
label: getModelLabel(model),
|
|
91
|
+
value: model.id,
|
|
92
|
+
disabled: !!issue
|
|
93
|
+
}))
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
return [...sessions, ...byProvider];
|
|
97
|
+
}, [
|
|
98
|
+
models,
|
|
99
|
+
providers,
|
|
100
|
+
providerIssues,
|
|
101
|
+
unreachableSessions,
|
|
102
|
+
targetSession,
|
|
103
|
+
targetSessionStatus,
|
|
104
|
+
currentUser.username,
|
|
105
|
+
strings.providerIssue,
|
|
106
|
+
t
|
|
107
|
+
]);
|
|
108
|
+
if (isLoadingModels && models.length === 0 && !targetSession) return /* @__PURE__ */ jsx("span", {
|
|
109
|
+
className: clsx(chipClassName, "cursor-default animate-pulse"),
|
|
110
|
+
children: t.loading
|
|
172
111
|
});
|
|
173
|
-
if (
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
112
|
+
if (modelsError && models.length === 0) return /* @__PURE__ */ jsxs("span", {
|
|
113
|
+
className: clsx(chipClassName, "cursor-default text-(--theme-error)"),
|
|
114
|
+
children: [t.failed, /* @__PURE__ */ jsxs("button", {
|
|
115
|
+
type: "button",
|
|
116
|
+
onClick: () => refreshModels(),
|
|
117
|
+
className: "flex items-center gap-1 text-xs text-(--theme-element) hover:underline",
|
|
118
|
+
children: [/* @__PURE__ */ jsx(FaUndoAlt, { className: "h-2.5 w-2.5" }), t.retry]
|
|
119
|
+
})]
|
|
120
|
+
});
|
|
121
|
+
if (groups.length === 0 && hasLoadedModels) return /* @__PURE__ */ jsx("span", {
|
|
122
|
+
className: clsx(chipClassName, "cursor-default"),
|
|
123
|
+
children: t.none
|
|
183
124
|
});
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
125
|
+
const selected = models.find((m) => m.id === selectedProvider);
|
|
126
|
+
const issue = providerIssueFor(providerIssues, selected);
|
|
127
|
+
return /* @__PURE__ */ jsx(Combobox, {
|
|
128
|
+
"aria-label": t.choose,
|
|
129
|
+
placeholder: t.search,
|
|
130
|
+
items: groups,
|
|
131
|
+
value: selectedProvider,
|
|
132
|
+
onValueChange: (id) => id && setSelectedProvider(id),
|
|
133
|
+
disabled: groups.length === 0,
|
|
134
|
+
triggerTooltip: issue ? `${issueLabel(strings.providerIssue, issue)} \u2014 ${issueDetail(strings.providerIssue, issue)}` : void 0,
|
|
135
|
+
trigger: /* @__PURE__ */ jsxs("button", {
|
|
190
136
|
type: "button",
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
children: selectedModelName ? /* @__PURE__ */ jsxs("div", {
|
|
201
|
-
className: "flex items-baseline gap-1.5 min-w-0",
|
|
202
|
-
children: [
|
|
203
|
-
/* @__PURE__ */ jsx("span", {
|
|
204
|
-
title: selectedModelName,
|
|
205
|
-
className: cx("truncate", bare ? "text-[13px] font-medium theme-text" : "text-sm font-medium text-(--theme-panel)"),
|
|
206
|
-
children: selectedModelName
|
|
207
|
-
}),
|
|
208
|
-
selectedProviderName && /* @__PURE__ */ jsx("span", {
|
|
209
|
-
title: selectedProviderName,
|
|
210
|
-
className: "theme-muted-text truncate flex-shrink-0 max-w-[45%] text-xs",
|
|
211
|
-
children: selectedProviderName
|
|
212
|
-
}),
|
|
213
|
-
selectedModelIssue && /* @__PURE__ */ jsx(FaExclamationTriangle, {
|
|
214
|
-
title: issueSummary(selectedModelIssue),
|
|
215
|
-
className: "h-3.5 w-3.5 text-amber-500 flex-shrink-0 self-center"
|
|
216
|
-
})
|
|
217
|
-
]
|
|
218
|
-
}) : /* @__PURE__ */ jsx("span", {
|
|
219
|
-
className: cx(textClass, targetSessionStatus === "connecting" ? "text-amber-600" : "theme-muted-text"),
|
|
220
|
-
children: triggerText
|
|
221
|
-
})
|
|
137
|
+
className: chipClassName,
|
|
138
|
+
children: [selected ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
139
|
+
/* @__PURE__ */ jsx("span", {
|
|
140
|
+
className: "truncate font-medium theme-text",
|
|
141
|
+
children: getModelLabel(selected)
|
|
142
|
+
}),
|
|
143
|
+
selected.provider && /* @__PURE__ */ jsx("span", {
|
|
144
|
+
className: "max-w-[45%] shrink-0 truncate text-xs",
|
|
145
|
+
children: selected.provider
|
|
222
146
|
}),
|
|
223
|
-
/* @__PURE__ */ jsx(
|
|
224
|
-
]
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
onKeyDown: (e) => {
|
|
230
|
-
if (e.key === "Escape") {
|
|
231
|
-
e.preventDefault();
|
|
232
|
-
setIsOpen(false);
|
|
233
|
-
buttonRef.current?.focus();
|
|
234
|
-
}
|
|
235
|
-
},
|
|
236
|
-
style: {
|
|
237
|
-
position: "fixed",
|
|
238
|
-
...dropdownPosition.flipped ? { bottom: dropdownPosition.bottom } : { top: dropdownPosition.top },
|
|
239
|
-
left: dropdownPosition.left,
|
|
240
|
-
width: dropdownPosition.width,
|
|
241
|
-
zIndex: 9999
|
|
242
|
-
},
|
|
243
|
-
className: cx("theme-panel rounded-lg shadow-lg", "border theme-border", "overflow-hidden"),
|
|
244
|
-
children: [/* @__PURE__ */ jsx("div", {
|
|
245
|
-
className: "p-2 border-b theme-border",
|
|
246
|
-
children: /* @__PURE__ */ jsx("input", {
|
|
247
|
-
ref: inputRef,
|
|
248
|
-
type: "text",
|
|
249
|
-
value: searchQuery,
|
|
250
|
-
onChange: (e) => setSearchQuery(e.target.value),
|
|
251
|
-
placeholder: "Search models...",
|
|
252
|
-
className: cx("w-full px-3 py-2 text-sm rounded-md", "theme-muted-panel", "border theme-border", "focus:outline-none focus:ring-2 focus:ring-blue-500", "placeholder:theme-muted-text")
|
|
253
|
-
})
|
|
254
|
-
}), /* @__PURE__ */ jsxs("div", {
|
|
255
|
-
className: "max-h-80 overflow-y-auto",
|
|
256
|
-
children: [
|
|
257
|
-
targetSessionStatus && /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
|
|
258
|
-
className: "px-3 py-1.5 bg-amber-500/5 sticky top-0",
|
|
259
|
-
children: /* @__PURE__ */ jsxs("div", {
|
|
260
|
-
className: "flex items-center gap-2",
|
|
261
|
-
children: [
|
|
262
|
-
/* @__PURE__ */ jsx(FaCloud, { className: "h-3 w-3 text-amber-500" }),
|
|
263
|
-
/* @__PURE__ */ jsx("span", {
|
|
264
|
-
className: "text-xs font-semibold text-amber-600 uppercase tracking-wide flex-1",
|
|
265
|
-
children: targetSessionDisplayName
|
|
266
|
-
}),
|
|
267
|
-
/* @__PURE__ */ jsx("span", {
|
|
268
|
-
className: cx("text-[10px] px-1.5 py-0.5 rounded-full", targetSessionStatus === "connecting" ? "text-amber-600 bg-amber-500/10 animate-pulse" : "text-amber-500 bg-amber-500/10"),
|
|
269
|
-
children: targetSessionStatus === "connecting" ? "Connecting..." : "Not responding"
|
|
270
|
-
})
|
|
271
|
-
]
|
|
272
|
-
})
|
|
273
|
-
}), /* @__PURE__ */ jsx("div", {
|
|
274
|
-
className: "px-4 py-2 flex items-center gap-2",
|
|
275
|
-
children: /* @__PURE__ */ jsx("span", {
|
|
276
|
-
className: "text-sm theme-muted-text italic",
|
|
277
|
-
children: targetSessionStatus === "connecting" ? "Loading session models..." : "Service not responding — ensure it is running on the remote host"
|
|
278
|
-
})
|
|
279
|
-
})] }),
|
|
280
|
-
unreachableSessions.filter((s) => !targetSession || `${s.owner}:${s.name}` !== targetSession).map((s) => /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
|
|
281
|
-
className: "px-3 py-1.5 bg-amber-500/5 sticky top-0",
|
|
282
|
-
children: /* @__PURE__ */ jsxs("div", {
|
|
283
|
-
className: "flex items-center gap-2",
|
|
284
|
-
children: [
|
|
285
|
-
/* @__PURE__ */ jsx(FaCloud, { className: "h-3 w-3 text-amber-500" }),
|
|
286
|
-
/* @__PURE__ */ jsx("span", {
|
|
287
|
-
className: "text-xs font-semibold text-amber-600 uppercase tracking-wide flex-1",
|
|
288
|
-
children: s.name
|
|
289
|
-
}),
|
|
290
|
-
/* @__PURE__ */ jsx("span", {
|
|
291
|
-
className: "text-[10px] text-amber-500 bg-amber-500/10 px-1.5 py-0.5 rounded-full",
|
|
292
|
-
children: "Not responding"
|
|
293
|
-
})
|
|
294
|
-
]
|
|
295
|
-
})
|
|
296
|
-
}), /* @__PURE__ */ jsx("div", {
|
|
297
|
-
className: "px-4 py-2 flex items-center gap-2",
|
|
298
|
-
children: /* @__PURE__ */ jsx("span", {
|
|
299
|
-
className: "text-sm theme-muted-text italic",
|
|
300
|
-
children: "Service not responding — ensure it is running on the remote host"
|
|
301
|
-
})
|
|
302
|
-
})] }, `${s.owner}:${s.name}`)),
|
|
303
|
-
groupedModels.size === 0 && !targetSessionStatus && unreachableSessions.length === 0 ? /* @__PURE__ */ jsx("div", {
|
|
304
|
-
className: "p-4 text-center theme-muted-text text-sm",
|
|
305
|
-
children: "No models found"
|
|
306
|
-
}) : Array.from(groupedModels.entries()).map(([providerKey, providerModels]) => {
|
|
307
|
-
const providerInfo = providers.find((p) => `${p.user}:${p.name}` === providerKey);
|
|
308
|
-
const providerName = providerModels[0]?.provider || providerInfo?.name || "Unknown Provider";
|
|
309
|
-
const platform = providerInfo?.platformLabel;
|
|
310
|
-
const platformSuffix = providerInfo && platform && platform.toLowerCase() !== providerName.toLowerCase() ? platform : null;
|
|
311
|
-
const isOwner = providerInfo?.user === currentUser.username;
|
|
312
|
-
const owner = providerOwner(providerInfo, providerModels[0]);
|
|
313
|
-
const headerSuffix = [platformSuffix, owner && owner !== currentUser.username ? strings.modelPicker.sharedBy(owner) : null].filter(Boolean).join(" · ");
|
|
314
|
-
const settingsHref = providerInfo && extraLinks.providerSettings ? extraLinks.providerSettings(providerInfo.user, providerInfo.name) : null;
|
|
315
|
-
const issue = providerIssueFor(providerIssues, providerModels[0]);
|
|
316
|
-
return /* @__PURE__ */ jsxs("div", { children: [
|
|
317
|
-
/* @__PURE__ */ jsx("div", {
|
|
318
|
-
className: "px-3 py-1.5 theme-muted-panel sticky top-0",
|
|
319
|
-
children: /* @__PURE__ */ jsxs("div", {
|
|
320
|
-
className: "flex items-center gap-2",
|
|
321
|
-
children: [
|
|
322
|
-
/* @__PURE__ */ jsx(CspIcon, {
|
|
323
|
-
cspKind: providerInfo?.cspKind ?? "other",
|
|
324
|
-
className: "h-3 w-3"
|
|
325
|
-
}),
|
|
326
|
-
/* @__PURE__ */ jsx("span", {
|
|
327
|
-
className: "text-xs font-semibold theme-muted-text uppercase tracking-wide",
|
|
328
|
-
children: providerName
|
|
329
|
-
}),
|
|
330
|
-
/* @__PURE__ */ jsx("span", {
|
|
331
|
-
className: "min-w-0 flex-1 truncate text-xs theme-muted-text",
|
|
332
|
-
children: headerSuffix ? `· ${headerSuffix}` : ""
|
|
333
|
-
}),
|
|
334
|
-
/* @__PURE__ */ jsxs("span", {
|
|
335
|
-
className: "text-xs theme-muted-text",
|
|
336
|
-
children: [
|
|
337
|
-
"(",
|
|
338
|
-
providerModels.length,
|
|
339
|
-
")"
|
|
340
|
-
]
|
|
341
|
-
}),
|
|
342
|
-
issue && /* @__PURE__ */ jsx("span", {
|
|
343
|
-
className: "text-[10px] text-amber-600 bg-amber-500/10 px-1.5 py-0.5 rounded-full flex-shrink-0",
|
|
344
|
-
children: issueLabel(issue)
|
|
345
|
-
}),
|
|
346
|
-
isOwner && settingsHref && /* @__PURE__ */ jsx(LinkComponent, {
|
|
347
|
-
target: {
|
|
348
|
-
kind: "external",
|
|
349
|
-
href: settingsHref
|
|
350
|
-
},
|
|
351
|
-
onClick: (e) => e.stopPropagation(),
|
|
352
|
-
className: "p-0.5 rounded hover:theme-hover theme-muted-text hover:text-blue-600 transition-colors",
|
|
353
|
-
title: "Manage provider",
|
|
354
|
-
children: /* @__PURE__ */ jsx(FaCog, { className: "h-3 w-3" })
|
|
355
|
-
})
|
|
356
|
-
]
|
|
357
|
-
})
|
|
358
|
-
}),
|
|
359
|
-
issue && /* @__PURE__ */ jsxs("div", {
|
|
360
|
-
className: "flex items-start gap-2 px-4 py-1.5 bg-amber-500/5",
|
|
361
|
-
children: [/* @__PURE__ */ jsx(FaExclamationTriangle, { className: "h-3 w-3 text-amber-500 flex-shrink-0 mt-0.5" }), /* @__PURE__ */ jsx("span", {
|
|
362
|
-
className: "text-xs text-amber-600 line-clamp-2",
|
|
363
|
-
children: issueDetail(issue)
|
|
364
|
-
})]
|
|
365
|
-
}),
|
|
366
|
-
providerModels.map((model) => {
|
|
367
|
-
const modelName = getModelLabel(model);
|
|
368
|
-
const isSelected = model.id === selectedProvider;
|
|
369
|
-
return /* @__PURE__ */ jsxs("button", {
|
|
370
|
-
type: "button",
|
|
371
|
-
"aria-disabled": !!issue,
|
|
372
|
-
title: issue ? issueSummary(issue) : void 0,
|
|
373
|
-
onClick: () => {
|
|
374
|
-
if (!issue) handleSelect(model.id);
|
|
375
|
-
},
|
|
376
|
-
className: cx("w-full flex items-center gap-3 px-4 py-2 text-left", issue ? "opacity-40 cursor-not-allowed" : "cursor-pointer hover:bg-blue-500/5 transition-colors", isSelected && "bg-blue-500/10"),
|
|
377
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
378
|
-
title: modelName,
|
|
379
|
-
className: cx("flex-1 min-w-0 text-sm truncate", isSelected ? "text-blue-600 font-medium" : ""),
|
|
380
|
-
style: !isSelected ? { color: "var(--theme-panel, #1f2937)" } : void 0,
|
|
381
|
-
children: modelName
|
|
382
|
-
}), isSelected && /* @__PURE__ */ jsx(FaCheck, { className: "h-3.5 w-3.5 text-blue-600 flex-shrink-0" })]
|
|
383
|
-
}, model.id);
|
|
384
|
-
})
|
|
385
|
-
] }, providerKey);
|
|
386
|
-
})
|
|
387
|
-
]
|
|
388
|
-
})]
|
|
389
|
-
}), document.body)]
|
|
147
|
+
issue && /* @__PURE__ */ jsx(FaExclamationTriangle, { className: "h-3.5 w-3.5 shrink-0 text-amber-500" })
|
|
148
|
+
] }) : /* @__PURE__ */ jsx("span", {
|
|
149
|
+
className: clsx("truncate", targetSessionStatus === "connecting" && "animate-pulse"),
|
|
150
|
+
children: targetSessionStatus === "connecting" ? t.connecting : t.select
|
|
151
|
+
}), /* @__PURE__ */ jsx(FaChevronDown, { className: "h-3 w-3 shrink-0" })]
|
|
152
|
+
})
|
|
390
153
|
});
|
|
391
154
|
}
|
|
392
155
|
//#endregion
|
|
@@ -6,9 +6,9 @@ import { formatFileSize } from "../utils.js";
|
|
|
6
6
|
import Markdown from "../ui/Markdown.js";
|
|
7
7
|
import { fencePaste } from "../core/pasteLanguage.js";
|
|
8
8
|
import { splitPastes } from "../core/pastes.js";
|
|
9
|
-
import
|
|
10
|
-
import { Fragment } from "react";
|
|
11
|
-
import { Fragment as Fragment$1
|
|
9
|
+
import { clsx } from "clsx";
|
|
10
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
+
import { Fragment as Fragment$1 } from "react";
|
|
12
12
|
//#region src/ai/components/PastedText.tsx
|
|
13
13
|
/** A paste sent, or about to be sent, as its saved file. */
|
|
14
14
|
function PasteFileCard({ id, lines, bytes, saving = false, error, onRemove }) {
|
|
@@ -16,7 +16,7 @@ function PasteFileCard({ id, lines, bytes, saving = false, error, onRemove }) {
|
|
|
16
16
|
const detail = error ?? (saving ? t.saving : `${t.lines(lines)} · ${formatFileSize(bytes)}`);
|
|
17
17
|
return /* @__PURE__ */ jsxs("div", {
|
|
18
18
|
"data-testid": "paste-file-card",
|
|
19
|
-
className:
|
|
19
|
+
className: clsx("inline-flex max-w-full items-center gap-2.5 rounded-xl px-3 py-2 align-top whitespace-normal", "bg-(--theme-panel-bg)", error ? "border border-red-500" : "chat-hairline"),
|
|
20
20
|
children: [
|
|
21
21
|
/* @__PURE__ */ jsx(FiFile, { className: "h-5 w-5 flex-shrink-0 theme-muted-text" }),
|
|
22
22
|
/* @__PURE__ */ jsxs("div", {
|
|
@@ -25,7 +25,7 @@ function PasteFileCard({ id, lines, bytes, saving = false, error, onRemove }) {
|
|
|
25
25
|
className: "truncate text-sm font-medium theme-text",
|
|
26
26
|
children: id ? `paste-${id}.txt` : t.title
|
|
27
27
|
}), /* @__PURE__ */ jsx("div", {
|
|
28
|
-
className:
|
|
28
|
+
className: clsx("truncate text-xs", error ? "text-red-500" : "theme-muted-text"),
|
|
29
29
|
children: detail
|
|
30
30
|
})]
|
|
31
31
|
}),
|
|
@@ -45,23 +45,23 @@ function PasteFileCard({ id, lines, bytes, saving = false, error, onRemove }) {
|
|
|
45
45
|
* it reads as, one sent as its file as a card. The blank lines the composer puts around a
|
|
46
46
|
* paste would stack on the block's own spacing, so they are dropped. */
|
|
47
47
|
function UserMessageText({ content, pastes }) {
|
|
48
|
-
if (!pastes?.length) return /* @__PURE__ */ jsx(Fragment
|
|
48
|
+
if (!pastes?.length) return /* @__PURE__ */ jsx(Fragment, { children: content });
|
|
49
49
|
const segments = splitPastes(content, pastes);
|
|
50
50
|
const shows = (seg) => typeof seg !== "string" || seg.trim() !== "";
|
|
51
|
-
const blockSpacing = (i) =>
|
|
52
|
-
return /* @__PURE__ */ jsx(Fragment
|
|
51
|
+
const blockSpacing = (i) => clsx(segments.slice(0, i).some(shows) ? "mt-2.5" : "mt-0", segments.slice(i + 1).some(shows) ? "mb-2.5" : "mb-0");
|
|
52
|
+
return /* @__PURE__ */ jsx(Fragment, { children: segments.map((segment, i) => {
|
|
53
53
|
if (typeof segment === "string") {
|
|
54
54
|
let text = segment;
|
|
55
55
|
if (i > 0) text = text.replace(/^\n+/, "");
|
|
56
56
|
if (i < segments.length - 1) text = text.replace(/\n+$/, "");
|
|
57
|
-
return text ? /* @__PURE__ */ jsx(Fragment, { children: text }, i) : null;
|
|
57
|
+
return text ? /* @__PURE__ */ jsx(Fragment$1, { children: text }, i) : null;
|
|
58
58
|
}
|
|
59
59
|
if (segment.inline && segment.text !== void 0) return /* @__PURE__ */ jsx("span", {
|
|
60
|
-
className:
|
|
60
|
+
className: clsx("chat-paste-code block", blockSpacing(i)),
|
|
61
61
|
children: /* @__PURE__ */ jsx(Markdown, { children: fencePaste(segment.text) })
|
|
62
62
|
}, i);
|
|
63
63
|
return /* @__PURE__ */ jsx("span", {
|
|
64
|
-
className:
|
|
64
|
+
className: clsx("flex [&>*]:flex-1", blockSpacing(i)),
|
|
65
65
|
children: /* @__PURE__ */ jsx(PasteFileCard, {
|
|
66
66
|
id: segment.id,
|
|
67
67
|
lines: segment.lines,
|
|
@@ -1,29 +1,47 @@
|
|
|
1
1
|
import { FaExclamationTriangle } from "../../icons/fa/FaExclamationTriangle.js";
|
|
2
|
+
import { IoSettingsOutline } from "../../icons/io5/IoSettingsOutline.js";
|
|
2
3
|
import { useChatConfig } from "../core/config.js";
|
|
3
|
-
import { providerIssueFor } from "../core/providerIssues.js";
|
|
4
|
+
import { issueDetail, providerIssueFor } from "../core/providerIssues.js";
|
|
4
5
|
import { useChat } from "../core/ChatProvider.js";
|
|
6
|
+
import { getProviderKeyFromModelId } from "./ModelSelector.js";
|
|
5
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
8
|
//#region src/ai/components/ProviderIssueBanner.tsx
|
|
9
|
+
/** Why the selected model can't answer, with a way to fix it for the
|
|
10
|
+
* provider's owner, who alone can change its key or endpoint. */
|
|
7
11
|
function ProviderIssueBanner() {
|
|
8
|
-
const { strings } = useChatConfig();
|
|
9
|
-
const { models, providerIssues, selectedProvider } = useChat();
|
|
12
|
+
const { strings, extraLinks, LinkComponent } = useChatConfig();
|
|
13
|
+
const { models, providers, providerIssues, selectedProvider, currentUser } = useChat();
|
|
10
14
|
const selectedModel = models.find((m) => m.id === selectedProvider);
|
|
11
15
|
const issue = providerIssueFor(providerIssues, selectedModel);
|
|
12
|
-
if (!issue) return null;
|
|
13
|
-
const
|
|
14
|
-
const
|
|
16
|
+
if (!selectedModel || !issue) return null;
|
|
17
|
+
const t = strings.providerIssue;
|
|
18
|
+
const title = issue.status === "unauthorized" ? t.bannerKeyRejected(issue.provider) : t.bannerUnreachable(issue.provider);
|
|
19
|
+
const providerKey = getProviderKeyFromModelId(selectedModel.id);
|
|
20
|
+
const provider = providers.find((p) => `${p.user}:${p.name}` === providerKey);
|
|
21
|
+
const settingsHref = provider?.user === currentUser.username ? extraLinks.providerSettings?.(provider.user, provider.name) : void 0;
|
|
15
22
|
return /* @__PURE__ */ jsxs("div", {
|
|
16
23
|
className: "mx-4 mt-2 flex items-start gap-3 p-3 rounded-lg bg-amber-500/10 border border-amber-500/20",
|
|
17
|
-
children: [
|
|
18
|
-
className: "
|
|
19
|
-
|
|
20
|
-
className: "
|
|
21
|
-
children:
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
children: [
|
|
25
|
+
/* @__PURE__ */ jsx(FaExclamationTriangle, { className: "w-4 h-4 text-amber-500 shrink-0 mt-0.5" }),
|
|
26
|
+
/* @__PURE__ */ jsxs("div", {
|
|
27
|
+
className: "flex-1 min-w-0",
|
|
28
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
29
|
+
className: "text-sm font-medium text-amber-700",
|
|
30
|
+
children: title
|
|
31
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
32
|
+
className: "text-xs text-amber-600/80 mt-0.5",
|
|
33
|
+
children: issueDetail(t, issue)
|
|
34
|
+
})]
|
|
35
|
+
}),
|
|
36
|
+
settingsHref && /* @__PURE__ */ jsxs(LinkComponent, {
|
|
37
|
+
target: {
|
|
38
|
+
kind: "external",
|
|
39
|
+
href: settingsHref
|
|
40
|
+
},
|
|
41
|
+
className: "flex shrink-0 items-center gap-1 rounded-md px-2 py-1 text-xs font-medium text-amber-700 transition-colors hover:bg-amber-500/10",
|
|
42
|
+
children: [/* @__PURE__ */ jsx(IoSettingsOutline, { className: "h-3.5 w-3.5" }), strings.modelPicker.manageProvider]
|
|
43
|
+
})
|
|
44
|
+
]
|
|
27
45
|
});
|
|
28
46
|
}
|
|
29
47
|
//#endregion
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { LuChevronRight } from "../../icons/lu/LuChevronRight.js";
|
|
2
|
-
import
|
|
2
|
+
import { clsx } from "clsx";
|
|
3
3
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
4
|
//#region src/ai/components/Reasoning.tsx
|
|
5
5
|
/** The line a turn's reasoning folds under. It opens the reasoning in place,
|
|
@@ -12,16 +12,16 @@ function ReasoningToggle({ open, onToggle, shimmer = false, children }) {
|
|
|
12
12
|
"aria-expanded": open,
|
|
13
13
|
className: "group/reasoning -ml-1 flex items-center gap-x-1 rounded-md px-1 py-0.5 text-sm theme-muted-text transition-colors hover:theme-text",
|
|
14
14
|
children: [/* @__PURE__ */ jsx("span", {
|
|
15
|
-
className:
|
|
15
|
+
className: clsx(shimmer && "animate-shimmer text-shimmer"),
|
|
16
16
|
children
|
|
17
|
-
}), /* @__PURE__ */ jsx(LuChevronRight, { className:
|
|
17
|
+
}), /* @__PURE__ */ jsx(LuChevronRight, { className: clsx("h-3.5 w-3.5 transition-transform duration-200 motion-reduce:transition-none", open && "rotate-90") })]
|
|
18
18
|
});
|
|
19
19
|
}
|
|
20
20
|
/** The reasoning itself: smaller and quieter than the answer, hung off a
|
|
21
21
|
* hairline of the accent so it reads as the assistant's aside. */
|
|
22
22
|
function ReasoningBody({ className, children }) {
|
|
23
23
|
return /* @__PURE__ */ jsx("div", {
|
|
24
|
-
className:
|
|
24
|
+
className: clsx("chat-reasoning-body ml-1 mt-1.5 mb-3 pl-4 text-sm leading-relaxed theme-muted-text", className),
|
|
25
25
|
children
|
|
26
26
|
});
|
|
27
27
|
}
|