@mariozechner/pi-web-ui 0.5.44 → 0.5.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (346) hide show
  1. package/README.md +178 -99
  2. package/dist/ChatPanel.d.ts +15 -10
  3. package/dist/ChatPanel.d.ts.map +1 -1
  4. package/dist/ChatPanel.js +68 -100
  5. package/dist/ChatPanel.js.map +1 -1
  6. package/dist/{state/agent-session.d.ts → agent/agent.d.ts} +23 -19
  7. package/dist/agent/agent.d.ts.map +1 -0
  8. package/dist/{state/agent-session.js → agent/agent.js} +50 -32
  9. package/dist/agent/agent.js.map +1 -0
  10. package/dist/{state → agent}/transports/AppTransport.d.ts +1 -3
  11. package/dist/agent/transports/AppTransport.d.ts.map +1 -0
  12. package/dist/{state → agent}/transports/AppTransport.js +5 -4
  13. package/dist/{state → agent}/transports/AppTransport.js.map +1 -1
  14. package/dist/{state → agent}/transports/ProviderTransport.d.ts +1 -3
  15. package/dist/agent/transports/ProviderTransport.d.ts.map +1 -0
  16. package/dist/{state → agent}/transports/ProviderTransport.js +6 -7
  17. package/dist/agent/transports/ProviderTransport.js.map +1 -0
  18. package/dist/{state → agent}/transports/index.d.ts.map +1 -1
  19. package/dist/agent/transports/index.js.map +1 -0
  20. package/dist/{state → agent}/transports/proxy-types.d.ts.map +1 -1
  21. package/dist/agent/transports/proxy-types.js.map +1 -0
  22. package/dist/agent/transports/types.d.ts +12 -0
  23. package/dist/agent/transports/types.d.ts.map +1 -0
  24. package/dist/{state → agent}/transports/types.js.map +1 -1
  25. package/dist/{state → agent}/types.d.ts.map +1 -1
  26. package/dist/{state → agent}/types.js.map +1 -1
  27. package/dist/app.css +1 -1
  28. package/dist/components/AgentInterface.d.ts +7 -4
  29. package/dist/components/AgentInterface.d.ts.map +1 -1
  30. package/dist/components/AgentInterface.js +29 -17
  31. package/dist/components/AgentInterface.js.map +1 -1
  32. package/dist/components/ConsoleBlock.d.ts +1 -0
  33. package/dist/components/ConsoleBlock.d.ts.map +1 -1
  34. package/dist/components/ConsoleBlock.js +7 -1
  35. package/dist/components/ConsoleBlock.js.map +1 -1
  36. package/dist/components/ExpandableSection.d.ts +15 -0
  37. package/dist/components/ExpandableSection.d.ts.map +1 -0
  38. package/dist/components/ExpandableSection.js +63 -0
  39. package/dist/components/ExpandableSection.js.map +1 -0
  40. package/dist/components/MessageEditor.d.ts +8 -1
  41. package/dist/components/MessageEditor.d.ts.map +1 -1
  42. package/dist/components/MessageEditor.js +149 -6
  43. package/dist/components/MessageEditor.js.map +1 -1
  44. package/dist/components/MessageList.d.ts +3 -2
  45. package/dist/components/MessageList.d.ts.map +1 -1
  46. package/dist/components/MessageList.js +14 -1
  47. package/dist/components/MessageList.js.map +1 -1
  48. package/dist/components/Messages.d.ts +15 -6
  49. package/dist/components/Messages.d.ts.map +1 -1
  50. package/dist/components/Messages.js +17 -83
  51. package/dist/components/Messages.js.map +1 -1
  52. package/dist/components/ProviderKeyInput.d.ts.map +1 -1
  53. package/dist/components/ProviderKeyInput.js +6 -5
  54. package/dist/components/ProviderKeyInput.js.map +1 -1
  55. package/dist/components/SandboxedIframe.d.ts +29 -7
  56. package/dist/components/SandboxedIframe.d.ts.map +1 -1
  57. package/dist/components/SandboxedIframe.js +350 -282
  58. package/dist/components/SandboxedIframe.js.map +1 -1
  59. package/dist/components/message-renderer-registry.d.ts +12 -0
  60. package/dist/components/message-renderer-registry.d.ts.map +1 -0
  61. package/dist/components/message-renderer-registry.js +12 -0
  62. package/dist/components/message-renderer-registry.js.map +1 -0
  63. package/dist/components/sandbox/ArtifactsRuntimeProvider.d.ts +35 -0
  64. package/dist/components/sandbox/ArtifactsRuntimeProvider.d.ts.map +1 -0
  65. package/dist/components/sandbox/ArtifactsRuntimeProvider.js +189 -0
  66. package/dist/components/sandbox/ArtifactsRuntimeProvider.js.map +1 -0
  67. package/dist/components/sandbox/AttachmentsRuntimeProvider.d.ts +17 -0
  68. package/dist/components/sandbox/AttachmentsRuntimeProvider.d.ts.map +1 -0
  69. package/dist/components/sandbox/AttachmentsRuntimeProvider.js +64 -0
  70. package/dist/components/sandbox/AttachmentsRuntimeProvider.js.map +1 -0
  71. package/dist/components/sandbox/ConsoleRuntimeProvider.d.ts +42 -0
  72. package/dist/components/sandbox/ConsoleRuntimeProvider.d.ts.map +1 -0
  73. package/dist/components/sandbox/ConsoleRuntimeProvider.js +161 -0
  74. package/dist/components/sandbox/ConsoleRuntimeProvider.js.map +1 -0
  75. package/dist/components/sandbox/FileDownloadRuntimeProvider.d.ts +30 -0
  76. package/dist/components/sandbox/FileDownloadRuntimeProvider.d.ts.map +1 -0
  77. package/dist/components/sandbox/FileDownloadRuntimeProvider.js +97 -0
  78. package/dist/components/sandbox/FileDownloadRuntimeProvider.js.map +1 -0
  79. package/dist/components/sandbox/RuntimeMessageBridge.d.ts +19 -0
  80. package/dist/components/sandbox/RuntimeMessageBridge.d.ts.map +1 -0
  81. package/dist/components/sandbox/RuntimeMessageBridge.js +74 -0
  82. package/dist/components/sandbox/RuntimeMessageBridge.js.map +1 -0
  83. package/dist/components/sandbox/RuntimeMessageRouter.d.ts +65 -0
  84. package/dist/components/sandbox/RuntimeMessageRouter.d.ts.map +1 -0
  85. package/dist/components/sandbox/RuntimeMessageRouter.js +168 -0
  86. package/dist/components/sandbox/RuntimeMessageRouter.js.map +1 -0
  87. package/dist/components/sandbox/SandboxRuntimeProvider.d.ts +33 -0
  88. package/dist/components/sandbox/SandboxRuntimeProvider.d.ts.map +1 -0
  89. package/dist/components/sandbox/SandboxRuntimeProvider.js +2 -0
  90. package/dist/components/sandbox/SandboxRuntimeProvider.js.map +1 -0
  91. package/dist/dialogs/ApiKeyPromptDialog.d.ts.map +1 -1
  92. package/dist/dialogs/ApiKeyPromptDialog.js +2 -5
  93. package/dist/dialogs/ApiKeyPromptDialog.js.map +1 -1
  94. package/dist/dialogs/ModelSelector.js.map +1 -1
  95. package/dist/dialogs/PersistentStorageDialog.d.ts +17 -0
  96. package/dist/dialogs/PersistentStorageDialog.d.ts.map +1 -0
  97. package/dist/dialogs/PersistentStorageDialog.js +144 -0
  98. package/dist/dialogs/PersistentStorageDialog.js.map +1 -0
  99. package/dist/dialogs/SessionListDialog.d.ts +19 -0
  100. package/dist/dialogs/SessionListDialog.d.ts.map +1 -0
  101. package/dist/dialogs/SessionListDialog.js +152 -0
  102. package/dist/dialogs/SessionListDialog.js.map +1 -0
  103. package/dist/dialogs/SettingsDialog.d.ts.map +1 -1
  104. package/dist/dialogs/SettingsDialog.js +1 -0
  105. package/dist/dialogs/SettingsDialog.js.map +1 -1
  106. package/dist/index.d.ts +34 -16
  107. package/dist/index.d.ts.map +1 -1
  108. package/dist/index.js +32 -14
  109. package/dist/index.js.map +1 -1
  110. package/dist/prompts/prompts.d.ts +11 -0
  111. package/dist/prompts/prompts.d.ts.map +1 -0
  112. package/dist/prompts/prompts.js +272 -0
  113. package/dist/prompts/prompts.js.map +1 -0
  114. package/dist/storage/app-storage.d.ts +17 -12
  115. package/dist/storage/app-storage.d.ts.map +1 -1
  116. package/dist/storage/app-storage.js +13 -20
  117. package/dist/storage/app-storage.js.map +1 -1
  118. package/dist/storage/backends/indexeddb-storage-backend.d.ts +27 -0
  119. package/dist/storage/backends/indexeddb-storage-backend.d.ts.map +1 -0
  120. package/dist/storage/backends/indexeddb-storage-backend.js +166 -0
  121. package/dist/storage/backends/indexeddb-storage-backend.js.map +1 -0
  122. package/dist/storage/store.d.ts +23 -0
  123. package/dist/storage/store.d.ts.map +1 -0
  124. package/dist/storage/store.js +26 -0
  125. package/dist/storage/store.js.map +1 -0
  126. package/dist/storage/stores/provider-keys-store.d.ts +14 -0
  127. package/dist/storage/stores/provider-keys-store.d.ts.map +1 -0
  128. package/dist/storage/stores/provider-keys-store.js +27 -0
  129. package/dist/storage/stores/provider-keys-store.js.map +1 -0
  130. package/dist/storage/stores/sessions-store.d.ts +31 -0
  131. package/dist/storage/stores/sessions-store.d.ts.map +1 -0
  132. package/dist/storage/stores/sessions-store.js +113 -0
  133. package/dist/storage/stores/sessions-store.js.map +1 -0
  134. package/dist/storage/stores/settings-store.d.ts +14 -0
  135. package/dist/storage/stores/settings-store.d.ts.map +1 -0
  136. package/dist/storage/stores/settings-store.js +28 -0
  137. package/dist/storage/stores/settings-store.js.map +1 -0
  138. package/dist/storage/types.d.ts +156 -22
  139. package/dist/storage/types.d.ts.map +1 -1
  140. package/dist/tools/artifacts/ArtifactElement.d.ts +0 -1
  141. package/dist/tools/artifacts/ArtifactElement.d.ts.map +1 -1
  142. package/dist/tools/artifacts/ArtifactElement.js +0 -1
  143. package/dist/tools/artifacts/ArtifactElement.js.map +1 -1
  144. package/dist/tools/artifacts/ArtifactPill.d.ts +4 -0
  145. package/dist/tools/artifacts/ArtifactPill.d.ts.map +1 -0
  146. package/dist/tools/artifacts/ArtifactPill.js +22 -0
  147. package/dist/tools/artifacts/ArtifactPill.js.map +1 -0
  148. package/dist/tools/artifacts/Console.d.ts +18 -0
  149. package/dist/tools/artifacts/Console.d.ts.map +1 -0
  150. package/dist/tools/artifacts/Console.js +95 -0
  151. package/dist/tools/artifacts/Console.js.map +1 -0
  152. package/dist/tools/artifacts/DocxArtifact.d.ts +22 -0
  153. package/dist/tools/artifacts/DocxArtifact.d.ts.map +1 -0
  154. package/dist/tools/artifacts/DocxArtifact.js +208 -0
  155. package/dist/tools/artifacts/DocxArtifact.js.map +1 -0
  156. package/dist/tools/artifacts/ExcelArtifact.d.ts +24 -0
  157. package/dist/tools/artifacts/ExcelArtifact.d.ts.map +1 -0
  158. package/dist/tools/artifacts/ExcelArtifact.js +216 -0
  159. package/dist/tools/artifacts/ExcelArtifact.js.map +1 -0
  160. package/dist/tools/artifacts/GenericArtifact.d.ts +19 -0
  161. package/dist/tools/artifacts/GenericArtifact.d.ts.map +1 -0
  162. package/dist/tools/artifacts/GenericArtifact.js +117 -0
  163. package/dist/tools/artifacts/GenericArtifact.js.map +1 -0
  164. package/dist/tools/artifacts/HtmlArtifact.d.ts +8 -11
  165. package/dist/tools/artifacts/HtmlArtifact.d.ts.map +1 -1
  166. package/dist/tools/artifacts/HtmlArtifact.js +56 -88
  167. package/dist/tools/artifacts/HtmlArtifact.js.map +1 -1
  168. package/dist/tools/artifacts/ImageArtifact.d.ts +20 -0
  169. package/dist/tools/artifacts/ImageArtifact.d.ts.map +1 -0
  170. package/dist/tools/artifacts/ImageArtifact.js +120 -0
  171. package/dist/tools/artifacts/ImageArtifact.js.map +1 -0
  172. package/dist/tools/artifacts/MarkdownArtifact.d.ts +0 -1
  173. package/dist/tools/artifacts/MarkdownArtifact.d.ts.map +1 -1
  174. package/dist/tools/artifacts/MarkdownArtifact.js +0 -4
  175. package/dist/tools/artifacts/MarkdownArtifact.js.map +1 -1
  176. package/dist/tools/artifacts/PdfArtifact.d.ts +25 -0
  177. package/dist/tools/artifacts/PdfArtifact.d.ts.map +1 -0
  178. package/dist/tools/artifacts/PdfArtifact.js +184 -0
  179. package/dist/tools/artifacts/PdfArtifact.js.map +1 -0
  180. package/dist/tools/artifacts/SvgArtifact.d.ts +0 -1
  181. package/dist/tools/artifacts/SvgArtifact.d.ts.map +1 -1
  182. package/dist/tools/artifacts/SvgArtifact.js +0 -4
  183. package/dist/tools/artifacts/SvgArtifact.js.map +1 -1
  184. package/dist/tools/artifacts/TextArtifact.d.ts +0 -1
  185. package/dist/tools/artifacts/TextArtifact.d.ts.map +1 -1
  186. package/dist/tools/artifacts/TextArtifact.js +0 -4
  187. package/dist/tools/artifacts/TextArtifact.js.map +1 -1
  188. package/dist/tools/artifacts/artifacts-tool-renderer.d.ts +11 -0
  189. package/dist/tools/artifacts/artifacts-tool-renderer.d.ts.map +1 -0
  190. package/dist/tools/artifacts/artifacts-tool-renderer.js +262 -0
  191. package/dist/tools/artifacts/artifacts-tool-renderer.js.map +1 -0
  192. package/dist/tools/artifacts/artifacts.d.ts +10 -13
  193. package/dist/tools/artifacts/artifacts.d.ts.map +1 -1
  194. package/dist/tools/artifacts/artifacts.js +166 -344
  195. package/dist/tools/artifacts/artifacts.js.map +1 -1
  196. package/dist/tools/artifacts/index.d.ts +1 -0
  197. package/dist/tools/artifacts/index.d.ts.map +1 -1
  198. package/dist/tools/artifacts/index.js +1 -0
  199. package/dist/tools/artifacts/index.js.map +1 -1
  200. package/dist/tools/extract-document.d.ts +24 -0
  201. package/dist/tools/extract-document.d.ts.map +1 -0
  202. package/dist/tools/extract-document.js +193 -0
  203. package/dist/tools/extract-document.js.map +1 -0
  204. package/dist/tools/index.d.ts +9 -7
  205. package/dist/tools/index.d.ts.map +1 -1
  206. package/dist/tools/index.js +17 -13
  207. package/dist/tools/index.js.map +1 -1
  208. package/dist/tools/javascript-repl.d.ts +16 -15
  209. package/dist/tools/javascript-repl.d.ts.map +1 -1
  210. package/dist/tools/javascript-repl.js +101 -133
  211. package/dist/tools/javascript-repl.js.map +1 -1
  212. package/dist/tools/renderer-registry.d.ts +12 -0
  213. package/dist/tools/renderer-registry.d.ts.map +1 -1
  214. package/dist/tools/renderer-registry.js +78 -0
  215. package/dist/tools/renderer-registry.js.map +1 -1
  216. package/dist/tools/renderers/BashRenderer.d.ts +2 -4
  217. package/dist/tools/renderers/BashRenderer.d.ts.map +1 -1
  218. package/dist/tools/renderers/BashRenderer.js +30 -26
  219. package/dist/tools/renderers/BashRenderer.js.map +1 -1
  220. package/dist/tools/renderers/CalculateRenderer.d.ts +2 -4
  221. package/dist/tools/renderers/CalculateRenderer.d.ts.map +1 -1
  222. package/dist/tools/renderers/CalculateRenderer.js +32 -28
  223. package/dist/tools/renderers/CalculateRenderer.js.map +1 -1
  224. package/dist/tools/renderers/DefaultRenderer.d.ts +2 -4
  225. package/dist/tools/renderers/DefaultRenderer.d.ts.map +1 -1
  226. package/dist/tools/renderers/DefaultRenderer.js +78 -18
  227. package/dist/tools/renderers/DefaultRenderer.js.map +1 -1
  228. package/dist/tools/renderers/GetCurrentTimeRenderer.d.ts +2 -4
  229. package/dist/tools/renderers/GetCurrentTimeRenderer.d.ts.map +1 -1
  230. package/dist/tools/renderers/GetCurrentTimeRenderer.js +57 -21
  231. package/dist/tools/renderers/GetCurrentTimeRenderer.js.map +1 -1
  232. package/dist/tools/types.d.ts +5 -2
  233. package/dist/tools/types.d.ts.map +1 -1
  234. package/dist/utils/i18n.d.ts +424 -1
  235. package/dist/utils/i18n.d.ts.map +1 -1
  236. package/dist/utils/i18n.js +131 -7
  237. package/dist/utils/i18n.js.map +1 -1
  238. package/example/package.json +2 -1
  239. package/example/src/custom-messages.ts +112 -0
  240. package/example/src/main.ts +391 -38
  241. package/package.json +48 -43
  242. package/scripts/count-prompt-tokens.ts +88 -0
  243. package/src/ChatPanel.ts +93 -101
  244. package/src/{state/agent-session.ts → agent/agent.ts} +80 -55
  245. package/src/{state → agent}/transports/AppTransport.ts +6 -6
  246. package/src/{state → agent}/transports/ProviderTransport.ts +13 -7
  247. package/src/{state → agent}/transports/types.ts +8 -2
  248. package/src/components/AgentInterface.ts +32 -16
  249. package/src/components/ConsoleBlock.ts +5 -1
  250. package/src/components/ExpandableSection.ts +46 -0
  251. package/src/components/MessageEditor.ts +159 -5
  252. package/src/components/MessageList.ts +18 -3
  253. package/src/components/Messages.ts +48 -89
  254. package/src/components/ProviderKeyInput.ts +6 -5
  255. package/src/components/SandboxedIframe.ts +412 -321
  256. package/src/components/message-renderer-registry.ts +28 -0
  257. package/src/components/sandbox/ArtifactsRuntimeProvider.ts +219 -0
  258. package/src/components/sandbox/AttachmentsRuntimeProvider.ts +66 -0
  259. package/src/components/sandbox/ConsoleRuntimeProvider.ts +187 -0
  260. package/src/components/sandbox/FileDownloadRuntimeProvider.ts +110 -0
  261. package/src/components/sandbox/RuntimeMessageBridge.ts +82 -0
  262. package/src/components/sandbox/RuntimeMessageRouter.ts +216 -0
  263. package/src/components/sandbox/SandboxRuntimeProvider.ts +35 -0
  264. package/src/dialogs/ApiKeyPromptDialog.ts +2 -5
  265. package/src/dialogs/ModelSelector.ts +2 -2
  266. package/src/dialogs/PersistentStorageDialog.ts +141 -0
  267. package/src/dialogs/SessionListDialog.ts +148 -0
  268. package/src/dialogs/SettingsDialog.ts +1 -0
  269. package/src/index.ts +61 -20
  270. package/src/prompts/prompts.ts +282 -0
  271. package/src/storage/app-storage.ts +27 -24
  272. package/src/storage/backends/indexeddb-storage-backend.ts +193 -0
  273. package/src/storage/store.ts +33 -0
  274. package/src/storage/stores/provider-keys-store.ts +33 -0
  275. package/src/storage/stores/sessions-store.ts +130 -0
  276. package/src/storage/stores/settings-store.ts +34 -0
  277. package/src/storage/types.ts +182 -22
  278. package/src/tools/artifacts/ArtifactElement.ts +0 -1
  279. package/src/tools/artifacts/ArtifactPill.ts +25 -0
  280. package/src/tools/artifacts/Console.ts +93 -0
  281. package/src/tools/artifacts/DocxArtifact.ts +213 -0
  282. package/src/tools/artifacts/ExcelArtifact.ts +231 -0
  283. package/src/tools/artifacts/GenericArtifact.ts +117 -0
  284. package/src/tools/artifacts/HtmlArtifact.ts +64 -94
  285. package/src/tools/artifacts/ImageArtifact.ts +116 -0
  286. package/src/tools/artifacts/MarkdownArtifact.ts +0 -1
  287. package/src/tools/artifacts/PdfArtifact.ts +201 -0
  288. package/src/tools/artifacts/SvgArtifact.ts +0 -1
  289. package/src/tools/artifacts/TextArtifact.ts +0 -1
  290. package/src/tools/artifacts/artifacts-tool-renderer.ts +298 -0
  291. package/src/tools/artifacts/artifacts.ts +190 -366
  292. package/src/tools/artifacts/index.ts +1 -0
  293. package/src/tools/extract-document.ts +250 -0
  294. package/src/tools/index.ts +25 -14
  295. package/src/tools/javascript-repl.ts +138 -160
  296. package/src/tools/renderer-registry.ts +98 -0
  297. package/src/tools/renderers/BashRenderer.ts +33 -30
  298. package/src/tools/renderers/CalculateRenderer.ts +36 -31
  299. package/src/tools/renderers/DefaultRenderer.ts +84 -21
  300. package/src/tools/renderers/GetCurrentTimeRenderer.ts +68 -23
  301. package/src/tools/types.ts +10 -2
  302. package/src/utils/i18n.ts +203 -8
  303. package/dist/state/agent-session.d.ts.map +0 -1
  304. package/dist/state/agent-session.js.map +0 -1
  305. package/dist/state/transports/AppTransport.d.ts.map +0 -1
  306. package/dist/state/transports/ProviderTransport.d.ts.map +0 -1
  307. package/dist/state/transports/ProviderTransport.js.map +0 -1
  308. package/dist/state/transports/index.js.map +0 -1
  309. package/dist/state/transports/proxy-types.js.map +0 -1
  310. package/dist/state/transports/types.d.ts +0 -11
  311. package/dist/state/transports/types.d.ts.map +0 -1
  312. package/dist/storage/backends/chrome-storage-backend.d.ts +0 -18
  313. package/dist/storage/backends/chrome-storage-backend.d.ts.map +0 -1
  314. package/dist/storage/backends/chrome-storage-backend.js +0 -67
  315. package/dist/storage/backends/chrome-storage-backend.js.map +0 -1
  316. package/dist/storage/backends/indexeddb-backend.d.ts +0 -20
  317. package/dist/storage/backends/indexeddb-backend.d.ts.map +0 -1
  318. package/dist/storage/backends/indexeddb-backend.js +0 -89
  319. package/dist/storage/backends/indexeddb-backend.js.map +0 -1
  320. package/dist/storage/backends/local-storage-backend.d.ts +0 -18
  321. package/dist/storage/backends/local-storage-backend.d.ts.map +0 -1
  322. package/dist/storage/backends/local-storage-backend.js +0 -69
  323. package/dist/storage/backends/local-storage-backend.js.map +0 -1
  324. package/dist/storage/repositories/provider-keys-repository.d.ts +0 -34
  325. package/dist/storage/repositories/provider-keys-repository.d.ts.map +0 -1
  326. package/dist/storage/repositories/provider-keys-repository.js +0 -50
  327. package/dist/storage/repositories/provider-keys-repository.js.map +0 -1
  328. package/dist/storage/repositories/settings-repository.d.ts +0 -34
  329. package/dist/storage/repositories/settings-repository.d.ts.map +0 -1
  330. package/dist/storage/repositories/settings-repository.js +0 -46
  331. package/dist/storage/repositories/settings-repository.js.map +0 -1
  332. package/src/storage/backends/chrome-storage-backend.ts +0 -82
  333. package/src/storage/backends/indexeddb-backend.ts +0 -107
  334. package/src/storage/backends/local-storage-backend.ts +0 -74
  335. package/src/storage/repositories/provider-keys-repository.ts +0 -55
  336. package/src/storage/repositories/settings-repository.ts +0 -51
  337. /package/dist/{state → agent}/transports/index.d.ts +0 -0
  338. /package/dist/{state → agent}/transports/index.js +0 -0
  339. /package/dist/{state → agent}/transports/proxy-types.d.ts +0 -0
  340. /package/dist/{state → agent}/transports/proxy-types.js +0 -0
  341. /package/dist/{state → agent}/transports/types.js +0 -0
  342. /package/dist/{state → agent}/types.d.ts +0 -0
  343. /package/dist/{state → agent}/types.js +0 -0
  344. /package/src/{state → agent}/transports/index.ts +0 -0
  345. /package/src/{state → agent}/transports/proxy-types.ts +0 -0
  346. /package/src/{state → agent}/types.ts +0 -0
