@marimo-team/islands 0.20.5-dev9 → 0.20.5-dev90

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 (253) hide show
  1. package/dist/{Combination-Du-o_hC9.js → Combination-Dk6JxauT.js} +1 -1
  2. package/dist/{ConnectedDataExplorerComponent-DUS-zJoR.js → ConnectedDataExplorerComponent-Bh11efrC.js} +17 -17
  3. package/dist/{_baseIsEqual-5cAxzk6f.js → _baseIsEqual-CvgsjYoW.js} +38 -38
  4. package/dist/{_basePickBy-3JVb5wYm.js → _basePickBy-pTDW2_2A.js} +6 -6
  5. package/dist/{_baseUniq-DSSiFuIJ.js → _baseUniq-BUFhl85h.js} +1 -1
  6. package/dist/{any-language-editor-BL9o7y0_.js → any-language-editor-BIj11a2e.js} +19 -19
  7. package/dist/{architecture-7HQA4BMR-BxkNpYRp.js → architecture-7HQA4BMR-BmtmhGMc.js} +2 -2
  8. package/dist/{architectureDiagram-VXUJARFQ-DrJeyFHq.js → architectureDiagram-VXUJARFQ-Df0FNeBR.js} +14 -14
  9. package/dist/assets/__vite-browser-external-Us1ds95c.js +1 -0
  10. package/dist/assets/{worker-DUYMdbtA.js → worker-D10K3OOz.js} +2 -2
  11. package/dist/{blockDiagram-VD42YOAC-BJrP6qKc.js → blockDiagram-VD42YOAC-DszWqlLz.js} +7 -7
  12. package/dist/{button-KYalaJYu.js → button-DQpBib29.js} +24 -11
  13. package/dist/{c4Diagram-YG6GDRKO-Bo4gytQ5.js → c4Diagram-YG6GDRKO-Dyj8LoUX.js} +4 -4
  14. package/dist/{channel-IWLGkaBE.js → channel-CUFaIkTh.js} +1 -1
  15. package/dist/{check-C50jsehH.js → check-DpqPQmzz.js} +1 -1
  16. package/dist/{chunk-4F5CHEZ2-CxKDFd-t.js → chunk-4F5CHEZ2-CRwwZ2ED.js} +1 -1
  17. package/dist/{chunk-ABZYJK2D-CRwanrkd.js → chunk-ABZYJK2D-7QYXAAhe.js} +1 -1
  18. package/dist/{chunk-ATLVNIR6-CMMCMvOK.js → chunk-ATLVNIR6-pmHPAPSd.js} +1 -1
  19. package/dist/{chunk-B2363JML-e_W7KW1D.js → chunk-B2363JML-BuBMltZc.js} +1 -1
  20. package/dist/{chunk-B4BG7PRW-BNsHrGHG.js → chunk-B4BG7PRW-Dbta9cTX.js} +4 -4
  21. package/dist/{chunk-DI55MBZ5-DQeYbfMV.js → chunk-DI55MBZ5-DyKB35wC.js} +4 -4
  22. package/dist/{chunk-EXTU4WIE-CV_DQeaX.js → chunk-EXTU4WIE-BRFl4iNd.js} +1 -1
  23. package/dist/{chunk-FRFDVMJY-C7q09nvl.js → chunk-FRFDVMJY-Bk2LD5Te.js} +1 -1
  24. package/dist/{chunk-JA3XYJ7Z-Cmt--e0q.js → chunk-JA3XYJ7Z-BkrY9SdL.js} +2 -2
  25. package/dist/{chunk-JZLCHNYA-CkyMJnI9.js → chunk-JZLCHNYA-Bk_Lil-q.js} +4 -4
  26. package/dist/{chunk-N4CR4FBY-BJfHtJbD.js → chunk-N4CR4FBY-f5n6meOd.js} +5 -5
  27. package/dist/{chunk-PL6DKKU2-ChKBqnoD.js → chunk-PL6DKKU2-DiFkzMfM.js} +1 -1
  28. package/dist/{chunk-QN33PNHL-WOLIPUAJ.js → chunk-QN33PNHL-CXfJywHv.js} +1 -1
  29. package/dist/{chunk-QXUST7PY-DYuD50pU.js → chunk-QXUST7PY-D7-26sj3.js} +5 -5
  30. package/dist/{chunk-S3R3BYOJ-CsnX6RKs.js → chunk-S3R3BYOJ-BRT9vd1R.js} +3 -3
  31. package/dist/{chunk-SJTYNZTY-j6_1s5om.js → chunk-SJTYNZTY-BvVkbShU.js} +1 -1
  32. package/dist/{chunk-TCCFYFTB-DdLCbCTn.js → chunk-TCCFYFTB-DqxhgXG0.js} +31 -31
  33. package/dist/{chunk-TQ3KTPDO-CGsUIC73.js → chunk-TQ3KTPDO-CPkEruAA.js} +1 -1
  34. package/dist/{chunk-TZMSLE5B-B3eYTGCw.js → chunk-TZMSLE5B-DSfBOnzx.js} +1 -1
  35. package/dist/{chunk-UMXZTB3W--LdAK3Bv.js → chunk-UMXZTB3W-C4ypIY3V.js} +1 -1
  36. package/dist/{classDiagram-v2-WZHVMYZB-UTw37Gg8.js → classDiagram-2ON5EDUG-DphiMW3Y.js} +10 -10
  37. package/dist/{classDiagram-2ON5EDUG-C7C-oefv.js → classDiagram-v2-WZHVMYZB-BH1x5h4a.js} +10 -10
  38. package/dist/{clone-BJrS4PdE.js → clone-CEQ-pda1.js} +1 -1
  39. package/dist/{constants-D1Tbg_6B.js → constants-CytQ_3LM.js} +3 -3
  40. package/dist/{copy-oc-FcZzt.js → copy-BkBF0Xgk.js} +2 -2
  41. package/dist/{dagre-6UL2VRFP-BgsUhJrV.js → dagre-6UL2VRFP-DGEbtmgU.js} +12 -12
  42. package/dist/{dagre-CyZCGfV_.js → dagre-BVnNvbvD.js} +37 -37
  43. package/dist/{diagram-PSM6KHXK-BIUUOfKo.js → diagram-PSM6KHXK-CG_usglE.js} +15 -15
  44. package/dist/{diagram-QEK2KX5R-BFjolZQv.js → diagram-QEK2KX5R-CtGFEwzJ.js} +13 -13
  45. package/dist/{diagram-S2PKOQOG-4jfkWoZw.js → diagram-S2PKOQOG-ClKAGmbv.js} +13 -13
  46. package/dist/dist-B4MxkKHf.js +8 -0
  47. package/dist/{dist-De9X_Des.js → dist-B9EjSb9T.js} +1 -1
  48. package/dist/{dist-IW_ARJ3S.js → dist-BFxYppVR.js} +4 -4
  49. package/dist/{dist-D7ZGWV_9.js → dist-BGZ7TWS9.js} +3 -3
  50. package/dist/{dist-CwtEWuFb.js → dist-BSfYc7vq.js} +2 -2
  51. package/dist/{dist-DMS81OrU.js → dist-BUrWeMEP.js} +1 -1
  52. package/dist/dist-BYghZv6b.js +5 -0
  53. package/dist/dist-Be-uQhz5.js +6 -0
  54. package/dist/{dist-Ch_JuCvc.js → dist-BpMlUdNO.js} +3 -3
  55. package/dist/{dist-C6z8U-ms.js → dist-Bq5eYK43.js} +2 -2
  56. package/dist/{dist-BFL9TlzD.js → dist-Bq9zYwJs.js} +5 -5
  57. package/dist/{dist-7ZF--V_D.js → dist-C4K7pumm.js} +2 -2
  58. package/dist/{dist-Qjf6pcqK.js → dist-CAKwXCWI.js} +2 -2
  59. package/dist/dist-CB_xf0ju.js +5 -0
  60. package/dist/{dist-BwQHkjA9.js → dist-CDHl2i1x.js} +4 -4
  61. package/dist/dist-CK0qFAbF.js +8 -0
  62. package/dist/{dist-C4XMUaob.js → dist-CPlGUbk-.js} +2 -2
  63. package/dist/{dist-BT6_J2eq.js → dist-CSEWGuDq.js} +7 -2
  64. package/dist/dist-CYEk-qrr.js +8 -0
  65. package/dist/{dist-CYo3w-nC.js → dist-Cl5iM8xL.js} +3 -3
  66. package/dist/dist-CmKoWpMk.js +5 -0
  67. package/dist/{dist-I8MQW60_.js → dist-CseYuPtL.js} +2 -2
  68. package/dist/dist-D1nf4IQl.js +5 -0
  69. package/dist/{dist-CsqiXw7J.js → dist-D4gcY469.js} +2 -2
  70. package/dist/{dist-DUxS2paD.js → dist-D5NMgbbv.js} +2 -2
  71. package/dist/{dist-UYm1IE5s.js → dist-DERtJN02.js} +2 -2
  72. package/dist/{dist-CFToYDWO.js → dist-DEj2X26M.js} +2 -2
  73. package/dist/{dist-BuapEdlD.js → dist-DOoqn-VL.js} +70 -67
  74. package/dist/{dist-BLThQiU4.js → dist-DUretbKK.js} +2 -2
  75. package/dist/{dist-DEFZ7dnD.js → dist-D_-CGmlh.js} +2 -2
  76. package/dist/dist-Df3AcKpt.js +6 -0
  77. package/dist/dist-DgaFHt_I.js +5 -0
  78. package/dist/dist-Dk10C3ui.js +5 -0
  79. package/dist/{dist-D0f6Yrrb.js → dist-DodLQWPg.js} +1 -1
  80. package/dist/dist-DtyPVMHR.js +5 -0
  81. package/dist/{dist-Cb3cLT39.js → dist-HoZO6brh.js} +2 -2
  82. package/dist/{dist-Cqpjy6bK.js → dist-RNGn_-uD.js} +1 -1
  83. package/dist/{dist-BBcqvpvP.js → dist-Ux6dL_VB.js} +1 -1
  84. package/dist/{dist-B8Y11RWn.js → dist-WIWVvdBh.js} +2 -2
  85. package/dist/{dist-CB6qhQ8K.js → dist-gc9KgJuA.js} +1 -1
  86. package/dist/{dist-ovDpXuSB.js → dist-i-ud9aCA.js} +1 -1
  87. package/dist/dist-ko7WnHAO.js +5 -0
  88. package/dist/{dist-BTQbjEKU.js → dist-lNe4i1Nm.js} +1 -1
  89. package/dist/dist-of7gLRFK.js +8 -0
  90. package/dist/{erDiagram-Q2GNP2WA-Cq5Bz5lG.js → erDiagram-Q2GNP2WA-DPMseVVp.js} +10 -10
  91. package/dist/{error-banner-D0tXnwl4.js → error-banner-BctofTCP.js} +2 -2
  92. package/dist/{esm-BxMbHo0y.js → esm-BBkPJL8N.js} +29 -27
  93. package/dist/{flowDiagram-NV44I4VS-6WPJVFl7.js → flowDiagram-NV44I4VS-BpAIFwW7.js} +10 -10
  94. package/dist/{ganttDiagram-JELNMOA3-AfDhh9CI.js → ganttDiagram-JELNMOA3-DXYghZ9C.js} +3 -3
  95. package/dist/{gitGraph-G5XIXVHT-C0o6gecv.js → gitGraph-G5XIXVHT-ChHUSAop.js} +2 -2
  96. package/dist/{gitGraphDiagram-V2S2FVAM-BRSwuj0Q.js → gitGraphDiagram-V2S2FVAM-CBL-7g3_.js} +12 -12
  97. package/dist/{glide-data-editor-ByPNTNVG.js → glide-data-editor-DqxJOnJk.js} +63 -63
  98. package/dist/{graphlib-DZnBMcsX.js → graphlib-D18eZCT4.js} +10 -10
  99. package/dist/hasIn-B9AbGLj3.js +86 -0
  100. package/dist/{info-VBDWY6EO-Bzsods6X.js → info-VBDWY6EO-CwyXEo8E.js} +2 -2
  101. package/dist/{infoDiagram-HS3SLOUP-Cmxo6jKx.js → infoDiagram-HS3SLOUP-BXGbfBss.js} +12 -12
  102. package/dist/{isArrayLikeObject-Btu-i6_P.js → isArrayLikeObject-BrYl-ETg.js} +25 -26
  103. package/dist/{isEmpty-CZvUtYFp.js → isEmpty-C-xMag79.js} +2 -2
  104. package/dist/{isString-CBr7TEb7.js → isString-D-vNYDBA.js} +1 -1
  105. package/dist/{isSymbol-BuQsMXhk.js → isSymbol-Dyt2NSnN.js} +1 -1
  106. package/dist/{journeyDiagram-XKPGCS4Q-CKYr8cSR.js → journeyDiagram-XKPGCS4Q-D5BIjS4N.js} +3 -3
  107. package/dist/{kanban-definition-3W4ZIXB7-DVvAZzQD.js → kanban-definition-3W4ZIXB7-DhDkqxFB.js} +7 -7
  108. package/dist/{label-CV0KYhtH.js → label-BLDcDYdI.js} +6 -6
  109. package/dist/{loader-eJCvvApN.js → loader-DsE3MiYo.js} +2 -2
  110. package/dist/main.js +1673 -1163
  111. package/dist/{memoize-P1T1IGb9.js → memoize-Cs8aS5RW.js} +1 -1
  112. package/dist/merge-NuyC7LN7.js +51 -0
  113. package/dist/{mermaid-COOB_abB.js → mermaid-DkdSmFY8.js} +42 -42
  114. package/dist/{mermaid-parser.core-Cd-wu4tE.js → mermaid-parser.core-OkWZ8nr-.js} +8 -8
  115. package/dist/{min-CMDDtXJP.js → min-ECVRnCdn.js} +30 -30
  116. package/dist/{mindmap-definition-VGOIOE7T-1ExmnvYy.js → mindmap-definition-VGOIOE7T-BxQi78Vl.js} +9 -9
  117. package/dist/{now-BxlRp0OQ.js → now-BC2mX0ZT.js} +1 -1
  118. package/dist/{packet-DYOGHKS2-Bf1CvFco.js → packet-DYOGHKS2-C62XQjZh.js} +2 -2
  119. package/dist/{pie-VRWISCQL-LY_wbqji.js → pie-VRWISCQL-nfAKQJw3.js} +2 -2
  120. package/dist/{pieDiagram-ADFJNKIX-CJlIsdsU.js → pieDiagram-ADFJNKIX-DfSJXUHa.js} +13 -13
  121. package/dist/{purify.es-CyOIw8ru.js → purify.es-DGenX2XH.js} +67 -67
  122. package/dist/{quadrantDiagram-AYHSOK5B-BU78RiaH.js → quadrantDiagram-AYHSOK5B-CAcVWXc-.js} +2 -2
  123. package/dist/{radar-ZZBFDIW7-Ro3iXZCk.js → radar-ZZBFDIW7-lopS8_4j.js} +2 -2
  124. package/dist/{range-Dh0_-r8P.js → range-BKaWvVUE.js} +8 -8
  125. package/dist/reduce-CqQo8ppc.js +275 -0
  126. package/dist/{requirementDiagram-UZGBJVZJ-DACHtrFr.js → requirementDiagram-UZGBJVZJ-BU7dwzFM.js} +9 -9
  127. package/dist/{sankeyDiagram-TZEHDZUN-Bzg7_UWs.js → sankeyDiagram-TZEHDZUN-BVJnR4_b.js} +2 -2
  128. package/dist/{sequenceDiagram-WL72ISMW-agybEe9J.js → sequenceDiagram-WL72ISMW-CQcFQTwX.js} +4 -4
  129. package/dist/{slides-component-B0yK5GXP.js → slides-component-DwvL_HJi.js} +2 -2
  130. package/dist/{spec-Dq_reDGM.js → spec-CbYkiXG3.js} +5 -5
  131. package/dist/{stateDiagram-FKZM4ZOC-DehQAt8g.js → stateDiagram-FKZM4ZOC-Dx9AIGDe.js} +12 -12
  132. package/dist/{stateDiagram-v2-4FDKWEC3-8VzeREl9.js → stateDiagram-v2-4FDKWEC3-BIeUs-Ed.js} +10 -10
  133. package/dist/style.css +1 -1
  134. package/dist/{timeline-definition-IT6M3QCI-CdCfdaCF.js → timeline-definition-IT6M3QCI-D8B3p7ID.js} +2 -2
  135. package/dist/{toNumber-By7s5JC_.js → toNumber-CbZ70FdN.js} +2 -2
  136. package/dist/{toString-Ckpb50uw.js → toString-DbIAWQpF.js} +2 -2
  137. package/dist/{tooltip-CL8m4f9y.js → tooltip-SPkubVH3.js} +3 -3
  138. package/dist/{treemap-GDKQZRPO-DRxfDG65.js → treemap-GDKQZRPO-CkR-5ai2.js} +2 -2
  139. package/dist/{types-BwnzGcE4.js → types-0FB-N7AA.js} +519 -408
  140. package/dist/{uniq-cCc07Q8K.js → uniq-H2E5nMLq.js} +1 -1
  141. package/dist/{useAsyncData-B4hMFGnF.js → useAsyncData-D7-oahg5.js} +1 -1
  142. package/dist/{useDeepCompareMemoize-DuPhOXzr.js → useDeepCompareMemoize-DLS-bHHT.js} +5 -5
  143. package/dist/{useIframeCapabilities-CAt6D2EI.js → useIframeCapabilities-DFGZKWkO.js} +1 -1
  144. package/dist/{useTheme-BNYQnvu-.js → useTheme-D0rdoMBF.js} +6 -5
  145. package/dist/{vega-component-DouPy8AI.js → vega-component-CnO3mkFC.js} +11 -11
  146. package/dist/{xychartDiagram-PRI3JC2R-rEm_SIsC.js → xychartDiagram-PRI3JC2R-XO8FiQjU.js} +5 -5
  147. package/package.json +9 -9
  148. package/src/__mocks__/common.ts +41 -8
  149. package/src/__mocks__/requests.ts +1 -0
  150. package/src/components/chat/__tests__/useFileState.test.tsx +2 -3
  151. package/src/components/chat/acp/__tests__/context-utils.test.ts +2 -6
  152. package/src/components/datasources/components.tsx +3 -6
  153. package/src/components/datasources/datasources.tsx +8 -21
  154. package/src/components/editor/__tests__/data-attributes.test.tsx +2 -11
  155. package/src/components/editor/actions/types.ts +6 -1
  156. package/src/components/editor/actions/useNotebookActions.tsx +50 -13
  157. package/src/components/editor/cell/cell-context-menu.tsx +2 -6
  158. package/src/components/editor/chrome/types.ts +17 -0
  159. package/src/components/editor/connections/add-connection-dialog.tsx +27 -2
  160. package/src/components/editor/connections/database/__tests__/__snapshots__/as-code.test.ts.snap +105 -6
  161. package/src/components/editor/connections/database/__tests__/as-code.test.ts +101 -8
  162. package/src/components/editor/connections/database/as-code.ts +115 -25
  163. package/src/components/editor/connections/database/schemas.ts +49 -2
  164. package/src/components/editor/connections/storage/as-code.ts +1 -1
  165. package/src/components/editor/controls/command-palette.tsx +7 -0
  166. package/src/components/editor/controls/keyboard-shortcuts.tsx +3 -1
  167. package/src/components/editor/file-tree/__tests__/requesting-tree.test.ts +2 -3
  168. package/src/components/editor/file-tree/file-explorer.tsx +48 -62
  169. package/src/components/editor/file-tree/file-icons.tsx +132 -0
  170. package/src/components/editor/file-tree/file-viewer.tsx +1 -1
  171. package/src/components/editor/file-tree/tree-actions.tsx +107 -0
  172. package/src/components/editor/file-tree/types.ts +2 -96
  173. package/src/components/editor/header/filename-input.tsx +4 -1
  174. package/src/components/editor/navigation/__tests__/clipboard.test.ts +2 -4
  175. package/src/components/editor/output/console/ConsoleOutput.tsx +51 -2
  176. package/src/components/editor/output/console/__tests__/ConsoleOutput.test.tsx +97 -16
  177. package/src/components/editor/package-alert.tsx +4 -0
  178. package/src/components/icons/marimo-icons.tsx +2 -2
  179. package/src/components/pages/home-page.tsx +5 -5
  180. package/src/components/storage/__tests__/storage-snippets.test.ts +253 -0
  181. package/src/components/storage/components.tsx +0 -38
  182. package/src/components/storage/storage-file-viewer.tsx +1 -1
  183. package/src/components/storage/storage-inspector.tsx +66 -51
  184. package/src/components/storage/storage-snippets.ts +67 -0
  185. package/src/components/ui/command.tsx +2 -0
  186. package/src/core/ai/tools/__tests__/run-cells-tool.test.ts +206 -0
  187. package/src/core/ai/tools/run-cells-tool.ts +75 -40
  188. package/src/core/alerts/state.ts +1 -0
  189. package/src/core/cells/__tests__/cells.test.ts +62 -0
  190. package/src/core/cells/__tests__/session.test.ts +2 -7
  191. package/src/core/cells/cells.ts +25 -3
  192. package/src/core/cells/ids.ts +2 -1
  193. package/src/core/codemirror/compat/__tests__/jupyter.test.ts +2 -3
  194. package/src/core/codemirror/keymaps/vim.ts +32 -3
  195. package/src/core/codemirror/markdown/__tests__/commands.test.ts +2 -3
  196. package/src/core/config/__tests__/config-schema.test.ts +6 -2
  197. package/src/core/config/config-schema.ts +1 -0
  198. package/src/core/config/feature-flag.tsx +1 -1
  199. package/src/core/dom/ui-element-constants.ts +15 -0
  200. package/src/core/dom/ui-element.ts +3 -2
  201. package/src/core/export/__tests__/hooks.test.ts +3 -10
  202. package/src/core/hotkeys/__tests__/hotkeys.test.ts +64 -1
  203. package/src/core/hotkeys/hotkeys.ts +29 -3
  204. package/src/core/islands/bridge.ts +1 -0
  205. package/src/core/islands/components/web-components.tsx +2 -1
  206. package/src/core/network/__tests__/requests-network.test.ts +17 -0
  207. package/src/core/network/requests-lazy.ts +1 -0
  208. package/src/core/network/requests-network.ts +9 -0
  209. package/src/core/network/requests-static.ts +1 -0
  210. package/src/core/network/requests-toasting.tsx +1 -0
  211. package/src/core/network/types.ts +1 -0
  212. package/src/core/runtime/__tests__/runtime.test.ts +2 -8
  213. package/src/core/storage/__tests__/state.test.ts +1 -0
  214. package/src/core/wasm/bridge.ts +1 -0
  215. package/src/core/websocket/useMarimoKernelConnection.tsx +2 -0
  216. package/src/plugins/impl/DataTablePlugin.tsx +53 -3
  217. package/src/plugins/impl/FileBrowserPlugin.tsx +8 -5
  218. package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +2 -11
  219. package/src/plugins/impl/__tests__/DropdownPlugin.test.tsx +2 -11
  220. package/src/plugins/impl/anywidget/AnyWidgetPlugin.tsx +4 -1
  221. package/src/plugins/impl/data-explorer/ConnectedDataExplorerComponent.tsx +8 -1
  222. package/src/plugins/impl/mpl-interactive/MplInteractivePlugin.tsx +309 -0
  223. package/src/plugins/impl/mpl-interactive/__tests__/mpl-websocket-shim.test.ts +110 -0
  224. package/src/plugins/impl/mpl-interactive/mpl-websocket-shim.ts +57 -0
  225. package/src/plugins/impl/plotly/PlotlyPlugin.tsx +8 -2
  226. package/src/plugins/impl/vega/vega-component.tsx +7 -1
  227. package/src/plugins/impl/vega/vega.css +4 -11
  228. package/src/plugins/plugins.ts +2 -0
  229. package/src/utils/__tests__/copy.test.ts +129 -0
  230. package/src/utils/__tests__/download.test.tsx +12 -14
  231. package/src/utils/__tests__/filenames.test.ts +7 -0
  232. package/src/utils/__tests__/smartMatch.test.ts +61 -0
  233. package/src/utils/copy.ts +43 -0
  234. package/src/utils/filenames.ts +3 -0
  235. package/src/utils/smartMatch.ts +62 -0
  236. package/dist/_baseProperty-D1nWkRMz.js +0 -93
  237. package/dist/assets/__vite-browser-external-WSlCcXn_.js +0 -1
  238. package/dist/dist-BAeGo2rp.js +0 -5
  239. package/dist/dist-BqwCMSEa.js +0 -5
  240. package/dist/dist-Bum8FwTO.js +0 -6
  241. package/dist/dist-C0YiOwt_.js +0 -5
  242. package/dist/dist-C2uPv4iU.js +0 -5
  243. package/dist/dist-C5hOLsJN.js +0 -8
  244. package/dist/dist-C9NIAKMs.js +0 -8
  245. package/dist/dist-CCrzTtvk.js +0 -5
  246. package/dist/dist-CFS9i1rS.js +0 -8
  247. package/dist/dist-CyHZuhPH.js +0 -5
  248. package/dist/dist-CzcjWdIk.js +0 -6
  249. package/dist/dist-DaYyUSNC.js +0 -5
  250. package/dist/dist-DpDcJYNh.js +0 -8
  251. package/dist/dist-U_BfxcPn.js +0 -5
  252. package/dist/merge-CGQkMGzr.js +0 -51
  253. package/dist/reduce-BXFHs7IQ.js +0 -268
