@marimo-team/frontend 0.15.5 → 0.16.0

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 (223) hide show
  1. package/dist/assets/{ConnectedDataExplorerComponent-Cn5-l2X1.js → ConnectedDataExplorerComponent-BErMbWvG.js} +1 -1
  2. package/dist/assets/{ImageComparisonComponent-CEXMKKA4.js → ImageComparisonComponent-fTHv1Ih0.js} +1 -1
  3. package/dist/assets/{VegaLite-Bt14Ds9k.js → VegaLite-Bdi-TyfY.js} +6 -6
  4. package/dist/assets/_baseEach-CNBxBxvS.js +1 -0
  5. package/dist/assets/_baseMap-D1WHjKrd.js +1 -0
  6. package/dist/assets/_baseUniq-CCgDNtZb.js +1 -0
  7. package/dist/assets/_createAggregator-DcD0kTA5.js +1 -0
  8. package/dist/assets/agent-panel-Crv430aI.js +268 -0
  9. package/dist/assets/agent-panel-D92Mfy1i.css +1 -0
  10. package/dist/assets/{any-language-editor-DiwNT6zp.js → any-language-editor-CQh552Wu.js} +1 -1
  11. package/dist/assets/architectureDiagram-W76B3OCA-BAJeBxzt.js +36 -0
  12. package/dist/assets/{between-horizontal-start-FyewyCGn.js → between-horizontal-start-Boxgxbt_.js} +1 -1
  13. package/dist/assets/{blockDiagram-QIGZ2CNN-BrOkAf_c.js → blockDiagram-QIGZ2CNN-CL-1svEK.js} +1 -1
  14. package/dist/assets/{c4Diagram-FPNF74CW-BHPzDxE2.js → c4Diagram-FPNF74CW-BbEqbCTl.js} +5 -5
  15. package/dist/assets/channel-_2eNSz0n.js +1 -0
  16. package/dist/assets/chat-panel-CXh5Wl6C.js +3 -0
  17. package/dist/assets/{chunk-4BX2VUAB-DLxaCNYh.js → chunk-4BX2VUAB-C--8TXeE.js} +1 -1
  18. package/dist/assets/{chunk-55IACEB6-DdzvO3HR.js → chunk-55IACEB6-Bj00HDqq.js} +1 -1
  19. package/dist/assets/{chunk-FMBD7UC4-R5o-nSiG.js → chunk-FMBD7UC4-C-lhB6hN.js} +1 -1
  20. package/dist/assets/{chunk-K7UQS3LO-DxaMrGgG.js → chunk-K7UQS3LO-B-pGTXPt.js} +1 -1
  21. package/dist/assets/{chunk-QN33PNHL-DqS9-FYm.js → chunk-QN33PNHL-DqUzGhvm.js} +1 -1
  22. package/dist/assets/{chunk-QZHKN3VN-BZ-TzajS.js → chunk-QZHKN3VN-TntJHfSk.js} +1 -1
  23. package/dist/assets/{chunk-TVAH2DTR-BsgP2dyv.js → chunk-TVAH2DTR-HUJb1psV.js} +1 -1
  24. package/dist/assets/{chunk-TZMSLE5B-D-h3ahXI.js → chunk-TZMSLE5B-BK3C__t3.js} +1 -1
  25. package/dist/assets/{circle-play-CQtRZ-rT.js → circle-play-DBLOv1Yu.js} +1 -1
  26. package/dist/assets/classDiagram-KNZD7YFC-BGmh9POF.js +1 -0
  27. package/dist/assets/classDiagram-v2-RKCZMP56-BGmh9POF.js +1 -0
  28. package/dist/assets/{clear-button-BY6Z_ViL.js → clear-button-BeoFbEKH.js} +1 -1
  29. package/dist/assets/clone-BFDSPAj3.js +1 -0
  30. package/dist/assets/command-palette-CXZiSv0I.js +1 -0
  31. package/dist/assets/common-C7oJcmCT.js +1 -0
  32. package/dist/assets/{compile-Ct_jzdKr.js → compile-7L0MwhyI.js} +1 -1
  33. package/dist/assets/cose-bilkent-S5V4N54A-BMkGLcVC.js +1 -0
  34. package/dist/assets/dagre-5GWH7T2D-BJtRienS.js +4 -0
  35. package/dist/assets/{data-grid-overlay-editor-BN_wulc3.js → data-grid-overlay-editor-DBkmGtNs.js} +1 -1
  36. package/dist/assets/datasources-panel-B7FbYLiy.js +1 -0
  37. package/dist/assets/{dependency-graph-panel-BOmSCZf7.js → dependency-graph-panel-DEdOxp2X.js} +4 -4
  38. package/dist/assets/diagram-N5W7TBWH-CmECY3nb.js +24 -0
  39. package/dist/assets/diagram-QEK2KX5R-DMOVSNKD.js +43 -0
  40. package/dist/assets/diagram-S2PKOQOG-BiJ96PNQ.js +24 -0
  41. package/dist/assets/{documentation-panel-BxjJO_Gw.js → documentation-panel-xULhaEv3.js} +1 -1
  42. package/dist/assets/edit-page-BrYda9VE.js +129 -0
  43. package/dist/assets/{ellipsis-vertical-UHbmjI2n.js → ellipsis-vertical-BBqXIlc2.js} +1 -1
  44. package/dist/assets/{empty-state-BIBXzY_0.js → empty-state-B3dA3G5P.js} +1 -1
  45. package/dist/assets/{erDiagram-AWTI2OKA-E84mAle_.js → erDiagram-AWTI2OKA-MP1DiFRo.js} +1 -1
  46. package/dist/assets/{error-panel-MEvQ6K7h.js → error-panel-Cc1sv-Ag.js} +1 -1
  47. package/dist/assets/file-explorer-panel-Bw59Kva1.js +1 -0
  48. package/dist/assets/{flowDiagram-PVAE7QVJ-DfbIRSAW.js → flowDiagram-PVAE7QVJ-BX7caPp7.js} +1 -1
  49. package/dist/assets/{ganttDiagram-OWAHRB6G-DR4HZ1z_.js → ganttDiagram-OWAHRB6G-B462g4Yf.js} +3 -3
  50. package/dist/assets/gitGraphDiagram-NY62KEGX-CGgvZ9-9.js +65 -0
  51. package/dist/assets/{glide-data-editor-nNmo1lPq.js → glide-data-editor-C0gUFZON.js} +4 -4
  52. package/dist/assets/graph-CHRVBzY5.js +1 -0
  53. package/dist/assets/{home-page-9eW6qida.js → home-page-Fb2osjys.js} +3 -3
  54. package/dist/assets/{index-DMomwMcN.js → index-BVgAenPd.js} +1 -1
  55. package/dist/assets/{index-B8llrTSo.js → index-BY93Ejhl.js} +1 -1
  56. package/dist/assets/{index-BFSnz7iM.js → index-C-8WADat.js} +1 -1
  57. package/dist/assets/{index-CPN7TRA1.js → index-C-GhZ7ti.js} +1 -1
  58. package/dist/assets/{index-DyLSuOH1.js → index-C1v_Z9et.js} +1 -1
  59. package/dist/assets/{index-VPWqq2Pg.js → index-C4Tn5NvJ.js} +1 -1
  60. package/dist/assets/{index-BAH034Ue.js → index-C77h_TXN.js} +1 -1
  61. package/dist/assets/{index-Dt9UWeWn.js → index-CQDrxQ0j.js} +1 -1
  62. package/dist/assets/{index-DWOaniGT.js → index-CWMgowgL.js} +1 -1
  63. package/dist/assets/{index-B1_GXGaP.js → index-Clbi_Yaq.js} +1 -1
  64. package/dist/assets/{index-B7yXbrLa.js → index-CpTPJo4k.js} +1 -1
  65. package/dist/assets/{index-CknhX2Vy.css → index-Cx0bsY1w.css} +1 -1
  66. package/dist/assets/{index-DqzMPAC8.js → index-D1vmG6DS.js} +2 -2
  67. package/dist/assets/{index-c6If577Q.js → index-D9UKkrr2.js} +1 -1
  68. package/dist/assets/{index-CB2pnVQG.js → index-DEQvTChO.js} +1 -1
  69. package/dist/assets/{index-OC46250R.js → index-DKEudB02.js} +205 -197
  70. package/dist/assets/{index-CSgxTUzD.js → index-DRMm6SNo.js} +1 -1
  71. package/dist/assets/{index-Bq516OmX.js → index-DoRmcrKM.js} +1 -1
  72. package/dist/assets/{index-DSU75csX.js → index-lYa_leQE.js} +1 -1
  73. package/dist/assets/{index-BLu5CX6z.js → index-vmICa5KN.js} +1 -1
  74. package/dist/assets/{index-uacyUula.js → index-z9bohSQJ.js} +1 -1
  75. package/dist/assets/infoDiagram-STP46IZ2-CVyrdLc8.js +2 -0
  76. package/dist/assets/isEmpty-DU_ogP_D.js +1 -0
  77. package/dist/assets/{journeyDiagram-BIP6EPQ6-BBiFyygf.js → journeyDiagram-BIP6EPQ6-C6EgLP_Q.js} +1 -1
  78. package/dist/assets/{kanban-definition-6OIFK2YF-DhgA6Nt6.js → kanban-definition-6OIFK2YF-BXzYO1yj.js} +4 -4
  79. package/dist/assets/layout-jihVw5-i.js +1 -0
  80. package/dist/assets/linear-C4blANlC.js +1 -0
  81. package/dist/assets/{links-CbvGxbsJ.js → links-D59GIweI.js} +3 -3
  82. package/dist/assets/{logs-panel-B9SmTZAW.js → logs-panel-D401qzZh.js} +1 -1
  83. package/dist/assets/markdown-renderer-Cd9eYyaL.js +263 -0
  84. package/dist/assets/{agent-panel-DpQ6muj-.css → markdown-renderer-ClyzDMmG.css} +1 -1
  85. package/dist/assets/mermaid-BEVuRz_O.js +1 -0
  86. package/dist/assets/{mermaid.core-4nVOEVX3.js → mermaid.core-CaSnaLH0.js} +41 -41
  87. package/dist/assets/min-DUMu_zeK.js +1 -0
  88. package/dist/assets/{mindmap-definition-Q6HEUPPD-CVLQNn1q.js → mindmap-definition-Q6HEUPPD-BXUM5MT2.js} +2 -2
  89. package/dist/assets/{number-overlay-editor-CzRzXLcd.js → number-overlay-editor-4uWXGlPG.js} +1 -1
  90. package/dist/assets/{outline-panel-uvsS-YEQ.js → outline-panel-DIzkvm2I.js} +1 -1
  91. package/dist/assets/packages-panel-CJL0MVlj.js +1 -0
  92. package/dist/assets/{pieDiagram-ADFJNKIX-C5IQ5DBZ.js → pieDiagram-ADFJNKIX-Dxt5PVNo.js} +3 -3
  93. package/dist/assets/{quadrantDiagram-LMRXKWRM-CFXFnQxx.js → quadrantDiagram-LMRXKWRM-D4pUaA31.js} +1 -1
  94. package/dist/assets/{react-plotly-mzdv02_Y.js → react-plotly-cJZ0VWBq.js} +1 -1
  95. package/dist/assets/{requirementDiagram-4UW4RH46-D9bPC89T.js → requirementDiagram-4UW4RH46-DVRTjgas.js} +1 -1
  96. package/dist/assets/run-page-BUEnMC9w.js +1 -0
  97. package/dist/assets/sankeyDiagram-GR3RE2ED-CVFnD9C-.js +10 -0
  98. package/dist/assets/scratchpad-panel-BIgRENkI.js +1 -0
  99. package/dist/assets/secrets-panel-xY5-V_BD.js +1 -0
  100. package/dist/assets/{sequenceDiagram-C3RYC4MD-6N7_hY4k.js → sequenceDiagram-C3RYC4MD-_lY4ZN_S.js} +4 -4
  101. package/dist/assets/{slides-component-EcjC8sDK.js → slides-component-Xjymwj7X.js} +1 -1
  102. package/dist/assets/snippets-panel-CTPYW41n.js +1 -0
  103. package/dist/assets/sortBy-BNZKwiq_.js +1 -0
  104. package/dist/assets/state-C4NiC9tO.js +1 -0
  105. package/dist/assets/stateDiagram-KXAO66HF-Da0JQWCn.js +1 -0
  106. package/dist/assets/stateDiagram-v2-UMBNRL4Z-D5lYZOOt.js +1 -0
  107. package/dist/assets/storage-CMdLzB_c.js +26 -0
  108. package/dist/assets/terminal-BPwTkXae.js +10 -0
  109. package/dist/assets/time-Dv5_Ouz_.js +1 -0
  110. package/dist/assets/{timeline-definition-XQNQX7LJ-BEaynAiY.js → timeline-definition-XQNQX7LJ-Dxh5Zu2e.js} +1 -1
  111. package/dist/assets/tracing-BCIurUfa.js +2 -0
  112. package/dist/assets/{tracing-panel-BmuHLPrY.js → tracing-panel-DAzrzNmm.js} +2 -2
  113. package/dist/assets/{trash-UBqfK4mR.js → trash-Dc6DSjz_.js} +1 -1
  114. package/dist/assets/{tree-XiEycetl.js → tree-jheoerAX.js} +1 -1
  115. package/dist/assets/{treemap-75Q7IDZK-CnuVFbBG.js → treemap-75Q7IDZK-IgpxeGaf.js} +21 -21
  116. package/dist/assets/{ts-tags-CloPe9IY.js → ts-tags-DxCDHihD.js} +1 -1
  117. package/dist/assets/variable-panel-DYAiLBmF.js +1 -0
  118. package/dist/assets/{vega-component-DsTH4tuX.js → vega-component-BpfpiPKI.js} +1 -1
  119. package/dist/assets/{xychartDiagram-6GGTOJPD-Dcz3O-A3.js → xychartDiagram-6GGTOJPD-CmNigJ31.js} +1 -1
  120. package/dist/index.html +2 -2
  121. package/package.json +8 -4
  122. package/src/__tests__/mocks.ts +43 -0
  123. package/src/components/app-config/user-config-form.tsx +32 -0
  124. package/src/components/chat/acp/__tests__/__snapshots__/prompt.test.ts.snap +55 -23
  125. package/src/components/chat/acp/__tests__/context-utils.test.ts +222 -0
  126. package/src/components/chat/acp/__tests__/prompt.test.ts +1 -1
  127. package/src/components/chat/acp/__tests__/state.test.ts +2 -6
  128. package/src/components/chat/acp/agent-docs.tsx +33 -6
  129. package/src/components/chat/acp/agent-panel.css +0 -18
  130. package/src/components/chat/acp/agent-panel.tsx +397 -72
  131. package/src/components/chat/acp/agent-selector.tsx +7 -1
  132. package/src/components/chat/acp/blocks.tsx +40 -10
  133. package/src/components/chat/acp/common.tsx +10 -2
  134. package/src/components/chat/acp/context-utils.ts +127 -0
  135. package/src/components/chat/acp/prompt.ts +34 -10
  136. package/src/components/chat/acp/state.ts +1 -1
  137. package/src/components/chat/acp/types.ts +8 -0
  138. package/src/components/chat/chat-panel.tsx +23 -88
  139. package/src/components/chat/chat-utils.ts +127 -1
  140. package/src/components/chat/markdown-renderer.css +39 -0
  141. package/src/components/chat/markdown-renderer.tsx +7 -38
  142. package/src/components/chat/tool-call-accordion.tsx +113 -23
  143. package/src/components/editor/Cell.tsx +6 -0
  144. package/src/components/editor/actions/name-cell-input.tsx +6 -1
  145. package/src/components/editor/actions/useCellActionButton.tsx +3 -1
  146. package/src/components/editor/ai/__tests__/completion-utils.test.ts +178 -1
  147. package/src/components/editor/ai/add-cell-with-ai.tsx +68 -66
  148. package/src/components/editor/ai/ai-completion-editor.tsx +29 -26
  149. package/src/components/editor/ai/completion-handlers.tsx +44 -6
  150. package/src/components/editor/ai/completion-utils.ts +92 -0
  151. package/src/components/editor/ai/transport/chat-transport.tsx +36 -0
  152. package/src/components/editor/cell/StagedAICell.tsx +51 -0
  153. package/src/components/editor/cell/cell-actions.tsx +2 -1
  154. package/src/components/terminal/__tests__/state.test.ts +207 -0
  155. package/src/components/terminal/hooks.ts +41 -0
  156. package/src/components/terminal/state.ts +75 -0
  157. package/src/components/terminal/terminal.tsx +334 -13
  158. package/src/components/terminal/theme.tsx +56 -0
  159. package/src/core/ai/__tests__/staged-cells.test.ts +356 -0
  160. package/src/core/ai/staged-cells.ts +208 -0
  161. package/src/core/cells/cells.ts +1 -1
  162. package/src/core/codemirror/lsp/federated-lsp.ts +1 -1
  163. package/src/core/islands/main.ts +2 -2
  164. package/src/core/kernel/messages.ts +8 -12
  165. package/src/core/saving/__tests__/filename.test.ts +37 -0
  166. package/src/core/static/__tests__/download-html.test.ts +43 -1
  167. package/src/core/websocket/useMarimoWebSocket.tsx +2 -2
  168. package/src/css/app/Cell.css +11 -0
  169. package/src/plugins/core/RenderHTML.tsx +36 -2
  170. package/src/plugins/core/__test__/RenderHTML.test.ts +72 -0
  171. package/src/plugins/core/registerReactComponent.tsx +28 -0
  172. package/src/plugins/impl/FileBrowserPlugin.tsx +8 -2
  173. package/src/stories/cell.stories.tsx +1 -1
  174. package/src/stories/layout/vertical/one-column.stories.tsx +1 -1
  175. package/src/utils/__tests__/cell-urls.test.ts +29 -0
  176. package/src/utils/__tests__/filenames.test.ts +18 -0
  177. package/src/utils/__tests__/path.test.ts +38 -0
  178. package/src/utils/__tests__/urls.test.ts +56 -1
  179. package/src/utils/errors.ts +9 -0
  180. package/dist/assets/_baseEach-C1FLm7WW.js +0 -1
  181. package/dist/assets/_baseMap-DBVArUYD.js +0 -1
  182. package/dist/assets/_baseUniq-Dk7ZPJ3N.js +0 -1
  183. package/dist/assets/_createAggregator-Bn38fDd3.js +0 -1
  184. package/dist/assets/agent-panel-COUYnuIK.js +0 -475
  185. package/dist/assets/architectureDiagram-W76B3OCA-DBzWQKKu.js +0 -36
  186. package/dist/assets/channel-CjhbjOv4.js +0 -1
  187. package/dist/assets/chat-panel-BPXKoTnZ.js +0 -7
  188. package/dist/assets/chat-panel-Brrs_eeH.css +0 -1
  189. package/dist/assets/classDiagram-KNZD7YFC-DHs5cFzy.js +0 -1
  190. package/dist/assets/classDiagram-v2-RKCZMP56-DHs5cFzy.js +0 -1
  191. package/dist/assets/clone-DM1YNjEn.js +0 -1
  192. package/dist/assets/command-palette-S0bzQp7v.js +0 -1
  193. package/dist/assets/common-B8U9k2Ly.js +0 -1
  194. package/dist/assets/cose-bilkent-S5V4N54A-wz1Sfx7j.js +0 -1
  195. package/dist/assets/dagre-5GWH7T2D-BfpcVBgq.js +0 -4
  196. package/dist/assets/datasources-panel-DfuURLJw.js +0 -1
  197. package/dist/assets/diagram-N5W7TBWH-Bf0oqqQh.js +0 -24
  198. package/dist/assets/diagram-QEK2KX5R-ZTc3qikh.js +0 -43
  199. package/dist/assets/diagram-S2PKOQOG-tLScBy7Z.js +0 -24
  200. package/dist/assets/edit-page-DJ8kJZ9w.js +0 -129
  201. package/dist/assets/file-explorer-panel-CzNUJ63G.js +0 -1
  202. package/dist/assets/gitGraphDiagram-NY62KEGX-C1t6QtVa.js +0 -65
  203. package/dist/assets/graph-CssCVWIq.js +0 -1
  204. package/dist/assets/index-DcCIe7np.js +0 -28
  205. package/dist/assets/infoDiagram-STP46IZ2-CwiAoz9f.js +0 -2
  206. package/dist/assets/layout-DpQrxGW-.js +0 -1
  207. package/dist/assets/linear-NsreOeBF.js +0 -1
  208. package/dist/assets/mermaid-DSt0r6IQ.js +0 -1
  209. package/dist/assets/min-D259kI3t.js +0 -1
  210. package/dist/assets/packages-panel-xMz9W2hW.js +0 -1
  211. package/dist/assets/run-page-Bb68qdhQ.js +0 -1
  212. package/dist/assets/sankeyDiagram-GR3RE2ED-BSJOau8E.js +0 -10
  213. package/dist/assets/scratchpad-panel-BF4BO-U4.js +0 -1
  214. package/dist/assets/secrets-panel-CdIX44dQ.js +0 -1
  215. package/dist/assets/snippets-panel-Dco9h0rb.js +0 -1
  216. package/dist/assets/sortBy-aLGA-PGK.js +0 -1
  217. package/dist/assets/stateDiagram-KXAO66HF-Bd68WT3b.js +0 -1
  218. package/dist/assets/stateDiagram-v2-UMBNRL4Z-BXz_GSwb.js +0 -1
  219. package/dist/assets/storage-CGlP4lCF.js +0 -26
  220. package/dist/assets/terminal-CxkHubcu.js +0 -9
  221. package/dist/assets/time-D2nr1UgQ.js +0 -1
  222. package/dist/assets/tracing-kTqHxa7q.js +0 -2
  223. package/dist/assets/variable-panel-noTnH-AQ.js +0 -1
