@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
package/src/ChatPanel.ts
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { Badge } from "@mariozechner/mini-lit/dist/Badge.js";
|
|
2
|
+
import { html, LitElement } from "lit";
|
|
3
|
+
import { customElement, state } from "lit/decorators.js";
|
|
4
|
+
import "./components/AgentInterface.js";
|
|
5
|
+
import type { Agent, AgentTool } from "@draht/agent-core";
|
|
6
|
+
import type { AgentInterface } from "./components/AgentInterface.js";
|
|
7
|
+
import { ArtifactsRuntimeProvider } from "./components/sandbox/ArtifactsRuntimeProvider.js";
|
|
8
|
+
import { AttachmentsRuntimeProvider } from "./components/sandbox/AttachmentsRuntimeProvider.js";
|
|
9
|
+
import type { SandboxRuntimeProvider } from "./components/sandbox/SandboxRuntimeProvider.js";
|
|
10
|
+
import { ArtifactsPanel, ArtifactsToolRenderer } from "./tools/artifacts/index.js";
|
|
11
|
+
import { registerToolRenderer } from "./tools/renderer-registry.js";
|
|
12
|
+
import type { Attachment } from "./utils/attachment-utils.js";
|
|
13
|
+
import { i18n } from "./utils/i18n.js";
|
|
14
|
+
|
|
15
|
+
const BREAKPOINT = 800; // px - switch between overlay and side-by-side
|
|
16
|
+
|
|
17
|
+
@customElement("pi-chat-panel")
|
|
18
|
+
export class ChatPanel extends LitElement {
|
|
19
|
+
@state() public agent?: Agent;
|
|
20
|
+
@state() public agentInterface?: AgentInterface;
|
|
21
|
+
@state() public artifactsPanel?: ArtifactsPanel;
|
|
22
|
+
@state() private hasArtifacts = false;
|
|
23
|
+
@state() private artifactCount = 0;
|
|
24
|
+
@state() private showArtifactsPanel = false;
|
|
25
|
+
@state() private windowWidth = 0;
|
|
26
|
+
|
|
27
|
+
private resizeHandler = () => {
|
|
28
|
+
this.windowWidth = window.innerWidth;
|
|
29
|
+
this.requestUpdate();
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
createRenderRoot() {
|
|
33
|
+
return this;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
override connectedCallback() {
|
|
37
|
+
super.connectedCallback();
|
|
38
|
+
this.windowWidth = window.innerWidth; // Set initial width after connection
|
|
39
|
+
window.addEventListener("resize", this.resizeHandler);
|
|
40
|
+
this.style.display = "flex";
|
|
41
|
+
this.style.flexDirection = "column";
|
|
42
|
+
this.style.height = "100%";
|
|
43
|
+
this.style.minHeight = "0";
|
|
44
|
+
// Update width after initial render
|
|
45
|
+
requestAnimationFrame(() => {
|
|
46
|
+
this.windowWidth = window.innerWidth;
|
|
47
|
+
this.requestUpdate();
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
override disconnectedCallback() {
|
|
52
|
+
super.disconnectedCallback();
|
|
53
|
+
window.removeEventListener("resize", this.resizeHandler);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async setAgent(
|
|
57
|
+
agent: Agent,
|
|
58
|
+
config?: {
|
|
59
|
+
onApiKeyRequired?: (provider: string) => Promise<boolean>;
|
|
60
|
+
onBeforeSend?: () => void | Promise<void>;
|
|
61
|
+
onCostClick?: () => void;
|
|
62
|
+
sandboxUrlProvider?: () => string;
|
|
63
|
+
toolsFactory?: (
|
|
64
|
+
agent: Agent,
|
|
65
|
+
agentInterface: AgentInterface,
|
|
66
|
+
artifactsPanel: ArtifactsPanel,
|
|
67
|
+
runtimeProvidersFactory: () => SandboxRuntimeProvider[],
|
|
68
|
+
) => AgentTool<any>[];
|
|
69
|
+
},
|
|
70
|
+
) {
|
|
71
|
+
this.agent = agent;
|
|
72
|
+
|
|
73
|
+
// Create AgentInterface
|
|
74
|
+
this.agentInterface = document.createElement("agent-interface") as AgentInterface;
|
|
75
|
+
this.agentInterface.session = agent;
|
|
76
|
+
this.agentInterface.enableAttachments = true;
|
|
77
|
+
this.agentInterface.enableModelSelector = true;
|
|
78
|
+
this.agentInterface.enableThinkingSelector = true;
|
|
79
|
+
this.agentInterface.showThemeToggle = false;
|
|
80
|
+
this.agentInterface.onApiKeyRequired = config?.onApiKeyRequired;
|
|
81
|
+
this.agentInterface.onBeforeSend = config?.onBeforeSend;
|
|
82
|
+
this.agentInterface.onCostClick = config?.onCostClick;
|
|
83
|
+
|
|
84
|
+
// Set up artifacts panel
|
|
85
|
+
this.artifactsPanel = new ArtifactsPanel();
|
|
86
|
+
this.artifactsPanel.agent = agent; // Pass agent for HTML artifact runtime providers
|
|
87
|
+
if (config?.sandboxUrlProvider) {
|
|
88
|
+
this.artifactsPanel.sandboxUrlProvider = config.sandboxUrlProvider;
|
|
89
|
+
}
|
|
90
|
+
// Register the standalone tool renderer (not the panel itself)
|
|
91
|
+
registerToolRenderer("artifacts", new ArtifactsToolRenderer(this.artifactsPanel));
|
|
92
|
+
|
|
93
|
+
// Runtime providers factory for REPL tools (read-write access)
|
|
94
|
+
const runtimeProvidersFactory = () => {
|
|
95
|
+
const attachments: Attachment[] = [];
|
|
96
|
+
for (const message of this.agent!.state.messages) {
|
|
97
|
+
if (message.role === "user-with-attachments") {
|
|
98
|
+
message.attachments?.forEach((a: any) => {
|
|
99
|
+
attachments.push(a);
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const providers: SandboxRuntimeProvider[] = [];
|
|
104
|
+
|
|
105
|
+
// Add attachments provider if there are attachments
|
|
106
|
+
if (attachments.length > 0) {
|
|
107
|
+
providers.push(new AttachmentsRuntimeProvider(attachments));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Add artifacts provider with read-write access (for REPL)
|
|
111
|
+
providers.push(new ArtifactsRuntimeProvider(this.artifactsPanel!, this.agent!, true));
|
|
112
|
+
|
|
113
|
+
return providers;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
this.artifactsPanel.onArtifactsChange = () => {
|
|
117
|
+
const count = this.artifactsPanel?.artifacts?.size ?? 0;
|
|
118
|
+
const created = count > this.artifactCount;
|
|
119
|
+
this.hasArtifacts = count > 0;
|
|
120
|
+
this.artifactCount = count;
|
|
121
|
+
if (this.hasArtifacts && created) {
|
|
122
|
+
this.showArtifactsPanel = true;
|
|
123
|
+
}
|
|
124
|
+
this.requestUpdate();
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
this.artifactsPanel.onClose = () => {
|
|
128
|
+
this.showArtifactsPanel = false;
|
|
129
|
+
this.requestUpdate();
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
this.artifactsPanel.onOpen = () => {
|
|
133
|
+
this.showArtifactsPanel = true;
|
|
134
|
+
this.requestUpdate();
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// Set tools on the agent
|
|
138
|
+
// Pass runtimeProvidersFactory so consumers can configure their own REPL tools
|
|
139
|
+
const additionalTools =
|
|
140
|
+
config?.toolsFactory?.(agent, this.agentInterface, this.artifactsPanel, runtimeProvidersFactory) || [];
|
|
141
|
+
const tools = [this.artifactsPanel.tool, ...additionalTools];
|
|
142
|
+
this.agent.setTools(tools);
|
|
143
|
+
|
|
144
|
+
// Reconstruct artifacts from existing messages
|
|
145
|
+
// Temporarily disable the onArtifactsChange callback to prevent auto-opening on load
|
|
146
|
+
const originalCallback = this.artifactsPanel.onArtifactsChange;
|
|
147
|
+
this.artifactsPanel.onArtifactsChange = undefined;
|
|
148
|
+
await this.artifactsPanel.reconstructFromMessages(this.agent.state.messages);
|
|
149
|
+
this.artifactsPanel.onArtifactsChange = originalCallback;
|
|
150
|
+
|
|
151
|
+
this.hasArtifacts = this.artifactsPanel.artifacts.size > 0;
|
|
152
|
+
this.artifactCount = this.artifactsPanel.artifacts.size;
|
|
153
|
+
|
|
154
|
+
this.requestUpdate();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
render() {
|
|
158
|
+
if (!this.agent || !this.agentInterface) {
|
|
159
|
+
return html`<div class="flex items-center justify-center h-full">
|
|
160
|
+
<div class="text-muted-foreground">No agent set</div>
|
|
161
|
+
</div>`;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const isMobile = this.windowWidth < BREAKPOINT;
|
|
165
|
+
|
|
166
|
+
// Set panel props
|
|
167
|
+
if (this.artifactsPanel) {
|
|
168
|
+
this.artifactsPanel.collapsed = !this.showArtifactsPanel;
|
|
169
|
+
this.artifactsPanel.overlay = isMobile;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return html`
|
|
173
|
+
<div class="relative w-full h-full overflow-hidden flex">
|
|
174
|
+
<div class="h-full" style="${!isMobile && this.showArtifactsPanel && this.hasArtifacts ? "width: 50%;" : "width: 100%;"}">
|
|
175
|
+
${this.agentInterface}
|
|
176
|
+
</div>
|
|
177
|
+
|
|
178
|
+
<!-- Floating pill when artifacts exist and panel is collapsed -->
|
|
179
|
+
${
|
|
180
|
+
this.hasArtifacts && !this.showArtifactsPanel
|
|
181
|
+
? html`
|
|
182
|
+
<button
|
|
183
|
+
class="absolute z-30 top-4 left-1/2 -translate-x-1/2 pointer-events-auto"
|
|
184
|
+
@click=${() => {
|
|
185
|
+
this.showArtifactsPanel = true;
|
|
186
|
+
this.requestUpdate();
|
|
187
|
+
}}
|
|
188
|
+
title=${i18n("Show artifacts")}
|
|
189
|
+
>
|
|
190
|
+
${Badge(html`
|
|
191
|
+
<span class="inline-flex items-center gap-1">
|
|
192
|
+
<span>${i18n("Artifacts")}</span>
|
|
193
|
+
<span class="text-[10px] leading-none bg-primary-foreground/20 text-primary-foreground rounded px-1 font-mono tabular-nums">${this.artifactCount}</span>
|
|
194
|
+
</span>
|
|
195
|
+
`)}
|
|
196
|
+
</button>
|
|
197
|
+
`
|
|
198
|
+
: ""
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
<div class="h-full ${isMobile ? "absolute inset-0 pointer-events-none" : ""}" style="${!isMobile ? (!this.hasArtifacts || !this.showArtifactsPanel ? "display: none;" : "width: 50%;") : ""}">
|
|
202
|
+
${this.artifactsPanel}
|
|
203
|
+
</div>
|
|
204
|
+
</div>
|
|
205
|
+
`;
|
|
206
|
+
}
|
|
207
|
+
}
|
package/src/app.css
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/* Import Claude theme from mini-lit */
|
|
2
|
+
@import "@mariozechner/mini-lit/styles/themes/default.css";
|
|
3
|
+
|
|
4
|
+
/* Tell Tailwind to scan mini-lit components */
|
|
5
|
+
/* biome-ignore lint/suspicious/noUnknownAtRules: Tailwind 4 source directive */
|
|
6
|
+
@source "../../../node_modules/@mariozechner/mini-lit/dist";
|
|
7
|
+
|
|
8
|
+
/* Import Tailwind */
|
|
9
|
+
/* biome-ignore lint/correctness/noInvalidPositionAtImportRule: fuck you */
|
|
10
|
+
@import "tailwindcss";
|
|
11
|
+
|
|
12
|
+
body {
|
|
13
|
+
font-size: 16px;
|
|
14
|
+
-webkit-font-smoothing: antialiased;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
* {
|
|
18
|
+
scrollbar-width: thin;
|
|
19
|
+
scrollbar-color: var(--color-border) rgba(0, 0, 0, 0);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
*::-webkit-scrollbar {
|
|
23
|
+
width: 8px;
|
|
24
|
+
height: 8px;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
*::-webkit-scrollbar-track {
|
|
28
|
+
background: transparent;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
*::-webkit-scrollbar-thumb {
|
|
32
|
+
background-color: var(--color-border);
|
|
33
|
+
border-radius: 4px;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
*::-webkit-scrollbar-thumb:hover {
|
|
37
|
+
background-color: rgba(0, 0, 0, 0);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* Fix cursor for dialog close buttons */
|
|
41
|
+
.fixed.inset-0 button[aria-label*="Close"],
|
|
42
|
+
.fixed.inset-0 button[type="button"] {
|
|
43
|
+
cursor: pointer;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* Shimmer animation for thinking text */
|
|
47
|
+
@keyframes shimmer {
|
|
48
|
+
0% {
|
|
49
|
+
background-position: -200% 0;
|
|
50
|
+
}
|
|
51
|
+
100% {
|
|
52
|
+
background-position: 200% 0;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.animate-shimmer {
|
|
57
|
+
animation: shimmer 2s ease-in-out infinite;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* User message with fancy pill styling */
|
|
61
|
+
.user-message-container {
|
|
62
|
+
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
|
63
|
+
position: relative;
|
|
64
|
+
background: linear-gradient(135deg, rgba(217, 79, 0, 0.12), rgba(255, 107, 0, 0.12), rgba(212, 165, 0, 0.12));
|
|
65
|
+
border: 1px solid rgba(255, 107, 0, 0.25);
|
|
66
|
+
backdrop-filter: blur(10px);
|
|
67
|
+
max-width: 100%;
|
|
68
|
+
}
|
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
import { streamSimple, type ToolResultMessage, type Usage } from "@draht/ai";
|
|
2
|
+
import { html, LitElement } from "lit";
|
|
3
|
+
import { customElement, property, query } from "lit/decorators.js";
|
|
4
|
+
import { ModelSelector } from "../dialogs/ModelSelector.js";
|
|
5
|
+
import type { MessageEditor } from "./MessageEditor.js";
|
|
6
|
+
import "./MessageEditor.js";
|
|
7
|
+
import "./MessageList.js";
|
|
8
|
+
import "./Messages.js"; // Import for side effects to register the custom elements
|
|
9
|
+
import { getAppStorage } from "../storage/app-storage.js";
|
|
10
|
+
import "./StreamingMessageContainer.js";
|
|
11
|
+
import type { Agent, AgentEvent } from "@draht/agent-core";
|
|
12
|
+
import type { Attachment } from "../utils/attachment-utils.js";
|
|
13
|
+
import { formatUsage } from "../utils/format.js";
|
|
14
|
+
import { i18n } from "../utils/i18n.js";
|
|
15
|
+
import { createStreamFn } from "../utils/proxy-utils.js";
|
|
16
|
+
import type { UserMessageWithAttachments } from "./Messages.js";
|
|
17
|
+
import type { StreamingMessageContainer } from "./StreamingMessageContainer.js";
|
|
18
|
+
|
|
19
|
+
@customElement("agent-interface")
|
|
20
|
+
export class AgentInterface extends LitElement {
|
|
21
|
+
// Optional external session: when provided, this component becomes a view over the session
|
|
22
|
+
@property({ attribute: false }) session?: Agent;
|
|
23
|
+
@property({ type: Boolean }) enableAttachments = true;
|
|
24
|
+
@property({ type: Boolean }) enableModelSelector = true;
|
|
25
|
+
@property({ type: Boolean }) enableThinkingSelector = true;
|
|
26
|
+
@property({ type: Boolean }) showThemeToggle = false;
|
|
27
|
+
// Optional custom API key prompt handler - if not provided, uses default dialog
|
|
28
|
+
@property({ attribute: false }) onApiKeyRequired?: (provider: string) => Promise<boolean>;
|
|
29
|
+
// Optional callback called before sending a message
|
|
30
|
+
@property({ attribute: false }) onBeforeSend?: () => void | Promise<void>;
|
|
31
|
+
// Optional callback called before executing a tool call - return false to prevent execution
|
|
32
|
+
@property({ attribute: false }) onBeforeToolCall?: (toolName: string, args: any) => boolean | Promise<boolean>;
|
|
33
|
+
// Optional callback called when cost display is clicked
|
|
34
|
+
@property({ attribute: false }) onCostClick?: () => void;
|
|
35
|
+
|
|
36
|
+
// References
|
|
37
|
+
@query("message-editor") private _messageEditor!: MessageEditor;
|
|
38
|
+
@query("streaming-message-container") private _streamingContainer!: StreamingMessageContainer;
|
|
39
|
+
|
|
40
|
+
private _autoScroll = true;
|
|
41
|
+
private _lastScrollTop = 0;
|
|
42
|
+
private _lastClientHeight = 0;
|
|
43
|
+
private _scrollContainer?: HTMLElement;
|
|
44
|
+
private _resizeObserver?: ResizeObserver;
|
|
45
|
+
private _unsubscribeSession?: () => void;
|
|
46
|
+
|
|
47
|
+
public setInput(text: string, attachments?: Attachment[]) {
|
|
48
|
+
const update = () => {
|
|
49
|
+
if (!this._messageEditor) requestAnimationFrame(update);
|
|
50
|
+
else {
|
|
51
|
+
this._messageEditor.value = text;
|
|
52
|
+
this._messageEditor.attachments = attachments || [];
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
update();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public setAutoScroll(enabled: boolean) {
|
|
59
|
+
this._autoScroll = enabled;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
protected override createRenderRoot(): HTMLElement | DocumentFragment {
|
|
63
|
+
return this;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
override willUpdate(changedProperties: Map<string, any>) {
|
|
67
|
+
super.willUpdate(changedProperties);
|
|
68
|
+
|
|
69
|
+
// Re-subscribe when session property changes
|
|
70
|
+
if (changedProperties.has("session")) {
|
|
71
|
+
this.setupSessionSubscription();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
override async connectedCallback() {
|
|
76
|
+
super.connectedCallback();
|
|
77
|
+
|
|
78
|
+
this.style.display = "flex";
|
|
79
|
+
this.style.flexDirection = "column";
|
|
80
|
+
this.style.height = "100%";
|
|
81
|
+
this.style.minHeight = "0";
|
|
82
|
+
|
|
83
|
+
// Wait for first render to get scroll container
|
|
84
|
+
await this.updateComplete;
|
|
85
|
+
this._scrollContainer = this.querySelector(".overflow-y-auto") as HTMLElement;
|
|
86
|
+
|
|
87
|
+
if (this._scrollContainer) {
|
|
88
|
+
// Set up ResizeObserver to detect content changes
|
|
89
|
+
this._resizeObserver = new ResizeObserver(() => {
|
|
90
|
+
if (this._autoScroll && this._scrollContainer) {
|
|
91
|
+
this._scrollContainer.scrollTop = this._scrollContainer.scrollHeight;
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Observe the content container inside the scroll container
|
|
96
|
+
const contentContainer = this._scrollContainer.querySelector(".max-w-3xl");
|
|
97
|
+
if (contentContainer) {
|
|
98
|
+
this._resizeObserver.observe(contentContainer);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Set up scroll listener with better detection
|
|
102
|
+
this._scrollContainer.addEventListener("scroll", this._handleScroll);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Subscribe to external session if provided
|
|
106
|
+
this.setupSessionSubscription();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
override disconnectedCallback() {
|
|
110
|
+
super.disconnectedCallback();
|
|
111
|
+
|
|
112
|
+
// Clean up observers and listeners
|
|
113
|
+
if (this._resizeObserver) {
|
|
114
|
+
this._resizeObserver.disconnect();
|
|
115
|
+
this._resizeObserver = undefined;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (this._scrollContainer) {
|
|
119
|
+
this._scrollContainer.removeEventListener("scroll", this._handleScroll);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (this._unsubscribeSession) {
|
|
123
|
+
this._unsubscribeSession();
|
|
124
|
+
this._unsubscribeSession = undefined;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private setupSessionSubscription() {
|
|
129
|
+
if (this._unsubscribeSession) {
|
|
130
|
+
this._unsubscribeSession();
|
|
131
|
+
this._unsubscribeSession = undefined;
|
|
132
|
+
}
|
|
133
|
+
if (!this.session) return;
|
|
134
|
+
|
|
135
|
+
// Set default streamFn with proxy support if not already set
|
|
136
|
+
if (this.session.streamFn === streamSimple) {
|
|
137
|
+
this.session.streamFn = createStreamFn(async () => {
|
|
138
|
+
const enabled = await getAppStorage().settings.get<boolean>("proxy.enabled");
|
|
139
|
+
return enabled ? (await getAppStorage().settings.get<string>("proxy.url")) || undefined : undefined;
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Set default getApiKey if not already set
|
|
144
|
+
if (!this.session.getApiKey) {
|
|
145
|
+
this.session.getApiKey = async (provider: string) => {
|
|
146
|
+
const key = await getAppStorage().providerKeys.get(provider);
|
|
147
|
+
return key ?? undefined;
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
this._unsubscribeSession = this.session.subscribe(async (ev: AgentEvent) => {
|
|
152
|
+
switch (ev.type) {
|
|
153
|
+
case "message_start":
|
|
154
|
+
case "message_end":
|
|
155
|
+
case "turn_start":
|
|
156
|
+
case "turn_end":
|
|
157
|
+
case "agent_start":
|
|
158
|
+
this.requestUpdate();
|
|
159
|
+
break;
|
|
160
|
+
case "agent_end":
|
|
161
|
+
// Clear streaming container when agent finishes
|
|
162
|
+
if (this._streamingContainer) {
|
|
163
|
+
this._streamingContainer.isStreaming = false;
|
|
164
|
+
this._streamingContainer.setMessage(null, true);
|
|
165
|
+
}
|
|
166
|
+
this.requestUpdate();
|
|
167
|
+
break;
|
|
168
|
+
case "message_update":
|
|
169
|
+
if (this._streamingContainer) {
|
|
170
|
+
const isStreaming = this.session?.state.isStreaming || false;
|
|
171
|
+
this._streamingContainer.isStreaming = isStreaming;
|
|
172
|
+
this._streamingContainer.setMessage(ev.message, !isStreaming);
|
|
173
|
+
}
|
|
174
|
+
this.requestUpdate();
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
private _handleScroll = (_ev: any) => {
|
|
181
|
+
if (!this._scrollContainer) return;
|
|
182
|
+
|
|
183
|
+
const currentScrollTop = this._scrollContainer.scrollTop;
|
|
184
|
+
const scrollHeight = this._scrollContainer.scrollHeight;
|
|
185
|
+
const clientHeight = this._scrollContainer.clientHeight;
|
|
186
|
+
const distanceFromBottom = scrollHeight - currentScrollTop - clientHeight;
|
|
187
|
+
|
|
188
|
+
// Ignore relayout due to message editor getting pushed up by stats
|
|
189
|
+
if (clientHeight < this._lastClientHeight) {
|
|
190
|
+
this._lastClientHeight = clientHeight;
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Only disable auto-scroll if user scrolled UP or is far from bottom
|
|
195
|
+
if (currentScrollTop !== 0 && currentScrollTop < this._lastScrollTop && distanceFromBottom > 50) {
|
|
196
|
+
this._autoScroll = false;
|
|
197
|
+
} else if (distanceFromBottom < 10) {
|
|
198
|
+
// Re-enable if very close to bottom
|
|
199
|
+
this._autoScroll = true;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
this._lastScrollTop = currentScrollTop;
|
|
203
|
+
this._lastClientHeight = clientHeight;
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
public async sendMessage(input: string, attachments?: Attachment[]) {
|
|
207
|
+
if ((!input.trim() && attachments?.length === 0) || this.session?.state.isStreaming) return;
|
|
208
|
+
const session = this.session;
|
|
209
|
+
if (!session) throw new Error("No session set on AgentInterface");
|
|
210
|
+
if (!session.state.model) throw new Error("No model set on AgentInterface");
|
|
211
|
+
|
|
212
|
+
// Check if API key exists for the provider (only needed in direct mode)
|
|
213
|
+
const provider = session.state.model.provider;
|
|
214
|
+
const apiKey = await getAppStorage().providerKeys.get(provider);
|
|
215
|
+
|
|
216
|
+
// If no API key, prompt for it
|
|
217
|
+
if (!apiKey) {
|
|
218
|
+
if (!this.onApiKeyRequired) {
|
|
219
|
+
console.error("No API key configured and no onApiKeyRequired handler set");
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const success = await this.onApiKeyRequired(provider);
|
|
224
|
+
|
|
225
|
+
// If still no API key, abort the send
|
|
226
|
+
if (!success) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Call onBeforeSend hook before sending
|
|
232
|
+
if (this.onBeforeSend) {
|
|
233
|
+
await this.onBeforeSend();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Only clear editor after we know we can send
|
|
237
|
+
this._messageEditor.value = "";
|
|
238
|
+
this._messageEditor.attachments = [];
|
|
239
|
+
this._autoScroll = true; // Enable auto-scroll when sending a message
|
|
240
|
+
|
|
241
|
+
// Compose message with attachments if any
|
|
242
|
+
if (attachments && attachments.length > 0) {
|
|
243
|
+
const message: UserMessageWithAttachments = {
|
|
244
|
+
role: "user-with-attachments",
|
|
245
|
+
content: input,
|
|
246
|
+
attachments,
|
|
247
|
+
timestamp: Date.now(),
|
|
248
|
+
};
|
|
249
|
+
await this.session?.prompt(message);
|
|
250
|
+
} else {
|
|
251
|
+
await this.session?.prompt(input);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
private renderMessages() {
|
|
256
|
+
if (!this.session)
|
|
257
|
+
return html`<div class="p-4 text-center text-muted-foreground">${i18n("No session available")}</div>`;
|
|
258
|
+
const state = this.session.state;
|
|
259
|
+
// Build a map of tool results to allow inline rendering in assistant messages
|
|
260
|
+
const toolResultsById = new Map<string, ToolResultMessage<any>>();
|
|
261
|
+
for (const message of state.messages) {
|
|
262
|
+
if (message.role === "toolResult") {
|
|
263
|
+
toolResultsById.set(message.toolCallId, message);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return html`
|
|
267
|
+
<div class="flex flex-col gap-3">
|
|
268
|
+
<!-- Stable messages list - won't re-render during streaming -->
|
|
269
|
+
<message-list
|
|
270
|
+
.messages=${this.session.state.messages}
|
|
271
|
+
.tools=${state.tools}
|
|
272
|
+
.pendingToolCalls=${this.session ? this.session.state.pendingToolCalls : new Set<string>()}
|
|
273
|
+
.isStreaming=${state.isStreaming}
|
|
274
|
+
.onCostClick=${this.onCostClick}
|
|
275
|
+
></message-list>
|
|
276
|
+
|
|
277
|
+
<!-- Streaming message container - manages its own updates -->
|
|
278
|
+
<streaming-message-container
|
|
279
|
+
class="${state.isStreaming ? "" : "hidden"}"
|
|
280
|
+
.tools=${state.tools}
|
|
281
|
+
.isStreaming=${state.isStreaming}
|
|
282
|
+
.pendingToolCalls=${state.pendingToolCalls}
|
|
283
|
+
.toolResultsById=${toolResultsById}
|
|
284
|
+
.onCostClick=${this.onCostClick}
|
|
285
|
+
></streaming-message-container>
|
|
286
|
+
</div>
|
|
287
|
+
`;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
private renderStats() {
|
|
291
|
+
if (!this.session) return html`<div class="text-xs h-5"></div>`;
|
|
292
|
+
|
|
293
|
+
const state = this.session.state;
|
|
294
|
+
const totals = state.messages
|
|
295
|
+
.filter((m) => m.role === "assistant")
|
|
296
|
+
.reduce(
|
|
297
|
+
(acc, msg: any) => {
|
|
298
|
+
const usage = msg.usage;
|
|
299
|
+
if (usage) {
|
|
300
|
+
acc.input += usage.input;
|
|
301
|
+
acc.output += usage.output;
|
|
302
|
+
acc.cacheRead += usage.cacheRead;
|
|
303
|
+
acc.cacheWrite += usage.cacheWrite;
|
|
304
|
+
acc.cost.total += usage.cost.total;
|
|
305
|
+
}
|
|
306
|
+
return acc;
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
input: 0,
|
|
310
|
+
output: 0,
|
|
311
|
+
cacheRead: 0,
|
|
312
|
+
cacheWrite: 0,
|
|
313
|
+
totalTokens: 0,
|
|
314
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
315
|
+
} satisfies Usage,
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
const hasTotals = totals.input || totals.output || totals.cacheRead || totals.cacheWrite;
|
|
319
|
+
const totalsText = hasTotals ? formatUsage(totals) : "";
|
|
320
|
+
|
|
321
|
+
return html`
|
|
322
|
+
<div class="text-xs text-muted-foreground flex justify-between items-center h-5">
|
|
323
|
+
<div class="flex items-center gap-1">
|
|
324
|
+
${this.showThemeToggle ? html`<theme-toggle></theme-toggle>` : html``}
|
|
325
|
+
</div>
|
|
326
|
+
<div class="flex ml-auto items-center gap-3">
|
|
327
|
+
${
|
|
328
|
+
totalsText
|
|
329
|
+
? this.onCostClick
|
|
330
|
+
? html`<span class="cursor-pointer hover:text-foreground transition-colors" @click=${this.onCostClick}>${totalsText}</span>`
|
|
331
|
+
: html`<span>${totalsText}</span>`
|
|
332
|
+
: ""
|
|
333
|
+
}
|
|
334
|
+
</div>
|
|
335
|
+
</div>
|
|
336
|
+
`;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
override render() {
|
|
340
|
+
if (!this.session)
|
|
341
|
+
return html`<div class="p-4 text-center text-muted-foreground">${i18n("No session set")}</div>`;
|
|
342
|
+
|
|
343
|
+
const session = this.session;
|
|
344
|
+
const state = this.session.state;
|
|
345
|
+
return html`
|
|
346
|
+
<div class="flex flex-col h-full bg-background text-foreground">
|
|
347
|
+
<!-- Messages Area -->
|
|
348
|
+
<div class="flex-1 overflow-y-auto">
|
|
349
|
+
<div class="max-w-3xl mx-auto p-4 pb-0">${this.renderMessages()}</div>
|
|
350
|
+
</div>
|
|
351
|
+
|
|
352
|
+
<!-- Input Area -->
|
|
353
|
+
<div class="shrink-0">
|
|
354
|
+
<div class="max-w-3xl mx-auto px-2">
|
|
355
|
+
<message-editor
|
|
356
|
+
.isStreaming=${state.isStreaming}
|
|
357
|
+
.currentModel=${state.model}
|
|
358
|
+
.thinkingLevel=${state.thinkingLevel}
|
|
359
|
+
.showAttachmentButton=${this.enableAttachments}
|
|
360
|
+
.showModelSelector=${this.enableModelSelector}
|
|
361
|
+
.showThinkingSelector=${this.enableThinkingSelector}
|
|
362
|
+
.onSend=${(input: string, attachments: Attachment[]) => {
|
|
363
|
+
this.sendMessage(input, attachments);
|
|
364
|
+
}}
|
|
365
|
+
.onAbort=${() => session.abort()}
|
|
366
|
+
.onModelSelect=${() => {
|
|
367
|
+
ModelSelector.open(state.model, (model) => session.setModel(model));
|
|
368
|
+
}}
|
|
369
|
+
.onThinkingChange=${
|
|
370
|
+
this.enableThinkingSelector
|
|
371
|
+
? (level: "off" | "minimal" | "low" | "medium" | "high") => {
|
|
372
|
+
session.setThinkingLevel(level);
|
|
373
|
+
}
|
|
374
|
+
: undefined
|
|
375
|
+
}
|
|
376
|
+
></message-editor>
|
|
377
|
+
${this.renderStats()}
|
|
378
|
+
</div>
|
|
379
|
+
</div>
|
|
380
|
+
</div>
|
|
381
|
+
`;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Register custom element with guard
|
|
386
|
+
if (!customElements.get("agent-interface")) {
|
|
387
|
+
customElements.define("agent-interface", AgentInterface);
|
|
388
|
+
}
|