@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.
Files changed (265) hide show
  1. package/README.md +252 -0
  2. package/dist/ChatPanel.d.ts +23 -0
  3. package/dist/ChatPanel.d.ts.map +1 -0
  4. package/dist/ChatPanel.js +224 -0
  5. package/dist/ChatPanel.js.map +1 -0
  6. package/dist/app.css +2 -0
  7. package/dist/components/AgentInterface.d.ts +35 -0
  8. package/dist/components/AgentInterface.d.ts.map +1 -0
  9. package/dist/components/AgentInterface.js +308 -0
  10. package/dist/components/AgentInterface.js.map +1 -0
  11. package/dist/components/AttachmentTile.d.ts +12 -0
  12. package/dist/components/AttachmentTile.d.ts.map +1 -0
  13. package/dist/components/AttachmentTile.js +114 -0
  14. package/dist/components/AttachmentTile.js.map +1 -0
  15. package/dist/components/ConsoleBlock.d.ts +11 -0
  16. package/dist/components/ConsoleBlock.d.ts.map +1 -0
  17. package/dist/components/ConsoleBlock.js +77 -0
  18. package/dist/components/ConsoleBlock.js.map +1 -0
  19. package/dist/components/Input.d.ts +26 -0
  20. package/dist/components/Input.d.ts.map +1 -0
  21. package/dist/components/Input.js +56 -0
  22. package/dist/components/Input.js.map +1 -0
  23. package/dist/components/MessageEditor.d.ts +38 -0
  24. package/dist/components/MessageEditor.d.ts.map +1 -0
  25. package/dist/components/MessageEditor.js +296 -0
  26. package/dist/components/MessageEditor.js.map +1 -0
  27. package/dist/components/MessageList.d.ts +13 -0
  28. package/dist/components/MessageList.d.ts.map +1 -0
  29. package/dist/components/MessageList.js +88 -0
  30. package/dist/components/MessageList.js.map +1 -0
  31. package/dist/components/Messages.d.ts +53 -0
  32. package/dist/components/Messages.d.ts.map +1 -0
  33. package/dist/components/Messages.js +323 -0
  34. package/dist/components/Messages.js.map +1 -0
  35. package/dist/components/ProviderKeyInput.d.ts +16 -0
  36. package/dist/components/ProviderKeyInput.d.ts.map +1 -0
  37. package/dist/components/ProviderKeyInput.js +183 -0
  38. package/dist/components/ProviderKeyInput.js.map +1 -0
  39. package/dist/components/SandboxedIframe.d.ts +63 -0
  40. package/dist/components/SandboxedIframe.d.ts.map +1 -0
  41. package/dist/components/SandboxedIframe.js +435 -0
  42. package/dist/components/SandboxedIframe.js.map +1 -0
  43. package/dist/components/StreamingMessageContainer.d.ts +17 -0
  44. package/dist/components/StreamingMessageContainer.d.ts.map +1 -0
  45. package/dist/components/StreamingMessageContainer.js +114 -0
  46. package/dist/components/StreamingMessageContainer.js.map +1 -0
  47. package/dist/dialogs/ApiKeyPromptDialog.d.ts +15 -0
  48. package/dist/dialogs/ApiKeyPromptDialog.d.ts.map +1 -0
  49. package/dist/dialogs/ApiKeyPromptDialog.js +80 -0
  50. package/dist/dialogs/ApiKeyPromptDialog.js.map +1 -0
  51. package/dist/dialogs/AttachmentOverlay.d.ts +32 -0
  52. package/dist/dialogs/AttachmentOverlay.d.ts.map +1 -0
  53. package/dist/dialogs/AttachmentOverlay.js +575 -0
  54. package/dist/dialogs/AttachmentOverlay.js.map +1 -0
  55. package/dist/dialogs/ModelSelector.d.ts +27 -0
  56. package/dist/dialogs/ModelSelector.d.ts.map +1 -0
  57. package/dist/dialogs/ModelSelector.js +334 -0
  58. package/dist/dialogs/ModelSelector.js.map +1 -0
  59. package/dist/dialogs/SettingsDialog.d.ts +31 -0
  60. package/dist/dialogs/SettingsDialog.d.ts.map +1 -0
  61. package/dist/dialogs/SettingsDialog.js +228 -0
  62. package/dist/dialogs/SettingsDialog.js.map +1 -0
  63. package/dist/index.d.ts +46 -0
  64. package/dist/index.d.ts.map +1 -0
  65. package/dist/index.js +51 -0
  66. package/dist/index.js.map +1 -0
  67. package/dist/state/agent-session.d.ts +58 -0
  68. package/dist/state/agent-session.d.ts.map +1 -0
  69. package/dist/state/agent-session.js +252 -0
  70. package/dist/state/agent-session.js.map +1 -0
  71. package/dist/state/transports/AppTransport.d.ts +13 -0
  72. package/dist/state/transports/AppTransport.d.ts.map +1 -0
  73. package/dist/state/transports/AppTransport.js +316 -0
  74. package/dist/state/transports/AppTransport.js.map +1 -0
  75. package/dist/state/transports/ProviderTransport.d.ts +12 -0
  76. package/dist/state/transports/ProviderTransport.d.ts.map +1 -0
  77. package/dist/state/transports/ProviderTransport.js +44 -0
  78. package/dist/state/transports/ProviderTransport.js.map +1 -0
  79. package/dist/state/transports/index.d.ts +4 -0
  80. package/dist/state/transports/index.d.ts.map +1 -0
  81. package/dist/state/transports/index.js +4 -0
  82. package/dist/state/transports/index.js.map +1 -0
  83. package/dist/state/transports/proxy-types.d.ts +48 -0
  84. package/dist/state/transports/proxy-types.d.ts.map +1 -0
  85. package/dist/state/transports/proxy-types.js +2 -0
  86. package/dist/state/transports/proxy-types.js.map +1 -0
  87. package/dist/state/transports/types.d.ts +11 -0
  88. package/dist/state/transports/types.d.ts.map +1 -0
  89. package/dist/state/transports/types.js +2 -0
  90. package/dist/state/transports/types.js.map +1 -0
  91. package/dist/state/types.d.ts +15 -0
  92. package/dist/state/types.d.ts.map +1 -0
  93. package/dist/state/types.js +2 -0
  94. package/dist/state/types.js.map +1 -0
  95. package/dist/storage/app-storage.d.ts +26 -0
  96. package/dist/storage/app-storage.d.ts.map +1 -0
  97. package/dist/storage/app-storage.js +44 -0
  98. package/dist/storage/app-storage.js.map +1 -0
  99. package/dist/storage/backends/chrome-storage-backend.d.ts +18 -0
  100. package/dist/storage/backends/chrome-storage-backend.d.ts.map +1 -0
  101. package/dist/storage/backends/chrome-storage-backend.js +67 -0
  102. package/dist/storage/backends/chrome-storage-backend.js.map +1 -0
  103. package/dist/storage/backends/indexeddb-backend.d.ts +20 -0
  104. package/dist/storage/backends/indexeddb-backend.d.ts.map +1 -0
  105. package/dist/storage/backends/indexeddb-backend.js +89 -0
  106. package/dist/storage/backends/indexeddb-backend.js.map +1 -0
  107. package/dist/storage/backends/local-storage-backend.d.ts +18 -0
  108. package/dist/storage/backends/local-storage-backend.d.ts.map +1 -0
  109. package/dist/storage/backends/local-storage-backend.js +69 -0
  110. package/dist/storage/backends/local-storage-backend.js.map +1 -0
  111. package/dist/storage/repositories/provider-keys-repository.d.ts +34 -0
  112. package/dist/storage/repositories/provider-keys-repository.d.ts.map +1 -0
  113. package/dist/storage/repositories/provider-keys-repository.js +50 -0
  114. package/dist/storage/repositories/provider-keys-repository.js.map +1 -0
  115. package/dist/storage/repositories/settings-repository.d.ts +34 -0
  116. package/dist/storage/repositories/settings-repository.d.ts.map +1 -0
  117. package/dist/storage/repositories/settings-repository.js +46 -0
  118. package/dist/storage/repositories/settings-repository.js.map +1 -0
  119. package/dist/storage/types.d.ts +43 -0
  120. package/dist/storage/types.d.ts.map +1 -0
  121. package/dist/storage/types.js +2 -0
  122. package/dist/storage/types.js.map +1 -0
  123. package/dist/tools/artifacts/ArtifactElement.d.ts +10 -0
  124. package/dist/tools/artifacts/ArtifactElement.d.ts.map +1 -0
  125. package/dist/tools/artifacts/ArtifactElement.js +12 -0
  126. package/dist/tools/artifacts/ArtifactElement.js.map +1 -0
  127. package/dist/tools/artifacts/HtmlArtifact.d.ts +30 -0
  128. package/dist/tools/artifacts/HtmlArtifact.d.ts.map +1 -0
  129. package/dist/tools/artifacts/HtmlArtifact.js +217 -0
  130. package/dist/tools/artifacts/HtmlArtifact.js.map +1 -0
  131. package/dist/tools/artifacts/MarkdownArtifact.d.ts +20 -0
  132. package/dist/tools/artifacts/MarkdownArtifact.d.ts.map +1 -0
  133. package/dist/tools/artifacts/MarkdownArtifact.js +84 -0
  134. package/dist/tools/artifacts/MarkdownArtifact.js.map +1 -0
  135. package/dist/tools/artifacts/SvgArtifact.d.ts +19 -0
  136. package/dist/tools/artifacts/SvgArtifact.d.ts.map +1 -0
  137. package/dist/tools/artifacts/SvgArtifact.js +80 -0
  138. package/dist/tools/artifacts/SvgArtifact.js.map +1 -0
  139. package/dist/tools/artifacts/TextArtifact.d.ts +20 -0
  140. package/dist/tools/artifacts/TextArtifact.d.ts.map +1 -0
  141. package/dist/tools/artifacts/TextArtifact.js +147 -0
  142. package/dist/tools/artifacts/TextArtifact.js.map +1 -0
  143. package/dist/tools/artifacts/artifacts.d.ts +67 -0
  144. package/dist/tools/artifacts/artifacts.d.ts.map +1 -0
  145. package/dist/tools/artifacts/artifacts.js +836 -0
  146. package/dist/tools/artifacts/artifacts.js.map +1 -0
  147. package/dist/tools/artifacts/index.d.ts +7 -0
  148. package/dist/tools/artifacts/index.d.ts.map +1 -0
  149. package/dist/tools/artifacts/index.js +7 -0
  150. package/dist/tools/artifacts/index.js.map +1 -0
  151. package/dist/tools/index.d.ts +14 -0
  152. package/dist/tools/index.d.ts.map +1 -0
  153. package/dist/tools/index.js +29 -0
  154. package/dist/tools/index.js.map +1 -0
  155. package/dist/tools/javascript-repl.d.ts +43 -0
  156. package/dist/tools/javascript-repl.d.ts.map +1 -0
  157. package/dist/tools/javascript-repl.js +252 -0
  158. package/dist/tools/javascript-repl.js.map +1 -0
  159. package/dist/tools/renderer-registry.d.ts +11 -0
  160. package/dist/tools/renderer-registry.d.ts.map +1 -0
  161. package/dist/tools/renderer-registry.js +15 -0
  162. package/dist/tools/renderer-registry.js.map +1 -0
  163. package/dist/tools/renderers/BashRenderer.d.ts +12 -0
  164. package/dist/tools/renderers/BashRenderer.d.ts.map +1 -0
  165. package/dist/tools/renderers/BashRenderer.js +35 -0
  166. package/dist/tools/renderers/BashRenderer.js.map +1 -0
  167. package/dist/tools/renderers/CalculateRenderer.d.ts +12 -0
  168. package/dist/tools/renderers/CalculateRenderer.d.ts.map +1 -0
  169. package/dist/tools/renderers/CalculateRenderer.js +38 -0
  170. package/dist/tools/renderers/CalculateRenderer.js.map +1 -0
  171. package/dist/tools/renderers/DefaultRenderer.d.ts +8 -0
  172. package/dist/tools/renderers/DefaultRenderer.d.ts.map +1 -0
  173. package/dist/tools/renderers/DefaultRenderer.js +31 -0
  174. package/dist/tools/renderers/DefaultRenderer.js.map +1 -0
  175. package/dist/tools/renderers/GetCurrentTimeRenderer.d.ts +12 -0
  176. package/dist/tools/renderers/GetCurrentTimeRenderer.d.ts.map +1 -0
  177. package/dist/tools/renderers/GetCurrentTimeRenderer.js +30 -0
  178. package/dist/tools/renderers/GetCurrentTimeRenderer.js.map +1 -0
  179. package/dist/tools/types.d.ts +7 -0
  180. package/dist/tools/types.d.ts.map +1 -0
  181. package/dist/tools/types.js +2 -0
  182. package/dist/tools/types.js.map +1 -0
  183. package/dist/utils/attachment-utils.d.ts +19 -0
  184. package/dist/utils/attachment-utils.d.ts.map +1 -0
  185. package/dist/utils/attachment-utils.js +415 -0
  186. package/dist/utils/attachment-utils.js.map +1 -0
  187. package/dist/utils/auth-token.d.ts +3 -0
  188. package/dist/utils/auth-token.d.ts.map +1 -0
  189. package/dist/utils/auth-token.js +19 -0
  190. package/dist/utils/auth-token.js.map +1 -0
  191. package/dist/utils/format.d.ts +6 -0
  192. package/dist/utils/format.d.ts.map +1 -0
  193. package/dist/utils/format.js +47 -0
  194. package/dist/utils/format.js.map +1 -0
  195. package/dist/utils/i18n.d.ts +111 -0
  196. package/dist/utils/i18n.d.ts.map +1 -0
  197. package/dist/utils/i18n.js +224 -0
  198. package/dist/utils/i18n.js.map +1 -0
  199. package/dist/utils/test-sessions.d.ts +347 -0
  200. package/dist/utils/test-sessions.d.ts.map +1 -0
  201. package/dist/utils/test-sessions.js +2215 -0
  202. package/dist/utils/test-sessions.js.map +1 -0
  203. package/example/README.md +61 -0
  204. package/example/index.html +13 -0
  205. package/example/package-lock.json +1965 -0
  206. package/example/package.json +22 -0
  207. package/example/src/app.css +1 -0
  208. package/example/src/main.ts +57 -0
  209. package/example/src/test-sessions.ts +104 -0
  210. package/example/tsconfig.json +15 -0
  211. package/example/vite.config.ts +6 -0
  212. package/package.json +45 -0
  213. package/src/ChatPanel.ts +214 -0
  214. package/src/app.css +44 -0
  215. package/src/components/AgentInterface.ts +316 -0
  216. package/src/components/AttachmentTile.ts +112 -0
  217. package/src/components/ConsoleBlock.ts +67 -0
  218. package/src/components/Input.ts +112 -0
  219. package/src/components/MessageEditor.ts +272 -0
  220. package/src/components/MessageList.ts +82 -0
  221. package/src/components/Messages.ts +310 -0
  222. package/src/components/ProviderKeyInput.ts +170 -0
  223. package/src/components/SandboxedIframe.ts +525 -0
  224. package/src/components/StreamingMessageContainer.ts +101 -0
  225. package/src/dialogs/ApiKeyPromptDialog.ts +76 -0
  226. package/src/dialogs/AttachmentOverlay.ts +635 -0
  227. package/src/dialogs/ModelSelector.ts +324 -0
  228. package/src/dialogs/SettingsDialog.ts +223 -0
  229. package/src/index.ts +63 -0
  230. package/src/state/agent-session.ts +311 -0
  231. package/src/state/transports/AppTransport.ts +363 -0
  232. package/src/state/transports/ProviderTransport.ts +49 -0
  233. package/src/state/transports/index.ts +3 -0
  234. package/src/state/transports/proxy-types.ts +15 -0
  235. package/src/state/transports/types.ts +16 -0
  236. package/src/state/types.ts +11 -0
  237. package/src/storage/app-storage.ts +53 -0
  238. package/src/storage/backends/chrome-storage-backend.ts +82 -0
  239. package/src/storage/backends/indexeddb-backend.ts +107 -0
  240. package/src/storage/backends/local-storage-backend.ts +74 -0
  241. package/src/storage/repositories/provider-keys-repository.ts +55 -0
  242. package/src/storage/repositories/settings-repository.ts +51 -0
  243. package/src/storage/types.ts +48 -0
  244. package/src/tools/artifacts/ArtifactElement.ts +15 -0
  245. package/src/tools/artifacts/HtmlArtifact.ts +221 -0
  246. package/src/tools/artifacts/MarkdownArtifact.ts +81 -0
  247. package/src/tools/artifacts/SvgArtifact.ts +77 -0
  248. package/src/tools/artifacts/TextArtifact.ts +148 -0
  249. package/src/tools/artifacts/artifacts.ts +888 -0
  250. package/src/tools/artifacts/index.ts +6 -0
  251. package/src/tools/index.ts +35 -0
  252. package/src/tools/javascript-repl.ts +309 -0
  253. package/src/tools/renderer-registry.ts +18 -0
  254. package/src/tools/renderers/BashRenderer.ts +45 -0
  255. package/src/tools/renderers/CalculateRenderer.ts +49 -0
  256. package/src/tools/renderers/DefaultRenderer.ts +36 -0
  257. package/src/tools/renderers/GetCurrentTimeRenderer.ts +39 -0
  258. package/src/tools/types.ts +7 -0
  259. package/src/utils/attachment-utils.ts +472 -0
  260. package/src/utils/auth-token.ts +22 -0
  261. package/src/utils/format.ts +42 -0
  262. package/src/utils/i18n.ts +343 -0
  263. package/src/utils/test-sessions.ts +2247 -0
  264. package/tsconfig.build.json +20 -0
  265. package/tsconfig.json +7 -0
