@marimo-team/frontend 0.25.3-dev12 → 0.25.3-dev121
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-_Aw9_foI.js +1 -0
- package/dist/assets/{ConnectedDataExplorerComponent-TprnxZeJ.js → ConnectedDataExplorerComponent-BRgqbAsc.js} +1 -1
- package/dist/assets/{ErrorBoundary-CM13wrd8.js → ErrorBoundary-DxSzjvbx.js} +2 -2
- package/dist/assets/JsonOutput-B7pGj79O.js +11 -0
- package/dist/assets/{LazyAnyLanguageCodeMirror-B4fCknq1.js → LazyAnyLanguageCodeMirror-BPYE76jR.js} +2 -2
- package/dist/assets/MarimoErrorOutput-Bzo6Bkp5.js +10 -0
- package/dist/assets/{RenderHTML-DX6i99Op.js → RenderHTML-D6AC0IhC.js} +1 -1
- package/dist/assets/RunButton-BjYsPiKa.js +1 -0
- package/dist/assets/__vite-browser-external-BynxqS4w.js +1 -0
- package/dist/assets/__vite-browser-external-DTOT9NDN.js +1 -0
- package/dist/assets/{add-cell-with-ai-BpD_790A.js → add-cell-with-ai-CFrR1iW4.js} +6 -6
- package/dist/assets/{add-connection-dialog-B1BXCjex.js → add-connection-dialog-CyNAB_W2.js} +1 -1
- package/dist/assets/{agent-panel-CDFUc1uu.js → agent-panel-VOrccb5q.js} +3 -3
- package/dist/assets/ai-model-dropdown-Bfxv_zvu.js +13 -0
- package/dist/assets/{alert-dialog-BaPGzeOK.js → alert-dialog-C-sWmKva.js} +1 -1
- package/dist/assets/any-language-editor-DUMe1UCk.js +1 -0
- package/dist/assets/{app-config-button-AdV-QDD7.js → app-config-button-BRcjv8A_.js} +1 -1
- package/dist/assets/arrays-0R2reF2y.js +1 -0
- package/dist/assets/{blob-BG8yoQmx.js → blob-DTYOX-m5.js} +1 -1
- package/dist/assets/{button-COI-NGrx.js → button-D8gVKwe-.js} +1 -1
- package/dist/assets/{cache-panel-DTY1dqvi.js → cache-panel-BV-c7ed0.js} +1 -1
- package/dist/assets/{capabilities-BJvf-MD4.js → capabilities-Pq1YNPlK.js} +1 -1
- package/dist/assets/cell-editor-eKgYK8Bd.js +42 -0
- package/dist/assets/{cell-link-8Auigi8P.js → cell-link-D3rkqgbU.js} +1 -1
- package/dist/assets/{cells-C6YGYGdi.js → cells-2HETnlvE.js} +18 -18
- package/dist/assets/{chat-display-DhgI2MS9.js → chat-display-BrT4VJJK.js} +1 -1
- package/dist/assets/chat-panel-qpPNEwxV.js +4 -0
- package/dist/assets/chat-ui-SAdJxg-k.js +4 -0
- package/dist/assets/{circle-DXf-VWZG.js → circle-B6D_6D8w.js} +1 -1
- package/dist/assets/{code-visibility-BJImdpDi.js → code-visibility-DYDagVy7.js} +1 -1
- package/dist/assets/column-preview-yI7-INkZ.js +1 -0
- package/dist/assets/command-palette-DF-B_YpW.js +1 -0
- package/dist/assets/{common-Sl3uthjg.js → common-DgXKwblm.js} +1 -1
- package/dist/assets/components-CBE8cOLK.js +1 -0
- package/dist/assets/{components-3cou6Mka.js → components-CuMhY4Sf.js} +1 -1
- package/dist/assets/{components-CskjK6xZ.js → components-DCo7mAWO.js} +1 -1
- package/dist/assets/config-CD1IuZFJ.js +1 -0
- package/dist/assets/{config-DRIpOKqh.js → config-DMz7oN0r.js} +1 -1
- package/dist/assets/{config-CA1V-wjh.js → config-DpY86_Yq.js} +1 -1
- package/dist/assets/{copy-D8O_CYCG.js → copy-KlhazlhB.js} +1 -1
- package/dist/assets/{copy-icon-BIiRXh_3.js → copy-icon-8-a5vovc.js} +1 -1
- package/dist/assets/{createReducer-DgRODTW6.js → createReducer-CaI_Oa0v.js} +1 -1
- package/dist/assets/{datasource-DfbNb83e.js → datasource-D9JMcEAP.js} +1 -1
- package/dist/assets/{dates-CfmdTL6B.js → dates-CQMwFko2.js} +1 -1
- package/dist/assets/{dependency-graph-panel-L5cBE5sl.js → dependency-graph-panel-BVJWPCvw.js} +3 -3
- package/dist/assets/{dist-iaThVDkA.js → dist-jlhMTKo_.js} +1 -1
- package/dist/assets/{documentation-panel-D7S8JIc2.js → documentation-panel--wI4XHso.js} +1 -1
- package/dist/assets/{download-C03FOElq.js → download-Dod42wfD.js} +1 -1
- package/dist/assets/{dropdown-menu-C1rnl-0o.js → dropdown-menu-DS6umAnH.js} +1 -1
- package/dist/assets/edit-page-Uj9YSRMX.js +9 -0
- package/dist/assets/{error-banner-COZ7SfYV.js → error-banner-B5kzVf_0.js} +1 -1
- package/dist/assets/{error-entries-CLnPDv7b.js → error-entries-D00nWhWE.js} +1 -1
- package/dist/assets/{error-panel-D2xrYLue.js → error-panel-CcN_1PWf.js} +1 -1
- package/dist/assets/{es-Cqst59WX.js → es-N9SHpmG7.js} +1 -1
- package/dist/assets/{feedback-button-DzPunIqU.js → feedback-button-ByGgqUsX.js} +1 -1
- package/dist/assets/feedback-button-tbWwJ_t6.js +1 -0
- package/dist/assets/file-explorer-panel-eTEqtMPW.js +1 -0
- package/dist/assets/{file-name-input-B4fRaQkI.js → file-name-input-D5SfTNWh.js} +1 -1
- package/dist/assets/{floating-outline-B5MaM1JS.js → floating-outline-B-k3P_AO.js} +1 -1
- package/dist/assets/{formats-yZ06h1Pl.js → formats-zylfwejq.js} +1 -1
- package/dist/assets/{formatting-BqrP-n49.js → formatting-uom1es4b.js} +1 -1
- package/dist/assets/{fullscreen-B9tHcfpV.js → fullscreen-BWTs8pYZ.js} +1 -1
- package/dist/assets/{gallery-page-BefvdsM7.js → gallery-page-BzbMhDHi.js} +1 -1
- package/dist/assets/{ganttDiagram-EL5Y4UJY-2n7Ig5Jm.js → ganttDiagram-EL5Y4UJY-CYqkCfFG.js} +1 -1
- package/dist/assets/{glide-data-editor-C3eaYlXg.js → glide-data-editor-BMqhUrHI.js} +2 -2
- package/dist/assets/{glide-utils-CIrAxdjv.js → glide-utils-HFqhvWHI.js} +2 -2
- package/dist/assets/home-page-DJcYWiga.js +1 -0
- package/dist/assets/{hooks-DzML9nez.js → hooks-CGLb_CCz.js} +1 -1
- package/dist/assets/{html-to-image-DK9Zl1lU.js → html-to-image-D_0rd3_B.js} +1 -1
- package/dist/assets/index-BnhSnwvB.css +2 -0
- package/dist/assets/index-bSIORW11.js +30 -0
- package/dist/assets/{input-BoBxR50a.js → input-DmHMEZGh.js} +1 -1
- package/dist/assets/kiosk-mode-DFS6HopF.js +1 -0
- package/dist/assets/{layout-Ck1RoL1B.js → layout-C2XnLAfO.js} +5 -5
- package/dist/assets/linear-Bma8J3iM.js +1 -0
- package/dist/assets/links-DX2w97N9.js +1 -0
- package/dist/assets/{logs-panel-BRytaWaO.js → logs-panel-B3T1GFBk.js} +1 -1
- package/dist/assets/{markdown-renderer-DX0Xrrfy.js → markdown-renderer-BhiTvxtL.js} +1 -1
- package/dist/assets/{menu-items-BrwnTgyK.js → menu-items-BgUyNUpu.js} +1 -1
- package/dist/assets/{mermaid-C4EiVqcd.js → mermaid-BKXlWk4F.js} +2 -2
- package/dist/assets/{name-cell-input-DlpTvPLa.js → name-cell-input-CgAObR6N.js} +1 -1
- package/dist/assets/numbers-CsZVw3Dc.js +1 -0
- package/dist/assets/{objects-snlMn4yk.js → objects-rLPlQ3X_.js} +1 -1
- package/dist/assets/{once-Haz_uxEL.js → once-6KNTRW0u.js} +1 -1
- package/dist/assets/{outline-panel-B2WIpz-1.js → outline-panel-BhBb7Vqw.js} +1 -1
- package/dist/assets/{package-data-dv4FBHxR.js → package-data-CKPmBDtg.js} +1 -1
- package/dist/assets/packages-panel-Bt0xNpFl.js +1 -0
- package/dist/assets/{panel-accordion-state-BqSH5ttk.js → panel-accordion-state-ZsUdpAYn.js} +1 -1
- package/dist/assets/panels-DJghvIKo.js +1 -0
- package/dist/assets/precisionRound-BxlUs8G4.js +1 -0
- package/dist/assets/process-output-CeOMwE17.js +1 -0
- package/dist/assets/{quadrantDiagram-AXDQQJYC-yBhCDI7E.js → quadrantDiagram-AXDQQJYC-D9KH5Zge.js} +1 -1
- package/dist/assets/react-vega-BTNeboR2.js +189 -0
- package/dist/assets/{readonly-code-display-MnMEoYW5.js → readonly-code-display-TvOC7ESE.js} +1 -1
- package/dist/assets/{readonly-python-code-ggs4hAzC.js → readonly-python-code-Cqkfdeyo.js} +1 -1
- package/dist/assets/{renderShortcut-C1JwYcqW.js → renderShortcut-rUwGqKgC.js} +1 -1
- package/dist/assets/{requests-lt0JK8Jt.js → requests-BIFR-h4M.js} +1 -1
- package/dist/assets/requests-DuLJN9Tf.js +1 -0
- package/dist/assets/{reveal-component-PiV1eQk9.js → reveal-component-CwE6U9oJ.js} +37 -37
- package/dist/assets/run-page-CtonWTtJ.js +1 -0
- package/dist/assets/{runs-BLp5jezv.js → runs-C49hleBy.js} +1 -1
- package/dist/assets/{runtime-details-state-CoL-p0MC.js → runtime-details-state-CKqJSfSR.js} +1 -1
- package/dist/assets/{save-worker-BbUSwZRG.js → save-worker-UwdGcNK5.js} +2 -1
- package/dist/assets/scratchpad-panel-CwVBedfZ.js +1 -0
- package/dist/assets/{scroll-to-bottom-button-1FyowpWV.js → scroll-to-bottom-button-CIkON2WJ.js} +1 -1
- package/dist/assets/{secrets-panel-D7KN4tVs.js → secrets-panel-bcfEKxRx.js} +1 -1
- package/dist/assets/{select-DTBn5eTb.js → select-3CVGlYrC.js} +1 -1
- package/dist/assets/{session-DqoWmb7X.js → session-CMLIzZKw.js} +1 -1
- package/dist/assets/session-panel-CdHdKUCF.js +1 -0
- package/dist/assets/{snippets-panel-ClGHtEKQ.js → snippets-panel-DYLWOLYw.js} +1 -1
- package/dist/assets/{spec-BfSBcO5B.js → spec-DlH4Mp7d.js} +1 -1
- package/dist/assets/{state-uwRvJLb8.js → state-3mcyTS9q.js} +1 -1
- package/dist/assets/state-B_MCKzBL.js +1 -0
- package/dist/assets/{state-C3oDoyFe.js → state-C5WAeuTu.js} +1 -1
- package/dist/assets/{state-DxQnC_1I.js → state-Cv2JW3l9.js} +1 -1
- package/dist/assets/{state-DtGB7xeJ.js → state-DZN9vZ6z.js} +1 -1
- package/dist/assets/state-DgECPVFs.js +107 -0
- package/dist/assets/{strings-B2vv-ENv.js → strings-C25fhMR_.js} +1 -1
- package/dist/assets/{swiper-component-a07domrZ.js → swiper-component-CRGRy5_O.js} +1 -1
- package/dist/assets/{switch-CTaiAjrz.js → switch-DYjBfpNJ.js} +1 -1
- package/dist/assets/{tabs-BuQlZFbt.js → tabs-CY8E4r9U.js} +1 -1
- package/dist/assets/{terminal-wnSHYT8N.js → terminal-Ca-kubKb.js} +1 -1
- package/dist/assets/{time-MgToa8VM.js → time-DswSXBDk.js} +1 -1
- package/dist/assets/{toast-BGzLGnvw.js → toast-Gzxi-le_.js} +1 -1
- package/dist/assets/tooltip-Brya4Ghn.js +61 -0
- package/dist/assets/{tooltip-C9TuY18o.js → tooltip-CkGm8X7m.js} +1 -1
- package/dist/assets/tracing-B0J2ctTm.js +1 -0
- package/dist/assets/tracing-panel-C1OCpoeE.js +2 -0
- package/dist/assets/{tree-actions-DnUokmva.js → tree-actions-sjBxIglc.js} +1 -1
- package/dist/assets/{use-restore-focus-BxEGwIbk.js → use-restore-focus-DdS7Tl0F.js} +1 -1
- package/dist/assets/{useAddCell-IN24thff.js → useAddCell-CaXnFGki.js} +1 -1
- package/dist/assets/useAsyncData-qvoPYHtx.js +1 -0
- package/dist/assets/useBoolean-Dd2nWXFJ.js +1 -0
- package/dist/assets/useCellActionButton-CJ90CMEj.js +1 -0
- package/dist/assets/useDebounce-DwTQ_1Pu.js +1 -0
- package/dist/assets/{useDeepCompareMemoize-DFIVN9dn.js → useDeepCompareMemoize-B2DVK2De.js} +1 -1
- package/dist/assets/useDelayElapsed-BlsDT4TV.js +1 -0
- package/dist/assets/{useDeleteCell-fQlM46Y2.js → useDeleteCell-D9N85822.js} +1 -1
- package/dist/assets/{useDependencyPanelTab-ImXYINm_.js → useDependencyPanelTab-CzAE0cm-.js} +1 -1
- package/dist/assets/{useEvent-Dfcbgjhg.js → useEvent-BDjgaBtz.js} +1 -1
- package/dist/assets/{useEventListener-D9cftcHj.js → useEventListener-CjDeLqFC.js} +1 -1
- package/dist/assets/{useIframeCapabilities-CwTkczVN.js → useIframeCapabilities-Czr9e4uG.js} +1 -1
- package/dist/assets/{useInstallPackage-BW06eter.js → useInstallPackage-wcDq9hYT.js} +1 -1
- package/dist/assets/{useInterval--x3s1VpI.js → useInterval-DJdQmXnq.js} +1 -1
- package/dist/assets/useNotebookActions-CAMEQ38y.js +1 -0
- package/dist/assets/{useRunCells-DSJPXE-j.js → useRunCells-DGakx8hj.js} +1 -1
- package/dist/assets/{useSplitCell-DQyMF5gd.js → useSplitCell-AK5BKPGM.js} +1 -1
- package/dist/assets/useStateForKey-CGMJQT8x.js +1 -0
- package/dist/assets/{useTheme-n8Dj1zH7.js → useTheme-BAA27lh7.js} +1 -1
- package/dist/assets/{utils-CaQYvazl.js → utils-DfElfBpz.js} +1 -1
- package/dist/assets/vega-component-BeyxAJad.js +1 -0
- package/dist/assets/{vega-embed-container-DJPNkHPb.js → vega-embed-container-DtArSJz1.js} +1 -1
- package/dist/assets/vega-loader.browser-Bu25TKax.js +6 -0
- package/dist/assets/worker-jm2VC7pS.js +159 -0
- package/dist/assets/{write-secret-modal-DzKAInL2.js → write-secret-modal-BEx-EUlh.js} +1 -1
- package/dist/assets/{xychartDiagram-S5SC5T6Z-KbGzG6-u.js → xychartDiagram-S5SC5T6Z-CeRnBS5e.js} +1 -1
- package/dist/index.html +81 -79
- package/package.json +12 -12
- package/src/__tests__/host-imports.test.ts +1 -0
- package/src/__tests__/mount.test.ts +0 -20
- 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__/state.test.ts +2 -2
- package/src/components/chat/acp/blocks.tsx +2 -2
- 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/__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__/date-popover.test.tsx +72 -0
- package/src/components/data-table/__tests__/export-actions.test.tsx +105 -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 +7 -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/row-viewer-panel/row-viewer.tsx +2 -2
- 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 +63 -2
- package/src/components/datasources/__tests__/table-list.test.tsx +118 -0
- package/src/components/datasources/column-preview.tsx +8 -2
- package/src/components/datasources/datasources.tsx +47 -35
- 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/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/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/packages-panel.tsx +4 -7
- 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/command-palette.tsx +22 -10
- 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__/use-file-search.test.tsx +91 -0
- package/src/components/editor/file-tree/file-explorer.tsx +10 -5
- 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/notebook-banner.tsx +1 -0
- package/src/components/editor/output/MarimoTracebackOutput.tsx +2 -2
- 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 +6 -4
- 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.tsx +2 -2
- 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.tsx +2 -2
- package/src/components/ui/alert-dialog.tsx +1 -1
- 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 +483 -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 +158 -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 +42 -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 +139 -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 +22 -0
- package/src/core/document/model.ts +52 -0
- package/src/core/document/select.ts +41 -0
- package/src/core/document/state.ts +374 -0
- package/src/core/document/sync.ts +363 -0
- package/src/core/host/AGENTS.md +12 -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 +41 -0
- package/src/core/host/__tests__/notebook-client.test.ts +511 -132
- package/src/core/host/__tests__/static.test.ts +167 -0
- package/src/core/host/__tests__/wasm.test.ts +152 -0
- package/src/core/host/backend.ts +10 -0
- package/src/core/host/connection.ts +1 -40
- package/src/core/host/notebook-client.ts +204 -133
- package/src/core/host/resources/attachments.ts +22 -0
- package/src/core/host/resources/document.ts +101 -0
- package/src/core/host/static.ts +70 -0
- package/src/core/host/wasm.ts +92 -0
- package/src/core/hotkeys/actions.ts +2 -4
- package/src/core/islands/__tests__/bootstrap.test.ts +0 -26
- package/src/core/islands/bootstrap.ts +0 -1
- package/src/core/islands/components/__tests__/IslandControls.test.tsx +7 -5
- 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 +15 -58
- package/src/core/runtime/runtime.ts +25 -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/bridge.ts +38 -10
- package/src/core/wasm/store.ts +3 -3
- package/src/core/wasm/worker/bootstrap.ts +2 -1
- package/src/core/wasm/worker/getMarimoWheel.ts +1 -1
- package/src/core/wasm/worker/types.ts +11 -0
- package/src/core/wasm/worker/worker.ts +48 -0
- package/src/core/websocket/__tests__/connection-state.test.ts +0 -12
- package/src/core/websocket/__tests__/useNotebookClient.hook.test.tsx +153 -70
- package/src/core/websocket/__tests__/useNotebookSession.hook.test.tsx +151 -428
- package/src/core/websocket/connection-state.ts +0 -10
- package/src/core/websocket/state.ts +7 -0
- package/src/core/websocket/transports/__tests__/notebook.test.ts +502 -0
- package/src/core/websocket/transports/notebook.ts +213 -0
- package/src/core/websocket/useNotebookClient.ts +38 -29
- package/src/core/websocket/useNotebookSession.tsx +1 -14
- 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 +0 -1
- 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__/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 +12 -10
- 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/state/reducer.ts +1 -1
- package/src/plugins/impl/data-frames/DataFramePlugin.tsx +1 -2
- 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 +48 -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 +28 -17
- 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/assets/CellStatus-DVOHpdeJ.js +0 -1
- package/dist/assets/JsonOutput-Cx1iyibB.js +0 -11
- package/dist/assets/MarimoErrorOutput-s_5GOX8k.js +0 -10
- package/dist/assets/RunButton-BKoh0zYG.js +0 -1
- package/dist/assets/__vite-browser-external-CYO_P3S8.js +0 -1
- package/dist/assets/__vite-browser-external-FeDVGZU8.js +0 -1
- package/dist/assets/ai-model-dropdown-i1vfGeVi.js +0 -13
- package/dist/assets/any-language-editor-BdTWdMCI.js +0 -1
- package/dist/assets/arrays-BqUx5USx.js +0 -1
- package/dist/assets/cell-editor-BKJ06H5F.js +0 -21
- package/dist/assets/chat-panel-Cau71Ygr.js +0 -4
- package/dist/assets/chat-ui-C5ql1Up-.js +0 -4
- package/dist/assets/circle-play-CYIb0qT6.js +0 -1
- package/dist/assets/column-preview-BZ_iNhtJ.js +0 -1
- package/dist/assets/command-palette-DDC3kBet.js +0 -1
- package/dist/assets/components-YBmtV0p6.js +0 -1
- package/dist/assets/config-Dy28fDwn.js +0 -1
- package/dist/assets/edit-page-D_Rg0gQO.js +0 -9
- package/dist/assets/feedback-button-Kla7i5RT.js +0 -1
- package/dist/assets/file-explorer-panel-Uk4_QIJQ.js +0 -1
- package/dist/assets/home-page-hmvKlc1w.js +0 -4
- package/dist/assets/index-6b4mf8Ai.css +0 -2
- package/dist/assets/index-ptK1gxmo.js +0 -30
- package/dist/assets/kiosk-mode-BZECfg9k.js +0 -1
- package/dist/assets/linear-0eij_aAG.js +0 -1
- package/dist/assets/link-NuFVR7G9.js +0 -1
- package/dist/assets/links-DkkL9DVa.js +0 -1
- package/dist/assets/numbers-Mk3-CcoU.js +0 -1
- package/dist/assets/packages-panel-CI5ff9b-.js +0 -1
- package/dist/assets/panels-BCcZ_yLK.js +0 -107
- package/dist/assets/precisionRound-Bq_nmf5u.js +0 -1
- package/dist/assets/process-output-f3C3PD0Z.js +0 -1
- package/dist/assets/react-vega-C3rYGtP6.js +0 -189
- package/dist/assets/requests-CgSY1X4a.js +0 -1
- package/dist/assets/run-page-AfZhtEh1.js +0 -1
- package/dist/assets/scratchpad-panel-D2rWg4k8.js +0 -1
- package/dist/assets/session-panel-CnANhcTo.js +0 -1
- package/dist/assets/state-BAJYRhmO.js +0 -1
- package/dist/assets/state-CbjBtkdd.js +0 -3
- package/dist/assets/tooltip-mT3CGest.js +0 -61
- package/dist/assets/tracing-Dz2rYOFL.js +0 -1
- package/dist/assets/tracing-panel-DqQY9V91.js +0 -2
- package/dist/assets/useAsyncData-C7aJJz0Y.js +0 -1
- package/dist/assets/useBoolean-d0Tty94N.js +0 -1
- package/dist/assets/useCellActionButton-DmaFZ4oF.js +0 -1
- package/dist/assets/useDebounce-B_ccBZTp.js +0 -1
- package/dist/assets/useNotebookActions-DHt3s-ia.js +0 -1
- package/dist/assets/vega-component-IouMe13t.js +0 -1
- package/dist/assets/vega-loader.browser-CLRkNgm7.js +0 -6
- package/dist/assets/worker-DTQ7oH2A.js +0 -158
- package/dist/assets/ws-BXWY541V.js +0 -22
- package/src/core/host/__tests__/events.test.ts +0 -46
- package/src/core/host/__tests__/protocol.test.ts +0 -214
- package/src/core/host/events.ts +0 -60
- package/src/core/host/protocol.ts +0 -1943
- package/src/core/state/__tests__/jotai.test.ts +0 -25
- package/src/core/websocket/__tests__/createConnectionTransport.test.ts +0 -49
- 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
|
@@ -1,161 +1,260 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
3
|
import { Logger } from "@/utils/Logger";
|
|
4
|
+
import {
|
|
5
|
+
createNotebookReducer,
|
|
6
|
+
type NotebookEvent,
|
|
7
|
+
NotebookEvents,
|
|
8
|
+
type NotebookTransport,
|
|
9
|
+
type Result,
|
|
10
|
+
StreamStopped,
|
|
11
|
+
} from "@marimo-team/client";
|
|
12
|
+
import type {
|
|
13
|
+
Attachment,
|
|
14
|
+
Change,
|
|
15
|
+
EditResponseOutput,
|
|
16
|
+
NotebookId,
|
|
17
|
+
NotebookOutput,
|
|
18
|
+
} from "@marimo-team/client/protocol";
|
|
4
19
|
import { type Atom, atom, type createStore } from "jotai/vanilla";
|
|
20
|
+
import { z } from "zod";
|
|
5
21
|
import {
|
|
6
|
-
type ConnectionStatus,
|
|
7
|
-
ConnectionState,
|
|
8
22
|
ConnectionClosedReason,
|
|
9
|
-
|
|
10
|
-
type
|
|
11
|
-
TransportReadyState,
|
|
23
|
+
ConnectionState,
|
|
24
|
+
type ConnectionStatus,
|
|
12
25
|
} from "./connection";
|
|
13
|
-
import {
|
|
26
|
+
import { attachments } from "./resources/attachments";
|
|
27
|
+
import { type Document, document } from "./resources/document";
|
|
28
|
+
|
|
29
|
+
/** The kinds the editor reads, with marimo's own notifications. */
|
|
30
|
+
const TYPES = ["notebook", "cell", "document", "attachment", "marimo"] as const;
|
|
31
|
+
|
|
32
|
+
type EditorEvent = NotebookEvent<(typeof TYPES)[number]>;
|
|
33
|
+
|
|
34
|
+
/** A native marimo notification, passed through without interpretation. */
|
|
35
|
+
const MarimoMessage = z.object({ marimo: z.unknown() });
|
|
14
36
|
|
|
15
37
|
/** A connection to a notebook on a marimo host. */
|
|
16
38
|
export interface NotebookClient {
|
|
17
|
-
/**
|
|
39
|
+
/** Client connection status, independent of application session readiness. */
|
|
18
40
|
readonly connectionAtom: Atom<ConnectionStatus>;
|
|
41
|
+
/** Identity and capabilities, available while the stream is connected and ready. */
|
|
42
|
+
readonly notebookAtom: Atom<NotebookOutput | null>;
|
|
43
|
+
/**
|
|
44
|
+
* The most recently confirmed document.
|
|
45
|
+
*
|
|
46
|
+
* Null until a snapshot is complete, after reset or removal, and on
|
|
47
|
+
* connections without host document events. A temporary disconnection
|
|
48
|
+
* retains the last document. Partial updates are not exposed.
|
|
49
|
+
*/
|
|
50
|
+
readonly documentAtom: Atom<Document | null>;
|
|
51
|
+
/** Apply an edit to the notebook captured by the caller, even across a reset. */
|
|
52
|
+
edit(
|
|
53
|
+
notebookId: NotebookId,
|
|
54
|
+
changes: readonly Change[],
|
|
55
|
+
options: { readonly idempotencyKey: string; readonly signal: AbortSignal },
|
|
56
|
+
): Promise<Result<EditResponseOutput>>;
|
|
57
|
+
/** Live attachments, empty until the stream is ready or while disconnected. */
|
|
58
|
+
readonly attachmentsAtom: Atom<readonly Attachment[]>;
|
|
19
59
|
/** Attempt to restore a closed connection after checking host health. */
|
|
20
60
|
reconnect(): Promise<void>;
|
|
21
61
|
}
|
|
22
62
|
|
|
23
|
-
/** The transport and application callbacks needed to
|
|
63
|
+
/** The transport and application callbacks needed to follow a notebook. */
|
|
24
64
|
export interface NotebookConnection {
|
|
25
|
-
/**
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
url: () => string;
|
|
29
|
-
/** Host events require an intact stream; legacy notifications are independent. */
|
|
30
|
-
messageFormat: "host" | "session";
|
|
31
|
-
/** Abort to close the transport and clear the client's state. */
|
|
65
|
+
/** Opens one connection to the notebook's stream. */
|
|
66
|
+
transport: NotebookTransport;
|
|
67
|
+
/** Abort to close the stream and clear the client's state. */
|
|
32
68
|
signal: AbortSignal;
|
|
33
|
-
/** Wait until the host can accept the initial connection. */
|
|
34
|
-
waitUntilReady: () => Promise<void>;
|
|
35
69
|
/** Check whether the host is reachable before an explicit reconnect. */
|
|
36
70
|
checkHealth: () => Promise<boolean>;
|
|
37
|
-
/** Decode wire text without interpreting its event type. */
|
|
38
|
-
decodeJson: (message: string) => unknown;
|
|
39
71
|
/** Handle a native marimo notification. Its payload has not been validated. */
|
|
40
72
|
onNotification: (notification: unknown) => void;
|
|
41
|
-
/** Report a malformed message without coupling the client to editor UI. */
|
|
42
|
-
onMessageError: (error: unknown) => void;
|
|
43
|
-
/** Choose the connection status and retry policy after the transport closes. */
|
|
44
|
-
classifyClose: (event: CloseEvent) => CloseDecision;
|
|
45
73
|
}
|
|
46
74
|
|
|
47
|
-
|
|
48
|
-
|
|
75
|
+
const disconnected = {
|
|
76
|
+
state: ConnectionState.CLOSED,
|
|
77
|
+
code: ConnectionClosedReason.KERNEL_DISCONNECTED,
|
|
78
|
+
reason: "kernel not found",
|
|
79
|
+
} satisfies ConnectionStatus;
|
|
80
|
+
|
|
81
|
+
const malformed = {
|
|
82
|
+
...disconnected,
|
|
83
|
+
reason: "Failed to decode notebook message",
|
|
84
|
+
} satisfies ConnectionStatus;
|
|
85
|
+
|
|
86
|
+
function isStatus(value: unknown): value is ConnectionStatus {
|
|
87
|
+
return typeof value === "object" && value !== null && "state" in value;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* The status to show after the stream stopped.
|
|
92
|
+
*
|
|
93
|
+
* A transport says which status its stop means. An event the protocol
|
|
94
|
+
* rejects is malformed. Anything else means the host was not reached.
|
|
95
|
+
*/
|
|
96
|
+
function statusAfter(error: unknown): ConnectionStatus {
|
|
97
|
+
if (error instanceof StreamStopped && isStatus(error.cause)) {
|
|
98
|
+
return error.cause;
|
|
99
|
+
}
|
|
100
|
+
if (error instanceof StreamStopped && error.cause instanceof z.ZodError) {
|
|
101
|
+
return malformed;
|
|
102
|
+
}
|
|
103
|
+
return disconnected;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Create a client without opening a connection.
|
|
108
|
+
* The session supplies edits authenticated for the same host as its stream.
|
|
109
|
+
*/
|
|
110
|
+
export function createNotebookClient(
|
|
111
|
+
store: ReturnType<typeof createStore>,
|
|
112
|
+
{ edit }: { edit?: NotebookClient["edit"] } = {},
|
|
113
|
+
) {
|
|
114
|
+
// `useNotebookSession` copies this into the editor's shared `connectionAtom`,
|
|
115
|
+
// which gates features until MO-8051 derives availability separately.
|
|
49
116
|
const connectionAtom = atom<ConnectionStatus>({
|
|
50
117
|
state: ConnectionState.NOT_STARTED,
|
|
51
118
|
});
|
|
119
|
+
// One atom holds the whole stream, which resource atoms derive from.
|
|
120
|
+
const reducer = createNotebookReducer({ document, attachments });
|
|
121
|
+
const streamAtom = atom(reducer.initial);
|
|
122
|
+
// A resumed stream is live, but presence waits for this connection's ready.
|
|
123
|
+
const readyAtom = atom(false);
|
|
124
|
+
const presentAtom = atom(
|
|
125
|
+
(get) =>
|
|
126
|
+
get(readyAtom) && get(connectionAtom).state === ConnectionState.OPEN,
|
|
127
|
+
);
|
|
128
|
+
const notebookAtom = atom((get) =>
|
|
129
|
+
get(presentAtom) ? get(streamAtom).notebook : null,
|
|
130
|
+
);
|
|
131
|
+
const documentAtom = atom(
|
|
132
|
+
(get) => get(streamAtom).resources.document.current,
|
|
133
|
+
);
|
|
134
|
+
const attachmentsAtom = atom((get) =>
|
|
135
|
+
get(presentAtom)
|
|
136
|
+
? get(streamAtom).resources.attachments
|
|
137
|
+
: attachments.initial,
|
|
138
|
+
);
|
|
52
139
|
let reconnect: (() => Promise<void>) | undefined;
|
|
53
140
|
const client: NotebookClient = {
|
|
54
141
|
connectionAtom,
|
|
142
|
+
notebookAtom,
|
|
143
|
+
documentAtom,
|
|
144
|
+
edit: async (notebookId, changes, options) => {
|
|
145
|
+
options.signal.throwIfAborted();
|
|
146
|
+
const currentId = store.get(streamAtom).notebookId;
|
|
147
|
+
if (currentId !== null && currentId !== notebookId) {
|
|
148
|
+
throw new Error("The notebook changed before the edit was sent");
|
|
149
|
+
}
|
|
150
|
+
if (edit === undefined) {
|
|
151
|
+
return {
|
|
152
|
+
ok: false,
|
|
153
|
+
status: 501,
|
|
154
|
+
problem: {
|
|
155
|
+
type: "tag:marimo.io,2026:host/unsupported-operation",
|
|
156
|
+
title: "Document editing is unavailable on this connection",
|
|
157
|
+
status: 501,
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
return edit(notebookId, changes, options);
|
|
162
|
+
},
|
|
163
|
+
attachmentsAtom,
|
|
55
164
|
reconnect: async () => reconnect?.(),
|
|
56
165
|
};
|
|
57
166
|
|
|
167
|
+
function setStatus(status: ConnectionStatus) {
|
|
168
|
+
store.set(readyAtom, false);
|
|
169
|
+
store.set(connectionAtom, status);
|
|
170
|
+
}
|
|
171
|
+
|
|
58
172
|
function connect({
|
|
59
|
-
|
|
60
|
-
url,
|
|
61
|
-
messageFormat,
|
|
173
|
+
transport,
|
|
62
174
|
signal,
|
|
63
|
-
waitUntilReady,
|
|
64
175
|
checkHealth,
|
|
65
176
|
onNotification,
|
|
66
|
-
onMessageError,
|
|
67
|
-
decodeJson,
|
|
68
|
-
classifyClose,
|
|
69
177
|
}: NotebookConnection) {
|
|
70
178
|
if (signal.aborted) {
|
|
71
179
|
return;
|
|
72
180
|
}
|
|
73
|
-
let
|
|
74
|
-
let terminal = false;
|
|
181
|
+
let events: NotebookEvents<(typeof TYPES)[number]> | undefined;
|
|
75
182
|
let checkingHealth = false;
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
store.set(connectionAtom, status);
|
|
79
|
-
const disconnected = {
|
|
80
|
-
state: ConnectionState.CLOSED,
|
|
81
|
-
code: ConnectionClosedReason.KERNEL_DISCONNECTED,
|
|
82
|
-
reason: "kernel not found",
|
|
83
|
-
} satisfies ConnectionStatus;
|
|
84
|
-
|
|
85
|
-
function retry(code?: number, reason?: string) {
|
|
86
|
-
if (!signal.aborted && !terminal && shouldRetry) {
|
|
87
|
-
shouldRetry = false;
|
|
88
|
-
transport.reconnect(code, reason);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
183
|
+
// Whether the replica still holds the state the last cursor describes.
|
|
184
|
+
let resumable = true;
|
|
91
185
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
shouldRetry = true;
|
|
97
|
-
setStatus({ state: ConnectionState.OPEN });
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function onClose(event: CloseEvent) {
|
|
101
|
-
if (signal.aborted || terminal) {
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
Logger.warn("Notebook connection closed", event.code, event.reason);
|
|
105
|
-
const decision = classifyClose(event);
|
|
106
|
-
setStatus(decision.status);
|
|
107
|
-
if (decision.kind === "terminal") {
|
|
108
|
-
terminal = true;
|
|
109
|
-
transport.close();
|
|
110
|
-
} else if (decision.kind === "retry") {
|
|
111
|
-
retry(event.code, event.reason);
|
|
112
|
-
} else {
|
|
113
|
-
terminal = true;
|
|
186
|
+
/** Mirror one stream into the atoms until it closes. */
|
|
187
|
+
function follow(cursor?: string) {
|
|
188
|
+
if (cursor === undefined) {
|
|
189
|
+
store.set(streamAtom, reducer.initial);
|
|
114
190
|
}
|
|
191
|
+
resumable = true;
|
|
192
|
+
const current = new NotebookEvents(transport, {
|
|
193
|
+
types: TYPES,
|
|
194
|
+
cursor,
|
|
195
|
+
signal,
|
|
196
|
+
// TODO(MO-8051): Gate editing and running on the notebook's operations
|
|
197
|
+
// and request results instead. The stream's status would then only
|
|
198
|
+
// mark the view as stale.
|
|
199
|
+
onStatus: (status) => {
|
|
200
|
+
if (status === "connecting") {
|
|
201
|
+
setStatus({ state: ConnectionState.CONNECTING });
|
|
202
|
+
} else if (status === "connected") {
|
|
203
|
+
setStatus({ state: ConnectionState.OPEN });
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
current.subscribe(
|
|
208
|
+
(event) => apply(current, event),
|
|
209
|
+
(error) => {
|
|
210
|
+
if (!signal.aborted && current === events) {
|
|
211
|
+
Logger.warn("Notebook stream stopped", error);
|
|
212
|
+
const status = statusAfter(error);
|
|
213
|
+
if (status === malformed) {
|
|
214
|
+
stopMalformed(current);
|
|
215
|
+
} else {
|
|
216
|
+
setStatus(status);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
);
|
|
221
|
+
events = current;
|
|
115
222
|
}
|
|
116
223
|
|
|
117
|
-
function
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
setStatus(
|
|
123
|
-
retry();
|
|
224
|
+
function stopMalformed(source: NotebookEvents<string>) {
|
|
225
|
+
source.close();
|
|
226
|
+
// The cursor can already include an event the replica rejected.
|
|
227
|
+
resumable = false;
|
|
228
|
+
store.set(streamAtom, reducer.initial);
|
|
229
|
+
setStatus(malformed);
|
|
124
230
|
}
|
|
125
231
|
|
|
126
|
-
function
|
|
127
|
-
if (
|
|
232
|
+
function apply(source: NotebookEvents<string>, event: EditorEvent) {
|
|
233
|
+
if (event.event === "marimo") {
|
|
234
|
+
const message = MarimoMessage.safeParse(event.data);
|
|
235
|
+
if (message.success) {
|
|
236
|
+
onNotification(message.data.marimo);
|
|
237
|
+
} else {
|
|
238
|
+
Logger.error("Failed to decode notebook message", event);
|
|
239
|
+
stopMalformed(source);
|
|
240
|
+
}
|
|
128
241
|
return;
|
|
129
242
|
}
|
|
130
|
-
let event;
|
|
131
243
|
try {
|
|
132
|
-
|
|
244
|
+
store.set(streamAtom, reducer.reduce(store.get(streamAtom), event));
|
|
133
245
|
} catch (error) {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
terminal = true;
|
|
140
|
-
setStatus({
|
|
141
|
-
...disconnected,
|
|
142
|
-
reason: "Failed to decode notebook message",
|
|
143
|
-
});
|
|
144
|
-
transport.close();
|
|
246
|
+
// An event the replica cannot apply, such as another notebook's.
|
|
247
|
+
Logger.error("Failed to apply notebook event", error);
|
|
248
|
+
stopMalformed(source);
|
|
145
249
|
return;
|
|
146
250
|
}
|
|
147
|
-
if (event.event === "
|
|
148
|
-
|
|
251
|
+
if (event.event === "ready" || event.event === "reset") {
|
|
252
|
+
store.set(readyAtom, event.event === "ready");
|
|
149
253
|
}
|
|
150
254
|
}
|
|
151
255
|
|
|
152
256
|
reconnect = async () => {
|
|
153
|
-
if (
|
|
154
|
-
signal.aborted ||
|
|
155
|
-
checkingHealth ||
|
|
156
|
-
transport.readyState === TransportReadyState.OPEN ||
|
|
157
|
-
transport.readyState === TransportReadyState.CONNECTING
|
|
158
|
-
) {
|
|
257
|
+
if (signal.aborted || checkingHealth || events?.status !== "closed") {
|
|
159
258
|
return;
|
|
160
259
|
}
|
|
161
260
|
checkingHealth = true;
|
|
@@ -166,17 +265,13 @@ export function createNotebookClient(store: ReturnType<typeof createStore>) {
|
|
|
166
265
|
return;
|
|
167
266
|
}
|
|
168
267
|
if (!healthy) {
|
|
169
|
-
shouldRetry = false;
|
|
170
268
|
setStatus(disconnected);
|
|
171
269
|
return;
|
|
172
270
|
}
|
|
173
|
-
|
|
174
|
-
shouldRetry = true;
|
|
175
|
-
transport.reconnect();
|
|
271
|
+
follow(resumable ? (events.cursor ?? undefined) : undefined);
|
|
176
272
|
} catch (error) {
|
|
177
273
|
if (!signal.aborted) {
|
|
178
274
|
Logger.error("Notebook health check failed", error);
|
|
179
|
-
shouldRetry = false;
|
|
180
275
|
setStatus(disconnected);
|
|
181
276
|
}
|
|
182
277
|
} finally {
|
|
@@ -187,39 +282,15 @@ export function createNotebookClient(store: ReturnType<typeof createStore>) {
|
|
|
187
282
|
signal.addEventListener(
|
|
188
283
|
"abort",
|
|
189
284
|
() => {
|
|
190
|
-
transport.removeEventListener("open", onOpen);
|
|
191
|
-
transport.removeEventListener("close", onClose);
|
|
192
|
-
transport.removeEventListener("error", onError);
|
|
193
|
-
transport.removeEventListener("message", onMessage);
|
|
194
|
-
transport.close();
|
|
195
285
|
reconnect = undefined;
|
|
286
|
+
store.set(streamAtom, reducer.initial);
|
|
196
287
|
setStatus({ state: ConnectionState.NOT_STARTED });
|
|
197
288
|
},
|
|
198
289
|
{ once: true },
|
|
199
290
|
);
|
|
200
291
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
transport.addEventListener("error", onError);
|
|
204
|
-
transport.addEventListener("message", onMessage);
|
|
205
|
-
|
|
206
|
-
if (transport.readyState === TransportReadyState.CLOSED) {
|
|
207
|
-
setStatus({ state: ConnectionState.CONNECTING });
|
|
208
|
-
void waitUntilReady()
|
|
209
|
-
.then(() => {
|
|
210
|
-
if (!signal.aborted) {
|
|
211
|
-
transport.reconnect();
|
|
212
|
-
}
|
|
213
|
-
})
|
|
214
|
-
.catch((error) => {
|
|
215
|
-
if (!signal.aborted) {
|
|
216
|
-
Logger.error("Healthy connection never made", error);
|
|
217
|
-
terminal = true;
|
|
218
|
-
setStatus(disconnected);
|
|
219
|
-
transport.close();
|
|
220
|
-
}
|
|
221
|
-
});
|
|
222
|
-
}
|
|
292
|
+
setStatus({ state: ConnectionState.CONNECTING });
|
|
293
|
+
follow();
|
|
223
294
|
}
|
|
224
295
|
|
|
225
296
|
return { client, connect };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import type { Attachment } from "@marimo-team/client/protocol";
|
|
4
|
+
import type { NotebookResource } from "@marimo-team/client";
|
|
5
|
+
|
|
6
|
+
/** Attachment records retained with the stream's resume cursor. */
|
|
7
|
+
export const attachments: NotebookResource<
|
|
8
|
+
readonly Attachment[],
|
|
9
|
+
"attachment"
|
|
10
|
+
> = {
|
|
11
|
+
initial: [],
|
|
12
|
+
on: {
|
|
13
|
+
attachment: (state, { data: { attachment } }) =>
|
|
14
|
+
state.some((item) => item.id === attachment.id)
|
|
15
|
+
? state.map((item) => (item.id === attachment.id ? attachment : item))
|
|
16
|
+
: [...state, attachment],
|
|
17
|
+
"attachment.removed": (state, { data }) =>
|
|
18
|
+
state.some((item) => item.id === data.id)
|
|
19
|
+
? state.filter((item) => item.id !== data.id)
|
|
20
|
+
: state,
|
|
21
|
+
},
|
|
22
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import type * as Protocol from "@marimo-team/client/protocol";
|
|
4
|
+
import type { NotebookResource } from "@marimo-team/client";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The notebook's content at one revision.
|
|
8
|
+
*
|
|
9
|
+
* The revision record carries the cell order, app config, etags, and
|
|
10
|
+
* version. Each cell in its order has a record in `cells`.
|
|
11
|
+
*/
|
|
12
|
+
export interface Document {
|
|
13
|
+
readonly revision: Protocol.Document;
|
|
14
|
+
readonly cells: ReadonlyMap<Protocol.CellId, Protocol.Cell>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** The records of a revision still being received. */
|
|
18
|
+
interface PendingDocument {
|
|
19
|
+
/** Null until the `document` event that closes the group arrives. */
|
|
20
|
+
readonly revision: Protocol.Document | null;
|
|
21
|
+
readonly cells: ReadonlyMap<Protocol.CellId, Protocol.Cell>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The confirmed document and the revision being assembled after it.
|
|
26
|
+
*
|
|
27
|
+
* A snapshot's records are pending until `ready`. A live revision is pending
|
|
28
|
+
* from its first cell until its `document` event.
|
|
29
|
+
*/
|
|
30
|
+
interface DocumentState {
|
|
31
|
+
readonly current: Document | null;
|
|
32
|
+
readonly pending: PendingDocument | null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function pendingOf(state: DocumentState): PendingDocument {
|
|
36
|
+
return (
|
|
37
|
+
state.pending ?? {
|
|
38
|
+
revision: state.current?.revision ?? null,
|
|
39
|
+
cells: state.current?.cells ?? new Map(),
|
|
40
|
+
}
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Confirm a revision whose records are complete. */
|
|
45
|
+
function publish(
|
|
46
|
+
revision: Protocol.Document,
|
|
47
|
+
cells: ReadonlyMap<Protocol.CellId, Protocol.Cell>,
|
|
48
|
+
): DocumentState {
|
|
49
|
+
const order = new Set(revision.order);
|
|
50
|
+
if (
|
|
51
|
+
order.size !== revision.order.length ||
|
|
52
|
+
order.size !== cells.size ||
|
|
53
|
+
[...order].some((id) => !cells.has(id)) ||
|
|
54
|
+
[...cells.values()].some((cell) => cell.version > revision.version)
|
|
55
|
+
) {
|
|
56
|
+
throw new Error("Document records do not match its revision and order");
|
|
57
|
+
}
|
|
58
|
+
return { current: { revision, cells }, pending: null };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export const document: NotebookResource<DocumentState, "cell" | "document"> = {
|
|
62
|
+
initial: { current: null, pending: null },
|
|
63
|
+
on: {
|
|
64
|
+
cell: (state, { data }) => {
|
|
65
|
+
const pending = pendingOf(state);
|
|
66
|
+
return {
|
|
67
|
+
...state,
|
|
68
|
+
pending: {
|
|
69
|
+
revision: pending.revision,
|
|
70
|
+
cells: new Map(pending.cells).set(data.cell.id, data.cell),
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
},
|
|
74
|
+
"cell.removed": (state, { data }) => {
|
|
75
|
+
const pending = pendingOf(state);
|
|
76
|
+
if (!pending.cells.has(data.id)) {
|
|
77
|
+
return state;
|
|
78
|
+
}
|
|
79
|
+
const cells = new Map(pending.cells);
|
|
80
|
+
cells.delete(data.id);
|
|
81
|
+
return { ...state, pending: { ...pending, cells } };
|
|
82
|
+
},
|
|
83
|
+
document: (state, { data }, { phase }) => {
|
|
84
|
+
const { cells } = pendingOf(state);
|
|
85
|
+
return phase === "snapshot"
|
|
86
|
+
? { ...state, pending: { revision: data.document, cells } }
|
|
87
|
+
: publish(data.document, cells);
|
|
88
|
+
},
|
|
89
|
+
ready: (state, _event, { phase }) => {
|
|
90
|
+
if (state.pending === null) {
|
|
91
|
+
return state;
|
|
92
|
+
}
|
|
93
|
+
if (phase === "live") {
|
|
94
|
+
throw new Error("Stream ended with an incomplete document update");
|
|
95
|
+
}
|
|
96
|
+
return state.pending.revision === null
|
|
97
|
+
? { current: null, pending: null }
|
|
98
|
+
: publish(state.pending.revision, state.pending.cells);
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import type * as Protocol from "@marimo-team/client/protocol";
|
|
4
|
+
import type { NotebookBackend } from "./backend";
|
|
5
|
+
|
|
6
|
+
/** A document snapshot with its own identity and revision metadata. */
|
|
7
|
+
export interface StaticNotebookSnapshot {
|
|
8
|
+
readonly notebook: Omit<Protocol.Notebook, "operations">;
|
|
9
|
+
readonly document: Protocol.Document;
|
|
10
|
+
readonly cells: readonly Protocol.Cell[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Serve a document without a server, runtime, or mutation operations.
|
|
15
|
+
* Callers and readers must treat the snapshot records as immutable.
|
|
16
|
+
*/
|
|
17
|
+
export function createStaticNotebookBackend(
|
|
18
|
+
snapshot: StaticNotebookSnapshot,
|
|
19
|
+
): NotebookBackend {
|
|
20
|
+
const frames: Protocol.Event[] = [
|
|
21
|
+
{
|
|
22
|
+
event: "notebook",
|
|
23
|
+
data: { notebook: { ...snapshot.notebook, operations: [] } },
|
|
24
|
+
},
|
|
25
|
+
...snapshot.cells.map((cell): Protocol.CellEvent => ({
|
|
26
|
+
event: "cell",
|
|
27
|
+
data: { cell },
|
|
28
|
+
})),
|
|
29
|
+
{ event: "document", data: { document: snapshot.document } },
|
|
30
|
+
{ event: "ready", id: "static", data: {} },
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
transport: ({ cursor, signal }, { open, message }) =>
|
|
35
|
+
new Promise((resolve) => {
|
|
36
|
+
if (signal.aborted) {
|
|
37
|
+
resolve();
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
// Finishing would ask NotebookEvents to reconnect. A snapshot stays open
|
|
41
|
+
// until its reader closes, even though there are no more updates.
|
|
42
|
+
signal.addEventListener("abort", () => resolve(), { once: true });
|
|
43
|
+
open();
|
|
44
|
+
if (cursor !== null && !signal.aborted) {
|
|
45
|
+
message({ event: "reset", data: {} });
|
|
46
|
+
}
|
|
47
|
+
for (const frame of frames) {
|
|
48
|
+
if (signal.aborted) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
message(frame);
|
|
52
|
+
}
|
|
53
|
+
}),
|
|
54
|
+
client: {
|
|
55
|
+
edit: async (_changes, options) => {
|
|
56
|
+
options?.signal?.throwIfAborted();
|
|
57
|
+
return {
|
|
58
|
+
ok: false,
|
|
59
|
+
status: 501,
|
|
60
|
+
problem: {
|
|
61
|
+
type: "tag:marimo.io,2026:host/unsupported-operation",
|
|
62
|
+
title: "Unsupported operation",
|
|
63
|
+
status: 501,
|
|
64
|
+
detail: "Static notebooks do not support editing",
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|