@@ -1,16 +1,19 @@
1
- import { html, i18n, type TemplateResult } from "@mariozechner/mini-lit";
1
+ import { html, i18n } from "@mariozechner/mini-lit";
2
2
  import type { AgentTool, ToolResultMessage } from "@mariozechner/pi-ai";
3
3
  import { type Static, Type } from "@sinclair/typebox";
4
+ import { createRef, ref } from "lit/directives/ref.js";
5
+ import { Code } from "lucide";
4
6
  import { type SandboxFile, SandboxIframe, type SandboxResult } from "../components/SandboxedIframe.js";
7
+ import type { SandboxRuntimeProvider } from "../components/sandbox/SandboxRuntimeProvider.js";
8
+ import { JAVASCRIPT_REPL_TOOL_DESCRIPTION } from "../prompts/prompts.js";
5
9
  import type { Attachment } from "../utils/attachment-utils.js";
6
-
7
- import { registerToolRenderer } from "./renderer-registry.js";
8
- import type { ToolRenderer } from "./types.js";
10
+ import { registerToolRenderer, renderCollapsibleHeader, renderHeader } from "./renderer-registry.js";
11
+ import type { ToolRenderer, ToolRenderResult } from "./types.js";
9
12
 
10
13
  // Execute JavaScript code with attachments using SandboxedIframe
