@draht/web-ui 2026.3.2-2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (372) hide show
  1. package/CHANGELOG.md +276 -0
  2. package/README.md +601 -0
  3. package/dist/ChatPanel.d.ts +28 -0
  4. package/dist/ChatPanel.d.ts.map +1 -0
  5. package/dist/ChatPanel.js +193 -0
  6. package/dist/ChatPanel.js.map +1 -0
  7. package/dist/app.css +2 -0
  8. package/dist/components/AgentInterface.d.ts +39 -0
  9. package/dist/components/AgentInterface.d.ts.map +1 -0
  10. package/dist/components/AgentInterface.js +384 -0
  11. package/dist/components/AgentInterface.js.map +1 -0
  12. package/dist/components/AttachmentTile.d.ts +12 -0
  13. package/dist/components/AttachmentTile.d.ts.map +1 -0
  14. package/dist/components/AttachmentTile.js +110 -0
  15. package/dist/components/AttachmentTile.js.map +1 -0
  16. package/dist/components/ConsoleBlock.d.ts +12 -0
  17. package/dist/components/ConsoleBlock.d.ts.map +1 -0
  18. package/dist/components/ConsoleBlock.js +81 -0
  19. package/dist/components/ConsoleBlock.js.map +1 -0
  20. package/dist/components/CustomProviderCard.d.ts +17 -0
  21. package/dist/components/CustomProviderCard.d.ts.map +1 -0
  22. package/dist/components/CustomProviderCard.js +112 -0
  23. package/dist/components/CustomProviderCard.js.map +1 -0
  24. package/dist/components/ExpandableSection.d.ts +15 -0
  25. package/dist/components/ExpandableSection.d.ts.map +1 -0
  26. package/dist/components/ExpandableSection.js +61 -0
  27. package/dist/components/ExpandableSection.js.map +1 -0
  28. package/dist/components/Input.d.ts +26 -0
  29. package/dist/components/Input.d.ts.map +1 -0
  30. package/dist/components/Input.js +57 -0
  31. package/dist/components/Input.js.map +1 -0
  32. package/dist/components/MessageEditor.d.ts +44 -0
  33. package/dist/components/MessageEditor.d.ts.map +1 -0
  34. package/dist/components/MessageEditor.js +418 -0
  35. package/dist/components/MessageEditor.js.map +1 -0
  36. package/dist/components/MessageList.d.ts +14 -0
  37. package/dist/components/MessageList.d.ts.map +1 -0
  38. package/dist/components/MessageList.js +104 -0
  39. package/dist/components/MessageList.js.map +1 -0
  40. package/dist/components/Messages.d.ts +95 -0
  41. package/dist/components/Messages.d.ts.map +1 -0
  42. package/dist/components/Messages.js +363 -0
  43. package/dist/components/Messages.js.map +1 -0
  44. package/dist/components/ProviderKeyInput.d.ts +16 -0
  45. package/dist/components/ProviderKeyInput.d.ts.map +1 -0
  46. package/dist/components/ProviderKeyInput.js +168 -0
  47. package/dist/components/ProviderKeyInput.js.map +1 -0
  48. package/dist/components/SandboxedIframe.d.ts +85 -0
  49. package/dist/components/SandboxedIframe.d.ts.map +1 -0
  50. package/dist/components/SandboxedIframe.js +518 -0
  51. package/dist/components/SandboxedIframe.js.map +1 -0
  52. package/dist/components/StreamingMessageContainer.d.ts +19 -0
  53. package/dist/components/StreamingMessageContainer.d.ts.map +1 -0
  54. package/dist/components/StreamingMessageContainer.js +117 -0
  55. package/dist/components/StreamingMessageContainer.js.map +1 -0
  56. package/dist/components/ThinkingBlock.d.ts +11 -0
  57. package/dist/components/ThinkingBlock.d.ts.map +1 -0
  58. package/dist/components/ThinkingBlock.js +56 -0
  59. package/dist/components/ThinkingBlock.js.map +1 -0
  60. package/dist/components/message-renderer-registry.d.ts +12 -0
  61. package/dist/components/message-renderer-registry.d.ts.map +1 -0
  62. package/dist/components/message-renderer-registry.js +12 -0
  63. package/dist/components/message-renderer-registry.js.map +1 -0
  64. package/dist/components/sandbox/ArtifactsRuntimeProvider.d.ts +35 -0
  65. package/dist/components/sandbox/ArtifactsRuntimeProvider.d.ts.map +1 -0
  66. package/dist/components/sandbox/ArtifactsRuntimeProvider.js +192 -0
  67. package/dist/components/sandbox/ArtifactsRuntimeProvider.js.map +1 -0
  68. package/dist/components/sandbox/AttachmentsRuntimeProvider.d.ts +17 -0
  69. package/dist/components/sandbox/AttachmentsRuntimeProvider.d.ts.map +1 -0
  70. package/dist/components/sandbox/AttachmentsRuntimeProvider.js +65 -0
  71. package/dist/components/sandbox/AttachmentsRuntimeProvider.js.map +1 -0
  72. package/dist/components/sandbox/ConsoleRuntimeProvider.d.ts +42 -0
  73. package/dist/components/sandbox/ConsoleRuntimeProvider.d.ts.map +1 -0
  74. package/dist/components/sandbox/ConsoleRuntimeProvider.js +159 -0
  75. package/dist/components/sandbox/ConsoleRuntimeProvider.js.map +1 -0
  76. package/dist/components/sandbox/FileDownloadRuntimeProvider.d.ts +30 -0
  77. package/dist/components/sandbox/FileDownloadRuntimeProvider.d.ts.map +1 -0
  78. package/dist/components/sandbox/FileDownloadRuntimeProvider.js +95 -0
  79. package/dist/components/sandbox/FileDownloadRuntimeProvider.js.map +1 -0
  80. package/dist/components/sandbox/RuntimeMessageBridge.d.ts +19 -0
  81. package/dist/components/sandbox/RuntimeMessageBridge.d.ts.map +1 -0
  82. package/dist/components/sandbox/RuntimeMessageBridge.js +74 -0
  83. package/dist/components/sandbox/RuntimeMessageBridge.js.map +1 -0
  84. package/dist/components/sandbox/RuntimeMessageRouter.d.ts +65 -0
  85. package/dist/components/sandbox/RuntimeMessageRouter.d.ts.map +1 -0
  86. package/dist/components/sandbox/RuntimeMessageRouter.js +166 -0
  87. package/dist/components/sandbox/RuntimeMessageRouter.js.map +1 -0
  88. package/dist/components/sandbox/SandboxRuntimeProvider.d.ts +48 -0
  89. package/dist/components/sandbox/SandboxRuntimeProvider.d.ts.map +1 -0
  90. package/dist/components/sandbox/SandboxRuntimeProvider.js +2 -0
  91. package/dist/components/sandbox/SandboxRuntimeProvider.js.map +1 -0
  92. package/dist/dialogs/ApiKeyPromptDialog.d.ts +15 -0
  93. package/dist/dialogs/ApiKeyPromptDialog.d.ts.map +1 -0
  94. package/dist/dialogs/ApiKeyPromptDialog.js +77 -0
  95. package/dist/dialogs/ApiKeyPromptDialog.js.map +1 -0
  96. package/dist/dialogs/AttachmentOverlay.d.ts +32 -0
  97. package/dist/dialogs/AttachmentOverlay.d.ts.map +1 -0
  98. package/dist/dialogs/AttachmentOverlay.js +576 -0
  99. package/dist/dialogs/AttachmentOverlay.js.map +1 -0
  100. package/dist/dialogs/CustomProviderDialog.d.ts +25 -0
  101. package/dist/dialogs/CustomProviderDialog.d.ts.map +1 -0
  102. package/dist/dialogs/CustomProviderDialog.js +270 -0
  103. package/dist/dialogs/CustomProviderDialog.js.map +1 -0
  104. package/dist/dialogs/ModelSelector.d.ts +27 -0
  105. package/dist/dialogs/ModelSelector.d.ts.map +1 -0
  106. package/dist/dialogs/ModelSelector.js +320 -0
  107. package/dist/dialogs/ModelSelector.js.map +1 -0
  108. package/dist/dialogs/PersistentStorageDialog.d.ts +17 -0
  109. package/dist/dialogs/PersistentStorageDialog.d.ts.map +1 -0
  110. package/dist/dialogs/PersistentStorageDialog.js +144 -0
  111. package/dist/dialogs/PersistentStorageDialog.js.map +1 -0
  112. package/dist/dialogs/ProvidersModelsTab.d.ts +20 -0
  113. package/dist/dialogs/ProvidersModelsTab.d.ts.map +1 -0
  114. package/dist/dialogs/ProvidersModelsTab.js +188 -0
  115. package/dist/dialogs/ProvidersModelsTab.js.map +1 -0
  116. package/dist/dialogs/SessionListDialog.d.ts +19 -0
  117. package/dist/dialogs/SessionListDialog.d.ts.map +1 -0
  118. package/dist/dialogs/SessionListDialog.js +152 -0
  119. package/dist/dialogs/SessionListDialog.js.map +1 -0
  120. package/dist/dialogs/SettingsDialog.d.ts +30 -0
  121. package/dist/dialogs/SettingsDialog.d.ts.map +1 -0
  122. package/dist/dialogs/SettingsDialog.js +222 -0
  123. package/dist/dialogs/SettingsDialog.js.map +1 -0
  124. package/dist/index.d.ts +67 -0
  125. package/dist/index.d.ts.map +1 -0
  126. package/dist/index.js +70 -0
  127. package/dist/index.js.map +1 -0
  128. package/dist/prompts/prompts.d.ts +11 -0
  129. package/dist/prompts/prompts.d.ts.map +1 -0
  130. package/dist/prompts/prompts.js +272 -0
  131. package/dist/prompts/prompts.js.map +1 -0
  132. package/dist/storage/app-storage.d.ts +33 -0
  133. package/dist/storage/app-storage.d.ts.map +1 -0
  134. package/dist/storage/app-storage.js +43 -0
  135. package/dist/storage/app-storage.js.map +1 -0
  136. package/dist/storage/backends/indexeddb-storage-backend.d.ts +27 -0
  137. package/dist/storage/backends/indexeddb-storage-backend.d.ts.map +1 -0
  138. package/dist/storage/backends/indexeddb-storage-backend.js +167 -0
  139. package/dist/storage/backends/indexeddb-storage-backend.js.map +1 -0
  140. package/dist/storage/store.d.ts +23 -0
  141. package/dist/storage/store.d.ts.map +1 -0
  142. package/dist/storage/store.js +24 -0
  143. package/dist/storage/store.js.map +1 -0
  144. package/dist/storage/stores/custom-providers-store.d.ts +25 -0
  145. package/dist/storage/stores/custom-providers-store.d.ts.map +1 -0
  146. package/dist/storage/stores/custom-providers-store.js +35 -0
  147. package/dist/storage/stores/custom-providers-store.js.map +1 -0
  148. package/dist/storage/stores/provider-keys-store.d.ts +14 -0
  149. package/dist/storage/stores/provider-keys-store.d.ts.map +1 -0
  150. package/dist/storage/stores/provider-keys-store.js +27 -0
  151. package/dist/storage/stores/provider-keys-store.js.map +1 -0
  152. package/dist/storage/stores/sessions-store.d.ts +32 -0
  153. package/dist/storage/stores/sessions-store.d.ts.map +1 -0
  154. package/dist/storage/stores/sessions-store.js +113 -0
  155. package/dist/storage/stores/sessions-store.js.map +1 -0
  156. package/dist/storage/stores/settings-store.d.ts +14 -0
  157. package/dist/storage/stores/settings-store.d.ts.map +1 -0
  158. package/dist/storage/stores/settings-store.js +28 -0
  159. package/dist/storage/stores/settings-store.js.map +1 -0
  160. package/dist/storage/types.d.ts +176 -0
  161. package/dist/storage/types.d.ts.map +1 -0
  162. package/dist/storage/types.js +2 -0
  163. package/dist/storage/types.js.map +1 -0
  164. package/dist/tools/artifacts/ArtifactElement.d.ts +9 -0
  165. package/dist/tools/artifacts/ArtifactElement.d.ts.map +1 -0
  166. package/dist/tools/artifacts/ArtifactElement.js +8 -0
  167. package/dist/tools/artifacts/ArtifactElement.js.map +1 -0
  168. package/dist/tools/artifacts/ArtifactPill.d.ts +4 -0
  169. package/dist/tools/artifacts/ArtifactPill.d.ts.map +1 -0
  170. package/dist/tools/artifacts/ArtifactPill.js +23 -0
  171. package/dist/tools/artifacts/ArtifactPill.js.map +1 -0
  172. package/dist/tools/artifacts/Console.d.ts +18 -0
  173. package/dist/tools/artifacts/Console.d.ts.map +1 -0
  174. package/dist/tools/artifacts/Console.js +92 -0
  175. package/dist/tools/artifacts/Console.js.map +1 -0
  176. package/dist/tools/artifacts/DocxArtifact.d.ts +22 -0
  177. package/dist/tools/artifacts/DocxArtifact.d.ts.map +1 -0
  178. package/dist/tools/artifacts/DocxArtifact.js +205 -0
  179. package/dist/tools/artifacts/DocxArtifact.js.map +1 -0
  180. package/dist/tools/artifacts/ExcelArtifact.d.ts +24 -0
  181. package/dist/tools/artifacts/ExcelArtifact.d.ts.map +1 -0
  182. package/dist/tools/artifacts/ExcelArtifact.js +213 -0
  183. package/dist/tools/artifacts/ExcelArtifact.js.map +1 -0
  184. package/dist/tools/artifacts/GenericArtifact.d.ts +19 -0
  185. package/dist/tools/artifacts/GenericArtifact.d.ts.map +1 -0
  186. package/dist/tools/artifacts/GenericArtifact.js +114 -0
  187. package/dist/tools/artifacts/GenericArtifact.js.map +1 -0
  188. package/dist/tools/artifacts/HtmlArtifact.d.ts +27 -0
  189. package/dist/tools/artifacts/HtmlArtifact.d.ts.map +1 -0
  190. package/dist/tools/artifacts/HtmlArtifact.js +187 -0
  191. package/dist/tools/artifacts/HtmlArtifact.js.map +1 -0
  192. package/dist/tools/artifacts/ImageArtifact.d.ts +20 -0
  193. package/dist/tools/artifacts/ImageArtifact.d.ts.map +1 -0
  194. package/dist/tools/artifacts/ImageArtifact.js +117 -0
  195. package/dist/tools/artifacts/ImageArtifact.js.map +1 -0
  196. package/dist/tools/artifacts/MarkdownArtifact.d.ts +19 -0
  197. package/dist/tools/artifacts/MarkdownArtifact.d.ts.map +1 -0
  198. package/dist/tools/artifacts/MarkdownArtifact.js +79 -0
  199. package/dist/tools/artifacts/MarkdownArtifact.js.map +1 -0
  200. package/dist/tools/artifacts/PdfArtifact.d.ts +25 -0
  201. package/dist/tools/artifacts/PdfArtifact.d.ts.map +1 -0
  202. package/dist/tools/artifacts/PdfArtifact.js +181 -0
  203. package/dist/tools/artifacts/PdfArtifact.js.map +1 -0
  204. package/dist/tools/artifacts/SvgArtifact.d.ts +18 -0
  205. package/dist/tools/artifacts/SvgArtifact.d.ts.map +1 -0
  206. package/dist/tools/artifacts/SvgArtifact.js +75 -0
  207. package/dist/tools/artifacts/SvgArtifact.js.map +1 -0
  208. package/dist/tools/artifacts/TextArtifact.d.ts +19 -0
  209. package/dist/tools/artifacts/TextArtifact.d.ts.map +1 -0
  210. package/dist/tools/artifacts/TextArtifact.js +141 -0
  211. package/dist/tools/artifacts/TextArtifact.js.map +1 -0
  212. package/dist/tools/artifacts/artifacts-tool-renderer.d.ts +11 -0
  213. package/dist/tools/artifacts/artifacts-tool-renderer.d.ts.map +1 -0
  214. package/dist/tools/artifacts/artifacts-tool-renderer.js +273 -0
  215. package/dist/tools/artifacts/artifacts-tool-renderer.js.map +1 -0
  216. package/dist/tools/artifacts/artifacts.d.ts +63 -0
  217. package/dist/tools/artifacts/artifacts.d.ts.map +1 -0
  218. package/dist/tools/artifacts/artifacts.js +664 -0
  219. package/dist/tools/artifacts/artifacts.js.map +1 -0
  220. package/dist/tools/artifacts/index.d.ts +8 -0
  221. package/dist/tools/artifacts/index.d.ts.map +1 -0
  222. package/dist/tools/artifacts/index.js +8 -0
  223. package/dist/tools/artifacts/index.js.map +1 -0
  224. package/dist/tools/extract-document.d.ts +24 -0
  225. package/dist/tools/extract-document.d.ts.map +1 -0
  226. package/dist/tools/extract-document.js +216 -0
  227. package/dist/tools/extract-document.js.map +1 -0
  228. package/dist/tools/index.d.ts +16 -0
  229. package/dist/tools/index.d.ts.map +1 -0
  230. package/dist/tools/index.js +33 -0
  231. package/dist/tools/index.js.map +1 -0
  232. package/dist/tools/javascript-repl.d.ts +44 -0
  233. package/dist/tools/javascript-repl.d.ts.map +1 -0
  234. package/dist/tools/javascript-repl.js +224 -0
  235. package/dist/tools/javascript-repl.js.map +1 -0
  236. package/dist/tools/renderer-registry.d.ts +23 -0
  237. package/dist/tools/renderer-registry.d.ts.map +1 -0
  238. package/dist/tools/renderer-registry.js +107 -0
  239. package/dist/tools/renderer-registry.js.map +1 -0
  240. package/dist/tools/renderers/BashRenderer.d.ts +10 -0
  241. package/dist/tools/renderers/BashRenderer.d.ts.map +1 -0
  242. package/dist/tools/renderers/BashRenderer.js +42 -0
  243. package/dist/tools/renderers/BashRenderer.js.map +1 -0
  244. package/dist/tools/renderers/CalculateRenderer.d.ts +10 -0
  245. package/dist/tools/renderers/CalculateRenderer.d.ts.map +1 -0
  246. package/dist/tools/renderers/CalculateRenderer.js +45 -0
  247. package/dist/tools/renderers/CalculateRenderer.js.map +1 -0
  248. package/dist/tools/renderers/DefaultRenderer.d.ts +6 -0
  249. package/dist/tools/renderers/DefaultRenderer.d.ts.map +1 -0
  250. package/dist/tools/renderers/DefaultRenderer.js +94 -0
  251. package/dist/tools/renderers/DefaultRenderer.js.map +1 -0
  252. package/dist/tools/renderers/GetCurrentTimeRenderer.d.ts +10 -0
  253. package/dist/tools/renderers/GetCurrentTimeRenderer.d.ts.map +1 -0
  254. package/dist/tools/renderers/GetCurrentTimeRenderer.js +72 -0
  255. package/dist/tools/renderers/GetCurrentTimeRenderer.js.map +1 -0
  256. package/dist/tools/types.d.ts +10 -0
  257. package/dist/tools/types.d.ts.map +1 -0
  258. package/dist/tools/types.js +2 -0
  259. package/dist/tools/types.js.map +1 -0
  260. package/dist/utils/attachment-utils.d.ts +19 -0
  261. package/dist/utils/attachment-utils.d.ts.map +1 -0
  262. package/dist/utils/attachment-utils.js +415 -0
  263. package/dist/utils/attachment-utils.js.map +1 -0
  264. package/dist/utils/auth-token.d.ts +3 -0
  265. package/dist/utils/auth-token.d.ts.map +1 -0
  266. package/dist/utils/auth-token.js +19 -0
  267. package/dist/utils/auth-token.js.map +1 -0
  268. package/dist/utils/format.d.ts +6 -0
  269. package/dist/utils/format.d.ts.map +1 -0
  270. package/dist/utils/format.js +47 -0
  271. package/dist/utils/format.js.map +1 -0
  272. package/dist/utils/i18n.d.ts +636 -0
  273. package/dist/utils/i18n.d.ts.map +1 -0
  274. package/dist/utils/i18n.js +418 -0
  275. package/dist/utils/i18n.js.map +1 -0
  276. package/dist/utils/model-discovery.d.ts +38 -0
  277. package/dist/utils/model-discovery.d.ts.map +1 -0
  278. package/dist/utils/model-discovery.js +243 -0
  279. package/dist/utils/model-discovery.js.map +1 -0
  280. package/dist/utils/proxy-utils.d.ts +45 -0
  281. package/dist/utils/proxy-utils.d.ts.map +1 -0
  282. package/dist/utils/proxy-utils.js +116 -0
  283. package/dist/utils/proxy-utils.js.map +1 -0
  284. package/dist/utils/test-sessions.d.ts +359 -0
  285. package/dist/utils/test-sessions.d.ts.map +1 -0
  286. package/dist/utils/test-sessions.js +2325 -0
  287. package/dist/utils/test-sessions.js.map +1 -0
  288. package/example/README.md +61 -0
  289. package/example/index.html +13 -0
  290. package/example/package.json +25 -0
  291. package/example/src/app.css +1 -0
  292. package/example/src/custom-messages.ts +99 -0
  293. package/example/src/env.d.ts +1 -0
  294. package/example/src/main.ts +421 -0
  295. package/example/tsconfig.json +23 -0
  296. package/example/vite.config.ts +6 -0
  297. package/package.json +53 -0
  298. package/scripts/count-prompt-tokens.ts +88 -0
  299. package/src/ChatPanel.ts +207 -0
  300. package/src/app.css +68 -0
  301. package/src/components/AgentInterface.ts +388 -0
  302. package/src/components/AttachmentTile.ts +107 -0
  303. package/src/components/ConsoleBlock.ts +72 -0
  304. package/src/components/CustomProviderCard.ts +100 -0
  305. package/src/components/ExpandableSection.ts +46 -0
  306. package/src/components/Input.ts +113 -0
  307. package/src/components/MessageEditor.ts +400 -0
  308. package/src/components/MessageList.ts +95 -0
  309. package/src/components/Messages.ts +383 -0
  310. package/src/components/ProviderKeyInput.ts +153 -0
  311. package/src/components/SandboxedIframe.ts +626 -0
  312. package/src/components/StreamingMessageContainer.ts +103 -0
  313. package/src/components/ThinkingBlock.ts +43 -0
  314. package/src/components/message-renderer-registry.ts +28 -0
  315. package/src/components/sandbox/ArtifactsRuntimeProvider.ts +219 -0
  316. package/src/components/sandbox/AttachmentsRuntimeProvider.ts +66 -0
  317. package/src/components/sandbox/ConsoleRuntimeProvider.ts +186 -0
  318. package/src/components/sandbox/FileDownloadRuntimeProvider.ts +110 -0
  319. package/src/components/sandbox/RuntimeMessageBridge.ts +82 -0
  320. package/src/components/sandbox/RuntimeMessageRouter.ts +216 -0
  321. package/src/components/sandbox/SandboxRuntimeProvider.ts +52 -0
  322. package/src/dialogs/ApiKeyPromptDialog.ts +75 -0
  323. package/src/dialogs/AttachmentOverlay.ts +636 -0
  324. package/src/dialogs/CustomProviderDialog.ts +274 -0
  325. package/src/dialogs/ModelSelector.ts +313 -0
  326. package/src/dialogs/PersistentStorageDialog.ts +144 -0
  327. package/src/dialogs/ProvidersModelsTab.ts +212 -0
  328. package/src/dialogs/SessionListDialog.ts +150 -0
  329. package/src/dialogs/SettingsDialog.ts +214 -0
  330. package/src/index.ts +119 -0
  331. package/src/prompts/prompts.ts +282 -0
  332. package/src/storage/app-storage.ts +60 -0
  333. package/src/storage/backends/indexeddb-storage-backend.ts +193 -0
  334. package/src/storage/store.ts +33 -0
  335. package/src/storage/stores/custom-providers-store.ts +62 -0
  336. package/src/storage/stores/provider-keys-store.ts +33 -0
  337. package/src/storage/stores/sessions-store.ts +136 -0
  338. package/src/storage/stores/settings-store.ts +34 -0
  339. package/src/storage/types.ts +206 -0
  340. package/src/tools/artifacts/ArtifactElement.ts +14 -0
  341. package/src/tools/artifacts/ArtifactPill.ts +26 -0
  342. package/src/tools/artifacts/Console.ts +93 -0
  343. package/src/tools/artifacts/DocxArtifact.ts +213 -0
  344. package/src/tools/artifacts/ExcelArtifact.ts +231 -0
  345. package/src/tools/artifacts/GenericArtifact.ts +117 -0
  346. package/src/tools/artifacts/HtmlArtifact.ts +195 -0
  347. package/src/tools/artifacts/ImageArtifact.ts +116 -0
  348. package/src/tools/artifacts/MarkdownArtifact.ts +82 -0
  349. package/src/tools/artifacts/PdfArtifact.ts +201 -0
  350. package/src/tools/artifacts/SvgArtifact.ts +78 -0
  351. package/src/tools/artifacts/TextArtifact.ts +148 -0
  352. package/src/tools/artifacts/artifacts-tool-renderer.ts +310 -0
  353. package/src/tools/artifacts/artifacts.ts +713 -0
  354. package/src/tools/artifacts/index.ts +7 -0
  355. package/src/tools/extract-document.ts +275 -0
  356. package/src/tools/index.ts +46 -0
  357. package/src/tools/javascript-repl.ts +293 -0
  358. package/src/tools/renderer-registry.ts +130 -0
  359. package/src/tools/renderers/BashRenderer.ts +52 -0
  360. package/src/tools/renderers/CalculateRenderer.ts +58 -0
  361. package/src/tools/renderers/DefaultRenderer.ts +103 -0
  362. package/src/tools/renderers/GetCurrentTimeRenderer.ts +92 -0
  363. package/src/tools/types.ts +15 -0
  364. package/src/utils/attachment-utils.ts +472 -0
  365. package/src/utils/auth-token.ts +22 -0
  366. package/src/utils/format.ts +42 -0
  367. package/src/utils/i18n.ts +653 -0
  368. package/src/utils/model-discovery.ts +277 -0
  369. package/src/utils/proxy-utils.ts +134 -0
  370. package/src/utils/test-sessions.ts +2357 -0
  371. package/tsconfig.build.json +20 -0
  372. package/tsconfig.json +7 -0