@@ -8,15 +8,9 @@ import CoreweaveIcon from "@marimo-team/llm-info/icons/coreweave.svg?inline";
8
8
  import CoreweaveDarkIcon from "@marimo-team/llm-info/icons/coreweave-dark.svg?inline";
9
9
  import {
10
10
  DatabaseZapIcon,
11
- FileCodeIcon,
12
- FileIcon,
13
- FileSpreadsheetIcon,
14
- FileTextIcon,
15
- FileVideoIcon,
16
11
  GithubIcon,
17
12
  GlobeIcon,
18
13
  HardDriveIcon,
19
- ImageIcon,
20
14
  } from "lucide-react";
21
15
  import GoogleCloudIcon from "@/components/databases/icons/google-cloud-storage.svg?inline";
22
16
  import GoogleDriveIcon from "@/components/databases/icons/google-drive.svg?inline";
@@ -24,38 +18,6 @@ import type { KnownStorageProtocol } from "@/core/storage/types";
24
18
  import { useTheme } from "@/theme/useTheme";
25
19
  import { cn } from "@/utils/cn";
26
20
 
27
- export function renderFileIcon(name: string): React.ReactNode {
28
- const ext = name.split(".").pop()?.toLowerCase();
29
- switch (ext) {
30
- case "png":
31
- case "jpg":
32
- case "jpeg":
33
- case "gif":
34
- case "svg":
35
- case "webp":
36
- return <ImageIcon className="h-3.5 w-3.5 text-purple-500" />;
37
- case "csv":
38
- case "parquet":
39
- case "arrow":
40
- case "xlsx":
41
- return <FileSpreadsheetIcon className="h-3.5 w-3.5 text-green-500" />;
42
- case "py":
43
- case "js":
44
- case "ts":
45
- case "json":
46
- return <FileCodeIcon className="h-3.5 w-3.5 text-blue-500" />;
47
- case "mp4":
48
- case "mpeg":
49
- return <FileVideoIcon className="h-3.5 w-3.5 text-orange-500" />;
50
- case "txt":
51
- case "md":
52
- case "log":
53
- return <FileTextIcon className="h-3.5 w-3.5 text-muted-foreground" />;
54
- default:
55
- return <FileIcon className="h-3.5 w-3.5 text-muted-foreground" />;
56
- }
57
- }
58
-
59
21
  type IconEntry =
