@marimo-team/islands 0.25.3-dev20 → 0.25.3-dev200
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-BsWiIPFk.js} +22 -20
- package/dist/{ErrorBoundary-DAPIRYqd.js → ErrorBoundary-UWARECs-.js} +19 -19
- package/dist/{abnfDiagram-VCTEODGH-D-Ez91gV.js → abnfDiagram-VCTEODGH-I2x4Yu25.js} +2 -2
- package/dist/any-language-editor-BVFHDDQ3.js +75 -0
- package/dist/{architectureDiagram-5GKGNRK7-CTpcNtO8.js → architectureDiagram-5GKGNRK7-1bSQMKzK.js} +1 -1
- package/dist/{arrays-CN1djC4k.js → arrays-DoQ-AILx.js} +15 -27
- package/dist/assets/__vite-browser-external-BCJsx8QS.js +1 -0
- package/dist/assets/worker-DodjWT_Z.js +113 -0
- package/dist/{button-CvjkwdSk.js → button-BQRdqQi1.js} +199 -192
- package/dist/{capabilities-CQ5IjhzL.js → capabilities-C2QUNRXi.js} +2 -2
- package/dist/{chat-ui-DaHynk8g.js → chat-ui-DXDoSuS4.js} +370 -369
- package/dist/{check-EDwSwP89.js → check-C9Mp9eCK.js} +1 -1
- package/dist/{chunk-IMKFNOWR-DGxi-zeo.js → chunk-IMKFNOWR-fsCGAJ9G.js} +1 -1
- package/dist/{chunk-SHT3W25Y-CV9J3rAB.js → chunk-SHT3W25Y-D2STW4hj.js} +1 -1
- package/dist/{chunk-SVP7TREG-BL-9Fqgg.js → chunk-SVP7TREG-CiY7cQG-.js} +1 -1
- package/dist/{chunk-TICWLB2K-Cu_XMjDM.js → chunk-TICWLB2K-C_t5RBp9.js} +1 -1
- package/dist/{chunk-YOKDWASO-DPZk8vhS.js → chunk-YOKDWASO-DKplTrBY.js} +20 -20
- package/dist/{classDiagram-ZZMXUADV-BXXV13Hq.js → classDiagram-ZZMXUADV-B2JvZAP4.js} +2 -2
- package/dist/{classDiagram-v2-VYDZK3BY-B7FXJ1Cp.js → classDiagram-v2-VYDZK3BY-L8VsZG5A.js} +2 -2
- package/dist/code-visibility-33fxfX1p.js +134 -0
- package/dist/{common-DsUPhkD6.js → common-D09oJJ8Z.js} +6040 -5865
- package/dist/{copy-icon-Ft7t9bqz.js → copy-icon-De584pZ4.js} +3 -3
- package/dist/{cynefinDiagram-5FMLGOSQ-CH96rwba.js → cynefinDiagram-5FMLGOSQ-DdllC264.js} +1 -1
- package/dist/{dagre-GXQ25YYZ-BD3PmpFY.js → dagre-GXQ25YYZ-BaWHw4_f.js} +1 -1
- package/dist/{data-grid-overlay-editor-D7UuVsgB.js → data-grid-overlay-editor-lGsInXfR.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-pFt-tUDe.js} +1 -1
- package/dist/{diagram-UQ7AKVKN-muQtNZc4.js → diagram-UQ7AKVKN-DqGJiBp4.js} +1 -1
- package/dist/{diagram-VX7I27RA-CdhuZAqR.js → diagram-VX7I27RA-Dr-yzMjf.js} +1 -1
- package/dist/{diagram-Z3DM3KII-BD8mXjOj.js → diagram-Z3DM3KII-8xzJzv-w.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-NlnsmNA6.js} +1481 -1433
- package/dist/{ebnfDiagram-PWID7BFC-BIN1sweE.js → ebnfDiagram-PWID7BFC-Br04Wy-E.js} +2 -2
- package/dist/{erDiagram-RLTQ6QDP-Ctp5cfoN.js → erDiagram-RLTQ6QDP-D5-XVgvv.js} +1 -1
- package/dist/{error-banner-D-WCIiMt.js → error-banner-DxJ3s8YO.js} +86 -86
- package/dist/{error-entries-Bv6bZPF3.js → error-entries-C2Ux0Fjt.js} +8404 -9095
- 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-B0CMhVv7.js +374 -0
- package/dist/{flowDiagram-HODETNUW-3EfLrlFb.js → flowDiagram-HODETNUW-CVl2kREF.js} +2 -2
- package/dist/{formats-27CW_UP4.js → formats-BCYthwyc.js} +3 -3
- package/dist/{ganttDiagram-EL5Y4UJY-BQT16JSc.js → ganttDiagram-EL5Y4UJY-Dh_4cBJZ.js} +2 -2
- package/dist/{glide-data-editor-KvS90MGh.js → glide-data-editor-CL535A5q.js} +370 -368
- package/dist/{glide-utils-DJyqh0EM.js → glide-utils-D98laidb.js} +16 -16
- package/dist/{globals-yruoELTW.js → globals-Bsr6T2yD.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-CkH7vdBZ.js} +477 -380
- package/dist/{infoDiagram-27XIBGKW-DipQoHxl.js → infoDiagram-27XIBGKW-BJ4vNcZE.js} +1 -1
- package/dist/{ishikawaDiagram-5VMMS53U-DgFMjSGz.js → ishikawaDiagram-5VMMS53U-B_Jy1Dsm.js} +1 -1
- package/dist/{kanban-definition-UXKFOSKX-w1ACVjgl.js → kanban-definition-UXKFOSKX-C6MkS9vw.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 +9706 -7709
- package/dist/{mermaid-CHo49Yv-.js → mermaid-Bh918qrq.js} +52 -52
- package/dist/mermaid-HWGCJPDP-Du3-HPQA.js +2 -0
- package/dist/{mindmap-definition-YA3MSWOX-CrT0KSfX.js → mindmap-definition-YA3MSWOX-BP3ZTuir.js} +1 -1
- package/dist/{number-overlay-editor-R2i0FdxV.js → number-overlay-editor-CzbkG4E2.js} +3 -3
- package/dist/{objects-CzUgoMIF.js → objects-DCmMvZtn.js} +1 -3
- package/dist/{once-BtE-wKRL.js → once-NnpX_HnX.js} +1 -1
- package/dist/{pegDiagram-XKGWAZYB-BqvywNJA.js → pegDiagram-XKGWAZYB-nNGOQtnt.js} +2 -2
- package/dist/{pieDiagram-E7YTZNPT-BfxJ_XCc.js → pieDiagram-E7YTZNPT-CpRz5BZz.js} +1 -1
- package/dist/precisionRound-DhAzeRcm.js +49 -0
- package/dist/{process-output-BZg56CCb.js → process-output-OujAtFW4.js} +5 -5
- package/dist/{quadrantDiagram-AXDQQJYC-Ccw0xMjN.js → quadrantDiagram-AXDQQJYC-Blrm7V7_.js} +1 -1
- package/dist/{railroadDiagram-O6MQD6OU-8vFLVTPu.js → railroadDiagram-O6MQD6OU-BqOEN4I0.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-CEfg6vu9.js} +1 -1
- package/dist/{reveal-component-C42-YBfQ.js → reveal-component-CbEK0sNX.js} +733 -710
- package/dist/{select-BButFi-d.js → select-2KOiReNY.js} +536 -457
- package/dist/{sequenceDiagram-WJ2MYXX4-CXFMnxGh.js → sequenceDiagram-WJ2MYXX4-IOmpQcOi.js} +1 -1
- package/dist/{spec-BwarBpUJ.js → spec-Dkp64vVQ.js} +41 -11
- package/dist/{stateDiagram-D77RDMKH-DQeA_CwI.js → stateDiagram-D77RDMKH-tgU-EY9_.js} +2 -2
- package/dist/{stateDiagram-v2-MP3YSRHH-BxxQucm1.js → stateDiagram-v2-MP3YSRHH-BofhG6q1.js} +2 -2
- package/dist/style.css +1 -1
- package/dist/{swimlanes-42K2YHIH-Do6L-cYg.js → swimlanes-42K2YHIH-DW4vWDBd.js} +1 -1
- package/dist/{swimlanesDiagram-VR7AAH4N-DUzPvSVU.js → swimlanesDiagram-VR7AAH4N-BJcrqUJr.js} +2 -2
- package/dist/{swiper-component-DKntyn8p.js → swiper-component-BVfnhbnt.js} +2 -2
- package/dist/{time-BieKwdYS.js → time-BaMbJs4z.js} +18 -18
- package/dist/{timeline-definition-24CTP7MA-D3c46WM5.js → timeline-definition-24CTP7MA-D_QU3NGs.js} +1 -1
- package/dist/{toDate-UtiwmnH2.js → toDate-BNDEXbfV.js} +44 -43
- 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-ASDni9vH.js} +15 -14
- package/dist/{useDateFormatter-D8XruNGG.js → useDateFormatter-D3Tkfzej.js} +2 -2
- package/dist/{useDeepCompareMemoize-DXwzRINI.js → useDeepCompareMemoize-B1Ogyf53.js} +1 -1
- package/dist/{useIframeCapabilities-DBETvvPl.js → useIframeCapabilities-6a_yDNXp.js} +1 -1
- package/dist/{useLifecycle-j7D9uD1o.js → useLifecycle-Bfpa2m7k.js} +28 -25
- package/dist/{useTheme-CRnjkku0.js → useTheme-5TwyOh3B.js} +522 -314
- package/dist/{vega-component-BeF9dfyO.js → vega-component-BHS9UOtC.js} +158 -158
- 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-YFYsNIAh.js} +1 -1
- package/dist/{wardleyDiagram-VM6X3IG4-48cUWkfA.js → wardleyDiagram-VM6X3IG4-i_n8mAWC.js} +1 -1
- package/dist/{xychartDiagram-S5SC5T6Z-CswYgYeu.js → xychartDiagram-S5SC5T6Z-BYGN95wW.js} +2 -2
- package/package.json +16 -16
- package/src/__mocks__/document.tsx +63 -0
- package/src/__mocks__/notebook.ts +3 -4
- package/src/__mocks__/partial.ts +26 -0
- package/src/__mocks__/requests.ts +0 -3
- package/src/__tests__/main.test.tsx +24 -1
- 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/app-config-form.tsx +5 -9
- 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 +56 -43
- package/src/components/datasources/utils.ts +32 -23
- package/src/components/debugger/debugger-code.tsx +5 -0
- package/src/components/dependency-graph/custom-node.tsx +1 -1
- 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 +13 -19
- 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 -14
- package/src/components/editor/__tests__/dynamic-favicon.test.tsx +111 -18
- package/src/components/editor/__tests__/output-persistence.test.tsx +6 -2
- 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 +26 -31
- package/src/components/editor/actions/useConfigActions.tsx +3 -4
- package/src/components/editor/actions/useNotebookActions.tsx +19 -23
- package/src/components/editor/actions/useRestartKernel.tsx +1 -1
- package/src/components/editor/actions/useSetCodeVisibility.ts +11 -12
- package/src/components/editor/actions/useSetOutputsExpanded.ts +14 -19
- 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/alerts/stdin-blocking-alert.tsx +1 -1
- 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 +36 -43
- package/src/components/editor/cell/code/language-toggle.tsx +8 -7
- package/src/components/editor/cell/collapse.tsx +11 -4
- 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 +10 -6
- package/src/components/editor/chrome/components/__tests__/feedback-button.test.tsx +1 -1
- package/src/components/editor/chrome/components/feedback-button.tsx +2 -8
- 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/dependency-graph-panel.tsx +1 -1
- 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/session-panel.tsx +1 -1
- package/src/components/editor/chrome/panels/snippets-panel.tsx +10 -12
- package/src/components/editor/chrome/panels/variable-panel.tsx +1 -5
- 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/minimap-state.ts +1 -1
- 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/__tests__/cell-column.test.tsx +1 -1
- package/src/components/editor/columns/cell-column.tsx +1 -1
- package/src/components/editor/columns/sortable-column.tsx +2 -2
- package/src/components/editor/connections/components.tsx +5 -10
- package/src/components/editor/controls/Controls.tsx +4 -4
- 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 +17 -29
- 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-list.tsx +1 -1
- package/src/components/editor/links/cell-link.tsx +17 -7
- package/src/components/editor/navigation/__tests__/clipboard.test.ts +60 -38
- package/src/components/editor/navigation/__tests__/navigation.test.ts +108 -85
- package/src/components/editor/navigation/__tests__/selection.test.ts +43 -21
- package/src/components/editor/navigation/clipboard.ts +17 -14
- package/src/components/editor/navigation/focus-utils.ts +23 -4
- package/src/components/editor/navigation/multi-cell-action-toolbar.tsx +17 -33
- package/src/components/editor/navigation/navigation.ts +71 -70
- package/src/components/editor/navigation/selection.ts +30 -29
- package/src/components/editor/notebook-banner.tsx +1 -0
- package/src/components/editor/notebook-cell.tsx +29 -31
- 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 +22 -33
- package/src/components/editor/renderers/grid-layout/grid-layout.tsx +12 -6
- package/src/components/editor/renderers/slides-layout/__tests__/plugin.test.ts +0 -1
- package/src/components/editor/renderers/slides-layout/slides-layout.tsx +1 -1
- package/src/components/editor/renderers/vertical-layout/__tests__/useFocusFirstEditor.test.ts +4 -4
- package/src/components/editor/renderers/vertical-layout/sidebar/toggle.tsx +7 -1
- package/src/components/editor/renderers/vertical-layout/useFocusFirstEditor.ts +1 -1
- package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +1 -1
- package/src/components/editor/stdin-blocking-alert.tsx +1 -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 +19 -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 -11
- package/src/components/slides/__tests__/minimap-actions.test.tsx +22 -29
- package/src/components/slides/__tests__/minimap.test.ts +38 -19
- package/src/components/slides/__tests__/slide-scroll-hint.test.tsx +37 -0
- package/src/components/slides/minimap.tsx +17 -12
- package/src/components/slides/reveal-component.tsx +58 -26
- package/src/components/slides/reveal-slides.css +5 -4
- package/src/components/slides/slide-cell-view.tsx +4 -5
- 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 +8 -25
- package/src/components/tracing/tracing-keyboard.test.tsx +85 -0
- package/src/components/tracing/tracing.tsx +46 -42
- 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/MarimoApp.tsx +1 -1
- package/src/core/ai/__tests__/staged-cells.test.ts +27 -26
- package/src/core/ai/context/providers/__tests__/cell-output.test.ts +8 -12
- package/src/core/ai/context/providers/cell-output.ts +2 -2
- 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 +40 -88
- 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 +86 -80
- 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 -37
- package/src/core/cells/__tests__/actions.test.ts +624 -35
- package/src/core/cells/__tests__/add-missing-import.test.ts +7 -8
- package/src/core/cells/__tests__/arrange.test.ts +432 -0
- package/src/core/cells/__tests__/cell.test.ts +39 -0
- package/src/core/cells/__tests__/cells.test.ts +218 -2491
- package/src/core/cells/__tests__/collapseConsoleOutputs.test.ts +54 -0
- package/src/core/cells/__tests__/document.test.ts +341 -0
- package/src/core/cells/__tests__/editor.test.ts +57 -0
- package/src/core/cells/__tests__/flush-document.test.ts +40 -0
- package/src/core/cells/__tests__/harness.ts +106 -0
- package/src/core/cells/__tests__/layout.test.ts +177 -0
- package/src/core/cells/__tests__/notebook-outline.test.ts +120 -0
- package/src/core/cells/__tests__/outputs.test.tsx +71 -53
- package/src/core/cells/__tests__/session.test.ts +445 -211
- package/src/core/cells/__tests__/utils.test.ts +9 -108
- package/src/core/cells/__tests__/with-document-save.test.ts +67 -0
- package/src/core/cells/actions.ts +636 -6
- package/src/core/cells/add-missing-import.ts +47 -43
- package/src/core/cells/arrange.ts +529 -0
- package/src/core/cells/cell.ts +14 -3
- package/src/core/cells/cells.ts +120 -1337
- package/src/core/cells/columns.ts +38 -0
- package/src/core/cells/document.ts +289 -0
- package/src/core/cells/edit.ts +30 -0
- package/src/core/cells/editor.ts +82 -0
- package/src/core/cells/layout.ts +221 -0
- package/src/core/cells/outputs.ts +12 -28
- package/src/core/cells/pending-delete-service.ts +3 -7
- package/src/core/cells/session.ts +76 -53
- package/src/core/cells/types.ts +22 -6
- package/src/core/cells/utils.ts +10 -8
- 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/copilot/__tests__/getCodes.test.ts +6 -7
- package/src/core/codemirror/copilot/getCodes.ts +2 -2
- 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/__tests__/notebook-lsp.test.ts +1 -2
- package/src/core/codemirror/lsp/notebook-lsp.ts +6 -5
- package/src/core/codemirror/lsp/utils.ts +1 -1
- 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__/app-config.test.ts +64 -0
- package/src/core/config/__tests__/config-schema.test.ts +13 -6
- package/src/core/config/config-schema.ts +13 -3
- package/src/core/config/config.ts +26 -12
- 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/documentation/doc-lookup.ts +1 -1
- package/src/core/edit-app.tsx +21 -28
- package/src/core/errors/error-entries.ts +4 -1
- 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 +69 -43
- package/src/core/islands/__tests__/bridge.test.ts +72 -25
- package/src/core/islands/bootstrap.ts +46 -54
- package/src/core/islands/bridge.ts +74 -23
- package/src/core/islands/components/__tests__/IslandControls.test.tsx +7 -5
- package/src/core/islands/components/web-components.tsx +1 -1
- 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 +19 -72
- package/src/core/meta/__tests__/code-visibility.test.tsx +78 -70
- package/src/core/meta/code-visibility.ts +18 -9
- 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/__tests__/operations.test.ts +51 -0
- package/src/core/notebook/attachments.ts +38 -0
- package/src/core/notebook/document/__tests__/codemirror.test.ts +586 -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 +19 -0
- package/src/core/notebook/operations.ts +30 -0
- package/src/core/notebook/replica/AGENTS.md +11 -0
- package/src/core/notebook/replica/__tests__/attachments.test.ts +56 -0
- package/src/core/{host → notebook/replica}/__tests__/document.test.ts +18 -16
- 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/{host → notebook/replica}/resources/document.ts +4 -4
- 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/__tests__/state.test.ts +111 -0
- package/src/core/saving/__tests__/useAutoSave.test.tsx +67 -0
- package/src/core/saving/filename.ts +1 -1
- package/src/core/saving/save-component.tsx +28 -36
- package/src/core/saving/state.ts +38 -39
- package/src/core/saving/useAutoSave.ts +17 -53
- package/src/core/session/__tests__/backend.test.ts +236 -0
- package/src/core/session/__tests__/useNotebookReplica.hook.test.tsx +184 -0
- package/src/core/session/__tests__/useNotebookSession.hook.test.tsx +663 -0
- package/src/core/session/active-notebook.ts +22 -0
- package/src/core/session/backend.ts +123 -0
- package/src/core/session/embedded.ts +7 -0
- package/src/core/{websocket → session}/types.ts +2 -2
- package/src/core/session/useNotebookReplica.ts +74 -0
- package/src/core/{websocket → session}/useNotebookSession.tsx +77 -72
- 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 +306 -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 +166 -0
- package/src/core/transports/wasm.ts +97 -0
- package/src/core/wasm/__tests__/bridge.test.ts +214 -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 +20 -15
- 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 +2 -5
- 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 +2 -5
- package/src/stories/runtime-story.tsx +23 -17
- 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/code-visibility-DkCqn-PO.js +0 -138
- package/dist/feedback-button-wnAMXOGA.js +0 -379
- package/dist/mermaid-HWGCJPDP-ByCsZnYF.js +0 -2
- package/dist/precisionRound-DMIVQrt2.js +0 -97
- package/src/components/editor/navigation/__tests__/state.test.ts +0 -81
- package/src/components/editor/navigation/state.ts +0 -65
- 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 -11
- package/src/core/host/__tests__/events.test.ts +0 -46
- package/src/core/host/__tests__/fixtures.ts +0 -39
- package/src/core/host/__tests__/notebook-client.test.ts +0 -303
- package/src/core/host/__tests__/protocol.test.ts +0 -214
- package/src/core/host/__tests__/stream.test.ts +0 -211
- package/src/core/host/connection.ts +0 -81
- package/src/core/host/events.ts +0 -60
- package/src/core/host/notebook-client.ts +0 -272
- package/src/core/host/protocol.ts +0 -1943
- package/src/core/host/resources/resource.ts +0 -59
- package/src/core/host/stream.ts +0 -213
- 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/utils/__tests__/id-tree.test.ts +0 -2145
- package/src/utils/id-tree.tsx +0 -1134
- /package/src/{core/attachments/toast.ts → components/editor/agent-toast.ts} +0 -0
- /package/src/core/{websocket → session}/connection-utils.ts +0 -0
package/src/utils/id-tree.tsx
DELETED
|
@@ -1,1134 +0,0 @@
|
|
|
1
|
-
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
-
|
|
3
|
-
import { Memoize } from "typescript-memoize";
|
|
4
|
-
import { reorderColumnSizes } from "@/components/editor/columns/storage";
|
|
5
|
-
import { SETUP_CELL_ID } from "@/core/cells/ids";
|
|
6
|
-
import { arrayDelete, arrayInsert, arrayInsertMany, arrayMove } from "./arrays";
|
|
7
|
-
import { Logger } from "./Logger";
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Branded number to help with type safety
|
|
11
|
-
*/
|
|
12
|
-
export type CellColumnIndex = number & { __brand: "CellColumnIndex" };
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Branded string to help with type safety
|
|
16
|
-
*/
|
|
17
|
-
export type CellColumnId = string & { __brand: "CellColumnId" };
|
|
18
|
-
/**
|
|
19
|
-
* Weakly-branded number, since making `__brand` required causes type errors
|
|
20
|
-
*/
|
|
21
|
-
export type CellIndex = number & { __brand?: "CellIndex" };
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Tree data structure for handling ids with nested children
|
|
25
|
-
*/
|
|
26
|
-
export class TreeNode<T> {
|
|
27
|
-
public value: T;
|
|
28
|
-
public isCollapsed: boolean;
|
|
29
|
-
public children: TreeNode<T>[];
|
|
30
|
-
|
|
31
|
-
public constructor(value: T, isCollapsed: boolean, children: TreeNode<T>[]) {
|
|
32
|
-
this.value = value;
|
|
33
|
-
this.isCollapsed = isCollapsed;
|
|
34
|
-
this.children = children;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Recursively count the number of nodes in the tree
|
|
39
|
-
*/
|
|
40
|
-
@Memoize()
|
|
41
|
-
public geDescendantCount(): number {
|
|
42
|
-
let count = 0;
|
|
43
|
-
const stack = [...this.children];
|
|
44
|
-
|
|
45
|
-
while (stack.length > 0) {
|
|
46
|
-
// oxlint-disable-next-line typescript/no-non-null-assertion
|
|
47
|
-
const node = stack.pop()!;
|
|
48
|
-
count++;
|
|
49
|
-
|
|
50
|
-
// Add children to stack
|
|
51
|
-
for (let i = node.children.length - 1; i >= 0; i--) {
|
|
52
|
-
stack.push(node.children[i]);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return count;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
@Memoize()
|
|
60
|
-
public getDescendants(): T[] {
|
|
61
|
-
const result: T[] = [];
|
|
62
|
-
const stack = [...this.children];
|
|
63
|
-
|
|
64
|
-
while (stack.length > 0) {
|
|
65
|
-
// oxlint-disable-next-line typescript/no-non-null-assertion
|
|
66
|
-
const node = stack.pop()!;
|
|
67
|
-
result.push(node.value);
|
|
68
|
-
|
|
69
|
-
// Add children to stack in reverse order to maintain correct traversal order
|
|
70
|
-
for (let i = node.children.length - 1; i >= 0; i--) {
|
|
71
|
-
stack.push(node.children[i]);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return result;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
@Memoize()
|
|
79
|
-
public get inOrderIds(): T[] {
|
|
80
|
-
const result: T[] = [];
|
|
81
|
-
|
|
82
|
-
// Use depth-first traversal to preserve logical document order
|
|
83
|
-
const traverse = (node: TreeNode<T>) => {
|
|
84
|
-
result.push(node.value);
|
|
85
|
-
for (const child of node.children) {
|
|
86
|
-
traverse(child);
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
for (const child of this.children) {
|
|
91
|
-
traverse(child);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return result;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
public toString(): string {
|
|
98
|
-
if (this.isCollapsed) {
|
|
99
|
-
return `${this.value} (collapsed)`;
|
|
100
|
-
}
|
|
101
|
-
return String(this.value);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
public equals(other: TreeNode<T>): boolean {
|
|
105
|
-
return this.value === other.value;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
let uniqueId = 0;
|
|
110
|
-
|
|
111
|
-
export class CollapsibleTree<T> {
|
|
112
|
-
public readonly nodes: TreeNode<T>[];
|
|
113
|
-
public readonly id: CellColumnId;
|
|
114
|
-
|
|
115
|
-
private constructor(nodes: TreeNode<T>[], id: CellColumnId) {
|
|
116
|
-
this.nodes = nodes;
|
|
117
|
-
this.id = id;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
public static from<T>(ids: T[]): CollapsibleTree<T> {
|
|
121
|
-
const id = `tree_${uniqueId++}` as CellColumnId;
|
|
122
|
-
return new CollapsibleTree(
|
|
123
|
-
ids.map((id) => new TreeNode(id, false, [])),
|
|
124
|
-
id,
|
|
125
|
-
);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Create a new tree from ids, preserving structure from previous tree if possible
|
|
130
|
-
*/
|
|
131
|
-
public static fromWithPreviousShape<T>(
|
|
132
|
-
ids: T[],
|
|
133
|
-
previousTree?: CollapsibleTree<T>,
|
|
134
|
-
): CollapsibleTree<T> {
|
|
135
|
-
if (!previousTree) {
|
|
136
|
-
return CollapsibleTree.from(ids);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Reuse the previous tree's id if possible
|
|
140
|
-
const id = previousTree.id;
|
|
141
|
-
|
|
142
|
-
// Create new tree with nothing collapsed
|
|
143
|
-
let newTree = new CollapsibleTree(
|
|
144
|
-
ids.map((id) => new TreeNode(id, false, [])),
|
|
145
|
-
id,
|
|
146
|
-
);
|
|
147
|
-
|
|
148
|
-
// Collapse nodes that were collapsed in the previous tree
|
|
149
|
-
for (const id of ids) {
|
|
150
|
-
if (previousTree.isCollapsed(id)) {
|
|
151
|
-
const children = previousTree._nodeMap.get(id)?.children ?? [];
|
|
152
|
-
// Find the first child that is also in the new tree, going backwards
|
|
153
|
-
for (let i = children.length - 1; i >= 0; i--) {
|
|
154
|
-
const child = children[i];
|
|
155
|
-
if (newTree._nodeMap.has(child.value)) {
|
|
156
|
-
newTree = newTree.collapse(id, child.value);
|
|
157
|
-
break;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
return newTree;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
public withNodes(nodes: TreeNode<T>[]): CollapsibleTree<T> {
|
|
167
|
-
return new CollapsibleTree(nodes, this.id);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
@Memoize()
|
|
171
|
-
public get topLevelIds(): T[] {
|
|
172
|
-
return this.nodes.map((n) => n.value);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
@Memoize()
|
|
176
|
-
public get inOrderIds(): T[] {
|
|
177
|
-
return this.nodes.flatMap((n) => [n.value, ...n.inOrderIds]);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
@Memoize()
|
|
181
|
-
public get idSet(): Set<T> {
|
|
182
|
-
return new Set(this.inOrderIds);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
public get length(): number {
|
|
186
|
-
return this.nodes.length;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
@Memoize()
|
|
190
|
-
public get _nodeMap(): Map<T, TreeNode<T>> {
|
|
191
|
-
const result = new Map<T, TreeNode<T>>();
|
|
192
|
-
for (const node of this.nodes) {
|
|
193
|
-
result.set(node.value, node);
|
|
194
|
-
}
|
|
195
|
-
return result;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Get the descendants of the given node
|
|
200
|
-
*
|
|
201
|
-
* Only works for the top-level nodes
|
|
202
|
-
*/
|
|
203
|
-
public getDescendants(id: T): T[] {
|
|
204
|
-
const node = this._nodeMap.get(id);
|
|
205
|
-
if (!node) {
|
|
206
|
-
Logger.warn(
|
|
207
|
-
`Node ${id} not found in tree. Valid ids: ${this.topLevelIds}`,
|
|
208
|
-
);
|
|
209
|
-
return [];
|
|
210
|
-
}
|
|
211
|
-
return node.getDescendants();
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* Check if the given node is collapsed
|
|
216
|
-
*
|
|
217
|
-
* Only works for the top-level nodes
|
|
218
|
-
*/
|
|
219
|
-
public isCollapsed(id: T): boolean {
|
|
220
|
-
const node = this._nodeMap.get(id);
|
|
221
|
-
if (!node) {
|
|
222
|
-
Logger.warn(
|
|
223
|
-
`Node ${id} not found in tree. Valid ids: ${this.topLevelIds}`,
|
|
224
|
-
);
|
|
225
|
-
return false;
|
|
226
|
-
}
|
|
227
|
-
return node.isCollapsed;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* Get the index of the given node, or throw
|
|
232
|
-
*/
|
|
233
|
-
public indexOfOrThrow(id: T): CellIndex {
|
|
234
|
-
const index = this.nodes.findIndex((n) => n.value === id);
|
|
235
|
-
if (index === -1) {
|
|
236
|
-
throw new Error(
|
|
237
|
-
`Node ${id} not found in tree. Valid ids: ${this.topLevelIds}`,
|
|
238
|
-
);
|
|
239
|
-
}
|
|
240
|
-
return index as CellIndex;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* Get the top level nodes in the given range.
|
|
245
|
-
* This does not include descendants.
|
|
246
|
-
*/
|
|
247
|
-
public slice(start: number, end: number): T[] {
|
|
248
|
-
return this.nodes.slice(start, end).map((n) => n.value);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* Move the given node to the front
|
|
253
|
-
*/
|
|
254
|
-
public moveToFront(id: T): CollapsibleTree<T> {
|
|
255
|
-
const index = this.indexOfOrThrow(id);
|
|
256
|
-
return this.withNodes(arrayMove(this.nodes, index, 0));
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Move the given node to the back
|
|
261
|
-
*/
|
|
262
|
-
public moveToBack(id: T): CollapsibleTree<T> {
|
|
263
|
-
const index = this.indexOfOrThrow(id);
|
|
264
|
-
return this.withNodes(arrayMove(this.nodes, index, this.nodes.length - 1));
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* Collapse everything past the given node @param id
|
|
269
|
-
* until @param until or the end of the tree
|
|
270
|
-
*/
|
|
271
|
-
public collapse(id: T, until: T | undefined): CollapsibleTree<T> {
|
|
272
|
-
const nodeIndex = this.nodes.findIndex((n) => n.value === id);
|
|
273
|
-
if (nodeIndex === -1) {
|
|
274
|
-
throw new Error(
|
|
275
|
-
`Node ${id} not found in tree. Valid ids: ${this.topLevelIds}`,
|
|
276
|
-
);
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
const untilIndex =
|
|
280
|
-
until === undefined
|
|
281
|
-
? this.nodes.length
|
|
282
|
-
: this.nodes.findIndex((n) => n.value === until);
|
|
283
|
-
|
|
284
|
-
if (untilIndex === -1) {
|
|
285
|
-
throw new Error(`Node ${until} not found in tree`);
|
|
286
|
-
}
|
|
287
|
-
if (untilIndex < nodeIndex) {
|
|
288
|
-
throw new Error(`Node ${until} is before node ${id}`);
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
const nodes = [...this.nodes];
|
|
292
|
-
const node = nodes[nodeIndex];
|
|
293
|
-
if (node.isCollapsed) {
|
|
294
|
-
throw new Error(`Node ${id} is already collapsed`);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// Fold the next nodes into the current node
|
|
298
|
-
const children = nodes.splice(nodeIndex + 1, untilIndex - nodeIndex);
|
|
299
|
-
nodes[nodeIndex] = new TreeNode(node.value, true, children);
|
|
300
|
-
|
|
301
|
-
return this.withNodes(nodes);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
/**
|
|
305
|
-
* Collapse all nodes in the tree, including nested ones
|
|
306
|
-
*
|
|
307
|
-
* Only works for the top-level nodes
|
|
308
|
-
* Does not collapse the children of already collapsed nodes
|
|
309
|
-
*/
|
|
310
|
-
public collapseAll(
|
|
311
|
-
collapseRanges: ({ id: T; until: T | undefined } | null)[],
|
|
312
|
-
): CollapsibleTree<T> {
|
|
313
|
-
const nodes = [...this.nodes];
|
|
314
|
-
if (collapseRanges.length === 0) {
|
|
315
|
-
throw new Error("No collapse ranges provided");
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
if (collapseRanges.length !== nodes.length) {
|
|
319
|
-
throw new Error(
|
|
320
|
-
`Collapse ranges length ${collapseRanges.length} does not match tree length ${nodes.length}`,
|
|
321
|
-
);
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
// Start from the end of the list and collapse nodes children first
|
|
325
|
-
let nodeIndex = nodes.length - 1;
|
|
326
|
-
while (nodeIndex >= 0) {
|
|
327
|
-
const node = nodes[nodeIndex];
|
|
328
|
-
const range = collapseRanges[nodeIndex];
|
|
329
|
-
|
|
330
|
-
if (!node.isCollapsed && range) {
|
|
331
|
-
const { id, until } = range;
|
|
332
|
-
if (id !== node.value) {
|
|
333
|
-
throw new Error(
|
|
334
|
-
`Node ${node.value} does not match collapse range id ${id}`,
|
|
335
|
-
);
|
|
336
|
-
}
|
|
337
|
-
const untilIndex =
|
|
338
|
-
until === undefined
|
|
339
|
-
? nodes.length
|
|
340
|
-
: nodes.findIndex((n) => n.value === until);
|
|
341
|
-
|
|
342
|
-
if (untilIndex === -1) {
|
|
343
|
-
throw new Error(`Node ${until} not found in tree`);
|
|
344
|
-
}
|
|
345
|
-
if (untilIndex < nodeIndex) {
|
|
346
|
-
throw new Error(`Node ${until} is before node ${id}`);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
// Fold the next nodes into the current node
|
|
350
|
-
const children = nodes.splice(nodeIndex + 1, untilIndex - nodeIndex);
|
|
351
|
-
nodes[nodeIndex] = new TreeNode(node.value, true, children);
|
|
352
|
-
nodeIndex--;
|
|
353
|
-
} else {
|
|
354
|
-
// Move to the next node
|
|
355
|
-
nodeIndex--;
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
return this.withNodes(nodes);
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
/**
|
|
363
|
-
* Expand a node and all of its children.
|
|
364
|
-
* If the node is already expanded, returns the same tree (no-op).
|
|
365
|
-
*/
|
|
366
|
-
public expand(id: T): CollapsibleTree<T> {
|
|
367
|
-
const nodeIndex = this.nodes.findIndex((n) => n.value === id);
|
|
368
|
-
if (nodeIndex === -1) {
|
|
369
|
-
throw new Error(
|
|
370
|
-
`Node ${id} not found in tree. Valid ids: ${this.topLevelIds}`,
|
|
371
|
-
);
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
let nodes = [...this.nodes];
|
|
375
|
-
const node = nodes[nodeIndex];
|
|
376
|
-
if (!node.isCollapsed) {
|
|
377
|
-
// Already expanded, no-op
|
|
378
|
-
return this;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
nodes[nodeIndex] = new TreeNode(node.value, false, []);
|
|
382
|
-
nodes = arrayInsertMany(nodes, nodeIndex + 1, node.children);
|
|
383
|
-
|
|
384
|
-
return this.withNodes(nodes);
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
/**
|
|
388
|
-
* Expand all collapsed nodes in the tree, including nested ones
|
|
389
|
-
*/
|
|
390
|
-
public expandAll(): CollapsibleTree<T> {
|
|
391
|
-
let nodes = [...this.nodes];
|
|
392
|
-
let nodeIndex = 0;
|
|
393
|
-
|
|
394
|
-
while (nodeIndex < nodes.length) {
|
|
395
|
-
const node = nodes[nodeIndex];
|
|
396
|
-
if (node.isCollapsed) {
|
|
397
|
-
// Replace the collapsed node with an expanded one
|
|
398
|
-
nodes[nodeIndex] = new TreeNode(node.value, false, []);
|
|
399
|
-
// Add the children of the collapsed node to the list
|
|
400
|
-
nodes = arrayInsertMany(nodes, nodeIndex + 1, node.children);
|
|
401
|
-
nodeIndex++;
|
|
402
|
-
} else {
|
|
403
|
-
// Move to the next node
|
|
404
|
-
nodeIndex++;
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
return this.withNodes(nodes);
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
/**
|
|
412
|
-
* Move a node from one index to another
|
|
413
|
-
*/
|
|
414
|
-
public move(fromIdx: number, toIdx: number): CollapsibleTree<T> {
|
|
415
|
-
return this.withNodes(arrayMove(this.nodes, fromIdx, toIdx));
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
/**
|
|
419
|
-
* Get the node at the given index
|
|
420
|
-
*/
|
|
421
|
-
public at(index: number): T | undefined {
|
|
422
|
-
return this.nodes.at(index)?.value;
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
/**
|
|
426
|
-
* Get the node at the given index
|
|
427
|
-
*/
|
|
428
|
-
public atOrThrow(index: number): T {
|
|
429
|
-
const node = this.nodes.at(index);
|
|
430
|
-
if (node === undefined) {
|
|
431
|
-
throw new Error(`Node at index ${index} not found in tree`);
|
|
432
|
-
}
|
|
433
|
-
return node.value;
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
/**
|
|
437
|
-
* Get the first node, or throw
|
|
438
|
-
*/
|
|
439
|
-
public first(): T {
|
|
440
|
-
return this.atOrThrow(0);
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
/**
|
|
444
|
-
* Get the last node, or throw
|
|
445
|
-
*/
|
|
446
|
-
public last(): T {
|
|
447
|
-
return this.atOrThrow(this.nodes.length - 1);
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
/**
|
|
451
|
-
* Get the next node after the given node, does not wrap.
|
|
452
|
-
*/
|
|
453
|
-
public after(id: T): T | undefined {
|
|
454
|
-
const index = this.indexOfOrThrow(id);
|
|
455
|
-
if (index === this.nodes.length - 1) {
|
|
456
|
-
return undefined;
|
|
457
|
-
}
|
|
458
|
-
return this.at(index + 1);
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
/**
|
|
462
|
-
* Get the previous node before the given node, does not wrap.
|
|
463
|
-
*/
|
|
464
|
-
public before(id: T): T | undefined {
|
|
465
|
-
const index = this.indexOfOrThrow(id);
|
|
466
|
-
if (index === 0) {
|
|
467
|
-
return undefined;
|
|
468
|
-
}
|
|
469
|
-
return this.at(index - 1);
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
/**
|
|
473
|
-
* Insert a node at the given index
|
|
474
|
-
*/
|
|
475
|
-
public insert(id: T, index: number): CollapsibleTree<T> {
|
|
476
|
-
return this.withNodes(
|
|
477
|
-
arrayInsert(this.nodes, index, new TreeNode(id, false, [])),
|
|
478
|
-
);
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
/**
|
|
482
|
-
* Insert a node at the end
|
|
483
|
-
*/
|
|
484
|
-
public insertAtEnd(id: T): CollapsibleTree<T> {
|
|
485
|
-
return this.insert(id, this.nodes.length);
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
/**
|
|
489
|
-
* Insert a node at the start
|
|
490
|
-
*/
|
|
491
|
-
public insertAtStart(id: T): CollapsibleTree<T> {
|
|
492
|
-
return this.insert(id, 0);
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
/**
|
|
496
|
-
* Delete a node, expand if it was collapsed
|
|
497
|
-
*/
|
|
498
|
-
public deleteAtIndex(idx: number): CollapsibleTree<T> {
|
|
499
|
-
const id = this.atOrThrow(idx);
|
|
500
|
-
// Expand the node first (if collapsed) to bring children back to top level
|
|
501
|
-
const tree = this.expand(id);
|
|
502
|
-
return tree.withNodes(arrayDelete(tree.nodes, idx));
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
public delete(id: T): CollapsibleTree<T> {
|
|
506
|
-
const index = this.indexOfOrThrow(id);
|
|
507
|
-
return this.deleteAtIndex(index);
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
/**
|
|
511
|
-
* Get the number of nodes in the tree, not-including the given node
|
|
512
|
-
*/
|
|
513
|
-
public getCount(id: T): number {
|
|
514
|
-
return this._nodeMap.get(id)?.geDescendantCount() ?? 0;
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
/**
|
|
518
|
-
* Find and expand the node and all of its children
|
|
519
|
-
*/
|
|
520
|
-
public findAndExpandDeep(id: T): CollapsibleTree<T> {
|
|
521
|
-
const found = this.find(id);
|
|
522
|
-
if (found.length === 0) {
|
|
523
|
-
return this;
|
|
524
|
-
}
|
|
525
|
-
return found.reduce<CollapsibleTree<T>>(
|
|
526
|
-
(acc, node) => acc.expand(node),
|
|
527
|
-
this,
|
|
528
|
-
);
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
/**
|
|
532
|
-
* Find a node, returning the path to it
|
|
533
|
-
* With the last element being the node itself
|
|
534
|
-
*/
|
|
535
|
-
public find(id: T): T[] {
|
|
536
|
-
// We need to recursively find the node
|
|
537
|
-
function findNode(nodes: TreeNode<T>[], path: T[]): T[] {
|
|
538
|
-
for (const node of nodes) {
|
|
539
|
-
if (node.value === id) {
|
|
540
|
-
return [...path, id];
|
|
541
|
-
}
|
|
542
|
-
const result = findNode(node.children, [...path, node.value]);
|
|
543
|
-
if (result.length > 0) {
|
|
544
|
-
return result;
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
return [];
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
return findNode(this.nodes, []);
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
/**
|
|
554
|
-
* Split the tree into two trees
|
|
555
|
-
* @param id the id of the node to split at
|
|
556
|
-
* @returns a tuple of the left and right trees
|
|
557
|
-
*/
|
|
558
|
-
public split(id: T): [CollapsibleTree<T>, CollapsibleTree<T> | undefined] {
|
|
559
|
-
const index = this.nodes.findIndex((n) => n.value === id);
|
|
560
|
-
if (index === -1) {
|
|
561
|
-
throw new Error(`Node ${id} not found in tree`);
|
|
562
|
-
}
|
|
563
|
-
const leftNodes = this.nodes.slice(0, index);
|
|
564
|
-
const rightNodes = this.nodes.slice(index);
|
|
565
|
-
if (leftNodes.length === 0) {
|
|
566
|
-
return [CollapsibleTree.from<T>([]), this.withNodes(rightNodes)];
|
|
567
|
-
}
|
|
568
|
-
const left = this.withNodes(leftNodes);
|
|
569
|
-
const right = CollapsibleTree.from<T>([]).withNodes(rightNodes);
|
|
570
|
-
return [left, right];
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
public equals(other: CollapsibleTree<T>): boolean {
|
|
574
|
-
return (
|
|
575
|
-
this.nodes.length === other.nodes.length &&
|
|
576
|
-
this.nodes.every((n, i) => n.value === other.nodes[i].value)
|
|
577
|
-
);
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
public toString(): string {
|
|
581
|
-
let depth = 0;
|
|
582
|
-
let result = "";
|
|
583
|
-
const asString = (nodes: TreeNode<T>[]) => {
|
|
584
|
-
for (const node of nodes) {
|
|
585
|
-
result += `${" ".repeat(depth * 2)}${node.toString()}\n`;
|
|
586
|
-
depth += 1;
|
|
587
|
-
asString(node.children);
|
|
588
|
-
depth -= 1;
|
|
589
|
-
}
|
|
590
|
-
};
|
|
591
|
-
asString(this.nodes);
|
|
592
|
-
return result;
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
export class MultiColumn<T> {
|
|
597
|
-
private readonly columns: readonly CollapsibleTree<T>[];
|
|
598
|
-
|
|
599
|
-
public constructor(columns: readonly CollapsibleTree<T>[]) {
|
|
600
|
-
this.columns = columns;
|
|
601
|
-
|
|
602
|
-
// Ensure there is always at least one column
|
|
603
|
-
if (columns.length === 0) {
|
|
604
|
-
this.columns = [CollapsibleTree.from([])];
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
public static from<T>(idsList: T[][]): MultiColumn<T> {
|
|
609
|
-
return new MultiColumn(idsList.map((ids) => CollapsibleTree.from(ids)));
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
/**
|
|
613
|
-
* Create a new MultiColumn from idsList,
|
|
614
|
-
* attempting to preserve structure from previous MultiColumn if possible.
|
|
615
|
-
*/
|
|
616
|
-
public static fromWithPreviousShape<T>(
|
|
617
|
-
idsList: T[],
|
|
618
|
-
previousShape: MultiColumn<T>,
|
|
619
|
-
): MultiColumn<T> {
|
|
620
|
-
if (!previousShape) {
|
|
621
|
-
return MultiColumn.from([idsList]);
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
// Get previous columns for reference
|
|
625
|
-
const previousColumns = previousShape.getColumns();
|
|
626
|
-
|
|
627
|
-
// Split the ids into their respective columns
|
|
628
|
-
const splitIds: T[][] = Array.from(
|
|
629
|
-
{ length: previousColumns.length },
|
|
630
|
-
() => [],
|
|
631
|
-
);
|
|
632
|
-
let lastUsedColumnIdx = 0;
|
|
633
|
-
for (const id of idsList) {
|
|
634
|
-
const column = previousColumns.findIndex((c) => c.idSet.has(id));
|
|
635
|
-
if (column === -1) {
|
|
636
|
-
// No column found, use the last used column
|
|
637
|
-
splitIds[lastUsedColumnIdx].push(id);
|
|
638
|
-
} else {
|
|
639
|
-
lastUsedColumnIdx = column;
|
|
640
|
-
splitIds[column].push(id);
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
const newColumns = splitIds.map((ids, idx) =>
|
|
645
|
-
CollapsibleTree.fromWithPreviousShape(ids, previousColumns[idx]),
|
|
646
|
-
);
|
|
647
|
-
|
|
648
|
-
return new MultiColumn(newColumns);
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
@Memoize()
|
|
652
|
-
public isEmpty(): boolean {
|
|
653
|
-
if (this.columns.length === 0) {
|
|
654
|
-
return true;
|
|
655
|
-
}
|
|
656
|
-
return this.columns.every((c) => c.nodes.length === 0);
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
public static fromIdsAndColumns<T>(
|
|
660
|
-
idAndColumns: [T, number | undefined | null][],
|
|
661
|
-
): MultiColumn<T> {
|
|
662
|
-
// If column is undefined, use the previous column
|
|
663
|
-
// Ensure there is always at least one column
|
|
664
|
-
const numColumns = Math.max(
|
|
665
|
-
1,
|
|
666
|
-
...idAndColumns.map(([, column]) => (column ?? 0) + 1),
|
|
667
|
-
);
|
|
668
|
-
const idsList: T[][] = Array.from({ length: numColumns }, () => []);
|
|
669
|
-
|
|
670
|
-
let prevColumn = 0;
|
|
671
|
-
for (const [id, column] of idAndColumns) {
|
|
672
|
-
// Avoid negative column indices
|
|
673
|
-
if (column === undefined || column === null || column < 0) {
|
|
674
|
-
idsList[prevColumn].push(id);
|
|
675
|
-
} else {
|
|
676
|
-
idsList[column].push(id);
|
|
677
|
-
prevColumn = column;
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
return MultiColumn.from(idsList);
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
@Memoize()
|
|
685
|
-
public get topLevelIds(): T[][] {
|
|
686
|
-
return this.columns.map((c) => c.topLevelIds);
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
public get iterateTopLevelIds(): Iterable<T> {
|
|
690
|
-
const columns = this.columns;
|
|
691
|
-
|
|
692
|
-
function* iter() {
|
|
693
|
-
for (const column of columns) {
|
|
694
|
-
for (const id of column.topLevelIds) {
|
|
695
|
-
yield id;
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
return iter();
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
@Memoize()
|
|
704
|
-
public get inOrderIds(): T[] {
|
|
705
|
-
return this.columns.flatMap((c) => c.inOrderIds);
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
public get colLength(): number {
|
|
709
|
-
return this.columns.length;
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
@Memoize()
|
|
713
|
-
public get idLength(): number {
|
|
714
|
-
return this.columns.reduce((acc, c) => acc + c.nodes.length, 0);
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
@Memoize()
|
|
718
|
-
public get _columnMap(): Map<CellColumnId, CollapsibleTree<T>> {
|
|
719
|
-
return new Map(this.columns.map((c) => [c.id, c]));
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
public at(idx: number): CollapsibleTree<T> | undefined {
|
|
723
|
-
return this.columns[idx];
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
public get(columnId: CellColumnId): CollapsibleTree<T> | undefined {
|
|
727
|
-
return this._columnMap.get(columnId);
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
public atOrThrow(idx: number): CollapsibleTree<T> {
|
|
731
|
-
const column = this.columns[idx];
|
|
732
|
-
if (!column) {
|
|
733
|
-
throw new Error(`Column ${idx} not found`);
|
|
734
|
-
}
|
|
735
|
-
return column;
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
public hasOnlyOneColumn(): boolean {
|
|
739
|
-
return this.columns.length === 1;
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
public getColumns(): readonly CollapsibleTree<T>[] {
|
|
743
|
-
return this.columns;
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
public hasOnlyOneId(): boolean {
|
|
747
|
-
return this.idLength === 1;
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
@Memoize()
|
|
751
|
-
public getColumnIds(): CellColumnId[] {
|
|
752
|
-
return this.columns.map((c) => c.id);
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
public indexOf(column: CollapsibleTree<T>): number {
|
|
756
|
-
return this.columns.indexOf(column);
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
public addColumn(
|
|
760
|
-
columnId: CellColumnId,
|
|
761
|
-
initialIds: T[] = [],
|
|
762
|
-
): MultiColumn<T> {
|
|
763
|
-
return new MultiColumn(
|
|
764
|
-
this.columns.flatMap((c) => {
|
|
765
|
-
if (c.id === columnId) {
|
|
766
|
-
return [c, CollapsibleTree.from(initialIds)];
|
|
767
|
-
}
|
|
768
|
-
return [c];
|
|
769
|
-
}),
|
|
770
|
-
);
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
public insertBreakpoint(cellId: T): MultiColumn<T> {
|
|
774
|
-
const column = this.findWithId(cellId);
|
|
775
|
-
const [left, right] = column.split(cellId);
|
|
776
|
-
const newColumns = this.columns.flatMap((c) => {
|
|
777
|
-
if (c === column) {
|
|
778
|
-
return [left, right].filter(Boolean);
|
|
779
|
-
}
|
|
780
|
-
return [c];
|
|
781
|
-
});
|
|
782
|
-
return new MultiColumn(newColumns);
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
public delete(columnId: CellColumnId): MultiColumn<T> {
|
|
786
|
-
// Move cells to preceding column
|
|
787
|
-
// If its the first column, move the cells to the next column
|
|
788
|
-
|
|
789
|
-
// Noop if there is only one column
|
|
790
|
-
if (this.columns.length <= 1) {
|
|
791
|
-
return this;
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
const columnIndex = this.indexOfOrThrow(columnId);
|
|
795
|
-
const targetColumnIndex = columnIndex === 0 ? 1 : columnIndex - 1;
|
|
796
|
-
|
|
797
|
-
const columns = [...this.columns];
|
|
798
|
-
const column = columns[columnIndex];
|
|
799
|
-
columns[targetColumnIndex] = column.withNodes([
|
|
800
|
-
...columns[targetColumnIndex].nodes,
|
|
801
|
-
...column.nodes,
|
|
802
|
-
]);
|
|
803
|
-
columns.splice(columnIndex, 1);
|
|
804
|
-
return new MultiColumn(columns);
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
public mergeAllColumns(): MultiColumn<T> {
|
|
808
|
-
if (this.columns.length <= 1) {
|
|
809
|
-
return this;
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
const nodes = this.columns.flatMap((c) => c.nodes);
|
|
813
|
-
const firstColumn = this.columns[0];
|
|
814
|
-
return new MultiColumn([firstColumn.withNodes(nodes)]);
|
|
815
|
-
}
|
|
816
|
-
|
|
817
|
-
public moveWithinColumn(
|
|
818
|
-
col: CellColumnId,
|
|
819
|
-
fromIdx: CellIndex,
|
|
820
|
-
toIdx: CellIndex,
|
|
821
|
-
): MultiColumn<T> {
|
|
822
|
-
return this.transform(col, (c) => {
|
|
823
|
-
return c.move(fromIdx, toIdx);
|
|
824
|
-
});
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
public moveAcrossColumns(
|
|
828
|
-
fromCol: CellColumnId,
|
|
829
|
-
fromId: T,
|
|
830
|
-
toCol: CellColumnId,
|
|
831
|
-
toId: T | undefined,
|
|
832
|
-
): MultiColumn<T> {
|
|
833
|
-
// Find indices once to avoid repeated lookups
|
|
834
|
-
const fromColumnIndex = this.indexOfOrThrow(fromCol);
|
|
835
|
-
const toColumnIndex = this.indexOfOrThrow(toCol);
|
|
836
|
-
|
|
837
|
-
// Only copy the columns we're modifying
|
|
838
|
-
const newColumns = [...this.columns];
|
|
839
|
-
const fromColumn = newColumns[fromColumnIndex];
|
|
840
|
-
|
|
841
|
-
// Find the node once
|
|
842
|
-
const nodeIndex = fromColumn.nodes.findIndex((n) => n.value === fromId);
|
|
843
|
-
if (nodeIndex === -1) {
|
|
844
|
-
throw new Error(`Node ${fromId} not found in column ${fromCol}`);
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
const node = fromColumn.nodes[nodeIndex];
|
|
848
|
-
|
|
849
|
-
// Create new columns with the changes
|
|
850
|
-
newColumns[fromColumnIndex] = fromColumn.withNodes(
|
|
851
|
-
fromColumn.nodes.filter((_, i) => i !== nodeIndex),
|
|
852
|
-
);
|
|
853
|
-
|
|
854
|
-
const toColumn = newColumns[toColumnIndex];
|
|
855
|
-
if (toId) {
|
|
856
|
-
const toIndex = toColumn.indexOfOrThrow(toId);
|
|
857
|
-
newColumns[toColumnIndex] = toColumn.withNodes(
|
|
858
|
-
arrayInsert(toColumn.nodes, toIndex, node),
|
|
859
|
-
);
|
|
860
|
-
} else {
|
|
861
|
-
newColumns[toColumnIndex] = toColumn.withNodes([node, ...toColumn.nodes]);
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
return new MultiColumn(newColumns);
|
|
865
|
-
}
|
|
866
|
-
|
|
867
|
-
/**
|
|
868
|
-
* Move multiple cells to be immediately before (or after) a target cell.
|
|
869
|
-
* Cells are inserted in their original order.
|
|
870
|
-
*
|
|
871
|
-
* @throws Error if any cellId is not found in any column.
|
|
872
|
-
* If targetId is among the moved cells, cells are inserted at the end of the target column.
|
|
873
|
-
*/
|
|
874
|
-
public moveCellsRelativeTo(
|
|
875
|
-
cellIds: T[],
|
|
876
|
-
targetId: T,
|
|
877
|
-
position: "before" | "after",
|
|
878
|
-
): MultiColumn<T> {
|
|
879
|
-
if (cellIds.length === 0) {
|
|
880
|
-
return this;
|
|
881
|
-
}
|
|
882
|
-
|
|
883
|
-
const cellIdSet = new Set(cellIds);
|
|
884
|
-
const targetColumn = this.findWithId(targetId);
|
|
885
|
-
const targetColIndex = this.indexOfOrThrow(targetColumn.id);
|
|
886
|
-
|
|
887
|
-
// Collect nodes to move
|
|
888
|
-
const nodesToMove: TreeNode<T>[] = [];
|
|
889
|
-
for (const id of cellIds) {
|
|
890
|
-
const col = this.findWithId(id);
|
|
891
|
-
const node = col.nodes.find((n) => n.value === id);
|
|
892
|
-
if (!node) {
|
|
893
|
-
throw new Error(`Node ${id} not found in column ${col.id}`);
|
|
894
|
-
}
|
|
895
|
-
nodesToMove.push(node);
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
// Remove moved cells from all columns
|
|
899
|
-
const columnsWithRemovals = this.columns.map((col) =>
|
|
900
|
-
col.withNodes(col.nodes.filter((n) => !cellIdSet.has(n.value))),
|
|
901
|
-
);
|
|
902
|
-
|
|
903
|
-
// Find target index in the cleaned column
|
|
904
|
-
const cleanedTargetCol = columnsWithRemovals[targetColIndex];
|
|
905
|
-
let insertIndex = cleanedTargetCol.nodes.findIndex(
|
|
906
|
-
(n) => n.value === targetId,
|
|
907
|
-
);
|
|
908
|
-
|
|
909
|
-
// If target was one of the moved cells, insert at end
|
|
910
|
-
if (insertIndex === -1) {
|
|
911
|
-
insertIndex = cleanedTargetCol.nodes.length;
|
|
912
|
-
} else if (position === "after") {
|
|
913
|
-
insertIndex += 1;
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
// Insert all moved nodes at target position
|
|
917
|
-
const newTargetNodes = arrayInsertMany(
|
|
918
|
-
cleanedTargetCol.nodes,
|
|
919
|
-
insertIndex,
|
|
920
|
-
nodesToMove,
|
|
921
|
-
);
|
|
922
|
-
columnsWithRemovals[targetColIndex] =
|
|
923
|
-
cleanedTargetCol.withNodes(newTargetNodes);
|
|
924
|
-
|
|
925
|
-
return new MultiColumn(columnsWithRemovals);
|
|
926
|
-
}
|
|
927
|
-
|
|
928
|
-
public indexOfOrThrow(id: CellColumnId): number {
|
|
929
|
-
const index = this.columns.findIndex((c) => c.id === id);
|
|
930
|
-
if (index === -1) {
|
|
931
|
-
throw new Error(
|
|
932
|
-
`Column ${id} not found. Possible values: ${this.getColumnIds().join(", ")}`,
|
|
933
|
-
);
|
|
934
|
-
}
|
|
935
|
-
return index;
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
public moveColumn(
|
|
939
|
-
fromCol: CellColumnId,
|
|
940
|
-
toCol: CellColumnId | "_left_" | "_right_",
|
|
941
|
-
): MultiColumn<T> {
|
|
942
|
-
if (fromCol === toCol) {
|
|
943
|
-
return this;
|
|
944
|
-
}
|
|
945
|
-
const fromIdx = this.indexOfOrThrow(fromCol);
|
|
946
|
-
const toIdx =
|
|
947
|
-
toCol === "_left_"
|
|
948
|
-
? fromIdx - 1
|
|
949
|
-
: toCol === "_right_"
|
|
950
|
-
? fromIdx + 1
|
|
951
|
-
: this.indexOfOrThrow(toCol);
|
|
952
|
-
|
|
953
|
-
reorderColumnSizes(fromIdx, toIdx);
|
|
954
|
-
return new MultiColumn(arrayMove([...this.columns], fromIdx, toIdx));
|
|
955
|
-
}
|
|
956
|
-
|
|
957
|
-
public moveToNewColumn(cellId: T): MultiColumn<T> {
|
|
958
|
-
const fromColumn = this.findWithId(cellId);
|
|
959
|
-
|
|
960
|
-
// Create new columns array with the cell removed from its original column
|
|
961
|
-
const columns = this.columns.map((c) => {
|
|
962
|
-
if (c.id === fromColumn.id) {
|
|
963
|
-
return c.delete(cellId);
|
|
964
|
-
}
|
|
965
|
-
return c;
|
|
966
|
-
});
|
|
967
|
-
|
|
968
|
-
// Insert into new column
|
|
969
|
-
const newColumn = CollapsibleTree.from([cellId]);
|
|
970
|
-
return new MultiColumn([...columns, newColumn]);
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
public findWithId(id: T): CollapsibleTree<T> {
|
|
974
|
-
const found = this.columns.find((c) => {
|
|
975
|
-
return c.inOrderIds.includes(id);
|
|
976
|
-
});
|
|
977
|
-
if (!found) {
|
|
978
|
-
Logger.log(
|
|
979
|
-
`Possible values: ${this.columns.map((c) => c.inOrderIds).join(", ")}`,
|
|
980
|
-
);
|
|
981
|
-
throw new Error(`Cell ${id} not found in any column`);
|
|
982
|
-
}
|
|
983
|
-
return found;
|
|
984
|
-
}
|
|
985
|
-
|
|
986
|
-
/**
|
|
987
|
-
* Transform the column containing the given cell id
|
|
988
|
-
* @param id the id of the cell to transform
|
|
989
|
-
* @param fn the function to transform the column
|
|
990
|
-
* @returns new MultiColumn with the transformed column
|
|
991
|
-
* If the column was not updated, we return the object.
|
|
992
|
-
*/
|
|
993
|
-
public transformWithCellId(
|
|
994
|
-
id: T,
|
|
995
|
-
fn: (tree: CollapsibleTree<T>) => CollapsibleTree<T>,
|
|
996
|
-
): MultiColumn<T> {
|
|
997
|
-
let didChange = false;
|
|
998
|
-
|
|
999
|
-
const columns = this.columns.map((c) => {
|
|
1000
|
-
if (c.inOrderIds.includes(id)) {
|
|
1001
|
-
const newColumn = fn(c);
|
|
1002
|
-
if (c !== newColumn) {
|
|
1003
|
-
didChange = true;
|
|
1004
|
-
}
|
|
1005
|
-
return newColumn;
|
|
1006
|
-
}
|
|
1007
|
-
return c;
|
|
1008
|
-
});
|
|
1009
|
-
|
|
1010
|
-
// Avoid unnecessary re-renders
|
|
1011
|
-
if (!didChange) {
|
|
1012
|
-
return this;
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
return new MultiColumn(columns);
|
|
1016
|
-
}
|
|
1017
|
-
|
|
1018
|
-
public setupCellExists(): boolean {
|
|
1019
|
-
return this.columns.some((c) => c.topLevelIds.includes(SETUP_CELL_ID as T));
|
|
1020
|
-
}
|
|
1021
|
-
|
|
1022
|
-
public insertId(id: T, col: CellColumnId, index: CellIndex): MultiColumn<T> {
|
|
1023
|
-
return this.transform(col, (c) => c.insert(id, index));
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1026
|
-
public deleteById(cellId: T): MultiColumn<T> {
|
|
1027
|
-
return this.transformWithCellId(cellId, (c) => c.delete(cellId));
|
|
1028
|
-
}
|
|
1029
|
-
|
|
1030
|
-
/**
|
|
1031
|
-
* Remove the given cells from the tree, then re-insert each at its
|
|
1032
|
-
* (columnId, index). Used to undo a move (e.g. cut-paste) by restoring
|
|
1033
|
-
* cells to their previous positions. Placements are sorted by
|
|
1034
|
-
* (columnId, index) so insert order keeps indices valid.
|
|
1035
|
-
*/
|
|
1036
|
-
public placeCells(
|
|
1037
|
-
placements: Array<{ id: T; columnId: CellColumnId; index: CellIndex }>,
|
|
1038
|
-
): MultiColumn<T> {
|
|
1039
|
-
if (placements.length === 0) {
|
|
1040
|
-
return this;
|
|
1041
|
-
}
|
|
1042
|
-
let result = this.deleteById(placements[0].id);
|
|
1043
|
-
for (const { id } of placements.slice(1)) {
|
|
1044
|
-
result = result.deleteById(id);
|
|
1045
|
-
}
|
|
1046
|
-
const sorted = [...placements].toSorted((a, b) => {
|
|
1047
|
-
if (a.columnId !== b.columnId) {
|
|
1048
|
-
return a.columnId.localeCompare(b.columnId);
|
|
1049
|
-
}
|
|
1050
|
-
return (a.index as number) - (b.index as number);
|
|
1051
|
-
});
|
|
1052
|
-
for (const { id, columnId, index } of sorted) {
|
|
1053
|
-
result = result.insertId(id, columnId, index);
|
|
1054
|
-
}
|
|
1055
|
-
return result;
|
|
1056
|
-
}
|
|
1057
|
-
|
|
1058
|
-
public compact(): MultiColumn<T> {
|
|
1059
|
-
// Don't compact if there's only one column
|
|
1060
|
-
if (this.columns.length === 1) {
|
|
1061
|
-
return this;
|
|
1062
|
-
}
|
|
1063
|
-
// If no need to compact, return the same tree
|
|
1064
|
-
// to avoid unnecessary re-renders
|
|
1065
|
-
const someEmpty = this.columns.some((c) => c.nodes.length === 0);
|
|
1066
|
-
if (!someEmpty) {
|
|
1067
|
-
return this;
|
|
1068
|
-
}
|
|
1069
|
-
|
|
1070
|
-
return new MultiColumn(this.columns.filter((c) => c.nodes.length > 0));
|
|
1071
|
-
}
|
|
1072
|
-
|
|
1073
|
-
/**
|
|
1074
|
-
* Transform the column with the given column id
|
|
1075
|
-
*/
|
|
1076
|
-
public transform(
|
|
1077
|
-
columnId: CellColumnId,
|
|
1078
|
-
fn: (tree: CollapsibleTree<T>) => CollapsibleTree<T>,
|
|
1079
|
-
): MultiColumn<T> {
|
|
1080
|
-
// Use columnMap for faster lookup
|
|
1081
|
-
const column = this._columnMap.get(columnId);
|
|
1082
|
-
if (!column) {
|
|
1083
|
-
Logger.warn(`Column ${columnId} not found`);
|
|
1084
|
-
return this;
|
|
1085
|
-
}
|
|
1086
|
-
|
|
1087
|
-
const newColumn = fn(column);
|
|
1088
|
-
if (column === newColumn) {
|
|
1089
|
-
return this;
|
|
1090
|
-
}
|
|
1091
|
-
|
|
1092
|
-
const newColumns = this.columns.map((c) => {
|
|
1093
|
-
if (c.id === columnId) {
|
|
1094
|
-
return newColumn;
|
|
1095
|
-
}
|
|
1096
|
-
return c;
|
|
1097
|
-
});
|
|
1098
|
-
|
|
1099
|
-
return new MultiColumn(newColumns);
|
|
1100
|
-
}
|
|
1101
|
-
|
|
1102
|
-
/**
|
|
1103
|
-
* Apply a transformation function to all columns
|
|
1104
|
-
* @param fn The function to transform each column
|
|
1105
|
-
* @returns A new MultiColumn if any changes were made, otherwise this
|
|
1106
|
-
*/
|
|
1107
|
-
public transformAll(
|
|
1108
|
-
fn: (tree: CollapsibleTree<T>) => CollapsibleTree<T>,
|
|
1109
|
-
): MultiColumn<T> {
|
|
1110
|
-
let didChange = false;
|
|
1111
|
-
|
|
1112
|
-
// Apply the transformation to all columns
|
|
1113
|
-
const newColumns = this.columns.map((column) => {
|
|
1114
|
-
const newColumn = fn(column);
|
|
1115
|
-
if (!column.equals(newColumn)) {
|
|
1116
|
-
didChange = true;
|
|
1117
|
-
}
|
|
1118
|
-
return newColumn;
|
|
1119
|
-
});
|
|
1120
|
-
|
|
1121
|
-
// Avoid unnecessary re-renders if nothing changed
|
|
1122
|
-
if (!didChange) {
|
|
1123
|
-
return this;
|
|
1124
|
-
}
|
|
1125
|
-
|
|
1126
|
-
return new MultiColumn(newColumns);
|
|
1127
|
-
}
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
export const visibleForTesting = {
|
|
1131
|
-
reset: () => {
|
|
1132
|
-
uniqueId = 0;
|
|
1133
|
-
},
|
|
1134
|
-
};
|