@mariozechner/pi-web-ui 0.5.44 → 0.5.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +178 -99
- package/dist/ChatPanel.d.ts +15 -10
- package/dist/ChatPanel.d.ts.map +1 -1
- package/dist/ChatPanel.js +68 -100
- package/dist/ChatPanel.js.map +1 -1
- package/dist/{state/agent-session.d.ts → agent/agent.d.ts} +23 -19
- package/dist/agent/agent.d.ts.map +1 -0
- package/dist/{state/agent-session.js → agent/agent.js} +50 -32
- package/dist/agent/agent.js.map +1 -0
- package/dist/{state → agent}/transports/AppTransport.d.ts +1 -3
- package/dist/agent/transports/AppTransport.d.ts.map +1 -0
- package/dist/{state → agent}/transports/AppTransport.js +5 -4
- package/dist/{state → agent}/transports/AppTransport.js.map +1 -1
- package/dist/{state → agent}/transports/ProviderTransport.d.ts +1 -3
- package/dist/agent/transports/ProviderTransport.d.ts.map +1 -0
- package/dist/{state → agent}/transports/ProviderTransport.js +6 -7
- package/dist/agent/transports/ProviderTransport.js.map +1 -0
- package/dist/{state → agent}/transports/index.d.ts.map +1 -1
- package/dist/agent/transports/index.js.map +1 -0
- package/dist/{state → agent}/transports/proxy-types.d.ts.map +1 -1
- package/dist/agent/transports/proxy-types.js.map +1 -0
- package/dist/agent/transports/types.d.ts +12 -0
- package/dist/agent/transports/types.d.ts.map +1 -0
- package/dist/{state → agent}/transports/types.js.map +1 -1
- package/dist/{state → agent}/types.d.ts.map +1 -1
- package/dist/{state → agent}/types.js.map +1 -1
- package/dist/app.css +1 -1
- package/dist/components/AgentInterface.d.ts +7 -4
- package/dist/components/AgentInterface.d.ts.map +1 -1
- package/dist/components/AgentInterface.js +29 -17
- package/dist/components/AgentInterface.js.map +1 -1
- package/dist/components/ConsoleBlock.d.ts +1 -0
- package/dist/components/ConsoleBlock.d.ts.map +1 -1
- package/dist/components/ConsoleBlock.js +7 -1
- package/dist/components/ConsoleBlock.js.map +1 -1
- package/dist/components/ExpandableSection.d.ts +15 -0
- package/dist/components/ExpandableSection.d.ts.map +1 -0
- package/dist/components/ExpandableSection.js +63 -0
- package/dist/components/ExpandableSection.js.map +1 -0
- package/dist/components/MessageEditor.d.ts +8 -1
- package/dist/components/MessageEditor.d.ts.map +1 -1
- package/dist/components/MessageEditor.js +149 -6
- package/dist/components/MessageEditor.js.map +1 -1
- package/dist/components/MessageList.d.ts +3 -2
- package/dist/components/MessageList.d.ts.map +1 -1
- package/dist/components/MessageList.js +14 -1
- package/dist/components/MessageList.js.map +1 -1
- package/dist/components/Messages.d.ts +15 -6
- package/dist/components/Messages.d.ts.map +1 -1
- package/dist/components/Messages.js +17 -83
- package/dist/components/Messages.js.map +1 -1
- package/dist/components/ProviderKeyInput.d.ts.map +1 -1
- package/dist/components/ProviderKeyInput.js +6 -5
- package/dist/components/ProviderKeyInput.js.map +1 -1
- package/dist/components/SandboxedIframe.d.ts +29 -7
- package/dist/components/SandboxedIframe.d.ts.map +1 -1
- package/dist/components/SandboxedIframe.js +350 -282
- package/dist/components/SandboxedIframe.js.map +1 -1
- 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 +189 -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 +64 -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 +161 -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 +97 -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 +168 -0
- package/dist/components/sandbox/RuntimeMessageRouter.js.map +1 -0
- package/dist/components/sandbox/SandboxRuntimeProvider.d.ts +33 -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.map +1 -1
- package/dist/dialogs/ApiKeyPromptDialog.js +2 -5
- package/dist/dialogs/ApiKeyPromptDialog.js.map +1 -1
- package/dist/dialogs/ModelSelector.js.map +1 -1
- 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/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.map +1 -1
- package/dist/dialogs/SettingsDialog.js +1 -0
- package/dist/dialogs/SettingsDialog.js.map +1 -1
- package/dist/index.d.ts +34 -16
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +32 -14
- package/dist/index.js.map +1 -1
- 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 +17 -12
- package/dist/storage/app-storage.d.ts.map +1 -1
- package/dist/storage/app-storage.js +13 -20
- package/dist/storage/app-storage.js.map +1 -1
- 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 +166 -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 +26 -0
- package/dist/storage/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 +31 -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 +156 -22
- package/dist/storage/types.d.ts.map +1 -1
- package/dist/tools/artifacts/ArtifactElement.d.ts +0 -1
- package/dist/tools/artifacts/ArtifactElement.d.ts.map +1 -1
- package/dist/tools/artifacts/ArtifactElement.js +0 -1
- package/dist/tools/artifacts/ArtifactElement.js.map +1 -1
- 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 +22 -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 +95 -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 +208 -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 +216 -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 +117 -0
- package/dist/tools/artifacts/GenericArtifact.js.map +1 -0
- package/dist/tools/artifacts/HtmlArtifact.d.ts +8 -11
- package/dist/tools/artifacts/HtmlArtifact.d.ts.map +1 -1
- package/dist/tools/artifacts/HtmlArtifact.js +56 -88
- package/dist/tools/artifacts/HtmlArtifact.js.map +1 -1
- 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 +120 -0
- package/dist/tools/artifacts/ImageArtifact.js.map +1 -0
- package/dist/tools/artifacts/MarkdownArtifact.d.ts +0 -1
- package/dist/tools/artifacts/MarkdownArtifact.d.ts.map +1 -1
- package/dist/tools/artifacts/MarkdownArtifact.js +0 -4
- package/dist/tools/artifacts/MarkdownArtifact.js.map +1 -1
- 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 +184 -0
- package/dist/tools/artifacts/PdfArtifact.js.map +1 -0
- package/dist/tools/artifacts/SvgArtifact.d.ts +0 -1
- package/dist/tools/artifacts/SvgArtifact.d.ts.map +1 -1
- package/dist/tools/artifacts/SvgArtifact.js +0 -4
- package/dist/tools/artifacts/SvgArtifact.js.map +1 -1
- package/dist/tools/artifacts/TextArtifact.d.ts +0 -1
- package/dist/tools/artifacts/TextArtifact.d.ts.map +1 -1
- package/dist/tools/artifacts/TextArtifact.js +0 -4
- package/dist/tools/artifacts/TextArtifact.js.map +1 -1
- 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 +262 -0
- package/dist/tools/artifacts/artifacts-tool-renderer.js.map +1 -0
- package/dist/tools/artifacts/artifacts.d.ts +10 -13
- package/dist/tools/artifacts/artifacts.d.ts.map +1 -1
- package/dist/tools/artifacts/artifacts.js +166 -344
- package/dist/tools/artifacts/artifacts.js.map +1 -1
- package/dist/tools/artifacts/index.d.ts +1 -0
- package/dist/tools/artifacts/index.d.ts.map +1 -1
- package/dist/tools/artifacts/index.js +1 -0
- package/dist/tools/artifacts/index.js.map +1 -1
- 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 +193 -0
- package/dist/tools/extract-document.js.map +1 -0
- package/dist/tools/index.d.ts +9 -7
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +17 -13
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/javascript-repl.d.ts +16 -15
- package/dist/tools/javascript-repl.d.ts.map +1 -1
- package/dist/tools/javascript-repl.js +101 -133
- package/dist/tools/javascript-repl.js.map +1 -1
- package/dist/tools/renderer-registry.d.ts +12 -0
- package/dist/tools/renderer-registry.d.ts.map +1 -1
- package/dist/tools/renderer-registry.js +78 -0
- package/dist/tools/renderer-registry.js.map +1 -1
- package/dist/tools/renderers/BashRenderer.d.ts +2 -4
- package/dist/tools/renderers/BashRenderer.d.ts.map +1 -1
- package/dist/tools/renderers/BashRenderer.js +30 -26
- package/dist/tools/renderers/BashRenderer.js.map +1 -1
- package/dist/tools/renderers/CalculateRenderer.d.ts +2 -4
- package/dist/tools/renderers/CalculateRenderer.d.ts.map +1 -1
- package/dist/tools/renderers/CalculateRenderer.js +32 -28
- package/dist/tools/renderers/CalculateRenderer.js.map +1 -1
- package/dist/tools/renderers/DefaultRenderer.d.ts +2 -4
- package/dist/tools/renderers/DefaultRenderer.d.ts.map +1 -1
- package/dist/tools/renderers/DefaultRenderer.js +78 -18
- package/dist/tools/renderers/DefaultRenderer.js.map +1 -1
- package/dist/tools/renderers/GetCurrentTimeRenderer.d.ts +2 -4
- package/dist/tools/renderers/GetCurrentTimeRenderer.d.ts.map +1 -1
- package/dist/tools/renderers/GetCurrentTimeRenderer.js +57 -21
- package/dist/tools/renderers/GetCurrentTimeRenderer.js.map +1 -1
- package/dist/tools/types.d.ts +5 -2
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/utils/i18n.d.ts +424 -1
- package/dist/utils/i18n.d.ts.map +1 -1
- package/dist/utils/i18n.js +131 -7
- package/dist/utils/i18n.js.map +1 -1
- package/example/package.json +2 -1
- package/example/src/custom-messages.ts +112 -0
- package/example/src/main.ts +391 -38
- package/package.json +48 -43
- package/scripts/count-prompt-tokens.ts +88 -0
- package/src/ChatPanel.ts +93 -101
- package/src/{state/agent-session.ts → agent/agent.ts} +80 -55
- package/src/{state → agent}/transports/AppTransport.ts +6 -6
- package/src/{state → agent}/transports/ProviderTransport.ts +13 -7
- package/src/{state → agent}/transports/types.ts +8 -2
- package/src/components/AgentInterface.ts +32 -16
- package/src/components/ConsoleBlock.ts +5 -1
- package/src/components/ExpandableSection.ts +46 -0
- package/src/components/MessageEditor.ts +159 -5
- package/src/components/MessageList.ts +18 -3
- package/src/components/Messages.ts +48 -89
- package/src/components/ProviderKeyInput.ts +6 -5
- package/src/components/SandboxedIframe.ts +412 -321
- 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 +187 -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 +35 -0
- package/src/dialogs/ApiKeyPromptDialog.ts +2 -5
- package/src/dialogs/ModelSelector.ts +2 -2
- package/src/dialogs/PersistentStorageDialog.ts +141 -0
- package/src/dialogs/SessionListDialog.ts +148 -0
- package/src/dialogs/SettingsDialog.ts +1 -0
- package/src/index.ts +61 -20
- package/src/prompts/prompts.ts +282 -0
- package/src/storage/app-storage.ts +27 -24
- package/src/storage/backends/indexeddb-storage-backend.ts +193 -0
- package/src/storage/store.ts +33 -0
- package/src/storage/stores/provider-keys-store.ts +33 -0
- package/src/storage/stores/sessions-store.ts +130 -0
- package/src/storage/stores/settings-store.ts +34 -0
- package/src/storage/types.ts +182 -22
- package/src/tools/artifacts/ArtifactElement.ts +0 -1
- package/src/tools/artifacts/ArtifactPill.ts +25 -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 +64 -94
- package/src/tools/artifacts/ImageArtifact.ts +116 -0
- package/src/tools/artifacts/MarkdownArtifact.ts +0 -1
- package/src/tools/artifacts/PdfArtifact.ts +201 -0
- package/src/tools/artifacts/SvgArtifact.ts +0 -1
- package/src/tools/artifacts/TextArtifact.ts +0 -1
- package/src/tools/artifacts/artifacts-tool-renderer.ts +298 -0
- package/src/tools/artifacts/artifacts.ts +190 -366
- package/src/tools/artifacts/index.ts +1 -0
- package/src/tools/extract-document.ts +250 -0
- package/src/tools/index.ts +25 -14
- package/src/tools/javascript-repl.ts +138 -160
- package/src/tools/renderer-registry.ts +98 -0
- package/src/tools/renderers/BashRenderer.ts +33 -30
- package/src/tools/renderers/CalculateRenderer.ts +36 -31
- package/src/tools/renderers/DefaultRenderer.ts +84 -21
- package/src/tools/renderers/GetCurrentTimeRenderer.ts +68 -23
- package/src/tools/types.ts +10 -2
- package/src/utils/i18n.ts +203 -8
- package/dist/state/agent-session.d.ts.map +0 -1
- package/dist/state/agent-session.js.map +0 -1
- package/dist/state/transports/AppTransport.d.ts.map +0 -1
- package/dist/state/transports/ProviderTransport.d.ts.map +0 -1
- package/dist/state/transports/ProviderTransport.js.map +0 -1
- package/dist/state/transports/index.js.map +0 -1
- package/dist/state/transports/proxy-types.js.map +0 -1
- package/dist/state/transports/types.d.ts +0 -11
- package/dist/state/transports/types.d.ts.map +0 -1
- package/dist/storage/backends/chrome-storage-backend.d.ts +0 -18
- package/dist/storage/backends/chrome-storage-backend.d.ts.map +0 -1
- package/dist/storage/backends/chrome-storage-backend.js +0 -67
- package/dist/storage/backends/chrome-storage-backend.js.map +0 -1
- package/dist/storage/backends/indexeddb-backend.d.ts +0 -20
- package/dist/storage/backends/indexeddb-backend.d.ts.map +0 -1
- package/dist/storage/backends/indexeddb-backend.js +0 -89
- package/dist/storage/backends/indexeddb-backend.js.map +0 -1
- package/dist/storage/backends/local-storage-backend.d.ts +0 -18
- package/dist/storage/backends/local-storage-backend.d.ts.map +0 -1
- package/dist/storage/backends/local-storage-backend.js +0 -69
- package/dist/storage/backends/local-storage-backend.js.map +0 -1
- package/dist/storage/repositories/provider-keys-repository.d.ts +0 -34
- package/dist/storage/repositories/provider-keys-repository.d.ts.map +0 -1
- package/dist/storage/repositories/provider-keys-repository.js +0 -50
- package/dist/storage/repositories/provider-keys-repository.js.map +0 -1
- package/dist/storage/repositories/settings-repository.d.ts +0 -34
- package/dist/storage/repositories/settings-repository.d.ts.map +0 -1
- package/dist/storage/repositories/settings-repository.js +0 -46
- package/dist/storage/repositories/settings-repository.js.map +0 -1
- package/src/storage/backends/chrome-storage-backend.ts +0 -82
- package/src/storage/backends/indexeddb-backend.ts +0 -107
- package/src/storage/backends/local-storage-backend.ts +0 -74
- package/src/storage/repositories/provider-keys-repository.ts +0 -55
- package/src/storage/repositories/settings-repository.ts +0 -51
- /package/dist/{state → agent}/transports/index.d.ts +0 -0
- /package/dist/{state → agent}/transports/index.js +0 -0
- /package/dist/{state → agent}/transports/proxy-types.d.ts +0 -0
- /package/dist/{state → agent}/transports/proxy-types.js +0 -0
- /package/dist/{state → agent}/transports/types.js +0 -0
- /package/dist/{state → agent}/types.d.ts +0 -0
- /package/dist/{state → agent}/types.js +0 -0
- /package/src/{state → agent}/transports/index.ts +0 -0
- /package/src/{state → agent}/transports/proxy-types.ts +0 -0
- /package/src/{state → agent}/types.ts +0 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import type { SandboxRuntimeProvider } from "./SandboxRuntimeProvider.js";
|
|
2
|
+
|
|
3
|
+
// Type declaration for chrome extension API (when available)
|
|
4
|
+
declare const chrome: any;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Message consumer interface - components that want to receive messages from sandboxes
|
|
8
|
+
*/
|
|
9
|
+
export interface MessageConsumer {
|
|
10
|
+
/**
|
|
11
|
+
* Handle a message from a sandbox.
|
|
12
|
+
* All consumers receive all messages - decide internally what to handle.
|
|
13
|
+
*/
|
|
14
|
+
handleMessage(message: any): Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Sandbox context - tracks active sandboxes and their consumers
|
|
19
|
+
*/
|
|
20
|
+
interface SandboxContext {
|
|
21
|
+
sandboxId: string;
|
|
22
|
+
iframe: HTMLIFrameElement | null; // null until setSandboxIframe() or null for user scripts
|
|
23
|
+
providers: SandboxRuntimeProvider[];
|
|
24
|
+
consumers: Set<MessageConsumer>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Centralized message router for all runtime communication.
|
|
29
|
+
*
|
|
30
|
+
* This singleton replaces all individual window.addEventListener("message") calls
|
|
31
|
+
* with a single global listener that routes messages to the appropriate handlers.
|
|
32
|
+
* Also handles user script messages from chrome.runtime.onUserScriptMessage.
|
|
33
|
+
*
|
|
34
|
+
* Benefits:
|
|
35
|
+
* - Single global listener instead of multiple independent listeners
|
|
36
|
+
* - Automatic cleanup when sandboxes are destroyed
|
|
37
|
+
* - Support for bidirectional communication (providers) and broadcasting (consumers)
|
|
38
|
+
* - Works with both sandbox iframes and user scripts
|
|
39
|
+
* - Clear lifecycle management
|
|
40
|
+
*/
|
|
41
|
+
export class RuntimeMessageRouter {
|
|
42
|
+
private sandboxes = new Map<string, SandboxContext>();
|
|
43
|
+
private messageListener: ((e: MessageEvent) => void) | null = null;
|
|
44
|
+
private userScriptMessageListener:
|
|
45
|
+
| ((message: any, sender: any, sendResponse: (response: any) => void) => boolean)
|
|
46
|
+
| null = null;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Register a new sandbox with its runtime providers.
|
|
50
|
+
* Call this BEFORE creating the iframe (for sandbox contexts) or executing user script.
|
|
51
|
+
*/
|
|
52
|
+
registerSandbox(sandboxId: string, providers: SandboxRuntimeProvider[], consumers: MessageConsumer[]): void {
|
|
53
|
+
this.sandboxes.set(sandboxId, {
|
|
54
|
+
sandboxId,
|
|
55
|
+
iframe: null, // Will be set via setSandboxIframe() for sandbox contexts
|
|
56
|
+
providers,
|
|
57
|
+
consumers: new Set(consumers),
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Setup global listener if not already done
|
|
61
|
+
this.setupListener();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Update the iframe reference for a sandbox.
|
|
66
|
+
* Call this AFTER creating the iframe.
|
|
67
|
+
* This is needed so providers can send responses back to the sandbox.
|
|
68
|
+
*/
|
|
69
|
+
setSandboxIframe(sandboxId: string, iframe: HTMLIFrameElement): void {
|
|
70
|
+
const context = this.sandboxes.get(sandboxId);
|
|
71
|
+
if (context) {
|
|
72
|
+
context.iframe = iframe;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Unregister a sandbox and remove all its consumers.
|
|
78
|
+
* Call this when the sandbox is destroyed.
|
|
79
|
+
*/
|
|
80
|
+
unregisterSandbox(sandboxId: string): void {
|
|
81
|
+
this.sandboxes.delete(sandboxId);
|
|
82
|
+
|
|
83
|
+
// If no more sandboxes, remove global listeners
|
|
84
|
+
if (this.sandboxes.size === 0) {
|
|
85
|
+
// Remove iframe listener
|
|
86
|
+
if (this.messageListener) {
|
|
87
|
+
window.removeEventListener("message", this.messageListener);
|
|
88
|
+
this.messageListener = null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Remove user script listener
|
|
92
|
+
if (this.userScriptMessageListener && typeof chrome !== "undefined" && chrome.runtime?.onUserScriptMessage) {
|
|
93
|
+
chrome.runtime.onUserScriptMessage.removeListener(this.userScriptMessageListener);
|
|
94
|
+
this.userScriptMessageListener = null;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Add a message consumer for a sandbox.
|
|
101
|
+
* Consumers receive broadcast messages (console, execution-complete, etc.)
|
|
102
|
+
*/
|
|
103
|
+
addConsumer(sandboxId: string, consumer: MessageConsumer): void {
|
|
104
|
+
const context = this.sandboxes.get(sandboxId);
|
|
105
|
+
if (context) {
|
|
106
|
+
context.consumers.add(consumer);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Remove a message consumer from a sandbox.
|
|
112
|
+
*/
|
|
113
|
+
removeConsumer(sandboxId: string, consumer: MessageConsumer): void {
|
|
114
|
+
const context = this.sandboxes.get(sandboxId);
|
|
115
|
+
if (context) {
|
|
116
|
+
context.consumers.delete(consumer);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Setup the global message listeners (called automatically)
|
|
122
|
+
*/
|
|
123
|
+
private setupListener(): void {
|
|
124
|
+
// Setup sandbox iframe listener
|
|
125
|
+
if (!this.messageListener) {
|
|
126
|
+
this.messageListener = async (e: MessageEvent) => {
|
|
127
|
+
const { sandboxId, messageId } = e.data;
|
|
128
|
+
if (!sandboxId) return;
|
|
129
|
+
|
|
130
|
+
const context = this.sandboxes.get(sandboxId);
|
|
131
|
+
if (!context) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Create respond() function for bidirectional communication
|
|
136
|
+
const respond = (response: any) => {
|
|
137
|
+
context.iframe?.contentWindow?.postMessage(
|
|
138
|
+
{
|
|
139
|
+
type: "runtime-response",
|
|
140
|
+
messageId,
|
|
141
|
+
sandboxId,
|
|
142
|
+
...response,
|
|
143
|
+
},
|
|
144
|
+
"*",
|
|
145
|
+
);
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
// 1. Try provider handlers first (for bidirectional comm)
|
|
149
|
+
for (const provider of context.providers) {
|
|
150
|
+
if (provider.handleMessage) {
|
|
151
|
+
await provider.handleMessage(e.data, respond);
|
|
152
|
+
// Don't stop - let consumers also handle the message
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// 2. Broadcast to consumers (one-way messages or lifecycle events)
|
|
157
|
+
for (const consumer of context.consumers) {
|
|
158
|
+
await consumer.handleMessage(e.data);
|
|
159
|
+
// Don't stop - let all consumers see the message
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
window.addEventListener("message", this.messageListener);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Setup user script message listener
|
|
167
|
+
if (!this.userScriptMessageListener) {
|
|
168
|
+
// Guard: check if we're in extension context
|
|
169
|
+
if (typeof chrome === "undefined" || !chrome.runtime?.onUserScriptMessage) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
this.userScriptMessageListener = (message: any, _sender: any, sendResponse: (response: any) => void) => {
|
|
174
|
+
const { sandboxId } = message;
|
|
175
|
+
if (!sandboxId) return false;
|
|
176
|
+
|
|
177
|
+
const context = this.sandboxes.get(sandboxId);
|
|
178
|
+
if (!context) return false;
|
|
179
|
+
|
|
180
|
+
const respond = (response: any) => {
|
|
181
|
+
sendResponse({
|
|
182
|
+
...response,
|
|
183
|
+
sandboxId,
|
|
184
|
+
});
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
// Route to providers (async)
|
|
188
|
+
(async () => {
|
|
189
|
+
// 1. Try provider handlers first (for bidirectional comm)
|
|
190
|
+
for (const provider of context.providers) {
|
|
191
|
+
if (provider.handleMessage) {
|
|
192
|
+
await provider.handleMessage(message, respond);
|
|
193
|
+
// Don't stop - let consumers also handle the message
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// 2. Broadcast to consumers (one-way messages or lifecycle events)
|
|
198
|
+
for (const consumer of context.consumers) {
|
|
199
|
+
await consumer.handleMessage(message);
|
|
200
|
+
// Don't stop - let all consumers see the message
|
|
201
|
+
}
|
|
202
|
+
})();
|
|
203
|
+
|
|
204
|
+
return true; // Indicates async response
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
chrome.runtime.onUserScriptMessage.addListener(this.userScriptMessageListener);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Global singleton instance.
|
|
214
|
+
* Import this from wherever you need to interact with the message router.
|
|
215
|
+
*/
|
|
216
|
+
export const RUNTIME_MESSAGE_ROUTER = new RuntimeMessageRouter();
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interface for providing runtime capabilities to sandboxed iframes.
|
|
3
|
+
* Each provider injects data and runtime functions into the sandbox context.
|
|
4
|
+
*/
|
|
5
|
+
export interface SandboxRuntimeProvider {
|
|
6
|
+
/**
|
|
7
|
+
* Returns data to inject into window scope.
|
|
8
|
+
* Keys become window properties (e.g., { attachments: [...] } -> window.attachments)
|
|
9
|
+
*/
|
|
10
|
+
getData(): Record<string, any>;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Returns a runtime function that will be stringified and executed in the sandbox.
|
|
14
|
+
* The function receives sandboxId and has access to data from getData() via window.
|
|
15
|
+
*
|
|
16
|
+
* IMPORTANT: This function will be converted to string via .toString() and injected
|
|
17
|
+
* into the sandbox, so it cannot reference external variables or imports.
|
|
18
|
+
*/
|
|
19
|
+
getRuntime(): (sandboxId: string) => void;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Optional message handler for bidirectional communication.
|
|
23
|
+
* All providers receive all messages - decide internally what to handle.
|
|
24
|
+
*
|
|
25
|
+
* @param message - The message from the sandbox
|
|
26
|
+
* @param respond - Function to send a response back to the sandbox
|
|
27
|
+
*/
|
|
28
|
+
handleMessage?(message: any, respond: (response: any) => void): Promise<void>;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Optional documentation describing what globals/functions this provider injects.
|
|
32
|
+
* This will be appended to tool descriptions dynamically so the LLM knows what's available.
|
|
33
|
+
*/
|
|
34
|
+
getDescription(): string;
|
|
35
|
+
}
|
|
@@ -29,7 +29,7 @@ export class ApiKeyPromptDialog extends DialogBase {
|
|
|
29
29
|
|
|
30
30
|
// Poll for key existence - when key is added, resolve and close
|
|
31
31
|
const checkInterval = setInterval(async () => {
|
|
32
|
-
const hasKey = await getAppStorage().providerKeys.
|
|
32
|
+
const hasKey = !!(await getAppStorage().providerKeys.get(this.provider));
|
|
33
33
|
if (hasKey) {
|
|
34
34
|
clearInterval(checkInterval);
|
|
35
35
|
if (this.resolvePromise) {
|
|
@@ -64,11 +64,8 @@ export class ApiKeyPromptDialog extends DialogBase {
|
|
|
64
64
|
children: html`
|
|
65
65
|
${DialogHeader({
|
|
66
66
|
title: i18n("API Key Required"),
|
|
67
|
-
description: i18n("Enter your API key for {provider}").replace("{provider}", this.provider),
|
|
68
67
|
})}
|
|
69
|
-
<
|
|
70
|
-
<provider-key-input .provider=${this.provider}></provider-key-input>
|
|
71
|
-
</div>
|
|
68
|
+
<provider-key-input .provider=${this.provider}></provider-key-input>
|
|
72
69
|
`,
|
|
73
70
|
})}
|
|
74
71
|
`;
|
|
@@ -101,7 +101,7 @@ export class ModelSelector extends DialogBase {
|
|
|
101
101
|
|
|
102
102
|
// Fetch details for each model and convert to Model format
|
|
103
103
|
const ollamaModelPromises: Promise<Model<any> | null>[] = models
|
|
104
|
-
.map(async (model) => {
|
|
104
|
+
.map(async (model: any) => {
|
|
105
105
|
try {
|
|
106
106
|
// Get model details
|
|
107
107
|
const details = await ollama.show({
|
|
@@ -144,7 +144,7 @@ export class ModelSelector extends DialogBase {
|
|
|
144
144
|
return null;
|
|
145
145
|
}
|
|
146
146
|
})
|
|
147
|
-
.filter((m) => m !== null);
|
|
147
|
+
.filter((m: any) => m !== null);
|
|
148
148
|
|
|
149
149
|
const results = await Promise.all(ollamaModelPromises);
|
|
150
150
|
this.ollamaModels = results.filter((m): m is Model<any> => m !== null);
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { Button, DialogBase, DialogContent, DialogHeader, html } from "@mariozechner/mini-lit";
|
|
2
|
+
import { customElement, state } from "lit/decorators.js";
|
|
3
|
+
import { i18n } from "../utils/i18n.js";
|
|
4
|
+
|
|
5
|
+
@customElement("persistent-storage-dialog")
|
|
6
|
+
export class PersistentStorageDialog extends DialogBase {
|
|
7
|
+
@state() private requesting = false;
|
|
8
|
+
|
|
9
|
+
private resolvePromise?: (userApproved: boolean) => void;
|
|
10
|
+
|
|
11
|
+
protected modalWidth = "min(500px, 90vw)";
|
|
12
|
+
protected modalHeight = "auto";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Request persistent storage permission.
|
|
16
|
+
* Returns true if browser granted persistent storage, false otherwise.
|
|
17
|
+
*/
|
|
18
|
+
static async request(): Promise<boolean> {
|
|
19
|
+
// Check if already persisted
|
|
20
|
+
if (navigator.storage?.persisted) {
|
|
21
|
+
const alreadyPersisted = await navigator.storage.persisted();
|
|
22
|
+
if (alreadyPersisted) {
|
|
23
|
+
console.log("✓ Persistent storage already granted");
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Show dialog and wait for user response
|
|
29
|
+
const dialog = new PersistentStorageDialog();
|
|
30
|
+
dialog.open();
|
|
31
|
+
|
|
32
|
+
const userApproved = await new Promise<boolean>((resolve) => {
|
|
33
|
+
dialog.resolvePromise = resolve;
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
if (!userApproved) {
|
|
37
|
+
console.warn("⚠ User declined persistent storage - sessions may be lost");
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// User approved, request from browser
|
|
42
|
+
if (!navigator.storage?.persist) {
|
|
43
|
+
console.warn("⚠ Persistent storage API not available");
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
const granted = await navigator.storage.persist();
|
|
49
|
+
if (granted) {
|
|
50
|
+
console.log("✓ Persistent storage granted - sessions will be preserved");
|
|
51
|
+
} else {
|
|
52
|
+
console.warn("⚠ Browser denied persistent storage - sessions may be lost under storage pressure");
|
|
53
|
+
}
|
|
54
|
+
return granted;
|
|
55
|
+
} catch (error) {
|
|
56
|
+
console.error("Failed to request persistent storage:", error);
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
private handleGrant() {
|
|
62
|
+
if (this.resolvePromise) {
|
|
63
|
+
this.resolvePromise(true);
|
|
64
|
+
this.resolvePromise = undefined;
|
|
65
|
+
}
|
|
66
|
+
this.close();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private handleDeny() {
|
|
70
|
+
if (this.resolvePromise) {
|
|
71
|
+
this.resolvePromise(false);
|
|
72
|
+
this.resolvePromise = undefined;
|
|
73
|
+
}
|
|
74
|
+
this.close();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
override close() {
|
|
78
|
+
super.close();
|
|
79
|
+
if (this.resolvePromise) {
|
|
80
|
+
this.resolvePromise(false);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
protected override renderContent() {
|
|
85
|
+
return html`
|
|
86
|
+
${DialogContent({
|
|
87
|
+
children: html`
|
|
88
|
+
${DialogHeader({
|
|
89
|
+
title: i18n("Storage Permission Required"),
|
|
90
|
+
description: i18n("This app needs persistent storage to save your conversations"),
|
|
91
|
+
})}
|
|
92
|
+
|
|
93
|
+
<div class="mt-4 flex flex-col gap-4">
|
|
94
|
+
<div class="flex gap-3 p-4 bg-warning/10 border border-warning/20 rounded-lg">
|
|
95
|
+
<div class="flex-shrink-0 text-warning">
|
|
96
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
97
|
+
<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path>
|
|
98
|
+
<line x1="12" y1="9" x2="12" y2="13"></line>
|
|
99
|
+
<line x1="12" y1="17" x2="12.01" y2="17"></line>
|
|
100
|
+
</svg>
|
|
101
|
+
</div>
|
|
102
|
+
<div class="text-sm">
|
|
103
|
+
<p class="font-medium text-foreground mb-1">${i18n("Why is this needed?")}</p>
|
|
104
|
+
<p class="text-muted-foreground">
|
|
105
|
+
${i18n(
|
|
106
|
+
"Without persistent storage, your browser may delete saved conversations when it needs disk space. Granting this permission ensures your chat history is preserved.",
|
|
107
|
+
)}
|
|
108
|
+
</p>
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
|
|
112
|
+
<div class="text-sm text-muted-foreground">
|
|
113
|
+
<p class="mb-2">${i18n("What this means:")}</p>
|
|
114
|
+
<ul class="list-disc list-inside space-y-1 ml-2">
|
|
115
|
+
<li>${i18n("Your conversations will be saved locally in your browser")}</li>
|
|
116
|
+
<li>${i18n("Data will not be deleted automatically to free up space")}</li>
|
|
117
|
+
<li>${i18n("You can still manually clear data at any time")}</li>
|
|
118
|
+
<li>${i18n("No data is sent to external servers")}</li>
|
|
119
|
+
</ul>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
<div class="mt-6 flex gap-3 justify-end">
|
|
124
|
+
${Button({
|
|
125
|
+
variant: "outline",
|
|
126
|
+
onClick: () => this.handleDeny(),
|
|
127
|
+
disabled: this.requesting,
|
|
128
|
+
children: i18n("Continue Anyway"),
|
|
129
|
+
})}
|
|
130
|
+
${Button({
|
|
131
|
+
variant: "default",
|
|
132
|
+
onClick: () => this.handleGrant(),
|
|
133
|
+
disabled: this.requesting,
|
|
134
|
+
children: this.requesting ? i18n("Requesting...") : i18n("Grant Permission"),
|
|
135
|
+
})}
|
|
136
|
+
</div>
|
|
137
|
+
`,
|
|
138
|
+
})}
|
|
139
|
+
`;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { DialogBase, DialogContent, DialogHeader, html } from "@mariozechner/mini-lit";
|
|
2
|
+
import { customElement, state } from "lit/decorators.js";
|
|
3
|
+
import { getAppStorage } from "../storage/app-storage.js";
|
|
4
|
+
import type { SessionMetadata } from "../storage/types.js";
|
|
5
|
+
import { formatUsage } from "../utils/format.js";
|
|
6
|
+
import { i18n } from "../utils/i18n.js";
|
|
7
|
+
|
|
8
|
+
@customElement("session-list-dialog")
|
|
9
|
+
export class SessionListDialog extends DialogBase {
|
|
10
|
+
@state() private sessions: SessionMetadata[] = [];
|
|
11
|
+
@state() private loading = true;
|
|
12
|
+
|
|
13
|
+
private onSelectCallback?: (sessionId: string) => void;
|
|
14
|
+
private onDeleteCallback?: (sessionId: string) => void;
|
|
15
|
+
private deletedSessions = new Set<string>();
|
|
16
|
+
private closedViaSelection = false;
|
|
17
|
+
|
|
18
|
+
protected modalWidth = "min(600px, 90vw)";
|
|
19
|
+
protected modalHeight = "min(700px, 90vh)";
|
|
20
|
+
|
|
21
|
+
static async open(onSelect: (sessionId: string) => void, onDelete?: (sessionId: string) => void) {
|
|
22
|
+
const dialog = new SessionListDialog();
|
|
23
|
+
dialog.onSelectCallback = onSelect;
|
|
24
|
+
dialog.onDeleteCallback = onDelete;
|
|
25
|
+
dialog.open();
|
|
26
|
+
await dialog.loadSessions();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
private async loadSessions() {
|
|
30
|
+
this.loading = true;
|
|
31
|
+
try {
|
|
32
|
+
const storage = getAppStorage();
|
|
33
|
+
this.sessions = await storage.sessions.getAllMetadata();
|
|
34
|
+
} catch (err) {
|
|
35
|
+
console.error("Failed to load sessions:", err);
|
|
36
|
+
this.sessions = [];
|
|
37
|
+
} finally {
|
|
38
|
+
this.loading = false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private async handleDelete(sessionId: string, event: Event) {
|
|
43
|
+
event.stopPropagation();
|
|
44
|
+
|
|
45
|
+
if (!confirm(i18n("Delete this session?"))) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
const storage = getAppStorage();
|
|
51
|
+
if (!storage.sessions) return;
|
|
52
|
+
|
|
53
|
+
await storage.sessions.deleteSession(sessionId);
|
|
54
|
+
await this.loadSessions();
|
|
55
|
+
|
|
56
|
+
// Track deleted session
|
|
57
|
+
this.deletedSessions.add(sessionId);
|
|
58
|
+
} catch (err) {
|
|
59
|
+
console.error("Failed to delete session:", err);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
override close() {
|
|
64
|
+
super.close();
|
|
65
|
+
|
|
66
|
+
// Only notify about deleted sessions if dialog wasn't closed via selection
|
|
67
|
+
if (!this.closedViaSelection && this.onDeleteCallback && this.deletedSessions.size > 0) {
|
|
68
|
+
for (const sessionId of this.deletedSessions) {
|
|
69
|
+
this.onDeleteCallback(sessionId);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
private handleSelect(sessionId: string) {
|
|
75
|
+
this.closedViaSelection = true;
|
|
76
|
+
if (this.onSelectCallback) {
|
|
77
|
+
this.onSelectCallback(sessionId);
|
|
78
|
+
}
|
|
79
|
+
this.close();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
private formatDate(isoString: string): string {
|
|
83
|
+
const date = new Date(isoString);
|
|
84
|
+
const now = new Date();
|
|
85
|
+
const diff = now.getTime() - date.getTime();
|
|
86
|
+
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
|
|
87
|
+
|
|
88
|
+
if (days === 0) {
|
|
89
|
+
return i18n("Today");
|
|
90
|
+
} else if (days === 1) {
|
|
91
|
+
return i18n("Yesterday");
|
|
92
|
+
} else if (days < 7) {
|
|
93
|
+
return i18n("{days} days ago").replace("{days}", days.toString());
|
|
94
|
+
} else {
|
|
95
|
+
return date.toLocaleDateString();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
protected override renderContent() {
|
|
100
|
+
return html`
|
|
101
|
+
${DialogContent({
|
|
102
|
+
className: "h-full flex flex-col",
|
|
103
|
+
children: html`
|
|
104
|
+
${DialogHeader({
|
|
105
|
+
title: i18n("Sessions"),
|
|
106
|
+
description: i18n("Load a previous conversation"),
|
|
107
|
+
})}
|
|
108
|
+
|
|
109
|
+
<div class="flex-1 overflow-y-auto mt-4 space-y-2">
|
|
110
|
+
${
|
|
111
|
+
this.loading
|
|
112
|
+
? html`<div class="text-center py-8 text-muted-foreground">${i18n("Loading...")}</div>`
|
|
113
|
+
: this.sessions.length === 0
|
|
114
|
+
? html`<div class="text-center py-8 text-muted-foreground">${i18n("No sessions yet")}</div>`
|
|
115
|
+
: this.sessions.map(
|
|
116
|
+
(session) => html`
|
|
117
|
+
<div
|
|
118
|
+
class="group flex items-start gap-3 p-3 rounded-lg border border-border hover:bg-secondary/50 cursor-pointer transition-colors"
|
|
119
|
+
@click=${() => this.handleSelect(session.id)}
|
|
120
|
+
>
|
|
121
|
+
<div class="flex-1 min-w-0">
|
|
122
|
+
<div class="font-medium text-sm text-foreground truncate">${session.title}</div>
|
|
123
|
+
<div class="text-xs text-muted-foreground mt-1">${this.formatDate(session.lastModified)}</div>
|
|
124
|
+
<div class="text-xs text-muted-foreground mt-1">
|
|
125
|
+
${session.messageCount} ${i18n("messages")} · ${formatUsage(session.usage)}
|
|
126
|
+
</div>
|
|
127
|
+
</div>
|
|
128
|
+
<button
|
|
129
|
+
class="opacity-0 group-hover:opacity-100 p-1 rounded hover:bg-destructive/10 text-destructive transition-opacity"
|
|
130
|
+
@click=${(e: Event) => this.handleDelete(session.id, e)}
|
|
131
|
+
title=${i18n("Delete")}
|
|
132
|
+
>
|
|
133
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
134
|
+
<path d="M3 6h18"></path>
|
|
135
|
+
<path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"></path>
|
|
136
|
+
<path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"></path>
|
|
137
|
+
</svg>
|
|
138
|
+
</button>
|
|
139
|
+
</div>
|
|
140
|
+
`,
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
</div>
|
|
144
|
+
`,
|
|
145
|
+
})}
|
|
146
|
+
`;
|
|
147
|
+
}
|
|
148
|
+
}
|