60
22
  | { src: string; dark?: string }
61
23
  | React.ComponentType<{ className?: string }>;
@@ -5,6 +5,7 @@ import type React from "react";
5
5
  import { useCallback } from "react";
6
6
  import { useLocale } from "react-aria";
7
7
  import { FilePreviewHeader } from "@/components/editor/file-tree/file-header";
8
+ import { renderFileIcon } from "@/components/editor/file-tree/file-icons";
8
9
  import {
9
10
  FileContentRenderer,
10
11
  isMediaMime,
@@ -18,7 +19,6 @@ import { formatBytes } from "@/utils/formatting";
18
19
  import { Logger } from "@/utils/Logger";
19
20
  import { CopyClipboardIcon } from "../icons/copy-icon";
20
21
  import { Button } from "../ui/button";
21
- import { renderFileIcon } from "./components";
22
22
 
23
23
  const MAX_MEDIA_PREVIEW_SIZE = 100 * 1024 * 1024; // 100 MB
24
24
 
@@ -2,29 +2,38 @@
2
2
 
3
3
  import { CommandList } from "cmdk";
4
4
  import {
5
- ChevronRightIcon,
6
5
  CopyIcon,
7
6
  DownloadIcon,
8
7
  FolderIcon,
9
8
  HardDriveIcon,
10
9
  HelpCircleIcon,
11
10
  LoaderCircle,
12
- MoreVerticalIcon,
13
11
  PlusIcon,
14
- RefreshCwIcon,
15
12
  ViewIcon,
16
13
  XIcon,
17
14
  } from "lucide-react";
18
15
  import React, { useCallback, useState } from "react";
19
16
  import { useLocale } from "react-aria";
20
17
  import { EngineVariable } from "@/components/databases/engine-variable";
18
+ import { useAddCodeToNewCell } from "@/components/editor/cell/useAddCell";
21
19
  import { PanelEmptyState } from "@/components/editor/chrome/panels/empty-state";
22
20
  import { AddConnectionDialog } from "@/components/editor/connections/add-connection-dialog";
21
+ import {
22
+ FILE_ICON_COLOR,
23
+ renderFileIcon,
24
+ } from "@/components/editor/file-tree/file-icons";
25
+ import {
26
+ MENU_ITEM_ICON_CLASS,
27
+ MoreActionsButton,
28
+ RefreshIconButton,
29
+ TreeChevron,
30
+ } from "@/components/editor/file-tree/tree-actions";
23
31
  import { Command, CommandInput, CommandItem } from "@/components/ui/command";
24
32
  import {
25
33
  DropdownMenu,
26
34
  DropdownMenuContent,
27
35
  DropdownMenuItem,
36
+ DropdownMenuSeparator,
28
37
  DropdownMenuTrigger,
29
38
  } from "@/components/ui/dropdown-menu";
30
39
  import { Tooltip } from "@/components/ui/tooltip";
@@ -40,7 +49,7 @@ import type {
40
49
  StorageNamespace,
41
50
  StoragePathKey,
42
51
  } from "@/core/storage/types";
43
- import { storagePathKey, storageUrl } from "@/core/storage/types";
52
+ import { storagePathKey } from "@/core/storage/types";
44
53
  import type { VariableName } from "@/core/variables/types";
45
54
  import { cn } from "@/utils/cn";
46
55
  import { copyToClipboard } from "@/utils/copy";
@@ -49,8 +58,9 @@ import { formatBytes } from "@/utils/formatting";
49
58
  import { Logger } from "@/utils/Logger";
50
59
  import { ErrorState } from "../datasources/components";
51
60
  import { Button } from "../ui/button";
52
- import { ProtocolIcon, renderFileIcon } from "./components";
61
+ import { ProtocolIcon } from "./components";
53
62
  import { StorageFileViewer } from "./storage-file-viewer";
63
+ import { STORAGE_SNIPPETS } from "./storage-snippets";
54
64
 
55
65
  interface OpenFileInfo {
56
66
  entry: StorageEntry;
@@ -138,6 +148,7 @@ const StorageEntryChildren: React.FC<{
138
148
  namespace: string;
139
149
  protocol: string;
140
150
  rootPath: string;
151
+ backendType: StorageNamespace["backendType"];
141
152
  prefix: string;
142
153
  depth: number;
143
154
  locale: string;
@@ -147,6 +158,7 @@ const StorageEntryChildren: React.FC<{
147
158
  namespace,
148
159
  protocol,
149
160
  rootPath,
161
+ backendType,
150
162
  prefix,
151
163
  depth,
152
164
  locale,
@@ -207,6 +219,7 @@ const StorageEntryChildren: React.FC<{
207
219
  namespace={namespace}
208
220
  protocol={protocol}
209
221
  rootPath={rootPath}
222
+ backendType={backendType}
210
223
  depth={depth}
211
224
  locale={locale}
212
225
  searchValue={searchValue}
@@ -222,6 +235,7 @@ const StorageEntryRow: React.FC<{
222
235
  namespace: string;
223
236
  protocol: string;
224
237
  rootPath: string;
238
+ backendType: StorageNamespace["backendType"];
225
239
  depth: number;
226
240
  locale: string;
227
241
  searchValue: string;
@@ -231,6 +245,7 @@ const StorageEntryRow: React.FC<{
231
245
  namespace,
232
246
  protocol,
233
247
  rootPath,
248
+ backendType,
234
249
  depth,
235
250
  locale,
236
251
  searchValue,
@@ -238,6 +253,7 @@ const StorageEntryRow: React.FC<{
238
253
  }) => {
239
254
  const [isExpanded, setIsExpanded] = useState(false);
240
255
  const { entriesByPath } = useStorage();
256
+ const addCodeToNewCell = useAddCodeToNewCell();
241
257
  const isDir = entry.kind === "directory";
242
258
  const name = displayName(entry.path);
243
259
  const hasSearch = !!searchValue.trim();
@@ -305,17 +321,14 @@ const StorageEntryRow: React.FC<{
305
321
  }}
306
322
  >
307
323
  {isDir ? (
308
- <ChevronRightIcon
309
- className={cn(
310
- "h-3 w-3 shrink-0 transition-transform",
311
- effectiveExpanded && "rotate-90",
312
- )}
313
- />
324
+ <TreeChevron isExpanded={effectiveExpanded} className="h-3 w-3" />
314
325
  ) : (
315
326
  <span className="w-3 shrink-0" />
316
327
  )}
317
328
  {isDir ? (
318
- <FolderIcon className="h-3.5 w-3.5 text-amber-500 shrink-0" />
329
+ <FolderIcon
330
+ className={cn("h-3.5 w-3.5 shrink-0", FILE_ICON_COLOR.directory)}
331
+ />
319
332
  ) : (
320
333
  renderFileIcon(name)
321
334
  )}
@@ -337,14 +350,10 @@ const StorageEntryRow: React.FC<{
337
350
  )}
338
351
  <DropdownMenu>
339
352
  <DropdownMenuTrigger asChild={true}>
340
- <Button
341
- variant="text"
342
- size="icon"
343
- className="opacity-0 group-hover:opacity-100 transition-opacity hover:shadow-none hover:text-link text-muted-foreground"
353
+ <MoreActionsButton
354
+ iconClassName="h-3 w-3"
344
355
  onClick={(e) => e.stopPropagation()}
345
- >
346
- <MoreVerticalIcon className="h-3 w-3" />
347
- </Button>
356
+ />
348
357
  </DropdownMenuTrigger>
349
358
  <DropdownMenuContent
350
359
  align="end"
@@ -355,26 +364,47 @@ const StorageEntryRow: React.FC<{
355
364
  <DropdownMenuItem
356
365
  onSelect={() => onOpenFile({ entry, namespace })}
357
366
  >
358
- <ViewIcon className="h-3.5 w-3.5 mr-2" />
367
+ <ViewIcon className={MENU_ITEM_ICON_CLASS} />
359
368
  View
360
369
  </DropdownMenuItem>
361
370
  )}
362
371
  <DropdownMenuItem
363
372
  onSelect={async () => {
364
- const url = storageUrl(protocol, rootPath, entry.path);
365
- await copyToClipboard(url.toString());
373
+ await copyToClipboard(entry.path);
366
374
  toast({ title: "Copied to clipboard" });
367
375
  }}
368
376
  >
369
- <CopyIcon className="h-3.5 w-3.5 mr-2" />
370
- Copy URL
377
+ <CopyIcon className={MENU_ITEM_ICON_CLASS} />
378
+ Copy path
371
379
  </DropdownMenuItem>
372
380
  {!isDir && (
373
381
  <DropdownMenuItem onSelect={() => handleDownload()}>
374
- <DownloadIcon className="h-3.5 w-3.5 mr-2" />
382
+ <DownloadIcon className={MENU_ITEM_ICON_CLASS} />
375
383
  Download
376
384
  </DropdownMenuItem>
377
385
  )}
386
+ <DropdownMenuSeparator />
387
+ {STORAGE_SNIPPETS.map((snippet) => {
388
+ const code = snippet.getCode({
389
+ variableName: namespace,
390
+ protocol,
391
+ entry,
392
+ backendType,
393
+ });
394
+ if (code === null) {
395
+ return null;
396
+ }
397
+ const Icon = snippet.icon;
398
+ return (
399
+ <DropdownMenuItem
400
+ key={snippet.id}
401
+ onSelect={() => addCodeToNewCell(code)}
402
+ >
403
+ <Icon className={MENU_ITEM_ICON_CLASS} />
404
+ {snippet.label}
405
+ </DropdownMenuItem>
406
+ );
407
+ })}
378
408
  </DropdownMenuContent>
379
409
  </DropdownMenu>
380
410
  </div>
@@ -384,6 +414,7 @@ const StorageEntryRow: React.FC<{
384
414
  namespace={namespace}
385
415
  protocol={protocol}
386
416
  rootPath={rootPath}
417
+ backendType={backendType}
387
418
  prefix={entry.path}
388
419
  depth={depth + 1}
389
420
  locale={locale}
@@ -402,7 +433,6 @@ const StorageNamespaceSection: React.FC<{
402
433
  onOpenFile: (info: OpenFileInfo) => void;
403
434
  }> = ({ namespace, locale, searchValue, onOpenFile }) => {
404
435
  const [isExpanded, setIsExpanded] = useState(true);
405
- const [isSpinning, setIsSpinning] = useState(false);
406
436
  const { entriesByPath } = useStorage();
407
437
  const { clearNamespaceCache } = useStorageActions();
408
438
  const namespaceName = namespace.name ?? namespace.displayName;
@@ -417,10 +447,8 @@ const StorageNamespaceSection: React.FC<{
417
447
  const handleRefresh = useCallback(
418
448
  (e: React.MouseEvent) => {
419
449
  e.stopPropagation();
420
- setIsSpinning(true);
421
450
  clearNamespaceCache(namespaceName);
422
451
  refetch();
423
- setTimeout(() => setIsSpinning(false), 500);
424
452
  },
425
453
  [namespaceName, clearNamespaceCache, refetch],
426
454
  );
@@ -441,12 +469,7 @@ const StorageNamespaceSection: React.FC<{
441
469
  onSelect={() => setIsExpanded(!isExpanded)}
442
470
  className="flex flex-row font-semibold h-7 text-xs gap-1.5 bg-(--slate-2) text-muted-foreground rounded-none"
443
471
  >
444
- <ChevronRightIcon
445
- className={cn(
446
- "h-3 w-3 shrink-0 transition-transform",
447
- isExpanded && "rotate-90",
448
- )}
449
- />
472
+ <TreeChevron isExpanded={isExpanded} className="h-3 w-3" />
450
473
  <ProtocolIcon protocol={namespace.protocol} />
451
474
  <span>{namespace.displayName}</span>
452
475
  {namespace.name && (
@@ -454,21 +477,12 @@ const StorageNamespaceSection: React.FC<{
454
477
  (<EngineVariable variableName={namespace.name as VariableName} />)
455
478
  </span>
456
479
  )}
457
- <Tooltip content="Refresh storage connection">
458
- <Button
459
- variant="ghost"
460
- size="icon"
461
- className="hover:bg-transparent hover:shadow-none"
462
- onClick={handleRefresh}
463
- >
464
- <RefreshCwIcon
465
- className={cn(
466
- "h-3 w-3 text-muted-foreground hover:text-foreground",
467
- isSpinning && "animate-[spin_0.5s]",
468
- )}
469
- />
470
- </Button>
471
- </Tooltip>
480
+ <RefreshIconButton
481
+ onClick={handleRefresh}
482
+ tooltip="Refresh storage connection"
483
+ className="p-0"
484
+ iconClassName="h-3 w-3"
485
+ />
472
486
  <span className="text-[10px] text-muted-foreground font-normal tabular-nums ml-auto">
473
487
  {namespace.rootPath || "(root)"}
474
488
  </span>
@@ -488,7 +502,7 @@ const StorageNamespaceSection: React.FC<{
488
502
  <ErrorState
489
503
  error={error}
490
504
  style={indentStyle(1)}
491
- className="py-1 text-xs h-auto"
505
+ className="py-1 text-xs h-auto overflow-auto max-h-32 items-start"
492
506
  showIcon={false}
493
507
  />
494
508
  )}
@@ -515,6 +529,7 @@ const StorageNamespaceSection: React.FC<{
515
529
  namespace={namespaceName}
516
530
  protocol={namespace.protocol}
517
531
  rootPath={namespace.rootPath}
532
+ backendType={namespace.backendType}
518
533
  depth={1}
519
534
  locale={locale}
520
535
  searchValue={searchValue}
@@ -540,7 +555,7 @@ export const StorageInspector: React.FC = () => {
540
555
  title="No storage connected"
541
556
  description={
542
557
  <span>
543
- Create an Obstore or fsspec connection in your notebook. See the{" "}
558
+ Create an obstore or fsspec connection in your notebook. See the{" "}
544
559
  <a
545
560
  className="text-link"
546
561
  href="https://docs.marimo.io/guides/working_with_data/remote_storage/#quick-start"
@@ -0,0 +1,67 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import type { LucideIcon } from "lucide-react";
4
+ import { BookOpenIcon, LinkIcon } from "lucide-react";
5
+ import type { StorageEntry, StorageNamespace } from "@/core/storage/types";
6
+
7
+ type BackendType = StorageNamespace["backendType"];
8
+
9
+ export interface StorageSnippetContext {
10
+ variableName: string;
11
+ protocol: string;
12
+ entry: StorageEntry;
13
+ backendType: BackendType;
14
+ }
15
+
16
+ export interface StorageSnippet {
17
+ id: string;
18
+ label: string;
19
+ icon: LucideIcon;
20
+ /** Return the code string, or null to hide the snippet for this context. */
21
+ getCode: (ctx: StorageSnippetContext) => string | null;
22
+ }
23
+
24
+ const NOT_SIGNABLE_PROTOCOLS = new Set(["http", "file", "in-memory"]);
25
+
26
+ function escapeForPythonString(value: string): string {
27
+ return JSON.stringify(value).slice(1, -1);
28
+ }
29
+
30
+ export const STORAGE_SNIPPETS: StorageSnippet[] = [
31
+ {
32
+ id: "read-file",
33
+ label: "Insert read snippet",
34
+ icon: BookOpenIcon,
35
+ getCode: (ctx) => {
36
+ if (ctx.entry.kind === "directory") {
37
+ return null;
38
+ }
39
+ const path = escapeForPythonString(ctx.entry.path);
40
+ if (ctx.backendType === "obstore") {
41
+ return `_data = ${ctx.variableName}.get("${path}").bytes()\n_data`;
42
+ }
43
+ return `_data = ${ctx.variableName}.cat_file("${path}")\n_data`;
44
+ },
45
+ },
46
+ {
47
+ id: "download-file",
48
+ label: "Insert download snippet",
49
+ icon: LinkIcon,
50
+ getCode: (ctx) => {
51
+ if (ctx.entry.kind === "directory") {
52
+ return null;
53
+ }
54
+ const path = escapeForPythonString(ctx.entry.path);
55
+ if (ctx.backendType === "obstore") {
56
+ if (NOT_SIGNABLE_PROTOCOLS.has(ctx.protocol)) {
57
+ return null;
58
+ }
59
+ return `from datetime import timedelta\nfrom obstore import sign\n\nsigned_url = sign(\n ${ctx.variableName}, "GET", "${path}",\n expires_in=timedelta(hours=1),\n)\nsigned_url`;
60
+ }
61
+ const filename = escapeForPythonString(
62
+ ctx.entry.path.split("/").pop() || "download",
63
+ );
64
+ return `${ctx.variableName}.get("${path}", "${filename}")`;
65
+ },
66
+ },
67
+ ];
@@ -7,6 +7,7 @@ import { Search } from "lucide-react";
7
7
  import * as React from "react";
8
8
  import { Dialog, DialogContent } from "@/components/ui/dialog";
9
9
  import { cn } from "@/utils/cn";
10
+ import { smartMatchFilter } from "@/utils/smartMatch";
10
11
  import { Strings } from "@/utils/strings";
11
12
  import {
12
13
  MENU_ITEM_DISABLED,
@@ -21,6 +22,7 @@ const Command = React.forwardRef<
21
22
  >(({ className, ...props }, ref) => (
22
23
  <CommandPrimitive
23
24
  ref={ref}
25
+ filter={smartMatchFilter}
24
26
  className={cn(
25
27
  "flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
26
28
  className,
@@ -411,6 +411,212 @@ describe("RunStaleCellsTool", () => {
411
411
  });
412
412
  });
413
413
 
414
+ describe("output truncation", () => {
415
+ it("should summarize text/html output instead of dumping raw content", async () => {
416
+ const notebook = MockNotebook.notebookState({
417
+ cellData: {
418
+ [cellId1]: { code: "fig.show()", edited: true },
419
+ },
420
+ });
421
+ store.set(notebookAtom, notebook);
422
+
423
+ vi.mocked(runCells).mockImplementation(async () => {
424
+ const updatedNotebook = store.get(notebookAtom);
425
+ updatedNotebook.cellRuntime[cellId1] = {
426
+ ...updatedNotebook.cellRuntime[cellId1],
427
+ status: "idle",
428
+ };
429
+ store.set(notebookAtom, updatedNotebook);
430
+ });
431
+
432
+ const largeHtml = `<div>${"x".repeat(2_000_000)}</div>`;
433
+ vi.mocked(getCellContextData).mockReturnValue({
434
+ cellOutput: {
435
+ outputType: "text",
436
+ processedContent: null,
437
+ imageUrl: null,
438
+ output: { mimetype: "text/html", data: largeHtml },
439
+ },
440
+ consoleOutputs: null,
441
+ cellName: "cell1",
442
+ } as never);
443
+
444
+ const result = await tool.handler({}, toolContext as never);
445
+
446
+ expect(result.status).toBe("success");
447
+ const output = result.cellsToOutput?.[cellId1]?.cellOutput ?? "";
448
+ expect(output).toContain("HTML Output:");
449
+ expect(output).toContain("text/html");
450
+ expect(output.length).toBeLessThan(200);
451
+ expect(output).not.toContain(largeHtml);
452
+ });
453
+
454
+ it("should truncate large text output to MAX_TEXT_OUTPUT_CHARS", async () => {
455
+ const notebook = MockNotebook.notebookState({
456
+ cellData: {
457
+ [cellId1]: { code: "print(big_string)", edited: true },
458
+ },
459
+ });
460
+ store.set(notebookAtom, notebook);
461
+
462
+ vi.mocked(runCells).mockImplementation(async () => {
463
+ const updatedNotebook = store.get(notebookAtom);
464
+ updatedNotebook.cellRuntime[cellId1] = {
465
+ ...updatedNotebook.cellRuntime[cellId1],
466
+ status: "idle",
467
+ };
468
+ store.set(notebookAtom, updatedNotebook);
469
+ });
470
+
471
+ const largeText = "a".repeat(10_000);
472
+ vi.mocked(getCellContextData).mockReturnValue({
473
+ cellOutput: {
474
+ outputType: "text",
475
+ processedContent: largeText,
476
+ imageUrl: null,
477
+ output: { mimetype: "text/plain", data: largeText },
478
+ },
479
+ consoleOutputs: null,
480
+ cellName: "cell1",
481
+ } as never);
482
+
483
+ const result = await tool.handler({}, toolContext as never);
484
+
485
+ const output = result.cellsToOutput?.[cellId1]?.cellOutput ?? "";
486
+ expect(output).toContain("[TRUNCATED:");
487
+ expect(output).toContain("Full output visible in the notebook UI.");
488
+ // Output should be capped (2000 chars content + "Output:\n" prefix + truncation message)
489
+ expect(output.length).toBeLessThan(2200);
490
+ });
491
+
492
+ it("should omit output for cells that exceed total output budget", async () => {
493
+ const cellIds = Array.from(
494
+ { length: 25 },
495
+ (_, i) => `budget-cell-${i}` as CellId,
496
+ );
497
+ const cellData: Record<string, { code: string; edited: boolean }> = {};
498
+ for (const id of cellIds) {
499
+ cellData[id] = { code: "x = 1", edited: true };
500
+ }
501
+
502
+ const notebook = MockNotebook.notebookState({ cellData });
503
+ store.set(notebookAtom, notebook);
504
+
505
+ vi.mocked(runCells).mockImplementation(async () => {
506
+ const updatedNotebook = store.get(notebookAtom);
507
+ for (const id of cellIds) {
508
+ updatedNotebook.cellRuntime[id] = {
509
+ ...updatedNotebook.cellRuntime[id],
510
+ status: "idle",
511
+ };
512
+ }
513
+ store.set(notebookAtom, updatedNotebook);
514
+ });
515
+
516
+ // Each cell produces ~2008 chars of formatted output ("Output:\n" + 2000 chars).
517
+ // After 20 cells the running total exceeds MAX_TOOL_OUTPUT_CHARS (40,000).
518
+ const content = "a".repeat(2000);
519
+ vi.mocked(getCellContextData).mockReturnValue({
520
+ cellOutput: {
521
+ outputType: "text",
522
+ processedContent: content,
523
+ imageUrl: null,
524
+ output: { mimetype: "text/plain", data: content },
525
+ },
526
+ consoleOutputs: null,
527
+ cellName: "cell",
528
+ } as never);
529
+
530
+ const result = await tool.handler({}, toolContext as never);
531
+
532
+ expect(result.cellsToOutput?.[cellIds[0]]?.cellOutput).toContain(
533
+ "Output:",
534
+ );
535
+ expect(result.cellsToOutput?.[cellIds[24]]?.cellOutput).toBe(
536
+ "Cell executed (output omitted due to context limits).",
537
+ );
538
+ });
539
+
540
+ it("should use higher truncation limit for error outputs", async () => {
541
+ const notebook = MockNotebook.notebookState({
542
+ cellData: {
543
+ [cellId1]: { code: "raise Exception()", edited: true },
544
+ },
545
+ });
546
+ store.set(notebookAtom, notebook);
547
+
548
+ vi.mocked(runCells).mockImplementation(async () => {
549
+ const updatedNotebook = store.get(notebookAtom);
550
+ updatedNotebook.cellRuntime[cellId1] = {
551
+ ...updatedNotebook.cellRuntime[cellId1],
552
+ status: "idle",
553
+ };
554
+ store.set(notebookAtom, updatedNotebook);
555
+ });
556
+
557
+ // 2500 chars sits between MAX_TEXT_OUTPUT_CHARS (2000) and MAX_ERROR_OUTPUT_CHARS (3000)
558
+ const errorContent = "E".repeat(2500);
559
+ vi.mocked(getCellContextData).mockReturnValue({
560
+ cellOutput: {
561
+ outputType: "text",
562
+ processedContent: errorContent,
563
+ imageUrl: null,
564
+ output: {
565
+ mimetype: "application/vnd.marimo+error",
566
+ data: errorContent,
567
+ },
568
+ },
569
+ consoleOutputs: null,
570
+ cellName: "cell1",
571
+ } as never);
572
+
573
+ const result = await tool.handler({}, toolContext as never);
574
+
575
+ const output = result.cellsToOutput?.[cellId1]?.cellOutput ?? "";
576
+ expect(output).not.toContain("[TRUNCATED:");
577
+ expect(output).toContain(errorContent);
578
+ });
579
+
580
+ it("should truncate large console output", async () => {
581
+ const notebook = MockNotebook.notebookState({
582
+ cellData: {
583
+ [cellId1]: { code: 'print("x" * 10000)', edited: true },
584
+ },
585
+ });
586
+ store.set(notebookAtom, notebook);
587
+
588
+ vi.mocked(runCells).mockImplementation(async () => {
589
+ const updatedNotebook = store.get(notebookAtom);
590
+ updatedNotebook.cellRuntime[cellId1] = {
591
+ ...updatedNotebook.cellRuntime[cellId1],
592
+ status: "idle",
593
+ };
594
+ store.set(notebookAtom, updatedNotebook);
595
+ });
596
+
597
+ const largeConsoleText = "x".repeat(10_000);
598
+ vi.mocked(getCellContextData).mockReturnValue({
599
+ cellOutput: null,
600
+ consoleOutputs: [
601
+ {
602
+ outputType: "text",
603
+ processedContent: largeConsoleText,
604
+ imageUrl: null,
605
+ output: { mimetype: "text/plain", data: largeConsoleText },
606
+ },
607
+ ],
608
+ cellName: "cell1",
609
+ } as never);
610
+
611
+ const result = await tool.handler({}, toolContext as never);
612
+
613
+ const consoleOutput =
614
+ result.cellsToOutput?.[cellId1]?.consoleOutput ?? "";
615
+ expect(consoleOutput).toContain("[TRUNCATED:");
616
+ expect(consoleOutput.length).toBeLessThan(2200);
617
+ });
618
+ });
619
+
414
620
  describe("cell execution completion", () => {
415
621
  it("should complete immediately if cells are already idle", async () => {
416
622
  const notebook = MockNotebook.notebookState({