@@ -0,0 +1,836 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { Button, Diff, icon } from "@mariozechner/mini-lit";
8
+ import { StringEnum } from "@mariozechner/pi-ai";
9
+ import { Type } from "@sinclair/typebox";
10
+ import { html, LitElement } from "lit";
11
+ import { customElement, property, state } from "lit/decorators.js";
12
+ import { createRef, ref } from "lit/directives/ref.js";
13
+ import { X } from "lucide";
14
+ import { i18n } from "../../utils/i18n.js";
15
+ import { HtmlArtifact } from "./HtmlArtifact.js";
16
+ import { MarkdownArtifact } from "./MarkdownArtifact.js";
17
+ import { SvgArtifact } from "./SvgArtifact.js";
18
+ import { TextArtifact } from "./TextArtifact.js";
19
+ import "@mariozechner/mini-lit/dist/MarkdownBlock.js";
20
+ import "@mariozechner/mini-lit/dist/CodeBlock.js";
21
+ // JSON-schema friendly parameters object (LLM-facing)
22
+ const artifactsParamsSchema = Type.Object({
23
+ command: StringEnum(["create", "update", "rewrite", "get", "delete", "logs"], {
24
+ description: "The operation to perform",
25
+ }),
26
+ filename: Type.String({ description: "Filename including extension (e.g., 'index.html', 'script.js')" }),
27
+ title: Type.Optional(Type.String({ description: "Display title for the tab (defaults to filename)" })),
28
+ content: Type.Optional(Type.String({ description: "File content" })),
29
+ old_str: Type.Optional(Type.String({ description: "String to replace (for update command)" })),
30
+ new_str: Type.Optional(Type.String({ description: "Replacement string (for update command)" })),
31
+ });
32
+ // Minimal helper to render plain text outputs consistently
33
+ function plainOutput(text) {
34
+ return html `<div class="text-xs text-muted-foreground whitespace-pre-wrap font-mono">${text}</div>`;
35
+ }
36
+ let ArtifactsPanel = class ArtifactsPanel extends LitElement {
37
+ constructor() {
38
+ super(...arguments);
39
+ this._artifacts = new Map();
40
+ this._activeFilename = null;
41
+ // Programmatically managed artifact elements
42
+ this.artifactElements = new Map();
43
+ this.contentRef = createRef();
44
+ // Collapsed mode: hides panel content but can show a floating reopen pill
45
+ this.collapsed = false;
46
+ // Overlay mode: when true, panel renders full-screen overlay (mobile)
47
+ this.overlay = false;
48
+ }
49
+ // Public getter for artifacts
50
+ get artifacts() {
51
+ return this._artifacts;
52
+ }
53
+ createRenderRoot() {
54
+ return this; // light DOM for shared styles
55
+ }
56
+ connectedCallback() {
57
+ super.connectedCallback();
58
+ this.style.display = "block";
59
+ this.style.height = "100%";
60
+ // Reattach existing artifact elements when panel is re-inserted into the DOM
61
+ requestAnimationFrame(() => {
62
+ const container = this.contentRef.value;
63
+ if (!container)
64
+ return;
65
+ // Ensure we have an active filename
66
+ if (!this._activeFilename && this._artifacts.size > 0) {
67
+ this._activeFilename = Array.from(this._artifacts.keys())[0];
68
+ }
69
+ this.artifactElements.forEach((element, name) => {
70
+ if (!element.parentElement)
71
+ container.appendChild(element);
72
+ element.style.display = name === this._activeFilename ? "block" : "none";
73
+ });
74
+ });
75
+ }
76
+ disconnectedCallback() {
77
+ super.disconnectedCallback();
78
+ // Do not tear down artifact elements; keep them to restore on next mount
79
+ }
80
+ // Helper to determine file type from extension
81
+ getFileType(filename) {
82
+ const ext = filename.split(".").pop()?.toLowerCase();
83
+ if (ext === "html")
84
+ return "html";
85
+ if (ext === "svg")
86
+ return "svg";
87
+ if (ext === "md" || ext === "markdown")
88
+ return "markdown";
89
+ return "text";
90
+ }
91
+ // Helper to determine language for syntax highlighting
92
+ getLanguageFromFilename(filename) {
93
+ if (!filename)
94
+ return "text";
95
+ const ext = filename.split(".").pop()?.toLowerCase();
96
+ const languageMap = {
97
+ js: "javascript",
98
+ jsx: "javascript",
99
+ ts: "typescript",
100
+ tsx: "typescript",
101
+ html: "html",
102
+ css: "css",
103
+ scss: "scss",
104
+ json: "json",
105
+ py: "python",
106
+ md: "markdown",
107
+ svg: "xml",
108
+ xml: "xml",
109
+ yaml: "yaml",
110
+ yml: "yaml",
111
+ sh: "bash",
112
+ bash: "bash",
113
+ sql: "sql",
114
+ java: "java",
115
+ c: "c",
116
+ cpp: "cpp",
117
+ cs: "csharp",
118
+ go: "go",
119
+ rs: "rust",
120
+ php: "php",
121
+ rb: "ruby",
122
+ swift: "swift",
123
+ kt: "kotlin",
124
+ r: "r",
125
+ };
126
+ return languageMap[ext || ""] || "text";
127
+ }
128
+ // Get or create artifact element
129
+ getOrCreateArtifactElement(filename, content, title) {
130
+ let element = this.artifactElements.get(filename);
131
+ if (!element) {
132
+ const type = this.getFileType(filename);
133
+ if (type === "html") {
134
+ element = new HtmlArtifact();
135
+ element.attachments = this.attachmentsProvider?.() || [];
136
+ if (this.sandboxUrlProvider) {
137
+ element.sandboxUrlProvider = this.sandboxUrlProvider;
138
+ }
139
+ }
140
+ else if (type === "svg") {
141
+ element = new SvgArtifact();
142
+ }
143
+ else if (type === "markdown") {
144
+ element = new MarkdownArtifact();
145
+ }
146
+ else {
147
+ element = new TextArtifact();
148
+ }
149
+ element.filename = filename;
150
+ element.displayTitle = title;
151
+ element.content = content;
152
+ element.style.display = "none";
153
+ element.style.height = "100%";
154
+ // Store element
155
+ this.artifactElements.set(filename, element);
156
+ // Add to DOM - try immediately if container exists, otherwise schedule
157
+ const newElement = element;
158
+ if (this.contentRef.value) {
159
+ this.contentRef.value.appendChild(newElement);
160
+ }
161
+ else {
162
+ requestAnimationFrame(() => {
163
+ if (this.contentRef.value && !newElement.parentElement) {
164
+ this.contentRef.value.appendChild(newElement);
165
+ }
166
+ });
167
+ }
168
+ }
169
+ else {
170
+ // Just update content
171
+ element.content = content;
172
+ element.displayTitle = title;
173
+ if (element instanceof HtmlArtifact) {
174
+ element.attachments = this.attachmentsProvider?.() || [];
175
+ }
176
+ }
177
+ return element;
178
+ }
179
+ // Show/hide artifact elements
180
+ showArtifact(filename) {
181
+ // Ensure the active element is in the DOM
182
+ requestAnimationFrame(() => {
183
+ this.artifactElements.forEach((element, name) => {
184
+ if (this.contentRef.value && !element.parentElement) {
185
+ this.contentRef.value.appendChild(element);
186
+ }
187
+ element.style.display = name === filename ? "block" : "none";
188
+ });
189
+ });
190
+ this._activeFilename = filename;
191
+ this.requestUpdate(); // Only for tab bar update
192
+ }
193
+ // Open panel and focus an artifact tab by filename
194
+ openArtifact(filename) {
195
+ if (this._artifacts.has(filename)) {
196
+ this.showArtifact(filename);
197
+ // Ask host to open panel (AgentInterface demo listens to onOpen)
198
+ this.onOpen?.();
199
+ }
200
+ }
201
+ // Build the AgentTool (no details payload; return only output strings)
202
+ get tool() {
203
+ return {
204
+ label: "Artifacts",
205
+ name: "artifacts",
206
+ description: `Creates and manages file artifacts. Each artifact is a file with a filename and content.
207
+
208
+ IMPORTANT: Always prefer updating existing files over creating new ones. Check available files first.
209
+
210
+ Commands:
211
+ 1. create: Create a new file
212
+ - filename: Name with extension (required, e.g., 'index.html', 'script.js', 'README.md')
213
+ - title: Display name for the tab (optional, defaults to filename)
214
+ - content: File content (required)
215
+
216
+ 2. update: Update part of an existing file
217
+ - filename: File to update (required)
218
+ - old_str: Exact string to replace (required)
219
+ - new_str: Replacement string (required)
220
+
221
+ 3. rewrite: Completely replace a file's content
222
+ - filename: File to rewrite (required)
223
+ - content: New content (required)
224
+ - title: Optionally update display title
225
+
226
+ 4. get: Retrieve the full content of a file
227
+ - filename: File to retrieve (required)
228
+ - Returns the complete file content
229
+
230
+ 5. delete: Delete a file
231
+ - filename: File to delete (required)
232
+
233
+ 6. logs: Get console logs and errors (HTML files only)
234
+ - filename: HTML file to get logs for (required)
235
+ - Returns all console output and runtime errors
236
+
237
+ For text/html artifacts with attachments:
238
+ - HTML artifacts automatically have access to user attachments via JavaScript
239
+ - Available global functions in HTML artifacts:
240
+ * listFiles() - Returns array of {id, fileName, mimeType, size} for all attachments
241
+ * readTextFile(attachmentId) - Returns text content of attachment (for CSV, JSON, text files)
242
+ * readBinaryFile(attachmentId) - Returns Uint8Array of binary data (for images, Excel, etc.)
243
+ - Example HTML artifact that processes a CSV attachment:
244
+ <script>
245
+ // List available files
246
+ const files = listFiles();
247
+ console.log('Available files:', files);
248
+
249
+ // Find CSV file
250
+ const csvFile = files.find(f => f.mimeType === 'text/csv');
251
+ if (csvFile) {
252
+ const csvContent = readTextFile(csvFile.id);
253
+ // Process CSV data...
254
+ }
255
+
256
+ // Display image
257
+ const imageFile = files.find(f => f.mimeType.startsWith('image/'));
258
+ if (imageFile) {
259
+ const bytes = readBinaryFile(imageFile.id);
260
+ const blob = new Blob([bytes], {type: imageFile.mimeType});
261
+ const url = URL.createObjectURL(blob);
262
+ document.body.innerHTML = '<img src="' + url + '">';
263
+ }
264
+ </script>
265
+
266
+ For text/html artifacts:
267
+ - Must be a single self-contained file
268
+ - External scripts: Use CDNs like https://esm.sh, https://unpkg.com, or https://cdnjs.cloudflare.com
269
+ - Preferred: Use https://esm.sh for npm packages (e.g., https://esm.sh/three for Three.js)
270
+ - For ES modules, use: <script type="module">import * as THREE from 'https://esm.sh/three';</script>
271
+ - For Three.js specifically: import from 'https://esm.sh/three' or 'https://esm.sh/three@0.160.0'
272
+ - For addons: import from 'https://esm.sh/three/examples/jsm/controls/OrbitControls.js'
273
+ - No localStorage/sessionStorage - use in-memory variables only
274
+ - CSS should be included inline
275
+ - CRITICAL REMINDER FOR HTML ARTIFACTS:
276
+ - ALWAYS set a background color inline in <style> or directly on body element
277
+ - Failure to set a background color is a COMPLIANCE ERROR
278
+ - Background color MUST be explicitly defined to ensure visibility and proper rendering
279
+ - Can embed base64 images directly in img tags
280
+ - Ensure the layout is responsive as the iframe might be resized
281
+ - Note: Network errors (404s) for external scripts may not be captured in logs due to browser security
282
+
283
+ For application/vnd.ant.code artifacts:
284
+ - Include the language parameter for syntax highlighting
285
+ - Supports all major programming languages
286
+
287
+ For text/markdown:
288
+ - Standard markdown syntax
289
+ - Will be rendered with full formatting
290
+ - Can include base64 images using markdown syntax
291
+
292
+ For image/svg+xml:
293
+ - Complete SVG markup
294
+ - Will be rendered inline
295
+ - Can embed raster images as base64 in SVG
296
+
297
+ CRITICAL REMINDER FOR ALL ARTIFACTS:
298
+ - Prefer to update existing files rather than creating new ones
299
+ - Keep filenames consistent and descriptive
300
+ - Use appropriate file extensions
301
+ - Ensure HTML artifacts have a defined background color
302
+ `,
303
+ parameters: artifactsParamsSchema,
304
+ // Execute mutates our local store and returns a plain output
305
+ execute: async (_toolCallId, args, _signal) => {
306
+ const output = await this.executeCommand(args);
307
+ return { output, details: undefined };
308
+ },
309
+ };
310
+ }
311
+ // ToolRenderer implementation
312
+ renderParams(params, isStreaming) {
313
+ if (isStreaming && !params.command) {
314
+ return html `<div class="text-sm text-muted-foreground">${i18n("Processing artifact...")}</div>`;
315
+ }
316
+ let commandLabel = i18n("Processing");
317
+ if (params.command) {
318
+ switch (params.command) {
319
+ case "create":
320
+ commandLabel = i18n("Create");
321
+ break;
322
+ case "update":
323
+ commandLabel = i18n("Update");
324
+ break;
325
+ case "rewrite":
326
+ commandLabel = i18n("Rewrite");
327
+ break;
328
+ case "get":
329
+ commandLabel = i18n("Get");
330
+ break;
331
+ case "delete":
332
+ commandLabel = i18n("Delete");
333
+ break;
334
+ case "logs":
335
+ commandLabel = i18n("Get logs");
336
+ break;
337
+ default:
338
+ commandLabel = params.command.charAt(0).toUpperCase() + params.command.slice(1);
339
+ }
340
+ }
341
+ const filename = params.filename || "";
342
+ switch (params.command) {
343
+ case "create":
344
+ return html `
345
+ <div
346
+ class="text-sm cursor-pointer hover:bg-muted/50 rounded-sm px-2 py-1"
347
+ @click=${() => this.openArtifact(params.filename)}
348
+ >
349
+ <div>
350
+ <span class="font-medium">${i18n("Create")}</span>
351
+ <span class="text-muted-foreground ml-1">${filename}</span>
352
+ </div>
353
+ ${params.content
354
+ ? html `<code-block
355
+ .code=${params.content}
356
+ language=${this.getLanguageFromFilename(params.filename)}
357
+ class="mt-2"
358
+ ></code-block>`
359
+ : ""}
360
+ </div>
361
+ `;
362
+ case "update":
363
+ return html `
364
+ <div
365
+ class="text-sm cursor-pointer hover:bg-muted/50 rounded-sm px-2 py-1"
366
+ @click=${() => this.openArtifact(params.filename)}
367
+ >
368
+ <div>
369
+ <span class="font-medium">${i18n("Update")}</span>
370
+ <span class="text-muted-foreground ml-1">${filename}</span>
371
+ </div>
372
+ ${params.old_str !== undefined && params.new_str !== undefined
373
+ ? Diff({ oldText: params.old_str, newText: params.new_str, className: "mt-2" })
374
+ : ""}
375
+ </div>
376
+ `;
377
+ case "rewrite":
378
+ return html `
379
+ <div
380
+ class="text-sm cursor-pointer hover:bg-muted/50 rounded-sm px-2 py-1"
381
+ @click=${() => this.openArtifact(params.filename)}
382
+ >
383
+ <div>
384
+ <span class="font-medium">${i18n("Rewrite")}</span>
385
+ <span class="text-muted-foreground ml-1">${filename}</span>
386
+ </div>
387
+ ${params.content
388
+ ? html `<code-block
389
+ .code=${params.content}
390
+ language=${this.getLanguageFromFilename(params.filename)}
391
+ class="mt-2"
392
+ ></code-block>`
393
+ : ""}
394
+ </div>
395
+ `;
396
+ case "get":
397
+ return html `
398
+ <div
399
+ class="text-sm cursor-pointer hover:bg-muted/50 rounded-sm px-2 py-1"
400
+ @click=${() => this.openArtifact(params.filename)}
401
+ >
402
+ <span class="font-medium">${i18n("Get")}</span>
403
+ <span class="text-muted-foreground ml-1">${filename}</span>
404
+ </div>
405
+ `;
406
+ case "delete":
407
+ return html `
408
+ <div
409
+ class="text-sm cursor-pointer hover:bg-muted/50 rounded-sm px-2 py-1"
410
+ @click=${() => this.openArtifact(params.filename)}
411
+ >
412
+ <span class="font-medium">${i18n("Delete")}</span>
413
+ <span class="text-muted-foreground ml-1">${filename}</span>
414
+ </div>
415
+ `;
416
+ case "logs":
417
+ return html `
418
+ <div
419
+ class="text-sm cursor-pointer hover:bg-muted/50 rounded-sm px-2 py-1"
420
+ @click=${() => this.openArtifact(params.filename)}
421
+ >
422
+ <span class="font-medium">${i18n("Get logs")}</span>
423
+ <span class="text-muted-foreground ml-1">${filename}</span>
424
+ </div>
425
+ `;
426
+ default:
427
+ // Fallback for any command not yet handled during streaming
428
+ return html `
429
+ <div
430
+ class="text-sm cursor-pointer hover:bg-muted/50 rounded-sm px-2 py-1"
431
+ @click=${() => this.openArtifact(params.filename)}
432
+ >
433
+ <span class="font-medium">${commandLabel}</span>
434
+ <span class="text-muted-foreground ml-1">${filename}</span>
435
+ </div>
436
+ `;
437
+ }
438
+ }
439
+ renderResult(params, result) {
440
+ // Make result clickable to focus the referenced file when applicable
441
+ const content = result.output || i18n("(no output)");
442
+ return html `
443
+ <div class="cursor-pointer hover:bg-muted/50 rounded-sm px-2 py-1" @click=${() => this.openArtifact(params.filename)}>
444
+ ${plainOutput(content)}
445
+ </div>
446
+ `;
447
+ }
448
+ // Re-apply artifacts by scanning a message list (optional utility)
449
+ async reconstructFromMessages(messages) {
450
+ const toolCalls = new Map();
451
+ const artifactToolName = "artifacts";
452
+ // 1) Collect tool calls from assistant messages
453
+ for (const message of messages) {
454
+ if (message.role === "assistant") {
455
+ for (const block of message.content) {
456
+ if (block.type === "toolCall" && block.name === artifactToolName) {
457
+ toolCalls.set(block.id, block);
458
+ }
459
+ }
460
+ }
461
+ }
462
+ // 2) Build an ordered list of successful artifact operations
463
+ const operations = [];
464
+ for (const m of messages) {
465
+ if (m.role === "toolResult" && m.toolName === artifactToolName && !m.isError) {
466
+ const toolCallId = m.toolCallId;
467
+ const call = toolCalls.get(toolCallId);
468
+ if (!call)
469
+ continue;
470
+ const params = call.arguments;
471
+ if (params.command === "get" || params.command === "logs")
472
+ continue; // no state change
473
+ operations.push(params);
474
+ }
475
+ }
476
+ const finalArtifacts = new Map();
477
+ for (const op of operations) {
478
+ const filename = op.filename;
479
+ switch (op.command) {
480
+ case "create": {
481
+ if (op.content) {
482
+ finalArtifacts.set(filename, { title: op.title || filename, content: op.content });
483
+ }
484
+ break;
485
+ }
486
+ case "rewrite": {
487
+ if (op.content) {
488
+ // If file didn't exist earlier but rewrite succeeded, treat as fresh content
489
+ const existing = finalArtifacts.get(filename);
490
+ finalArtifacts.set(filename, { title: op.title || existing?.title || filename, content: op.content });
491
+ }
492
+ break;
493
+ }
494
+ case "update": {
495
+ const existing = finalArtifacts.get(filename);
496
+ if (!existing)
497
+ break; // skip invalid update (shouldn't happen for successful results)
498
+ if (op.old_str !== undefined && op.new_str !== undefined) {
499
+ existing.content = existing.content.replace(op.old_str, op.new_str);
500
+ finalArtifacts.set(filename, existing);
501
+ }
502
+ break;
503
+ }
504
+ case "delete": {
505
+ finalArtifacts.delete(filename);
506
+ break;
507
+ }
508
+ case "get":
509
+ case "logs":
510
+ // Ignored above, just for completeness
511
+ break;
512
+ }
513
+ }
514
+ // 4) Reset current UI state before bulk create
515
+ this._artifacts.clear();
516
+ this.artifactElements.forEach((el) => {
517
+ el.remove();
518
+ });
519
+ this.artifactElements.clear();
520
+ this._activeFilename = null;
521
+ this._artifacts = new Map(this._artifacts);
522
+ // 5) Create artifacts in a single pass without waiting for iframe execution or tab switching
523
+ for (const [filename, { title, content }] of finalArtifacts.entries()) {
524
+ const createParams = { command: "create", filename, title, content };
525
+ try {
526
+ await this.createArtifact(createParams, { skipWait: true, silent: true });
527
+ }
528
+ catch {
529
+ // Ignore failures during reconstruction
530
+ }
531
+ }
532
+ // 6) Show first artifact if any exist, and notify listeners once
533
+ if (!this._activeFilename && this._artifacts.size > 0) {
534
+ this.showArtifact(Array.from(this._artifacts.keys())[0]);
535
+ }
536
+ this.onArtifactsChange?.();
537
+ this.requestUpdate();
538
+ }
539
+ // Core command executor
540
+ async executeCommand(params, options = {}) {
541
+ switch (params.command) {
542
+ case "create":
543
+ return await this.createArtifact(params, options);
544
+ case "update":
545
+ return await this.updateArtifact(params, options);
546
+ case "rewrite":
547
+ return await this.rewriteArtifact(params, options);
548
+ case "get":
549
+ return this.getArtifact(params);
550
+ case "delete":
551
+ return this.deleteArtifact(params);
552
+ case "logs":
553
+ return this.getLogs(params);
554
+ default:
555
+ // Should never happen with TypeBox validation
556
+ return `Error: Unknown command ${params.command}`;
557
+ }
558
+ }
559
+ // Wait for HTML artifact execution and get logs
560
+ async waitForHtmlExecution(filename) {
561
+ const element = this.artifactElements.get(filename);
562
+ if (!(element instanceof HtmlArtifact)) {
563
+ return "";
564
+ }
565
+ return new Promise((resolve) => {
566
+ let resolved = false;
567
+ // Listen for the execution-complete message
568
+ const messageHandler = (event) => {
569
+ if (event.data?.type === "execution-complete" && event.data?.artifactId === filename) {
570
+ if (!resolved) {
571
+ resolved = true;
572
+ window.removeEventListener("message", messageHandler);
573
+ // Get the logs from the element
574
+ const logs = element.getLogs();
575
+ if (logs.includes("[error]")) {
576
+ resolve(`\n\nExecution completed with errors:\n${logs}`);
577
+ }
578
+ else if (logs !== `No logs for ${filename}`) {
579
+ resolve(`\n\nExecution logs:\n${logs}`);
580
+ }
581
+ else {
582
+ resolve("");
583
+ }
584
+ }
585
+ }
586
+ };
587
+ window.addEventListener("message", messageHandler);
588
+ // Fallback timeout in case the message never arrives
589
+ setTimeout(() => {
590
+ if (!resolved) {
591
+ resolved = true;
592
+ window.removeEventListener("message", messageHandler);
593
+ // Get whatever logs we have so far
594
+ const logs = element.getLogs();
595
+ if (logs.includes("[error]")) {
596
+ resolve(`\n\nExecution timed out with errors:\n${logs}`);
597
+ }
598
+ else if (logs !== `No logs for ${filename}`) {
599
+ resolve(`\n\nExecution timed out. Partial logs:\n${logs}`);
600
+ }
601
+ else {
602
+ resolve("");
603
+ }
604
+ }
605
+ }, 1500);
606
+ });
607
+ }
608
+ async createArtifact(params, options = {}) {
609
+ if (!params.filename || !params.content) {
610
+ return "Error: create command requires filename and content";
611
+ }
612
+ if (this._artifacts.has(params.filename)) {
613
+ return `Error: File ${params.filename} already exists`;
614
+ }
615
+ const title = params.title || params.filename;
616
+ const artifact = {
617
+ filename: params.filename,
618
+ title: title,
619
+ content: params.content,
620
+ createdAt: new Date(),
621
+ updatedAt: new Date(),
622
+ };
623
+ this._artifacts.set(params.filename, artifact);
624
+ this._artifacts = new Map(this._artifacts);
625
+ // Create or update element
626
+ this.getOrCreateArtifactElement(params.filename, params.content, title);
627
+ if (!options.silent) {
628
+ this.showArtifact(params.filename);
629
+ this.onArtifactsChange?.();
630
+ this.requestUpdate();
631
+ }
632
+ // For HTML files, wait for execution
633
+ let result = `Created file ${params.filename}`;
634
+ if (this.getFileType(params.filename) === "html" && !options.skipWait) {
635
+ const logs = await this.waitForHtmlExecution(params.filename);
636
+ result += logs;
637
+ }
638
+ return result;
639
+ }
640
+ async updateArtifact(params, options = {}) {
641
+ const artifact = this._artifacts.get(params.filename);
642
+ if (!artifact) {
643
+ const files = Array.from(this._artifacts.keys());
644
+ if (files.length === 0)
645
+ return `Error: File ${params.filename} not found. No files have been created yet.`;
646
+ return `Error: File ${params.filename} not found. Available files: ${files.join(", ")}`;
647
+ }
648
+ if (!params.old_str || params.new_str === undefined) {
649
+ return "Error: update command requires old_str and new_str";
650
+ }
651
+ if (!artifact.content.includes(params.old_str)) {
652
+ return `Error: String not found in file. Here is the full content:\n\n${artifact.content}`;
653
+ }
654
+ artifact.content = artifact.content.replace(params.old_str, params.new_str);
655
+ artifact.updatedAt = new Date();
656
+ this._artifacts.set(params.filename, artifact);
657
+ // Update element
658
+ this.getOrCreateArtifactElement(params.filename, artifact.content, artifact.title);
659
+ if (!options.silent) {
660
+ this.onArtifactsChange?.();
661
+ this.requestUpdate();
662
+ }
663
+ // Show the artifact
664
+ this.showArtifact(params.filename);
665
+ // For HTML files, wait for execution
666
+ let result = `Updated file ${params.filename}`;
667
+ if (this.getFileType(params.filename) === "html" && !options.skipWait) {
668
+ const logs = await this.waitForHtmlExecution(params.filename);
669
+ result += logs;
670
+ }
671
+ return result;
672
+ }
673
+ async rewriteArtifact(params, options = {}) {
674
+ const artifact = this._artifacts.get(params.filename);
675
+ if (!artifact) {
676
+ const files = Array.from(this._artifacts.keys());
677
+ if (files.length === 0)
678
+ return `Error: File ${params.filename} not found. No files have been created yet.`;
679
+ return `Error: File ${params.filename} not found. Available files: ${files.join(", ")}`;
680
+ }
681
+ if (!params.content) {
682
+ return "Error: rewrite command requires content";
683
+ }
684
+ artifact.content = params.content;
685
+ if (params.title)
686
+ artifact.title = params.title;
687
+ artifact.updatedAt = new Date();
688
+ this._artifacts.set(params.filename, artifact);
689
+ // Update element
690
+ this.getOrCreateArtifactElement(params.filename, artifact.content, artifact.title);
691
+ if (!options.silent) {
692
+ this.onArtifactsChange?.();
693
+ }
694
+ // Show the artifact
695
+ this.showArtifact(params.filename);
696
+ // For HTML files, wait for execution
697
+ let result = `Rewrote file ${params.filename}`;
698
+ if (this.getFileType(params.filename) === "html" && !options.skipWait) {
699
+ const logs = await this.waitForHtmlExecution(params.filename);
700
+ result += logs;
701
+ }
702
+ return result;
703
+ }
704
+ getArtifact(params) {
705
+ const artifact = this._artifacts.get(params.filename);
706
+ if (!artifact) {
707
+ const files = Array.from(this._artifacts.keys());
708
+ if (files.length === 0)
709
+ return `Error: File ${params.filename} not found. No files have been created yet.`;
710
+ return `Error: File ${params.filename} not found. Available files: ${files.join(", ")}`;
711
+ }
712
+ return artifact.content;
713
+ }
714
+ deleteArtifact(params) {
715
+ const artifact = this._artifacts.get(params.filename);
716
+ if (!artifact) {
717
+ const files = Array.from(this._artifacts.keys());
718
+ if (files.length === 0)
719
+ return `Error: File ${params.filename} not found. No files have been created yet.`;
720
+ return `Error: File ${params.filename} not found. Available files: ${files.join(", ")}`;
721
+ }
722
+ this._artifacts.delete(params.filename);
723
+ this._artifacts = new Map(this._artifacts);
724
+ // Remove element
725
+ const element = this.artifactElements.get(params.filename);
726
+ if (element) {
727
+ element.remove();
728
+ this.artifactElements.delete(params.filename);
729
+ }
730
+ // Show another artifact if this was active
731
+ if (this._activeFilename === params.filename) {
732
+ const remaining = Array.from(this._artifacts.keys());
733
+ if (remaining.length > 0) {
734
+ this.showArtifact(remaining[0]);
735
+ }
736
+ else {
737
+ this._activeFilename = null;
738
+ this.requestUpdate();
739
+ }
740
+ }
741
+ this.onArtifactsChange?.();
742
+ this.requestUpdate();
743
+ return `Deleted file ${params.filename}`;
744
+ }
745
+ getLogs(params) {
746
+ const element = this.artifactElements.get(params.filename);
747
+ if (!element) {
748
+ const files = Array.from(this._artifacts.keys());
749
+ if (files.length === 0)
750
+ return `Error: File ${params.filename} not found. No files have been created yet.`;
751
+ return `Error: File ${params.filename} not found. Available files: ${files.join(", ")}`;
752
+ }
753
+ if (!(element instanceof HtmlArtifact)) {
754
+ return `Error: File ${params.filename} is not an HTML file. Logs are only available for HTML files.`;
755
+ }
756
+ return element.getLogs();
757
+ }
758
+ render() {
759
+ const artifacts = Array.from(this._artifacts.values());
760
+ // Panel is hidden when collapsed OR when there are no artifacts
761
+ const showPanel = artifacts.length > 0 && !this.collapsed;
762
+ return html `
763
+ <div
764
+ class="${showPanel ? "" : "hidden"} ${this.overlay ? "fixed inset-0 z-40 pointer-events-auto backdrop-blur-sm bg-background/95" : "relative"} h-full flex flex-col bg-background text-card-foreground ${!this.overlay ? "border-l border-border" : ""} overflow-hidden shadow-xl"
765
+ >
766
+ <!-- Tab bar (always shown when there are artifacts) -->
767
+ <div class="flex items-center justify-between border-b border-border bg-background">
768
+ <div class="flex overflow-x-auto">
769
+ ${artifacts.map((a) => {
770
+ const isActive = a.filename === this._activeFilename;
771
+ const activeClass = isActive
772
+ ? "border-primary text-primary"
773
+ : "border-transparent text-muted-foreground hover:text-foreground";
774
+ return html `
775
+ <button
776
+ class="px-3 py-2 whitespace-nowrap border-b-2 ${activeClass}"
777
+ @click=${() => this.showArtifact(a.filename)}
778
+ >
779
+ <span class="font-mono text-xs">${a.filename}</span>
780
+ </button>
781
+ `;
782
+ })}
783
+ </div>
784
+ <div class="flex items-center gap-1 px-2">
785
+ ${(() => {
786
+ const active = this._activeFilename ? this.artifactElements.get(this._activeFilename) : undefined;
787
+ return active ? active.getHeaderButtons() : "";
788
+ })()}
789
+ ${Button({
790
+ variant: "ghost",
791
+ size: "sm",
792
+ onClick: () => this.onClose?.(),
793
+ title: i18n("Close artifacts"),
794
+ children: icon(X, "sm"),
795
+ })}
796
+ </div>
797
+ </div>
798
+
799
+ <!-- Content area where artifact elements are added programmatically -->
800
+ <div class="flex-1 overflow-hidden" ${ref(this.contentRef)}></div>
801
+ </div>
802
+ `;
803
+ }
804
+ };
805
+ __decorate([
806
+ state()
807
+ ], ArtifactsPanel.prototype, "_artifacts", void 0);
808
+ __decorate([
809
+ state()
810
+ ], ArtifactsPanel.prototype, "_activeFilename", void 0);
811
+ __decorate([
812
+ property({ attribute: false })
813
+ ], ArtifactsPanel.prototype, "attachmentsProvider", void 0);
814
+ __decorate([
815
+ property({ attribute: false })
816
+ ], ArtifactsPanel.prototype, "sandboxUrlProvider", void 0);
817
+ __decorate([
818
+ property({ attribute: false })
819
+ ], ArtifactsPanel.prototype, "onArtifactsChange", void 0);
820
+ __decorate([
821
+ property({ attribute: false })
822
+ ], ArtifactsPanel.prototype, "onClose", void 0);
823
+ __decorate([
824
+ property({ attribute: false })
825
+ ], ArtifactsPanel.prototype, "onOpen", void 0);
826
+ __decorate([
827
+ property({ type: Boolean })
828
+ ], ArtifactsPanel.prototype, "collapsed", void 0);
829
+ __decorate([
830
+ property({ type: Boolean })
831
+ ], ArtifactsPanel.prototype, "overlay", void 0);
832
+ ArtifactsPanel = __decorate([
833
+ customElement("artifacts-panel")
834
+ ], ArtifactsPanel);
835
+ export { ArtifactsPanel };
836
+ //# sourceMappingURL=artifacts.js.map