@@ -0,0 +1,222 @@
1
+ /* Copyright 2024 Marimo. All rights reserved. */
2
+
3
+ import { beforeEach, describe, expect, it, type Mocked, vi } from "vitest";
4
+ import {
5
+ convertFilesToResourceLinks,
6
+ parseContextFromPrompt,
7
+ } from "../context-utils";
8
+
9
+ // Mock dependencies
10
+ vi.mock("@/utils/fileToBase64", () => ({
11
+ blobToString: vi.fn(),
12
+ }));
13
+
14
+ vi.mock("@/core/ai/context/context", () => ({
15
+ getAIContextRegistry: vi.fn(),
16
+ }));
17
+
18
+ vi.mock("@/core/state/jotai", () => ({
19
+ store: {},
20
+ }));
21
+
22
+ vi.mock("@/utils/Logger", () => ({
23
+ Logger: {
24
+ error: vi.fn(),
25
+ debug: vi.fn(),
26
+ },
27
+ }));
28
+
29
+ import { getAIContextRegistry } from "@/core/ai/context/context";
30
+ import type {
31
+ AIContextItem,
32
+ AIContextRegistry,
33
+ ContextLocatorId,
34
+ } from "@/core/ai/context/registry";
35
+ import { blobToString } from "@/utils/fileToBase64";
36
+
37
+ const CONTEXT_ID = "context1" as ContextLocatorId;
38
+
39
+ describe("convertFilesToResourceLinks", () => {
40
+ beforeEach(() => {
41
+ vi.clearAllMocks();
42
+ });
43
+
44
+ it("should convert files to resource links", async () => {
45
+ const mockFile = new File(["content"], "test.txt", { type: "text/plain" });
46
+ vi.mocked(blobToString).mockResolvedValue(
47
+ "data:text/plain;base64,Y29udGVudA==",
48
+ );
49
+
50
+ const result = await convertFilesToResourceLinks([mockFile]);
51
+
52
+ expect(result).toEqual([
53
+ {
54
+ type: "resource_link",
55
+ uri: "data:text/plain;base64,Y29udGVudA==",
56
+ mimeType: "text/plain",
57
+ name: "test.txt",
58
+ },
59
+ ]);
60
+ });
61
+
62
+ it("should handle empty file array", async () => {
63
+ const result = await convertFilesToResourceLinks([]);
64
+ expect(result).toEqual([]);
65
+ });
66
+
67
+ it("should handle file conversion errors gracefully", async () => {
68
+ const mockFile = new File(["content"], "test.txt", { type: "text/plain" });
69
+ vi.mocked(blobToString).mockRejectedValue(new Error("Conversion failed"));
70
+
71
+ const result = await convertFilesToResourceLinks([mockFile]);
72
+
73
+ expect(result).toEqual([]);
74
+ });
75
+
76
+ it("should process multiple files", async () => {
77
+ const file1 = new File(["content1"], "test1.txt", { type: "text/plain" });
78
+ const file2 = new File(["content2"], "test2.txt", { type: "text/plain" });
79
+
80
+ vi.mocked(blobToString)
81
+ .mockResolvedValueOnce("data:text/plain;base64,Y29udGVudDE=")
82
+ .mockResolvedValueOnce("data:text/plain;base64,Y29udGVudDI=");
83
+
84
+ const result = await convertFilesToResourceLinks([file1, file2]);
85
+
86
+ expect(result).toHaveLength(2);
87
+ expect((result[0] as { name: string }).name).toBe("test1.txt");
88
+ expect((result[1] as { name: string }).name).toBe("test2.txt");
89
+ });
90
+ });
91
+
92
+ describe("parseContextFromPrompt", () => {
93
+ const mockRegistry = {
94
+ parseAllContextIds: vi.fn(),
95
+ formatContextForAI: vi.fn(),
96
+ getAttachmentsForContext: vi.fn(),
97
+ } as unknown as Mocked<AIContextRegistry<AIContextItem>>;
98
+
99
+ beforeEach(() => {
100
+ vi.clearAllMocks();
101
+ vi.mocked(getAIContextRegistry).mockReturnValue(mockRegistry);
102
+ });
103
+
104
+ it("should return empty blocks when no @ symbol in prompt", async () => {
105
+ const result = await parseContextFromPrompt("simple prompt");
106
+
107
+ expect(result).toEqual({
108
+ contextBlocks: [],
109
+ attachmentBlocks: [],
110
+ });
111
+ });
112
+
113
+ it("should return empty blocks when no context IDs found", async () => {
114
+ mockRegistry.parseAllContextIds.mockReturnValue([]);
115
+
116
+ const result = await parseContextFromPrompt("prompt with @ but no context");
117
+
118
+ expect(result).toEqual({
119
+ contextBlocks: [],
120
+ attachmentBlocks: [],
121
+ });
122
+ });
123
+
124
+ it("should create context blocks when context IDs are found", async () => {
125
+ mockRegistry.parseAllContextIds.mockReturnValue([CONTEXT_ID]);
126
+ mockRegistry.formatContextForAI.mockReturnValue("formatted context");
127
+ mockRegistry.getAttachmentsForContext.mockResolvedValue([]);
128
+
129
+ const result = await parseContextFromPrompt("prompt with @context1");
130
+
131
+ expect(result.contextBlocks).toHaveLength(1);
132
+ expect(result.contextBlocks[0]).toEqual({
133
+ type: "resource",
134
+ resource: {
135
+ uri: "context.md",
136
+ mimeType: "text/markdown",
137
+ text: "formatted context",
138
+ },
139
+ });
140
+ expect(result.attachmentBlocks).toHaveLength(0);
141
+ });
142
+
143
+ it("should create attachment blocks when attachments are found", async () => {
144
+ mockRegistry.parseAllContextIds.mockReturnValue([CONTEXT_ID]);
145
+ mockRegistry.formatContextForAI.mockReturnValue("formatted context");
146
+ mockRegistry.getAttachmentsForContext.mockResolvedValue([
147
+ {
148
+ type: "file",
149
+ url: "http://example.com/file.pdf",
150
+ mediaType: "application/pdf",
151
+ filename: "file.pdf",
152
+ },
153
+ ]);
154
+
155
+ const result = await parseContextFromPrompt("prompt with @context1");
156
+
157
+ expect(result.contextBlocks).toHaveLength(1);
158
+ expect(result.attachmentBlocks).toHaveLength(1);
159
+ expect(result.attachmentBlocks[0]).toEqual({
160
+ type: "resource_link",
161
+ uri: "http://example.com/file.pdf",
162
+ mimeType: "application/pdf",
163
+ name: "file.pdf",
164
+ });
165
+ });
166
+
167
+ it("should handle empty context string gracefully", async () => {
168
+ mockRegistry.parseAllContextIds.mockReturnValue([CONTEXT_ID]);
169
+ mockRegistry.formatContextForAI.mockReturnValue(" ");
170
+ mockRegistry.getAttachmentsForContext.mockResolvedValue([]);
171
+
172
+ const result = await parseContextFromPrompt("prompt with @context1");
173
+
174
+ expect(result.contextBlocks).toHaveLength(0);
175
+ expect(result.attachmentBlocks).toHaveLength(0);
176
+ });
177
+
178
+ it("should handle registry errors gracefully", async () => {
179
+ vi.mocked(getAIContextRegistry).mockImplementation(() => {
180
+ throw new Error("Registry error");
181
+ });
182
+
183
+ const result = await parseContextFromPrompt("prompt with @context1");
184
+
185
+ expect(result).toEqual({
186
+ contextBlocks: [],
187
+ attachmentBlocks: [],
188
+ });
189
+ });
190
+
191
+ it("should handle attachment errors gracefully", async () => {
192
+ mockRegistry.parseAllContextIds.mockReturnValue([CONTEXT_ID]);
193
+ mockRegistry.formatContextForAI.mockReturnValue("formatted context");
194
+ mockRegistry.getAttachmentsForContext.mockRejectedValue(
195
+ new Error("Attachment error"),
196
+ );
197
+
198
+ const result = await parseContextFromPrompt("prompt with @context1");
199
+
200
+ expect(result.contextBlocks).toHaveLength(1);
201
+ expect(result.attachmentBlocks).toHaveLength(0);
202
+ });
203
+
204
+ it("should use url as name when filename is not provided", async () => {
205
+ mockRegistry.parseAllContextIds.mockReturnValue([CONTEXT_ID]);
206
+ mockRegistry.formatContextForAI.mockReturnValue("formatted context");
207
+ mockRegistry.getAttachmentsForContext.mockResolvedValue([
208
+ {
209
+ type: "file",
210
+ url: "http://example.com/file.pdf",
211
+ mediaType: "application/pdf",
212
+ filename: undefined,
213
+ },
214
+ ]);
215
+
216
+ const result = await parseContextFromPrompt("prompt with @context1");
217
+
218
+ expect((result.attachmentBlocks[0] as { name: string }).name).toBe(
219
+ "http://example.com/file.pdf",
220
+ );
221
+ });
222
+ });
@@ -1,4 +1,4 @@
1
- /* Copyright 2025 Marimo. All rights reserved. */
1
+ /* Copyright 2024 Marimo. All rights reserved. */
2
2
 
