@mariozechner/pi-web-ui 0.5.44
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 +252 -0
- package/dist/ChatPanel.d.ts +23 -0
- package/dist/ChatPanel.d.ts.map +1 -0
- package/dist/ChatPanel.js +224 -0
- package/dist/ChatPanel.js.map +1 -0
- package/dist/app.css +2 -0
- package/dist/components/AgentInterface.d.ts +35 -0
- package/dist/components/AgentInterface.d.ts.map +1 -0
- package/dist/components/AgentInterface.js +308 -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 +114 -0
- package/dist/components/AttachmentTile.js.map +1 -0
- package/dist/components/ConsoleBlock.d.ts +11 -0
- package/dist/components/ConsoleBlock.d.ts.map +1 -0
- package/dist/components/ConsoleBlock.js +77 -0
- package/dist/components/ConsoleBlock.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 +56 -0
- package/dist/components/Input.js.map +1 -0
- package/dist/components/MessageEditor.d.ts +38 -0
- package/dist/components/MessageEditor.d.ts.map +1 -0
- package/dist/components/MessageEditor.js +296 -0
- package/dist/components/MessageEditor.js.map +1 -0
- package/dist/components/MessageList.d.ts +13 -0
- package/dist/components/MessageList.d.ts.map +1 -0
- package/dist/components/MessageList.js +88 -0
- package/dist/components/MessageList.js.map +1 -0
- package/dist/components/Messages.d.ts +53 -0
- package/dist/components/Messages.d.ts.map +1 -0
- package/dist/components/Messages.js +323 -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 +183 -0
- package/dist/components/ProviderKeyInput.js.map +1 -0
- package/dist/components/SandboxedIframe.d.ts +63 -0
- package/dist/components/SandboxedIframe.d.ts.map +1 -0
- package/dist/components/SandboxedIframe.js +435 -0
- package/dist/components/SandboxedIframe.js.map +1 -0
- package/dist/components/StreamingMessageContainer.d.ts +17 -0
- package/dist/components/StreamingMessageContainer.d.ts.map +1 -0
- package/dist/components/StreamingMessageContainer.js +114 -0
- package/dist/components/StreamingMessageContainer.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 +80 -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 +575 -0
- package/dist/dialogs/AttachmentOverlay.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 +334 -0
- package/dist/dialogs/ModelSelector.js.map +1 -0
- package/dist/dialogs/SettingsDialog.d.ts +31 -0
- package/dist/dialogs/SettingsDialog.d.ts.map +1 -0
- package/dist/dialogs/SettingsDialog.js +228 -0
- package/dist/dialogs/SettingsDialog.js.map +1 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +51 -0
- package/dist/index.js.map +1 -0
- package/dist/state/agent-session.d.ts +58 -0
- package/dist/state/agent-session.d.ts.map +1 -0
- package/dist/state/agent-session.js +252 -0
- package/dist/state/agent-session.js.map +1 -0
- package/dist/state/transports/AppTransport.d.ts +13 -0
- package/dist/state/transports/AppTransport.d.ts.map +1 -0
- package/dist/state/transports/AppTransport.js +316 -0
- package/dist/state/transports/AppTransport.js.map +1 -0
- package/dist/state/transports/ProviderTransport.d.ts +12 -0
- package/dist/state/transports/ProviderTransport.d.ts.map +1 -0
- package/dist/state/transports/ProviderTransport.js +44 -0
- package/dist/state/transports/ProviderTransport.js.map +1 -0
- package/dist/state/transports/index.d.ts +4 -0
- package/dist/state/transports/index.d.ts.map +1 -0
- package/dist/state/transports/index.js +4 -0
- package/dist/state/transports/index.js.map +1 -0
- package/dist/state/transports/proxy-types.d.ts +48 -0
- package/dist/state/transports/proxy-types.d.ts.map +1 -0
- package/dist/state/transports/proxy-types.js +2 -0
- package/dist/state/transports/proxy-types.js.map +1 -0
- package/dist/state/transports/types.d.ts +11 -0
- package/dist/state/transports/types.d.ts.map +1 -0
- package/dist/state/transports/types.js +2 -0
- package/dist/state/transports/types.js.map +1 -0
- package/dist/state/types.d.ts +15 -0
- package/dist/state/types.d.ts.map +1 -0
- package/dist/state/types.js +2 -0
- package/dist/state/types.js.map +1 -0
- package/dist/storage/app-storage.d.ts +26 -0
- package/dist/storage/app-storage.d.ts.map +1 -0
- package/dist/storage/app-storage.js +44 -0
- package/dist/storage/app-storage.js.map +1 -0
- package/dist/storage/backends/chrome-storage-backend.d.ts +18 -0
- package/dist/storage/backends/chrome-storage-backend.d.ts.map +1 -0
- package/dist/storage/backends/chrome-storage-backend.js +67 -0
- package/dist/storage/backends/chrome-storage-backend.js.map +1 -0
- package/dist/storage/backends/indexeddb-backend.d.ts +20 -0
- package/dist/storage/backends/indexeddb-backend.d.ts.map +1 -0
- package/dist/storage/backends/indexeddb-backend.js +89 -0
- package/dist/storage/backends/indexeddb-backend.js.map +1 -0
- package/dist/storage/backends/local-storage-backend.d.ts +18 -0
- package/dist/storage/backends/local-storage-backend.d.ts.map +1 -0
- package/dist/storage/backends/local-storage-backend.js +69 -0
- package/dist/storage/backends/local-storage-backend.js.map +1 -0
- package/dist/storage/repositories/provider-keys-repository.d.ts +34 -0
- package/dist/storage/repositories/provider-keys-repository.d.ts.map +1 -0
- package/dist/storage/repositories/provider-keys-repository.js +50 -0
- package/dist/storage/repositories/provider-keys-repository.js.map +1 -0
- package/dist/storage/repositories/settings-repository.d.ts +34 -0
- package/dist/storage/repositories/settings-repository.d.ts.map +1 -0
- package/dist/storage/repositories/settings-repository.js +46 -0
- package/dist/storage/repositories/settings-repository.js.map +1 -0
- package/dist/storage/types.d.ts +43 -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 +10 -0
- package/dist/tools/artifacts/ArtifactElement.d.ts.map +1 -0
- package/dist/tools/artifacts/ArtifactElement.js +12 -0
- package/dist/tools/artifacts/ArtifactElement.js.map +1 -0
- package/dist/tools/artifacts/HtmlArtifact.d.ts +30 -0
- package/dist/tools/artifacts/HtmlArtifact.d.ts.map +1 -0
- package/dist/tools/artifacts/HtmlArtifact.js +217 -0
- package/dist/tools/artifacts/HtmlArtifact.js.map +1 -0
- package/dist/tools/artifacts/MarkdownArtifact.d.ts +20 -0
- package/dist/tools/artifacts/MarkdownArtifact.d.ts.map +1 -0
- package/dist/tools/artifacts/MarkdownArtifact.js +84 -0
- package/dist/tools/artifacts/MarkdownArtifact.js.map +1 -0
- package/dist/tools/artifacts/SvgArtifact.d.ts +19 -0
- package/dist/tools/artifacts/SvgArtifact.d.ts.map +1 -0
- package/dist/tools/artifacts/SvgArtifact.js +80 -0
- package/dist/tools/artifacts/SvgArtifact.js.map +1 -0
- package/dist/tools/artifacts/TextArtifact.d.ts +20 -0
- package/dist/tools/artifacts/TextArtifact.d.ts.map +1 -0
- package/dist/tools/artifacts/TextArtifact.js +147 -0
- package/dist/tools/artifacts/TextArtifact.js.map +1 -0
- package/dist/tools/artifacts/artifacts.d.ts +67 -0
- package/dist/tools/artifacts/artifacts.d.ts.map +1 -0
- package/dist/tools/artifacts/artifacts.js +836 -0
- package/dist/tools/artifacts/artifacts.js.map +1 -0
- package/dist/tools/artifacts/index.d.ts +7 -0
- package/dist/tools/artifacts/index.d.ts.map +1 -0
- package/dist/tools/artifacts/index.js +7 -0
- package/dist/tools/artifacts/index.js.map +1 -0
- package/dist/tools/index.d.ts +14 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +29 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/javascript-repl.d.ts +43 -0
- package/dist/tools/javascript-repl.d.ts.map +1 -0
- package/dist/tools/javascript-repl.js +252 -0
- package/dist/tools/javascript-repl.js.map +1 -0
- package/dist/tools/renderer-registry.d.ts +11 -0
- package/dist/tools/renderer-registry.d.ts.map +1 -0
- package/dist/tools/renderer-registry.js +15 -0
- package/dist/tools/renderer-registry.js.map +1 -0
- package/dist/tools/renderers/BashRenderer.d.ts +12 -0
- package/dist/tools/renderers/BashRenderer.d.ts.map +1 -0
- package/dist/tools/renderers/BashRenderer.js +35 -0
- package/dist/tools/renderers/BashRenderer.js.map +1 -0
- package/dist/tools/renderers/CalculateRenderer.d.ts +12 -0
- package/dist/tools/renderers/CalculateRenderer.d.ts.map +1 -0
- package/dist/tools/renderers/CalculateRenderer.js +38 -0
- package/dist/tools/renderers/CalculateRenderer.js.map +1 -0
- package/dist/tools/renderers/DefaultRenderer.d.ts +8 -0
- package/dist/tools/renderers/DefaultRenderer.d.ts.map +1 -0
- package/dist/tools/renderers/DefaultRenderer.js +31 -0
- package/dist/tools/renderers/DefaultRenderer.js.map +1 -0
- package/dist/tools/renderers/GetCurrentTimeRenderer.d.ts +12 -0
- package/dist/tools/renderers/GetCurrentTimeRenderer.d.ts.map +1 -0
- package/dist/tools/renderers/GetCurrentTimeRenderer.js +30 -0
- package/dist/tools/renderers/GetCurrentTimeRenderer.js.map +1 -0
- package/dist/tools/types.d.ts +7 -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 +111 -0
- package/dist/utils/i18n.d.ts.map +1 -0
- package/dist/utils/i18n.js +224 -0
- package/dist/utils/i18n.js.map +1 -0
- package/dist/utils/test-sessions.d.ts +347 -0
- package/dist/utils/test-sessions.d.ts.map +1 -0
- package/dist/utils/test-sessions.js +2215 -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-lock.json +1965 -0
- package/example/package.json +22 -0
- package/example/src/app.css +1 -0
- package/example/src/main.ts +57 -0
- package/example/src/test-sessions.ts +104 -0
- package/example/tsconfig.json +15 -0
- package/example/vite.config.ts +6 -0
- package/package.json +45 -0
- package/src/ChatPanel.ts +214 -0
- package/src/app.css +44 -0
- package/src/components/AgentInterface.ts +316 -0
- package/src/components/AttachmentTile.ts +112 -0
- package/src/components/ConsoleBlock.ts +67 -0
- package/src/components/Input.ts +112 -0
- package/src/components/MessageEditor.ts +272 -0
- package/src/components/MessageList.ts +82 -0
- package/src/components/Messages.ts +310 -0
- package/src/components/ProviderKeyInput.ts +170 -0
- package/src/components/SandboxedIframe.ts +525 -0
- package/src/components/StreamingMessageContainer.ts +101 -0
- package/src/dialogs/ApiKeyPromptDialog.ts +76 -0
- package/src/dialogs/AttachmentOverlay.ts +635 -0
- package/src/dialogs/ModelSelector.ts +324 -0
- package/src/dialogs/SettingsDialog.ts +223 -0
- package/src/index.ts +63 -0
- package/src/state/agent-session.ts +311 -0
- package/src/state/transports/AppTransport.ts +363 -0
- package/src/state/transports/ProviderTransport.ts +49 -0
- package/src/state/transports/index.ts +3 -0
- package/src/state/transports/proxy-types.ts +15 -0
- package/src/state/transports/types.ts +16 -0
- package/src/state/types.ts +11 -0
- package/src/storage/app-storage.ts +53 -0
- package/src/storage/backends/chrome-storage-backend.ts +82 -0
- package/src/storage/backends/indexeddb-backend.ts +107 -0
- package/src/storage/backends/local-storage-backend.ts +74 -0
- package/src/storage/repositories/provider-keys-repository.ts +55 -0
- package/src/storage/repositories/settings-repository.ts +51 -0
- package/src/storage/types.ts +48 -0
- package/src/tools/artifacts/ArtifactElement.ts +15 -0
- package/src/tools/artifacts/HtmlArtifact.ts +221 -0
- package/src/tools/artifacts/MarkdownArtifact.ts +81 -0
- package/src/tools/artifacts/SvgArtifact.ts +77 -0
- package/src/tools/artifacts/TextArtifact.ts +148 -0
- package/src/tools/artifacts/artifacts.ts +888 -0
- package/src/tools/artifacts/index.ts +6 -0
- package/src/tools/index.ts +35 -0
- package/src/tools/javascript-repl.ts +309 -0
- package/src/tools/renderer-registry.ts +18 -0
- package/src/tools/renderers/BashRenderer.ts +45 -0
- package/src/tools/renderers/CalculateRenderer.ts +49 -0
- package/src/tools/renderers/DefaultRenderer.ts +36 -0
- package/src/tools/renderers/GetCurrentTimeRenderer.ts +39 -0
- package/src/tools/types.ts +7 -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 +343 -0
- package/src/utils/test-sessions.ts +2247 -0
- package/tsconfig.build.json +20 -0
- package/tsconfig.json +7 -0
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { Button, html, icon } from "@mariozechner/mini-lit";
|
|
2
|
+
import type { Model } from "@mariozechner/pi-ai";
|
|
3
|
+
import { LitElement } from "lit";
|
|
4
|
+
import { customElement, property, state } from "lit/decorators.js";
|
|
5
|
+
import { createRef, ref } from "lit/directives/ref.js";
|
|
6
|
+
import { Loader2, Paperclip, Send, Sparkles, Square } from "lucide";
|
|
7
|
+
import "./AttachmentTile.js";
|
|
8
|
+
import { type Attachment, loadAttachment } from "../utils/attachment-utils.js";
|
|
9
|
+
import { i18n } from "../utils/i18n.js";
|
|
10
|
+
|
|
11
|
+
@customElement("message-editor")
|
|
12
|
+
export class MessageEditor extends LitElement {
|
|
13
|
+
private _value = "";
|
|
14
|
+
private textareaRef = createRef<HTMLTextAreaElement>();
|
|
15
|
+
|
|
16
|
+
@property()
|
|
17
|
+
get value() {
|
|
18
|
+
return this._value;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
set value(val: string) {
|
|
22
|
+
const oldValue = this._value;
|
|
23
|
+
this._value = val;
|
|
24
|
+
this.requestUpdate("value", oldValue);
|
|
25
|
+
this.updateComplete.then(() => {
|
|
26
|
+
const textarea = this.textareaRef.value;
|
|
27
|
+
if (textarea) {
|
|
28
|
+
textarea.style.height = "auto";
|
|
29
|
+
textarea.style.height = `${Math.min(textarea.scrollHeight, 200)}px`;
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@property() isStreaming = false;
|
|
35
|
+
@property() currentModel?: Model<any>;
|
|
36
|
+
@property() showAttachmentButton = true;
|
|
37
|
+
@property() showModelSelector = true;
|
|
38
|
+
@property() showThinking = false; // Disabled for now
|
|
39
|
+
@property() onInput?: (value: string) => void;
|
|
40
|
+
@property() onSend?: (input: string, attachments: Attachment[]) => void;
|
|
41
|
+
@property() onAbort?: () => void;
|
|
42
|
+
@property() onModelSelect?: () => void;
|
|
43
|
+
@property() onFilesChange?: (files: Attachment[]) => void;
|
|
44
|
+
@property() attachments: Attachment[] = [];
|
|
45
|
+
@property() maxFiles = 10;
|
|
46
|
+
@property() maxFileSize = 20 * 1024 * 1024; // 20MB
|
|
47
|
+
@property() acceptedTypes =
|
|
48
|
+
"image/*,application/pdf,.docx,.pptx,.xlsx,.xls,.txt,.md,.json,.xml,.html,.css,.js,.ts,.jsx,.tsx,.yml,.yaml";
|
|
49
|
+
|
|
50
|
+
@state() processingFiles = false;
|
|
51
|
+
private fileInputRef = createRef<HTMLInputElement>();
|
|
52
|
+
|
|
53
|
+
protected override createRenderRoot(): HTMLElement | DocumentFragment {
|
|
54
|
+
return this;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private handleTextareaInput = (e: Event) => {
|
|
58
|
+
const textarea = e.target as HTMLTextAreaElement;
|
|
59
|
+
this.value = textarea.value;
|
|
60
|
+
textarea.style.height = "auto";
|
|
61
|
+
textarea.style.height = `${Math.min(textarea.scrollHeight, 200)}px`;
|
|
62
|
+
this.onInput?.(this.value);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
private handleKeyDown = (e: KeyboardEvent) => {
|
|
66
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
67
|
+
e.preventDefault();
|
|
68
|
+
if (!this.isStreaming && !this.processingFiles && (this.value.trim() || this.attachments.length > 0)) {
|
|
69
|
+
this.handleSend();
|
|
70
|
+
}
|
|
71
|
+
} else if (e.key === "Escape" && this.isStreaming) {
|
|
72
|
+
e.preventDefault();
|
|
73
|
+
this.onAbort?.();
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
private handleSend = () => {
|
|
78
|
+
this.onSend?.(this.value, this.attachments);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
private handleAttachmentClick = () => {
|
|
82
|
+
this.fileInputRef.value?.click();
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
private async handleFilesSelected(e: Event) {
|
|
86
|
+
const input = e.target as HTMLInputElement;
|
|
87
|
+
const files = Array.from(input.files || []);
|
|
88
|
+
if (files.length === 0) return;
|
|
89
|
+
|
|
90
|
+
if (files.length + this.attachments.length > this.maxFiles) {
|
|
91
|
+
alert(`Maximum ${this.maxFiles} files allowed`);
|
|
92
|
+
input.value = "";
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
this.processingFiles = true;
|
|
97
|
+
const newAttachments: Attachment[] = [];
|
|
98
|
+
|
|
99
|
+
for (const file of files) {
|
|
100
|
+
try {
|
|
101
|
+
if (file.size > this.maxFileSize) {
|
|
102
|
+
alert(`${file.name} exceeds maximum size of ${Math.round(this.maxFileSize / 1024 / 1024)}MB`);
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const attachment = await loadAttachment(file);
|
|
107
|
+
newAttachments.push(attachment);
|
|
108
|
+
} catch (error) {
|
|
109
|
+
console.error(`Error processing ${file.name}:`, error);
|
|
110
|
+
alert(`Failed to process ${file.name}: ${String(error)}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
this.attachments = [...this.attachments, ...newAttachments];
|
|
115
|
+
this.onFilesChange?.(this.attachments);
|
|
116
|
+
this.processingFiles = false;
|
|
117
|
+
input.value = ""; // Reset input
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private removeFile(fileId: string) {
|
|
121
|
+
this.attachments = this.attachments.filter((f) => f.id !== fileId);
|
|
122
|
+
this.onFilesChange?.(this.attachments);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
private adjustTextareaHeight() {
|
|
126
|
+
const textarea = this.textareaRef.value;
|
|
127
|
+
if (textarea) {
|
|
128
|
+
// Reset height to auto to get accurate scrollHeight
|
|
129
|
+
textarea.style.height = "auto";
|
|
130
|
+
// Only adjust if there's content, otherwise keep minimal height
|
|
131
|
+
if (this.value.trim()) {
|
|
132
|
+
textarea.style.height = `${Math.min(textarea.scrollHeight, 200)}px`;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
override firstUpdated() {
|
|
138
|
+
const textarea = this.textareaRef.value;
|
|
139
|
+
if (textarea) {
|
|
140
|
+
// Don't adjust height on first render - let it be minimal
|
|
141
|
+
textarea.focus();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
override updated() {
|
|
146
|
+
// Only adjust height when component updates if there's content
|
|
147
|
+
if (this.value) {
|
|
148
|
+
this.adjustTextareaHeight();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
override render() {
|
|
153
|
+
return html`
|
|
154
|
+
<div class="bg-card rounded-xl border border-border shadow-sm">
|
|
155
|
+
<!-- Attachments -->
|
|
156
|
+
${
|
|
157
|
+
this.attachments.length > 0
|
|
158
|
+
? html`
|
|
159
|
+
<div class="px-4 pt-3 pb-2 flex flex-wrap gap-2">
|
|
160
|
+
${this.attachments.map(
|
|
161
|
+
(attachment) => html`
|
|
162
|
+
<attachment-tile
|
|
163
|
+
.attachment=${attachment}
|
|
164
|
+
.showDelete=${true}
|
|
165
|
+
.onDelete=${() => this.removeFile(attachment.id)}
|
|
166
|
+
></attachment-tile>
|
|
167
|
+
`,
|
|
168
|
+
)}
|
|
169
|
+
</div>
|
|
170
|
+
`
|
|
171
|
+
: ""
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
<textarea
|
|
175
|
+
class="w-full bg-transparent p-4 text-foreground placeholder-muted-foreground outline-none resize-none overflow-y-auto"
|
|
176
|
+
placeholder=${i18n("Type a message...")}
|
|
177
|
+
rows="1"
|
|
178
|
+
style="max-height: 200px;"
|
|
179
|
+
.value=${this.value}
|
|
180
|
+
@input=${this.handleTextareaInput}
|
|
181
|
+
@keydown=${this.handleKeyDown}
|
|
182
|
+
${ref(this.textareaRef)}
|
|
183
|
+
></textarea>
|
|
184
|
+
|
|
185
|
+
<!-- Hidden file input -->
|
|
186
|
+
<input
|
|
187
|
+
type="file"
|
|
188
|
+
${ref(this.fileInputRef)}
|
|
189
|
+
@change=${this.handleFilesSelected}
|
|
190
|
+
accept=${this.acceptedTypes}
|
|
191
|
+
multiple
|
|
192
|
+
style="display: none;"
|
|
193
|
+
/>
|
|
194
|
+
|
|
195
|
+
<!-- Button Row -->
|
|
196
|
+
<div class="px-2 pb-2 flex items-center justify-between">
|
|
197
|
+
<!-- Left side - attachment and quick action buttons -->
|
|
198
|
+
<div class="flex gap-2 items-center">
|
|
199
|
+
${
|
|
200
|
+
this.showAttachmentButton
|
|
201
|
+
? this.processingFiles
|
|
202
|
+
? html`
|
|
203
|
+
<div class="h-8 w-8 flex items-center justify-center">
|
|
204
|
+
${icon(Loader2, "sm", "animate-spin text-muted-foreground")}
|
|
205
|
+
</div>
|
|
206
|
+
`
|
|
207
|
+
: html`
|
|
208
|
+
${Button({
|
|
209
|
+
variant: "ghost",
|
|
210
|
+
size: "icon",
|
|
211
|
+
className: "h-8 w-8",
|
|
212
|
+
onClick: this.handleAttachmentClick,
|
|
213
|
+
children: icon(Paperclip, "sm"),
|
|
214
|
+
})}
|
|
215
|
+
`
|
|
216
|
+
: ""
|
|
217
|
+
}
|
|
218
|
+
</div>
|
|
219
|
+
|
|
220
|
+
<!-- Model selector and send on the right -->
|
|
221
|
+
<div class="flex gap-2 items-center">
|
|
222
|
+
${
|
|
223
|
+
this.showModelSelector && this.currentModel
|
|
224
|
+
? html`
|
|
225
|
+
${Button({
|
|
226
|
+
variant: "ghost",
|
|
227
|
+
size: "sm",
|
|
228
|
+
onClick: () => {
|
|
229
|
+
// Focus textarea before opening model selector so focus returns there
|
|
230
|
+
this.textareaRef.value?.focus();
|
|
231
|
+
// Wait for next frame to ensure focus takes effect before dialog captures it
|
|
232
|
+
requestAnimationFrame(() => {
|
|
233
|
+
this.onModelSelect?.();
|
|
234
|
+
});
|
|
235
|
+
},
|
|
236
|
+
children: html`
|
|
237
|
+
${icon(Sparkles, "sm")}
|
|
238
|
+
<span class="ml-1">${this.currentModel.id}</span>
|
|
239
|
+
`,
|
|
240
|
+
className: "h-8 text-xs truncate",
|
|
241
|
+
})}
|
|
242
|
+
`
|
|
243
|
+
: ""
|
|
244
|
+
}
|
|
245
|
+
${
|
|
246
|
+
this.isStreaming
|
|
247
|
+
? html`
|
|
248
|
+
${Button({
|
|
249
|
+
variant: "ghost",
|
|
250
|
+
size: "icon",
|
|
251
|
+
onClick: this.onAbort,
|
|
252
|
+
children: icon(Square, "sm"),
|
|
253
|
+
className: "h-8 w-8",
|
|
254
|
+
})}
|
|
255
|
+
`
|
|
256
|
+
: html`
|
|
257
|
+
${Button({
|
|
258
|
+
variant: "ghost",
|
|
259
|
+
size: "icon",
|
|
260
|
+
onClick: this.handleSend,
|
|
261
|
+
disabled: (!this.value.trim() && this.attachments.length === 0) || this.processingFiles,
|
|
262
|
+
children: html`<div style="transform: rotate(-45deg)">${icon(Send, "sm")}</div>`,
|
|
263
|
+
className: "h-8 w-8",
|
|
264
|
+
})}
|
|
265
|
+
`
|
|
266
|
+
}
|
|
267
|
+
</div>
|
|
268
|
+
</div>
|
|
269
|
+
</div>
|
|
270
|
+
`;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { html } from "@mariozechner/mini-lit";
|
|
2
|
+
import type {
|
|
3
|
+
AgentTool,
|
|
4
|
+
AssistantMessage as AssistantMessageType,
|
|
5
|
+
Message,
|
|
6
|
+
ToolResultMessage as ToolResultMessageType,
|
|
7
|
+
} from "@mariozechner/pi-ai";
|
|
8
|
+
import { LitElement, type TemplateResult } from "lit";
|
|
9
|
+
import { property } from "lit/decorators.js";
|
|
10
|
+
import { repeat } from "lit/directives/repeat.js";
|
|
11
|
+
|
|
12
|
+
export class MessageList extends LitElement {
|
|
13
|
+
@property({ type: Array }) messages: Message[] = [];
|
|
14
|
+
@property({ type: Array }) tools: AgentTool[] = [];
|
|
15
|
+
@property({ type: Object }) pendingToolCalls?: Set<string>;
|
|
16
|
+
@property({ type: Boolean }) isStreaming: boolean = false;
|
|
17
|
+
|
|
18
|
+
protected override createRenderRoot(): HTMLElement | DocumentFragment {
|
|
19
|
+
return this;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
override connectedCallback(): void {
|
|
23
|
+
super.connectedCallback();
|
|
24
|
+
this.style.display = "block";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
private buildRenderItems() {
|
|
28
|
+
// Map tool results by call id for quick lookup
|
|
29
|
+
const resultByCallId = new Map<string, ToolResultMessageType>();
|
|
30
|
+
for (const message of this.messages) {
|
|
31
|
+
if (message.role === "toolResult") {
|
|
32
|
+
resultByCallId.set(message.toolCallId, message);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const items: Array<{ key: string; template: TemplateResult }> = [];
|
|
37
|
+
let index = 0;
|
|
38
|
+
for (const msg of this.messages) {
|
|
39
|
+
if (msg.role === "user") {
|
|
40
|
+
items.push({
|
|
41
|
+
key: `msg:${index}`,
|
|
42
|
+
template: html`<user-message .message=${msg}></user-message>`,
|
|
43
|
+
});
|
|
44
|
+
index++;
|
|
45
|
+
} else if (msg.role === "assistant") {
|
|
46
|
+
const amsg = msg as AssistantMessageType;
|
|
47
|
+
items.push({
|
|
48
|
+
key: `msg:${index}`,
|
|
49
|
+
template: html`<assistant-message
|
|
50
|
+
.message=${amsg}
|
|
51
|
+
.tools=${this.tools}
|
|
52
|
+
.isStreaming=${this.isStreaming}
|
|
53
|
+
.pendingToolCalls=${this.pendingToolCalls}
|
|
54
|
+
.toolResultsById=${resultByCallId}
|
|
55
|
+
.hideToolCalls=${false}
|
|
56
|
+
></assistant-message>`,
|
|
57
|
+
});
|
|
58
|
+
index++;
|
|
59
|
+
} else {
|
|
60
|
+
// Skip standalone toolResult messages; they are rendered via paired tool-message above
|
|
61
|
+
// For completeness, other roles are not expected
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return items;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
override render() {
|
|
68
|
+
const items = this.buildRenderItems();
|
|
69
|
+
return html`<div class="flex flex-col gap-3">
|
|
70
|
+
${repeat(
|
|
71
|
+
items,
|
|
72
|
+
(it) => it.key,
|
|
73
|
+
(it) => it.template,
|
|
74
|
+
)}
|
|
75
|
+
</div>`;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Register custom element
|
|
80
|
+
if (!customElements.get("message-list")) {
|
|
81
|
+
customElements.define("message-list", MessageList);
|
|
82
|
+
}
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import { Button, html, icon } from "@mariozechner/mini-lit";
|
|
2
|
+
import type {
|
|
3
|
+
AgentTool,
|
|
4
|
+
AssistantMessage as AssistantMessageType,
|
|
5
|
+
ToolCall,
|
|
6
|
+
ToolResultMessage as ToolResultMessageType,
|
|
7
|
+
UserMessage as UserMessageType,
|
|
8
|
+
} from "@mariozechner/pi-ai";
|
|
9
|
+
import type { AgentToolResult } from "@mariozechner/pi-ai/dist/agent/types.js";
|
|
10
|
+
import { LitElement, type TemplateResult } from "lit";
|
|
11
|
+
import { customElement, property, state } from "lit/decorators.js";
|
|
12
|
+
import { Bug, Loader, Wrench } from "lucide";
|
|
13
|
+
import { renderToolParams, renderToolResult } from "../tools/index.js";
|
|
14
|
+
import type { Attachment } from "../utils/attachment-utils.js";
|
|
15
|
+
import { formatUsage } from "../utils/format.js";
|
|
16
|
+
import { i18n } from "../utils/i18n.js";
|
|
17
|
+
|
|
18
|
+
export type UserMessageWithAttachments = UserMessageType & { attachments?: Attachment[] };
|
|
19
|
+
export type AppMessage = AssistantMessageType | UserMessageWithAttachments | ToolResultMessageType;
|
|
20
|
+
|
|
21
|
+
@customElement("user-message")
|
|
22
|
+
export class UserMessage extends LitElement {
|
|
23
|
+
@property({ type: Object }) message!: UserMessageWithAttachments;
|
|
24
|
+
|
|
25
|
+
protected override createRenderRoot(): HTMLElement | DocumentFragment {
|
|
26
|
+
return this;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
override connectedCallback(): void {
|
|
30
|
+
super.connectedCallback();
|
|
31
|
+
this.style.display = "block";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
override render() {
|
|
35
|
+
const content =
|
|
36
|
+
typeof this.message.content === "string"
|
|
37
|
+
? this.message.content
|
|
38
|
+
: this.message.content.find((c) => c.type === "text")?.text || "";
|
|
39
|
+
|
|
40
|
+
return html`
|
|
41
|
+
<div class="py-2 px-4 border-l-4 border-accent-foreground/60 text-primary-foreground">
|
|
42
|
+
<markdown-block .content=${content}></markdown-block>
|
|
43
|
+
${
|
|
44
|
+
this.message.attachments && this.message.attachments.length > 0
|
|
45
|
+
? html`
|
|
46
|
+
<div class="mt-3 flex flex-wrap gap-2">
|
|
47
|
+
${this.message.attachments.map(
|
|
48
|
+
(attachment) => html` <attachment-tile .attachment=${attachment}></attachment-tile> `,
|
|
49
|
+
)}
|
|
50
|
+
</div>
|
|
51
|
+
`
|
|
52
|
+
: ""
|
|
53
|
+
}
|
|
54
|
+
</div>
|
|
55
|
+
`;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@customElement("assistant-message")
|
|
60
|
+
export class AssistantMessage extends LitElement {
|
|
61
|
+
@property({ type: Object }) message!: AssistantMessageType;
|
|
62
|
+
@property({ type: Array }) tools?: AgentTool<any>[];
|
|
63
|
+
@property({ type: Object }) pendingToolCalls?: Set<string>;
|
|
64
|
+
@property({ type: Boolean }) hideToolCalls = false;
|
|
65
|
+
@property({ type: Object }) toolResultsById?: Map<string, ToolResultMessageType>;
|
|
66
|
+
@property({ type: Boolean }) isStreaming: boolean = false;
|
|
67
|
+
|
|
68
|
+
protected override createRenderRoot(): HTMLElement | DocumentFragment {
|
|
69
|
+
return this;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
override connectedCallback(): void {
|
|
73
|
+
super.connectedCallback();
|
|
74
|
+
this.style.display = "block";
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
override render() {
|
|
78
|
+
// Render content in the order it appears
|
|
79
|
+
const orderedParts: TemplateResult[] = [];
|
|
80
|
+
|
|
81
|
+
for (const chunk of this.message.content) {
|
|
82
|
+
if (chunk.type === "text" && chunk.text.trim() !== "") {
|
|
83
|
+
orderedParts.push(html`<markdown-block .content=${chunk.text}></markdown-block>`);
|
|
84
|
+
} else if (chunk.type === "thinking" && chunk.thinking.trim() !== "") {
|
|
85
|
+
orderedParts.push(html` <markdown-block .content=${chunk.thinking} .isThinking=${true}></markdown-block> `);
|
|
86
|
+
} else if (chunk.type === "toolCall") {
|
|
87
|
+
if (!this.hideToolCalls) {
|
|
88
|
+
const tool = this.tools?.find((t) => t.name === chunk.name);
|
|
89
|
+
const pending = this.pendingToolCalls?.has(chunk.id) ?? false;
|
|
90
|
+
const result = this.toolResultsById?.get(chunk.id);
|
|
91
|
+
const aborted = !pending && !result && !this.isStreaming;
|
|
92
|
+
orderedParts.push(
|
|
93
|
+
html`<tool-message
|
|
94
|
+
.tool=${tool}
|
|
95
|
+
.toolCall=${chunk}
|
|
96
|
+
.result=${result}
|
|
97
|
+
.pending=${pending}
|
|
98
|
+
.aborted=${aborted}
|
|
99
|
+
.isStreaming=${this.isStreaming}
|
|
100
|
+
></tool-message>`,
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return html`
|
|
107
|
+
<div>
|
|
108
|
+
${orderedParts.length ? html` <div class="px-4 flex flex-col gap-3">${orderedParts}</div> ` : ""}
|
|
109
|
+
${
|
|
110
|
+
this.message.usage
|
|
111
|
+
? html` <div class="px-4 mt-2 text-xs text-muted-foreground">${formatUsage(this.message.usage)}</div> `
|
|
112
|
+
: ""
|
|
113
|
+
}
|
|
114
|
+
${
|
|
115
|
+
this.message.stopReason === "error" && this.message.errorMessage
|
|
116
|
+
? html`
|
|
117
|
+
<div class="mx-4 mt-3 p-3 bg-destructive/10 text-destructive rounded-lg text-sm overflow-hidden">
|
|
118
|
+
<strong>${i18n("Error:")}</strong> ${this.message.errorMessage}
|
|
119
|
+
</div>
|
|
120
|
+
`
|
|
121
|
+
: ""
|
|
122
|
+
}
|
|
123
|
+
${
|
|
124
|
+
this.message.stopReason === "aborted"
|
|
125
|
+
? html`<span class="text-sm text-destructive italic">${i18n("Request aborted")}</span>`
|
|
126
|
+
: ""
|
|
127
|
+
}
|
|
128
|
+
</div>
|
|
129
|
+
`;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
@customElement("tool-message-debug")
|
|
134
|
+
export class ToolMessageDebugView extends LitElement {
|
|
135
|
+
@property({ type: Object }) callArgs: any;
|
|
136
|
+
@property({ type: String }) result?: AgentToolResult<any>;
|
|
137
|
+
@property({ type: Boolean }) hasResult: boolean = false;
|
|
138
|
+
|
|
139
|
+
protected override createRenderRoot(): HTMLElement | DocumentFragment {
|
|
140
|
+
return this; // light DOM for shared styles
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
override connectedCallback(): void {
|
|
144
|
+
super.connectedCallback();
|
|
145
|
+
this.style.display = "block";
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
private pretty(value: unknown): { content: string; isJson: boolean } {
|
|
149
|
+
try {
|
|
150
|
+
if (typeof value === "string") {
|
|
151
|
+
const maybeJson = JSON.parse(value);
|
|
152
|
+
return { content: JSON.stringify(maybeJson, null, 2), isJson: true };
|
|
153
|
+
}
|
|
154
|
+
return { content: JSON.stringify(value, null, 2), isJson: true };
|
|
155
|
+
} catch {
|
|
156
|
+
return { content: typeof value === "string" ? value : String(value), isJson: false };
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
override render() {
|
|
161
|
+
const output = this.pretty(this.result?.output);
|
|
162
|
+
const details = this.pretty(this.result?.details);
|
|
163
|
+
|
|
164
|
+
return html`
|
|
165
|
+
<div class="mt-3 flex flex-col gap-2">
|
|
166
|
+
<div>
|
|
167
|
+
<div class="text-xs font-medium mb-1 text-muted-foreground">${i18n("Call")}</div>
|
|
168
|
+
<code-block .code=${this.pretty(this.callArgs).content} language="json"></code-block>
|
|
169
|
+
</div>
|
|
170
|
+
<div>
|
|
171
|
+
<div class="text-xs font-medium mb-1 text-muted-foreground">${i18n("Result")}</div>
|
|
172
|
+
${
|
|
173
|
+
this.hasResult
|
|
174
|
+
? html`<code-block .code=${output.content} language="${output.isJson ? "json" : "text"}"></code-block>
|
|
175
|
+
<code-block .code=${details.content} language="${details.isJson ? "json" : "text"}"></code-block>`
|
|
176
|
+
: html`<div class="text-xs text-muted-foreground">${i18n("(no result)")}</div>`
|
|
177
|
+
}
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
`;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
@customElement("tool-message")
|
|
185
|
+
export class ToolMessage extends LitElement {
|
|
186
|
+
@property({ type: Object }) toolCall!: ToolCall;
|
|
187
|
+
@property({ type: Object }) tool?: AgentTool<any>;
|
|
188
|
+
@property({ type: Object }) result?: ToolResultMessageType;
|
|
189
|
+
@property({ type: Boolean }) pending: boolean = false;
|
|
190
|
+
@property({ type: Boolean }) aborted: boolean = false;
|
|
191
|
+
@property({ type: Boolean }) isStreaming: boolean = false;
|
|
192
|
+
@state() private _showDebug = false;
|
|
193
|
+
|
|
194
|
+
protected override createRenderRoot(): HTMLElement | DocumentFragment {
|
|
195
|
+
return this;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
override connectedCallback(): void {
|
|
199
|
+
super.connectedCallback();
|
|
200
|
+
this.style.display = "block";
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
private toggleDebug = () => {
|
|
204
|
+
this._showDebug = !this._showDebug;
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
override render() {
|
|
208
|
+
const toolLabel = this.tool?.label || this.toolCall.name;
|
|
209
|
+
const toolName = this.tool?.name || this.toolCall.name;
|
|
210
|
+
const isError = this.result?.isError === true;
|
|
211
|
+
const hasResult = !!this.result;
|
|
212
|
+
|
|
213
|
+
let statusIcon: TemplateResult;
|
|
214
|
+
if (this.pending || (this.isStreaming && !hasResult)) {
|
|
215
|
+
statusIcon = html`<span class="inline-block text-muted-foreground animate-spin">${icon(Loader, "sm")}</span>`;
|
|
216
|
+
} else if (this.aborted && !hasResult) {
|
|
217
|
+
statusIcon = html`<span class="inline-block text-destructive">${icon(Wrench, "sm")}</span>`;
|
|
218
|
+
} else if (hasResult && isError) {
|
|
219
|
+
statusIcon = html`<span class="inline-block text-destructive">${icon(Wrench, "sm")}</span>`;
|
|
220
|
+
} else if (hasResult) {
|
|
221
|
+
statusIcon = html`<span class="inline-block text-muted-foreground">${icon(Wrench, "sm")}</span>`;
|
|
222
|
+
} else {
|
|
223
|
+
statusIcon = html`<span class="inline-block text-muted-foreground">${icon(Wrench, "sm")}</span>`;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Normalize error text
|
|
227
|
+
let errorMessage = this.result?.output || "";
|
|
228
|
+
if (isError) {
|
|
229
|
+
try {
|
|
230
|
+
const parsed = JSON.parse(errorMessage);
|
|
231
|
+
if ((parsed as any).error) errorMessage = (parsed as any).error;
|
|
232
|
+
else if ((parsed as any).message) errorMessage = (parsed as any).message;
|
|
233
|
+
} catch {}
|
|
234
|
+
errorMessage = errorMessage.replace(/^(Tool )?Error:\s*/i, "");
|
|
235
|
+
errorMessage = errorMessage.replace(/^Error:\s*/i, "");
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const paramsTpl = renderToolParams(
|
|
239
|
+
toolName,
|
|
240
|
+
this.toolCall.arguments,
|
|
241
|
+
this.isStreaming || (this.pending && !hasResult),
|
|
242
|
+
);
|
|
243
|
+
const resultTpl =
|
|
244
|
+
hasResult && !isError ? renderToolResult(toolName, this.toolCall.arguments, this.result!) : undefined;
|
|
245
|
+
|
|
246
|
+
return html`
|
|
247
|
+
<div class="p-2.5 border border-border rounded-md bg-card text-card-foreground">
|
|
248
|
+
<div class="flex items-center justify-between text-xs text-muted-foreground">
|
|
249
|
+
<div class="flex items-center gap-2">
|
|
250
|
+
${statusIcon}
|
|
251
|
+
<span class="font-medium">${toolLabel}</span>
|
|
252
|
+
</div>
|
|
253
|
+
${Button({
|
|
254
|
+
variant: this._showDebug ? "default" : "ghost",
|
|
255
|
+
size: "sm",
|
|
256
|
+
onClick: this.toggleDebug,
|
|
257
|
+
children: icon(Bug, "sm"),
|
|
258
|
+
className: "text-muted-foreground",
|
|
259
|
+
})}
|
|
260
|
+
</div>
|
|
261
|
+
|
|
262
|
+
${
|
|
263
|
+
this._showDebug
|
|
264
|
+
? html`<tool-message-debug
|
|
265
|
+
.callArgs=${this.toolCall.arguments}
|
|
266
|
+
.result=${this.result}
|
|
267
|
+
.hasResult=${!!this.result}
|
|
268
|
+
></tool-message-debug>`
|
|
269
|
+
: html`
|
|
270
|
+
<div class="mt-2 text-sm text-muted-foreground">${paramsTpl}</div>
|
|
271
|
+
${
|
|
272
|
+
this.pending && !hasResult
|
|
273
|
+
? html`<div class="mt-2 text-sm text-muted-foreground">${i18n("Waiting for tool result…")}</div>`
|
|
274
|
+
: ""
|
|
275
|
+
}
|
|
276
|
+
${
|
|
277
|
+
this.aborted && !hasResult
|
|
278
|
+
? html`<div class="mt-2 text-sm text-muted-foreground">${i18n("Call was aborted; no result.")}</div>`
|
|
279
|
+
: ""
|
|
280
|
+
}
|
|
281
|
+
${
|
|
282
|
+
hasResult && isError
|
|
283
|
+
? html`<div class="mt-2 p-2 border border-destructive rounded bg-destructive/10 text-sm text-destructive">
|
|
284
|
+
${errorMessage}
|
|
285
|
+
</div>`
|
|
286
|
+
: ""
|
|
287
|
+
}
|
|
288
|
+
${resultTpl ? html`<div class="mt-2">${resultTpl}</div>` : ""}
|
|
289
|
+
`
|
|
290
|
+
}
|
|
291
|
+
</div>
|
|
292
|
+
`;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
@customElement("aborted-message")
|
|
297
|
+
export class AbortedMessage extends LitElement {
|
|
298
|
+
protected override createRenderRoot(): HTMLElement | DocumentFragment {
|
|
299
|
+
return this;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
override connectedCallback(): void {
|
|
303
|
+
super.connectedCallback();
|
|
304
|
+
this.style.display = "block";
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
protected override render(): unknown {
|
|
308
|
+
return html`<span class="text-sm text-destructive italic">${i18n("Request aborted")}</span>`;
|
|
309
|
+
}
|
|
310
|
+
}
|