@marimo-team/islands 0.25.3-dev19 → 0.25.3-dev192
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-BBbOElk3.js} +21 -19
- package/dist/{ErrorBoundary-CBHB755J.js → ErrorBoundary-Dw6uRKVk.js} +19 -19
- package/dist/{abnfDiagram-VCTEODGH-D-Ez91gV.js → abnfDiagram-VCTEODGH-DhMT6Uhd.js} +2 -2
- package/dist/any-language-editor-BVFHDDQ3.js +75 -0
- package/dist/{architectureDiagram-5GKGNRK7-CTpcNtO8.js → architectureDiagram-5GKGNRK7-BVbEiJLq.js} +1 -1
- package/dist/{arrays-CN1djC4k.js → arrays-R6JsImiT.js} +8 -0
- package/dist/assets/__vite-browser-external-C8gQho07.js +1 -0
- package/dist/assets/worker-C6e9ERyd.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-BguxTQEL.js} +541 -540
- package/dist/{check-EDwSwP89.js → check-C9Mp9eCK.js} +1 -1
- package/dist/{chunk-IMKFNOWR-DGxi-zeo.js → chunk-IMKFNOWR-By5hSbJJ.js} +1 -1
- package/dist/{chunk-SHT3W25Y-CV9J3rAB.js → chunk-SHT3W25Y-DFrEYG54.js} +1 -1
- package/dist/{chunk-SVP7TREG-BL-9Fqgg.js → chunk-SVP7TREG-DFXGc9ew.js} +1 -1
- package/dist/{chunk-TICWLB2K-Cu_XMjDM.js → chunk-TICWLB2K-DZdNIpoZ.js} +1 -1
- package/dist/{chunk-YOKDWASO-DPZk8vhS.js → chunk-YOKDWASO-DKplTrBY.js} +20 -20
- package/dist/{classDiagram-ZZMXUADV-BXXV13Hq.js → classDiagram-ZZMXUADV-Cgj_eEQA.js} +2 -2
- package/dist/{classDiagram-v2-VYDZK3BY-B7FXJ1Cp.js → classDiagram-v2-VYDZK3BY-DLAUrSqj.js} +2 -2
- package/dist/{code-visibility-CEXXuuOE.js → code-visibility-v_7ijLtx.js} +25 -25
- package/dist/{common-OLE92qsR.js → common-BUwWHpnK.js} +6994 -6819
- package/dist/{copy-icon-Ft7t9bqz.js → copy-icon-De584pZ4.js} +3 -3
- package/dist/{cynefinDiagram-5FMLGOSQ-CH96rwba.js → cynefinDiagram-5FMLGOSQ-Cikf9AKa.js} +1 -1
- package/dist/{dagre-GXQ25YYZ-BD3PmpFY.js → dagre-GXQ25YYZ-C5KEi5_l.js} +1 -1
- package/dist/{data-grid-overlay-editor-CpHX98zN.js → data-grid-overlay-editor-4IZymnXZ.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-BaoBTQjR.js} +1 -1
- package/dist/{diagram-UQ7AKVKN-muQtNZc4.js → diagram-UQ7AKVKN-DDufBsUA.js} +1 -1
- package/dist/{diagram-VX7I27RA-CdhuZAqR.js → diagram-VX7I27RA-CY6RALJ8.js} +1 -1
- package/dist/{diagram-Z3DM3KII-BD8mXjOj.js → diagram-Z3DM3KII-Bc-LHsD6.js} +1 -1
- package/dist/{dialog-CEBFZNyc.js → dialog-DVRfQuhD.js} +25 -25
- package/dist/{dist-F8Ep36rk.js → dist-B1M0bGO3.js} +1 -1
- package/dist/{dist-D6d86eNJ.js → dist-BA13RJj5.js} +1 -1
- package/dist/{dist-Bvs24R_E.js → dist-BJtmSgcE.js} +48 -48
- package/dist/{dist-CN_BeG2I.js → dist-Ba2VIn1K.js} +53 -53
- package/dist/{dist-PXYI_yuP.js → dist-Bj4bmwUm.js} +12 -12
- package/dist/{dist-DoE4XJBC.js → dist-BmS-ZGjl.js} +8626 -8626
- package/dist/{dist-BorMD-uX.js → dist-Bzogh7CW.js} +87 -87
- package/dist/{dist-CN8zI-XC.js → dist-CThPVKaP.js} +1385 -1382
- package/dist/{dist-DiDibOYr.js → dist-CTi5FTiV.js} +87 -87
- package/dist/{dist-diIx5qJW.js → dist-Ck_C32ye.js} +2 -2
- package/dist/{dist-BLfKmR-l.js → dist-D1fnFp1J.js} +1 -1
- package/dist/{dist-OR1OnN-y.js → dist-D3x9__Fu.js} +46 -46
- package/dist/{dist-BMQf5zIS.js → dist-D7mrh5sO.js} +36 -36
- package/dist/{dist-CSJ0MDRw.js → dist-D9Q3xjiP.js} +1 -1
- package/dist/{dist-BuaRHxOc.js → dist-DP60Ubh8.js} +2 -2
- package/dist/{dist-CSbh1PNP.js → dist-DTv9KkpW.js} +12 -12
- package/dist/{dist-D-wwqlzJ.js → dist-DVVwOfrD.js} +109 -109
- package/dist/{dist-C8fAKSH1.js → dist-DYXM73-k.js} +36 -36
- package/dist/{dist-DBtSl3sf.js → dist-DjeybPOb.js} +1 -1
- package/dist/{dist-CrSP_H1M.js → dist-DtGn9N4h.js} +197 -197
- package/dist/{dist-CLdpGQVr.js → dist-KN80qWCP.js} +2 -2
- package/dist/{dist-DpBpA_n1.js → dist-Lm-D-Vr6.js} +3 -3
- package/dist/{dist-Cg7gzw-a.js → dist-O0qFpEJh.js} +56 -56
- package/dist/{dist-CkTTIdBe.js → dist-OUle2RT7.js} +207 -207
- package/dist/{dist-BK4Oj99o.js → dist-T5rh6JvL.js} +1 -1
- package/dist/{dropdown-menu-CP_qCYHC.js → dropdown-menu-drDjxJyV.js} +1481 -1433
- package/dist/{ebnfDiagram-PWID7BFC-BIN1sweE.js → ebnfDiagram-PWID7BFC-Cz0J-tcC.js} +2 -2
- package/dist/{erDiagram-RLTQ6QDP-Ctp5cfoN.js → erDiagram-RLTQ6QDP-DTXTDgFu.js} +1 -1
- package/dist/{error-banner-D-WCIiMt.js → error-banner-DxJ3s8YO.js} +86 -86
- package/dist/{error-entries-Bv6bZPF3.js → error-entries-CZ1NQ6Kv.js} +4771 -4702
- package/dist/{esm-D2gTsXEV.js → esm-Bbw_Njjx.js} +575 -575
- package/dist/{esm-AMFssoNp.js → esm-mDQNEFe_.js} +354 -354
- package/dist/{feedback-button-_f7fx7zj.js → feedback-button-MpeoIn1D.js} +37 -37
- package/dist/{flowDiagram-HODETNUW-3EfLrlFb.js → flowDiagram-HODETNUW-RbwWf2zc.js} +2 -2
- package/dist/{formats-27CW_UP4.js → formats-DbXnijtH.js} +3 -3
- package/dist/{ganttDiagram-EL5Y4UJY-BQT16JSc.js → ganttDiagram-EL5Y4UJY-Dh_4cBJZ.js} +2 -2
- package/dist/{glide-data-editor-Cmu4vWKD.js → glide-data-editor-DU8_EDG5.js} +370 -368
- package/dist/{glide-utils-DJyqh0EM.js → glide-utils-D98laidb.js} +16 -16
- package/dist/{globals-1Hfhz6Dx.js → globals-BeBDOk22.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-D_4_hQ8U.js} +668 -622
- package/dist/{infoDiagram-27XIBGKW-DipQoHxl.js → infoDiagram-27XIBGKW-DlmzZjmI.js} +1 -1
- package/dist/{ishikawaDiagram-5VMMS53U-DgFMjSGz.js → ishikawaDiagram-5VMMS53U-EN3TYP_q.js} +1 -1
- package/dist/{kanban-definition-UXKFOSKX-w1ACVjgl.js → kanban-definition-UXKFOSKX-CK_-Ru7Q.js} +1 -1
- package/dist/{label-BDKF3Afc.js → label-Qvw1E97W.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 +9822 -7824
- package/dist/{mermaid-CHo49Yv-.js → mermaid-DU9ZRmkG.js} +38 -38
- package/dist/mermaid-HWGCJPDP-Du3-HPQA.js +2 -0
- package/dist/{mindmap-definition-YA3MSWOX-CrT0KSfX.js → mindmap-definition-YA3MSWOX-C-hqgUvF.js} +1 -1
- package/dist/{number-overlay-editor-BQN3-Nye.js → number-overlay-editor-BoIkyh6H.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-BiMejYPB.js} +2 -2
- package/dist/{pieDiagram-E7YTZNPT-BfxJ_XCc.js → pieDiagram-E7YTZNPT-B1zCZ1eq.js} +1 -1
- package/dist/precisionRound-DhAzeRcm.js +49 -0
- package/dist/{process-output-BZg56CCb.js → process-output-DxSCHBWp.js} +4 -4
- package/dist/{quadrantDiagram-AXDQQJYC-Ccw0xMjN.js → quadrantDiagram-AXDQQJYC-Blrm7V7_.js} +1 -1
- package/dist/{railroadDiagram-O6MQD6OU-8vFLVTPu.js → railroadDiagram-O6MQD6OU-BLcfaASl.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-DtBOf52-.js} +1 -1
- package/dist/{reveal-component-nqUW7zgn.js → reveal-component-5M3a2rpj.js} +734 -711
- package/dist/{select-BButFi-d.js → select-2KOiReNY.js} +536 -457
- package/dist/{sequenceDiagram-WJ2MYXX4-CXFMnxGh.js → sequenceDiagram-WJ2MYXX4-DIwF19_n.js} +1 -1
- package/dist/{spec-BwarBpUJ.js → spec-DZDnnlgT.js} +41 -11
- package/dist/{stateDiagram-D77RDMKH-DQeA_CwI.js → stateDiagram-D77RDMKH-sWk1AdYL.js} +2 -2
- package/dist/{stateDiagram-v2-MP3YSRHH-BxxQucm1.js → stateDiagram-v2-MP3YSRHH-CiNo-OI7.js} +2 -2
- package/dist/style.css +1 -1
- package/dist/{swimlanes-42K2YHIH-Do6L-cYg.js → swimlanes-42K2YHIH-Bj3hiOY5.js} +1 -1
- package/dist/{swimlanesDiagram-VR7AAH4N-DUzPvSVU.js → swimlanesDiagram-VR7AAH4N-BzEo2r_N.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-CrPQk42H.js} +1 -1
- package/dist/{toDate-UtiwmnH2.js → toDate-Cu2pIZcC.js} +37 -36
- package/dist/{toast-B9UMEi8u.js → toast-B5t9o6s_.js} +219 -201
- package/dist/{tooltip-BO1REbqs.js → tooltip-s4zZxwKe.js} +325 -301
- package/dist/{use-restore-focus-BEM7sFWl.js → use-restore-focus-BgDTd2tc.js} +1482 -1433
- package/dist/{useAsyncData-Deh5kszT.js → useAsyncData-DEhLdRIu.js} +15 -14
- package/dist/{useDateFormatter-D8XruNGG.js → useDateFormatter-D3Tkfzej.js} +2 -2
- package/dist/{useDeepCompareMemoize-DXwzRINI.js → useDeepCompareMemoize-RjZGVhBV.js} +1 -1
- package/dist/{useIframeCapabilities-DBETvvPl.js → useIframeCapabilities-CSdWn6NP.js} +1 -1
- package/dist/{useLifecycle-j7D9uD1o.js → useLifecycle-DtcB2s1z.js} +28 -25
- package/dist/{useTheme-CRnjkku0.js → useTheme-CoEtr4mK.js} +301 -297
- package/dist/{vega-component-BeF9dfyO.js → vega-component-BhjTrP9-.js} +148 -148
- 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-BaYMoIpj.js} +1 -1
- package/dist/{wardleyDiagram-VM6X3IG4-48cUWkfA.js → wardleyDiagram-VM6X3IG4-DjHeuj8p.js} +1 -1
- package/dist/{xychartDiagram-S5SC5T6Z-CswYgYeu.js → xychartDiagram-S5SC5T6Z-Bc_hcuXS.js} +2 -2
- package/package.json +16 -16
- package/src/__mocks__/document.tsx +54 -0
- package/src/__mocks__/notebook.ts +2 -1
- package/src/__mocks__/requests.ts +0 -3
- package/src/__tests__/module-entry.test.ts +44 -0
- package/src/__tests__/mount.test.ts +1 -21
- package/src/__tests__/{host-imports.test.ts → replica-imports.test.ts} +12 -11
- 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/app-config/user-config-form.tsx +35 -0
- package/src/components/chat/__tests__/message-queue.test.tsx +17 -0
- package/src/components/chat/__tests__/notebook-access-selector.test.tsx +64 -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 +20 -101
- package/src/components/chat/chat-utils.ts +4 -8
- package/src/components/chat/notebook-access-selector.stories.tsx +22 -0
- package/src/components/chat/notebook-access-selector.tsx +100 -0
- package/src/components/data-table/TableBottomBar.tsx +4 -1
- 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__/pagination.test.tsx +28 -1
- 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 +17 -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/pagination.tsx +44 -33
- 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 +96 -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 +17 -12
- package/src/components/datasources/datasources.tsx +55 -42
- 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/minimap-content.tsx +2 -6
- package/src/components/dependency-graph/panels.tsx +4 -2
- 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__/Output.test.tsx +50 -32
- package/src/{core/attachments/__tests__/toast.test.tsx → components/editor/__tests__/agent-toast.test.tsx} +1 -1
- package/src/components/editor/__tests__/data-attributes.test.tsx +28 -13
- package/src/components/editor/__tests__/dynamic-favicon.test.tsx +111 -18
- package/src/components/editor/__tests__/output-persistence.test.tsx +6 -1
- package/src/components/editor/__tests__/package-alert.test.tsx +22 -1
- package/src/components/editor/actions/__tests__/name-cell-input.test.tsx +69 -0
- package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +34 -8
- package/src/components/editor/actions/__tests__/pair-with-agent.test.tsx +2 -6
- package/src/components/editor/actions/name-cell-input.tsx +4 -3
- package/src/components/editor/actions/pair-with-agent-commands.ts +2 -2
- package/src/components/editor/actions/pair-with-agent-modal.tsx +19 -16
- package/src/components/editor/actions/useCellActionButton.tsx +20 -26
- package/src/components/editor/actions/useNotebookActions.tsx +14 -19
- package/src/components/editor/actions/useRestartKernel.tsx +1 -1
- package/src/components/editor/actions/useSetCodeVisibility.ts +10 -11
- package/src/components/editor/actions/useSetOutputsExpanded.ts +13 -18
- 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/__tests__/connection-notice.test.tsx +1 -1
- package/src/components/editor/alerts/connection-notice.tsx +1 -1
- package/src/components/editor/alerts/startup-logs-alert.tsx +1 -0
- package/src/components/editor/app-container.tsx +1 -1
- package/src/components/editor/boundary/__tests__/ErrorBoundary.test.tsx +1 -1
- package/src/components/editor/cell/CreateCellButton.tsx +15 -21
- package/src/components/editor/cell/DeleteButton.tsx +3 -2
- package/src/components/editor/cell/RunButton.tsx +2 -2
- package/src/components/editor/cell/StagedAICell.tsx +10 -2
- package/src/components/editor/cell/StopButton.tsx +2 -2
- package/src/components/editor/cell/__tests__/StagedAICell.test.tsx +3 -1
- package/src/components/editor/cell/__tests__/useShouldShowInterrupt.test.ts +39 -0
- package/src/components/editor/cell/cell-actions.tsx +12 -1
- package/src/components/editor/cell/code/__tests__/language-toggle.test.tsx +54 -0
- package/src/components/editor/cell/code/cell-editor.tsx +22 -36
- 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/useAddCell.ts +4 -8
- package/src/components/editor/cell/useDeleteCell.tsx +10 -36
- package/src/components/editor/cell/useRunCells.ts +2 -5
- package/src/components/editor/cell/useShouldShowInterrupt.ts +2 -12
- package/src/components/editor/cell/useSplitCell.tsx +2 -1
- package/src/components/editor/chrome/components/__tests__/feedback-button.test.tsx +1 -1
- package/src/components/editor/chrome/components/feedback-button.tsx +1 -1
- package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +31 -2
- package/src/components/editor/chrome/panels/__tests__/runtime-details.test.tsx +1 -1
- package/src/components/editor/chrome/panels/__tests__/sandbox-recovery.test.tsx +1 -1
- package/src/components/editor/chrome/panels/cache-panel.tsx +1 -0
- package/src/components/editor/chrome/panels/logs-panel.tsx +2 -1
- 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 +10 -12
- package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +9 -18
- package/src/components/editor/chrome/wrapper/__tests__/document-sync-status.test.tsx +92 -0
- package/src/components/editor/chrome/wrapper/__tests__/pending-ai-cells.test.tsx +61 -2
- package/src/components/editor/chrome/wrapper/footer-items/attachment-status.tsx +4 -4
- package/src/components/editor/chrome/wrapper/footer-items/backend-status.tsx +1 -1
- package/src/components/editor/chrome/wrapper/footer-items/document-sync-status.tsx +54 -0
- package/src/components/editor/chrome/wrapper/footer-items/lsp-status.tsx +1 -1
- package/src/components/editor/chrome/wrapper/footer-items/machine-stats.tsx +1 -1
- package/src/components/editor/chrome/wrapper/footer-items/runtime-settings.tsx +1 -7
- package/src/components/editor/chrome/wrapper/footer.tsx +2 -0
- 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/columns/sortable-column.tsx +1 -1
- package/src/components/editor/connections/components.tsx +5 -10
- package/src/components/editor/controls/Controls.tsx +3 -3
- 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/__tests__/notebook-menu-dropdown.test.tsx +10 -4
- package/src/components/editor/controls/command-palette.tsx +22 -10
- package/src/components/editor/controls/keyboard-shortcuts.tsx +14 -5
- package/src/components/editor/documentation.css +34 -11
- package/src/components/editor/dynamic-favicon.tsx +21 -40
- package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +9 -23
- package/src/components/editor/errors/auto-fix.tsx +4 -8
- package/src/components/editor/errors/send-error-report-button.tsx +3 -6
- 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 +48 -15
- 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/__tests__/status.test.tsx +5 -3
- package/src/components/editor/header/app-header.tsx +1 -1
- package/src/components/editor/header/status.tsx +40 -20
- package/src/components/editor/links/__tests__/cell-link.test.tsx +148 -0
- package/src/components/editor/links/cell-link.tsx +16 -6
- package/src/components/editor/navigation/__tests__/clipboard.test.ts +60 -38
- package/src/components/editor/navigation/__tests__/navigation.test.ts +73 -64
- package/src/components/editor/navigation/clipboard.ts +12 -11
- package/src/components/editor/navigation/focus-utils.ts +4 -4
- package/src/components/editor/navigation/multi-cell-action-toolbar.tsx +11 -20
- package/src/components/editor/navigation/navigation.ts +20 -28
- package/src/components/editor/notebook-banner.tsx +1 -0
- package/src/components/editor/notebook-cell.tsx +19 -13
- package/src/components/editor/output/MarimoErrorOutput.tsx +2 -1
- package/src/components/editor/output/MarimoTracebackOutput.tsx +19 -17
- package/src/components/editor/output/Outputs.css +8 -0
- 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/output/console/__tests__/ConsoleOutput.test.tsx +125 -69
- package/src/components/editor/package-alert.tsx +14 -7
- package/src/components/editor/renderers/cell-array.tsx +10 -17
- 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/markdown/markdown-renderer.tsx +4 -8
- 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/edit-page.tsx +32 -17
- package/src/components/pages/home-page.tsx +164 -91
- package/src/components/scratchpad/scratchpad.test.tsx +97 -0
- package/src/components/scratchpad/scratchpad.tsx +20 -10
- package/src/components/slides/__tests__/minimap-actions.test.tsx +9 -25
- package/src/components/slides/__tests__/slide-scroll-hint.test.tsx +37 -0
- package/src/components/slides/minimap.tsx +7 -3
- package/src/components/slides/reveal-component.tsx +57 -25
- package/src/components/slides/reveal-slides.css +5 -4
- package/src/components/slides/slide-cell-view.tsx +4 -4
- package/src/components/slides/slide-form.tsx +1 -0
- package/src/components/slides/slide-scroll-hint.tsx +3 -1
- package/src/components/sort/SortableCellsProvider.tsx +2 -1
- 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/__tests__/staged-cells.test.ts +26 -25
- package/src/core/ai/context/providers/datasource.ts +6 -2
- package/src/core/ai/staged-cells.ts +15 -18
- package/src/core/ai/tools/__tests__/edit-notebook-tool.test.ts +6 -22
- package/src/core/ai/tools/__tests__/registry.test.ts +17 -0
- package/src/core/ai/tools/base.ts +18 -15
- package/src/core/ai/tools/edit-notebook-tool.ts +75 -71
- package/src/core/ai/tools/registry.ts +13 -10
- package/src/core/ai/tools/sample-tool.ts +6 -7
- package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +4 -0
- package/src/core/cells/__tests__/actions.test.ts +474 -34
- package/src/core/cells/__tests__/add-missing-import.test.ts +7 -8
- package/src/core/cells/__tests__/cell.test.ts +39 -0
- package/src/core/cells/__tests__/cells.test.ts +260 -1165
- package/src/core/cells/__tests__/collapseConsoleOutputs.test.ts +54 -0
- package/src/core/cells/__tests__/document.test.ts +414 -0
- package/src/core/cells/__tests__/flush-document.test.ts +40 -0
- package/src/core/cells/__tests__/notebook-outline.test.ts +114 -0
- package/src/core/cells/__tests__/outputs.test.tsx +71 -53
- package/src/core/cells/__tests__/session.test.ts +117 -36
- package/src/core/cells/__tests__/with-document-save.test.ts +67 -0
- package/src/core/cells/actions.ts +391 -3
- package/src/core/cells/add-missing-import.ts +46 -42
- package/src/core/cells/cell.ts +14 -3
- package/src/core/cells/cells.ts +99 -599
- package/src/core/cells/columns.ts +38 -0
- package/src/core/cells/document.ts +392 -0
- package/src/core/cells/outputs.ts +12 -28
- package/src/core/cells/pending-delete-service.ts +2 -6
- package/src/core/cells/session.ts +1 -1
- package/src/core/cells/types.ts +22 -1
- package/src/core/codemirror/__tests__/setup.test.ts +29 -29
- package/src/core/codemirror/ai/__tests__/resources.test.ts +117 -0
- package/src/core/codemirror/ai/resources.ts +1 -1
- package/src/core/codemirror/cells/__tests__/extensions.test.ts +42 -1
- package/src/core/codemirror/cells/extensions.ts +27 -9
- package/src/core/codemirror/cells/state.ts +1 -1
- package/src/core/codemirror/format.ts +6 -5
- package/src/core/codemirror/keymaps/vim.ts +4 -8
- package/src/core/codemirror/language/__tests__/sql.test.ts +22 -20
- package/src/core/codemirror/language/__tests__/utils.test.ts +187 -15
- package/src/core/codemirror/language/cell-code.ts +114 -0
- package/src/core/codemirror/language/extension.ts +52 -35
- 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 +16 -24
- package/src/core/codemirror/language/panel/panel.tsx +1 -0
- package/src/core/codemirror/language/types.ts +7 -7
- package/src/core/codemirror/language/utils.ts +9 -39
- package/src/core/codemirror/lsp/notebook-lsp.ts +6 -5
- package/src/core/codemirror/readonly/__tests__/extension.test.ts +1 -1
- package/src/core/codemirror/readonly/extension.ts +2 -2
- package/src/core/codemirror/rtc/extension.ts +24 -13
- 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/config/__tests__/config-schema.test.ts +12 -5
- package/src/core/config/config-schema.ts +7 -1
- package/src/core/datasets/__tests__/data-source-discovery.test.ts +5 -5
- package/src/core/datasets/data-source-discovery.ts +4 -6
- package/src/core/edit-app.tsx +19 -23
- package/src/core/errors/error-entries.ts +3 -0
- package/src/core/export/hooks.ts +1 -1
- package/src/core/export/layout.ts +2 -2
- package/src/core/hotkeys/actions.ts +2 -4
- package/src/core/islands/__tests__/bootstrap.test.ts +73 -43
- package/src/core/islands/__tests__/bridge.test.ts +72 -25
- package/src/core/islands/bootstrap.ts +46 -53
- package/src/core/islands/bridge.ts +74 -23
- 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/kernel/__tests__/handlers.test.ts +7 -153
- package/src/core/kernel/handlers.ts +18 -59
- package/src/core/network/__tests__/connection.test.ts +1 -1
- package/src/core/network/__tests__/requests-lazy.test.ts +1 -3
- package/src/core/network/connection-notice.ts +1 -1
- package/src/core/network/connection.ts +1 -1
- package/src/core/network/requests-lazy.ts +1 -4
- package/src/core/network/requests-network.ts +0 -25
- package/src/core/network/requests-static.ts +0 -3
- package/src/core/network/requests-toasting.tsx +0 -3
- package/src/core/network/types.ts +0 -12
- package/src/core/network/useConnectionNotice.ts +8 -2
- package/src/core/notebook/attachments.ts +38 -0
- package/src/core/notebook/document/__tests__/codemirror.test.ts +552 -0
- package/src/core/notebook/document/__tests__/draft.test.ts +363 -0
- package/src/core/notebook/document/__tests__/fixtures.ts +92 -0
- package/src/core/notebook/document/__tests__/hold.test.ts +88 -0
- package/src/core/notebook/document/__tests__/hooks.test.tsx +391 -0
- package/src/core/notebook/document/__tests__/lifecycle-scenarios.test.ts +61 -0
- package/src/core/notebook/document/__tests__/model.test.ts +247 -0
- package/src/core/notebook/document/__tests__/protocol-fixtures.ts +38 -0
- package/src/core/notebook/document/__tests__/recovery-scenarios.test.ts +230 -0
- package/src/core/notebook/document/__tests__/scenario-fixture.ts +43 -0
- package/src/core/notebook/document/__tests__/scenarios.test.ts +205 -0
- package/src/core/notebook/document/__tests__/state.test.ts +459 -0
- package/src/core/notebook/document/__tests__/submission.test.ts +203 -0
- package/src/core/notebook/document/__tests__/test-host.test.ts +92 -0
- package/src/core/notebook/document/__tests__/test-host.ts +247 -0
- package/src/core/notebook/document/__tests__/test-notebook.ts +149 -0
- package/src/core/notebook/document/__tests__/user.ts +61 -0
- package/src/core/notebook/document/codemirror.ts +216 -0
- package/src/core/notebook/document/draft.ts +502 -0
- package/src/core/notebook/document/hooks.tsx +118 -0
- package/src/core/notebook/document/index.ts +24 -0
- package/src/core/notebook/document/model.ts +105 -0
- package/src/core/notebook/document/select.ts +41 -0
- package/src/core/notebook/document/state.ts +378 -0
- package/src/core/notebook/document/sync.ts +418 -0
- package/src/core/notebook/index.ts +18 -0
- package/src/core/notebook/replica/AGENTS.md +11 -0
- package/src/core/notebook/replica/__tests__/attachments.test.ts +56 -0
- package/src/core/notebook/replica/__tests__/document.test.ts +126 -0
- package/src/core/notebook/replica/__tests__/fixtures.ts +79 -0
- package/src/core/notebook/replica/__tests__/replica.test.ts +550 -0
- package/src/core/notebook/replica/backend.ts +19 -0
- package/src/core/notebook/replica/connection.ts +42 -0
- package/src/core/notebook/replica/index.ts +13 -0
- package/src/core/notebook/replica/replica.ts +286 -0
- package/src/core/notebook/replica/resources/attachments.ts +22 -0
- package/src/core/notebook/replica/resources/document.ts +101 -0
- package/src/core/packages/__tests__/useSandboxController.test.tsx +1 -1
- package/src/core/packages/useSandboxController.ts +1 -1
- package/src/core/run-app.tsx +48 -51
- package/src/core/runtime/__tests__/adapter.test.ts +1 -1
- package/src/core/runtime/__tests__/createWsUrl.test.ts +77 -7
- package/src/core/runtime/__tests__/runtime.test.ts +32 -58
- package/src/core/runtime/adapter.ts +1 -1
- package/src/core/runtime/config.ts +2 -2
- package/src/core/runtime/runtime.ts +38 -42
- package/src/core/runtime/types.ts +0 -2
- package/src/core/saving/filename.ts +1 -1
- package/src/core/saving/save-component.tsx +10 -4
- package/src/core/saving/useAutoSave.ts +1 -1
- package/src/core/session/__tests__/backend.test.ts +235 -0
- package/src/core/session/__tests__/useNotebookReplica.hook.test.tsx +183 -0
- package/src/core/session/__tests__/useNotebookSession.hook.test.tsx +663 -0
- package/src/core/session/active-notebook.ts +11 -0
- package/src/core/session/backend.ts +125 -0
- package/src/core/{websocket → session}/types.ts +2 -2
- package/src/core/session/useNotebookReplica.ts +72 -0
- package/src/core/{websocket → session}/useNotebookSession.tsx +74 -68
- 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/{websocket → transports}/__tests__/connection-state.test.ts +1 -13
- package/src/core/transports/__tests__/server.test.ts +408 -0
- package/src/core/transports/__tests__/static.test.ts +175 -0
- package/src/core/transports/__tests__/wasm.test.ts +166 -0
- package/src/core/{websocket → transports}/connection-state.ts +9 -16
- package/src/core/transports/server.ts +152 -0
- package/src/core/transports/static.ts +84 -0
- package/src/core/transports/wasm.ts +97 -0
- package/src/core/wasm/__tests__/bridge.test.ts +213 -50
- package/src/core/wasm/__tests__/worker.test.ts +187 -0
- package/src/core/wasm/bridge.ts +75 -69
- package/src/core/wasm/rpc.ts +15 -3
- 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/save-worker.ts +9 -10
- package/src/core/wasm/worker/types.ts +29 -18
- package/src/core/wasm/worker/worker.ts +88 -44
- package/src/css/globals.css +66 -22
- 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 +1 -5
- 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 +69 -37
- 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 +387 -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 +153 -0
- package/src/plugins/impl/anywidget/__tests__/widget-binding.test.ts +241 -2
- package/src/plugins/impl/anywidget/model.ts +3 -1
- package/src/plugins/impl/anywidget/registry.ts +18 -14
- 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/cell.stories.tsx +1 -1
- package/src/stories/data-table.stories.tsx +31 -0
- package/src/stories/editor.stories.tsx +24 -17
- package/src/stories/form-wrapper.stories.tsx +2 -2
- package/src/stories/layout/vertical/one-column.stories.tsx +1 -1
- package/src/stories/runtime-story.tsx +23 -16
- 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__/{createReducer.test.ts → createReducer.test.tsx} +29 -0
- package/src/utils/__tests__/numbers.test.ts +105 -1
- package/src/utils/__tests__/objects.test.ts +21 -0
- package/src/utils/arrays.ts +11 -2
- package/src/utils/blob.ts +1 -1
- package/src/utils/createReducer.ts +53 -26
- package/src/utils/maps.ts +0 -3
- package/src/utils/numbers.ts +79 -18
- package/src/utils/objects.ts +4 -9
- 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/ai/tools/__tests__/run-cells-tool.test.ts +0 -656
- package/src/core/ai/tools/run-cells-tool.ts +0 -303
- package/src/core/attachments/state.ts +0 -21
- package/src/core/cells/__tests__/apply-transaction.test.ts +0 -991
- package/src/core/cells/__tests__/document-changes.test.ts +0 -1140
- package/src/core/cells/__tests__/document-roundtrip.test.ts +0 -392
- package/src/core/cells/document-changes.ts +0 -944
- package/src/core/host/AGENTS.md +0 -6
- 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/connection.ts +0 -81
- 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/__tests__/useNotebookSession.hook.test.tsx +0 -883
- 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
- /package/src/{core/attachments/toast.ts → components/editor/agent-toast.ts} +0 -0
- /package/src/core/{websocket → session}/connection-utils.ts +0 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A CodeMirror binding between an editor and one cell's code.
|
|
5
|
+
*
|
|
6
|
+
* The editor and the document model each hold the code, and the binding keeps
|
|
7
|
+
* them equal in both directions.
|
|
8
|
+
*
|
|
9
|
+
* - Push. An update that could change the code writes it to the model before
|
|
10
|
+
* the update returns, so the model keeps pace with typing.
|
|
11
|
+
* - Pull. A model change reaches the editor in a microtask, because a view
|
|
12
|
+
* cannot dispatch while it is constructing or updating. The binding keeps
|
|
13
|
+
* the code both sides last agreed on, so the model's echo of a push is
|
|
14
|
+
* skipped while a different value, even one written during that push, is
|
|
15
|
+
* pulled. A real change is marked remote, stays out of undo history, and
|
|
16
|
+
* waits for IME composition to end.
|
|
17
|
+
*
|
|
18
|
+
* The editor shows code through marimo's language adapters, so a markdown
|
|
19
|
+
* cell edits the text inside `mo.md`. Both directions compare the cell's
|
|
20
|
+
* Python code rather than the editor text, so an adapter that normalizes what
|
|
21
|
+
* it reads back leaves typing alone.
|
|
22
|
+
*
|
|
23
|
+
* A pulled change replaces only what differs, using `diff` from
|
|
24
|
+
* `@codemirror/merge`. Separate edits stay separate, so the selection, folds,
|
|
25
|
+
* and diagnostics between them keep their place. The diff uses a scan limit of
|
|
26
|
+
* 500, as the merge view does. Past it, the search trades precision for speed,
|
|
27
|
+
* so a rewrite of a large cell costs microseconds and maps the selection
|
|
28
|
+
* coarsely instead of blocking the editor.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import {
|
|
32
|
+
type EditorState,
|
|
33
|
+
type Extension,
|
|
34
|
+
Transaction,
|
|
35
|
+
} from "@codemirror/state";
|
|
36
|
+
import { type EditorView, ViewPlugin, type ViewUpdate } from "@codemirror/view";
|
|
37
|
+
import type { CellId } from "@marimo-team/client/protocol";
|
|
38
|
+
import type { Atom, createStore } from "jotai/vanilla";
|
|
39
|
+
import {
|
|
40
|
+
editorShowsCellCode,
|
|
41
|
+
getCellCode,
|
|
42
|
+
prepareCellCodeUpdate,
|
|
43
|
+
} from "../../codemirror/language/cell-code";
|
|
44
|
+
import { languageAdapterState } from "../../codemirror/language/extension";
|
|
45
|
+
import { languageMetadataField } from "../../codemirror/language/metadata";
|
|
46
|
+
import type { DocumentModel } from "./model";
|
|
47
|
+
import { selectCellField } from "./select";
|
|
48
|
+
|
|
49
|
+
interface Binding {
|
|
50
|
+
readonly store: ReturnType<typeof createStore>;
|
|
51
|
+
readonly model: DocumentModel;
|
|
52
|
+
readonly cellId: CellId;
|
|
53
|
+
/** Undefined while the cell is absent, null while its source is withheld. */
|
|
54
|
+
readonly code: Atom<string | null | undefined>;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Bind an editor to one cell's code.
|
|
59
|
+
*
|
|
60
|
+
* Editors without marimo's language configuration bind their text as is.
|
|
61
|
+
*/
|
|
62
|
+
export function cellCodeBinding({
|
|
63
|
+
store,
|
|
64
|
+
model,
|
|
65
|
+
cellId,
|
|
66
|
+
}: {
|
|
67
|
+
store: ReturnType<typeof createStore>;
|
|
68
|
+
model: DocumentModel;
|
|
69
|
+
cellId: CellId;
|
|
70
|
+
}): Extension {
|
|
71
|
+
const binding: Binding = {
|
|
72
|
+
store,
|
|
73
|
+
model,
|
|
74
|
+
cellId,
|
|
75
|
+
code: selectCellField(model.documentAtom, cellId, "code"),
|
|
76
|
+
};
|
|
77
|
+
return ViewPlugin.define((view) => new CellCodeSync(view, binding), {
|
|
78
|
+
eventObservers: {
|
|
79
|
+
compositionend() {
|
|
80
|
+
this.compositionEnded();
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
class CellCodeSync {
|
|
87
|
+
// Only the initial load infers a mode; subsequent edits preserve it.
|
|
88
|
+
#initialized = false;
|
|
89
|
+
#scheduled = false;
|
|
90
|
+
/** A pull arrived during IME composition and runs once it ends. */
|
|
91
|
+
#deferred = false;
|
|
92
|
+
/**
|
|
93
|
+
* The code the editor and the model last agreed on.
|
|
94
|
+
*
|
|
95
|
+
* A change the model cannot take clears it, so once the cell is available
|
|
96
|
+
* again its code is pulled even if it matches what was agreed before.
|
|
97
|
+
*/
|
|
98
|
+
#synced: string | undefined;
|
|
99
|
+
#destroyed = false;
|
|
100
|
+
readonly #view: EditorView;
|
|
101
|
+
readonly #binding: Binding;
|
|
102
|
+
readonly #unsubscribe: () => void;
|
|
103
|
+
|
|
104
|
+
public constructor(view: EditorView, binding: Binding) {
|
|
105
|
+
this.#view = view;
|
|
106
|
+
this.#binding = binding;
|
|
107
|
+
// A change to the agreed code is the echo of a push and needs no pull.
|
|
108
|
+
this.#unsubscribe = binding.store.sub(binding.code, () => {
|
|
109
|
+
if (binding.store.get(binding.code) !== this.#synced) {
|
|
110
|
+
this.#schedule();
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
this.#schedule();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
public update(update: ViewUpdate) {
|
|
117
|
+
if (
|
|
118
|
+
changesCode(update) &&
|
|
119
|
+
!update.transactions.every((tr) => tr.annotation(Transaction.remote))
|
|
120
|
+
) {
|
|
121
|
+
this.#push(update.state);
|
|
122
|
+
}
|
|
123
|
+
if (this.#deferred && !update.view.composing) {
|
|
124
|
+
this.#schedule();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Run a pull that waited for the composition.
|
|
130
|
+
*
|
|
131
|
+
* A composition that ends without changing the text need not produce an
|
|
132
|
+
* update. CodeMirror finishes handling the end in a microtask, so the pull
|
|
133
|
+
* waits for the next task.
|
|
134
|
+
*/
|
|
135
|
+
public compositionEnded() {
|
|
136
|
+
if (this.#deferred) {
|
|
137
|
+
setTimeout(() => this.#schedule(), 0);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
public destroy() {
|
|
142
|
+
this.#destroyed = true;
|
|
143
|
+
this.#unsubscribe();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Write the editor's code to the model unless the editor already shows it. */
|
|
147
|
+
#push(state: EditorState) {
|
|
148
|
+
const { store, model, cellId, code } = this.#binding;
|
|
149
|
+
const current = store.get(code);
|
|
150
|
+
if (typeof current !== "string") {
|
|
151
|
+
this.#synced = undefined;
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
this.#initialized = true;
|
|
155
|
+
if (editorShowsCellCode(state, current)) {
|
|
156
|
+
this.#synced = current;
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const next = getCellCode(state);
|
|
160
|
+
this.#synced = next;
|
|
161
|
+
store.set(model.editAtom, (tx) => tx.setCode(cellId, next));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** Show the model's code unless the editor already shows it. */
|
|
165
|
+
#pull() {
|
|
166
|
+
this.#scheduled = false;
|
|
167
|
+
const next = this.#binding.store.get(this.#binding.code);
|
|
168
|
+
if (this.#destroyed || typeof next !== "string") {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
if (this.#view.composing) {
|
|
172
|
+
this.#deferred = true;
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
this.#deferred = false;
|
|
176
|
+
if (next !== this.#synced) {
|
|
177
|
+
const update = prepareCellCodeUpdate(this.#view.state, next, {
|
|
178
|
+
initialize: !this.#initialized,
|
|
179
|
+
});
|
|
180
|
+
if (update) {
|
|
181
|
+
this.#view.dispatch(update, {
|
|
182
|
+
annotations: [
|
|
183
|
+
Transaction.remote.of(true),
|
|
184
|
+
Transaction.addToHistory.of(false),
|
|
185
|
+
],
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
this.#synced = next;
|
|
190
|
+
this.#initialized = true;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** Pull once after the current update, however many changes arrive. */
|
|
194
|
+
#schedule() {
|
|
195
|
+
if (!this.#scheduled) {
|
|
196
|
+
this.#scheduled = true;
|
|
197
|
+
queueMicrotask(() => this.#pull());
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Whether an update could change the code an editor represents.
|
|
204
|
+
*
|
|
205
|
+
* Language metadata, such as an SQL cell's output name, changes the code
|
|
206
|
+
* without changing the text.
|
|
207
|
+
*/
|
|
208
|
+
function changesCode(update: ViewUpdate): boolean {
|
|
209
|
+
return (
|
|
210
|
+
update.docChanged ||
|
|
211
|
+
update.state.field(languageAdapterState, false) !==
|
|
212
|
+
update.startState.field(languageAdapterState, false) ||
|
|
213
|
+
update.state.field(languageMetadataField, false) !==
|
|
214
|
+
update.startState.field(languageMetadataField, false)
|
|
215
|
+
);
|
|
216
|
+
}
|
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import type { Document } from "../replica";
|
|
4
|
+
import * as Protocol from "@marimo-team/client/protocol";
|
|
5
|
+
|
|
6
|
+
/** The fields a client edits. Null code means the host withheld source. */
|
|
7
|
+
export type EditableCell = Readonly<
|
|
8
|
+
Pick<Protocol.Cell, "id" | "code" | "name">
|
|
9
|
+
> & {
|
|
10
|
+
readonly config: Readonly<Protocol.CellConfig>;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
type CellWithSource = EditableCell & { readonly code: string };
|
|
14
|
+
|
|
15
|
+
/** A confirmed document with a draft applied. */
|
|
16
|
+
export interface EditableDocument {
|
|
17
|
+
readonly order: readonly Protocol.CellId[];
|
|
18
|
+
readonly cells: ReadonlyMap<Protocol.CellId, EditableCell>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Fields to change on a cell. A field left out keeps its value, including within config. */
|
|
22
|
+
export interface CellEdit {
|
|
23
|
+
readonly code?: string;
|
|
24
|
+
readonly name?: string;
|
|
25
|
+
readonly config?: Partial<Readonly<Protocol.CellConfig>>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type MutableEdit = { -readonly [K in keyof CellEdit]: CellEdit[K] };
|
|
29
|
+
|
|
30
|
+
/** Where a created cell goes. With neither, at the end. */
|
|
31
|
+
export interface Placement {
|
|
32
|
+
readonly after?: Protocol.CellId;
|
|
33
|
+
readonly before?: Protocol.CellId;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** What this client wants one cell to become. */
|
|
37
|
+
type CellDraft =
|
|
38
|
+
| { readonly kind: "edit"; readonly edit: CellEdit }
|
|
39
|
+
| {
|
|
40
|
+
readonly kind: "create";
|
|
41
|
+
readonly cell: CellWithSource;
|
|
42
|
+
readonly placement: Placement;
|
|
43
|
+
}
|
|
44
|
+
| { readonly kind: "delete" };
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* This client's changes, layered over a confirmed document.
|
|
48
|
+
*
|
|
49
|
+
* A draft is a document delta in the terms of the host's edit request:
|
|
50
|
+
* per-cell puts and deletes, and optionally the order as a whole. The view
|
|
51
|
+
* a person edits and the request that synchronizes it both derive from it.
|
|
52
|
+
*/
|
|
53
|
+
export interface Draft {
|
|
54
|
+
readonly cells: ReadonlyMap<Protocol.CellId, CellDraft>;
|
|
55
|
+
/** The full order to apply, or null to follow the host's. */
|
|
56
|
+
readonly order: readonly Protocol.CellId[] | null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export const EMPTY_DRAFT: Draft = { cells: new Map(), order: null };
|
|
60
|
+
|
|
61
|
+
export function isEmptyDraft(draft: Draft): boolean {
|
|
62
|
+
return draft.cells.size === 0 && draft.order === null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Merging -------------------------------------------------------------------
|
|
66
|
+
|
|
67
|
+
/** Copy the defined fields of `patch` over `base`. */
|
|
68
|
+
function assignDefined<T extends object>(base: T, patch: Partial<T>): T {
|
|
69
|
+
const result = { ...base };
|
|
70
|
+
for (const key in patch) {
|
|
71
|
+
const value = patch[key];
|
|
72
|
+
if (value !== undefined) {
|
|
73
|
+
result[key] = value;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** The defined fields of `to` that differ from `from`. */
|
|
80
|
+
function differingFields<T extends object>(
|
|
81
|
+
from: T,
|
|
82
|
+
to: Partial<T>,
|
|
83
|
+
): Partial<T> {
|
|
84
|
+
const result: Partial<T> = {};
|
|
85
|
+
for (const key in to) {
|
|
86
|
+
const value = to[key];
|
|
87
|
+
if (value !== undefined && value !== from[key]) {
|
|
88
|
+
result[key] = value;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function isEmpty(object: object): boolean {
|
|
95
|
+
return Object.keys(object).length === 0;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function sameOrder(
|
|
99
|
+
a: readonly Protocol.CellId[],
|
|
100
|
+
b: readonly Protocol.CellId[],
|
|
101
|
+
): boolean {
|
|
102
|
+
return a.length === b.length && a.every((id, index) => id === b[index]);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Combine two edits, the later one winning field by field. */
|
|
106
|
+
function mergeEdit(previous: CellEdit, edit: CellEdit): CellEdit {
|
|
107
|
+
const merged = assignDefined(previous, edit);
|
|
108
|
+
return edit.config === undefined
|
|
109
|
+
? merged
|
|
110
|
+
: { ...merged, config: assignDefined(previous.config ?? {}, edit.config) };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** The cell with an edit applied, as a new object with the editable fields. */
|
|
114
|
+
function applyEdit(cell: CellWithSource, edit: CellEdit): CellWithSource;
|
|
115
|
+
function applyEdit(cell: EditableCell, edit: CellEdit): EditableCell;
|
|
116
|
+
function applyEdit(cell: EditableCell, edit: CellEdit): EditableCell {
|
|
117
|
+
return {
|
|
118
|
+
id: cell.id,
|
|
119
|
+
code: edit.code ?? cell.code,
|
|
120
|
+
name: edit.name ?? cell.name,
|
|
121
|
+
config:
|
|
122
|
+
edit.config === undefined
|
|
123
|
+
? cell.config
|
|
124
|
+
: assignDefined(cell.config, edit.config),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function sameCell(a: EditableCell, b: EditableCell): boolean {
|
|
129
|
+
return (
|
|
130
|
+
a.id === b.id &&
|
|
131
|
+
a.code === b.code &&
|
|
132
|
+
a.name === b.name &&
|
|
133
|
+
isEmpty(differingFields(a.config, b.config))
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** The edit that turns `from` into `to`. Empty when they are the same. */
|
|
138
|
+
function editBetween(from: CellWithSource, to: CellWithSource): CellEdit {
|
|
139
|
+
const edit: MutableEdit = {};
|
|
140
|
+
if (to.code !== from.code) {
|
|
141
|
+
edit.code = to.code;
|
|
142
|
+
}
|
|
143
|
+
if (to.name !== from.name) {
|
|
144
|
+
edit.name = to.name;
|
|
145
|
+
}
|
|
146
|
+
const config = differingFields(from.config, to.config);
|
|
147
|
+
if (!isEmpty(config)) {
|
|
148
|
+
edit.config = config;
|
|
149
|
+
}
|
|
150
|
+
return edit;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** The fields of `edit` still waiting after `sent` was accepted. */
|
|
154
|
+
function remainingEdit(edit: CellEdit, sent: CellEdit): CellEdit {
|
|
155
|
+
const remaining: MutableEdit = {};
|
|
156
|
+
if (edit.code !== undefined && edit.code !== sent.code) {
|
|
157
|
+
remaining.code = edit.code;
|
|
158
|
+
}
|
|
159
|
+
if (edit.name !== undefined && edit.name !== sent.name) {
|
|
160
|
+
remaining.name = edit.name;
|
|
161
|
+
}
|
|
162
|
+
if (edit.config !== undefined) {
|
|
163
|
+
const config = differingFields(sent.config ?? {}, edit.config);
|
|
164
|
+
if (!isEmpty(config)) {
|
|
165
|
+
remaining.config = config;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return remaining;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Transitions ---------------------------------------------------------------
|
|
172
|
+
//
|
|
173
|
+
// Transitions are total. One with no effect returns its input, so a store
|
|
174
|
+
// can skip the write and its notifications.
|
|
175
|
+
|
|
176
|
+
function withCell(
|
|
177
|
+
draft: Draft,
|
|
178
|
+
id: Protocol.CellId,
|
|
179
|
+
cell: CellDraft | undefined,
|
|
180
|
+
): Draft {
|
|
181
|
+
const cells = new Map(draft.cells);
|
|
182
|
+
if (cell === undefined) {
|
|
183
|
+
cells.delete(id);
|
|
184
|
+
} else {
|
|
185
|
+
cells.set(id, cell);
|
|
186
|
+
}
|
|
187
|
+
return { ...draft, cells };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** Place a cell, falling back to the end if its anchor has disappeared. */
|
|
191
|
+
function insertAt(
|
|
192
|
+
order: readonly Protocol.CellId[],
|
|
193
|
+
id: Protocol.CellId,
|
|
194
|
+
placement: Placement,
|
|
195
|
+
): Protocol.CellId[] {
|
|
196
|
+
const without = order.filter((other) => other !== id);
|
|
197
|
+
let index = without.length;
|
|
198
|
+
if (placement.after !== undefined) {
|
|
199
|
+
const anchor = without.indexOf(placement.after);
|
|
200
|
+
if (anchor !== -1) {
|
|
201
|
+
index = anchor + 1;
|
|
202
|
+
}
|
|
203
|
+
} else if (placement.before !== undefined) {
|
|
204
|
+
const anchor = without.indexOf(placement.before);
|
|
205
|
+
if (anchor !== -1) {
|
|
206
|
+
index = anchor;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return [...without.slice(0, index), id, ...without.slice(index)];
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Edit a cell's fields.
|
|
214
|
+
*
|
|
215
|
+
* An edit to a cell the host has removed is dropped: there is no state to
|
|
216
|
+
* apply it to. So is an edit to a cell this draft deletes.
|
|
217
|
+
*/
|
|
218
|
+
export function editCell(
|
|
219
|
+
confirmed: Document,
|
|
220
|
+
draft: Draft,
|
|
221
|
+
id: Protocol.CellId,
|
|
222
|
+
edit: CellEdit,
|
|
223
|
+
): Draft {
|
|
224
|
+
const current = draft.cells.get(id);
|
|
225
|
+
if (current?.kind === "delete") {
|
|
226
|
+
return draft;
|
|
227
|
+
}
|
|
228
|
+
if (current?.kind === "create") {
|
|
229
|
+
const cell = applyEdit(current.cell, edit);
|
|
230
|
+
return sameCell(cell, current.cell)
|
|
231
|
+
? draft
|
|
232
|
+
: withCell(draft, id, { ...current, cell });
|
|
233
|
+
}
|
|
234
|
+
const base = confirmed.cells.get(id);
|
|
235
|
+
if (base === undefined) {
|
|
236
|
+
return draft;
|
|
237
|
+
}
|
|
238
|
+
const previous = current?.edit ?? {};
|
|
239
|
+
const next = mergeEdit(previous, edit);
|
|
240
|
+
return sameCell(applyEdit(base, previous), applyEdit(base, next))
|
|
241
|
+
? draft
|
|
242
|
+
: withCell(draft, id, { kind: "edit", edit: next });
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Create a cell with the id the client chose.
|
|
247
|
+
*
|
|
248
|
+
* Omitted fields take the protocol's defaults. A draft order, if any, gains
|
|
249
|
+
* the cell at its placement so that it stays complete.
|
|
250
|
+
*/
|
|
251
|
+
export function createCell(
|
|
252
|
+
draft: Draft,
|
|
253
|
+
input: Protocol.CellInput,
|
|
254
|
+
placement: Placement = {},
|
|
255
|
+
): Draft {
|
|
256
|
+
const cell = parseCell(input);
|
|
257
|
+
const next = withCell(draft, cell.id, { kind: "create", cell, placement });
|
|
258
|
+
return draft.order === null
|
|
259
|
+
? next
|
|
260
|
+
: { ...next, order: insertAt(draft.order, cell.id, placement) };
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Bring back a cell this draft deletes, with the given content.
|
|
265
|
+
*
|
|
266
|
+
* A deletion not yet sent is withdrawn and the cell edited in place. One in
|
|
267
|
+
* flight is superseded by a creation, so the cell returns once the host has
|
|
268
|
+
* removed it. Either way the cell takes the requested placement.
|
|
269
|
+
*/
|
|
270
|
+
export function restoreCell(
|
|
271
|
+
confirmed: Document,
|
|
272
|
+
draft: Draft,
|
|
273
|
+
input: Protocol.CellInput,
|
|
274
|
+
placement: Placement = {},
|
|
275
|
+
sent: Draft = EMPTY_DRAFT,
|
|
276
|
+
): Draft {
|
|
277
|
+
const { id, code, name, config } = parseCell(input);
|
|
278
|
+
const withdrawn = withCell(draft, id, undefined);
|
|
279
|
+
const next =
|
|
280
|
+
sent.cells.get(id)?.kind === "delete"
|
|
281
|
+
? createCell(withdrawn, input, placement)
|
|
282
|
+
: editCell(confirmed, withdrawn, id, { code, name, config });
|
|
283
|
+
const { order } = viewDocument(confirmed, next);
|
|
284
|
+
const placed = insertAt(order, id, placement);
|
|
285
|
+
return sameOrder(order, placed) ? next : setOrder(next, placed);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/** A cell input with the protocol's defaults filled in. */
|
|
289
|
+
function parseCell(input: Protocol.CellInput): CellWithSource {
|
|
290
|
+
const parsed = Protocol.CellInput.parse(input);
|
|
291
|
+
return {
|
|
292
|
+
id: parsed.id,
|
|
293
|
+
code: parsed.code,
|
|
294
|
+
name: parsed.name,
|
|
295
|
+
config: parsed.config ?? Protocol.CellConfigInput.parse({}),
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/** Forget an unsent creation; otherwise retain the deletion until confirmed. */
|
|
300
|
+
export function deleteCell(
|
|
301
|
+
draft: Draft,
|
|
302
|
+
id: Protocol.CellId,
|
|
303
|
+
sent: Draft = EMPTY_DRAFT,
|
|
304
|
+
): Draft {
|
|
305
|
+
const current = draft.cells.get(id);
|
|
306
|
+
if (current?.kind === "delete") {
|
|
307
|
+
return draft;
|
|
308
|
+
}
|
|
309
|
+
const next = withCell(
|
|
310
|
+
draft,
|
|
311
|
+
id,
|
|
312
|
+
current?.kind === "create" && sent.cells.get(id)?.kind !== "create"
|
|
313
|
+
? undefined
|
|
314
|
+
: { kind: "delete" },
|
|
315
|
+
);
|
|
316
|
+
return draft.order === null
|
|
317
|
+
? next
|
|
318
|
+
: { ...next, order: draft.order.filter((other) => other !== id) };
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/** Replace the order. Lists each cell of the document once, as `set_order` does. */
|
|
322
|
+
export function setOrder(
|
|
323
|
+
draft: Draft,
|
|
324
|
+
order: readonly Protocol.CellId[],
|
|
325
|
+
): Draft {
|
|
326
|
+
return draft.order !== null && sameOrder(draft.order, order)
|
|
327
|
+
? draft
|
|
328
|
+
: { ...draft, order: [...order] };
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/** Drop satisfied deletions, retaining edits to missing cells for recovery. */
|
|
332
|
+
export function prune(
|
|
333
|
+
confirmed: Document,
|
|
334
|
+
draft: Draft,
|
|
335
|
+
sent: Draft = EMPTY_DRAFT,
|
|
336
|
+
): Draft {
|
|
337
|
+
let cells: Map<Protocol.CellId, CellDraft> | undefined;
|
|
338
|
+
for (const [id, cell] of draft.cells) {
|
|
339
|
+
if (
|
|
340
|
+
cell.kind === "delete" &&
|
|
341
|
+
!confirmed.cells.has(id) &&
|
|
342
|
+
sent.cells.get(id)?.kind !== "create"
|
|
343
|
+
) {
|
|
344
|
+
cells ??= new Map(draft.cells);
|
|
345
|
+
cells.delete(id);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
return cells === undefined ? draft : { ...draft, cells };
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Release a draft after the accepted revision is present in the source.
|
|
353
|
+
*
|
|
354
|
+
* `sent` is the draft the accepted request was derived from. A field is
|
|
355
|
+
* released when its value is still the one sent; a created cell becomes an
|
|
356
|
+
* edit of whatever changed after it was sent. The model enforces the revision
|
|
357
|
+
* precondition; this pure transition assumes it has already been checked.
|
|
358
|
+
*/
|
|
359
|
+
export function acknowledge(draft: Draft, sent: Draft): Draft {
|
|
360
|
+
let next = draft;
|
|
361
|
+
for (const [id, accepted] of sent.cells) {
|
|
362
|
+
const current = next.cells.get(id);
|
|
363
|
+
if (current?.kind === "delete" && accepted.kind === "delete") {
|
|
364
|
+
next = withCell(next, id, undefined);
|
|
365
|
+
} else if (current?.kind === "create" && accepted.kind === "create") {
|
|
366
|
+
next = withEdit(next, id, editBetween(accepted.cell, current.cell));
|
|
367
|
+
} else if (current?.kind === "edit" && accepted.kind === "edit") {
|
|
368
|
+
next = withEdit(next, id, remainingEdit(current.edit, accepted.edit));
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
if (
|
|
372
|
+
next.order !== null &&
|
|
373
|
+
sent.order !== null &&
|
|
374
|
+
sameOrder(next.order, sent.order)
|
|
375
|
+
) {
|
|
376
|
+
next = { ...next, order: null };
|
|
377
|
+
}
|
|
378
|
+
return next;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function withEdit(draft: Draft, id: Protocol.CellId, edit: CellEdit): Draft {
|
|
382
|
+
return withCell(
|
|
383
|
+
draft,
|
|
384
|
+
id,
|
|
385
|
+
isEmpty(edit) ? undefined : { kind: "edit", edit },
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Projections ---------------------------------------------------------------
|
|
390
|
+
|
|
391
|
+
/** A create or edit as the edit it makes to an existing cell. */
|
|
392
|
+
function editOf(local: Exclude<CellDraft, { kind: "delete" }>): CellEdit {
|
|
393
|
+
return local.kind === "edit" ? local.edit : local.cell;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* The confirmed document with a draft applied.
|
|
398
|
+
*
|
|
399
|
+
* A cell without a draft is the confirmed record itself, so its identity is
|
|
400
|
+
* stable until the host changes it. Cells the host added since a draft order
|
|
401
|
+
* was set go at the end; cells it removed leave the order.
|
|
402
|
+
*/
|
|
403
|
+
export function viewDocument(
|
|
404
|
+
confirmed: Document,
|
|
405
|
+
draft: Draft,
|
|
406
|
+
): EditableDocument {
|
|
407
|
+
const cells = new Map<Protocol.CellId, EditableCell>();
|
|
408
|
+
for (const [id, cell] of confirmed.cells) {
|
|
409
|
+
const local = draft.cells.get(id);
|
|
410
|
+
if (local?.kind === "delete") {
|
|
411
|
+
continue;
|
|
412
|
+
}
|
|
413
|
+
cells.set(id, local === undefined ? cell : applyEdit(cell, editOf(local)));
|
|
414
|
+
}
|
|
415
|
+
let order: readonly Protocol.CellId[] =
|
|
416
|
+
draft.order ?? confirmed.revision.order;
|
|
417
|
+
for (const [id, local] of draft.cells) {
|
|
418
|
+
if (local.kind === "create" && !confirmed.cells.has(id)) {
|
|
419
|
+
cells.set(id, local.cell);
|
|
420
|
+
if (draft.order === null) {
|
|
421
|
+
order = insertAt(order, id, local.placement);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
const placed = new Set(order);
|
|
426
|
+
const unplaced = [...cells.keys()].filter((id) => !placed.has(id));
|
|
427
|
+
if (unplaced.length > 0 || order.some((id) => !cells.has(id))) {
|
|
428
|
+
order = [...order.filter((id) => cells.has(id)), ...unplaced];
|
|
429
|
+
}
|
|
430
|
+
return {
|
|
431
|
+
order,
|
|
432
|
+
cells,
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* The edit request that would make the host match the view.
|
|
438
|
+
*
|
|
439
|
+
* Preconditions are the confirmed etags. A put with no effect is left out.
|
|
440
|
+
* Cells come before the order, so one request can create a cell and then
|
|
441
|
+
* place it. Returns null if a put lacks source or its edited cell was removed,
|
|
442
|
+
* keeping the draft atomic and retaining local intent for recovery.
|
|
443
|
+
*/
|
|
444
|
+
export function toChanges(
|
|
445
|
+
confirmed: Document,
|
|
446
|
+
draft: Draft,
|
|
447
|
+
): Protocol.Change[] | null {
|
|
448
|
+
const changes: Protocol.Change[] = [];
|
|
449
|
+
let stagedOrder = confirmed.revision.order;
|
|
450
|
+
const stagedIds = new Set(stagedOrder);
|
|
451
|
+
for (const [id, local] of draft.cells) {
|
|
452
|
+
const base = confirmed.cells.get(id);
|
|
453
|
+
if (local.kind === "delete") {
|
|
454
|
+
if (base !== undefined) {
|
|
455
|
+
changes.push({ kind: "delete_cell", id, base_etag: base.etag });
|
|
456
|
+
stagedOrder = stagedOrder.filter((other) => other !== id);
|
|
457
|
+
stagedIds.delete(id);
|
|
458
|
+
}
|
|
459
|
+
} else if (local.kind === "create") {
|
|
460
|
+
const anchor = local.placement.after ?? local.placement.before;
|
|
461
|
+
const placement =
|
|
462
|
+
anchor === undefined || stagedIds.has(anchor) ? local.placement : {};
|
|
463
|
+
changes.push({
|
|
464
|
+
kind: "put_cell",
|
|
465
|
+
base_etag: null,
|
|
466
|
+
cell: local.cell,
|
|
467
|
+
...placement,
|
|
468
|
+
});
|
|
469
|
+
if (base === undefined) {
|
|
470
|
+
stagedOrder = insertAt(stagedOrder, id, placement);
|
|
471
|
+
stagedIds.add(id);
|
|
472
|
+
}
|
|
473
|
+
} else if (base !== undefined) {
|
|
474
|
+
const cell = applyEdit(base, local.edit);
|
|
475
|
+
if (!sameCell(base, cell)) {
|
|
476
|
+
const { code } = cell;
|
|
477
|
+
if (code === null) {
|
|
478
|
+
return null;
|
|
479
|
+
}
|
|
480
|
+
changes.push({
|
|
481
|
+
kind: "put_cell",
|
|
482
|
+
base_etag: base.etag,
|
|
483
|
+
cell: { ...cell, code },
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
} else {
|
|
487
|
+
return null;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
const { order } = viewDocument(confirmed, draft);
|
|
491
|
+
if (
|
|
492
|
+
(draft.order !== null && !sameOrder(order, confirmed.revision.order)) ||
|
|
493
|
+
!sameOrder(order, stagedOrder)
|
|
494
|
+
) {
|
|
495
|
+
changes.push({
|
|
496
|
+
kind: "set_order",
|
|
497
|
+
base_etag: confirmed.revision.order_etag,
|
|
498
|
+
order: [...order],
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
return changes;
|
|
502
|
+
}
|