3
3
  import { describe, expect, it } from "vitest";
4
4
  import { getAgentPrompt } from "../prompt";
@@ -604,17 +604,13 @@ describe("state utility functions", () => {
604
604
  describe("getAgentConnectionCommand", () => {
605
605
  it("should return correct command for claude", () => {
606
606
  expect(getAgentConnectionCommand("claude")).toMatchInlineSnapshot(`
607
- "npx supergateway --stdio\\
608
- "npx @zed-industries/claude-code-acp" \\
609
- --outputTransport ws --port 3017 "
607
+ "npx stdio-to-ws "npx @zed-industries/claude-code-acp" --port 3017"
610
608
  `);
611
609
  });
612
610
 
613
611
  it("should return correct command for gemini", () => {
614
612
  expect(getAgentConnectionCommand("gemini")).toMatchInlineSnapshot(`
615
- "npx supergateway --stdio\\
616
- "npx @google/gemini-cli --experimental-acp" \\
617
- --outputTransport ws --port 3019 "
613
+ "npx stdio-to-ws "npx @google/gemini-cli --experimental-acp" --port 3019"
618
614
  `);
619
615
  });
620
616
  });
@@ -1,9 +1,14 @@
1
1
  /* Copyright 2024 Marimo. All rights reserved. */
2
2
 
3
- import { TerminalIcon } from "lucide-react";
3
+ import { useAtomValue } from "jotai";
4
+ import { TerminalIcon, TerminalSquareIcon } from "lucide-react";
4
5
  import { memo } from "react";
5
6
  import { AiProviderIcon } from "@/components/ai/ai-provider-icon";
6
7
  import { CopyClipboardIcon } from "@/components/icons/copy-icon";
8
+ import { useTerminalCommands } from "@/components/terminal/hooks";
9
+ import { Button } from "@/components/ui/button";
10
+ import { Tooltip } from "@/components/ui/tooltip";
11
+ import { capabilitiesAtom } from "@/core/config/capabilities";
7
12
  import { cn } from "@/utils/cn";
8
13
  import {
9
14
  type ExternalAgentId,
@@ -22,6 +27,12 @@ const AgentDocItem = memo<AgentDocItemProps>(
22
27
  ({ agentId, showCopy = true, className }) => {
23
28
  const command = getAgentConnectionCommand(agentId);
24
29
  const displayName = getAgentDisplayName(agentId);
30
+ const capabilities = useAtomValue(capabilitiesAtom);
31
+ const { sendCommand } = useTerminalCommands();
32
+
33
+ const handleSendToTerminal = () => {
34
+ sendCommand(command);
35
+ };
25
36
 
26
37
  return (
27
38
  <div className={cn("space-y-2", className)}>
@@ -32,12 +43,28 @@ const AgentDocItem = memo<AgentDocItemProps>(
32
43
  <div className="bg-muted/50 rounded-md p-2 border">
33
44
  <div className="flex items-start gap-2 text-xs">
34
45
  <TerminalIcon className="h-4 w-4 mt-0.5 text-muted-foreground flex-shrink-0" />
35
- <code className="text-xs font-mono break-all flex-1 whitespace-pre-wrap">
46
+ <code className="text-xs font-mono break-words flex-1 whitespace-pre-wrap">
36
47
  {command}
37
48
  </code>
38
- {showCopy && (
39
- <CopyClipboardIcon value={command} className="h-3 w-3" />
40
- )}
49
+ <div className="flex items-center gap-1 flex-shrink-0">
50
+ {showCopy && (
51
+ <Button size="xs" variant="outline" className="border">
52
+ <CopyClipboardIcon value={command} className="h-3 w-3" />
53
+ </Button>
54
+ )}
55
+ {capabilities.terminal && (
56
+ <Tooltip content="Run in terminal" delayDuration={100}>
57
+ <Button
58
+ onClick={handleSendToTerminal}
59
+ title="Send to terminal"
60
+ size="xs"
61
+ variant="outline"
62
+ >
63
+ <TerminalSquareIcon className="h-3 w-3" />
64
+ </Button>
65
+ </Tooltip>
66
+ )}
67
+ </div>
41
68
  </div>
42
69
  </div>
43
70
  </div>
@@ -48,7 +75,7 @@ AgentDocItem.displayName = "AgentDocItem";
48
75
 
49
76
  interface AgentDocsProps {
50
77
  title?: string;
51
- description?: string;
78
+ description?: React.ReactNode;
52
79
  agents?: ExternalAgentId[];
53
80
  showCopy?: boolean;
54
81
  className?: string;
@@ -1,23 +1,5 @@
1
- @source "../node_modules/streamdown/dist/index.js";
2
-
3
1
  .mo-agent-panel {
4
- /* Make headers a bit smaller */
5
- --text-2xl: 1rem;
6
- --text-3xl: 1rem;
7
-
8
2
  pre {
9
3
  width: 100%;
10
4
  }
11
-
12
- [data-code-block-container="true"] {
13
- margin-top: 0 !important;
14
- }
15
-
16
- [data-code-block-header="true"] {
17
- padding: 4px !important;
18
- }
19
-
20
- code {
21
- font-size: inherit !important;
22
- }
23
5
  }