@marimo-team/islands 0.25.3-dev14 → 0.25.3-dev140
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/{ConnectedDataExplorerComponent-CNMliVzw.js → ConnectedDataExplorerComponent-BAiSVaHj.js} +17 -15
- package/dist/{ErrorBoundary-S_edj6VQ.js → ErrorBoundary-CTJ1U1fx.js} +16 -16
- package/dist/{abnfDiagram-VCTEODGH-D-Ez91gV.js → abnfDiagram-VCTEODGH-BBHN92-F.js} +2 -2
- package/dist/any-language-editor-3E24SnCV.js +75 -0
- package/dist/{architectureDiagram-5GKGNRK7-CTpcNtO8.js → architectureDiagram-5GKGNRK7-C0Rd64qh.js} +1 -1
- package/dist/{arrays-CN1djC4k.js → arrays-R6JsImiT.js} +8 -0
- package/dist/assets/__vite-browser-external-BgQNn4dS.js +1 -0
- package/dist/assets/worker-FKe3RHT4.js +113 -0
- package/dist/{button-CvjkwdSk.js → button-BQRdqQi1.js} +199 -192
- package/dist/{capabilities-CQ5IjhzL.js → capabilities-CI4xUPsp.js} +2 -2
- package/dist/{chat-ui-DaHynk8g.js → chat-ui-CvB8Map3.js} +1142 -1141
- package/dist/{check-EDwSwP89.js → check-SpOMoCLP.js} +1 -1
- package/dist/{chunk-IMKFNOWR-DGxi-zeo.js → chunk-IMKFNOWR-hJXey5p2.js} +1 -1
- package/dist/{chunk-SHT3W25Y-CV9J3rAB.js → chunk-SHT3W25Y-JMzTmmVW.js} +1 -1
- package/dist/{chunk-SVP7TREG-BL-9Fqgg.js → chunk-SVP7TREG-BIyoHTXi.js} +1 -1
- package/dist/{chunk-TICWLB2K-Cu_XMjDM.js → chunk-TICWLB2K-CCzH2Uwa.js} +1 -1
- package/dist/{chunk-YOKDWASO-DPZk8vhS.js → chunk-YOKDWASO-DKplTrBY.js} +20 -20
- package/dist/{classDiagram-ZZMXUADV-BXXV13Hq.js → classDiagram-ZZMXUADV-DB1kOPS6.js} +2 -2
- package/dist/{classDiagram-v2-VYDZK3BY-B7FXJ1Cp.js → classDiagram-v2-VYDZK3BY-CPaFL0OB.js} +2 -2
- package/dist/{code-visibility-BBdICLTP.js → code-visibility-C1jZfwPW.js} +6 -6
- package/dist/{common-7r9HNiTn.js → common-CNv_7gvI.js} +6766 -6590
- package/dist/{copy-icon-Ft7t9bqz.js → copy-icon-C1trdvVt.js} +3 -3
- package/dist/{cynefinDiagram-5FMLGOSQ-CH96rwba.js → cynefinDiagram-5FMLGOSQ-CF93uliH.js} +1 -1
- package/dist/{dagre-GXQ25YYZ-BD3PmpFY.js → dagre-GXQ25YYZ-BBB-_p9D.js} +1 -1
- package/dist/{data-grid-overlay-editor-Bz1FTAfW.js → data-grid-overlay-editor-t0t17DPI.js} +5 -5
- package/dist/{data-grid-types-BH_r7g7l.js → data-grid-types-0PJ-9N5n.js} +1 -1
- package/dist/{diagram-S7CK7UJ4-BsSJJldB.js → diagram-S7CK7UJ4-BynCnbyt.js} +1 -1
- package/dist/{diagram-UQ7AKVKN-muQtNZc4.js → diagram-UQ7AKVKN-svOxikMa.js} +1 -1
- package/dist/{diagram-VX7I27RA-CdhuZAqR.js → diagram-VX7I27RA-BVQSnPx0.js} +1 -1
- package/dist/{diagram-Z3DM3KII-BD8mXjOj.js → diagram-Z3DM3KII-nR1J0BH5.js} +1 -1
- package/dist/{dialog-CEBFZNyc.js → dialog-DgeqyrEZ.js} +3 -3
- package/dist/{dist-DpBpA_n1.js → dist-Lm-D-Vr6.js} +3 -3
- package/dist/{dropdown-menu-CP_qCYHC.js → dropdown-menu-GWcGCcxE.js} +1277 -1229
- package/dist/{ebnfDiagram-PWID7BFC-BIN1sweE.js → ebnfDiagram-PWID7BFC-C9tq9-Qo.js} +2 -2
- package/dist/{erDiagram-RLTQ6QDP-Ctp5cfoN.js → erDiagram-RLTQ6QDP-BiaeB7Fi.js} +1 -1
- package/dist/{error-banner-D-WCIiMt.js → error-banner-D40pmfQQ.js} +83 -83
- package/dist/{error-entries-Bv6bZPF3.js → error-entries-CmFDQ9CA.js} +1018 -977
- package/dist/{feedback-button-BAheGhdl.js → feedback-button-JzaiyZZm.js} +23 -23
- package/dist/{flowDiagram-HODETNUW-3EfLrlFb.js → flowDiagram-HODETNUW-f-sLm87k.js} +2 -2
- package/dist/{formats-27CW_UP4.js → formats-CCxaLPNs.js} +3 -3
- package/dist/{ganttDiagram-EL5Y4UJY-BQT16JSc.js → ganttDiagram-EL5Y4UJY-Dh_4cBJZ.js} +2 -2
- package/dist/{glide-data-editor-CAvrTnyB.js → glide-data-editor-B3eq-1SC.js} +370 -368
- package/dist/{glide-utils-DJyqh0EM.js → glide-utils-Dz_F4pAY.js} +16 -16
- package/dist/{globals-C03jB5fY.js → globals-BW8TOLYQ.js} +3 -3
- package/dist/{highlighted-body-KPVGNVTW-zwjq21tf.js → highlighted-body-KPVGNVTW-3Ziwfz-V.js} +2 -2
- package/dist/{html-to-image-Bf8QMGi4.js → html-to-image-BrC8OJcj.js} +632 -595
- package/dist/{infoDiagram-27XIBGKW-DipQoHxl.js → infoDiagram-27XIBGKW-BHrWywBe.js} +1 -1
- package/dist/{ishikawaDiagram-5VMMS53U-DgFMjSGz.js → ishikawaDiagram-5VMMS53U-Da4s0Reg.js} +1 -1
- package/dist/{kanban-definition-UXKFOSKX-w1ACVjgl.js → kanban-definition-UXKFOSKX-DDf1GVLj.js} +1 -1
- package/dist/{label-BDKF3Afc.js → label-D9_-zSg-.js} +3 -3
- package/dist/{linear-Cnaq3Tfb.js → linear-DWERh7eI.js} +121 -73
- package/dist/{loader-BQOfjTIJ.js → loader-DOVB0Syl.js} +12 -12
- package/dist/main.js +7551 -7231
- package/dist/{mermaid-CHo49Yv-.js → mermaid-ClwTt7OD.js} +38 -38
- package/dist/mermaid-HWGCJPDP-Du3-HPQA.js +2 -0
- package/dist/{mindmap-definition-YA3MSWOX-CrT0KSfX.js → mindmap-definition-YA3MSWOX-D60oMZtT.js} +1 -1
- package/dist/{number-overlay-editor-DRF7iVLO.js → number-overlay-editor-DoUnDbPE.js} +3 -3
- package/dist/{objects-CzUgoMIF.js → objects-DCmMvZtn.js} +1 -3
- package/dist/{once-BtE-wKRL.js → once-eG8Vmvjd.js} +1 -1
- package/dist/{pegDiagram-XKGWAZYB-BqvywNJA.js → pegDiagram-XKGWAZYB-B9wFpMga.js} +2 -2
- package/dist/{pieDiagram-E7YTZNPT-BfxJ_XCc.js → pieDiagram-E7YTZNPT-BZwvMuBT.js} +1 -1
- package/dist/precisionRound-DhAzeRcm.js +49 -0
- package/dist/{process-output-BZg56CCb.js → process-output-Bw-vzyoy.js} +5 -5
- package/dist/{quadrantDiagram-AXDQQJYC-Ccw0xMjN.js → quadrantDiagram-AXDQQJYC-Blrm7V7_.js} +1 -1
- package/dist/{railroadDiagram-O6MQD6OU-8vFLVTPu.js → railroadDiagram-O6MQD6OU-DqhZGsN2.js} +2 -2
- package/dist/{react-vega-BED0DjiB.js → react-vega-C4T9Rb_w.js} +12284 -12270
- package/dist/{requirementDiagram-BXWQKSXE-a_MxGvxe.js → requirementDiagram-BXWQKSXE-CL5dYARV.js} +1 -1
- package/dist/{reveal-component-QSvE6jwa.js → reveal-component-ClNn1CDl.js} +14 -14
- package/dist/{select-BButFi-d.js → select-CkF_GBxO.js} +537 -458
- package/dist/{sequenceDiagram-WJ2MYXX4-CXFMnxGh.js → sequenceDiagram-WJ2MYXX4-BSUCRd2x.js} +1 -1
- package/dist/{spec-BwarBpUJ.js → spec-DiSh_qJC.js} +41 -11
- package/dist/{stateDiagram-D77RDMKH-DQeA_CwI.js → stateDiagram-D77RDMKH-B-1Hvyb0.js} +2 -2
- package/dist/{stateDiagram-v2-MP3YSRHH-BxxQucm1.js → stateDiagram-v2-MP3YSRHH--HtiR4yB.js} +2 -2
- package/dist/style.css +1 -1
- package/dist/{swimlanes-42K2YHIH-Do6L-cYg.js → swimlanes-42K2YHIH-Bdfhrd-2.js} +1 -1
- package/dist/{swimlanesDiagram-VR7AAH4N-DUzPvSVU.js → swimlanesDiagram-VR7AAH4N-BJIfxSz0.js} +2 -2
- package/dist/{swiper-component-DKntyn8p.js → swiper-component-BkCm-kLg.js} +2 -2
- package/dist/{time-BieKwdYS.js → time-BaMbJs4z.js} +18 -18
- package/dist/{timeline-definition-24CTP7MA-D3c46WM5.js → timeline-definition-24CTP7MA-Cs4EfFD3.js} +1 -1
- package/dist/{toDate-UtiwmnH2.js → toDate-DGvRHwTk.js} +19 -18
- package/dist/{toast-B9UMEi8u.js → toast-Btbve3Lm.js} +231 -213
- package/dist/{tooltip-BO1REbqs.js → tooltip-CJ4Rqasp.js} +327 -303
- package/dist/{use-restore-focus-BEM7sFWl.js → use-restore-focus-CmGwRNId.js} +1482 -1433
- package/dist/{useAsyncData-Deh5kszT.js → useAsyncData-BmVgnMP5.js} +15 -14
- package/dist/{useDateFormatter-D8XruNGG.js → useDateFormatter-eCL_SdeG.js} +2 -2
- package/dist/{useDeepCompareMemoize-DXwzRINI.js → useDeepCompareMemoize-Ch_AeGu6.js} +1 -1
- package/dist/{useIframeCapabilities-DBETvvPl.js → useIframeCapabilities-CSdWn6NP.js} +1 -1
- package/dist/{useLifecycle-j7D9uD1o.js → useLifecycle-B5BX921H.js} +3 -3
- package/dist/{useTheme-CRnjkku0.js → useTheme-Bq9pi_vz.js} +6 -6
- package/dist/{vega-component-BeF9dfyO.js → vega-component-BgyZ-COt.js} +147 -147
- package/dist/{vega-embed-container-BNHE18G9.js → vega-embed-container--LYZqguO.js} +2 -2
- package/dist/{vega-loader.browser-DNST6uVf.js → vega-loader.browser-B9Pz0EGP.js} +298 -507
- package/dist/{vennDiagram-4TSXK5OY-BSZD8JcZ.js → vennDiagram-4TSXK5OY-DRu0vRjO.js} +1 -1
- package/dist/{wardleyDiagram-VM6X3IG4-48cUWkfA.js → wardleyDiagram-VM6X3IG4-DzbDxwDY.js} +1 -1
- package/dist/{xychartDiagram-S5SC5T6Z-CswYgYeu.js → xychartDiagram-S5SC5T6Z-CLWj6ZFQ.js} +2 -2
- package/package.json +14 -14
- package/src/__tests__/host-imports.test.ts +1 -0
- package/src/__tests__/module-entry.test.ts +32 -0
- package/src/__tests__/mount.test.ts +0 -20
- package/src/components/ai/__tests__/ai-model-dropdown.test.tsx +63 -0
- package/src/components/ai/ai-model-dropdown.tsx +11 -6
- package/src/components/app-config/ai-config.tsx +7 -2
- package/src/components/chat/__tests__/message-queue.test.tsx +17 -0
- package/src/components/chat/acp/__tests__/session-tabs.test.tsx +43 -0
- package/src/components/chat/acp/__tests__/state.test.ts +2 -2
- package/src/components/chat/acp/blocks.tsx +3 -2
- package/src/components/chat/acp/session-tabs.tsx +8 -3
- package/src/components/chat/chat-components.tsx +2 -0
- package/src/components/chat/chat-panel.tsx +9 -8
- package/src/components/chat/chat-utils.ts +4 -8
- package/src/components/data-table/TableTopBar.tsx +2 -0
- package/src/components/data-table/__tests__/chart-spec-model.test.ts +195 -195
- package/src/components/data-table/__tests__/column-explorer.test.tsx +27 -1
- package/src/components/data-table/__tests__/columns.test.tsx +105 -0
- package/src/components/data-table/__tests__/context-menu-interaction.test.tsx +130 -0
- package/src/components/data-table/__tests__/data-table.test.tsx +1 -0
- package/src/components/data-table/__tests__/date-popover.test.tsx +72 -0
- package/src/components/data-table/__tests__/export-actions.test.tsx +105 -0
- package/src/components/data-table/__tests__/keyboard-selection.test.tsx +188 -0
- package/src/components/data-table/__tests__/search-highlight.test.tsx +109 -0
- package/src/components/data-table/__tests__/url-detector.test.tsx +74 -0
- package/src/components/data-table/cell-selection/feature.ts +2 -2
- package/src/components/data-table/cell-selection/types.ts +2 -2
- package/src/components/data-table/charts/__tests__/chart-panel.test.tsx +74 -1
- package/src/components/data-table/charts/__tests__/spec-snapshot.test.ts +20 -20
- package/src/components/data-table/charts/__tests__/spec.test.ts +78 -52
- package/src/components/data-table/charts/__tests__/storage.test.ts +4 -1
- package/src/components/data-table/charts/chart-spec/spec.ts +68 -43
- package/src/components/data-table/charts/charts.tsx +40 -30
- package/src/components/data-table/charts/storage.ts +16 -5
- package/src/components/data-table/column-explorer-panel/column-explorer.tsx +1 -1
- package/src/components/data-table/column-summary/chart-spec-model.tsx +23 -16
- package/src/components/data-table/column-summary/column-summary.tsx +2 -4
- package/src/components/data-table/column-visibility-dropdown.tsx +8 -13
- package/src/components/data-table/columns.tsx +45 -46
- package/src/components/data-table/context-menu.tsx +36 -20
- package/src/components/data-table/data-table.tsx +13 -1
- package/src/components/data-table/date-popover.tsx +6 -4
- package/src/components/data-table/export-actions.tsx +76 -48
- package/src/components/data-table/range-focus/atoms.ts +4 -6
- package/src/components/data-table/range-focus/use-cell-range-selection.ts +65 -38
- package/src/components/data-table/range-focus/use-scroll-into-view.ts +21 -2
- package/src/components/data-table/renderers.tsx +16 -7
- package/src/components/data-table/row-viewer-panel/__tests__/row-viewer.test.tsx +24 -1
- package/src/components/data-table/row-viewer-panel/row-viewer.tsx +4 -4
- package/src/components/data-table/search-highlight.tsx +79 -0
- package/src/components/data-table/types.ts +1 -0
- package/src/components/data-table/url-detector.tsx +49 -9
- package/src/components/datasources/__tests__/column-actions.test.tsx +97 -0
- package/src/components/datasources/__tests__/column-preview.test.tsx +98 -5
- package/src/components/datasources/__tests__/datasources-interactions.test.tsx +181 -0
- package/src/components/datasources/__tests__/table-list.test.tsx +118 -0
- package/src/components/datasources/__tests__/utils.test.ts +57 -6
- package/src/components/datasources/column-preview.tsx +13 -4
- package/src/components/datasources/datasources.tsx +51 -39
- package/src/components/datasources/utils.ts +32 -23
- package/src/components/debugger/debugger-code.tsx +5 -0
- package/src/components/dependency-graph/dependency-graph-tree.tsx +8 -8
- package/src/components/dependency-graph/elements.ts +24 -21
- package/src/components/dependency-graph/panels.tsx +2 -1
- package/src/components/dependency-graph/utils/__tests__/layout.test.ts +18 -11
- package/src/components/editor/SortableCell.tsx +1 -3
- package/src/components/editor/__tests__/dynamic-favicon.test.tsx +111 -18
- package/src/components/editor/__tests__/package-alert.test.tsx +22 -1
- package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +24 -0
- package/src/components/editor/actions/pair-with-agent-modal.tsx +18 -15
- package/src/components/editor/ai/add-cell-with-ai.tsx +3 -3
- package/src/components/editor/ai/ai-completion-editor.tsx +1 -0
- package/src/components/editor/alerts/startup-logs-alert.tsx +1 -0
- package/src/components/editor/cell/CreateCellButton.tsx +8 -10
- package/src/components/editor/cell/DeleteButton.tsx +1 -0
- package/src/components/editor/cell/__tests__/useShouldShowInterrupt.test.ts +39 -0
- package/src/components/editor/cell/code/__tests__/language-toggle.test.tsx +54 -0
- package/src/components/editor/cell/code/cell-editor.tsx +1 -1
- package/src/components/editor/cell/code/language-toggle.tsx +8 -7
- package/src/components/editor/cell/collapse.tsx +5 -3
- package/src/components/editor/cell/useShouldShowInterrupt.ts +2 -12
- package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +30 -1
- package/src/components/editor/chrome/panels/cache-panel.tsx +1 -0
- package/src/components/editor/chrome/panels/outline/__tests__/outline-keyboard.test.tsx +84 -0
- package/src/components/editor/chrome/panels/outline/floating-outline.tsx +39 -14
- package/src/components/editor/chrome/panels/outline/useActiveOutline.tsx +3 -2
- package/src/components/editor/chrome/panels/packages-panel.tsx +4 -7
- package/src/components/editor/chrome/panels/secrets-panel.tsx +1 -0
- package/src/components/editor/chrome/panels/snippets-panel.tsx +5 -1
- package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +12 -18
- package/src/components/editor/chrome/wrapper/__tests__/pending-ai-cells.test.tsx +58 -1
- package/src/components/editor/chrome/wrapper/footer-items/runtime-settings.tsx +1 -7
- package/src/components/editor/chrome/wrapper/pending-ai-cells.tsx +12 -2
- package/src/components/editor/chrome/wrapper/storage.ts +1 -1
- package/src/components/editor/connections/components.tsx +1 -1
- package/src/components/editor/controls/__tests__/command-palette.test.tsx +53 -0
- package/src/components/editor/controls/__tests__/keyboard-shortcuts.test.tsx +61 -0
- package/src/components/editor/controls/command-palette.tsx +22 -10
- package/src/components/editor/controls/keyboard-shortcuts.tsx +14 -5
- package/src/components/editor/dynamic-favicon.tsx +21 -40
- package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +12 -23
- package/src/components/editor/file-tree/__tests__/file-explorer-interactions.test.tsx +6 -0
- package/src/components/editor/file-tree/__tests__/file-explorer-navigation.test.tsx +47 -0
- package/src/components/editor/file-tree/__tests__/use-file-search.test.tsx +91 -0
- package/src/components/editor/file-tree/file-explorer.tsx +45 -8
- package/src/components/editor/file-tree/file-name-input.tsx +1 -0
- package/src/components/editor/file-tree/file-viewer.tsx +4 -2
- package/src/components/editor/file-tree/use-file-search.ts +35 -21
- package/src/components/editor/header/status.tsx +28 -5
- package/src/components/editor/links/__tests__/cell-link.test.tsx +146 -0
- package/src/components/editor/links/cell-link.tsx +14 -5
- package/src/components/editor/navigation/focus-utils.ts +4 -4
- package/src/components/editor/notebook-banner.tsx +1 -0
- package/src/components/editor/notebook-cell.tsx +11 -2
- package/src/components/editor/output/MarimoTracebackOutput.tsx +19 -17
- package/src/components/editor/output/__tests__/traceback.test.tsx +27 -1
- package/src/components/editor/output/ansi-reduce.ts +33 -20
- package/src/components/editor/output/console/ConsoleOutput.tsx +19 -25
- package/src/components/editor/package-alert.tsx +14 -7
- package/src/components/editor/renderers/grid-layout/grid-layout.tsx +12 -6
- package/src/components/editor/renderers/vertical-layout/sidebar/toggle.tsx +7 -1
- package/src/components/home/components.tsx +17 -31
- package/src/components/icons/__tests__/large-spinner.test.tsx +39 -0
- package/src/components/icons/large-spinner.tsx +3 -14
- package/src/components/icons/spinner.tsx +1 -1
- package/src/components/mcp/mcp-status-indicator.tsx +1 -1
- package/src/components/pages/__tests__/home-page.test.tsx +116 -0
- package/src/components/pages/home-page.tsx +164 -91
- package/src/components/scratchpad/scratchpad.test.tsx +95 -0
- package/src/components/scratchpad/scratchpad.tsx +16 -3
- package/src/components/slides/reveal-component.tsx +4 -2
- package/src/components/slides/slide-cell-view.tsx +1 -1
- package/src/components/slides/slide-form.tsx +1 -0
- package/src/components/tracing/tracing-keyboard.test.tsx +85 -0
- package/src/components/tracing/tracing.tsx +45 -41
- package/src/components/ui/__tests__/draggable-popover.test.tsx +24 -0
- package/src/components/ui/__tests__/range-slider.test.tsx +165 -0
- package/src/components/ui/alert-dialog.tsx +1 -1
- package/src/components/ui/draggable-popover.tsx +24 -2
- package/src/components/ui/range-slider.tsx +22 -9
- package/src/core/ai/context/providers/datasource.ts +6 -2
- package/src/core/ai/tools/base.ts +16 -7
- package/src/core/ai/tools/edit-notebook-tool.ts +47 -45
- package/src/core/ai/tools/registry.ts +13 -8
- package/src/core/ai/tools/sample-tool.ts +6 -7
- package/src/core/attachments/state.ts +5 -8
- package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +4 -0
- package/src/core/cells/__tests__/cell.test.ts +39 -0
- package/src/core/cells/__tests__/cells.test.ts +3 -1
- package/src/core/cells/__tests__/collapseConsoleOutputs.test.ts +54 -0
- package/src/core/cells/__tests__/notebook-outline.test.ts +113 -0
- package/src/core/cells/__tests__/session.test.ts +117 -36
- package/src/core/cells/cell.ts +14 -3
- package/src/core/cells/cells.ts +3 -1
- package/src/core/cells/pending-delete-service.ts +2 -6
- package/src/core/cells/session.ts +1 -1
- package/src/core/cells/types.ts +10 -1
- package/src/core/codemirror/__tests__/setup.test.ts +29 -29
- package/src/core/codemirror/cells/__tests__/extensions.test.ts +15 -0
- package/src/core/codemirror/cells/extensions.ts +7 -3
- package/src/core/codemirror/keymaps/vim.ts +2 -6
- package/src/core/codemirror/language/__tests__/sql.test.ts +22 -20
- package/src/core/codemirror/language/extension.ts +8 -8
- package/src/core/codemirror/language/languages/markdown.ts +8 -6
- package/src/core/codemirror/language/languages/python.ts +16 -10
- package/src/core/codemirror/language/languages/sql/sql.ts +9 -7
- package/src/core/codemirror/language/panel/panel.tsx +1 -0
- package/src/core/codemirror/language/types.ts +7 -7
- package/src/core/codemirror/rtc/extension.ts +24 -12
- package/src/core/codemirror/rtc/loro/awareness.ts +34 -19
- package/src/core/codemirror/rtc/loro/sync.ts +1 -0
- package/src/core/codemirror/utils.ts +1 -0
- package/src/core/datasets/__tests__/data-source-discovery.test.ts +4 -4
- package/src/core/datasets/data-source-discovery.ts +4 -6
- package/src/core/document/__tests__/codemirror.test.ts +485 -0
- package/src/core/document/__tests__/draft.test.ts +322 -0
- package/src/core/document/__tests__/fixtures.ts +92 -0
- package/src/core/document/__tests__/hooks.test.tsx +386 -0
- package/src/core/document/__tests__/lifecycle-scenarios.test.ts +61 -0
- package/src/core/document/__tests__/model.test.ts +191 -0
- package/src/core/document/__tests__/protocol-fixtures.ts +38 -0
- package/src/core/document/__tests__/recovery-scenarios.test.ts +230 -0
- package/src/core/document/__tests__/scenario-fixture.ts +43 -0
- package/src/core/document/__tests__/scenarios.test.ts +205 -0
- package/src/core/document/__tests__/state.test.ts +438 -0
- package/src/core/document/__tests__/submission.test.ts +203 -0
- package/src/core/document/__tests__/test-host.test.ts +92 -0
- package/src/core/document/__tests__/test-host.ts +247 -0
- package/src/core/document/__tests__/test-notebook.ts +149 -0
- package/src/core/document/__tests__/user.ts +61 -0
- package/src/core/document/codemirror.ts +311 -0
- package/src/core/document/draft.ts +472 -0
- package/src/core/document/hooks.tsx +118 -0
- package/src/core/document/index.ts +15 -0
- package/src/core/document/model.ts +55 -0
- package/src/core/document/select.ts +41 -0
- package/src/core/document/state.ts +374 -0
- package/src/core/document/sync.ts +369 -0
- package/src/core/host/AGENTS.md +8 -2
- package/src/core/host/__tests__/attachments.test.ts +56 -0
- package/src/core/host/__tests__/document.test.ts +126 -0
- package/src/core/host/__tests__/fixtures.ts +71 -0
- package/src/core/host/__tests__/replica.test.ts +550 -0
- package/src/core/host/__tests__/static.test.ts +169 -0
- package/src/core/host/__tests__/wasm.test.ts +163 -0
- package/src/core/host/backend.ts +19 -0
- package/src/core/host/connection.ts +1 -40
- package/src/core/host/index.ts +14 -0
- package/src/core/host/replica.ts +286 -0
- package/src/core/host/resources/attachments.ts +22 -0
- package/src/core/host/resources/document.ts +101 -0
- package/src/core/host/static.ts +72 -0
- package/src/core/host/wasm.ts +107 -0
- package/src/core/hotkeys/actions.ts +2 -4
- package/src/core/islands/__tests__/bootstrap.test.ts +16 -42
- package/src/core/islands/__tests__/bridge.test.ts +31 -26
- package/src/core/islands/bootstrap.ts +22 -52
- package/src/core/islands/bridge.ts +31 -20
- package/src/core/islands/components/__tests__/IslandControls.test.tsx +7 -5
- package/src/core/islands/worker/__tests__/controller.test.ts +62 -4
- package/src/core/islands/worker/controller.ts +4 -10
- package/src/core/islands/worker/worker.tsx +33 -33
- package/src/core/kernel/__tests__/RuntimeState.test.ts +1 -1
- package/src/core/network/connection.ts +4 -2
- package/src/core/network/useConnectionNotice.ts +8 -2
- package/src/core/runtime/__tests__/createWsUrl.test.ts +77 -7
- package/src/core/runtime/__tests__/runtime.test.ts +32 -58
- package/src/core/runtime/runtime.ts +38 -42
- package/src/core/runtime/types.ts +0 -2
- package/src/core/state/__tests__/jotai.test.tsx +108 -0
- package/src/core/state/jotai.ts +1 -1
- package/src/core/static/__tests__/files.test.ts +38 -10
- package/src/core/static/files.ts +2 -0
- package/src/core/wasm/__tests__/bridge.test.ts +42 -1
- package/src/core/wasm/__tests__/worker.test.ts +182 -0
- package/src/core/wasm/bridge.ts +40 -21
- package/src/core/wasm/rpc.ts +0 -1
- package/src/core/wasm/store.ts +3 -3
- package/src/core/wasm/worker/bootstrap.ts +55 -51
- package/src/core/wasm/worker/getMarimoWheel.ts +1 -1
- package/src/core/wasm/worker/types.ts +23 -14
- package/src/core/wasm/worker/worker.ts +77 -36
- package/src/core/websocket/__tests__/backend.test.ts +228 -0
- package/src/core/websocket/__tests__/connection-state.test.ts +0 -12
- package/src/core/websocket/__tests__/useNotebookReplica.hook.test.tsx +141 -0
- package/src/core/websocket/__tests__/useNotebookSession.hook.test.tsx +154 -428
- package/src/core/websocket/backend.ts +130 -0
- package/src/core/websocket/connection-state.ts +1 -11
- package/src/core/websocket/state.ts +7 -0
- package/src/core/websocket/transports/__tests__/notebook.test.ts +405 -0
- package/src/core/websocket/transports/notebook.ts +159 -0
- package/src/core/websocket/types.ts +2 -2
- package/src/core/websocket/useNotebookReplica.ts +69 -0
- package/src/core/websocket/useNotebookSession.tsx +18 -25
- package/src/css/globals.css +5 -0
- package/src/hooks/__tests__/useAsyncData.test.tsx +148 -0
- package/src/hooks/__tests__/useDebounce.test.tsx +51 -0
- package/src/hooks/__tests__/useDeepCompareMemoize.test.tsx +39 -0
- package/src/hooks/__tests__/useDelayElapsed.test.tsx +28 -0
- package/src/hooks/__tests__/useInternalStateWithSync.test.tsx +20 -0
- package/src/hooks/__tests__/useScript.test.tsx +77 -0
- package/src/hooks/__tests__/useStateForKey.test.tsx +257 -0
- package/src/hooks/useAsyncData.ts +3 -3
- package/src/hooks/useDataSourceDiscovery.ts +6 -4
- package/src/hooks/useDebounce.ts +6 -10
- package/src/hooks/useDeepCompareMemoize.ts +1 -1
- package/src/hooks/useDelayElapsed.ts +14 -10
- package/src/hooks/useElapsedTime.ts +3 -3
- package/src/hooks/useEventListener.ts +1 -2
- package/src/hooks/useInputHistory.ts +4 -7
- package/src/hooks/useInternalStateWithSync.ts +6 -6
- package/src/hooks/useResizeObserver.ts +4 -7
- package/src/hooks/useScript.ts +67 -70
- package/src/hooks/useStateForKey.ts +93 -0
- package/src/mount.tsx +0 -4
- package/src/plugins/__tests__/plugin-object-schemas.test.ts +104 -0
- package/src/plugins/core/registerReactComponent.tsx +1 -1
- package/src/plugins/impl/CodeEditorPlugin.tsx +1 -1
- package/src/plugins/impl/DataEditorPlugin.tsx +1 -1
- package/src/plugins/impl/DataTablePlugin.tsx +29 -33
- package/src/plugins/impl/DropdownPlugin.tsx +2 -6
- package/src/plugins/impl/FileBrowserPlugin.tsx +4 -16
- package/src/plugins/impl/FormPlugin.tsx +1 -1
- package/src/plugins/impl/RangeSliderPlugin.tsx +16 -2
- package/src/plugins/impl/RefreshPlugin.tsx +1 -0
- package/src/plugins/impl/SliderPlugin.tsx +6 -8
- package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +117 -1
- package/src/plugins/impl/__tests__/FileBrowserPlugin.test.tsx +45 -0
- package/src/plugins/impl/__tests__/RangeSliderPlugin.test.tsx +109 -0
- package/src/plugins/impl/__tests__/SliderPlugin.test.tsx +24 -3
- package/src/plugins/impl/anywidget/__tests__/registry.test.ts +2 -0
- package/src/plugins/impl/anywidget/__tests__/widget-binding.test.ts +241 -2
- package/src/plugins/impl/anywidget/runtime.ts +21 -9
- package/src/plugins/impl/anywidget/widget-binding.ts +160 -82
- package/src/plugins/impl/chat/__tests__/chat-prompts.test.tsx +93 -0
- package/src/plugins/impl/chat/chat-ui.tsx +14 -11
- package/src/plugins/impl/code/any-language-editor.tsx +2 -2
- package/src/plugins/impl/data-editor/glide-data-editor.tsx +8 -5
- package/src/plugins/impl/data-editor/glide-utils.ts +3 -1
- package/src/plugins/impl/data-explorer/ConnectedDataExplorerComponent.tsx +1 -1
- package/src/plugins/impl/data-explorer/components/column-summary.tsx +4 -2
- package/src/plugins/impl/data-explorer/state/reducer.ts +1 -1
- package/src/plugins/impl/data-frames/DataFramePlugin.tsx +1 -2
- package/src/plugins/impl/data-frames/__tests__/sidebar.test.tsx +160 -0
- package/src/plugins/impl/data-frames/forms/__tests__/__snapshots__/form.test.tsx.snap +66 -66
- package/src/plugins/impl/data-frames/panel.tsx +43 -16
- package/src/plugins/impl/mpl-interactive/MplInteractivePlugin.tsx +5 -3
- package/src/plugins/impl/panel/PanelPlugin.tsx +16 -17
- package/src/plugins/impl/panel/__tests__/utils.test.ts +39 -1
- package/src/plugins/impl/panel/utils.ts +17 -0
- package/src/plugins/impl/plotly/PlotlyPlugin.tsx +2 -5
- package/src/plugins/impl/plotly/selection.ts +25 -19
- package/src/plugins/impl/vega/VegaPlugin.tsx +2 -3
- package/src/plugins/impl/vega/make-selectable.ts +4 -7
- package/src/plugins/layout/RoutesPlugin.tsx +1 -1
- package/src/stories/__tests__/editor-story.test.tsx +33 -0
- package/src/stories/button.stories.tsx +1 -0
- package/src/stories/editor.stories.tsx +24 -17
- package/src/stories/form-wrapper.stories.tsx +2 -2
- package/src/stories/select.stories.tsx +2 -2
- package/src/stories/switchable-multi-select.stories.tsx +1 -1
- package/src/utils/__tests__/arrays.test.ts +35 -0
- package/src/utils/__tests__/batch-requests.test.ts +3 -3
- package/src/utils/__tests__/numbers.test.ts +105 -1
- package/src/utils/__tests__/objects.test.ts +21 -0
- package/src/utils/arrays.ts +10 -0
- package/src/utils/blob.ts +1 -1
- package/src/utils/numbers.ts +79 -18
- package/src/utils/objects.ts +3 -7
- package/src/utils/once.ts +19 -19
- package/src/utils/tracer.ts +9 -5
- package/src/utils/zod-utils.ts +1 -3
- package/dist/any-language-editor-DZ_YTVus.js +0 -76
- package/dist/assets/__vite-browser-external-BXYkiFsk.js +0 -1
- package/dist/assets/worker-HPY9fbhz.js +0 -114
- package/dist/mermaid-HWGCJPDP-ByCsZnYF.js +0 -2
- package/dist/precisionRound-DMIVQrt2.js +0 -97
- package/src/core/host/__tests__/events.test.ts +0 -46
- package/src/core/host/__tests__/notebook-client.test.ts +0 -191
- package/src/core/host/__tests__/protocol.test.ts +0 -214
- package/src/core/host/events.ts +0 -60
- package/src/core/host/notebook-client.ts +0 -226
- package/src/core/host/protocol.ts +0 -1943
- package/src/core/state/__tests__/jotai.test.ts +0 -25
- package/src/core/wasm/worker/message-buffer.ts +0 -33
- package/src/core/websocket/__tests__/createConnectionTransport.test.ts +0 -49
- package/src/core/websocket/__tests__/useNotebookClient.hook.test.tsx +0 -140
- package/src/core/websocket/transports/__tests__/basic.test.ts +0 -275
- package/src/core/websocket/transports/__tests__/sse.test.ts +0 -335
- package/src/core/websocket/transports/__tests__/transport.test.ts +0 -308
- package/src/core/websocket/transports/__tests__/ws.test.ts +0 -125
- package/src/core/websocket/transports/basic.ts +0 -102
- package/src/core/websocket/transports/create.ts +0 -31
- package/src/core/websocket/transports/sse.ts +0 -308
- package/src/core/websocket/transports/transport.ts +0 -50
- package/src/core/websocket/transports/ws.ts +0 -98
- package/src/core/websocket/useNotebookClient.ts +0 -80
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import type { PyodideInterface } from "pyodide";
|
|
4
|
+
import { loadPyodide } from "pyodide";
|
|
5
|
+
import { afterEach, expect, it, vi } from "vitest";
|
|
6
|
+
import { defaultUserConfig } from "@/core/config/config-schema";
|
|
7
|
+
import { Deferred } from "@/utils/Deferred";
|
|
8
|
+
import { DefaultWasmController } from "../worker/bootstrap";
|
|
9
|
+
import { getController } from "../worker/getController";
|
|
10
|
+
import type { WasmRuntimeConfig } from "../runtime-config";
|
|
11
|
+
|
|
12
|
+
interface WorkerRequests {
|
|
13
|
+
startSession(opts: {
|
|
14
|
+
queryParameters: Record<string, string | string[]>;
|
|
15
|
+
code: string;
|
|
16
|
+
filename: string;
|
|
17
|
+
userConfig: ReturnType<typeof defaultUserConfig>;
|
|
18
|
+
}): Promise<void>;
|
|
19
|
+
notebookSubscribe(opts: { id: string; cursor: string | null }): Promise<void>;
|
|
20
|
+
notebookUnsubscribe(id: string): Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const { rpc, handlers } = vi.hoisted(() => ({
|
|
24
|
+
handlers: {} as {
|
|
25
|
+
requests: WorkerRequests;
|
|
26
|
+
bootstrap: (config: WasmRuntimeConfig) => void;
|
|
27
|
+
},
|
|
28
|
+
rpc: {
|
|
29
|
+
send: Object.assign(vi.fn(), {
|
|
30
|
+
initialized: vi.fn(),
|
|
31
|
+
initializedError: vi.fn(),
|
|
32
|
+
initializingMessage: vi.fn(),
|
|
33
|
+
notebookEvent: vi.fn(),
|
|
34
|
+
}),
|
|
35
|
+
},
|
|
36
|
+
}));
|
|
37
|
+
|
|
38
|
+
vi.mock("rpc-anywhere", () => ({
|
|
39
|
+
createRPCRequestHandler: (requests: WorkerRequests) => {
|
|
40
|
+
handlers.requests = requests;
|
|
41
|
+
return requests;
|
|
42
|
+
},
|
|
43
|
+
createWorkerParentTransport: vi.fn(),
|
|
44
|
+
createRPC: () => ({
|
|
45
|
+
...rpc,
|
|
46
|
+
addMessageListener: (
|
|
47
|
+
_name: string,
|
|
48
|
+
listener: (config: WasmRuntimeConfig) => void,
|
|
49
|
+
) => {
|
|
50
|
+
handlers.bootstrap = listener;
|
|
51
|
+
},
|
|
52
|
+
}),
|
|
53
|
+
}));
|
|
54
|
+
vi.mock("pyodide", () => ({ loadPyodide: vi.fn(), version: "314.0.0" }));
|
|
55
|
+
vi.mock("../worker/getController", () => ({ getController: vi.fn() }));
|
|
56
|
+
vi.mock("../worker/getMarimoWheel", () => ({
|
|
57
|
+
getMarimoWheel: () => "marimo-base",
|
|
58
|
+
}));
|
|
59
|
+
|
|
60
|
+
afterEach(() => {
|
|
61
|
+
vi.restoreAllMocks();
|
|
62
|
+
vi.clearAllMocks();
|
|
63
|
+
vi.resetModules();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
async function startWorker(subscribing = Promise.resolve()) {
|
|
67
|
+
const subscribers = new Map<string, (event: string) => void>();
|
|
68
|
+
const event = JSON.stringify({
|
|
69
|
+
event: "marimo",
|
|
70
|
+
data: { marimo: { op: "query-params-set", key: "startup", value: "ran" } },
|
|
71
|
+
});
|
|
72
|
+
const notebook = {
|
|
73
|
+
subscribe: vi.fn(
|
|
74
|
+
async (id: string, _cursor: unknown, emit: (event: string) => void) => {
|
|
75
|
+
await subscribing;
|
|
76
|
+
subscribers.set(id, emit);
|
|
77
|
+
},
|
|
78
|
+
),
|
|
79
|
+
unsubscribe: (id: string) => subscribers.delete(id),
|
|
80
|
+
destroy: vi.fn(),
|
|
81
|
+
};
|
|
82
|
+
const bridge = { notebook, destroy: vi.fn() };
|
|
83
|
+
const init = Object.assign(
|
|
84
|
+
vi.fn(() => subscribers.forEach((emit) => emit(event))),
|
|
85
|
+
{ destroy: vi.fn() },
|
|
86
|
+
);
|
|
87
|
+
const stop = Object.assign(
|
|
88
|
+
vi.fn(async () => subscribers.clear()),
|
|
89
|
+
{
|
|
90
|
+
destroy: vi.fn(),
|
|
91
|
+
},
|
|
92
|
+
);
|
|
93
|
+
const packages = { toJs: () => [], destroy: vi.fn() };
|
|
94
|
+
const resources = Object.assign([bridge, init, packages, stop], {
|
|
95
|
+
destroy: vi.fn(),
|
|
96
|
+
});
|
|
97
|
+
const pyodide = {
|
|
98
|
+
runPython: vi.fn(() => resources),
|
|
99
|
+
loadPackagesFromImports: vi.fn(async () => undefined),
|
|
100
|
+
loadedPackages: {},
|
|
101
|
+
};
|
|
102
|
+
vi.mocked(loadPyodide).mockResolvedValue(
|
|
103
|
+
pyodide as unknown as PyodideInterface,
|
|
104
|
+
);
|
|
105
|
+
const controller = new DefaultWasmController();
|
|
106
|
+
vi.spyOn(controller, "mountFilesystem").mockResolvedValue({
|
|
107
|
+
code: "",
|
|
108
|
+
filename: "notebook.py",
|
|
109
|
+
});
|
|
110
|
+
vi.mocked(getController).mockResolvedValue(controller);
|
|
111
|
+
await import("../worker/worker");
|
|
112
|
+
handlers.bootstrap({ version: "0.25.1" });
|
|
113
|
+
await handlers.requests.startSession({
|
|
114
|
+
code: "",
|
|
115
|
+
filename: "notebook.py",
|
|
116
|
+
queryParameters: {},
|
|
117
|
+
userConfig: defaultUserConfig(),
|
|
118
|
+
});
|
|
119
|
+
return { controller, init, notebook, event, pyodide };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
it("delivers startup notifications after a delayed first subscription, without rerunning on resubscribe", async () => {
|
|
123
|
+
const { controller, init, event, pyodide } = await startWorker();
|
|
124
|
+
try {
|
|
125
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
126
|
+
expect(rpc.send.initialized).toHaveBeenCalledOnce();
|
|
127
|
+
expect(pyodide.loadPackagesFromImports).toHaveResolved();
|
|
128
|
+
expect(init).not.toHaveBeenCalled();
|
|
129
|
+
|
|
130
|
+
await handlers.requests.notebookSubscribe({ id: "first", cursor: null });
|
|
131
|
+
await vi.waitFor(() => expect(init).toHaveBeenCalledOnce());
|
|
132
|
+
expect(rpc.send.notebookEvent.mock.calls).toEqual([
|
|
133
|
+
[{ id: "first", event }],
|
|
134
|
+
]);
|
|
135
|
+
|
|
136
|
+
await handlers.requests.notebookUnsubscribe("first");
|
|
137
|
+
await handlers.requests.notebookSubscribe({ id: "second", cursor: null });
|
|
138
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
139
|
+
expect(init).toHaveBeenCalledOnce();
|
|
140
|
+
} finally {
|
|
141
|
+
await controller.stopSession();
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("does not authorize startup through a subscription canceled while opening", async () => {
|
|
146
|
+
const subscribing = new Deferred<void>();
|
|
147
|
+
const { controller, init, notebook } = await startWorker(subscribing.promise);
|
|
148
|
+
try {
|
|
149
|
+
const opened = handlers.requests.notebookSubscribe({
|
|
150
|
+
id: "canceled",
|
|
151
|
+
cursor: null,
|
|
152
|
+
});
|
|
153
|
+
await vi.waitFor(() => expect(notebook.subscribe).toHaveBeenCalledOnce());
|
|
154
|
+
const closed = handlers.requests.notebookUnsubscribe("canceled");
|
|
155
|
+
subscribing.resolve();
|
|
156
|
+
await Promise.all([opened, closed]);
|
|
157
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
158
|
+
expect(init).not.toHaveBeenCalled();
|
|
159
|
+
|
|
160
|
+
await handlers.requests.notebookSubscribe({ id: "active", cursor: null });
|
|
161
|
+
await vi.waitFor(() => expect(init).toHaveBeenCalledOnce());
|
|
162
|
+
} finally {
|
|
163
|
+
await controller.stopSession();
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it("waits for a successful subscription after an opening failure", async () => {
|
|
168
|
+
const { controller, init, notebook } = await startWorker();
|
|
169
|
+
try {
|
|
170
|
+
notebook.subscribe.mockRejectedValueOnce(new Error("Subscription failed"));
|
|
171
|
+
await expect(
|
|
172
|
+
handlers.requests.notebookSubscribe({ id: "failed", cursor: null }),
|
|
173
|
+
).rejects.toThrow("Subscription failed");
|
|
174
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
175
|
+
expect(init).not.toHaveBeenCalled();
|
|
176
|
+
|
|
177
|
+
await handlers.requests.notebookSubscribe({ id: "active", cursor: null });
|
|
178
|
+
await vi.waitFor(() => expect(init).toHaveBeenCalledOnce());
|
|
179
|
+
} finally {
|
|
180
|
+
await controller.stopSession();
|
|
181
|
+
}
|
|
182
|
+
});
|
package/src/core/wasm/bridge.ts
CHANGED
|
@@ -8,9 +8,11 @@ import { Deferred } from "@/utils/Deferred";
|
|
|
8
8
|
import { throwNotImplemented } from "@/utils/functions";
|
|
9
9
|
import { Logger } from "@/utils/Logger";
|
|
10
10
|
import { reloadSafe } from "@/utils/reload-safe";
|
|
11
|
+
import { once } from "@/utils/once";
|
|
11
12
|
import { generateUUID } from "@/utils/uuid";
|
|
12
13
|
import { createModuleWorker } from "@/utils/worker";
|
|
13
14
|
import { notebookIsRunningAtom } from "../cells/cells";
|
|
15
|
+
import { createWasmNotebookBackend } from "../host";
|
|
14
16
|
import type { ClientCommand } from "../kernel/messages";
|
|
15
17
|
import { getInitialAppMode } from "../mode";
|
|
16
18
|
import { API } from "../network/api";
|
|
@@ -38,8 +40,6 @@ import type {
|
|
|
38
40
|
} from "../network/types";
|
|
39
41
|
import { filenameAtom } from "../saving/file-state";
|
|
40
42
|
import { store } from "../state/jotai";
|
|
41
|
-
import { BasicTransport } from "../websocket/transports/basic";
|
|
42
|
-
import type { IConnectionTransport } from "../websocket/transports/transport";
|
|
43
43
|
import { PyodideRouter } from "./router";
|
|
44
44
|
import { getWorkerRPC } from "./rpc";
|
|
45
45
|
import { getWasmRuntimeConfig } from "./runtime-config";
|
|
@@ -69,10 +69,8 @@ export class PyodideBridge implements RunRequests, EditRequests {
|
|
|
69
69
|
private rpc!: ReturnType<typeof getWorkerRPC<WorkerSchema>>;
|
|
70
70
|
private saveRpc: SaveWorker | undefined;
|
|
71
71
|
private pendingSessionSave: Promise<unknown> = Promise.resolve();
|
|
72
|
+
private notebookConsumers = new Map<string, (event: unknown) => void>();
|
|
72
73
|
private interruptBuffer?: Uint8Array;
|
|
73
|
-
private messageConsumer:
|
|
74
|
-
| ((message: MessageEvent<string>) => void)
|
|
75
|
-
| undefined;
|
|
76
74
|
|
|
77
75
|
public initialized = new Deferred<void>();
|
|
78
76
|
|
|
@@ -119,6 +117,10 @@ export class PyodideBridge implements RunRequests, EditRequests {
|
|
|
119
117
|
this.rpc = getWorkerRPC<WorkerSchema>(worker);
|
|
120
118
|
this.rpc.send.bootstrap(runtimeConfig);
|
|
121
119
|
|
|
120
|
+
this.rpc.addMessageListener("notebookEvent", ({ id, event }) => {
|
|
121
|
+
this.notebookConsumers.get(id)?.(JSON.parse(event));
|
|
122
|
+
});
|
|
123
|
+
|
|
122
124
|
// Listeners
|
|
123
125
|
this.rpc.addMessageListener("ready", () => {
|
|
124
126
|
this.startSession();
|
|
@@ -154,9 +156,6 @@ export class PyodideBridge implements RunRequests, EditRequests {
|
|
|
154
156
|
store.set(wasmInitStateAtom, { kind: "error", message: error });
|
|
155
157
|
this.initialized.reject(new Error(error));
|
|
156
158
|
});
|
|
157
|
-
this.rpc.addMessageListener("kernelMessage", ({ message }) => {
|
|
158
|
-
this.messageConsumer?.(new MessageEvent("message", { data: message }));
|
|
159
|
-
});
|
|
160
159
|
}
|
|
161
160
|
|
|
162
161
|
private async startSession() {
|
|
@@ -208,11 +207,37 @@ export class PyodideBridge implements RunRequests, EditRequests {
|
|
|
208
207
|
}
|
|
209
208
|
}
|
|
210
209
|
|
|
211
|
-
public
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
210
|
+
public async createNotebookBackend() {
|
|
211
|
+
await this.initialized.promise;
|
|
212
|
+
const { id } = await this.rpc.proxy.request.notebookInfo();
|
|
213
|
+
return createWasmNotebookBackend({
|
|
214
|
+
notebookId: id,
|
|
215
|
+
subscribe: async (cursor, emit, signal) => {
|
|
216
|
+
await this.initialized.promise;
|
|
217
|
+
signal.throwIfAborted();
|
|
218
|
+
const id = generateUUID();
|
|
219
|
+
this.notebookConsumers.set(id, emit);
|
|
220
|
+
const close = once(() => {
|
|
221
|
+
signal.removeEventListener("abort", close);
|
|
222
|
+
this.notebookConsumers.delete(id);
|
|
223
|
+
void this.rpc.proxy.request
|
|
224
|
+
.notebookUnsubscribe(id)
|
|
225
|
+
.catch(Logger.error);
|
|
226
|
+
});
|
|
227
|
+
signal.addEventListener("abort", close, { once: true });
|
|
228
|
+
try {
|
|
229
|
+
await this.rpc.proxy.request.notebookSubscribe({ id, cursor });
|
|
230
|
+
return close;
|
|
231
|
+
} catch (error) {
|
|
232
|
+
close();
|
|
233
|
+
throw error;
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
call: async (call) => {
|
|
237
|
+
await this.initialized.promise;
|
|
238
|
+
return this.rpc.proxy.request.notebookCall(call);
|
|
239
|
+
},
|
|
240
|
+
});
|
|
216
241
|
}
|
|
217
242
|
|
|
218
243
|
public sendRename: EditRequests["sendRename"] = async ({ filename }) => {
|
|
@@ -409,7 +434,7 @@ export class PyodideBridge implements RunRequests, EditRequests {
|
|
|
409
434
|
return response as Snippets;
|
|
410
435
|
};
|
|
411
436
|
|
|
412
|
-
public openFile: EditRequests["openFile"] = async (
|
|
437
|
+
public openFile: EditRequests["openFile"] = async () => {
|
|
413
438
|
const url = createShareableLink({
|
|
414
439
|
code: null,
|
|
415
440
|
baseUrl: window.location.origin,
|
|
@@ -453,7 +478,7 @@ export class PyodideBridge implements RunRequests, EditRequests {
|
|
|
453
478
|
return null;
|
|
454
479
|
};
|
|
455
480
|
|
|
456
|
-
public sendInstantiate: RunRequests["sendInstantiate"] = async (
|
|
481
|
+
public sendInstantiate: RunRequests["sendInstantiate"] = async () => {
|
|
457
482
|
return null;
|
|
458
483
|
};
|
|
459
484
|
|
|
@@ -719,12 +744,6 @@ export class PyodideBridge implements RunRequests, EditRequests {
|
|
|
719
744
|
}
|
|
720
745
|
}
|
|
721
746
|
|
|
722
|
-
export function createPyodideConnection(): IConnectionTransport {
|
|
723
|
-
return BasicTransport.withProducerCallback((callback) => {
|
|
724
|
-
PyodideBridge.INSTANCE.attachMessageConsumer(callback);
|
|
725
|
-
});
|
|
726
|
-
}
|
|
727
|
-
|
|
728
747
|
// Compose the worker name. The optional "::controller" suffix tells
|
|
729
748
|
// getController.ts that the host page provides a custom /wasm/controller.js
|
|
730
749
|
// and that the dynamic import should be attempted. Hosts opt in by setting
|
package/src/core/wasm/rpc.ts
CHANGED
package/src/core/wasm/store.ts
CHANGED
|
@@ -41,7 +41,7 @@ const urlFileStore: FileStore = {
|
|
|
41
41
|
};
|
|
42
42
|
|
|
43
43
|
export const domElementFileStore: FileStore = {
|
|
44
|
-
saveFile(
|
|
44
|
+
saveFile(_contents: string) {
|
|
45
45
|
// Do nothing
|
|
46
46
|
},
|
|
47
47
|
readFile() {
|
|
@@ -54,7 +54,7 @@ export const domElementFileStore: FileStore = {
|
|
|
54
54
|
};
|
|
55
55
|
|
|
56
56
|
const remoteDefaultFileStore: FileStore = {
|
|
57
|
-
saveFile(
|
|
57
|
+
saveFile(_contents: string) {
|
|
58
58
|
// Do nothing
|
|
59
59
|
},
|
|
60
60
|
readFile() {
|
|
@@ -79,7 +79,7 @@ export const mountConfigFileStore: FileStore = {
|
|
|
79
79
|
};
|
|
80
80
|
|
|
81
81
|
const emptyFileStore: FileStore = {
|
|
82
|
-
saveFile(
|
|
82
|
+
saveFile(_contents: string) {
|
|
83
83
|
// Do nothing
|
|
84
84
|
},
|
|
85
85
|
readFile() {
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
import { loadPyodide, type PyodideInterface } from "pyodide";
|
|
3
3
|
import type { PyCallable, PyProxy } from "pyodide/ffi";
|
|
4
|
-
import type { UserConfig } from "@/core/config/config-schema";
|
|
5
|
-
import type { NotificationPayload } from "@/core/kernel/messages";
|
|
6
|
-
import type { JsonString } from "@/utils/json/base64";
|
|
7
4
|
import { invariant } from "../../../utils/invariant";
|
|
5
|
+
import { once } from "../../../utils/once";
|
|
8
6
|
import { Logger } from "../../../utils/Logger";
|
|
9
7
|
import { WasmFileSystem } from "./fs";
|
|
10
8
|
import { getMarimoWheel } from "./getMarimoWheel";
|
|
11
9
|
import { t } from "./tracer";
|
|
12
|
-
import type { SerializedBridge, WasmController } from "./types";
|
|
10
|
+
import type { SerializedBridge, WasmController, WasmSession } from "./types";
|
|
13
11
|
import { shouldLoadDuckDBPackages } from "../utils";
|
|
14
12
|
|
|
15
13
|
const MAKE_SNAPSHOT = false;
|
|
@@ -45,7 +43,7 @@ export class DefaultWasmController implements WasmController {
|
|
|
45
43
|
protected pyodide: PyodideInterface | null = null;
|
|
46
44
|
private packageLoadQueue = Promise.resolve();
|
|
47
45
|
private sessionGeneration = 0;
|
|
48
|
-
private
|
|
46
|
+
private sessions = new Set<WasmSession>();
|
|
49
47
|
|
|
50
48
|
public get requirePyodide() {
|
|
51
49
|
invariant(this.pyodide, "Pyodide not loaded");
|
|
@@ -85,7 +83,7 @@ export class DefaultWasmController implements WasmController {
|
|
|
85
83
|
packages: [
|
|
86
84
|
"micropip",
|
|
87
85
|
"msgspec",
|
|
88
|
-
getMarimoWheel(
|
|
86
|
+
getMarimoWheel(),
|
|
89
87
|
"Markdown",
|
|
90
88
|
"pymdown-extensions",
|
|
91
89
|
"narwhals",
|
|
@@ -138,28 +136,15 @@ export class DefaultWasmController implements WasmController {
|
|
|
138
136
|
});
|
|
139
137
|
}
|
|
140
138
|
|
|
141
|
-
public async
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
filename: string | null;
|
|
145
|
-
onMessage: (message: JsonString<NotificationPayload>) => void;
|
|
146
|
-
userConfig: UserConfig;
|
|
147
|
-
}): Promise<SerializedBridge> {
|
|
139
|
+
public async prepareSession(
|
|
140
|
+
opts: Parameters<WasmController["prepareSession"]>[0],
|
|
141
|
+
): Promise<WasmSession> {
|
|
148
142
|
const sessionGeneration = this.sessionGeneration;
|
|
149
|
-
const { code, filename,
|
|
150
|
-
// We pass down a messenger object to the code
|
|
151
|
-
// This is used to have synchronous communication between the JS and Python code
|
|
152
|
-
// Previously, we used a queue, but this would not properly flush the queue
|
|
153
|
-
// during processing of a cell's code.
|
|
154
|
-
//
|
|
155
|
-
// This adds a messenger object to the global scope (import js; js.messenger.callback)
|
|
156
|
-
self.messenger = {
|
|
157
|
-
callback: onMessage,
|
|
158
|
-
};
|
|
143
|
+
const { code, filename, queryParameters, userConfig } = opts;
|
|
159
144
|
self.query_params = queryParameters;
|
|
160
145
|
self.user_config = userConfig;
|
|
161
146
|
|
|
162
|
-
const span = t.startSpan("
|
|
147
|
+
const span = t.startSpan("prepareSession.runPython");
|
|
163
148
|
const nbFilename = filename || WasmFileSystem.NOTEBOOK_FILENAME;
|
|
164
149
|
const sessionResources = this.requirePyodide.runPython(
|
|
165
150
|
`
|
|
@@ -169,14 +154,12 @@ export class DefaultWasmController implements WasmController {
|
|
|
169
154
|
import js
|
|
170
155
|
from marimo._pyodide.bootstrap import create_session, instantiate
|
|
171
156
|
|
|
172
|
-
assert js.messenger, "messenger is not defined"
|
|
173
157
|
assert js.query_params, "query_params is not defined"
|
|
174
158
|
|
|
175
159
|
def create_session_resources():
|
|
176
160
|
session, bridge = create_session(
|
|
177
161
|
filename="${nbFilename}",
|
|
178
162
|
query_params=js.query_params.to_py(),
|
|
179
|
-
message_callback=js.messenger.callback,
|
|
180
163
|
user_config=js.user_config.to_py(),
|
|
181
164
|
)
|
|
182
165
|
session_task = None
|
|
@@ -198,6 +181,7 @@ export class DefaultWasmController implements WasmController {
|
|
|
198
181
|
if task is not None:
|
|
199
182
|
await asyncio.gather(task, return_exceptions=True)
|
|
200
183
|
if bridge is not None:
|
|
184
|
+
bridge.close()
|
|
201
185
|
bridge.session = None
|
|
202
186
|
task = None
|
|
203
187
|
kernel_task = None
|
|
@@ -235,45 +219,65 @@ export class DefaultWasmController implements WasmController {
|
|
|
235
219
|
} finally {
|
|
236
220
|
packagesProxy.destroy();
|
|
237
221
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
//
|
|
244
|
-
// a sense of responsiveness.
|
|
222
|
+
let stopped = false;
|
|
223
|
+
let stopping: Promise<void> | undefined;
|
|
224
|
+
const releaseInit = once(() => initSession.destroy());
|
|
225
|
+
const isStopped = () =>
|
|
226
|
+
stopped || sessionGeneration !== this.sessionGeneration;
|
|
227
|
+
// Load in parallel with subscription setup; only start() executes cells.
|
|
245
228
|
const dependenciesReady = this.packageLoadQueue.then(() => {
|
|
246
|
-
if (
|
|
229
|
+
if (isStopped()) {
|
|
247
230
|
return;
|
|
248
231
|
}
|
|
249
232
|
return this.loadNotebookDeps(code, foundPackages);
|
|
250
233
|
});
|
|
251
234
|
this.packageLoadQueue = dependenciesReady.catch(() => undefined);
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
235
|
+
const session: WasmSession = {
|
|
236
|
+
bridge: bridgeProxy as unknown as SerializedBridge,
|
|
237
|
+
start: once(async () => {
|
|
238
|
+
try {
|
|
239
|
+
if (isStopped()) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
await dependenciesReady;
|
|
243
|
+
if (!isStopped()) {
|
|
244
|
+
initSession(userConfig.runtime.auto_instantiate);
|
|
245
|
+
}
|
|
246
|
+
} catch (error) {
|
|
247
|
+
if (!isStopped()) {
|
|
248
|
+
throw error;
|
|
249
|
+
}
|
|
250
|
+
} finally {
|
|
251
|
+
releaseInit();
|
|
256
252
|
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
253
|
+
}),
|
|
254
|
+
stop: () => {
|
|
255
|
+
stopped = true;
|
|
256
|
+
releaseInit();
|
|
257
|
+
stopping ??= (async () => {
|
|
258
|
+
await stopSession();
|
|
259
|
+
stopSession.destroy();
|
|
260
|
+
bridgeProxy.destroy();
|
|
261
|
+
this.sessions.delete(session);
|
|
262
|
+
})().catch((error: unknown) => {
|
|
263
|
+
stopping = undefined;
|
|
264
|
+
throw error;
|
|
265
|
+
});
|
|
266
|
+
return stopping;
|
|
267
|
+
},
|
|
268
|
+
};
|
|
269
|
+
this.sessions.add(session);
|
|
270
|
+
return session;
|
|
265
271
|
}
|
|
266
272
|
|
|
267
273
|
public async stopSession(): Promise<void> {
|
|
268
274
|
this.sessionGeneration += 1;
|
|
269
|
-
const
|
|
275
|
+
const sessions = [...this.sessions];
|
|
270
276
|
let hasFailure = false;
|
|
271
277
|
let firstFailure: unknown;
|
|
272
|
-
for (const
|
|
278
|
+
for (const session of sessions) {
|
|
273
279
|
try {
|
|
274
|
-
await stop();
|
|
275
|
-
this.activeSessionStops.delete(stop);
|
|
276
|
-
stop.destroy();
|
|
280
|
+
await session.stop();
|
|
277
281
|
} catch (error) {
|
|
278
282
|
if (!hasFailure) {
|
|
279
283
|
hasFailure = true;
|
|
@@ -2,11 +2,7 @@
|
|
|
2
2
|
import type { PyodideInterface } from "pyodide";
|
|
3
3
|
import type { WasmRuntimeConfig } from "../runtime-config";
|
|
4
4
|
import type { UserConfig } from "@/core/config/config-schema";
|
|
5
|
-
import type {
|
|
6
|
-
ClientCommand,
|
|
7
|
-
NotificationPayload,
|
|
8
|
-
} from "@/core/kernel/messages";
|
|
9
|
-
import type { JsonString } from "@/utils/json/base64";
|
|
5
|
+
import type { ClientCommand } from "@/core/kernel/messages";
|
|
10
6
|
import type {
|
|
11
7
|
CodeCompletionRequest,
|
|
12
8
|
CopyNotebookRequest,
|
|
@@ -58,19 +54,21 @@ export interface WasmController {
|
|
|
58
54
|
code: string;
|
|
59
55
|
filename: string | null;
|
|
60
56
|
}): Promise<{ code: string; filename: string }>;
|
|
61
|
-
/**
|
|
62
|
-
|
|
63
|
-
* @param opts.code - The code to start with
|
|
64
|
-
* @param opts.fallbackCode - The code to fallback to
|
|
65
|
-
* @param opts.filename - The filename to start with
|
|
66
|
-
*/
|
|
67
|
-
startSession(opts: {
|
|
57
|
+
/** Prepare a session and load its dependencies without executing cells. */
|
|
58
|
+
prepareSession(opts: {
|
|
68
59
|
queryParameters: Record<string, string | string[]>;
|
|
69
60
|
code: string;
|
|
70
61
|
filename: string | null;
|
|
71
62
|
userConfig: UserConfig;
|
|
72
|
-
|
|
73
|
-
|
|
63
|
+
}): Promise<WasmSession>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface WasmSession {
|
|
67
|
+
readonly bridge: SerializedBridge;
|
|
68
|
+
/** Start once dependencies are ready. Repeated calls share the same start. */
|
|
69
|
+
start(): Promise<void>;
|
|
70
|
+
/** Prevent a pending start and release the session and its bridge. */
|
|
71
|
+
stop(): Promise<void>;
|
|
74
72
|
}
|
|
75
73
|
|
|
76
74
|
export interface RawBridge {
|
|
@@ -121,4 +119,15 @@ export type SerializedBridge = {
|
|
|
121
119
|
: RawBridge[P];
|
|
122
120
|
} & {
|
|
123
121
|
save(payload: string): Promise<void>;
|
|
122
|
+
notebook: {
|
|
123
|
+
readonly id: string;
|
|
124
|
+
subscribe(
|
|
125
|
+
id: string,
|
|
126
|
+
cursor: string | null,
|
|
127
|
+
emit: (event: string) => void,
|
|
128
|
+
): Promise<void>;
|
|
129
|
+
unsubscribe(id: string): void;
|
|
130
|
+
call(call: string): Promise<string>;
|
|
131
|
+
destroy(): void;
|
|
132
|
+
};
|
|
124
133
|
};
|