@marimo-team/frontend 0.19.8-dev3 → 0.19.8-dev31
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/assets/{CellStatus-CTtb89yx.js → CellStatus-BjDayMOF.js} +1 -1
- package/dist/assets/{Combination-C_mRvd_Z.js → Combination-izwufzWe.js} +1 -1
- package/dist/assets/{ConnectedDataExplorerComponent-CX7YQX42.js → ConnectedDataExplorerComponent-C1BlkUlk.js} +1 -1
- package/dist/assets/{DeferredRequestRegistry-CO2AyNfd.js → DeferredRequestRegistry-tv0PqJZ0.js} +1 -1
- package/dist/assets/{ErrorBoundary-ChCiwl15.js → ErrorBoundary-B9Ifj8Jf.js} +1 -1
- package/dist/assets/{ImageComparisonComponent-BVBGmVcA.js → ImageComparisonComponent-BVXYpigZ.js} +1 -1
- package/dist/assets/{ImperativeModal-CKeCkc1k.js → ImperativeModal-DZr52ffu.js} +1 -1
- package/dist/assets/{Inputs-CBxLLWdV.js → Inputs-CbenuziM.js} +1 -1
- package/dist/assets/{JsonOutput-BTBD86gT.js → JsonOutput-BUok9dqY.js} +11 -11
- package/dist/assets/{LazyAnyLanguageCodeMirror-Be7SB10I.js → LazyAnyLanguageCodeMirror-BqosdyZU.js} +2 -2
- package/dist/assets/MarimoErrorOutput-Dxf6p5lw.js +7 -0
- package/dist/assets/{RenderHTML-IBhB_MjX.js → RenderHTML-BUCe3qCj.js} +1 -1
- package/dist/assets/{SSRProvider-DBS9qs3r.js → SSRProvider-DrpctNAg.js} +1 -1
- package/dist/assets/__vite-browser-external-D-ioOGDE.js +1 -0
- package/dist/assets/__vite-browser-external-DBckR0WR.js +1 -0
- package/dist/assets/{add-cell-with-ai-DsiiVErB.js → add-cell-with-ai-J44RQi7o.js} +6 -6
- package/dist/assets/{add-database-form-B1frIn24.js → add-database-form-D8ppo-Wx.js} +1 -1
- package/dist/assets/{agent-panel-B_Ur2utx.js → agent-panel-Cf8Ij8-7.js} +5 -5
- package/dist/assets/{ai-model-dropdown-Dix8JIRc.js → ai-model-dropdown-DC4t5PJE.js} +4 -4
- package/dist/assets/{alert-adkp2Qj8.js → alert-Cx8eFRUM.js} +1 -1
- package/dist/assets/{alert-dialog-CordKci7.js → alert-dialog-Ctz24SMn.js} +1 -1
- package/dist/assets/{any-language-editor-Z9d2XKUM.js → any-language-editor-DyXJSyeK.js} +1 -1
- package/dist/assets/{app-config-button-CYFJH9oz.js → app-config-button-xVJ4tqzu.js} +1 -1
- package/dist/assets/{architectureDiagram-VXUJARFQ-B-zQV2Ro.js → architectureDiagram-VXUJARFQ-B8zMpJcM.js} +1 -1
- package/dist/assets/{arrow-left-Cb_73j1f.js → arrow-left-VDC1u5rq.js} +1 -1
- package/dist/assets/{badge-Ce8wRjuQ.js → badge-D4keL3YC.js} +1 -1
- package/dist/assets/blob-Bgnx1kuY.js +1 -0
- package/dist/assets/{blockDiagram-VD42YOAC-B7BRAty8.js → blockDiagram-VD42YOAC-CF1a-_t0.js} +1 -1
- package/dist/assets/{bundle.esm-B-jCcPJK.js → bundle.esm-CBAPjfrh.js} +1 -1
- package/dist/assets/{button-YC1gW_kJ.js → button-CZ3Cs4qb.js} +1 -1
- package/dist/assets/{c4Diagram-YG6GDRKO-C5JyC9v_.js → c4Diagram-YG6GDRKO-YIsEsUdQ.js} +1 -1
- package/dist/assets/{cache-panel-BfCxvTOh.js → cache-panel-C_9WDApV.js} +1 -1
- package/dist/assets/{card-BAxUmnnT.js → card-DdlNjkEA.js} +1 -1
- package/dist/assets/{cell-editor-BQ5bBOiS.js → cell-editor-DqRAVfCm.js} +14 -14
- package/dist/assets/{cell-link-DW0zobk9.js → cell-link-dnd6DfEI.js} +1 -1
- package/dist/assets/{cells-BcMpgRWV.js → cells-CiFhRLUd.js} +32 -32
- package/dist/assets/channel-CXFsg7SH.js +1 -0
- package/dist/assets/{chart-no-axes-column-W42b2ZIs.js → chart-no-axes-column-qvVRjhv1.js} +1 -1
- package/dist/assets/{chat-components-BpIX7pUp.js → chat-components-C5FA1-Vu.js} +1 -1
- package/dist/assets/{chat-display-CVXM0cmr.js → chat-display-ChS1NL3o.js} +1 -1
- package/dist/assets/chat-panel-Duk4139r.js +3 -0
- package/dist/assets/check-Dr3SxUsb.js +1 -0
- package/dist/assets/{chevron-right-DwagBitu.js → chevron-right--18M_6o9.js} +1 -1
- package/dist/assets/{chunk-5FQGJX7Z-Bmb7yu_c.js → chunk-5FQGJX7Z-T2jyw7ZD.js} +3 -3
- package/dist/assets/{chunk-ABZYJK2D-Coo5qi7b.js → chunk-ABZYJK2D-D1A06qKo.js} +3 -3
- package/dist/assets/{chunk-ATLVNIR6-C0ULYreM.js → chunk-ATLVNIR6-C5FWd1qN.js} +1 -1
- package/dist/assets/{chunk-B4BG7PRW-D6Q5lT5Z.js → chunk-B4BG7PRW-BEayfxv_.js} +1 -1
- package/dist/assets/{chunk-DI55MBZ5-BUUtkB-8.js → chunk-DI55MBZ5-BSrtupT8.js} +1 -1
- package/dist/assets/{chunk-EXTU4WIE-DQy5cN5X.js → chunk-EXTU4WIE-wi_fHsHN.js} +1 -1
- package/dist/assets/{chunk-JA3XYJ7Z-99Y1UR46.js → chunk-JA3XYJ7Z-tKDxhnhw.js} +1 -1
- package/dist/assets/{chunk-JZLCHNYA-DRGkEV50.js → chunk-JZLCHNYA-DtvI9B9X.js} +1 -1
- package/dist/assets/{chunk-N4CR4FBY-DHFG9ldR.js → chunk-N4CR4FBY-BQcJpHO7.js} +2 -2
- package/dist/assets/{chunk-QN33PNHL-DB8FgLRd.js → chunk-QN33PNHL-BboKw3W6.js} +1 -1
- package/dist/assets/{chunk-QXUST7PY-_zxuSm7L.js → chunk-QXUST7PY-C4Ihfbmy.js} +1 -1
- package/dist/assets/{chunk-S3R3BYOJ-PVCoxQeQ.js → chunk-S3R3BYOJ-D1UiABQX.js} +1 -1
- package/dist/assets/{chunk-TZMSLE5B-KYGSDxPK.js → chunk-TZMSLE5B-BjrxQwUX.js} +1 -1
- package/dist/assets/{circle-check-C57eOpl0.js → circle-check-DquVD4wZ.js} +1 -1
- package/dist/assets/{circle-play-BuOtSgid.js → circle-play-EowqxNIC.js} +1 -1
- package/dist/assets/{circle-plus-CnWl9uZo.js → circle-plus-haI9GLDP.js} +1 -1
- package/dist/assets/classDiagram-2ON5EDUG-BHD5dOKT.js +1 -0
- package/dist/assets/classDiagram-v2-WZHVMYZB-VYd80hrd.js +1 -0
- package/dist/assets/{clear-button-CF0iBcV9.js → clear-button-DI7_bdOB.js} +1 -1
- package/dist/assets/{click-outside-container-B12FHFHm.js → click-outside-container-Npdlddni.js} +1 -1
- package/dist/assets/{clipboard-paste-BiDF-cqm.js → clipboard-paste-CpyPR-HZ.js} +1 -1
- package/dist/assets/{code-block-37QAKDTI-D5PRUB7R.js → code-block-37QAKDTI-FUPT3-7o.js} +1 -1
- package/dist/assets/{code-xml-XLwHyDBr.js → code-xml-CgN_Yig7.js} +1 -1
- package/dist/assets/{column-preview-v3J210SK.js → column-preview-BUTci9vI.js} +1 -1
- package/dist/assets/{command-DOzj5zu3.js → command-MHa10zT2.js} +1 -1
- package/dist/assets/{command-palette-B94wuFEk.js → command-palette-BBecgDwg.js} +1 -1
- package/dist/assets/{common-DHF6uQdA.js → common-CR6DwVIy.js} +1 -1
- package/dist/assets/compiler-runtime-B3qBwwSJ.js +1 -0
- package/dist/assets/{config-C3-Nti03.js → config-D2-Bw6QC.js} +1 -1
- package/dist/assets/{context-CJp-B2dx.js → context-Ga_vV5mE.js} +1 -1
- package/dist/assets/{copy-CQ15EONK.js → copy-D-8y6iMN.js} +1 -1
- package/dist/assets/{copy-icon-C1fNHY3M.js → copy-icon-C9v8EtBA.js} +1 -1
- package/dist/assets/createLucideIcon-BCdY6lG5.js +1 -0
- package/dist/assets/{createReducer-Dnna-AUO.js → createReducer-B3rBsy4P.js} +1 -1
- package/dist/assets/{dagre-6UL2VRFP-khPiqbzB.js → dagre-6UL2VRFP-CX3NGWnu.js} +1 -1
- package/dist/assets/{data-grid-overlay-editor-Ca1ELXWD.js → data-grid-overlay-editor-BTyw6887.js} +1 -1
- package/dist/assets/{database-zap-B9y7063w.js → database-zap-k4ePIFAU.js} +1 -1
- package/dist/assets/{datasource-BZ3O2HG1.js → datasource-C7Acf9BM.js} +1 -1
- package/dist/assets/{dates-Cx863TsG.js → dates-CT5PUuFo.js} +1 -1
- package/dist/assets/{dependency-graph-panel-s45LIDec.js → dependency-graph-panel-n8clB1B4.js} +4 -4
- package/dist/assets/{diagram-PSM6KHXK-C7-DZLOE.js → diagram-PSM6KHXK-C0Sfgs1J.js} +1 -1
- package/dist/assets/{diagram-QEK2KX5R-Ct2_XsRV.js → diagram-QEK2KX5R-DYSkW6Ju.js} +1 -1
- package/dist/assets/{diagram-S2PKOQOG-C82IZoPA.js → diagram-S2PKOQOG-CfHqQVo3.js} +1 -1
- package/dist/assets/{dialog-Dstm3Ubn.js → dialog-tQELcltn.js} +1 -1
- package/dist/assets/{dist-oarhZoo6.js → dist-BMEHQAhk.js} +1 -1
- package/dist/assets/{dist-PT7WsOdN.js → dist-DDGMbl6p.js} +1 -1
- package/dist/assets/{dist-BhLw_JdQ.js → dist-h1RhtzUB.js} +1 -1
- package/dist/assets/{documentation-panel-DRqbOPwM.js → documentation-panel-B4SYV31-.js} +1 -1
- package/dist/assets/{download-zuPzhwvm.js → download-CUgZOA9O.js} +1 -1
- package/dist/assets/{download-B9SUL40m.js → download-Dg7clfkc.js} +1 -1
- package/dist/assets/{dropdown-menu-D8v7RjW8.js → dropdown-menu-BUgFWmX1.js} +1 -1
- package/dist/assets/{edit-page-Dlr4v1eI.js → edit-page-CZzL95NE.js} +7 -7
- package/dist/assets/{ellipsis-5ip-qDfN.js → ellipsis-DfDsMPvA.js} +1 -1
- package/dist/assets/{ellipsis-vertical-C7jYiLCo.js → ellipsis-vertical-J1F7_WS9.js} +1 -1
- package/dist/assets/{empty-state-BAGXXrn8.js → empty-state-DrmGF88A.js} +1 -1
- package/dist/assets/{en-US-XLBup7bs.js → en-US-DU2jJCpg.js} +1 -1
- package/dist/assets/{erDiagram-Q2GNP2WA-Ftt2qDiM.js → erDiagram-Q2GNP2WA-BfRyUS6D.js} +1 -1
- package/dist/assets/{error-banner-JIexM8h5.js → error-banner-w4pz2qUB.js} +1 -1
- package/dist/assets/{error-panel-TqjIx6nC.js → error-panel-Dp0gIK8O.js} +1 -1
- package/dist/assets/{errors-B5AI4nf5.js → errors-CPlNr33a.js} +1 -1
- package/dist/assets/{es-CPSXxuS6.js → es-GXAIFcot.js} +5 -5
- package/dist/assets/{esm-BbNm2fB0.js → esm-BK6tLEHQ.js} +1 -1
- package/dist/assets/{eye-off-BhExYOph.js → eye-off-AK_9uodG.js} +1 -1
- package/dist/assets/{field-1uCBX4j4.js → field-D9jE8HEt.js} +1 -1
- package/dist/assets/{file-Cs1JbsV6.js → file-Ch78NKWp.js} +1 -1
- package/dist/assets/{file-explorer-panel-jku_uMQn.js → file-explorer-panel-BqVToUzC.js} +1 -1
- package/dist/assets/{file-plus-corner-Da9I6dgU.js → file-plus-corner-CvAy4H5W.js} +1 -1
- package/dist/assets/{file-video-camera-DW3v07j2.js → file-video-camera-C3wGzBnE.js} +1 -1
- package/dist/assets/{floating-outline-CZbF47gm.js → floating-outline-CZzxqWyr.js} +1 -1
- package/dist/assets/{flowDiagram-NV44I4VS-Ca9aZp-3.js → flowDiagram-NV44I4VS-DKkZCUwG.js} +1 -1
- package/dist/assets/{focus-BxGelf2n.js → focus-CtXToHnV.js} +1 -1
- package/dist/assets/{form-DQt-Ldr4.js → form-CvVbbh8Y.js} +1 -1
- package/dist/assets/{formats-CobRswjh.js → formats-DGZwRUPy.js} +1 -1
- package/dist/assets/gallery-page-1AmSypJ6.js +1 -0
- package/dist/assets/{ganttDiagram-JELNMOA3-PYFPdyuJ.js → ganttDiagram-JELNMOA3-J8yLTqxn.js} +1 -1
- package/dist/assets/{gitGraphDiagram-NY62KEGX-CfGWoYOo.js → gitGraphDiagram-NY62KEGX-DXw3a-uS.js} +1 -1
- package/dist/assets/{glide-data-editor-8ek3zzAO.js → glide-data-editor-Cw8eE9Xn.js} +4 -4
- package/dist/assets/globals-D1aM0aUX.js +1 -0
- package/dist/assets/{home-page-xDhfcHL1.js → home-page-DJMymeT8.js} +1 -1
- package/dist/assets/{hooks-MQa8CSUr.js → hooks-STSuFYve.js} +1 -1
- package/dist/assets/{house-DhFkiXz7.js → house-BI81AWSn.js} +1 -1
- package/dist/assets/{html-to-image-DKqpcM7V.js → html-to-image-BKGhRUqR.js} +1 -1
- package/dist/assets/{icons-Dw4USSvE.js → icons-DjR3qLG_.js} +1 -1
- package/dist/assets/{index-DuqUGjtK.js → index-CQLP3a6_.js} +22 -22
- package/dist/assets/index-GcHhnSIR.css +2 -0
- package/dist/assets/{infoDiagram-WHAUD3N6-Csbwq6Ks.js → infoDiagram-WHAUD3N6-32du45zK.js} +1 -1
- package/dist/assets/{input-DcAGg_4_.js → input-DHHmNa19.js} +1 -1
- package/dist/assets/{isValid-BLB2jskA.js → isValid-DpSSOU5z.js} +1 -1
- package/dist/assets/{journeyDiagram-XKPGCS4Q-DArwMxlY.js → journeyDiagram-XKPGCS4Q-Bmx7dA25.js} +1 -1
- package/dist/assets/{kanban-definition-3W4ZIXB7-CPjBJiO_.js → kanban-definition-3W4ZIXB7-DJzhLwsl.js} +1 -1
- package/dist/assets/{katex-DYp6oq7P.js → katex-CbllUrnh.js} +1 -1
- package/dist/assets/katex-axIMlGRc.js +1 -0
- package/dist/assets/{kbd-uRyPZ9w_.js → kbd-k3Sn_RwW.js} +1 -1
- package/dist/assets/kiosk-mode-B93ch1Yg.js +1 -0
- package/dist/assets/{label-CL9Xfexh.js → label-C--1sWU6.js} +1 -1
- package/dist/assets/{layout-D-YPCWvX.js → layout-DWzvYkJc.js} +4 -4
- package/dist/assets/{linear-BxhToEEF.js → linear-DWpCiijk.js} +1 -1
- package/dist/assets/{link-BsTPF7B0.js → link-DxicfMbs.js} +1 -1
- package/dist/assets/{links-BhgWpHdU.js → links-fh_IXIKE.js} +1 -1
- package/dist/assets/{logs-panel-DKSJRyF4.js → logs-panel-Cqc-8g3U.js} +1 -1
- package/dist/assets/{loro_wasm_bg-BnAHSQ7o.js → loro_wasm_bg-CdafknAX.js} +2 -2
- package/dist/assets/loro_wasm_bg-WKwciKJa.wasm +0 -0
- package/dist/assets/{loro_wasm_bg-CKQGJrH5.js → loro_wasm_bg-aCueZs8k.js} +1 -1
- package/dist/assets/{maps-t9yNKYA8.js → maps-D2_Mq1pZ.js} +1 -1
- package/dist/assets/markdown-renderer-Dotqbx_q.js +10 -0
- package/dist/assets/{menu-items-NblnuU_B.js → menu-items-vwmjPhjW.js} +1 -1
- package/dist/assets/mermaid-4DMBBIKO-Dn0v3H8Z.js +1 -0
- package/dist/assets/{mermaid-Bzaf_nA9.js → mermaid-dAaJQU1M.js} +3 -3
- package/dist/assets/{mhchem-DLDYYsJK.js → mhchem-2XRJK9de.js} +1 -1
- package/dist/assets/{mindmap-definition-VGOIOE7T-DVB4z5Yy.js → mindmap-definition-VGOIOE7T-whTFv8Zv.js} +1 -1
- package/dist/assets/{mode-CevBiFCd.js → mode-O-1bKZO6.js} +1 -1
- package/dist/assets/{multi-icon-CLzAg610.js → multi-icon-BWLbAXq5.js} +1 -1
- package/dist/assets/{multi-map-W4zci54U.js → multi-map-A4XNra08.js} +1 -1
- package/dist/assets/{name-cell-input-DwvdP1Ck.js → name-cell-input-Bb4XgE9B.js} +1 -1
- package/dist/assets/{number-overlay-editor-CvIcqV0W.js → number-overlay-editor-65UcMR52.js} +1 -1
- package/dist/assets/{outline-panel-Bg_CP1WV.js → outline-panel-DvQn81N7.js} +1 -1
- package/dist/assets/{packages-panel-DBgrbk-q.js → packages-panel-CFja8lcb.js} +1 -1
- package/dist/assets/{panel-context-C-UJlFyL.js → panel-context-2mVKaSYm.js} +1 -1
- package/dist/assets/panels-DkQNKTXx.js +1 -0
- package/dist/assets/{pieDiagram-ADFJNKIX-vwwb5uL8.js → pieDiagram-ADFJNKIX-4n07I4VV.js} +1 -1
- package/dist/assets/{play-BPIh-ZEU.js → play-GLWQQs7F.js} +1 -1
- package/dist/assets/{plus-BD5o34_i.js → plus-B7DF33lD.js} +1 -1
- package/dist/assets/{popover-BZZFx3Q8.js → popover-B-131r4d.js} +1 -1
- package/dist/assets/{precisionRound-CU2C3Vxx.js → precisionRound-C3fmBb6j.js} +1 -1
- package/dist/assets/{process-output-Cl2N-HlT.js → process-output-B1EqjOGn.js} +1 -1
- package/dist/assets/{quadrantDiagram-AYHSOK5B-Y8bEZ32G.js → quadrantDiagram-AYHSOK5B-CNm24EC2.js} +1 -1
- package/dist/assets/{react-BGmjiNul.js → react-Bj1aDYRI.js} +1 -1
- package/dist/assets/{react-dom-C9fstfnp.js → react-dom-CSu739Rf.js} +1 -1
- package/dist/assets/{react-plotly-CZMBCa5P.js → react-plotly-OyYr93y7.js} +1 -1
- package/dist/assets/{react-resizable-panels.browser.esm-BFvP7Upm.js → react-resizable-panels.browser.esm-Mq45xjWt.js} +1 -1
- package/dist/assets/{react-vega-BCDifxTe.js → react-vega-BEiHA2e9.js} +1 -1
- package/dist/assets/react-vega-BrNLU7si.js +1 -0
- package/dist/assets/readonly-python-code-3nLIGqDx.js +1 -0
- package/dist/assets/{refresh-ccw-DLEiQDS3.js → refresh-ccw-DN_xCV6A.js} +1 -1
- package/dist/assets/{refresh-cw-CQd-1kjx.js → refresh-cw-Dx8TEWFP.js} +1 -1
- package/dist/assets/{renderShortcut-zxgoVTeP.js → renderShortcut-Xr6z-RjZ.js} +1 -1
- package/dist/assets/request-registry-BWqkV683.js +1 -0
- package/dist/assets/{requests-BsVD4CdD.js → requests-B4FYHTZl.js} +1 -1
- package/dist/assets/requests-BQkjRazP.js +1 -0
- package/dist/assets/{requirementDiagram-UZGBJVZJ-ChckaTrX.js → requirementDiagram-UZGBJVZJ-B7BwuJGh.js} +1 -1
- package/dist/assets/{rotate-ccw-B_yU4f05.js → rotate-ccw-DPbJxVVN.js} +1 -1
- package/dist/assets/run-page-CyMNZt0d.js +1 -0
- package/dist/assets/{runs-X54OGDTq.js → runs-DyBOFmAK.js} +1 -1
- package/dist/assets/{sankeyDiagram-TZEHDZUN-DtU4O0-i.js → sankeyDiagram-TZEHDZUN-C8KpmdhC.js} +1 -1
- package/dist/assets/{save-PMHbdtqc.js → save-DZodxFnE.js} +1 -1
- package/dist/assets/{save-worker-DtF6B3PS.js → save-worker-CtJsIYIM.js} +3 -3
- package/dist/assets/{scratchpad-panel-CIxZIJ-1.js → scratchpad-panel-eznTRPdo.js} +1 -1
- package/dist/assets/{secrets-panel-TwfjwIVv.js → secrets-panel-CEMSgQv5.js} +1 -1
- package/dist/assets/{select-CX1Yi8J8.js → select-B7bfltkI.js} +1 -1
- package/dist/assets/{sequenceDiagram-WL72ISMW-BGXrTemh.js → sequenceDiagram-WL72ISMW-D0_AcPzd.js} +1 -1
- package/dist/assets/{session-panel-CNeWVmTR.js → session-panel-BCN5AHky.js} +1 -1
- package/dist/assets/{settings-DOXWMfVd.js → settings-OBbrbhij.js} +1 -1
- package/dist/assets/{share-Ba-pHZtq.js → share-rXkgGlhr.js} +1 -1
- package/dist/assets/{slides-component-CGMJm_kL.js → slides-component-C0UAH9QZ.js} +1 -1
- package/dist/assets/{snippets-panel-DMN_ZSXA.js → snippets-panel-Exg8CrcD.js} +1 -1
- package/dist/assets/{spec-D1kBp3jX.js → spec-Ch0xnJY4.js} +1 -1
- package/dist/assets/{spinner-DaIKav-i.js → spinner-DA8-7wQv.js} +1 -1
- package/dist/assets/{square-C8Tw_XXG.js → square-DPZjfUaq.js} +1 -1
- package/dist/assets/{square-function-CqXXKtIq.js → square-function-B6mgCeFJ.js} +1 -1
- package/dist/assets/{state-BLsnFVwy.js → state-CMAvJZxx.js} +1 -1
- package/dist/assets/{state-n1Xz9vt7.js → state-DAl4qLSi.js} +1 -1
- package/dist/assets/{stateDiagram-FKZM4ZOC-BwSf7RIr.js → stateDiagram-FKZM4ZOC-Dz_iVJd6.js} +1 -1
- package/dist/assets/stateDiagram-v2-4FDKWEC3-CupainMB.js +1 -0
- package/dist/assets/{switch-CfRu5JBF.js → switch-BF1BnESM.js} +1 -1
- package/dist/assets/{table-qbh6Daed.js → table-BOsFCeLh.js} +1 -1
- package/dist/assets/{table-DZR6ewbN.js → table-CfDbAm78.js} +1 -1
- package/dist/assets/{terminal-C0fH4d_0.js → terminal-BLSSaFMR.js} +1 -1
- package/dist/assets/{textarea-3ZXdUni1.js → textarea-sdXkC92D.js} +1 -1
- package/dist/assets/{time-EL4bnqqQ.js → time-CSCsAxXS.js} +1 -1
- package/dist/assets/{timeline-definition-IT6M3QCI-BZakpQwy.js → timeline-definition-IT6M3QCI-DPXEeoSm.js} +1 -1
- package/dist/assets/{toDate-CgbKQM5E.js → toDate-DETS9bBd.js} +1 -1
- package/dist/assets/{toggle-CWS-iCmF.js → toggle-CriARMQK.js} +1 -1
- package/dist/assets/{tooltip-DH6k_bBY.js → tooltip-C_WgOOcZ.js} +1 -1
- package/dist/assets/{tooltip-DxKBXCGp.js → tooltip-DqsDDLgU.js} +1 -1
- package/dist/assets/{tracing-zzjDz6Hy.js → tracing-BCW3BhkT.js} +1 -1
- package/dist/assets/tracing-panel-0nE7-znv.js +2 -0
- package/dist/assets/{trash-2-CyqGun26.js → trash-2-B8SQYdQq.js} +1 -1
- package/dist/assets/{trash-DMqDgJ5r.js → trash-Cv0yH8fs.js} +1 -1
- package/dist/assets/{tree-DHpFOpHo.js → tree-Hg81oJmp.js} +1 -1
- package/dist/assets/{triangle-alert-B65rDESJ.js → triangle-alert-U93NZYDl.js} +1 -1
- package/dist/assets/{types-CS34eOZi.js → types-C1UhS3qM.js} +1 -1
- package/dist/assets/{types-CW-zyy80.js → types-ChPQge47.js} +1 -1
- package/dist/assets/{use-toast-rmUWldD_.js → use-toast-T0_cQDma.js} +1 -1
- package/dist/assets/useAddCell-DQzBfMNz.js +1 -0
- package/dist/assets/{useAsyncData-BAVCkkQP.js → useAsyncData-CgmD3hjw.js} +1 -1
- package/dist/assets/{useBoolean-BjVFTMam.js → useBoolean-IZsSX_XF.js} +1 -1
- package/dist/assets/useCellActionButton-ChlSq72t.js +1 -0
- package/dist/assets/{useDateFormatter-D89e9xZY.js → useDateFormatter-CieT0-H-.js} +1 -1
- package/dist/assets/{useDebounce-CU_caGaU.js → useDebounce-B28kFfrM.js} +1 -1
- package/dist/assets/{useDeepCompareMemoize-DiyglPgJ.js → useDeepCompareMemoize-CFtQU7An.js} +1 -1
- package/dist/assets/{useDeleteCell-B8CxYiji.js → useDeleteCell-BocCAiDk.js} +1 -1
- package/dist/assets/{useDependencyPanelTab-YtWR0xDK.js → useDependencyPanelTab-BUgnrQ1n.js} +1 -1
- package/dist/assets/useEvent-BhXAndur.js +1 -0
- package/dist/assets/{useEventListener-DIUKKfEy.js → useEventListener-Cb-RVVEn.js} +1 -1
- package/dist/assets/{useIframeCapabilities-DzIlROXe.js → useIframeCapabilities-CC8VH6kZ.js} +1 -1
- package/dist/assets/{useInstallPackage-C-gjrVjb.js → useInstallPackage-Co79H1lQ.js} +1 -1
- package/dist/assets/{useInterval-y9MtYlSD.js → useInterval-BNfubus2.js} +1 -1
- package/dist/assets/{useLifecycle-D35CBukS.js → useLifecycle-ClI_npeg.js} +1 -1
- package/dist/assets/{useNonce-_Aax6sXd.js → useNonce-CS26E0hA.js} +1 -1
- package/dist/assets/{useNotebookActions-Dfh_7NPm.js → useNotebookActions-wEv3D5Ih.js} +1 -1
- package/dist/assets/{useNumberFormatter-CTkflual.js → useNumberFormatter-BX4DTYtT.js} +1 -1
- package/dist/assets/{usePress-CoEeT5jq.js → usePress-3CRs0fQg.js} +1 -1
- package/dist/assets/{useRunCells-rjrIgU8v.js → useRunCells-CNmJhP6h.js} +1 -1
- package/dist/assets/{useSplitCell-fcjSuBNO.js → useSplitCell-DwvRo6W7.js} +1 -1
- package/dist/assets/{useTheme-EGf2UOFo.js → useTheme-CTORu22_.js} +1 -1
- package/dist/assets/utilities.esm-OH_d3CNC.js +3 -0
- package/dist/assets/{utils-DXvhzCGS.js → utils-YqBXNpsM.js} +1 -1
- package/dist/assets/{vega-component-DAJbxD47.js → vega-component-7jd4fFCJ.js} +1 -1
- package/dist/assets/{vega-loader.browser-DXARUlxo.js → vega-loader.browser-BegSZk0G.js} +1 -1
- package/dist/assets/{worker-CUL1lW-N.js → worker-CyCdlLVf.js} +3 -3
- package/dist/assets/{workflow-CMjI9cxl.js → workflow-B12dAR4X.js} +1 -1
- package/dist/assets/{write-secret-modal-CgAvgY9w.js → write-secret-modal-DO-EOoNO.js} +1 -1
- package/dist/assets/{x-CoXDX2vQ.js → x-ZP5cObgf.js} +1 -1
- package/dist/assets/{xychartDiagram-PRI3JC2R-CAHFvN1H.js → xychartDiagram-PRI3JC2R-DINfGlOV.js} +1 -1
- package/dist/assets/{youtube-8p26v8EM.js → youtube--tNPNRy6.js} +1 -1
- package/dist/assets/zod-H_cgTO0M.js +40 -0
- package/dist/index.html +139 -139
- package/package.json +18 -18
- package/src/components/app-config/ai-config.tsx +11 -2
- package/src/components/app-config/user-config-form.tsx +0 -54
- package/src/components/chat/acp/__tests__/state.test.ts +69 -0
- package/src/components/chat/acp/state.ts +6 -6
- package/src/components/chat/chat-panel.tsx +47 -30
- package/src/components/data-table/__tests__/data-table.test.tsx +94 -2
- package/src/components/editor/actions/useCellActionButton.tsx +14 -1
- package/src/components/editor/cell/CreateCellButton.tsx +2 -1
- package/src/components/editor/cell/code/cell-editor.tsx +12 -0
- package/src/components/editor/renderers/cell-array.tsx +2 -1
- package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +12 -0
- package/src/components/pages/gallery-page.tsx +37 -6
- package/src/core/MarimoApp.tsx +12 -8
- package/src/core/ai/context/providers/file.ts +1 -1
- package/src/core/cells/__tests__/cells.test.ts +120 -0
- package/src/core/cells/cells.ts +14 -0
- package/src/core/codemirror/language/languages/markdown.ts +7 -0
- package/src/core/config/feature-flag.tsx +0 -4
- package/src/core/islands/__tests__/bridge.test.ts +241 -0
- package/src/core/islands/bridge.ts +22 -6
- package/src/core/run-app.tsx +11 -4
- package/src/core/static/__tests__/files.test.ts +195 -1
- package/src/core/static/files.ts +39 -9
- package/src/plugins/core/registerReactComponent.tsx +9 -1
- package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +164 -0
- package/src/plugins/impl/anywidget/AnyWidgetPlugin.tsx +7 -1
- package/src/utils/__tests__/blob.test.ts +3 -3
- package/src/utils/__tests__/mime-types.test.ts +8 -10
- package/src/utils/__tests__/url-parser.test.ts +22 -0
- package/src/utils/blob.ts +14 -27
- package/src/utils/mime-types.ts +5 -5
- package/src/utils/url-parser.ts +1 -1
- package/dist/assets/MarimoErrorOutput-DuaUr0W-.js +0 -7
- package/dist/assets/__vite-browser-external-BkzFKOxE.js +0 -1
- package/dist/assets/__vite-browser-external-CPyDqUtZ.js +0 -1
- package/dist/assets/blob-t6qcPM7K.js +0 -1
- package/dist/assets/channel-ownMsSXG.js +0 -1
- package/dist/assets/chat-panel-FiDMP_Qr.js +0 -3
- package/dist/assets/check-DdfN0k2d.js +0 -1
- package/dist/assets/classDiagram-2ON5EDUG-CSgD2v8F.js +0 -1
- package/dist/assets/classDiagram-v2-WZHVMYZB-YTETTIT0.js +0 -1
- package/dist/assets/compiler-runtime-DeeZ7FnK.js +0 -1
- package/dist/assets/createLucideIcon-CnW3RofX.js +0 -1
- package/dist/assets/gallery-page-D_5X4kKx.js +0 -1
- package/dist/assets/globals-D4t_HyAp.js +0 -1
- package/dist/assets/index-CikhHYAB.css +0 -2
- package/dist/assets/katex-BZrXWsTs.js +0 -1
- package/dist/assets/kiosk-mode-C9XznhoS.js +0 -1
- package/dist/assets/loro_wasm_bg-DV7Kb4_M.wasm +0 -0
- package/dist/assets/markdown-renderer-Bu12gPyJ.js +0 -10
- package/dist/assets/mermaid-4DMBBIKO-RE7Z6-fl.js +0 -1
- package/dist/assets/panels-CaoDGKmF.js +0 -1
- package/dist/assets/react-vega-DkHO4e2L.js +0 -1
- package/dist/assets/readonly-python-code-H1kzT-lB.js +0 -1
- package/dist/assets/request-registry-CjZcQuTP.js +0 -1
- package/dist/assets/requests-BSPGoGJL.js +0 -1
- package/dist/assets/run-page-BmTC7uxn.js +0 -1
- package/dist/assets/stateDiagram-v2-4FDKWEC3-SWMI9LKY.js +0 -1
- package/dist/assets/tracing-panel-DogOVTzf.js +0 -2
- package/dist/assets/useAddCell-mkD8zurm.js +0 -1
- package/dist/assets/useCellActionButton-BZ46_Pdt.js +0 -1
- package/dist/assets/useEvent-DO6uJBas.js +0 -1
- package/dist/assets/utilities.esm-CkqwfzLw.js +0 -3
- package/dist/assets/zod-Cg4WLWh2.js +0 -40
- /package/dist/assets/{copy-DHrHayPa.js → copy-BaRrAFL-.js} +0 -0
- /package/dist/assets/{defaultLocale-BLne0bXb.js → defaultLocale-B6z1Qyqt.js} +0 -0
- /package/dist/assets/{defaultLocale-JieDVWC_.js → defaultLocale-YteS-k_t.js} +0 -0
- /package/dist/assets/{emotion-is-prop-valid.esm-C59xfSYt.js → emotion-is-prop-valid.esm-Dangy3Bv.js} +0 -0
- /package/dist/assets/{extends-BiFDv3jB.js → extends-Dqvpuc10.js} +0 -0
- /package/dist/assets/{objectWithoutPropertiesLoose-DfWeGRFv.js → objectWithoutPropertiesLoose-DoKw85w0.js} +0 -0
- /package/dist/assets/{ordinal-C93T4L8H.js → ordinal-DAqJmfoU.js} +0 -0
- /package/dist/assets/{prop-types-DaaA-ptl.js → prop-types-DVDiRdwc.js} +0 -0
- /package/dist/assets/{purify.es-DZrAQFIu.js → purify.es-B-nzWya6.js} +0 -0
- /package/dist/assets/{range-1DwpgXvM.js → range-7fnH_zLA.js} +0 -0
|
@@ -6,7 +6,7 @@ import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
|
|
6
6
|
import { createLoader } from "@/plugins/impl/vega/vega-loader";
|
|
7
7
|
import { Functions } from "@/utils/functions";
|
|
8
8
|
import type { DataURLString } from "@/utils/json/base64";
|
|
9
|
-
import { patchFetch, patchVegaLoader } from "../files";
|
|
9
|
+
import { patchFetch, patchVegaLoader, resolveVirtualFileURL } from "../files";
|
|
10
10
|
|
|
11
11
|
// Start a tiny server to serve virtual files
|
|
12
12
|
const server = http.createServer((request, response) => {
|
|
@@ -350,6 +350,181 @@ describe("patchVegaLoader - loader.load", () => {
|
|
|
350
350
|
});
|
|
351
351
|
});
|
|
352
352
|
|
|
353
|
+
describe("resolveVirtualFileURL", () => {
|
|
354
|
+
// Mock URL.createObjectURL for jsdom environment
|
|
355
|
+
const mockBlobURLs = new Map<string, Blob>();
|
|
356
|
+
let blobCounter = 0;
|
|
357
|
+
|
|
358
|
+
beforeAll(() => {
|
|
359
|
+
URL.createObjectURL = vi.fn((blob: Blob) => {
|
|
360
|
+
const url = `blob:test-${blobCounter++}`;
|
|
361
|
+
mockBlobURLs.set(url, blob);
|
|
362
|
+
return url;
|
|
363
|
+
});
|
|
364
|
+
URL.revokeObjectURL = vi.fn((url: string) => {
|
|
365
|
+
mockBlobURLs.delete(url);
|
|
366
|
+
});
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
afterAll(() => {
|
|
370
|
+
mockBlobURLs.clear();
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
it("should return a blob URL for virtual files", () => {
|
|
374
|
+
const virtualFiles = {
|
|
375
|
+
"/@file/widget.js":
|
|
376
|
+
"data:text/javascript;base64,ZXhwb3J0IGRlZmF1bHQgeyByZW5kZXI6ICgpID0+IHt9IH0=" as DataURLString,
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
const result = resolveVirtualFileURL("/@file/widget.js", virtualFiles);
|
|
380
|
+
|
|
381
|
+
expect(result).toMatch(/^blob:/);
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
it("should return the original URL for non-virtual files", () => {
|
|
385
|
+
const virtualFiles = {};
|
|
386
|
+
|
|
387
|
+
const result = resolveVirtualFileURL(
|
|
388
|
+
"http://example.com/widget.js",
|
|
389
|
+
virtualFiles,
|
|
390
|
+
);
|
|
391
|
+
|
|
392
|
+
expect(result).toBe("http://example.com/widget.js");
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
it("should handle various URL formats", () => {
|
|
396
|
+
const virtualFiles = {
|
|
397
|
+
"/@file/module.js":
|
|
398
|
+
"data:text/javascript;base64,Y29uc29sZS5sb2coJ3Rlc3QnKQ==" as DataURLString,
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
const testUrls = [
|
|
402
|
+
"/@file/module.js",
|
|
403
|
+
"./@file/module.js",
|
|
404
|
+
"http://example.com/@file/module.js",
|
|
405
|
+
];
|
|
406
|
+
|
|
407
|
+
for (const url of testUrls) {
|
|
408
|
+
const result = resolveVirtualFileURL(url, virtualFiles);
|
|
409
|
+
expect(result).toMatch(/^blob:/);
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
it("should create blob URL with correct content", async () => {
|
|
414
|
+
const jsCode = "export default { render: () => {} }";
|
|
415
|
+
const base64Code = btoa(jsCode);
|
|
416
|
+
const virtualFiles = {
|
|
417
|
+
"/@file/test-module.js":
|
|
418
|
+
`data:text/javascript;base64,${base64Code}` as DataURLString,
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
const blobUrl = resolveVirtualFileURL(
|
|
422
|
+
"/@file/test-module.js",
|
|
423
|
+
virtualFiles,
|
|
424
|
+
);
|
|
425
|
+
|
|
426
|
+
expect(blobUrl).toMatch(/^blob:/);
|
|
427
|
+
expect(URL.createObjectURL).toHaveBeenCalled();
|
|
428
|
+
|
|
429
|
+
// Verify blob content through the mock
|
|
430
|
+
const blob = mockBlobURLs.get(blobUrl);
|
|
431
|
+
expect(blob).toBeDefined();
|
|
432
|
+
const text = await blob!.text();
|
|
433
|
+
expect(text).toBe(jsCode);
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
it("should handle file:// URLs with @file/ paths", () => {
|
|
437
|
+
const virtualFiles = {
|
|
438
|
+
"/@file/local-module.js":
|
|
439
|
+
"data:text/javascript;base64,ZXhwb3J0IGRlZmF1bHQge30=" as DataURLString,
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
const result = resolveVirtualFileURL(
|
|
443
|
+
"file:///Users/test/@file/local-module.js",
|
|
444
|
+
virtualFiles,
|
|
445
|
+
);
|
|
446
|
+
|
|
447
|
+
expect(result).toMatch(/^blob:/);
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
it("should handle different MIME types", async () => {
|
|
451
|
+
const virtualFiles = {
|
|
452
|
+
"/@file/script.js":
|
|
453
|
+
"data:application/javascript;base64,Y29uc3QgeCA9IDE=" as DataURLString,
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
const blobUrl = resolveVirtualFileURL("/@file/script.js", virtualFiles);
|
|
457
|
+
|
|
458
|
+
// Should still be a valid blob URL
|
|
459
|
+
expect(blobUrl).toMatch(/^blob:/);
|
|
460
|
+
|
|
461
|
+
// Verify blob content through the mock
|
|
462
|
+
const blob = mockBlobURLs.get(blobUrl);
|
|
463
|
+
expect(blob).toBeDefined();
|
|
464
|
+
const text = await blob!.text();
|
|
465
|
+
expect(text).toBe("const x = 1");
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
it("should handle blob: base URIs correctly", () => {
|
|
469
|
+
// Mock document.baseURI to simulate blob: protocol
|
|
470
|
+
const originalBaseURI = document.baseURI;
|
|
471
|
+
Object.defineProperty(document, "baseURI", {
|
|
472
|
+
value: "blob:https://example.com/uuid",
|
|
473
|
+
configurable: true,
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
const virtualFiles = {
|
|
477
|
+
"/@file/blob-module.js":
|
|
478
|
+
"data:text/javascript;base64,ZXhwb3J0IGRlZmF1bHQge30=" as DataURLString,
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
const result = resolveVirtualFileURL("/@file/blob-module.js", virtualFiles);
|
|
482
|
+
|
|
483
|
+
expect(result).toMatch(/^blob:/);
|
|
484
|
+
|
|
485
|
+
// Restore original baseURI
|
|
486
|
+
Object.defineProperty(document, "baseURI", {
|
|
487
|
+
value: originalBaseURI,
|
|
488
|
+
configurable: true,
|
|
489
|
+
});
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
it("should handle data URLs with no explicit MIME type", async () => {
|
|
493
|
+
const virtualFiles = {
|
|
494
|
+
"/@file/generic.bin": "data:;base64,SGVsbG8gV29ybGQ=" as DataURLString,
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
const blobUrl = resolveVirtualFileURL("/@file/generic.bin", virtualFiles);
|
|
498
|
+
expect(blobUrl).toMatch(/^blob:/);
|
|
499
|
+
|
|
500
|
+
// Verify blob content through the mock
|
|
501
|
+
const blob = mockBlobURLs.get(blobUrl);
|
|
502
|
+
expect(blob).toBeDefined();
|
|
503
|
+
const text = await blob!.text();
|
|
504
|
+
expect(text).toBe("Hello World");
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
it("should match URLs with prefix paths before /@file/", async () => {
|
|
508
|
+
const virtualFiles = {
|
|
509
|
+
"/@file/4263-66-yUGhgQXp.js":
|
|
510
|
+
"data:application/javascript;base64,ZnVuY3Rpb24gcmVuZGVyKCkge30=" as DataURLString,
|
|
511
|
+
};
|
|
512
|
+
|
|
513
|
+
const blobUrl = resolveVirtualFileURL(
|
|
514
|
+
"https://molab.marimo.app/preview/@file/4263-66-yUGhgQXp.js",
|
|
515
|
+
virtualFiles,
|
|
516
|
+
);
|
|
517
|
+
|
|
518
|
+
expect(blobUrl).toMatch(/^blob:/);
|
|
519
|
+
|
|
520
|
+
// Verify blob content through the mock
|
|
521
|
+
const blob = mockBlobURLs.get(blobUrl);
|
|
522
|
+
expect(blob).toBeDefined();
|
|
523
|
+
const text = await blob!.text();
|
|
524
|
+
expect(text).toBe("function render() {}");
|
|
525
|
+
});
|
|
526
|
+
});
|
|
527
|
+
|
|
353
528
|
describe("maybeGetVirtualFile utility function", () => {
|
|
354
529
|
it("should handle URLs without leading dots correctly", async () => {
|
|
355
530
|
const virtualFiles = {
|
|
@@ -370,6 +545,25 @@ describe("maybeGetVirtualFile utility function", () => {
|
|
|
370
545
|
expect(text2).toBe("test");
|
|
371
546
|
});
|
|
372
547
|
|
|
548
|
+
it("should match URLs with prefix paths before /@file/", async () => {
|
|
549
|
+
const virtualFiles = {
|
|
550
|
+
"/@file/4263-66-yUGhgQXp.js":
|
|
551
|
+
"data:application/javascript;base64,ZnVuY3Rpb24gcmVuZGVyKCkge30=" as DataURLString,
|
|
552
|
+
};
|
|
553
|
+
|
|
554
|
+
const unpatch = patchFetch(virtualFiles);
|
|
555
|
+
|
|
556
|
+
// Test URL with a prefix path before /@file/
|
|
557
|
+
const response = await window.fetch(
|
|
558
|
+
"https://molab.marimo.app/preview/@file/4263-66-yUGhgQXp.js",
|
|
559
|
+
);
|
|
560
|
+
const text = await response.text();
|
|
561
|
+
|
|
562
|
+
expect(text).toBe("function render() {}");
|
|
563
|
+
|
|
564
|
+
unpatch();
|
|
565
|
+
});
|
|
566
|
+
|
|
373
567
|
it("should handle complex file:// URLs with nested paths", async () => {
|
|
374
568
|
const virtualFiles = {
|
|
375
569
|
"/@file/nested/data.json":
|
package/src/core/static/files.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
3
|
import type { Loader } from "@/plugins/impl/vega/vega-loader";
|
|
4
|
+
import { deserializeBlob } from "@/utils/blob";
|
|
5
|
+
import type { DataURLString } from "@/utils/json/base64";
|
|
4
6
|
import { Logger } from "@/utils/Logger";
|
|
5
7
|
import { getStaticVirtualFiles } from "./static-state";
|
|
6
8
|
import type { StaticVirtualFiles } from "./types";
|
|
@@ -120,6 +122,24 @@ function withoutLeadingDot(path: string): string {
|
|
|
120
122
|
return path.startsWith(".") ? path.slice(1) : path;
|
|
121
123
|
}
|
|
122
124
|
|
|
125
|
+
/**
|
|
126
|
+
* Resolve a URL to a blob URL if it's a virtual file, for use with dynamic import().
|
|
127
|
+
* Unlike fetch, import() can't be patched, so we need to convert data URLs to blob URLs.
|
|
128
|
+
*
|
|
129
|
+
* @returns The original URL if not a virtual file, or a blob URL if it is
|
|
130
|
+
*/
|
|
131
|
+
export function resolveVirtualFileURL(
|
|
132
|
+
url: string,
|
|
133
|
+
files: StaticVirtualFiles = getStaticVirtualFiles(),
|
|
134
|
+
): string {
|
|
135
|
+
const vfile = maybeGetVirtualFile(url, files);
|
|
136
|
+
if (!vfile) {
|
|
137
|
+
return url;
|
|
138
|
+
}
|
|
139
|
+
const blob = deserializeBlob(vfile as DataURLString);
|
|
140
|
+
return URL.createObjectURL(blob);
|
|
141
|
+
}
|
|
142
|
+
|
|
123
143
|
function maybeGetVirtualFile(
|
|
124
144
|
url: string,
|
|
125
145
|
files: StaticVirtualFiles,
|
|
@@ -130,14 +150,11 @@ function maybeGetVirtualFile(
|
|
|
130
150
|
}
|
|
131
151
|
const pathname = new URL(url, base).pathname;
|
|
132
152
|
|
|
133
|
-
//
|
|
134
|
-
//
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
url = url.slice(indexOfFile);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
153
|
+
// Extract the /@file/... suffix from the URL or pathname
|
|
154
|
+
// This handles URLs like https://example.com/prefix/@file/foo.js
|
|
155
|
+
// or file:///path/to/@file/foo.js
|
|
156
|
+
const filePathFromUrl = extractFilePath(url);
|
|
157
|
+
const filePathFromPathname = extractFilePath(pathname);
|
|
141
158
|
|
|
142
159
|
// Few variations to grab the URL.
|
|
143
160
|
// This can happen if a static file was open at file:// or https://
|
|
@@ -145,6 +162,19 @@ function maybeGetVirtualFile(
|
|
|
145
162
|
files[url] ||
|
|
146
163
|
files[withoutLeadingDot(url)] ||
|
|
147
164
|
files[pathname] ||
|
|
148
|
-
files[withoutLeadingDot(pathname)]
|
|
165
|
+
files[withoutLeadingDot(pathname)] ||
|
|
166
|
+
(filePathFromUrl && files[filePathFromUrl]) ||
|
|
167
|
+
(filePathFromPathname && files[filePathFromPathname])
|
|
149
168
|
);
|
|
150
169
|
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Extract the /@file/... path from a URL string
|
|
173
|
+
*/
|
|
174
|
+
function extractFilePath(url: string): string | null {
|
|
175
|
+
const indexOfFile = url.indexOf("/@file/");
|
|
176
|
+
if (indexOfFile !== -1) {
|
|
177
|
+
return url.slice(indexOfFile);
|
|
178
|
+
}
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
@@ -101,10 +101,17 @@ function PluginSlotInternal<T>(
|
|
|
101
101
|
return plugin.validator.safeParse(parseDataset(hostElement));
|
|
102
102
|
});
|
|
103
103
|
|
|
104
|
+
// Incremented on each reset to invalidate memoized function references.
|
|
105
|
+
// This ensures that plugin functions (e.g., search) are re-created when
|
|
106
|
+
// the underlying UI element instance changes (new object-id), even if
|
|
107
|
+
// the element's data attributes haven't changed.
|
|
108
|
+
const [resetNonce, setResetNonce] = useState(0);
|
|
109
|
+
|
|
104
110
|
useImperativeHandle(ref, () => ({
|
|
105
111
|
reset: () => {
|
|
106
112
|
setValue(getInitialValue());
|
|
107
113
|
setParsedResult(plugin.validator.safeParse(parseDataset(hostElement)));
|
|
114
|
+
setResetNonce((n) => n + 1);
|
|
108
115
|
},
|
|
109
116
|
setChildren: (children) => {
|
|
110
117
|
setChildNodes(children);
|
|
@@ -224,7 +231,8 @@ function PluginSlotInternal<T>(
|
|
|
224
231
|
}
|
|
225
232
|
|
|
226
233
|
return methods;
|
|
227
|
-
|
|
234
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
235
|
+
}, [plugin.functions, hostElement, resetNonce]);
|
|
228
236
|
|
|
229
237
|
// If we failed to parse the initial value, render an error
|
|
230
238
|
if (!parsedResult.success) {
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { TooltipProvider } from "@radix-ui/react-tooltip";
|
|
4
|
+
import { act, render, screen, waitFor } from "@testing-library/react";
|
|
5
|
+
import { Provider } from "jotai";
|
|
6
|
+
import { beforeAll, describe, expect, it, vi } from "vitest";
|
|
7
|
+
import type { DownloadAsArgs } from "@/components/data-table/schemas";
|
|
8
|
+
import type { FieldTypesWithExternalType } from "@/components/data-table/types";
|
|
9
|
+
import { store } from "@/core/state/jotai";
|
|
10
|
+
import {
|
|
11
|
+
type GetDataUrl,
|
|
12
|
+
type GetRowIds,
|
|
13
|
+
LoadingDataTableComponent,
|
|
14
|
+
} from "../DataTablePlugin";
|
|
15
|
+
|
|
16
|
+
beforeAll(() => {
|
|
17
|
+
global.ResizeObserver = class ResizeObserver {
|
|
18
|
+
observe() {
|
|
19
|
+
// do nothing
|
|
20
|
+
}
|
|
21
|
+
unobserve() {
|
|
22
|
+
// do nothing
|
|
23
|
+
}
|
|
24
|
+
disconnect() {
|
|
25
|
+
// do nothing
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe("LoadingDataTableComponent", () => {
|
|
31
|
+
/**
|
|
32
|
+
* Regression test for https://github.com/marimo-team/marimo/issues/8023
|
|
33
|
+
*
|
|
34
|
+
* When a table is replaced via mo.output.replace() with updated data,
|
|
35
|
+
* but the initial page data (unsorted first page) hasn't changed,
|
|
36
|
+
* the useAsyncData hook's deps may all remain the same.
|
|
37
|
+
* Previously, the `search` function reference was memoized on
|
|
38
|
+
* [plugin.functions, hostElement] and wouldn't change on reset(),
|
|
39
|
+
* so the useAsyncData effect wouldn't re-fire.
|
|
40
|
+
*
|
|
41
|
+
* The fix adds a resetNonce to the functionMethods memo deps,
|
|
42
|
+
* so when the plugin is reset (table instance changes), the search
|
|
43
|
+
* function reference changes, triggering useAsyncData to re-fetch.
|
|
44
|
+
*
|
|
45
|
+
* This test verifies that when the search function reference changes
|
|
46
|
+
* (simulating reset()), the component re-fetches data even if
|
|
47
|
+
* props.data hasn't changed.
|
|
48
|
+
*/
|
|
49
|
+
it("should refetch data when search function reference changes", async () => {
|
|
50
|
+
const host = document.createElement("div");
|
|
51
|
+
const setValue = vi.fn();
|
|
52
|
+
|
|
53
|
+
// The initial page data string - identical for both renders.
|
|
54
|
+
// This simulates the case where only a row on page 2 changed,
|
|
55
|
+
// so the first page data is the same.
|
|
56
|
+
const initialPageData = JSON.stringify([
|
|
57
|
+
{ id: 1, status: "pending", value: 10 },
|
|
58
|
+
{ id: 2, status: "pending", value: 20 },
|
|
59
|
+
{ id: 3, status: "pending", value: 30 },
|
|
60
|
+
]);
|
|
61
|
+
|
|
62
|
+
const searchResult = {
|
|
63
|
+
data: [
|
|
64
|
+
{ id: 1, status: "pending", value: 10 },
|
|
65
|
+
{ id: 2, status: "pending", value: 20 },
|
|
66
|
+
{ id: 3, status: "pending", value: 30 },
|
|
67
|
+
],
|
|
68
|
+
total_rows: 4,
|
|
69
|
+
cell_styles: null,
|
|
70
|
+
cell_hover_texts: null,
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const searchFn1 = vi.fn().mockResolvedValue(searchResult);
|
|
74
|
+
const searchFn2 = vi.fn().mockResolvedValue(searchResult);
|
|
75
|
+
|
|
76
|
+
const fieldTypes: FieldTypesWithExternalType = [
|
|
77
|
+
["id", ["integer", "integer"]],
|
|
78
|
+
["status", ["string", "string"]],
|
|
79
|
+
["value", ["integer", "integer"]],
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
const commonProps = {
|
|
83
|
+
label: null,
|
|
84
|
+
totalRows: 4,
|
|
85
|
+
pagination: true,
|
|
86
|
+
pageSize: 3,
|
|
87
|
+
selection: "single" as const,
|
|
88
|
+
showDownload: false,
|
|
89
|
+
showFilters: false,
|
|
90
|
+
showColumnSummaries: false as const,
|
|
91
|
+
showDataTypes: false,
|
|
92
|
+
showPageSizeSelector: false,
|
|
93
|
+
showColumnExplorer: false,
|
|
94
|
+
showRowExplorer: false,
|
|
95
|
+
showChartBuilder: false,
|
|
96
|
+
rowHeaders: [] as FieldTypesWithExternalType,
|
|
97
|
+
fieldTypes,
|
|
98
|
+
totalColumns: 3,
|
|
99
|
+
maxColumns: "all" as const,
|
|
100
|
+
hasStableRowId: false,
|
|
101
|
+
lazy: false,
|
|
102
|
+
host,
|
|
103
|
+
enableSearch: true,
|
|
104
|
+
value: [] as (number | string | { rowId: string; columnName?: string })[],
|
|
105
|
+
setValue,
|
|
106
|
+
download_as: vi.fn() as DownloadAsArgs,
|
|
107
|
+
get_column_summaries: vi.fn().mockResolvedValue({
|
|
108
|
+
data: null,
|
|
109
|
+
stats: {},
|
|
110
|
+
bin_values: {},
|
|
111
|
+
value_counts: {},
|
|
112
|
+
show_charts: false,
|
|
113
|
+
}),
|
|
114
|
+
get_data_url: vi.fn() as GetDataUrl,
|
|
115
|
+
get_row_ids: vi.fn() as GetRowIds,
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const Wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
119
|
+
<Provider store={store}>
|
|
120
|
+
<TooltipProvider>{children}</TooltipProvider>
|
|
121
|
+
</Provider>
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
// Render with first search function
|
|
125
|
+
const { rerender } = render(
|
|
126
|
+
<Wrapper>
|
|
127
|
+
<LoadingDataTableComponent
|
|
128
|
+
{...commonProps}
|
|
129
|
+
data={initialPageData}
|
|
130
|
+
search={searchFn1}
|
|
131
|
+
/>
|
|
132
|
+
</Wrapper>,
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
// Wait for the table to render with data
|
|
136
|
+
await waitFor(() => {
|
|
137
|
+
expect(screen.getAllByRole("row").length).toBeGreaterThan(1);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// Search was called on initial load (fire-and-forget for canShowInitialPage)
|
|
141
|
+
expect(searchFn1).toHaveBeenCalled();
|
|
142
|
+
|
|
143
|
+
// Now rerender with the same data but a NEW search function reference.
|
|
144
|
+
// This simulates what happens after reset() when resetNonce increments
|
|
145
|
+
// and functionMethods is recreated.
|
|
146
|
+
await act(async () => {
|
|
147
|
+
rerender(
|
|
148
|
+
<Wrapper>
|
|
149
|
+
<LoadingDataTableComponent
|
|
150
|
+
{...commonProps}
|
|
151
|
+
data={initialPageData}
|
|
152
|
+
search={searchFn2}
|
|
153
|
+
/>
|
|
154
|
+
</Wrapper>,
|
|
155
|
+
);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// The new search function should be called because the search
|
|
159
|
+
// dependency changed in useAsyncData.
|
|
160
|
+
await waitFor(() => {
|
|
161
|
+
expect(searchFn2).toHaveBeenCalled();
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
});
|
|
@@ -8,6 +8,8 @@ import useEvent from "react-use-event-hook";
|
|
|
8
8
|
import { z } from "zod";
|
|
9
9
|
import { MarimoIncomingMessageEvent } from "@/core/dom/events";
|
|
10
10
|
import { asRemoteURL } from "@/core/runtime/config";
|
|
11
|
+
import { resolveVirtualFileURL } from "@/core/static/files";
|
|
12
|
+
import { isStaticNotebook } from "@/core/static/static-state";
|
|
11
13
|
import { useAsyncData } from "@/hooks/useAsyncData";
|
|
12
14
|
import { useDeepCompareMemoize } from "@/hooks/useDeepCompareMemoize";
|
|
13
15
|
import {
|
|
@@ -93,7 +95,11 @@ const AnyWidgetSlot = (
|
|
|
93
95
|
error,
|
|
94
96
|
refetch,
|
|
95
97
|
} = useAsyncData(async () => {
|
|
96
|
-
|
|
98
|
+
let url = asRemoteURL(jsUrl).toString();
|
|
99
|
+
// In static notebooks, resolve virtual files to blob URLs for import()
|
|
100
|
+
if (isStaticNotebook()) {
|
|
101
|
+
url = resolveVirtualFileURL(url);
|
|
102
|
+
}
|
|
97
103
|
return await import(/* @vite-ignore */ url);
|
|
98
104
|
// Re-render on jsHash change (which is a hash of the contents of the file)
|
|
99
105
|
// instead of a jsUrl change because URLs may change without the contents
|
|
@@ -20,7 +20,7 @@ describe("Blob serialization and deserialization", () => {
|
|
|
20
20
|
|
|
21
21
|
test("deserializeBlob should deserialize a base64 string to a Blob", async () => {
|
|
22
22
|
const serialized = await serializeBlob(testBlob);
|
|
23
|
-
const deserialized =
|
|
23
|
+
const deserialized = deserializeBlob(serialized);
|
|
24
24
|
expect(deserialized).toBeDefined();
|
|
25
25
|
expect(deserialized.size).toBe(testBlob.size);
|
|
26
26
|
expect(deserialized.type).toBe(testBlob.type);
|
|
@@ -28,7 +28,7 @@ describe("Blob serialization and deserialization", () => {
|
|
|
28
28
|
|
|
29
29
|
test("deserialized Blob should contain the original content", async () => {
|
|
30
30
|
const serialized = await serializeBlob(testBlob);
|
|
31
|
-
const deserialized =
|
|
31
|
+
const deserialized = deserializeBlob(serialized);
|
|
32
32
|
const reader = new FileReader();
|
|
33
33
|
// eslint-disable-next-line unicorn/prefer-blob-reading-methods
|
|
34
34
|
reader.readAsText(deserialized);
|
|
@@ -45,7 +45,7 @@ describe("Blob serialization and deserialization", () => {
|
|
|
45
45
|
type: "image/png",
|
|
46
46
|
});
|
|
47
47
|
const serialized = await serializeBlob(imageBlob);
|
|
48
|
-
const deserialized =
|
|
48
|
+
const deserialized = deserializeBlob(serialized);
|
|
49
49
|
expect(deserialized).toBeDefined();
|
|
50
50
|
expect(deserialized.size).toBe(imageBlob.size);
|
|
51
51
|
expect(deserialized.type).toBe(imageBlob.type);
|
|
@@ -132,7 +132,7 @@ describe("mime-types", () => {
|
|
|
132
132
|
|
|
133
133
|
describe("sortByPrecedence", () => {
|
|
134
134
|
it("should sort entries by precedence order", () => {
|
|
135
|
-
const entries:
|
|
135
|
+
const entries: [MimeType, string][] = [
|
|
136
136
|
["text/plain", "plain"],
|
|
137
137
|
["text/html", "html"],
|
|
138
138
|
["image/png", "png"],
|
|
@@ -151,7 +151,7 @@ describe("mime-types", () => {
|
|
|
151
151
|
});
|
|
152
152
|
|
|
153
153
|
it("should place unknown mime types at the end", () => {
|
|
154
|
-
const entries:
|
|
154
|
+
const entries: [MimeType, string][] = [
|
|
155
155
|
["text/plain", "plain"],
|
|
156
156
|
["text/html", "html"],
|
|
157
157
|
["application/json", "json"],
|
|
@@ -173,7 +173,7 @@ describe("mime-types", () => {
|
|
|
173
173
|
});
|
|
174
174
|
|
|
175
175
|
it("should handle empty precedence", () => {
|
|
176
|
-
const entries:
|
|
176
|
+
const entries: [MimeType, string][] = [
|
|
177
177
|
["text/plain", "plain"],
|
|
178
178
|
["text/html", "html"],
|
|
179
179
|
];
|
|
@@ -184,7 +184,7 @@ describe("mime-types", () => {
|
|
|
184
184
|
});
|
|
185
185
|
|
|
186
186
|
it("should not mutate original array", () => {
|
|
187
|
-
const entries:
|
|
187
|
+
const entries: [MimeType, string][] = [
|
|
188
188
|
["text/plain", "plain"],
|
|
189
189
|
["text/html", "html"],
|
|
190
190
|
];
|
|
@@ -198,7 +198,7 @@ describe("mime-types", () => {
|
|
|
198
198
|
|
|
199
199
|
describe("processMimeBundle", () => {
|
|
200
200
|
it("should filter and sort mime entries", () => {
|
|
201
|
-
const entries:
|
|
201
|
+
const entries: [MimeType, string][] = [
|
|
202
202
|
["text/plain", "plain"],
|
|
203
203
|
["text/html", "html"],
|
|
204
204
|
["image/png", "png"],
|
|
@@ -226,7 +226,7 @@ describe("mime-types", () => {
|
|
|
226
226
|
});
|
|
227
227
|
|
|
228
228
|
it("should use default config when not provided", () => {
|
|
229
|
-
const entries:
|
|
229
|
+
const entries: [MimeType, string][] = [
|
|
230
230
|
["text/html", "html"],
|
|
231
231
|
["image/png", "png"],
|
|
232
232
|
["text/markdown", "md"],
|
|
@@ -240,9 +240,7 @@ describe("mime-types", () => {
|
|
|
240
240
|
|
|
241
241
|
it("should preserve data associated with mime types", () => {
|
|
242
242
|
const htmlData = { content: "<h1>Hello</h1>" };
|
|
243
|
-
const entries:
|
|
244
|
-
["text/html", htmlData],
|
|
245
|
-
];
|
|
243
|
+
const entries: [MimeType, typeof htmlData][] = [["text/html", htmlData]];
|
|
246
244
|
|
|
247
245
|
const result = processMimeBundle(entries);
|
|
248
246
|
|
|
@@ -250,7 +248,7 @@ describe("mime-types", () => {
|
|
|
250
248
|
});
|
|
251
249
|
|
|
252
250
|
it("should sort by precedence after filtering", () => {
|
|
253
|
-
const entries:
|
|
251
|
+
const entries: [MimeType, string][] = [
|
|
254
252
|
["text/plain", "plain"],
|
|
255
253
|
["text/markdown", "md"],
|
|
256
254
|
["text/html", "html"],
|
|
@@ -76,4 +76,26 @@ describe("parseContent", () => {
|
|
|
76
76
|
url: "https://avatars.githubusercontent.com/u/123",
|
|
77
77
|
});
|
|
78
78
|
});
|
|
79
|
+
|
|
80
|
+
it("preserves newlines between URLs", () => {
|
|
81
|
+
const parts = parseContent("https://marimo.io\nhttps://github.com\n");
|
|
82
|
+
expect(parts).toEqual([
|
|
83
|
+
{ type: "url", url: "https://marimo.io" },
|
|
84
|
+
{ type: "text", value: "\n" },
|
|
85
|
+
{ type: "url", url: "https://github.com" },
|
|
86
|
+
{ type: "text", value: "\n" },
|
|
87
|
+
]);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("preserves whitespace in mixed content", () => {
|
|
91
|
+
const parts = parseContent(
|
|
92
|
+
"Line 1: https://marimo.io\nLine 2: https://github.com",
|
|
93
|
+
);
|
|
94
|
+
expect(parts).toEqual([
|
|
95
|
+
{ type: "text", value: "Line 1: " },
|
|
96
|
+
{ type: "url", url: "https://marimo.io" },
|
|
97
|
+
{ type: "text", value: "\nLine 2: " },
|
|
98
|
+
{ type: "url", url: "https://github.com" },
|
|
99
|
+
]);
|
|
100
|
+
});
|
|
79
101
|
});
|
package/src/utils/blob.ts
CHANGED
|
@@ -14,32 +14,19 @@ export function serializeBlob<T>(blob: Blob): Promise<DataURLString> {
|
|
|
14
14
|
});
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
export function deserializeBlob(serializedBlob: DataURLString):
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
for (let i = 0; i < len; i++) {
|
|
29
|
-
bytes[i] = binaryString.charCodeAt(i);
|
|
30
|
-
}
|
|
31
|
-
// Create a new Blob from the array buffer
|
|
32
|
-
const blob = new Blob([bytes], { type: mimeType });
|
|
33
|
-
resolve(blob);
|
|
34
|
-
} catch (error) {
|
|
35
|
-
reject(ensureError(error));
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function ensureError(error: unknown): Error {
|
|
41
|
-
if (error instanceof Error) {
|
|
42
|
-
return error;
|
|
17
|
+
export function deserializeBlob(serializedBlob: DataURLString): Blob {
|
|
18
|
+
// Extract the base64 data from the data URL
|
|
19
|
+
const [prefix, base64Data] = serializedBlob.split(",", 2);
|
|
20
|
+
const mimeType = /^data:(.+);base64$/.exec(prefix)?.[1];
|
|
21
|
+
// Decode the base64 string
|
|
22
|
+
const binaryString = atob(base64Data);
|
|
23
|
+
// Convert the binary string to an array buffer
|
|
24
|
+
const len = binaryString.length;
|
|
25
|
+
const bytes = new Uint8Array(len);
|
|
26
|
+
for (let i = 0; i < len; i++) {
|
|
27
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
43
28
|
}
|
|
44
|
-
|
|
29
|
+
// Create a new Blob from the array buffer
|
|
30
|
+
const blob = new Blob([bytes], { type: mimeType });
|
|
31
|
+
return blob;
|
|
45
32
|
}
|