11
14
  export async function executeJavaScript(
12
15
  code: string,
13
- attachments: Attachment[] = [],
16
+ runtimeProviders: SandboxRuntimeProvider[],
14
17
  signal?: AbortSignal,
15
18
  sandboxUrlProvider?: () => string,
16
19
  ): Promise<{ output: string; files?: SandboxFile[] }> {
@@ -32,31 +35,40 @@ export async function executeJavaScript(
32
35
  document.body.appendChild(sandbox);
33
36
 
34
37
  try {
35
- const sandboxId = `repl-${Date.now()}`;
36
- const result: SandboxResult = await sandbox.execute(sandboxId, code, attachments, signal);
38
+ const sandboxId = `repl-${Date.now()}-${Math.random().toString(36).substring(7)}`;
39
+
40
+ // Pass providers to execute (router handles all message routing)
41
+ // No additional consumers needed - execute() has its own internal consumer
42
+ const result: SandboxResult = await sandbox.execute(sandboxId, code, runtimeProviders, [], signal);
37
43
 
38
44
  // Remove the sandbox iframe after execution
39
45
  sandbox.remove();
40
46
 
41
- // Return plain text output
42
- if (!result.success) {
43
- // Return error as plain text
44
- return {
45
- output: `Error: ${result.error?.message || "Unknown error"}\n${result.error?.stack || ""}`,
46
- };
47
- }
48
-
49
47
  // Build plain text response
50
48
  let output = "";
51
49
 
52
50
  // Add console output - result.console contains { type: string, text: string } from sandbox.js
53
51
  if (result.console && result.console.length > 0) {
54
52
  for (const entry of result.console) {
55
- const prefix = entry.type === "error" ? "[ERROR]" : "";
56
- output += (prefix ? `${prefix} ` : "") + entry.text + "\n";
53
+ output += entry.text + "\n";
57
54
  }
58
55
  }
59
56
 
57
+ // Add error if execution failed
58
+ if (!result.success) {
59
+ if (output) output += "\n";
60
+ output += `Error: ${result.error?.message || "Unknown error"}\n${result.error?.stack || ""}`;
61
+
62
+ // Throw error so tool call is marked as failed
63
+ throw new Error(output.trim());
64
+ }
65
+
66
+ // Add return value if present
67
+ if (result.returnValue !== undefined) {
68
+ if (output) output += "\n";
69
+ output += `=> ${typeof result.returnValue === "object" ? JSON.stringify(result.returnValue, null, 2) : result.returnValue}`;
70
+ }
71
+
60
72
  // Add file notifications
61
73
  if (result.files && result.files.length > 0) {
62
74
  output += `\n[Files returned: ${result.files.length}]\n`;
@@ -92,94 +104,49 @@ export type JavaScriptReplToolResult = {
92
104
  };
93
105
 
94
106
  const javascriptReplSchema = Type.Object({
107
+ title: Type.String({
108
+ description:
109
+ "Brief title describing what the code snippet tries to achieve in active form, e.g. 'Calculating sum'",
110
+ }),
95
111
  code: Type.String({ description: "JavaScript code to execute" }),
96
112
  });
97
113
 
114
+ export type JavaScriptReplParams = Static<typeof javascriptReplSchema>;
115
+
116
+ interface JavaScriptReplResult {
117
+ output?: string;
118
+ files?: Array<{
119
+ fileName: string;
120
+ mimeType: string;
121
+ size: number;
122
+ contentBase64: string;
123
+ }>;
124
+ }
125
+
98
126
  export function createJavaScriptReplTool(): AgentTool<typeof javascriptReplSchema, JavaScriptReplToolResult> & {
99
- attachmentsProvider?: () => Attachment[];
127
+ runtimeProvidersFactory?: () => SandboxRuntimeProvider[];
100
128
  sandboxUrlProvider?: () => string;
101
129
  } {
102
130
  return {
103
131
  label: "JavaScript REPL",
104
132
  name: "javascript_repl",
105
- attachmentsProvider: () => [], // default to empty array
133
+ runtimeProvidersFactory: () => [], // default to empty array
106
134
  sandboxUrlProvider: undefined, // optional, for browser extensions
107
- description: `Execute JavaScript code in a sandboxed browser environment with full modern browser capabilities.
108
-
109
- Environment: Modern browser with ALL Web APIs available:
110
- - ES2023+ JavaScript (async/await, optional chaining, nullish coalescing, etc.)
111
- - DOM APIs (document, window, Canvas, WebGL, etc.)
112
- - Fetch API for HTTP requests
113
-
114
- Loading external libraries via dynamic imports (use esm.run):
115
- - XLSX (Excel files): const XLSX = await import('https://esm.run/xlsx');
116
- - Papa Parse (CSV): const Papa = (await import('https://esm.run/papaparse')).default;
117
- - Lodash: const _ = await import('https://esm.run/lodash-es');
118
- - D3.js: const d3 = await import('https://esm.run/d3');
119
- - Chart.js: const Chart = (await import('https://esm.run/chart.js/auto')).default;
120
- - Three.js: const THREE = await import('https://esm.run/three');
121
- - Any npm package: await import('https://esm.run/package-name')
122
-
123
- IMPORTANT for graphics/canvas:
124
- - Use fixed dimensions like 400x400 or 800x600, NOT window.innerWidth/Height
125
- - For Three.js: renderer.setSize(400, 400) and camera aspect ratio of 1
126
- - For Chart.js: Set options: { responsive: false, animation: false } to ensure immediate rendering
127
- - Web Storage (localStorage, sessionStorage, IndexedDB)
128
- - Web Workers, WebAssembly, WebSockets
129
- - Media APIs (Audio, Video, WebRTC)
130
- - File APIs (Blob, FileReader, etc.)
131
- - Crypto API for cryptography
132
- - And much more - anything a modern browser supports!
133
-
134
- Output:
135
- - console.log() - All output is captured as text
136
- - await returnFile(filename, content, mimeType?) - Create downloadable files (async function!)
137
- * Always use await with returnFile
138
- * REQUIRED: For Blob/Uint8Array binary content, you MUST supply a proper MIME type (e.g., "image/png").
139
- If omitted, the REPL throws an Error with stack trace pointing to the offending line.
140
- * Strings without a MIME default to text/plain.
141
- * Objects are auto-JSON stringified and default to application/json unless a MIME is provided.
142
- * Canvas images: Use toBlob() with await Promise wrapper
143
- * Examples:
144
- - await returnFile('data.txt', 'Hello World', 'text/plain')
145
- - await returnFile('data.json', {key: 'value'}, 'application/json')
146
- - await returnFile('data.csv', 'name,age\\nJohn,30', 'text/csv')
147
- - Chart.js example:
148
- const Chart = (await import('https://esm.run/chart.js/auto')).default;
149
- const canvas = document.createElement('canvas');
150
- canvas.width = 400; canvas.height = 300;
151
- document.body.appendChild(canvas);
152
- new Chart(canvas, {
153
- type: 'line',
154
- data: {
155
- labels: ['Jan', 'Feb', 'Mar', 'Apr'],
156
- datasets: [{ label: 'Sales', data: [10, 20, 15, 25], borderColor: 'blue' }]
157
- },
158
- options: { responsive: false, animation: false }
159
- });
160
- const blob = await new Promise(resolve => canvas.toBlob(resolve, 'image/png'));
161
- await returnFile('chart.png', blob, 'image/png');
162
-
163
- Global variables:
164
- - attachments[] - Array of attachment objects from user messages
165
- * Properties:
166
- - id: string (unique identifier)
167
- - fileName: string (e.g., "data.xlsx")
168
- - mimeType: string (e.g., "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
169
- - size: number (bytes)
170
- * Helper functions:
171
- - listFiles() - Returns array of {id, fileName, mimeType, size} for all attachments
172
- - readTextFile(attachmentId) - Returns text content of attachment (for CSV, JSON, text files)
173
- - readBinaryFile(attachmentId) - Returns Uint8Array of binary data (for images, Excel, etc.)
174
- * Examples:
175
- - const files = listFiles();
176
- - const csvContent = readTextFile(files[0].id); // Read CSV as text
177
- - const xlsxBytes = readBinaryFile(files[0].id); // Read Excel as binary
178
- - All standard browser globals (window, document, fetch, etc.)`,
135
+ get description() {
136
+ const runtimeProviderDescriptions =
137
+ this.runtimeProvidersFactory?.()
138
+ .map((d) => d.getDescription())
139
+ .filter((d) => d.trim().length > 0) || [];
140
+ return JAVASCRIPT_REPL_TOOL_DESCRIPTION(runtimeProviderDescriptions);
141
+ },
179
142
  parameters: javascriptReplSchema,
180
143
  execute: async function (_toolCallId: string, args: Static<typeof javascriptReplSchema>, signal?: AbortSignal) {
181
- const attachments = this.attachmentsProvider?.() || [];
182
- const result = await executeJavaScript(args.code, attachments, signal, this.sandboxUrlProvider);
144
+ const result = await executeJavaScript(
145
+ args.code,
146
+ this.runtimeProvidersFactory?.() ?? [],
147
+ signal,
148
+ this.sandboxUrlProvider,
149
+ );
183
150
  // Convert files to JSON-serializable with base64 payloads
184
151
  const files = (result.files || []).map((f) => {
185
152
  const toBase64 = (input: string | Uint8Array): { base64: string; size: number } => {
@@ -228,80 +195,91 @@ Global variables:
228
195
  // Export a default instance for backward compatibility
229
196
  export const javascriptReplTool = createJavaScriptReplTool();
230
197
 
231
- // JavaScript REPL renderer with streaming support
232
-
233
- interface JavaScriptReplParams {
234
- code: string;
235
- }
236
-
237
- interface JavaScriptReplResult {
238
- output?: string;
239
- files?: Array<{
240
- fileName: string;
241
- mimeType: string;
242
- size: number;
243
- contentBase64: string;
244
- }>;
245
- }
246
-
247
198
  export const javascriptReplRenderer: ToolRenderer<JavaScriptReplParams, JavaScriptReplResult> = {
248
- renderParams(params: JavaScriptReplParams, isStreaming?: boolean): TemplateResult {
249
- if (isStreaming && (!params.code || params.code.length === 0)) {
250
- return html`<div class="text-sm text-muted-foreground">Writing JavaScript code...</div>`;
251
- }
252
-
253
- return html`
254
- <div class="text-sm text-muted-foreground mb-2">${i18n("Executing JavaScript")}</div>
255
- <code-block .code=${params.code || ""} language="javascript"></code-block>
256
- `;
257
- },
258
-
259
- renderResult(_params: JavaScriptReplParams, result: ToolResultMessage<JavaScriptReplResult>): TemplateResult {
260
- // Console output is in the main output field, files are in details
261
- const output = result.output || "";
262
- const files = result.details?.files || [];
199
+ render(
200
+ params: JavaScriptReplParams | undefined,
201
+ result: ToolResultMessage<JavaScriptReplResult> | undefined,
202
+ isStreaming?: boolean,
203
+ ): ToolRenderResult {
204
+ // Determine status
205
+ const state = result ? (result.isError ? "error" : "complete") : isStreaming ? "inprogress" : "complete";
206
+
207
+ // Create refs for collapsible code section
208
+ const codeContentRef = createRef<HTMLDivElement>();
209
+ const codeChevronRef = createRef<HTMLSpanElement>();
210
+
211
+ // With result: show params + result
212
+ if (result && params) {
213
+ const output = result.output || "";
214
+ const files = result.details?.files || [];
215
+
216
+ const attachments: Attachment[] = files.map((f, i) => {
217
+ // Decode base64 content for text files to show in overlay
218
+ let extractedText: string | undefined;
219
+ const isTextBased =
220
+ f.mimeType?.startsWith("text/") ||
221
+ f.mimeType === "application/json" ||
222
+ f.mimeType === "application/javascript" ||
223
+ f.mimeType?.includes("xml");
224
+
225
+ if (isTextBased && f.contentBase64) {
226
+ try {
227
+ extractedText = atob(f.contentBase64);
228
+ } catch (e) {
229
+ console.warn("Failed to decode base64 content for", f.fileName);
230
+ }
231
+ }
263
232
 
264
- const attachments: Attachment[] = files.map((f, i) => {
265
- // Decode base64 content for text files to show in overlay
266
- let extractedText: string | undefined;
267
- const isTextBased =
268
- f.mimeType?.startsWith("text/") ||
269
- f.mimeType === "application/json" ||
270
- f.mimeType === "application/javascript" ||
271
- f.mimeType?.includes("xml");
233
+ return {
234
+ id: `repl-${Date.now()}-${i}`,
235
+ type: f.mimeType?.startsWith("image/") ? "image" : "document",
236
+ fileName: f.fileName || `file-${i}`,
237
+ mimeType: f.mimeType || "application/octet-stream",
238
+ size: f.size ?? 0,
239
+ content: f.contentBase64,
240
+ preview: f.mimeType?.startsWith("image/") ? f.contentBase64 : undefined,
241
+ extractedText,
242
+ };
243
+ });
272
244
 
273
- if (isTextBased && f.contentBase64) {
274
- try {
275
- extractedText = atob(f.contentBase64);
276
- } catch (e) {
277
- console.warn("Failed to decode base64 content for", f.fileName);
278
- }
279
- }
245
+ return {
246
+ content: html`
247
+ <div>
248
+ ${renderCollapsibleHeader(state, Code, params.title ? params.title : i18n("Executing JavaScript"), codeContentRef, codeChevronRef, false)}
249
+ <div ${ref(codeContentRef)} class="max-h-0 overflow-hidden transition-all duration-300 space-y-3">
250
+ <code-block .code=${params.code || ""} language="javascript"></code-block>
251
+ ${output ? html`<console-block .content=${output} .variant=${result.isError ? "error" : "default"}></console-block>` : ""}
252
+ </div>
253
+ ${
254
+ attachments.length
255
+ ? html`<div class="flex flex-wrap gap-2 mt-3">
256
+ ${attachments.map((att) => html`<attachment-tile .attachment=${att}></attachment-tile>`)}
257
+ </div>`
258
+ : ""
259
+ }
260
+ </div>
261
+ `,
262
+ isCustom: false,
263
+ };
264
+ }
280
265
 
266
+ // Just params (streaming or waiting for result)
267
+ if (params) {
281
268
  return {
282
- id: `repl-${Date.now()}-${i}`,
283
- type: f.mimeType?.startsWith("image/") ? "image" : "document",
284
- fileName: f.fileName || `file-${i}`,
285
- mimeType: f.mimeType || "application/octet-stream",
286
- size: f.size ?? 0,
287
- content: f.contentBase64,
288
- preview: f.mimeType?.startsWith("image/") ? f.contentBase64 : undefined,
289
- extractedText,
269
+ content: html`
270
+ <div>
271
+ ${renderCollapsibleHeader(state, Code, params.title ? params.title : i18n("Executing JavaScript"), codeContentRef, codeChevronRef, false)}
272
+ <div ${ref(codeContentRef)} class="max-h-0 overflow-hidden transition-all duration-300">
273
+ ${params.code ? html`<code-block .code=${params.code} language="javascript"></code-block>` : ""}
274
+ </div>
275
+ </div>
276
+ `,
277
+ isCustom: false,
290
278
  };
291
- });
279
+ }
292
280
 
293
- return html`
294
- <div class="flex flex-col gap-3">
295
- ${output ? html`<console-block .content=${output}></console-block>` : ""}
296
- ${
297
- attachments.length
298
- ? html`<div class="flex flex-wrap gap-2">
299
- ${attachments.map((att) => html`<attachment-tile .attachment=${att}></attachment-tile>`)}
300
- </div>`
301
- : ""
302
- }
303
- </div>
304
- `;
281
+ // No params or result yet
282
+ return { content: renderHeader(state, Code, i18n("Preparing JavaScript...")), isCustom: false };
305
283
  },
306
284
  };
307
285
 
@@ -1,3 +1,7 @@
1
+ import { html, icon, type TemplateResult } from "@mariozechner/mini-lit";
2
+ import type { Ref } from "lit/directives/ref.js";
3
+ import { ref } from "lit/directives/ref.js";
4
+ import { ChevronRight, Loader } from "lucide";
1
5
  import type { ToolRenderer } from "./types.js";
2
6
 
3
7
  // Registry of tool renderers
@@ -16,3 +20,97 @@ export function registerToolRenderer(toolName: string, renderer: ToolRenderer):
16
20
  export function getToolRenderer(toolName: string): ToolRenderer | undefined {
17
21
  return toolRenderers.get(toolName);
18
22
  }
23
+
24
+ /**
25
+ * Helper to render a header for tool renderers
26
+ * Shows icon on left when complete/error, spinner on right when in progress
27
+ */
28
+ export function renderHeader(
29
+ state: "inprogress" | "complete" | "error",
30
+ toolIcon: any,
31
+ text: string | TemplateResult,
32
+ ): TemplateResult {
33
+ const statusIcon = (iconComponent: any, color: string) =>
34
+ html`<span class="inline-block ${color}">${icon(iconComponent, "sm")}</span>`;
35
+
36
+ switch (state) {
37
+ case "inprogress":
38
+ return html`
39
+ <div class="flex items-center justify-between gap-2 text-sm text-muted-foreground">
40
+ <div class="flex items-center gap-2">
41
+ ${statusIcon(toolIcon, "text-foreground")}
42
+ ${text}
43
+ </div>
44
+ ${statusIcon(Loader, "text-foreground animate-spin")}
45
+ </div>
46
+ `;
47
+ case "complete":
48
+ return html`
49
+ <div class="flex items-center gap-2 text-sm text-muted-foreground">
50
+ ${statusIcon(toolIcon, "text-green-600 dark:text-green-500")}
51
+ ${text}
52
+ </div>
53
+ `;
54
+ case "error":
55
+ return html`
56
+ <div class="flex items-center gap-2 text-sm text-muted-foreground">
57
+ ${statusIcon(toolIcon, "text-destructive")}
58
+ ${text}
59
+ </div>
60
+ `;
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Helper to render a collapsible header for tool renderers
66
+ * Same as renderHeader but with a chevron button that toggles visibility of content
67
+ */
68
+ export function renderCollapsibleHeader(
69
+ state: "inprogress" | "complete" | "error",
70
+ toolIcon: any,
71
+ text: string | TemplateResult,
72
+ contentRef: Ref<HTMLElement>,
73
+ chevronRef: Ref<HTMLElement>,
74
+ defaultExpanded = false,
75
+ ): TemplateResult {
76
+ const statusIcon = (iconComponent: any, color: string) =>
77
+ html`<span class="inline-block ${color}">${icon(iconComponent, "sm")}</span>`;
78
+
79
+ const toggleContent = (e: Event) => {
80
+ e.preventDefault();
81
+ const content = contentRef.value;
82
+ const chevron = chevronRef.value;
83
+ if (content && chevron) {
84
+ const isCollapsed = content.classList.contains("max-h-0");
85
+ if (isCollapsed) {
86
+ content.classList.remove("max-h-0");
87
+ content.classList.add("max-h-[2000px]", "mt-3");
88
+ chevron.classList.add("rotate-90");
89
+ } else {
90
+ content.classList.remove("max-h-[2000px]", "mt-3");
91
+ content.classList.add("max-h-0");
92
+ chevron.classList.remove("rotate-90");
93
+ }
94
+ }
95
+ };
96
+
97
+ const toolIconColor =
98
+ state === "complete"
99
+ ? "text-green-600 dark:text-green-500"
100
+ : state === "error"
101
+ ? "text-destructive"
102
+ : "text-foreground";
103
+
104
+ return html`
105
+ <button @click=${toggleContent} class="flex items-center justify-between gap-2 text-sm text-muted-foreground w-full text-left hover:text-foreground transition-colors cursor-pointer">
106
+ <div class="flex items-center gap-2">
107
+ ${state === "inprogress" ? statusIcon(Loader, "text-foreground animate-spin") : ""}
108
+ ${statusIcon(toolIcon, toolIconColor)}
109
+ ${text}
110
+ </div>
111
+ <span class="inline-block text-muted-foreground transition-transform duration-300 ${defaultExpanded ? "rotate-90" : ""}" ${ref(chevronRef)}>
112
+ ${icon(ChevronRight, "sm")}
113
+ </span>
114
+ </button>
115
+ `;
116
+ }
@@ -1,7 +1,9 @@
1
- import { html, type TemplateResult } from "@mariozechner/mini-lit";
1
+ import { html } from "@mariozechner/mini-lit";
2
2
  import type { ToolResultMessage } from "@mariozechner/pi-ai";
3
+ import { SquareTerminal } from "lucide";
3
4
  import { i18n } from "../../utils/i18n.js";
4
- import type { ToolRenderer } from "../types.js";
5
+ import { renderHeader } from "../renderer-registry.js";
6
+ import type { ToolRenderer, ToolRenderResult } from "../types.js";
5
7
 
6
8
  interface BashParams {
7
9
  command: string;
@@ -9,37 +11,38 @@ interface BashParams {
9
11
 
10
12
  // Bash tool has undefined details (only uses output)
11
13
  export class BashRenderer implements ToolRenderer<BashParams, undefined> {
12
- renderParams(params: BashParams, isStreaming?: boolean): TemplateResult {
13
- if (isStreaming && (!params.command || params.command.length === 0)) {
14
- return html`<div class="text-sm text-muted-foreground">${i18n("Writing command...")}</div>`;
15
- }
16
-
17
- return html`
18
- <div class="text-sm text-muted-foreground">
19
- <span>${i18n("Running command:")}</span>
20
- <code class="ml-1 px-1.5 py-0.5 bg-muted rounded text-xs font-mono">${params.command}</code>
21
- </div>
22
- `;
23
- }
14
+ render(params: BashParams | undefined, result: ToolResultMessage<undefined> | undefined): ToolRenderResult {
15
+ const state = result ? (result.isError ? "error" : "complete") : "inprogress";
24
16
 
25
- renderResult(_params: BashParams, result: ToolResultMessage<undefined>): TemplateResult {
26
- const output = result.output || "";
27
- const isError = result.isError === true;
17
+ // With result: show command + output
18
+ if (result && params?.command) {
19
+ const output = result.output || "";
20
+ const combined = output ? `> ${params.command}\n\n${output}` : `> ${params.command}`;
21
+ return {
22
+ content: html`
23
+ <div class="space-y-3">
24
+ ${renderHeader(state, SquareTerminal, i18n("Running command..."))}
25
+ <console-block .content=${combined} .variant=${result.isError ? "error" : "default"}></console-block>
26
+ </div>
27
+ `,
28
+ isCustom: false,
29
+ };
30
+ }
28
31
 
29
- if (isError) {
30
- return html`
31
- <div class="text-sm">
32
- <div class="text-destructive font-medium mb-1">${i18n("Command failed:")}</div>
33
- <pre class="text-xs font-mono text-destructive bg-destructive/10 p-2 rounded overflow-x-auto">${output}</pre>
34
- </div>
35
- `;
32
+ // Just params (streaming or waiting)
33
+ if (params?.command) {
34
+ return {
35
+ content: html`
36
+ <div class="space-y-3">
37
+ ${renderHeader(state, SquareTerminal, i18n("Running command..."))}
38
+ <console-block .content=${`> ${params.command}`}></console-block>
39
+ </div>
40
+ `,
41
+ isCustom: false,
42
+ };
36
43
  }
37
44
 
38
- // Display the command output
39
- return html`
40
- <div class="text-sm">
41
- <pre class="text-xs font-mono text-foreground bg-muted/50 p-2 rounded overflow-x-auto">${output}</pre>
42
- </div>
43
- `;
45
+ // No params yet
46
+ return { content: renderHeader(state, SquareTerminal, i18n("Waiting for command...")), isCustom: false };
44
47
  }
45
48
  }
@@ -1,7 +1,9 @@
1
- import { html, type TemplateResult } from "@mariozechner/mini-lit";
1
+ import { html } from "@mariozechner/mini-lit";
2
2
  import type { ToolResultMessage } from "@mariozechner/pi-ai";
3
+ import { Calculator } from "lucide";
3
4
  import { i18n } from "../../utils/i18n.js";
4
- import type { ToolRenderer } from "../types.js";
5
+ import { renderHeader } from "../renderer-registry.js";
6
+ import type { ToolRenderer, ToolRenderResult } from "../types.js";
5
7
 
6
8
  interface CalculateParams {
7
9
  expression: string;
@@ -9,41 +11,44 @@ interface CalculateParams {
9
11
 
10
12
  // Calculate tool has undefined details (only uses output)
11
13
  export class CalculateRenderer implements ToolRenderer<CalculateParams, undefined> {
12
- renderParams(params: CalculateParams, isStreaming?: boolean): TemplateResult {
13
- if (isStreaming && !params.expression) {
14
- return html`<div class="text-sm text-muted-foreground">${i18n("Writing expression...")}</div>`;
15
- }
14
+ render(params: CalculateParams | undefined, result: ToolResultMessage<undefined> | undefined): ToolRenderResult {
15
+ const state = result ? (result.isError ? "error" : "complete") : "inprogress";
16
16
 
17
- return html`
18
- <div class="text-sm text-muted-foreground">
19
- <span>${i18n("Calculating")}</span>
20
- <code class="mx-1 px-1.5 py-0.5 bg-muted rounded text-xs font-mono">${params.expression}</code>
21
- </div>
22
- `;
23
- }
17
+ // Full params + full result
18
+ if (result && params?.expression) {
19
+ const output = result.output || "";
24
20
 
25
- renderResult(_params: CalculateParams, result: ToolResultMessage<undefined>): TemplateResult {
26
- // Parse the output to make it look nicer
27
- const output = result.output || "";
28
- const isError = result.isError === true;
21
+ // Error: show expression in header, error below
22
+ if (result.isError) {
23
+ return {
24
+ content: html`
25
+ <div class="space-y-3">
26
+ ${renderHeader(state, Calculator, params.expression)}
27
+ <div class="text-sm text-destructive">${output}</div>
28
+ </div>
29
+ `,
30
+ isCustom: false,
31
+ };
32
+ }
33
+
34
+ // Success: show expression = result in header
35
+ return { content: renderHeader(state, Calculator, `${params.expression} = ${output}`), isCustom: false };
36
+ }
29
37
 
30
- if (isError) {
31
- return html`<div class="text-sm text-destructive">${output}</div>`;
38
+ // Full params, no result: just show header with expression in it
39
+ if (params?.expression) {
40
+ return {
41
+ content: renderHeader(state, Calculator, `${i18n("Calculating")} ${params.expression}`),
42
+ isCustom: false,
43
+ };
32
44
  }
33
45
 
34
- // Try to split on = to show expression and result separately
35
- const parts = output.split(" = ");
36
- if (parts.length === 2) {
37
- return html`
38
- <div class="text-sm font-mono">
39
- <span class="text-muted-foreground">${parts[0]}</span>
40
- <span class="text-muted-foreground mx-1">=</span>
41
- <span class="text-foreground font-semibold">${parts[1]}</span>
42
- </div>
43
- `;
46
+ // Partial params (empty expression), no result
47
+ if (params && !params.expression) {
48
+ return { content: renderHeader(state, Calculator, i18n("Writing expression...")), isCustom: false };
44
49
  }
45
50
 
46
- // Fallback to showing the whole output
47
- return html`<div class="text-sm font-mono text-foreground">${output}</div>`;
51
+ // No params, no result
52
+ return { content: renderHeader(state, Calculator, i18n("Waiting for expression...")), isCustom: false };
48
53
  }
49
54
  }