@draht/web-ui 2026.3.2-2
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/CHANGELOG.md +276 -0
- package/README.md +601 -0
- package/dist/ChatPanel.d.ts +28 -0
- package/dist/ChatPanel.d.ts.map +1 -0
- package/dist/ChatPanel.js +193 -0
- package/dist/ChatPanel.js.map +1 -0
- package/dist/app.css +2 -0
- package/dist/components/AgentInterface.d.ts +39 -0
- package/dist/components/AgentInterface.d.ts.map +1 -0
- package/dist/components/AgentInterface.js +384 -0
- package/dist/components/AgentInterface.js.map +1 -0
- package/dist/components/AttachmentTile.d.ts +12 -0
- package/dist/components/AttachmentTile.d.ts.map +1 -0
- package/dist/components/AttachmentTile.js +110 -0
- package/dist/components/AttachmentTile.js.map +1 -0
- package/dist/components/ConsoleBlock.d.ts +12 -0
- package/dist/components/ConsoleBlock.d.ts.map +1 -0
- package/dist/components/ConsoleBlock.js +81 -0
- package/dist/components/ConsoleBlock.js.map +1 -0
- package/dist/components/CustomProviderCard.d.ts +17 -0
- package/dist/components/CustomProviderCard.d.ts.map +1 -0
- package/dist/components/CustomProviderCard.js +112 -0
- package/dist/components/CustomProviderCard.js.map +1 -0
- package/dist/components/ExpandableSection.d.ts +15 -0
- package/dist/components/ExpandableSection.d.ts.map +1 -0
- package/dist/components/ExpandableSection.js +61 -0
- package/dist/components/ExpandableSection.js.map +1 -0
- package/dist/components/Input.d.ts +26 -0
- package/dist/components/Input.d.ts.map +1 -0
- package/dist/components/Input.js +57 -0
- package/dist/components/Input.js.map +1 -0
- package/dist/components/MessageEditor.d.ts +44 -0
- package/dist/components/MessageEditor.d.ts.map +1 -0
- package/dist/components/MessageEditor.js +418 -0
- package/dist/components/MessageEditor.js.map +1 -0
- package/dist/components/MessageList.d.ts +14 -0
- package/dist/components/MessageList.d.ts.map +1 -0
- package/dist/components/MessageList.js +104 -0
- package/dist/components/MessageList.js.map +1 -0
- package/dist/components/Messages.d.ts +95 -0
- package/dist/components/Messages.d.ts.map +1 -0
- package/dist/components/Messages.js +363 -0
- package/dist/components/Messages.js.map +1 -0
- package/dist/components/ProviderKeyInput.d.ts +16 -0
- package/dist/components/ProviderKeyInput.d.ts.map +1 -0
- package/dist/components/ProviderKeyInput.js +168 -0
- package/dist/components/ProviderKeyInput.js.map +1 -0
- package/dist/components/SandboxedIframe.d.ts +85 -0
- package/dist/components/SandboxedIframe.d.ts.map +1 -0
- package/dist/components/SandboxedIframe.js +518 -0
- package/dist/components/SandboxedIframe.js.map +1 -0
- package/dist/components/StreamingMessageContainer.d.ts +19 -0
- package/dist/components/StreamingMessageContainer.d.ts.map +1 -0
- package/dist/components/StreamingMessageContainer.js +117 -0
- package/dist/components/StreamingMessageContainer.js.map +1 -0
- package/dist/components/ThinkingBlock.d.ts +11 -0
- package/dist/components/ThinkingBlock.d.ts.map +1 -0
- package/dist/components/ThinkingBlock.js +56 -0
- package/dist/components/ThinkingBlock.js.map +1 -0
- package/dist/components/message-renderer-registry.d.ts +12 -0
- package/dist/components/message-renderer-registry.d.ts.map +1 -0
- package/dist/components/message-renderer-registry.js +12 -0
- package/dist/components/message-renderer-registry.js.map +1 -0
- package/dist/components/sandbox/ArtifactsRuntimeProvider.d.ts +35 -0
- package/dist/components/sandbox/ArtifactsRuntimeProvider.d.ts.map +1 -0
- package/dist/components/sandbox/ArtifactsRuntimeProvider.js +192 -0
- package/dist/components/sandbox/ArtifactsRuntimeProvider.js.map +1 -0
- package/dist/components/sandbox/AttachmentsRuntimeProvider.d.ts +17 -0
- package/dist/components/sandbox/AttachmentsRuntimeProvider.d.ts.map +1 -0
- package/dist/components/sandbox/AttachmentsRuntimeProvider.js +65 -0
- package/dist/components/sandbox/AttachmentsRuntimeProvider.js.map +1 -0
- package/dist/components/sandbox/ConsoleRuntimeProvider.d.ts +42 -0
- package/dist/components/sandbox/ConsoleRuntimeProvider.d.ts.map +1 -0
- package/dist/components/sandbox/ConsoleRuntimeProvider.js +159 -0
- package/dist/components/sandbox/ConsoleRuntimeProvider.js.map +1 -0
- package/dist/components/sandbox/FileDownloadRuntimeProvider.d.ts +30 -0
- package/dist/components/sandbox/FileDownloadRuntimeProvider.d.ts.map +1 -0
- package/dist/components/sandbox/FileDownloadRuntimeProvider.js +95 -0
- package/dist/components/sandbox/FileDownloadRuntimeProvider.js.map +1 -0
- package/dist/components/sandbox/RuntimeMessageBridge.d.ts +19 -0
- package/dist/components/sandbox/RuntimeMessageBridge.d.ts.map +1 -0
- package/dist/components/sandbox/RuntimeMessageBridge.js +74 -0
- package/dist/components/sandbox/RuntimeMessageBridge.js.map +1 -0
- package/dist/components/sandbox/RuntimeMessageRouter.d.ts +65 -0
- package/dist/components/sandbox/RuntimeMessageRouter.d.ts.map +1 -0
- package/dist/components/sandbox/RuntimeMessageRouter.js +166 -0
- package/dist/components/sandbox/RuntimeMessageRouter.js.map +1 -0
- package/dist/components/sandbox/SandboxRuntimeProvider.d.ts +48 -0
- package/dist/components/sandbox/SandboxRuntimeProvider.d.ts.map +1 -0
- package/dist/components/sandbox/SandboxRuntimeProvider.js +2 -0
- package/dist/components/sandbox/SandboxRuntimeProvider.js.map +1 -0
- package/dist/dialogs/ApiKeyPromptDialog.d.ts +15 -0
- package/dist/dialogs/ApiKeyPromptDialog.d.ts.map +1 -0
- package/dist/dialogs/ApiKeyPromptDialog.js +77 -0
- package/dist/dialogs/ApiKeyPromptDialog.js.map +1 -0
- package/dist/dialogs/AttachmentOverlay.d.ts +32 -0
- package/dist/dialogs/AttachmentOverlay.d.ts.map +1 -0
- package/dist/dialogs/AttachmentOverlay.js +576 -0
- package/dist/dialogs/AttachmentOverlay.js.map +1 -0
- package/dist/dialogs/CustomProviderDialog.d.ts +25 -0
- package/dist/dialogs/CustomProviderDialog.d.ts.map +1 -0
- package/dist/dialogs/CustomProviderDialog.js +270 -0
- package/dist/dialogs/CustomProviderDialog.js.map +1 -0
- package/dist/dialogs/ModelSelector.d.ts +27 -0
- package/dist/dialogs/ModelSelector.d.ts.map +1 -0
- package/dist/dialogs/ModelSelector.js +320 -0
- package/dist/dialogs/ModelSelector.js.map +1 -0
- package/dist/dialogs/PersistentStorageDialog.d.ts +17 -0
- package/dist/dialogs/PersistentStorageDialog.d.ts.map +1 -0
- package/dist/dialogs/PersistentStorageDialog.js +144 -0
- package/dist/dialogs/PersistentStorageDialog.js.map +1 -0
- package/dist/dialogs/ProvidersModelsTab.d.ts +20 -0
- package/dist/dialogs/ProvidersModelsTab.d.ts.map +1 -0
- package/dist/dialogs/ProvidersModelsTab.js +188 -0
- package/dist/dialogs/ProvidersModelsTab.js.map +1 -0
- package/dist/dialogs/SessionListDialog.d.ts +19 -0
- package/dist/dialogs/SessionListDialog.d.ts.map +1 -0
- package/dist/dialogs/SessionListDialog.js +152 -0
- package/dist/dialogs/SessionListDialog.js.map +1 -0
- package/dist/dialogs/SettingsDialog.d.ts +30 -0
- package/dist/dialogs/SettingsDialog.d.ts.map +1 -0
- package/dist/dialogs/SettingsDialog.js +222 -0
- package/dist/dialogs/SettingsDialog.js.map +1 -0
- package/dist/index.d.ts +67 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +70 -0
- package/dist/index.js.map +1 -0
- package/dist/prompts/prompts.d.ts +11 -0
- package/dist/prompts/prompts.d.ts.map +1 -0
- package/dist/prompts/prompts.js +272 -0
- package/dist/prompts/prompts.js.map +1 -0
- package/dist/storage/app-storage.d.ts +33 -0
- package/dist/storage/app-storage.d.ts.map +1 -0
- package/dist/storage/app-storage.js +43 -0
- package/dist/storage/app-storage.js.map +1 -0
- package/dist/storage/backends/indexeddb-storage-backend.d.ts +27 -0
- package/dist/storage/backends/indexeddb-storage-backend.d.ts.map +1 -0
- package/dist/storage/backends/indexeddb-storage-backend.js +167 -0
- package/dist/storage/backends/indexeddb-storage-backend.js.map +1 -0
- package/dist/storage/store.d.ts +23 -0
- package/dist/storage/store.d.ts.map +1 -0
- package/dist/storage/store.js +24 -0
- package/dist/storage/store.js.map +1 -0
- package/dist/storage/stores/custom-providers-store.d.ts +25 -0
- package/dist/storage/stores/custom-providers-store.d.ts.map +1 -0
- package/dist/storage/stores/custom-providers-store.js +35 -0
- package/dist/storage/stores/custom-providers-store.js.map +1 -0
- package/dist/storage/stores/provider-keys-store.d.ts +14 -0
- package/dist/storage/stores/provider-keys-store.d.ts.map +1 -0
- package/dist/storage/stores/provider-keys-store.js +27 -0
- package/dist/storage/stores/provider-keys-store.js.map +1 -0
- package/dist/storage/stores/sessions-store.d.ts +32 -0
- package/dist/storage/stores/sessions-store.d.ts.map +1 -0
- package/dist/storage/stores/sessions-store.js +113 -0
- package/dist/storage/stores/sessions-store.js.map +1 -0
- package/dist/storage/stores/settings-store.d.ts +14 -0
- package/dist/storage/stores/settings-store.d.ts.map +1 -0
- package/dist/storage/stores/settings-store.js +28 -0
- package/dist/storage/stores/settings-store.js.map +1 -0
- package/dist/storage/types.d.ts +176 -0
- package/dist/storage/types.d.ts.map +1 -0
- package/dist/storage/types.js +2 -0
- package/dist/storage/types.js.map +1 -0
- package/dist/tools/artifacts/ArtifactElement.d.ts +9 -0
- package/dist/tools/artifacts/ArtifactElement.d.ts.map +1 -0
- package/dist/tools/artifacts/ArtifactElement.js +8 -0
- package/dist/tools/artifacts/ArtifactElement.js.map +1 -0
- package/dist/tools/artifacts/ArtifactPill.d.ts +4 -0
- package/dist/tools/artifacts/ArtifactPill.d.ts.map +1 -0
- package/dist/tools/artifacts/ArtifactPill.js +23 -0
- package/dist/tools/artifacts/ArtifactPill.js.map +1 -0
- package/dist/tools/artifacts/Console.d.ts +18 -0
- package/dist/tools/artifacts/Console.d.ts.map +1 -0
- package/dist/tools/artifacts/Console.js +92 -0
- package/dist/tools/artifacts/Console.js.map +1 -0
- package/dist/tools/artifacts/DocxArtifact.d.ts +22 -0
- package/dist/tools/artifacts/DocxArtifact.d.ts.map +1 -0
- package/dist/tools/artifacts/DocxArtifact.js +205 -0
- package/dist/tools/artifacts/DocxArtifact.js.map +1 -0
- package/dist/tools/artifacts/ExcelArtifact.d.ts +24 -0
- package/dist/tools/artifacts/ExcelArtifact.d.ts.map +1 -0
- package/dist/tools/artifacts/ExcelArtifact.js +213 -0
- package/dist/tools/artifacts/ExcelArtifact.js.map +1 -0
- package/dist/tools/artifacts/GenericArtifact.d.ts +19 -0
- package/dist/tools/artifacts/GenericArtifact.d.ts.map +1 -0
- package/dist/tools/artifacts/GenericArtifact.js +114 -0
- package/dist/tools/artifacts/GenericArtifact.js.map +1 -0
- package/dist/tools/artifacts/HtmlArtifact.d.ts +27 -0
- package/dist/tools/artifacts/HtmlArtifact.d.ts.map +1 -0
- package/dist/tools/artifacts/HtmlArtifact.js +187 -0
- package/dist/tools/artifacts/HtmlArtifact.js.map +1 -0
- package/dist/tools/artifacts/ImageArtifact.d.ts +20 -0
- package/dist/tools/artifacts/ImageArtifact.d.ts.map +1 -0
- package/dist/tools/artifacts/ImageArtifact.js +117 -0
- package/dist/tools/artifacts/ImageArtifact.js.map +1 -0
- package/dist/tools/artifacts/MarkdownArtifact.d.ts +19 -0
- package/dist/tools/artifacts/MarkdownArtifact.d.ts.map +1 -0
- package/dist/tools/artifacts/MarkdownArtifact.js +79 -0
- package/dist/tools/artifacts/MarkdownArtifact.js.map +1 -0
- package/dist/tools/artifacts/PdfArtifact.d.ts +25 -0
- package/dist/tools/artifacts/PdfArtifact.d.ts.map +1 -0
- package/dist/tools/artifacts/PdfArtifact.js +181 -0
- package/dist/tools/artifacts/PdfArtifact.js.map +1 -0
- package/dist/tools/artifacts/SvgArtifact.d.ts +18 -0
- package/dist/tools/artifacts/SvgArtifact.d.ts.map +1 -0
- package/dist/tools/artifacts/SvgArtifact.js +75 -0
- package/dist/tools/artifacts/SvgArtifact.js.map +1 -0
- package/dist/tools/artifacts/TextArtifact.d.ts +19 -0
- package/dist/tools/artifacts/TextArtifact.d.ts.map +1 -0
- package/dist/tools/artifacts/TextArtifact.js +141 -0
- package/dist/tools/artifacts/TextArtifact.js.map +1 -0
- package/dist/tools/artifacts/artifacts-tool-renderer.d.ts +11 -0
- package/dist/tools/artifacts/artifacts-tool-renderer.d.ts.map +1 -0
- package/dist/tools/artifacts/artifacts-tool-renderer.js +273 -0
- package/dist/tools/artifacts/artifacts-tool-renderer.js.map +1 -0
- package/dist/tools/artifacts/artifacts.d.ts +63 -0
- package/dist/tools/artifacts/artifacts.d.ts.map +1 -0
- package/dist/tools/artifacts/artifacts.js +664 -0
- package/dist/tools/artifacts/artifacts.js.map +1 -0
- package/dist/tools/artifacts/index.d.ts +8 -0
- package/dist/tools/artifacts/index.d.ts.map +1 -0
- package/dist/tools/artifacts/index.js +8 -0
- package/dist/tools/artifacts/index.js.map +1 -0
- package/dist/tools/extract-document.d.ts +24 -0
- package/dist/tools/extract-document.d.ts.map +1 -0
- package/dist/tools/extract-document.js +216 -0
- package/dist/tools/extract-document.js.map +1 -0
- package/dist/tools/index.d.ts +16 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +33 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/javascript-repl.d.ts +44 -0
- package/dist/tools/javascript-repl.d.ts.map +1 -0
- package/dist/tools/javascript-repl.js +224 -0
- package/dist/tools/javascript-repl.js.map +1 -0
- package/dist/tools/renderer-registry.d.ts +23 -0
- package/dist/tools/renderer-registry.d.ts.map +1 -0
- package/dist/tools/renderer-registry.js +107 -0
- package/dist/tools/renderer-registry.js.map +1 -0
- package/dist/tools/renderers/BashRenderer.d.ts +10 -0
- package/dist/tools/renderers/BashRenderer.d.ts.map +1 -0
- package/dist/tools/renderers/BashRenderer.js +42 -0
- package/dist/tools/renderers/BashRenderer.js.map +1 -0
- package/dist/tools/renderers/CalculateRenderer.d.ts +10 -0
- package/dist/tools/renderers/CalculateRenderer.d.ts.map +1 -0
- package/dist/tools/renderers/CalculateRenderer.js +45 -0
- package/dist/tools/renderers/CalculateRenderer.js.map +1 -0
- package/dist/tools/renderers/DefaultRenderer.d.ts +6 -0
- package/dist/tools/renderers/DefaultRenderer.d.ts.map +1 -0
- package/dist/tools/renderers/DefaultRenderer.js +94 -0
- package/dist/tools/renderers/DefaultRenderer.js.map +1 -0
- package/dist/tools/renderers/GetCurrentTimeRenderer.d.ts +10 -0
- package/dist/tools/renderers/GetCurrentTimeRenderer.d.ts.map +1 -0
- package/dist/tools/renderers/GetCurrentTimeRenderer.js +72 -0
- package/dist/tools/renderers/GetCurrentTimeRenderer.js.map +1 -0
- package/dist/tools/types.d.ts +10 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +2 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/utils/attachment-utils.d.ts +19 -0
- package/dist/utils/attachment-utils.d.ts.map +1 -0
- package/dist/utils/attachment-utils.js +415 -0
- package/dist/utils/attachment-utils.js.map +1 -0
- package/dist/utils/auth-token.d.ts +3 -0
- package/dist/utils/auth-token.d.ts.map +1 -0
- package/dist/utils/auth-token.js +19 -0
- package/dist/utils/auth-token.js.map +1 -0
- package/dist/utils/format.d.ts +6 -0
- package/dist/utils/format.d.ts.map +1 -0
- package/dist/utils/format.js +47 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/i18n.d.ts +636 -0
- package/dist/utils/i18n.d.ts.map +1 -0
- package/dist/utils/i18n.js +418 -0
- package/dist/utils/i18n.js.map +1 -0
- package/dist/utils/model-discovery.d.ts +38 -0
- package/dist/utils/model-discovery.d.ts.map +1 -0
- package/dist/utils/model-discovery.js +243 -0
- package/dist/utils/model-discovery.js.map +1 -0
- package/dist/utils/proxy-utils.d.ts +45 -0
- package/dist/utils/proxy-utils.d.ts.map +1 -0
- package/dist/utils/proxy-utils.js +116 -0
- package/dist/utils/proxy-utils.js.map +1 -0
- package/dist/utils/test-sessions.d.ts +359 -0
- package/dist/utils/test-sessions.d.ts.map +1 -0
- package/dist/utils/test-sessions.js +2325 -0
- package/dist/utils/test-sessions.js.map +1 -0
- package/example/README.md +61 -0
- package/example/index.html +13 -0
- package/example/package.json +25 -0
- package/example/src/app.css +1 -0
- package/example/src/custom-messages.ts +99 -0
- package/example/src/env.d.ts +1 -0
- package/example/src/main.ts +421 -0
- package/example/tsconfig.json +23 -0
- package/example/vite.config.ts +6 -0
- package/package.json +53 -0
- package/scripts/count-prompt-tokens.ts +88 -0
- package/src/ChatPanel.ts +207 -0
- package/src/app.css +68 -0
- package/src/components/AgentInterface.ts +388 -0
- package/src/components/AttachmentTile.ts +107 -0
- package/src/components/ConsoleBlock.ts +72 -0
- package/src/components/CustomProviderCard.ts +100 -0
- package/src/components/ExpandableSection.ts +46 -0
- package/src/components/Input.ts +113 -0
- package/src/components/MessageEditor.ts +400 -0
- package/src/components/MessageList.ts +95 -0
- package/src/components/Messages.ts +383 -0
- package/src/components/ProviderKeyInput.ts +153 -0
- package/src/components/SandboxedIframe.ts +626 -0
- package/src/components/StreamingMessageContainer.ts +103 -0
- package/src/components/ThinkingBlock.ts +43 -0
- package/src/components/message-renderer-registry.ts +28 -0
- package/src/components/sandbox/ArtifactsRuntimeProvider.ts +219 -0
- package/src/components/sandbox/AttachmentsRuntimeProvider.ts +66 -0
- package/src/components/sandbox/ConsoleRuntimeProvider.ts +186 -0
- package/src/components/sandbox/FileDownloadRuntimeProvider.ts +110 -0
- package/src/components/sandbox/RuntimeMessageBridge.ts +82 -0
- package/src/components/sandbox/RuntimeMessageRouter.ts +216 -0
- package/src/components/sandbox/SandboxRuntimeProvider.ts +52 -0
- package/src/dialogs/ApiKeyPromptDialog.ts +75 -0
- package/src/dialogs/AttachmentOverlay.ts +636 -0
- package/src/dialogs/CustomProviderDialog.ts +274 -0
- package/src/dialogs/ModelSelector.ts +313 -0
- package/src/dialogs/PersistentStorageDialog.ts +144 -0
- package/src/dialogs/ProvidersModelsTab.ts +212 -0
- package/src/dialogs/SessionListDialog.ts +150 -0
- package/src/dialogs/SettingsDialog.ts +214 -0
- package/src/index.ts +119 -0
- package/src/prompts/prompts.ts +282 -0
- package/src/storage/app-storage.ts +60 -0
- package/src/storage/backends/indexeddb-storage-backend.ts +193 -0
- package/src/storage/store.ts +33 -0
- package/src/storage/stores/custom-providers-store.ts +62 -0
- package/src/storage/stores/provider-keys-store.ts +33 -0
- package/src/storage/stores/sessions-store.ts +136 -0
- package/src/storage/stores/settings-store.ts +34 -0
- package/src/storage/types.ts +206 -0
- package/src/tools/artifacts/ArtifactElement.ts +14 -0
- package/src/tools/artifacts/ArtifactPill.ts +26 -0
- package/src/tools/artifacts/Console.ts +93 -0
- package/src/tools/artifacts/DocxArtifact.ts +213 -0
- package/src/tools/artifacts/ExcelArtifact.ts +231 -0
- package/src/tools/artifacts/GenericArtifact.ts +117 -0
- package/src/tools/artifacts/HtmlArtifact.ts +195 -0
- package/src/tools/artifacts/ImageArtifact.ts +116 -0
- package/src/tools/artifacts/MarkdownArtifact.ts +82 -0
- package/src/tools/artifacts/PdfArtifact.ts +201 -0
- package/src/tools/artifacts/SvgArtifact.ts +78 -0
- package/src/tools/artifacts/TextArtifact.ts +148 -0
- package/src/tools/artifacts/artifacts-tool-renderer.ts +310 -0
- package/src/tools/artifacts/artifacts.ts +713 -0
- package/src/tools/artifacts/index.ts +7 -0
- package/src/tools/extract-document.ts +275 -0
- package/src/tools/index.ts +46 -0
- package/src/tools/javascript-repl.ts +293 -0
- package/src/tools/renderer-registry.ts +130 -0
- package/src/tools/renderers/BashRenderer.ts +52 -0
- package/src/tools/renderers/CalculateRenderer.ts +58 -0
- package/src/tools/renderers/DefaultRenderer.ts +103 -0
- package/src/tools/renderers/GetCurrentTimeRenderer.ts +92 -0
- package/src/tools/types.ts +15 -0
- package/src/utils/attachment-utils.ts +472 -0
- package/src/utils/auth-token.ts +22 -0
- package/src/utils/format.ts +42 -0
- package/src/utils/i18n.ts +653 -0
- package/src/utils/model-discovery.ts +277 -0
- package/src/utils/proxy-utils.ts +134 -0
- package/src/utils/test-sessions.ts +2357 -0
- package/tsconfig.build.json +20 -0
- package/tsconfig.json +7 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import type { Model } from "@draht/ai";
|
|
2
|
+
import { i18n } from "@mariozechner/mini-lit";
|
|
3
|
+
import { Button } from "@mariozechner/mini-lit/dist/Button.js";
|
|
4
|
+
import { DialogBase } from "@mariozechner/mini-lit/dist/DialogBase.js";
|
|
5
|
+
import { Input } from "@mariozechner/mini-lit/dist/Input.js";
|
|
6
|
+
import { Label } from "@mariozechner/mini-lit/dist/Label.js";
|
|
7
|
+
import { Select } from "@mariozechner/mini-lit/dist/Select.js";
|
|
8
|
+
import { html, type TemplateResult } from "lit";
|
|
9
|
+
import { state } from "lit/decorators.js";
|
|
10
|
+
import { getAppStorage } from "../storage/app-storage.js";
|
|
11
|
+
import type { CustomProvider, CustomProviderType } from "../storage/stores/custom-providers-store.js";
|
|
12
|
+
import { discoverModels } from "../utils/model-discovery.js";
|
|
13
|
+
|
|
14
|
+
export class CustomProviderDialog extends DialogBase {
|
|
15
|
+
private provider?: CustomProvider;
|
|
16
|
+
private initialType?: CustomProviderType;
|
|
17
|
+
private onSaveCallback?: () => void;
|
|
18
|
+
|
|
19
|
+
@state() private name = "";
|
|
20
|
+
@state() private type: CustomProviderType = "openai-completions";
|
|
21
|
+
@state() private baseUrl = "";
|
|
22
|
+
@state() private apiKey = "";
|
|
23
|
+
@state() private testing = false;
|
|
24
|
+
@state() private testError = "";
|
|
25
|
+
@state() private discoveredModels: Model<any>[] = [];
|
|
26
|
+
|
|
27
|
+
protected modalWidth = "min(800px, 90vw)";
|
|
28
|
+
protected modalHeight = "min(700px, 90vh)";
|
|
29
|
+
|
|
30
|
+
static async open(
|
|
31
|
+
provider: CustomProvider | undefined,
|
|
32
|
+
initialType: CustomProviderType | undefined,
|
|
33
|
+
onSave?: () => void,
|
|
34
|
+
) {
|
|
35
|
+
const dialog = new CustomProviderDialog();
|
|
36
|
+
dialog.provider = provider;
|
|
37
|
+
dialog.initialType = initialType;
|
|
38
|
+
dialog.onSaveCallback = onSave;
|
|
39
|
+
document.body.appendChild(dialog);
|
|
40
|
+
dialog.initializeFromProvider();
|
|
41
|
+
dialog.open();
|
|
42
|
+
dialog.requestUpdate();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private initializeFromProvider() {
|
|
46
|
+
if (this.provider) {
|
|
47
|
+
this.name = this.provider.name;
|
|
48
|
+
this.type = this.provider.type;
|
|
49
|
+
this.baseUrl = this.provider.baseUrl;
|
|
50
|
+
this.apiKey = this.provider.apiKey || "";
|
|
51
|
+
this.discoveredModels = this.provider.models || [];
|
|
52
|
+
} else {
|
|
53
|
+
this.name = "";
|
|
54
|
+
this.type = this.initialType || "openai-completions";
|
|
55
|
+
this.baseUrl = "";
|
|
56
|
+
this.updateDefaultBaseUrl();
|
|
57
|
+
this.apiKey = "";
|
|
58
|
+
this.discoveredModels = [];
|
|
59
|
+
}
|
|
60
|
+
this.testError = "";
|
|
61
|
+
this.testing = false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
private updateDefaultBaseUrl() {
|
|
65
|
+
if (this.baseUrl) return;
|
|
66
|
+
|
|
67
|
+
const defaults: Record<string, string> = {
|
|
68
|
+
ollama: "http://localhost:11434",
|
|
69
|
+
"llama.cpp": "http://localhost:8080",
|
|
70
|
+
vllm: "http://localhost:8000",
|
|
71
|
+
lmstudio: "http://localhost:1234",
|
|
72
|
+
"openai-completions": "",
|
|
73
|
+
"openai-responses": "",
|
|
74
|
+
"anthropic-messages": "",
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
this.baseUrl = defaults[this.type] || "";
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private isAutoDiscoveryType(): boolean {
|
|
81
|
+
return this.type === "ollama" || this.type === "llama.cpp" || this.type === "vllm" || this.type === "lmstudio";
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
private async testConnection() {
|
|
85
|
+
if (!this.isAutoDiscoveryType()) return;
|
|
86
|
+
|
|
87
|
+
this.testing = true;
|
|
88
|
+
this.testError = "";
|
|
89
|
+
this.discoveredModels = [];
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
const models = await discoverModels(
|
|
93
|
+
this.type as "ollama" | "llama.cpp" | "vllm" | "lmstudio",
|
|
94
|
+
this.baseUrl,
|
|
95
|
+
this.apiKey || undefined,
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
this.discoveredModels = models.map((model) => ({
|
|
99
|
+
...model,
|
|
100
|
+
provider: this.name || this.type,
|
|
101
|
+
}));
|
|
102
|
+
|
|
103
|
+
this.testError = "";
|
|
104
|
+
} catch (error) {
|
|
105
|
+
this.testError = error instanceof Error ? error.message : String(error);
|
|
106
|
+
this.discoveredModels = [];
|
|
107
|
+
} finally {
|
|
108
|
+
this.testing = false;
|
|
109
|
+
this.requestUpdate();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private async save() {
|
|
114
|
+
if (!this.name || !this.baseUrl) {
|
|
115
|
+
alert(i18n("Please fill in all required fields"));
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
const storage = getAppStorage();
|
|
121
|
+
|
|
122
|
+
const provider: CustomProvider = {
|
|
123
|
+
id: this.provider?.id || crypto.randomUUID(),
|
|
124
|
+
name: this.name,
|
|
125
|
+
type: this.type,
|
|
126
|
+
baseUrl: this.baseUrl,
|
|
127
|
+
apiKey: this.apiKey || undefined,
|
|
128
|
+
models: this.isAutoDiscoveryType() ? undefined : this.provider?.models || [],
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
await storage.customProviders.set(provider);
|
|
132
|
+
|
|
133
|
+
if (this.onSaveCallback) {
|
|
134
|
+
this.onSaveCallback();
|
|
135
|
+
}
|
|
136
|
+
this.close();
|
|
137
|
+
} catch (error) {
|
|
138
|
+
console.error("Failed to save provider:", error);
|
|
139
|
+
alert(i18n("Failed to save provider"));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
protected override renderContent(): TemplateResult {
|
|
144
|
+
const providerTypes = [
|
|
145
|
+
{ value: "ollama", label: "Ollama (auto-discovery)" },
|
|
146
|
+
{ value: "llama.cpp", label: "llama.cpp (auto-discovery)" },
|
|
147
|
+
{ value: "vllm", label: "vLLM (auto-discovery)" },
|
|
148
|
+
{ value: "lmstudio", label: "LM Studio (auto-discovery)" },
|
|
149
|
+
{ value: "openai-completions", label: "OpenAI Completions Compatible" },
|
|
150
|
+
{ value: "openai-responses", label: "OpenAI Responses Compatible" },
|
|
151
|
+
{ value: "anthropic-messages", label: "Anthropic Messages Compatible" },
|
|
152
|
+
];
|
|
153
|
+
|
|
154
|
+
return html`
|
|
155
|
+
<div class="flex flex-col h-full overflow-hidden">
|
|
156
|
+
<div class="p-6 flex-shrink-0 border-b border-border">
|
|
157
|
+
<h2 class="text-lg font-semibold text-foreground">
|
|
158
|
+
${this.provider ? i18n("Edit Provider") : i18n("Add Provider")}
|
|
159
|
+
</h2>
|
|
160
|
+
</div>
|
|
161
|
+
|
|
162
|
+
<div class="flex-1 overflow-y-auto p-6">
|
|
163
|
+
<div class="flex flex-col gap-4">
|
|
164
|
+
<div class="flex flex-col gap-2">
|
|
165
|
+
${Label({ htmlFor: "provider-name", children: i18n("Provider Name") })}
|
|
166
|
+
${Input({
|
|
167
|
+
value: this.name,
|
|
168
|
+
placeholder: i18n("e.g., My Ollama Server"),
|
|
169
|
+
onInput: (e: Event) => {
|
|
170
|
+
this.name = (e.target as HTMLInputElement).value;
|
|
171
|
+
this.requestUpdate();
|
|
172
|
+
},
|
|
173
|
+
})}
|
|
174
|
+
</div>
|
|
175
|
+
|
|
176
|
+
<div class="flex flex-col gap-2">
|
|
177
|
+
${Label({ htmlFor: "provider-type", children: i18n("Provider Type") })}
|
|
178
|
+
${Select({
|
|
179
|
+
value: this.type,
|
|
180
|
+
options: providerTypes.map((pt) => ({
|
|
181
|
+
value: pt.value,
|
|
182
|
+
label: pt.label,
|
|
183
|
+
})),
|
|
184
|
+
onChange: (value: string) => {
|
|
185
|
+
this.type = value as CustomProviderType;
|
|
186
|
+
this.baseUrl = "";
|
|
187
|
+
this.updateDefaultBaseUrl();
|
|
188
|
+
this.requestUpdate();
|
|
189
|
+
},
|
|
190
|
+
width: "100%",
|
|
191
|
+
})}
|
|
192
|
+
</div>
|
|
193
|
+
|
|
194
|
+
<div class="flex flex-col gap-2">
|
|
195
|
+
${Label({ htmlFor: "base-url", children: i18n("Base URL") })}
|
|
196
|
+
${Input({
|
|
197
|
+
value: this.baseUrl,
|
|
198
|
+
placeholder: i18n("e.g., http://localhost:11434"),
|
|
199
|
+
onInput: (e: Event) => {
|
|
200
|
+
this.baseUrl = (e.target as HTMLInputElement).value;
|
|
201
|
+
this.requestUpdate();
|
|
202
|
+
},
|
|
203
|
+
})}
|
|
204
|
+
</div>
|
|
205
|
+
|
|
206
|
+
<div class="flex flex-col gap-2">
|
|
207
|
+
${Label({ htmlFor: "api-key", children: i18n("API Key (Optional)") })}
|
|
208
|
+
${Input({
|
|
209
|
+
type: "password",
|
|
210
|
+
value: this.apiKey,
|
|
211
|
+
placeholder: i18n("Leave empty if not required"),
|
|
212
|
+
onInput: (e: Event) => {
|
|
213
|
+
this.apiKey = (e.target as HTMLInputElement).value;
|
|
214
|
+
this.requestUpdate();
|
|
215
|
+
},
|
|
216
|
+
})}
|
|
217
|
+
</div>
|
|
218
|
+
|
|
219
|
+
${
|
|
220
|
+
this.isAutoDiscoveryType()
|
|
221
|
+
? html`
|
|
222
|
+
<div class="flex flex-col gap-2">
|
|
223
|
+
${Button({
|
|
224
|
+
onClick: () => this.testConnection(),
|
|
225
|
+
variant: "outline",
|
|
226
|
+
disabled: this.testing || !this.baseUrl,
|
|
227
|
+
children: this.testing ? i18n("Testing...") : i18n("Test Connection"),
|
|
228
|
+
})}
|
|
229
|
+
${this.testError ? html` <div class="text-sm text-destructive">${this.testError}</div> ` : ""}
|
|
230
|
+
${
|
|
231
|
+
this.discoveredModels.length > 0
|
|
232
|
+
? html`
|
|
233
|
+
<div class="text-sm text-muted-foreground">
|
|
234
|
+
${i18n("Discovered")} ${this.discoveredModels.length} ${i18n("models")}:
|
|
235
|
+
<ul class="list-disc list-inside mt-2">
|
|
236
|
+
${this.discoveredModels.slice(0, 5).map((model) => html`<li>${model.name}</li>`)}
|
|
237
|
+
${
|
|
238
|
+
this.discoveredModels.length > 5
|
|
239
|
+
? html`<li>...${i18n("and")} ${this.discoveredModels.length - 5} ${i18n("more")}</li>`
|
|
240
|
+
: ""
|
|
241
|
+
}
|
|
242
|
+
</ul>
|
|
243
|
+
</div>
|
|
244
|
+
`
|
|
245
|
+
: ""
|
|
246
|
+
}
|
|
247
|
+
</div>
|
|
248
|
+
`
|
|
249
|
+
: html` <div class="text-sm text-muted-foreground">
|
|
250
|
+
${i18n("For manual provider types, add models after saving the provider.")}
|
|
251
|
+
</div>`
|
|
252
|
+
}
|
|
253
|
+
</div>
|
|
254
|
+
</div>
|
|
255
|
+
|
|
256
|
+
<div class="p-6 flex-shrink-0 border-t border-border flex justify-end gap-2">
|
|
257
|
+
${Button({
|
|
258
|
+
onClick: () => this.close(),
|
|
259
|
+
variant: "ghost",
|
|
260
|
+
children: i18n("Cancel"),
|
|
261
|
+
})}
|
|
262
|
+
${Button({
|
|
263
|
+
onClick: () => this.save(),
|
|
264
|
+
variant: "default",
|
|
265
|
+
disabled: !this.name || !this.baseUrl,
|
|
266
|
+
children: i18n("Save"),
|
|
267
|
+
})}
|
|
268
|
+
</div>
|
|
269
|
+
</div>
|
|
270
|
+
`;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
customElements.define("custom-provider-dialog", CustomProviderDialog);
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import { getModels, getProviders, type Model, modelsAreEqual } from "@draht/ai";
|
|
2
|
+
import { icon } from "@mariozechner/mini-lit";
|
|
3
|
+
import { Badge } from "@mariozechner/mini-lit/dist/Badge.js";
|
|
4
|
+
import { Button } from "@mariozechner/mini-lit/dist/Button.js";
|
|
5
|
+
import { DialogHeader } from "@mariozechner/mini-lit/dist/Dialog.js";
|
|
6
|
+
import { DialogBase } from "@mariozechner/mini-lit/dist/DialogBase.js";
|
|
7
|
+
import { html, type PropertyValues, type TemplateResult } from "lit";
|
|
8
|
+
import { customElement, state } from "lit/decorators.js";
|
|
9
|
+
import { createRef, ref } from "lit/directives/ref.js";
|
|
10
|
+
import { Brain, Image as ImageIcon } from "lucide";
|
|
11
|
+
import { Input } from "../components/Input.js";
|
|
12
|
+
import { getAppStorage } from "../storage/app-storage.js";
|
|
13
|
+
import type { AutoDiscoveryProviderType } from "../storage/stores/custom-providers-store.js";
|
|
14
|
+
import { formatModelCost } from "../utils/format.js";
|
|
15
|
+
import { i18n } from "../utils/i18n.js";
|
|
16
|
+
import { discoverModels } from "../utils/model-discovery.js";
|
|
17
|
+
|
|
18
|
+
@customElement("agent-model-selector")
|
|
19
|
+
export class ModelSelector extends DialogBase {
|
|
20
|
+
@state() currentModel: Model<any> | null = null;
|
|
21
|
+
@state() searchQuery = "";
|
|
22
|
+
@state() filterThinking = false;
|
|
23
|
+
@state() filterVision = false;
|
|
24
|
+
@state() customProvidersLoading = false;
|
|
25
|
+
@state() selectedIndex = 0;
|
|
26
|
+
@state() private navigationMode: "mouse" | "keyboard" = "mouse";
|
|
27
|
+
@state() private customProviderModels: Model<any>[] = [];
|
|
28
|
+
|
|
29
|
+
private onSelectCallback?: (model: Model<any>) => void;
|
|
30
|
+
private scrollContainerRef = createRef<HTMLDivElement>();
|
|
31
|
+
private searchInputRef = createRef<HTMLInputElement>();
|
|
32
|
+
private lastMousePosition = { x: 0, y: 0 };
|
|
33
|
+
|
|
34
|
+
protected override modalWidth = "min(400px, 90vw)";
|
|
35
|
+
|
|
36
|
+
static async open(currentModel: Model<any> | null, onSelect: (model: Model<any>) => void) {
|
|
37
|
+
const selector = new ModelSelector();
|
|
38
|
+
selector.currentModel = currentModel;
|
|
39
|
+
selector.onSelectCallback = onSelect;
|
|
40
|
+
selector.open();
|
|
41
|
+
selector.loadCustomProviders();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
override async firstUpdated(changedProperties: PropertyValues): Promise<void> {
|
|
45
|
+
super.firstUpdated(changedProperties);
|
|
46
|
+
// Wait for dialog to be fully rendered
|
|
47
|
+
await this.updateComplete;
|
|
48
|
+
// Focus the search input when dialog opens
|
|
49
|
+
this.searchInputRef.value?.focus();
|
|
50
|
+
|
|
51
|
+
// Track actual mouse movement
|
|
52
|
+
this.addEventListener("mousemove", (e: MouseEvent) => {
|
|
53
|
+
// Check if mouse actually moved
|
|
54
|
+
if (e.clientX !== this.lastMousePosition.x || e.clientY !== this.lastMousePosition.y) {
|
|
55
|
+
this.lastMousePosition = { x: e.clientX, y: e.clientY };
|
|
56
|
+
// Only switch to mouse mode on actual mouse movement
|
|
57
|
+
if (this.navigationMode === "keyboard") {
|
|
58
|
+
this.navigationMode = "mouse";
|
|
59
|
+
// Update selection to the item under the mouse
|
|
60
|
+
const target = e.target as HTMLElement;
|
|
61
|
+
const modelItem = target.closest("[data-model-item]");
|
|
62
|
+
if (modelItem) {
|
|
63
|
+
const allItems = this.scrollContainerRef.value?.querySelectorAll("[data-model-item]");
|
|
64
|
+
if (allItems) {
|
|
65
|
+
const index = Array.from(allItems).indexOf(modelItem);
|
|
66
|
+
if (index !== -1) {
|
|
67
|
+
this.selectedIndex = index;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Add global keyboard handler for the dialog
|
|
76
|
+
this.addEventListener("keydown", (e: KeyboardEvent) => {
|
|
77
|
+
// Get filtered models to know the bounds
|
|
78
|
+
const filteredModels = this.getFilteredModels();
|
|
79
|
+
|
|
80
|
+
if (e.key === "ArrowDown") {
|
|
81
|
+
e.preventDefault();
|
|
82
|
+
this.navigationMode = "keyboard";
|
|
83
|
+
this.selectedIndex = Math.min(this.selectedIndex + 1, filteredModels.length - 1);
|
|
84
|
+
this.scrollToSelected();
|
|
85
|
+
} else if (e.key === "ArrowUp") {
|
|
86
|
+
e.preventDefault();
|
|
87
|
+
this.navigationMode = "keyboard";
|
|
88
|
+
this.selectedIndex = Math.max(this.selectedIndex - 1, 0);
|
|
89
|
+
this.scrollToSelected();
|
|
90
|
+
} else if (e.key === "Enter") {
|
|
91
|
+
e.preventDefault();
|
|
92
|
+
if (filteredModels[this.selectedIndex]) {
|
|
93
|
+
this.handleSelect(filteredModels[this.selectedIndex].model);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
private async loadCustomProviders() {
|
|
100
|
+
this.customProvidersLoading = true;
|
|
101
|
+
const allCustomModels: Model<any>[] = [];
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
const storage = getAppStorage();
|
|
105
|
+
const customProviders = await storage.customProviders.getAll();
|
|
106
|
+
|
|
107
|
+
// Load models from custom providers
|
|
108
|
+
for (const provider of customProviders) {
|
|
109
|
+
const isAutoDiscovery: boolean =
|
|
110
|
+
provider.type === "ollama" ||
|
|
111
|
+
provider.type === "llama.cpp" ||
|
|
112
|
+
provider.type === "vllm" ||
|
|
113
|
+
provider.type === "lmstudio";
|
|
114
|
+
|
|
115
|
+
if (isAutoDiscovery) {
|
|
116
|
+
try {
|
|
117
|
+
const models = await discoverModels(
|
|
118
|
+
provider.type as AutoDiscoveryProviderType,
|
|
119
|
+
provider.baseUrl,
|
|
120
|
+
provider.apiKey,
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
const modelsWithProvider = models.map((model) => ({
|
|
124
|
+
...model,
|
|
125
|
+
provider: provider.name,
|
|
126
|
+
}));
|
|
127
|
+
|
|
128
|
+
allCustomModels.push(...modelsWithProvider);
|
|
129
|
+
} catch (error) {
|
|
130
|
+
console.debug(`Failed to load models from ${provider.name}:`, error);
|
|
131
|
+
}
|
|
132
|
+
} else if (provider.models) {
|
|
133
|
+
// Manual provider - models already defined
|
|
134
|
+
allCustomModels.push(...provider.models);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
} catch (error) {
|
|
138
|
+
console.error("Failed to load custom providers:", error);
|
|
139
|
+
} finally {
|
|
140
|
+
this.customProviderModels = allCustomModels;
|
|
141
|
+
this.customProvidersLoading = false;
|
|
142
|
+
this.requestUpdate();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private formatTokens(tokens: number): string {
|
|
147
|
+
if (tokens >= 1000000) return `${(tokens / 1000000).toFixed(0)}M`;
|
|
148
|
+
if (tokens >= 1000) return `${(tokens / 1000).toFixed(0)}`;
|
|
149
|
+
return String(tokens);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
private handleSelect(model: Model<any>) {
|
|
153
|
+
if (model) {
|
|
154
|
+
this.onSelectCallback?.(model);
|
|
155
|
+
this.close();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
private getFilteredModels(): Array<{ provider: string; id: string; model: any }> {
|
|
160
|
+
// Collect all models from known providers
|
|
161
|
+
const allModels: Array<{ provider: string; id: string; model: any }> = [];
|
|
162
|
+
const knownProviders = getProviders();
|
|
163
|
+
|
|
164
|
+
for (const provider of knownProviders) {
|
|
165
|
+
const models = getModels(provider as any);
|
|
166
|
+
for (const model of models) {
|
|
167
|
+
allModels.push({ provider, id: model.id, model });
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Add custom provider models
|
|
172
|
+
for (const model of this.customProviderModels) {
|
|
173
|
+
allModels.push({ provider: model.provider, id: model.id, model });
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Filter models based on search and capability filters
|
|
177
|
+
let filteredModels = allModels;
|
|
178
|
+
|
|
179
|
+
// Apply search filter
|
|
180
|
+
if (this.searchQuery) {
|
|
181
|
+
filteredModels = filteredModels.filter(({ provider, id, model }) => {
|
|
182
|
+
const searchTokens = this.searchQuery
|
|
183
|
+
.toLowerCase()
|
|
184
|
+
.split(/\s+/)
|
|
185
|
+
.filter((t) => t);
|
|
186
|
+
const searchText = `${provider} ${id} ${model.name}`.toLowerCase();
|
|
187
|
+
return searchTokens.every((token) => searchText.includes(token));
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Apply capability filters
|
|
192
|
+
if (this.filterThinking) {
|
|
193
|
+
filteredModels = filteredModels.filter(({ model }) => model.reasoning);
|
|
194
|
+
}
|
|
195
|
+
if (this.filterVision) {
|
|
196
|
+
filteredModels = filteredModels.filter(({ model }) => model.input.includes("image"));
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Sort: current model first, then by provider
|
|
200
|
+
filteredModels.sort((a, b) => {
|
|
201
|
+
const aIsCurrent = modelsAreEqual(this.currentModel, a.model);
|
|
202
|
+
const bIsCurrent = modelsAreEqual(this.currentModel, b.model);
|
|
203
|
+
if (aIsCurrent && !bIsCurrent) return -1;
|
|
204
|
+
if (!aIsCurrent && bIsCurrent) return 1;
|
|
205
|
+
return a.provider.localeCompare(b.provider);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
return filteredModels;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
private scrollToSelected() {
|
|
212
|
+
requestAnimationFrame(() => {
|
|
213
|
+
const scrollContainer = this.scrollContainerRef.value;
|
|
214
|
+
const selectedElement = scrollContainer?.querySelectorAll("[data-model-item]")[
|
|
215
|
+
this.selectedIndex
|
|
216
|
+
] as HTMLElement;
|
|
217
|
+
if (selectedElement) {
|
|
218
|
+
selectedElement.scrollIntoView({ block: "nearest", behavior: "smooth" });
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
protected override renderContent(): TemplateResult {
|
|
224
|
+
const filteredModels = this.getFilteredModels();
|
|
225
|
+
|
|
226
|
+
return html`
|
|
227
|
+
<!-- Header and Search -->
|
|
228
|
+
<div class="p-6 pb-4 flex flex-col gap-4 border-b border-border flex-shrink-0">
|
|
229
|
+
${DialogHeader({ title: i18n("Select Model") })}
|
|
230
|
+
${Input({
|
|
231
|
+
placeholder: i18n("Search models..."),
|
|
232
|
+
value: this.searchQuery,
|
|
233
|
+
inputRef: this.searchInputRef,
|
|
234
|
+
onInput: (e: Event) => {
|
|
235
|
+
this.searchQuery = (e.target as HTMLInputElement).value;
|
|
236
|
+
this.selectedIndex = 0;
|
|
237
|
+
// Reset scroll position when search changes
|
|
238
|
+
if (this.scrollContainerRef.value) {
|
|
239
|
+
this.scrollContainerRef.value.scrollTop = 0;
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
})}
|
|
243
|
+
<div class="flex gap-2">
|
|
244
|
+
${Button({
|
|
245
|
+
variant: this.filterThinking ? "default" : "secondary",
|
|
246
|
+
size: "sm",
|
|
247
|
+
onClick: () => {
|
|
248
|
+
this.filterThinking = !this.filterThinking;
|
|
249
|
+
this.selectedIndex = 0;
|
|
250
|
+
if (this.scrollContainerRef.value) {
|
|
251
|
+
this.scrollContainerRef.value.scrollTop = 0;
|
|
252
|
+
}
|
|
253
|
+
},
|
|
254
|
+
className: "rounded-full",
|
|
255
|
+
children: html`<span class="inline-flex items-center gap-1">${icon(Brain, "sm")} ${i18n("Thinking")}</span>`,
|
|
256
|
+
})}
|
|
257
|
+
${Button({
|
|
258
|
+
variant: this.filterVision ? "default" : "secondary",
|
|
259
|
+
size: "sm",
|
|
260
|
+
onClick: () => {
|
|
261
|
+
this.filterVision = !this.filterVision;
|
|
262
|
+
this.selectedIndex = 0;
|
|
263
|
+
if (this.scrollContainerRef.value) {
|
|
264
|
+
this.scrollContainerRef.value.scrollTop = 0;
|
|
265
|
+
}
|
|
266
|
+
},
|
|
267
|
+
className: "rounded-full",
|
|
268
|
+
children: html`<span class="inline-flex items-center gap-1">${icon(ImageIcon, "sm")} ${i18n("Vision")}</span>`,
|
|
269
|
+
})}
|
|
270
|
+
</div>
|
|
271
|
+
</div>
|
|
272
|
+
|
|
273
|
+
<!-- Scrollable model list -->
|
|
274
|
+
<div class="flex-1 overflow-y-auto" ${ref(this.scrollContainerRef)}>
|
|
275
|
+
${filteredModels.map(({ provider, id, model }, index) => {
|
|
276
|
+
const isCurrent = modelsAreEqual(this.currentModel, model);
|
|
277
|
+
const isSelected = index === this.selectedIndex;
|
|
278
|
+
return html`
|
|
279
|
+
<div
|
|
280
|
+
data-model-item
|
|
281
|
+
class="px-4 py-3 ${
|
|
282
|
+
this.navigationMode === "mouse" ? "hover:bg-muted" : ""
|
|
283
|
+
} cursor-pointer border-b border-border ${isSelected ? "bg-accent" : ""}"
|
|
284
|
+
@click=${() => this.handleSelect(model)}
|
|
285
|
+
@mouseenter=${() => {
|
|
286
|
+
// Only update selection in mouse mode
|
|
287
|
+
if (this.navigationMode === "mouse") {
|
|
288
|
+
this.selectedIndex = index;
|
|
289
|
+
}
|
|
290
|
+
}}
|
|
291
|
+
>
|
|
292
|
+
<div class="flex items-center justify-between gap-2 mb-1">
|
|
293
|
+
<div class="flex items-center gap-2 flex-1 min-w-0">
|
|
294
|
+
<span class="text-sm font-medium text-foreground truncate">${id}</span>
|
|
295
|
+
${isCurrent ? html`<span class="text-green-500">✓</span>` : ""}
|
|
296
|
+
</div>
|
|
297
|
+
${Badge(provider, "outline")}
|
|
298
|
+
</div>
|
|
299
|
+
<div class="flex items-center justify-between text-xs text-muted-foreground">
|
|
300
|
+
<div class="flex items-center gap-2">
|
|
301
|
+
<span class="${model.reasoning ? "" : "opacity-30"}">${icon(Brain, "sm")}</span>
|
|
302
|
+
<span class="${model.input.includes("image") ? "" : "opacity-30"}">${icon(ImageIcon, "sm")}</span>
|
|
303
|
+
<span>${this.formatTokens(model.contextWindow)}K/${this.formatTokens(model.maxTokens)}K</span>
|
|
304
|
+
</div>
|
|
305
|
+
<span>${formatModelCost(model.cost)}</span>
|
|
306
|
+
</div>
|
|
307
|
+
</div>
|
|
308
|
+
`;
|
|
309
|
+
})}
|
|
310
|
+
</div>
|
|
311
|
+
`;
|
|
312
|
+
}
|
|
313
|
+
}
|