@@ -0,0 +1,636 @@
1
+ import "@mariozechner/mini-lit/dist/ModeToggle.js";
2
+ import { icon } from "@mariozechner/mini-lit";
3
+ import { Button } from "@mariozechner/mini-lit/dist/Button.js";
4
+ import { renderAsync } from "docx-preview";
5
+ import { html, LitElement } from "lit";
6
+ import { state } from "lit/decorators.js";
7
+ import { Download, X } from "lucide";
8
+ import * as pdfjsLib from "pdfjs-dist";
9
+ import * as XLSX from "xlsx";
10
+ import type { Attachment } from "../utils/attachment-utils.js";
11
+ import { i18n } from "../utils/i18n.js";
12
+
13
+ type FileType = "image" | "pdf" | "docx" | "pptx" | "excel" | "text";
14
+
15
+ export class AttachmentOverlay extends LitElement {
16
+ @state() private attachment?: Attachment;
17
+ @state() private showExtractedText = false;
18
+ @state() private error: string | null = null;
19
+
20
+ // Track current loading task to cancel if needed
21
+ private currentLoadingTask: any = null;
22
+ private onCloseCallback?: () => void;
23
+ private boundHandleKeyDown?: (e: KeyboardEvent) => void;
24
+
25
+ protected override createRenderRoot(): HTMLElement | DocumentFragment {
26
+ return this;
27
+ }
28
+
29
+ static open(attachment: Attachment, onClose?: () => void) {
30
+ const overlay = new AttachmentOverlay();
31
+ overlay.attachment = attachment;
32
+ overlay.onCloseCallback = onClose;
33
+ document.body.appendChild(overlay);
34
+ overlay.setupEventListeners();
35
+ }
36
+
37
+ private setupEventListeners() {
38
+ this.boundHandleKeyDown = (e: KeyboardEvent) => {
39
+ if (e.key === "Escape") {
40
+ this.close();
41
+ }
42
+ };
43
+ window.addEventListener("keydown", this.boundHandleKeyDown);
44
+ }
45
+
46
+ private close() {
47
+ this.cleanup();
48
+ if (this.boundHandleKeyDown) {
49
+ window.removeEventListener("keydown", this.boundHandleKeyDown);
50
+ }
51
+ this.onCloseCallback?.();
52
+ this.remove();
53
+ }
54
+
55
+ private getFileType(): FileType {
56
+ if (!this.attachment) return "text";
57
+
58
+ if (this.attachment.type === "image") return "image";
59
+ if (this.attachment.mimeType === "application/pdf") return "pdf";
60
+ if (this.attachment.mimeType?.includes("wordprocessingml")) return "docx";
61
+ if (
62
+ this.attachment.mimeType?.includes("presentationml") ||
63
+ this.attachment.fileName.toLowerCase().endsWith(".pptx")
64
+ )
65
+ return "pptx";
66
+ if (
67
+ this.attachment.mimeType?.includes("spreadsheetml") ||
68
+ this.attachment.mimeType?.includes("ms-excel") ||
69
+ this.attachment.fileName.toLowerCase().endsWith(".xlsx") ||
70
+ this.attachment.fileName.toLowerCase().endsWith(".xls")
71
+ )
72
+ return "excel";
73
+
74
+ return "text";
75
+ }
76
+
77
+ private getFileTypeLabel(): string {
78
+ const type = this.getFileType();
79
+ switch (type) {
80
+ case "pdf":
81
+ return i18n("PDF");
82
+ case "docx":
83
+ return i18n("Document");
84
+ case "pptx":
85
+ return i18n("Presentation");
86
+ case "excel":
87
+ return i18n("Spreadsheet");
88
+ default:
89
+ return "";
90
+ }
91
+ }
92
+
93
+ private handleBackdropClick = () => {
94
+ this.close();
95
+ };
96
+
97
+ private handleDownload = () => {
98
+ if (!this.attachment) return;
99
+
100
+ // Create a blob from the base64 content
101
+ const byteCharacters = atob(this.attachment.content);
102
+ const byteNumbers = new Array(byteCharacters.length);
103
+ for (let i = 0; i < byteCharacters.length; i++) {
104
+ byteNumbers[i] = byteCharacters.charCodeAt(i);
105
+ }
106
+ const byteArray = new Uint8Array(byteNumbers);
107
+ const blob = new Blob([byteArray], { type: this.attachment.mimeType });
108
+
109
+ // Create download link
110
+ const url = URL.createObjectURL(blob);
111
+ const a = document.createElement("a");
112
+ a.href = url;
113
+ a.download = this.attachment.fileName;
114
+ document.body.appendChild(a);
115
+ a.click();
116
+ document.body.removeChild(a);
117
+ URL.revokeObjectURL(url);
118
+ };
119
+
120
+ private cleanup() {
121
+ this.showExtractedText = false;
122
+ this.error = null;
123
+ // Cancel any loading PDF task when closing
124
+ if (this.currentLoadingTask) {
125
+ this.currentLoadingTask.destroy();
126
+ this.currentLoadingTask = null;
127
+ }
128
+ }
129
+
130
+ override render() {
131
+ if (!this.attachment) return html``;
132
+
133
+ return html`
134
+ <!-- Full screen overlay -->
135
+ <div class="fixed inset-0 bg-black/90 z-50 flex flex-col" @click=${this.handleBackdropClick}>
136
+ <!-- Compact header bar -->
137
+ <div class="bg-background/95 backdrop-blur border-b border-border" @click=${(e: Event) => e.stopPropagation()}>
138
+ <div class="px-4 py-2 flex items-center justify-between">
139
+ <div class="flex items-center gap-3 min-w-0">
140
+ <span class="text-sm font-medium text-foreground truncate">${this.attachment.fileName}</span>
141
+ </div>
142
+ <div class="flex items-center gap-2">
143
+ ${this.renderToggle()}
144
+ ${Button({
145
+ variant: "ghost",
146
+ size: "icon",
147
+ onClick: this.handleDownload,
148
+ children: icon(Download, "sm"),
149
+ className: "h-8 w-8",
150
+ })}
151
+ ${Button({
152
+ variant: "ghost",
153
+ size: "icon",
154
+ onClick: () => this.close(),
155
+ children: icon(X, "sm"),
156
+ className: "h-8 w-8",
157
+ })}
158
+ </div>
159
+ </div>
160
+ </div>
161
+
162
+ <!-- Content container -->
163
+ <div class="flex-1 flex items-center justify-center overflow-auto" @click=${(e: Event) => e.stopPropagation()}>
164
+ ${this.renderContent()}
165
+ </div>
166
+ </div>
167
+ `;
168
+ }
169
+
170
+ private renderToggle() {
171
+ if (!this.attachment) return html``;
172
+
173
+ const fileType = this.getFileType();
174
+ const hasExtractedText = !!this.attachment.extractedText;
175
+ const showToggle = fileType !== "image" && fileType !== "text" && fileType !== "pptx" && hasExtractedText;
176
+
177
+ if (!showToggle) return html``;
178
+
179
+ const fileTypeLabel = this.getFileTypeLabel();
180
+
181
+ return html`
182
+ <mode-toggle
183
+ .modes=${[fileTypeLabel, i18n("Text")]}
184
+ .selectedIndex=${this.showExtractedText ? 1 : 0}
185
+ @mode-change=${(e: CustomEvent<{ index: number; mode: string }>) => {
186
+ e.stopPropagation();
187
+ this.showExtractedText = e.detail.index === 1;
188
+ this.error = null;
189
+ }}
190
+ ></mode-toggle>
191
+ `;
192
+ }
193
+
194
+ private renderContent() {
195
+ if (!this.attachment) return html``;
196
+
197
+ // Error state
198
+ if (this.error) {
199
+ return html`
200
+ <div class="bg-destructive/10 border border-destructive text-destructive p-4 rounded-lg max-w-2xl">
201
+ <div class="font-medium mb-1">${i18n("Error loading file")}</div>
202
+ <div class="text-sm opacity-90">${this.error}</div>
203
+ </div>
204
+ `;
205
+ }
206
+
207
+ // Content based on file type
208
+ return this.renderFileContent();
209
+ }
210
+
211
+ private renderFileContent() {
212
+ if (!this.attachment) return html``;
213
+
214
+ const fileType = this.getFileType();
215
+
216
+ // Show extracted text if toggled
217
+ if (this.showExtractedText && fileType !== "image") {
218
+ return html`
219
+ <div class="bg-card border border-border text-foreground p-6 w-full h-full max-w-4xl overflow-auto">
220
+ <pre class="whitespace-pre-wrap font-mono text-xs leading-relaxed">${
221
+ this.attachment.extractedText || i18n("No text content available")
222
+ }</pre>
223
+ </div>
224
+ `;
225
+ }
226
+
227
+ // Render based on file type
228
+ switch (fileType) {
229
+ case "image": {
230
+ const imageUrl = `data:${this.attachment.mimeType};base64,${this.attachment.content}`;
231
+ return html`
232
+ <img src="${imageUrl}" class="max-w-full max-h-full object-contain rounded-lg shadow-lg" alt="${this.attachment.fileName}" />
233
+ `;
234
+ }
235
+
236
+ case "pdf":
237
+ return html`
238
+ <div
239
+ id="pdf-container"
240
+ class="bg-card text-foreground overflow-auto shadow-lg border border-border w-full h-full max-w-[1000px]"
241
+ ></div>
242
+ `;
243
+
244
+ case "docx":
245
+ return html`
246
+ <div
247
+ id="docx-container"
248
+ class="bg-card text-foreground overflow-auto shadow-lg border border-border w-full h-full max-w-[1000px]"
249
+ ></div>
250
+ `;
251
+
252
+ case "excel":
253
+ return html` <div id="excel-container" class="bg-card text-foreground overflow-auto w-full h-full"></div> `;
254
+
255
+ case "pptx":
256
+ return html`
257
+ <div
258
+ id="pptx-container"
259
+ class="bg-card text-foreground overflow-auto shadow-lg border border-border w-full h-full max-w-[1000px]"
260
+ ></div>
261
+ `;
262
+
263
+ default:
264
+ return html`
265
+ <div class="bg-card border border-border text-foreground p-6 w-full h-full max-w-4xl overflow-auto">
266
+ <pre class="whitespace-pre-wrap font-mono text-sm">${
267
+ this.attachment.extractedText || i18n("No content available")
268
+ }</pre>
269
+ </div>
270
+ `;
271
+ }
272
+ }
273
+
274
+ override async updated(changedProperties: Map<string, any>) {
275
+ super.updated(changedProperties);
276
+
277
+ // Only process if we need to render the actual file (not extracted text)
278
+ if (
279
+ (changedProperties.has("attachment") || changedProperties.has("showExtractedText")) &&
280
+ this.attachment &&
281
+ !this.showExtractedText &&
282
+ !this.error
283
+ ) {
284
+ const fileType = this.getFileType();
285
+
286
+ switch (fileType) {
287
+ case "pdf":
288
+ await this.renderPdf();
289
+ break;
290
+ case "docx":
291
+ await this.renderDocx();
292
+ break;
293
+ case "excel":
294
+ await this.renderExcel();
295
+ break;
296
+ case "pptx":
297
+ await this.renderExtractedText();
298
+ break;
299
+ }
300
+ }
301
+ }
302
+
303
+ private async renderPdf() {
304
+ const container = this.querySelector("#pdf-container");
305
+ if (!container || !this.attachment) return;
306
+
307
+ let pdf: any = null;
308
+
309
+ try {
310
+ // Convert base64 to ArrayBuffer
311
+ const arrayBuffer = this.base64ToArrayBuffer(this.attachment.content);
312
+
313
+ // Cancel any existing loading task
314
+ if (this.currentLoadingTask) {
315
+ this.currentLoadingTask.destroy();
316
+ }
317
+
318
+ // Load the PDF
319
+ this.currentLoadingTask = pdfjsLib.getDocument({ data: arrayBuffer });
320
+ pdf = await this.currentLoadingTask.promise;
321
+ this.currentLoadingTask = null;
322
+
323
+ // Clear container and add wrapper
324
+ container.innerHTML = "";
325
+ const wrapper = document.createElement("div");
326
+ wrapper.className = "";
327
+ container.appendChild(wrapper);
328
+
329
+ // Render all pages
330
+ for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) {
331
+ const page = await pdf.getPage(pageNum);
332
+
333
+ // Create a container for each page
334
+ const pageContainer = document.createElement("div");
335
+ pageContainer.className = "mb-4 last:mb-0";
336
+
337
+ // Create canvas for this page
338
+ const canvas = document.createElement("canvas");
339
+ const context = canvas.getContext("2d");
340
+
341
+ // Set scale for reasonable resolution
342
+ const viewport = page.getViewport({ scale: 1.5 });
343
+ canvas.height = viewport.height;
344
+ canvas.width = viewport.width;
345
+
346
+ // Style the canvas
347
+ canvas.className = "w-full max-w-full h-auto block mx-auto bg-white rounded shadow-sm border border-border";
348
+
349
+ // Fill white background for proper PDF rendering
350
+ if (context) {
351
+ context.fillStyle = "white";
352
+ context.fillRect(0, 0, canvas.width, canvas.height);
353
+ }
354
+
355
+ // Render page
356
+ await page.render({
357
+ canvasContext: context!,
358
+ viewport: viewport,
359
+ canvas: canvas,
360
+ }).promise;
361
+
362
+ pageContainer.appendChild(canvas);
363
+
364
+ // Add page separator for multi-page documents
365
+ if (pageNum < pdf.numPages) {
366
+ const separator = document.createElement("div");
367
+ separator.className = "h-px bg-border my-4";
368
+ pageContainer.appendChild(separator);
369
+ }
370
+
371
+ wrapper.appendChild(pageContainer);
372
+ }
373
+ } catch (error: any) {
374
+ console.error("Error rendering PDF:", error);
375
+ this.error = error?.message || i18n("Failed to load PDF");
376
+ } finally {
377
+ if (pdf) {
378
+ pdf.destroy();
379
+ }
380
+ }
381
+ }
382
+
383
+ private async renderDocx() {
384
+ const container = this.querySelector("#docx-container");
385
+ if (!container || !this.attachment) return;
386
+
387
+ try {
388
+ // Convert base64 to ArrayBuffer
389
+ const arrayBuffer = this.base64ToArrayBuffer(this.attachment.content);
390
+
391
+ // Clear container first
392
+ container.innerHTML = "";
393
+
394
+ // Create a wrapper div for the document
395
+ const wrapper = document.createElement("div");
396
+ wrapper.className = "docx-wrapper-custom";
397
+ container.appendChild(wrapper);
398
+
399
+ // Render the DOCX file into the wrapper
400
+ await renderAsync(arrayBuffer, wrapper as HTMLElement, undefined, {
401
+ className: "docx",
402
+ inWrapper: true,
403
+ ignoreWidth: true, // Let it be responsive
404
+ ignoreHeight: false,
405
+ ignoreFonts: false,
406
+ breakPages: true,
407
+ ignoreLastRenderedPageBreak: true,
408
+ experimental: false,
409
+ trimXmlDeclaration: true,
410
+ useBase64URL: false,
411
+ renderHeaders: true,
412
+ renderFooters: true,
413
+ renderFootnotes: true,
414
+ renderEndnotes: true,
415
+ });
416
+
417
+ // Apply custom styles to match theme and fix sizing
418
+ const style = document.createElement("style");
419
+ style.textContent = `
420
+ #docx-container {
421
+ padding: 0;
422
+ }
423
+
424
+ #docx-container .docx-wrapper-custom {
425
+ max-width: 100%;
426
+ overflow-x: auto;
427
+ }
428
+
429
+ #docx-container .docx-wrapper {
430
+ max-width: 100% !important;
431
+ margin: 0 !important;
432
+ background: transparent !important;
433
+ padding: 0em !important;
434
+ }
435
+
436
+ #docx-container .docx-wrapper > section.docx {
437
+ box-shadow: none !important;
438
+ border: none !important;
439
+ border-radius: 0 !important;
440
+ margin: 0 !important;
441
+ padding: 2em !important;
442
+ background: white !important;
443
+ color: black !important;
444
+ max-width: 100% !important;
445
+ width: 100% !important;
446
+ min-width: 0 !important;
447
+ overflow-x: auto !important;
448
+ }
449
+
450
+ /* Fix tables and wide content */
451
+ #docx-container table {
452
+ max-width: 100% !important;
453
+ width: auto !important;
454
+ overflow-x: auto !important;
455
+ display: block !important;
456
+ }
457
+
458
+ #docx-container img {
459
+ max-width: 100% !important;
460
+ height: auto !important;
461
+ }
462
+
463
+ /* Fix paragraphs and text */
464
+ #docx-container p,
465
+ #docx-container span,
466
+ #docx-container div {
467
+ max-width: 100% !important;
468
+ word-wrap: break-word !important;
469
+ overflow-wrap: break-word !important;
470
+ }
471
+
472
+ /* Hide page breaks in web view */
473
+ #docx-container .docx-page-break {
474
+ display: none !important;
475
+ }
476
+ `;
477
+ container.appendChild(style);
478
+ } catch (error: any) {
479
+ console.error("Error rendering DOCX:", error);
480
+ this.error = error?.message || i18n("Failed to load document");
481
+ }
482
+ }
483
+
484
+ private async renderExcel() {
485
+ const container = this.querySelector("#excel-container");
486
+ if (!container || !this.attachment) return;
487
+
488
+ try {
489
+ // Convert base64 to ArrayBuffer
490
+ const arrayBuffer = this.base64ToArrayBuffer(this.attachment.content);
491
+
492
+ // Read the workbook
493
+ const workbook = XLSX.read(arrayBuffer, { type: "array" });
494
+
495
+ // Clear container
496
+ container.innerHTML = "";
497
+ const wrapper = document.createElement("div");
498
+ wrapper.className = "overflow-auto h-full flex flex-col";
499
+ container.appendChild(wrapper);
500
+
501
+ // Create tabs for multiple sheets
502
+ if (workbook.SheetNames.length > 1) {
503
+ const tabContainer = document.createElement("div");
504
+ tabContainer.className = "flex gap-2 mb-4 border-b border-border sticky top-0 bg-card z-10";
505
+
506
+ const sheetContents: HTMLElement[] = [];
507
+
508
+ workbook.SheetNames.forEach((sheetName, index) => {
509
+ // Create tab button
510
+ const tab = document.createElement("button");
511
+ tab.textContent = sheetName;
512
+ tab.className =
513
+ index === 0
514
+ ? "px-4 py-2 text-sm font-medium border-b-2 border-primary text-primary"
515
+ : "px-4 py-2 text-sm font-medium text-muted-foreground hover:text-foreground hover:border-b-2 hover:border-border transition-colors";
516
+
517
+ // Create sheet content
518
+ const sheetDiv = document.createElement("div");
519
+ sheetDiv.style.display = index === 0 ? "flex" : "none";
520
+ sheetDiv.className = "flex-1 overflow-auto";
521
+ sheetDiv.appendChild(this.renderExcelSheet(workbook.Sheets[sheetName], sheetName));
522
+ sheetContents.push(sheetDiv);
523
+
524
+ // Tab click handler
525
+ tab.onclick = () => {
526
+ // Update tab styles
527
+ tabContainer.querySelectorAll("button").forEach((btn, btnIndex) => {
528
+ if (btnIndex === index) {
529
+ btn.className = "px-4 py-2 text-sm font-medium border-b-2 border-primary text-primary";
530
+ } else {
531
+ btn.className =
532
+ "px-4 py-2 text-sm font-medium text-muted-foreground hover:text-foreground hover:border-b-2 hover:border-border transition-colors";
533
+ }
534
+ });
535
+ // Show/hide sheets
536
+ sheetContents.forEach((content, contentIndex) => {
537
+ content.style.display = contentIndex === index ? "flex" : "none";
538
+ });
539
+ };
540
+
541
+ tabContainer.appendChild(tab);
542
+ });
543
+
544
+ wrapper.appendChild(tabContainer);
545
+ sheetContents.forEach((content) => {
546
+ wrapper.appendChild(content);
547
+ });
548
+ } else {
549
+ // Single sheet
550
+ const sheetName = workbook.SheetNames[0];
551
+ wrapper.appendChild(this.renderExcelSheet(workbook.Sheets[sheetName], sheetName));
552
+ }
553
+ } catch (error: any) {
554
+ console.error("Error rendering Excel:", error);
555
+ this.error = error?.message || i18n("Failed to load spreadsheet");
556
+ }
557
+ }
558
+
559
+ private renderExcelSheet(worksheet: any, sheetName: string): HTMLElement {
560
+ const sheetDiv = document.createElement("div");
561
+
562
+ // Generate HTML table
563
+ const htmlTable = XLSX.utils.sheet_to_html(worksheet, { id: `sheet-${sheetName}` });
564
+ const tempDiv = document.createElement("div");
565
+ tempDiv.innerHTML = htmlTable;
566
+
567
+ // Find and style the table
568
+ const table = tempDiv.querySelector("table");
569
+ if (table) {
570
+ table.className = "w-full border-collapse text-foreground";
571
+
572
+ // Style all cells
573
+ table.querySelectorAll("td, th").forEach((cell) => {
574
+ const cellEl = cell as HTMLElement;
575
+ cellEl.className = "border border-border px-3 py-2 text-sm text-left";
576
+ });
577
+
578
+ // Style header row
579
+ const headerCells = table.querySelectorAll("thead th, tr:first-child td");
580
+ if (headerCells.length > 0) {
581
+ headerCells.forEach((th) => {
582
+ const thEl = th as HTMLElement;
583
+ thEl.className =
584
+ "border border-border px-3 py-2 text-sm font-semibold bg-muted text-foreground sticky top-0";
585
+ });
586
+ }
587
+
588
+ // Alternate row colors
589
+ table.querySelectorAll("tbody tr:nth-child(even)").forEach((row) => {
590
+ const rowEl = row as HTMLElement;
591
+ rowEl.className = "bg-muted/30";
592
+ });
593
+
594
+ sheetDiv.appendChild(table);
595
+ }
596
+
597
+ return sheetDiv;
598
+ }
599
+
600
+ private base64ToArrayBuffer(base64: string): ArrayBuffer {
601
+ const binaryString = atob(base64);
602
+ const bytes = new Uint8Array(binaryString.length);
603
+ for (let i = 0; i < binaryString.length; i++) {
604
+ bytes[i] = binaryString.charCodeAt(i);
605
+ }
606
+ return bytes.buffer;
607
+ }
608
+
609
+ private async renderExtractedText() {
610
+ const container = this.querySelector("#pptx-container");
611
+ if (!container || !this.attachment) return;
612
+
613
+ try {
614
+ // Display the extracted text content
615
+ container.innerHTML = "";
616
+ const wrapper = document.createElement("div");
617
+ wrapper.className = "p-6 overflow-auto";
618
+
619
+ // Create a pre element to preserve formatting
620
+ const pre = document.createElement("pre");
621
+ pre.className = "whitespace-pre-wrap text-sm text-foreground font-mono";
622
+ pre.textContent = this.attachment.extractedText || i18n("No text content available");
623
+
624
+ wrapper.appendChild(pre);
625
+ container.appendChild(wrapper);
626
+ } catch (error: any) {
627
+ console.error("Error rendering extracted text:", error);
628
+ this.error = error?.message || i18n("Failed to display text content");
629
+ }
630
+ }
631
+ }
632
+
633
+ // Register the custom element only once
634
+ if (!customElements.get("attachment-overlay")) {
635
+ customElements.define("attachment-overlay", AttachmentOverlay);
636
+ }