@marimo-team/islands 0.20.5-dev8 → 0.20.5-dev87
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.
- package/dist/{Combination-Du-o_hC9.js → Combination-Dk6JxauT.js} +1 -1
- package/dist/{ConnectedDataExplorerComponent-DUS-zJoR.js → ConnectedDataExplorerComponent-pQ4sWAoT.js} +11 -11
- package/dist/{_baseIsEqual-5cAxzk6f.js → _baseIsEqual-CvgsjYoW.js} +38 -38
- package/dist/{_basePickBy-3JVb5wYm.js → _basePickBy-pTDW2_2A.js} +6 -6
- package/dist/{_baseUniq-DSSiFuIJ.js → _baseUniq-BUFhl85h.js} +1 -1
- package/dist/{any-language-editor-BL9o7y0_.js → any-language-editor-BIj11a2e.js} +19 -19
- package/dist/{architecture-7HQA4BMR-BxkNpYRp.js → architecture-7HQA4BMR-BmtmhGMc.js} +2 -2
- package/dist/{architectureDiagram-VXUJARFQ-DrJeyFHq.js → architectureDiagram-VXUJARFQ-Df0FNeBR.js} +14 -14
- package/dist/assets/__vite-browser-external-Us1ds95c.js +1 -0
- package/dist/assets/{worker-DUYMdbtA.js → worker-D10K3OOz.js} +2 -2
- package/dist/{blockDiagram-VD42YOAC-BJrP6qKc.js → blockDiagram-VD42YOAC-DszWqlLz.js} +7 -7
- package/dist/{button-KYalaJYu.js → button-DQpBib29.js} +24 -11
- package/dist/{c4Diagram-YG6GDRKO-Bo4gytQ5.js → c4Diagram-YG6GDRKO-Dyj8LoUX.js} +4 -4
- package/dist/{channel-IWLGkaBE.js → channel-CUFaIkTh.js} +1 -1
- package/dist/{check-C50jsehH.js → check-DpqPQmzz.js} +1 -1
- package/dist/{chunk-4F5CHEZ2-CxKDFd-t.js → chunk-4F5CHEZ2-CRwwZ2ED.js} +1 -1
- package/dist/{chunk-ABZYJK2D-CRwanrkd.js → chunk-ABZYJK2D-7QYXAAhe.js} +1 -1
- package/dist/{chunk-ATLVNIR6-CMMCMvOK.js → chunk-ATLVNIR6-pmHPAPSd.js} +1 -1
- package/dist/{chunk-B2363JML-e_W7KW1D.js → chunk-B2363JML-BuBMltZc.js} +1 -1
- package/dist/{chunk-B4BG7PRW-BNsHrGHG.js → chunk-B4BG7PRW-Dbta9cTX.js} +4 -4
- package/dist/{chunk-DI55MBZ5-DQeYbfMV.js → chunk-DI55MBZ5-DyKB35wC.js} +4 -4
- package/dist/{chunk-EXTU4WIE-CV_DQeaX.js → chunk-EXTU4WIE-BRFl4iNd.js} +1 -1
- package/dist/{chunk-FRFDVMJY-C7q09nvl.js → chunk-FRFDVMJY-Bk2LD5Te.js} +1 -1
- package/dist/{chunk-JA3XYJ7Z-Cmt--e0q.js → chunk-JA3XYJ7Z-BkrY9SdL.js} +2 -2
- package/dist/{chunk-JZLCHNYA-CkyMJnI9.js → chunk-JZLCHNYA-Bk_Lil-q.js} +4 -4
- package/dist/{chunk-N4CR4FBY-BJfHtJbD.js → chunk-N4CR4FBY-f5n6meOd.js} +5 -5
- package/dist/{chunk-PL6DKKU2-ChKBqnoD.js → chunk-PL6DKKU2-DiFkzMfM.js} +1 -1
- package/dist/{chunk-QN33PNHL-WOLIPUAJ.js → chunk-QN33PNHL-CXfJywHv.js} +1 -1
- package/dist/{chunk-QXUST7PY-DYuD50pU.js → chunk-QXUST7PY-D7-26sj3.js} +5 -5
- package/dist/{chunk-S3R3BYOJ-CsnX6RKs.js → chunk-S3R3BYOJ-BRT9vd1R.js} +3 -3
- package/dist/{chunk-SJTYNZTY-j6_1s5om.js → chunk-SJTYNZTY-BvVkbShU.js} +1 -1
- package/dist/{chunk-TCCFYFTB-DdLCbCTn.js → chunk-TCCFYFTB-DqxhgXG0.js} +31 -31
- package/dist/{chunk-TQ3KTPDO-CGsUIC73.js → chunk-TQ3KTPDO-CPkEruAA.js} +1 -1
- package/dist/{chunk-TZMSLE5B-B3eYTGCw.js → chunk-TZMSLE5B-DSfBOnzx.js} +1 -1
- package/dist/{chunk-UMXZTB3W--LdAK3Bv.js → chunk-UMXZTB3W-C4ypIY3V.js} +1 -1
- package/dist/{classDiagram-v2-WZHVMYZB-UTw37Gg8.js → classDiagram-2ON5EDUG-DphiMW3Y.js} +10 -10
- package/dist/{classDiagram-2ON5EDUG-C7C-oefv.js → classDiagram-v2-WZHVMYZB-BH1x5h4a.js} +10 -10
- package/dist/{clone-BJrS4PdE.js → clone-CEQ-pda1.js} +1 -1
- package/dist/{constants-D1Tbg_6B.js → constants-CytQ_3LM.js} +3 -3
- package/dist/{copy-oc-FcZzt.js → copy-BkBF0Xgk.js} +2 -2
- package/dist/{dagre-6UL2VRFP-BgsUhJrV.js → dagre-6UL2VRFP-DGEbtmgU.js} +12 -12
- package/dist/{dagre-CyZCGfV_.js → dagre-BVnNvbvD.js} +37 -37
- package/dist/{diagram-PSM6KHXK-BIUUOfKo.js → diagram-PSM6KHXK-CG_usglE.js} +15 -15
- package/dist/{diagram-QEK2KX5R-BFjolZQv.js → diagram-QEK2KX5R-CtGFEwzJ.js} +13 -13
- package/dist/{diagram-S2PKOQOG-4jfkWoZw.js → diagram-S2PKOQOG-ClKAGmbv.js} +13 -13
- package/dist/dist-B4MxkKHf.js +8 -0
- package/dist/{dist-De9X_Des.js → dist-B9EjSb9T.js} +1 -1
- package/dist/{dist-IW_ARJ3S.js → dist-BFxYppVR.js} +4 -4
- package/dist/{dist-D7ZGWV_9.js → dist-BGZ7TWS9.js} +3 -3
- package/dist/{dist-CwtEWuFb.js → dist-BSfYc7vq.js} +2 -2
- package/dist/{dist-DMS81OrU.js → dist-BUrWeMEP.js} +1 -1
- package/dist/dist-BYghZv6b.js +5 -0
- package/dist/dist-Be-uQhz5.js +6 -0
- package/dist/{dist-Ch_JuCvc.js → dist-BpMlUdNO.js} +3 -3
- package/dist/{dist-C6z8U-ms.js → dist-Bq5eYK43.js} +2 -2
- package/dist/{dist-BFL9TlzD.js → dist-Bq9zYwJs.js} +5 -5
- package/dist/{dist-7ZF--V_D.js → dist-C4K7pumm.js} +2 -2
- package/dist/{dist-Qjf6pcqK.js → dist-CAKwXCWI.js} +2 -2
- package/dist/dist-CB_xf0ju.js +5 -0
- package/dist/{dist-BwQHkjA9.js → dist-CDHl2i1x.js} +4 -4
- package/dist/dist-CK0qFAbF.js +8 -0
- package/dist/{dist-C4XMUaob.js → dist-CPlGUbk-.js} +2 -2
- package/dist/{dist-BT6_J2eq.js → dist-CSEWGuDq.js} +7 -2
- package/dist/dist-CYEk-qrr.js +8 -0
- package/dist/{dist-CYo3w-nC.js → dist-Cl5iM8xL.js} +3 -3
- package/dist/dist-CmKoWpMk.js +5 -0
- package/dist/{dist-I8MQW60_.js → dist-CseYuPtL.js} +2 -2
- package/dist/dist-D1nf4IQl.js +5 -0
- package/dist/{dist-CsqiXw7J.js → dist-D4gcY469.js} +2 -2
- package/dist/{dist-DUxS2paD.js → dist-D5NMgbbv.js} +2 -2
- package/dist/{dist-UYm1IE5s.js → dist-DERtJN02.js} +2 -2
- package/dist/{dist-CFToYDWO.js → dist-DEj2X26M.js} +2 -2
- package/dist/{dist-BuapEdlD.js → dist-DOoqn-VL.js} +70 -67
- package/dist/{dist-BLThQiU4.js → dist-DUretbKK.js} +2 -2
- package/dist/{dist-DEFZ7dnD.js → dist-D_-CGmlh.js} +2 -2
- package/dist/dist-Df3AcKpt.js +6 -0
- package/dist/dist-DgaFHt_I.js +5 -0
- package/dist/dist-Dk10C3ui.js +5 -0
- package/dist/{dist-D0f6Yrrb.js → dist-DodLQWPg.js} +1 -1
- package/dist/dist-DtyPVMHR.js +5 -0
- package/dist/{dist-Cb3cLT39.js → dist-HoZO6brh.js} +2 -2
- package/dist/{dist-Cqpjy6bK.js → dist-RNGn_-uD.js} +1 -1
- package/dist/{dist-BBcqvpvP.js → dist-Ux6dL_VB.js} +1 -1
- package/dist/{dist-B8Y11RWn.js → dist-WIWVvdBh.js} +2 -2
- package/dist/{dist-CB6qhQ8K.js → dist-gc9KgJuA.js} +1 -1
- package/dist/{dist-ovDpXuSB.js → dist-i-ud9aCA.js} +1 -1
- package/dist/dist-ko7WnHAO.js +5 -0
- package/dist/{dist-BTQbjEKU.js → dist-lNe4i1Nm.js} +1 -1
- package/dist/dist-of7gLRFK.js +8 -0
- package/dist/{erDiagram-Q2GNP2WA-Cq5Bz5lG.js → erDiagram-Q2GNP2WA-DPMseVVp.js} +10 -10
- package/dist/{error-banner-D0tXnwl4.js → error-banner-BctofTCP.js} +2 -2
- package/dist/{esm-BxMbHo0y.js → esm-BBkPJL8N.js} +29 -27
- package/dist/{flowDiagram-NV44I4VS-6WPJVFl7.js → flowDiagram-NV44I4VS-BpAIFwW7.js} +10 -10
- package/dist/{ganttDiagram-JELNMOA3-AfDhh9CI.js → ganttDiagram-JELNMOA3-DXYghZ9C.js} +3 -3
- package/dist/{gitGraph-G5XIXVHT-C0o6gecv.js → gitGraph-G5XIXVHT-ChHUSAop.js} +2 -2
- package/dist/{gitGraphDiagram-V2S2FVAM-BRSwuj0Q.js → gitGraphDiagram-V2S2FVAM-CBL-7g3_.js} +12 -12
- package/dist/{glide-data-editor-ByPNTNVG.js → glide-data-editor-DqxJOnJk.js} +63 -63
- package/dist/{graphlib-DZnBMcsX.js → graphlib-D18eZCT4.js} +10 -10
- package/dist/hasIn-B9AbGLj3.js +86 -0
- package/dist/{info-VBDWY6EO-Bzsods6X.js → info-VBDWY6EO-CwyXEo8E.js} +2 -2
- package/dist/{infoDiagram-HS3SLOUP-Cmxo6jKx.js → infoDiagram-HS3SLOUP-BXGbfBss.js} +12 -12
- package/dist/{isArrayLikeObject-Btu-i6_P.js → isArrayLikeObject-BrYl-ETg.js} +25 -26
- package/dist/{isEmpty-CZvUtYFp.js → isEmpty-C-xMag79.js} +2 -2
- package/dist/{isString-CBr7TEb7.js → isString-D-vNYDBA.js} +1 -1
- package/dist/{isSymbol-BuQsMXhk.js → isSymbol-Dyt2NSnN.js} +1 -1
- package/dist/{journeyDiagram-XKPGCS4Q-CKYr8cSR.js → journeyDiagram-XKPGCS4Q-D5BIjS4N.js} +3 -3
- package/dist/{kanban-definition-3W4ZIXB7-DVvAZzQD.js → kanban-definition-3W4ZIXB7-DhDkqxFB.js} +7 -7
- package/dist/{label-CV0KYhtH.js → label-BLDcDYdI.js} +6 -6
- package/dist/{loader-eJCvvApN.js → loader-DsE3MiYo.js} +2 -2
- package/dist/main.js +1673 -1163
- package/dist/{memoize-P1T1IGb9.js → memoize-Cs8aS5RW.js} +1 -1
- package/dist/merge-NuyC7LN7.js +51 -0
- package/dist/{mermaid-COOB_abB.js → mermaid-DkdSmFY8.js} +42 -42
- package/dist/{mermaid-parser.core-Cd-wu4tE.js → mermaid-parser.core-OkWZ8nr-.js} +8 -8
- package/dist/{min-CMDDtXJP.js → min-ECVRnCdn.js} +30 -30
- package/dist/{mindmap-definition-VGOIOE7T-1ExmnvYy.js → mindmap-definition-VGOIOE7T-BxQi78Vl.js} +9 -9
- package/dist/{now-BxlRp0OQ.js → now-BC2mX0ZT.js} +1 -1
- package/dist/{packet-DYOGHKS2-Bf1CvFco.js → packet-DYOGHKS2-C62XQjZh.js} +2 -2
- package/dist/{pie-VRWISCQL-LY_wbqji.js → pie-VRWISCQL-nfAKQJw3.js} +2 -2
- package/dist/{pieDiagram-ADFJNKIX-CJlIsdsU.js → pieDiagram-ADFJNKIX-DfSJXUHa.js} +13 -13
- package/dist/{purify.es-CyOIw8ru.js → purify.es-DGenX2XH.js} +67 -67
- package/dist/{quadrantDiagram-AYHSOK5B-BU78RiaH.js → quadrantDiagram-AYHSOK5B-CAcVWXc-.js} +2 -2
- package/dist/{radar-ZZBFDIW7-Ro3iXZCk.js → radar-ZZBFDIW7-lopS8_4j.js} +2 -2
- package/dist/{range-Dh0_-r8P.js → range-BKaWvVUE.js} +8 -8
- package/dist/reduce-CqQo8ppc.js +275 -0
- package/dist/{requirementDiagram-UZGBJVZJ-DACHtrFr.js → requirementDiagram-UZGBJVZJ-BU7dwzFM.js} +9 -9
- package/dist/{sankeyDiagram-TZEHDZUN-Bzg7_UWs.js → sankeyDiagram-TZEHDZUN-BVJnR4_b.js} +2 -2
- package/dist/{sequenceDiagram-WL72ISMW-agybEe9J.js → sequenceDiagram-WL72ISMW-CQcFQTwX.js} +4 -4
- package/dist/{slides-component-B0yK5GXP.js → slides-component-DwvL_HJi.js} +2 -2
- package/dist/{spec-Dq_reDGM.js → spec-CbYkiXG3.js} +5 -5
- package/dist/{stateDiagram-FKZM4ZOC-DehQAt8g.js → stateDiagram-FKZM4ZOC-Dx9AIGDe.js} +12 -12
- package/dist/{stateDiagram-v2-4FDKWEC3-8VzeREl9.js → stateDiagram-v2-4FDKWEC3-BIeUs-Ed.js} +10 -10
- package/dist/style.css +1 -1
- package/dist/{timeline-definition-IT6M3QCI-CdCfdaCF.js → timeline-definition-IT6M3QCI-D8B3p7ID.js} +2 -2
- package/dist/{toNumber-By7s5JC_.js → toNumber-CbZ70FdN.js} +2 -2
- package/dist/{toString-Ckpb50uw.js → toString-DbIAWQpF.js} +2 -2
- package/dist/{tooltip-CL8m4f9y.js → tooltip-SPkubVH3.js} +3 -3
- package/dist/{treemap-GDKQZRPO-DRxfDG65.js → treemap-GDKQZRPO-CkR-5ai2.js} +2 -2
- package/dist/{types-BwnzGcE4.js → types-0FB-N7AA.js} +519 -408
- package/dist/{uniq-cCc07Q8K.js → uniq-H2E5nMLq.js} +1 -1
- package/dist/{useAsyncData-B4hMFGnF.js → useAsyncData-D7-oahg5.js} +1 -1
- package/dist/{useDeepCompareMemoize-DuPhOXzr.js → useDeepCompareMemoize-DLS-bHHT.js} +5 -5
- package/dist/{useIframeCapabilities-CAt6D2EI.js → useIframeCapabilities-DFGZKWkO.js} +1 -1
- package/dist/{useTheme-BNYQnvu-.js → useTheme-D0rdoMBF.js} +6 -5
- package/dist/{vega-component-DouPy8AI.js → vega-component-D2knjGgv.js} +10 -10
- package/dist/{xychartDiagram-PRI3JC2R-rEm_SIsC.js → xychartDiagram-PRI3JC2R-XO8FiQjU.js} +5 -5
- package/package.json +9 -9
- package/src/__mocks__/common.ts +41 -8
- package/src/__mocks__/requests.ts +1 -0
- package/src/components/app-config/ai-config.tsx +10 -0
- package/src/components/chat/__tests__/useFileState.test.tsx +2 -3
- package/src/components/chat/acp/__tests__/context-utils.test.ts +2 -6
- package/src/components/datasources/components.tsx +3 -6
- package/src/components/datasources/datasources.tsx +8 -21
- package/src/components/editor/__tests__/data-attributes.test.tsx +2 -11
- package/src/components/editor/actions/types.ts +6 -1
- package/src/components/editor/actions/useNotebookActions.tsx +50 -13
- package/src/components/editor/cell/cell-context-menu.tsx +2 -6
- package/src/components/editor/chrome/types.ts +17 -0
- package/src/components/editor/connections/add-connection-dialog.tsx +27 -2
- package/src/components/editor/connections/database/__tests__/__snapshots__/as-code.test.ts.snap +105 -6
- package/src/components/editor/connections/database/__tests__/as-code.test.ts +101 -8
- package/src/components/editor/connections/database/as-code.ts +115 -25
- package/src/components/editor/connections/database/schemas.ts +49 -2
- package/src/components/editor/connections/storage/as-code.ts +1 -1
- package/src/components/editor/controls/command-palette.tsx +7 -0
- package/src/components/editor/controls/keyboard-shortcuts.tsx +3 -1
- package/src/components/editor/file-tree/__tests__/requesting-tree.test.ts +2 -3
- package/src/components/editor/file-tree/file-explorer.tsx +48 -62
- package/src/components/editor/file-tree/file-icons.tsx +132 -0
- package/src/components/editor/file-tree/file-viewer.tsx +1 -1
- package/src/components/editor/file-tree/tree-actions.tsx +107 -0
- package/src/components/editor/file-tree/types.ts +2 -96
- package/src/components/editor/header/filename-input.tsx +4 -1
- package/src/components/editor/navigation/__tests__/clipboard.test.ts +2 -4
- package/src/components/editor/output/console/ConsoleOutput.tsx +51 -2
- package/src/components/editor/output/console/__tests__/ConsoleOutput.test.tsx +97 -16
- package/src/components/icons/marimo-icons.tsx +2 -2
- package/src/components/pages/home-page.tsx +5 -5
- package/src/components/storage/__tests__/storage-snippets.test.ts +253 -0
- package/src/components/storage/components.tsx +0 -38
- package/src/components/storage/storage-file-viewer.tsx +1 -1
- package/src/components/storage/storage-inspector.tsx +66 -51
- package/src/components/storage/storage-snippets.ts +67 -0
- package/src/components/ui/command.tsx +2 -0
- package/src/components/ui/links.tsx +1 -0
- package/src/core/ai/tools/__tests__/run-cells-tool.test.ts +206 -0
- package/src/core/ai/tools/run-cells-tool.ts +75 -40
- package/src/core/cells/__tests__/cells.test.ts +62 -0
- package/src/core/cells/__tests__/session.test.ts +2 -7
- package/src/core/cells/cells.ts +25 -3
- package/src/core/cells/ids.ts +2 -1
- package/src/core/codemirror/compat/__tests__/jupyter.test.ts +2 -3
- package/src/core/codemirror/keymaps/vim.ts +32 -3
- package/src/core/codemirror/markdown/__tests__/commands.test.ts +2 -3
- package/src/core/config/__tests__/config-schema.test.ts +6 -2
- package/src/core/config/config-schema.ts +1 -0
- package/src/core/config/feature-flag.tsx +1 -1
- package/src/core/dom/ui-element-constants.ts +15 -0
- package/src/core/dom/ui-element.ts +3 -2
- package/src/core/export/__tests__/hooks.test.ts +3 -10
- package/src/core/hotkeys/__tests__/hotkeys.test.ts +64 -1
- package/src/core/hotkeys/hotkeys.ts +29 -3
- package/src/core/islands/bridge.ts +1 -0
- package/src/core/islands/components/web-components.tsx +2 -1
- package/src/core/network/__tests__/requests-network.test.ts +17 -0
- package/src/core/network/requests-lazy.ts +1 -0
- package/src/core/network/requests-network.ts +9 -0
- package/src/core/network/requests-static.ts +1 -0
- package/src/core/network/requests-toasting.tsx +1 -0
- package/src/core/network/types.ts +1 -0
- package/src/core/runtime/__tests__/runtime.test.ts +2 -8
- package/src/core/storage/__tests__/state.test.ts +1 -0
- package/src/core/wasm/bridge.ts +1 -0
- package/src/core/websocket/useMarimoKernelConnection.tsx +2 -0
- package/src/plugins/impl/DataTablePlugin.tsx +53 -3
- package/src/plugins/impl/FileBrowserPlugin.tsx +8 -5
- package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +2 -11
- package/src/plugins/impl/__tests__/DropdownPlugin.test.tsx +2 -11
- package/src/plugins/impl/anywidget/AnyWidgetPlugin.tsx +4 -1
- package/src/plugins/impl/mpl-interactive/MplInteractivePlugin.tsx +309 -0
- package/src/plugins/impl/mpl-interactive/__tests__/mpl-websocket-shim.test.ts +110 -0
- package/src/plugins/impl/mpl-interactive/mpl-websocket-shim.ts +57 -0
- package/src/plugins/impl/plotly/PlotlyPlugin.tsx +8 -2
- package/src/plugins/plugins.ts +2 -0
- package/src/utils/__tests__/copy.test.ts +129 -0
- package/src/utils/__tests__/download.test.tsx +12 -14
- package/src/utils/__tests__/filenames.test.ts +7 -0
- package/src/utils/__tests__/smartMatch.test.ts +61 -0
- package/src/utils/copy.ts +43 -0
- package/src/utils/filenames.ts +3 -0
- package/src/utils/smartMatch.ts +62 -0
- package/dist/_baseProperty-D1nWkRMz.js +0 -93
- package/dist/assets/__vite-browser-external-WSlCcXn_.js +0 -1
- package/dist/dist-BAeGo2rp.js +0 -5
- package/dist/dist-BqwCMSEa.js +0 -5
- package/dist/dist-Bum8FwTO.js +0 -6
- package/dist/dist-C0YiOwt_.js +0 -5
- package/dist/dist-C2uPv4iU.js +0 -5
- package/dist/dist-C5hOLsJN.js +0 -8
- package/dist/dist-C9NIAKMs.js +0 -8
- package/dist/dist-CCrzTtvk.js +0 -5
- package/dist/dist-CFS9i1rS.js +0 -8
- package/dist/dist-CyHZuhPH.js +0 -5
- package/dist/dist-CzcjWdIk.js +0 -6
- package/dist/dist-DaYyUSNC.js +0 -5
- package/dist/dist-DpDcJYNh.js +0 -8
- package/dist/dist-U_BfxcPn.js +0 -5
- package/dist/merge-CGQkMGzr.js +0 -51
- package/dist/reduce-BXFHs7IQ.js +0 -268
|
@@ -24,6 +24,11 @@ import type { CopilotMode } from "./registry";
|
|
|
24
24
|
const POST_EXECUTION_DELAY = 200;
|
|
25
25
|
const WAIT_FOR_CELLS_TIMEOUT = 30_000;
|
|
26
26
|
|
|
27
|
+
// Output size limits to prevent exceeding LLM token limits.
|
|
28
|
+
const MAX_TEXT_OUTPUT_CHARS = 2000;
|
|
29
|
+
const MAX_ERROR_OUTPUT_CHARS = 3000;
|
|
30
|
+
const MAX_TOOL_OUTPUT_CHARS = 40_000;
|
|
31
|
+
|
|
27
32
|
interface CellOutput {
|
|
28
33
|
consoleOutput?: string;
|
|
29
34
|
cellOutput?: string;
|
|
@@ -92,7 +97,7 @@ export class RunStaleCellsTool
|
|
|
92
97
|
|
|
93
98
|
await runCells({
|
|
94
99
|
cellIds: staleCells,
|
|
95
|
-
sendRun
|
|
100
|
+
sendRun,
|
|
96
101
|
prepareForRun,
|
|
97
102
|
notebook,
|
|
98
103
|
});
|
|
@@ -116,44 +121,59 @@ export class RunStaleCellsTool
|
|
|
116
121
|
const updatedNotebook = store.get(notebookAtom);
|
|
117
122
|
|
|
118
123
|
const cellsToOutput = new Map<CellId, CellOutput | null>();
|
|
119
|
-
let resultMessage = "";
|
|
120
124
|
let outputHasErrors = false;
|
|
125
|
+
let hasAnyConsoleOutput = false;
|
|
126
|
+
let totalOutputChars = 0;
|
|
121
127
|
|
|
122
128
|
for (const cellId of staleCells) {
|
|
123
129
|
const cellContextData = getCellContextData(cellId, updatedNotebook, {
|
|
124
130
|
includeConsoleOutput: true,
|
|
125
131
|
});
|
|
126
132
|
|
|
127
|
-
let cellOutputString: string | undefined;
|
|
128
|
-
let consoleOutputString: string | undefined;
|
|
129
|
-
|
|
130
133
|
const cellOutput = cellContextData.cellOutput;
|
|
131
134
|
const consoleOutputs = cellContextData.consoleOutputs;
|
|
132
135
|
const hasConsoleOutput = consoleOutputs && consoleOutputs.length > 0;
|
|
133
136
|
|
|
137
|
+
// Track errors regardless of budget
|
|
138
|
+
if (
|
|
139
|
+
(cellOutput && this.outputHasErrors(cellOutput)) ||
|
|
140
|
+
(hasConsoleOutput &&
|
|
141
|
+
consoleOutputs.some((output) => this.outputHasErrors(output)))
|
|
142
|
+
) {
|
|
143
|
+
outputHasErrors = true;
|
|
144
|
+
}
|
|
145
|
+
|
|
134
146
|
if (!cellOutput && !hasConsoleOutput) {
|
|
135
|
-
// Set null to show no output
|
|
136
147
|
cellsToOutput.set(cellId, null);
|
|
137
148
|
continue;
|
|
138
149
|
}
|
|
139
150
|
|
|
151
|
+
// If total budget exceeded, summarize remaining cells
|
|
152
|
+
if (totalOutputChars >= MAX_TOOL_OUTPUT_CHARS) {
|
|
153
|
+
cellsToOutput.set(cellId, {
|
|
154
|
+
cellOutput: "Cell executed (output omitted due to context limits).",
|
|
155
|
+
});
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
let cellOutputString: string | undefined;
|
|
160
|
+
let consoleOutputString: string | undefined;
|
|
161
|
+
|
|
140
162
|
if (cellOutput) {
|
|
141
163
|
cellOutputString = this.formatOutputString(cellOutput);
|
|
142
|
-
|
|
143
|
-
outputHasErrors = true;
|
|
144
|
-
}
|
|
164
|
+
totalOutputChars += cellOutputString.length;
|
|
145
165
|
}
|
|
146
166
|
|
|
147
167
|
if (hasConsoleOutput) {
|
|
168
|
+
hasAnyConsoleOutput = true;
|
|
148
169
|
consoleOutputString = consoleOutputs
|
|
149
170
|
.map((output) => this.formatOutputString(output))
|
|
150
171
|
.join("\n");
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
172
|
+
consoleOutputString = this.truncateString(
|
|
173
|
+
consoleOutputString,
|
|
174
|
+
MAX_TEXT_OUTPUT_CHARS,
|
|
175
|
+
);
|
|
176
|
+
totalOutputChars += consoleOutputString.length;
|
|
157
177
|
}
|
|
158
178
|
|
|
159
179
|
cellsToOutput.set(cellId, {
|
|
@@ -179,43 +199,58 @@ export class RunStaleCellsTool
|
|
|
179
199
|
return {
|
|
180
200
|
status: "success",
|
|
181
201
|
cellsToOutput: Object.fromEntries(cellsToOutput),
|
|
182
|
-
message:
|
|
202
|
+
message: hasAnyConsoleOutput
|
|
203
|
+
? "Console output represents the stdout or stderr of the cell (eg. print statements)."
|
|
204
|
+
: undefined,
|
|
183
205
|
next_steps: nextSteps,
|
|
184
206
|
};
|
|
185
207
|
};
|
|
186
208
|
|
|
187
209
|
private outputHasErrors(cellOutput: BaseOutput): boolean {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
output.mimetype === "application/vnd.marimo+
|
|
191
|
-
|
|
192
|
-
) {
|
|
193
|
-
return true;
|
|
194
|
-
}
|
|
195
|
-
return false;
|
|
210
|
+
return (
|
|
211
|
+
cellOutput.output.mimetype === "application/vnd.marimo+error" ||
|
|
212
|
+
cellOutput.output.mimetype === "application/vnd.marimo+traceback"
|
|
213
|
+
);
|
|
196
214
|
}
|
|
197
215
|
|
|
198
216
|
private formatOutputString(cellOutput: BaseOutput): string {
|
|
199
|
-
let outputString = "";
|
|
200
217
|
const { outputType, processedContent, imageUrl, output } = cellOutput;
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
} else if (typeof output.data === "string") {
|
|
206
|
-
outputString += output.data;
|
|
207
|
-
} else {
|
|
208
|
-
outputString += JSON.stringify(output.data);
|
|
209
|
-
}
|
|
210
|
-
} else if (outputType === "media") {
|
|
211
|
-
outputString += `Media Output: Contains ${output.mimetype} content`;
|
|
212
|
-
if (imageUrl) {
|
|
213
|
-
outputString += `\nImage URL: ${imageUrl}`;
|
|
214
|
-
}
|
|
218
|
+
|
|
219
|
+
if (outputType === "media") {
|
|
220
|
+
const base = `Media Output: Contains ${output.mimetype} content`;
|
|
221
|
+
return imageUrl ? `${base}\nImage URL: ${imageUrl}` : base;
|
|
215
222
|
}
|
|
216
|
-
|
|
223
|
+
|
|
224
|
+
if (output.mimetype === "text/html") {
|
|
225
|
+
// text/html (e.g. plotly figures, rich dataframes) can be millions of
|
|
226
|
+
// chars and is not interpretable by LLMs — summarize instead
|
|
227
|
+
const dataLength =
|
|
228
|
+
typeof output.data === "string"
|
|
229
|
+
? output.data.length
|
|
230
|
+
: JSON.stringify(output.data).length;
|
|
231
|
+
return `HTML Output: text/html content (${dataLength.toLocaleString()} chars). Full output visible in notebook UI.`;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const maxChars = this.outputHasErrors(cellOutput)
|
|
235
|
+
? MAX_ERROR_OUTPUT_CHARS
|
|
236
|
+
: MAX_TEXT_OUTPUT_CHARS;
|
|
237
|
+
|
|
238
|
+
let content = processedContent;
|
|
239
|
+
if (!content) {
|
|
240
|
+
content =
|
|
241
|
+
typeof output.data === "string"
|
|
242
|
+
? output.data
|
|
243
|
+
: JSON.stringify(output.data);
|
|
244
|
+
}
|
|
245
|
+
return `Output:\n${this.truncateString(content, maxChars)}`;
|
|
217
246
|
}
|
|
218
247
|
|
|
248
|
+
private truncateString(str: string, maxLength: number): string {
|
|
249
|
+
if (str.length <= maxLength) {
|
|
250
|
+
return str;
|
|
251
|
+
}
|
|
252
|
+
return `${str.slice(0, maxLength)}\n\n[TRUNCATED: ${str.length.toLocaleString()} → ${maxLength.toLocaleString()} chars. Full output visible in the notebook UI.]`;
|
|
253
|
+
}
|
|
219
254
|
/**
|
|
220
255
|
* Wait for cells to finish executing (status becomes "idle")
|
|
221
256
|
* Returns true if all cells finished executing, false if the timeout was reached
|
|
@@ -1439,6 +1439,68 @@ describe("cell reducer", () => {
|
|
|
1439
1439
|
expect(state.cellData["5" as CellId]).not.toBeUndefined();
|
|
1440
1440
|
});
|
|
1441
1441
|
|
|
1442
|
+
it("can set cell codes with names and configs", () => {
|
|
1443
|
+
const newIds = ["3", "4"] as CellId[];
|
|
1444
|
+
actions.setCellIds({ cellIds: newIds });
|
|
1445
|
+
actions.setCellCodes({
|
|
1446
|
+
codes: ["code1", "code2"],
|
|
1447
|
+
ids: newIds,
|
|
1448
|
+
codeIsStale: false,
|
|
1449
|
+
names: ["setup_cell", "analysis"],
|
|
1450
|
+
configs: [
|
|
1451
|
+
{ hide_code: true, disabled: false, column: null },
|
|
1452
|
+
{ hide_code: false, disabled: true, column: null },
|
|
1453
|
+
],
|
|
1454
|
+
});
|
|
1455
|
+
|
|
1456
|
+
expect(state.cellData["3" as CellId].name).toBe("setup_cell");
|
|
1457
|
+
expect(state.cellData["3" as CellId].config.hide_code).toBe(true);
|
|
1458
|
+
expect(state.cellData["3" as CellId].config.disabled).toBe(false);
|
|
1459
|
+
|
|
1460
|
+
expect(state.cellData["4" as CellId].name).toBe("analysis");
|
|
1461
|
+
expect(state.cellData["4" as CellId].config.hide_code).toBe(false);
|
|
1462
|
+
expect(state.cellData["4" as CellId].config.disabled).toBe(true);
|
|
1463
|
+
});
|
|
1464
|
+
|
|
1465
|
+
it("can set cell codes without names/configs (backward compat)", () => {
|
|
1466
|
+
const newIds = ["3"] as CellId[];
|
|
1467
|
+
actions.setCellIds({ cellIds: newIds });
|
|
1468
|
+
actions.setCellCodes({
|
|
1469
|
+
codes: ["code1"],
|
|
1470
|
+
ids: newIds,
|
|
1471
|
+
codeIsStale: false,
|
|
1472
|
+
});
|
|
1473
|
+
|
|
1474
|
+
// Should use defaults when names/configs not provided
|
|
1475
|
+
expect(state.cellData["3" as CellId].code).toBe("code1");
|
|
1476
|
+
expect(state.cellData["3" as CellId].config.hide_code).toBe(false);
|
|
1477
|
+
expect(state.cellData["3" as CellId].config.disabled).toBe(false);
|
|
1478
|
+
});
|
|
1479
|
+
|
|
1480
|
+
it("can update names and configs on existing cells via setCellCodes", () => {
|
|
1481
|
+
// Set initial state
|
|
1482
|
+
actions.setCellCodes({
|
|
1483
|
+
codes: ["x = 1"],
|
|
1484
|
+
ids: [firstCellId],
|
|
1485
|
+
codeIsStale: false,
|
|
1486
|
+
names: ["old_name"],
|
|
1487
|
+
configs: [{ hide_code: false, disabled: false, column: null }],
|
|
1488
|
+
});
|
|
1489
|
+
expect(state.cellData[firstCellId].name).toBe("old_name");
|
|
1490
|
+
expect(state.cellData[firstCellId].config.hide_code).toBe(false);
|
|
1491
|
+
|
|
1492
|
+
// Update with new name and config (same code)
|
|
1493
|
+
actions.setCellCodes({
|
|
1494
|
+
codes: ["x = 1"],
|
|
1495
|
+
ids: [firstCellId],
|
|
1496
|
+
codeIsStale: true,
|
|
1497
|
+
names: ["new_name"],
|
|
1498
|
+
configs: [{ hide_code: true, disabled: false, column: null }],
|
|
1499
|
+
});
|
|
1500
|
+
expect(state.cellData[firstCellId].name).toBe("new_name");
|
|
1501
|
+
expect(state.cellData[firstCellId].config.hide_code).toBe(true);
|
|
1502
|
+
});
|
|
1503
|
+
|
|
1442
1504
|
it("can fold and unfold all cells", () => {
|
|
1443
1505
|
actions.foldAll();
|
|
1444
1506
|
expect(foldAllBulk).toHaveBeenCalled();
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import type * as api from "@marimo-team/marimo-api";
|
|
4
4
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
5
5
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
6
|
+
import { Mocks } from "@/__mocks__/common";
|
|
6
7
|
import { parseOutline } from "@/core/dom/outline";
|
|
7
8
|
import { MultiColumn, visibleForTesting } from "@/utils/id-tree";
|
|
8
9
|
import { invariant } from "@/utils/invariant";
|
|
@@ -11,13 +12,7 @@ import { type CellId, SETUP_CELL_ID } from "../ids";
|
|
|
11
12
|
import { notebookStateFromSession } from "../session";
|
|
12
13
|
|
|
13
14
|
// Mock dependencies
|
|
14
|
-
vi.mock("@/utils/Logger", () => ({
|
|
15
|
-
Logger: {
|
|
16
|
-
error: vi.fn(),
|
|
17
|
-
warn: vi.fn(),
|
|
18
|
-
debug: vi.fn(),
|
|
19
|
-
},
|
|
20
|
-
}));
|
|
15
|
+
vi.mock("@/utils/Logger", () => ({ Logger: Mocks.quietLogger() }));
|
|
21
16
|
|
|
22
17
|
vi.mock("@/core/dom/outline", () => ({
|
|
23
18
|
parseOutline: vi.fn(),
|
package/src/core/cells/cells.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { historyField } from "@codemirror/commands";
|
|
4
4
|
import { type Atom, atom, useAtom, useAtomValue } from "jotai";
|
|
5
5
|
import { atomFamily, selectAtom, splitAtom } from "jotai/utils";
|
|
6
|
-
import { isEqual
|
|
6
|
+
import { isEqual } from "lodash-es";
|
|
7
7
|
import { createRef, type ReducerWithoutAction } from "react";
|
|
8
8
|
import type { CellHandle } from "@/components/editor/notebook-cell";
|
|
9
9
|
import {
|
|
@@ -801,7 +801,13 @@ const {
|
|
|
801
801
|
},
|
|
802
802
|
setCellCodes: (
|
|
803
803
|
state,
|
|
804
|
-
action: {
|
|
804
|
+
action: {
|
|
805
|
+
codes: string[];
|
|
806
|
+
ids: CellId[];
|
|
807
|
+
codeIsStale: boolean;
|
|
808
|
+
names?: string[];
|
|
809
|
+
configs?: CellConfig[];
|
|
810
|
+
},
|
|
805
811
|
) => {
|
|
806
812
|
invariant(
|
|
807
813
|
action.codes.length === action.ids.length,
|
|
@@ -814,15 +820,21 @@ const {
|
|
|
814
820
|
cell,
|
|
815
821
|
code,
|
|
816
822
|
cellId,
|
|
823
|
+
name,
|
|
824
|
+
config,
|
|
817
825
|
}: {
|
|
818
826
|
cell: CellData | undefined;
|
|
819
827
|
code: string;
|
|
820
828
|
cellId: CellId;
|
|
829
|
+
name?: string;
|
|
830
|
+
config?: CellConfig;
|
|
821
831
|
}) => {
|
|
822
832
|
if (!cell) {
|
|
823
833
|
return createCell({
|
|
824
834
|
id: cellId,
|
|
825
835
|
code,
|
|
836
|
+
name: name,
|
|
837
|
+
config: config,
|
|
826
838
|
lastCodeRun: action.codeIsStale ? null : code,
|
|
827
839
|
edited: action.codeIsStale && code.trim().length > 0,
|
|
828
840
|
});
|
|
@@ -843,6 +855,8 @@ const {
|
|
|
843
855
|
code: code,
|
|
844
856
|
edited,
|
|
845
857
|
lastCodeRun,
|
|
858
|
+
...(name !== undefined && { name }),
|
|
859
|
+
...(config !== undefined && { config }),
|
|
846
860
|
};
|
|
847
861
|
}
|
|
848
862
|
|
|
@@ -860,13 +874,19 @@ const {
|
|
|
860
874
|
code: code,
|
|
861
875
|
edited,
|
|
862
876
|
lastCodeRun,
|
|
877
|
+
...(name !== undefined && { name }),
|
|
878
|
+
...(config !== undefined && { config }),
|
|
863
879
|
};
|
|
864
880
|
};
|
|
865
881
|
|
|
866
|
-
for (
|
|
882
|
+
for (let i = 0; i < action.ids.length; i++) {
|
|
883
|
+
const cellId = action.ids[i];
|
|
884
|
+
const code = action.codes[i];
|
|
867
885
|
if (cellId === undefined || code === undefined) {
|
|
868
886
|
continue;
|
|
869
887
|
}
|
|
888
|
+
const name = action.names?.[i];
|
|
889
|
+
const config = action.configs?.[i];
|
|
870
890
|
nextState = {
|
|
871
891
|
...nextState,
|
|
872
892
|
cellData: {
|
|
@@ -875,6 +895,8 @@ const {
|
|
|
875
895
|
cell: nextState.cellData[cellId],
|
|
876
896
|
code,
|
|
877
897
|
cellId,
|
|
898
|
+
name,
|
|
899
|
+
config,
|
|
878
900
|
}),
|
|
879
901
|
},
|
|
880
902
|
};
|
package/src/core/cells/ids.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-redeclare */
|
|
3
3
|
|
|
4
|
+
import { OBJECT_ID_ATTR } from "@/core/dom/ui-element-constants";
|
|
4
5
|
import { invariant } from "@/utils/invariant";
|
|
5
6
|
import type { TypedString } from "../../utils/typed";
|
|
6
7
|
|
|
@@ -110,7 +111,7 @@ export function findCellId(element: HTMLElement): CellId | null {
|
|
|
110
111
|
export type UIElementId = `${CellId}-${string}`;
|
|
111
112
|
export const UIElementId = {
|
|
112
113
|
parse(element: Element): UIElementId | null {
|
|
113
|
-
return element.getAttribute(
|
|
114
|
+
return element.getAttribute(OBJECT_ID_ATTR) as UIElementId | null;
|
|
114
115
|
},
|
|
115
116
|
parseOrThrow(element: Element): UIElementId {
|
|
116
117
|
const id = UIElementId.parse(element);
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { EditorState } from "@codemirror/state";
|
|
4
4
|
import { EditorView } from "@codemirror/view";
|
|
5
5
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
6
|
+
import { MockModules } from "@/__mocks__/common";
|
|
6
7
|
import { MockRequestClient } from "@/__mocks__/requests";
|
|
7
8
|
import { toast } from "@/components/ui/use-toast";
|
|
8
9
|
import { store } from "@/core/state/jotai";
|
|
@@ -16,9 +17,7 @@ vi.mock("@/core/state/jotai", () => ({
|
|
|
16
17
|
},
|
|
17
18
|
}));
|
|
18
19
|
|
|
19
|
-
vi.mock("@/components/ui/use-toast", () => (
|
|
20
|
-
toast: vi.fn(),
|
|
21
|
-
}));
|
|
20
|
+
vi.mock("@/components/ui/use-toast", () => MockModules.toast());
|
|
22
21
|
|
|
23
22
|
// Mock the helper to get request client
|
|
24
23
|
const mockRequestClient = MockRequestClient.create();
|
|
@@ -247,6 +247,32 @@ function applyVimCommands(vimCommands: VimCommand[]) {
|
|
|
247
247
|
}
|
|
248
248
|
}
|
|
249
249
|
|
|
250
|
+
interface ExtendedVim {
|
|
251
|
+
getVimGlobalState_: () => {
|
|
252
|
+
macroModeState?: {
|
|
253
|
+
isRecording: boolean;
|
|
254
|
+
isPlaying: boolean;
|
|
255
|
+
};
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function isExtendedVim(vim: typeof Vim): vim is typeof Vim & ExtendedVim {
|
|
260
|
+
return (
|
|
261
|
+
"getVimGlobalState_" in vim && typeof vim.getVimGlobalState_ === "function"
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function isMacroActive() {
|
|
266
|
+
if (!isExtendedVim(Vim)) {
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
const macroModeState = Vim.getVimGlobalState_()?.macroModeState;
|
|
270
|
+
if (!macroModeState) {
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
return Boolean(macroModeState.isRecording || macroModeState.isPlaying);
|
|
274
|
+
}
|
|
275
|
+
|
|
250
276
|
class CodeMirrorVimSync {
|
|
251
277
|
private instances = new Set<EditorView>();
|
|
252
278
|
private isBroadcasting = false;
|
|
@@ -275,10 +301,13 @@ class CodeMirrorVimSync {
|
|
|
275
301
|
return;
|
|
276
302
|
}
|
|
277
303
|
invariant("mode" in e, 'Expected event to have a "mode" property');
|
|
304
|
+
const skipBroadcast = isMacroActive();
|
|
278
305
|
this.isBroadcasting = true;
|
|
279
306
|
// We use onIdle to keep the focused editor snappy
|
|
280
307
|
onIdle(() => {
|
|
281
|
-
|
|
308
|
+
if (!skipBroadcast) {
|
|
309
|
+
this.broadcastModeChange(instance, e.mode, e.subMode);
|
|
310
|
+
}
|
|
282
311
|
this.isBroadcasting = false;
|
|
283
312
|
});
|
|
284
313
|
});
|
|
@@ -338,9 +367,9 @@ class CodeMirrorVimSync {
|
|
|
338
367
|
}
|
|
339
368
|
break;
|
|
340
369
|
case "insert":
|
|
341
|
-
//
|
|
370
|
+
// only enter insert mode if we're not already in it
|
|
342
371
|
if (!vim.insertMode) {
|
|
343
|
-
Vim.handleKey(cm, "i", "");
|
|
372
|
+
Vim.handleKey(cm, "i", "mapping");
|
|
344
373
|
}
|
|
345
374
|
break;
|
|
346
375
|
case "visual":
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
test,
|
|
10
10
|
vi,
|
|
11
11
|
} from "vitest";
|
|
12
|
+
import { MockModules } from "@/__mocks__/common";
|
|
12
13
|
import { MockRequestClient } from "@/__mocks__/requests";
|
|
13
14
|
import { requestClientAtom } from "@/core/network/requests";
|
|
14
15
|
import { filenameAtom } from "@/core/saving/file-state";
|
|
@@ -25,9 +26,7 @@ import {
|
|
|
25
26
|
insertUL,
|
|
26
27
|
} from "../commands";
|
|
27
28
|
|
|
28
|
-
vi.mock("@/components/ui/use-toast", () => (
|
|
29
|
-
toast: vi.fn(),
|
|
30
|
-
}));
|
|
29
|
+
vi.mock("@/components/ui/use-toast", () => MockModules.toast());
|
|
31
30
|
|
|
32
31
|
import { toast } from "@/components/ui/use-toast";
|
|
33
32
|
|
|
@@ -70,7 +70,9 @@ test("default UserConfig - empty", () => {
|
|
|
70
70
|
"reference_highlighting": true,
|
|
71
71
|
"theme": "light",
|
|
72
72
|
},
|
|
73
|
-
"experimental": {
|
|
73
|
+
"experimental": {
|
|
74
|
+
"storage_inspector": true,
|
|
75
|
+
},
|
|
74
76
|
"formatting": {
|
|
75
77
|
"line_length": 79,
|
|
76
78
|
},
|
|
@@ -140,7 +142,9 @@ test("default UserConfig - one level", () => {
|
|
|
140
142
|
"reference_highlighting": true,
|
|
141
143
|
"theme": "light",
|
|
142
144
|
},
|
|
143
|
-
"experimental": {
|
|
145
|
+
"experimental": {
|
|
146
|
+
"storage_inspector": true,
|
|
147
|
+
},
|
|
144
148
|
"formatting": {
|
|
145
149
|
"line_length": 79,
|
|
146
150
|
},
|
|
@@ -186,6 +186,7 @@ export const UserConfigSchema = z
|
|
|
186
186
|
.looseObject({
|
|
187
187
|
markdown: z.boolean().optional(),
|
|
188
188
|
rtc: z.boolean().optional(),
|
|
189
|
+
storage_inspector: z.boolean().prefault(true),
|
|
189
190
|
// Add new experimental features here
|
|
190
191
|
})
|
|
191
192
|
// Pass through so that we don't remove any extra keys that the user has added.
|
|
@@ -21,7 +21,7 @@ const defaultValues: ExperimentalFeatures = {
|
|
|
21
21
|
rtc_v2: false,
|
|
22
22
|
cache_panel: false,
|
|
23
23
|
external_agents: import.meta.env.DEV,
|
|
24
|
-
storage_inspector:
|
|
24
|
+
storage_inspector: true,
|
|
25
25
|
};
|
|
26
26
|
|
|
27
27
|
export function getFeatureFlag<T extends keyof ExperimentalFeatures>(
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Stable identifier for a UI element, deterministic across re-executions
|
|
5
|
+
* of the same cell (based on cell ID + creation order).
|
|
6
|
+
* Used to synchronize multiple instances and kernel state.
|
|
7
|
+
*/
|
|
8
|
+
export const OBJECT_ID_ATTR = "object-id";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Random token that changes every time a UI element is constructed
|
|
12
|
+
* (i.e., every cell execution). Used to detect stale elements and
|
|
13
|
+
* force re-renders when a cell re-runs.
|
|
14
|
+
*/
|
|
15
|
+
export const RANDOM_ID_ATTR = "random-id";
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
MarimoValueInputEvent,
|
|
9
9
|
type MarimoValueInputEventType,
|
|
10
10
|
} from "./events";
|
|
11
|
+
import { RANDOM_ID_ATTR } from "./ui-element-constants";
|
|
11
12
|
import { UI_ELEMENT_REGISTRY } from "./uiregistry";
|
|
12
13
|
|
|
13
14
|
import "./ui-element.css";
|
|
@@ -189,7 +190,7 @@ export function initializeUIElement() {
|
|
|
189
190
|
// used like a React key. If the random-id changes, we need to unmount and
|
|
190
191
|
// remount its child.
|
|
191
192
|
static get observedAttributes() {
|
|
192
|
-
return [
|
|
193
|
+
return [RANDOM_ID_ATTR];
|
|
193
194
|
}
|
|
194
195
|
|
|
195
196
|
attributeChangedCallback(
|
|
@@ -199,7 +200,7 @@ export function initializeUIElement() {
|
|
|
199
200
|
) {
|
|
200
201
|
if (this.initialized) {
|
|
201
202
|
const hasChanged = oldValue !== newValue;
|
|
202
|
-
if (name ===
|
|
203
|
+
if (name === RANDOM_ID_ATTR && hasChanged) {
|
|
203
204
|
// deregister/clean-up this instance
|
|
204
205
|
this.disconnectedCallback();
|
|
205
206
|
// remove and re-add its child to force it to re-render; note that
|
|
@@ -5,6 +5,7 @@ import { createStore, Provider } from "jotai";
|
|
|
5
5
|
import type { ReactNode } from "react";
|
|
6
6
|
import * as React from "react";
|
|
7
7
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
8
|
+
import { MockModules, Mocks } from "@/__mocks__/common";
|
|
8
9
|
import type { CellId } from "@/core/cells/ids";
|
|
9
10
|
import { CellOutputId } from "@/core/cells/ids";
|
|
10
11
|
import type { CellRuntimeState } from "@/core/cells/types";
|
|
@@ -20,17 +21,9 @@ vi.mock("html-to-image", () => ({
|
|
|
20
21
|
toPng: vi.fn(),
|
|
21
22
|
}));
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
vi.mock("@/utils/Logger", () => ({
|
|
25
|
-
Logger: {
|
|
26
|
-
error: vi.fn(),
|
|
27
|
-
},
|
|
28
|
-
}));
|
|
24
|
+
vi.mock("@/utils/Logger", () => ({ Logger: Mocks.quietLogger() }));
|
|
29
25
|
|
|
30
|
-
|
|
31
|
-
vi.mock("@/components/ui/use-toast", () => ({
|
|
32
|
-
toast: vi.fn(),
|
|
33
|
-
}));
|
|
26
|
+
vi.mock("@/components/ui/use-toast", () => MockModules.toast());
|
|
34
27
|
|
|
35
28
|
// Mock cellsRuntimeAtom - must be defined inline in the factory function
|
|
36
29
|
vi.mock("@/core/cells/cells", async () => {
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
import { describe, expect, it } from "vitest";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
type Hotkey,
|
|
5
|
+
type HotkeyAction,
|
|
6
|
+
HotkeyProvider,
|
|
7
|
+
normalizeKeyString,
|
|
8
|
+
OverridingHotkeyProvider,
|
|
9
|
+
} from "../hotkeys";
|
|
4
10
|
|
|
5
11
|
/**
|
|
6
12
|
* Just a helper.
|
|
@@ -72,3 +78,60 @@ describe("HotkeyProvider platform separation", () => {
|
|
|
72
78
|
expect(linux.getHotkey("cell.format").key).toBe("Ctrl-Shift-L");
|
|
73
79
|
});
|
|
74
80
|
});
|
|
81
|
+
|
|
82
|
+
describe("normalizeKeyString", () => {
|
|
83
|
+
it("should capitalize multi-character base key names", () => {
|
|
84
|
+
expect(normalizeKeyString("Shift-enter")).toBe("Shift-Enter");
|
|
85
|
+
expect(normalizeKeyString("Cmd-enter")).toBe("Cmd-Enter");
|
|
86
|
+
expect(normalizeKeyString("Ctrl-backspace")).toBe("Ctrl-Backspace");
|
|
87
|
+
expect(normalizeKeyString("Alt-tab")).toBe("Alt-Tab");
|
|
88
|
+
expect(normalizeKeyString("Cmd-Shift-arrowUp")).toBe("Cmd-Shift-ArrowUp");
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("should leave already-correct key names unchanged", () => {
|
|
92
|
+
expect(normalizeKeyString("Shift-Enter")).toBe("Shift-Enter");
|
|
93
|
+
expect(normalizeKeyString("Cmd-Enter")).toBe("Cmd-Enter");
|
|
94
|
+
expect(normalizeKeyString("Mod-Shift-Enter")).toBe("Mod-Shift-Enter");
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("should leave single-character keys unchanged", () => {
|
|
98
|
+
expect(normalizeKeyString("Cmd-a")).toBe("Cmd-a");
|
|
99
|
+
expect(normalizeKeyString("Ctrl-Shift-z")).toBe("Ctrl-Shift-z");
|
|
100
|
+
expect(normalizeKeyString("a")).toBe("a");
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("should handle keys without modifiers", () => {
|
|
104
|
+
expect(normalizeKeyString("enter")).toBe("Enter");
|
|
105
|
+
expect(normalizeKeyString("Escape")).toBe("Escape");
|
|
106
|
+
expect(normalizeKeyString("F12")).toBe("F12");
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
describe("OverridingHotkeyProvider", () => {
|
|
111
|
+
it("should normalize lowercase key overrides", () => {
|
|
112
|
+
const provider = new OverridingHotkeyProvider(
|
|
113
|
+
{
|
|
114
|
+
"cell.run": "Shift-enter",
|
|
115
|
+
"cell.runAndNewBelow": "Cmd-enter",
|
|
116
|
+
},
|
|
117
|
+
{ platform: "mac" },
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
expect(provider.getHotkey("cell.run").key).toBe("Shift-Enter");
|
|
121
|
+
expect(provider.getHotkey("cell.runAndNewBelow").key).toBe("Cmd-Enter");
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("should return defaults when no override is set", () => {
|
|
125
|
+
const provider = new OverridingHotkeyProvider({}, { platform: "mac" });
|
|
126
|
+
expect(provider.getHotkey("cell.run").key).toBe("Cmd-Enter");
|
|
127
|
+
expect(provider.getHotkey("cell.runAndNewBelow").key).toBe("Shift-Enter");
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it("should pass through correctly-cased overrides unchanged", () => {
|
|
131
|
+
const provider = new OverridingHotkeyProvider(
|
|
132
|
+
{ "cell.run": "Shift-Enter" },
|
|
133
|
+
{ platform: "mac" },
|
|
134
|
+
);
|
|
135
|
+
expect(provider.getHotkey("cell.run").key).toBe("Shift-Enter");
|
|
136
|
+
});
|
|
137
|
+
});
|