@marimo-team/islands 0.25.3-dev27 → 0.25.3-dev271
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-DfG3PRaJ.js → ConnectedDataExplorerComponent-eLHPh6uu.js} +120 -116
- package/dist/{ErrorBoundary-DKAio_E4.js → ErrorBoundary-VLVnXoqP.js} +66 -71
- package/dist/_baseSet-BfI5FjBM.js +328 -0
- package/dist/{abnfDiagram-VCTEODGH-B3w-XWX8.js → abnfDiagram-VCTEODGH-hqd0IBtX.js} +2 -2
- package/dist/any-language-editor-DnG26QXh.js +75 -0
- package/dist/{architectureDiagram-5GKGNRK7-C18U9A6k.js → architectureDiagram-5GKGNRK7-BzLAFcc0.js} +1 -1
- package/dist/{arrays-CN1djC4k.js → arrays-DoQ-AILx.js} +15 -27
- package/dist/assertNever-DIU3JJbt.js +507 -0
- package/dist/assets/__vite-browser-external-BKBpimif.js +1 -0
- package/dist/assets/worker-CgoMtD-_.js +113 -0
- package/dist/{blockDiagram-I7D4REHJ-Cg25UrxT.js → blockDiagram-I7D4REHJ-Dkhe6_PP.js} +1 -1
- package/dist/{button-CvjkwdSk.js → button-0-RqY1DW.js} +229 -218
- package/dist/{capabilities-CQ5IjhzL.js → capabilities-B6yy_CBN.js} +2 -2
- package/dist/{chat-ui-DDJuqS87.js → chat-ui-BWIkve2R.js} +2071 -1780
- package/dist/{check-BMKBBrdc.js → check-gUSZbRsl.js} +1 -1
- package/dist/{chunk-IMKFNOWR-CrS5bzqW.js → chunk-IMKFNOWR-DOeVvuyE.js} +1 -1
- package/dist/{chunk-SHT3W25Y-StpvPvUG.js → chunk-SHT3W25Y-DZoFI8wR.js} +1 -1
- package/dist/{chunk-SVP7TREG-CBXjbYZC.js → chunk-SVP7TREG-CWu86PSW.js} +1 -1
- package/dist/{chunk-TICWLB2K-Dp6bIb-q.js → chunk-TICWLB2K-BrM0GoP3.js} +1 -1
- package/dist/{chunk-YOKDWASO-DPZk8vhS.js → chunk-YOKDWASO-DKplTrBY.js} +20 -20
- package/dist/{classDiagram-ZZMXUADV-BFYuxV5g.js → classDiagram-ZZMXUADV-DYi4V2bB.js} +2 -2
- package/dist/{classDiagram-v2-VYDZK3BY-D_tjXVMl.js → classDiagram-v2-VYDZK3BY-tN7FIk-m.js} +2 -2
- package/dist/{code-visibility-CFvLbCZJ.js → code-visibility-Dv4bHkOd.js} +43 -50
- package/dist/{common-1HUDUr7W.js → common-KrCVlCIg.js} +10277 -9719
- package/dist/config-BsPruWMR.js +636 -0
- package/dist/{copy-icon-BgUO8jsi.js → copy-icon-C2I8sw4h.js} +3 -3
- package/dist/createReducer-DdtT_7ji.js +67 -0
- package/dist/{cynefinDiagram-5FMLGOSQ-DSgYrHB2.js → cynefinDiagram-5FMLGOSQ-D5nCnDc-.js} +1 -1
- package/dist/{dagre-GXQ25YYZ-GYlruCBf.js → dagre-GXQ25YYZ-BHkd7f_i.js} +3 -3
- package/dist/{dagre-BGSfXNEb.js → dagre-mqiMAsFY.js} +531 -680
- package/dist/{data-grid-overlay-editor-ZxoN8gVB.js → data-grid-overlay-editor-BnCDQaMh.js} +5 -5
- package/dist/{data-grid-types-BH_r7g7l.js → data-grid-types-0PJ-9N5n.js} +1 -1
- package/dist/{debounce-C3jyATpc.js → debounce-DrhhziJv.js} +2 -2
- package/dist/{diagram-S7CK7UJ4-DRu5LX5_.js → diagram-S7CK7UJ4-D1ALppT2.js} +1 -1
- package/dist/{diagram-UQ7AKVKN-DD7uiejP.js → diagram-UQ7AKVKN-DLv0b83L.js} +1 -1
- package/dist/{diagram-VX7I27RA-ByH9KAJJ.js → diagram-VX7I27RA-7XJ7J6qg.js} +1 -1
- package/dist/{diagram-Z3DM3KII-BsQHPRke.js → diagram-Z3DM3KII-7M7zbHp_.js} +1 -1
- package/dist/{dialog-Cc479ZEH.js → dialog-DzCj1qQv.js} +26 -26
- package/dist/{dist-Bvs24R_E.js → dist-11T5wq3_.js} +31 -30
- package/dist/{dist-F8Ep36rk.js → dist-17ugwGZc.js} +59 -58
- package/dist/{dist-BMQf5zIS.js → dist-7cRjf--y.js} +123 -122
- package/dist/{dist-diIx5qJW.js → dist-B8pkfkzc.js} +31 -30
- package/dist/{dist-CN_BeG2I.js → dist-B9UoM_wq.js} +58 -57
- package/dist/{dist-CLdpGQVr.js → dist-BGuMTs1i.js} +118 -117
- package/dist/{dist-BK4Oj99o.js → dist-BHW11fni.js} +18 -17
- package/dist/dist-BLY4m1Wi.js +850 -0
- package/dist/{dist-PXYI_yuP.js → dist-BNlcVoxs.js} +24 -23
- package/dist/{dist-D6d86eNJ.js → dist-BQYe5Bju.js} +47 -46
- package/dist/{dist-CkTTIdBe.js → dist-BX1mn8na.js} +337 -337
- package/dist/{dist-DiDibOYr.js → dist-Bh0PTKUW.js} +151 -150
- package/dist/{dist-CSJ0MDRw.js → dist-CKaXK92p.js} +56 -55
- package/dist/{dist-C8fAKSH1.js → dist-CLhwfhpJ.js} +53 -52
- package/dist/{dist-DoE4XJBC.js → dist-CYeqK4Xw.js} +9801 -10649
- package/dist/{dist-BLfKmR-l.js → dist-Cxlp3WH6.js} +16 -15
- package/dist/{dist-D-wwqlzJ.js → dist-DSJPGj42.js} +158 -157
- package/dist/{dist-BorMD-uX.js → dist-DU9ICsWZ.js} +75 -74
- package/dist/{dist-DBtSl3sf.js → dist-DWv0ALBi.js} +47 -46
- package/dist/{dist-OR1OnN-y.js → dist-DiBpWEsB.js} +31 -30
- package/dist/{dist-CrSP_H1M.js → dist-DiLFTlnl.js} +242 -242
- package/dist/{dist-DpBpA_n1.js → dist-Lm-D-Vr6.js} +3 -3
- package/dist/{dist-CN8zI-XC.js → dist-X9CsotJl.js} +239 -239
- package/dist/{dist-CSbh1PNP.js → dist-cVDV5IuI.js} +23 -22
- package/dist/{dist-BuaRHxOc.js → dist-fRfaOoq8.js} +44 -43
- package/dist/{dist-Cg7gzw-a.js → dist-gTTI-9Px.js} +78 -77
- package/dist/dropdown-menu-CdXE15an.js +6089 -0
- package/dist/{ebnfDiagram-PWID7BFC-CQRK5_eT.js → ebnfDiagram-PWID7BFC-DHH1qlmV.js} +2 -2
- package/dist/{erDiagram-RLTQ6QDP-BKtWrDVh.js → erDiagram-RLTQ6QDP-fI_0dGef.js} +1 -1
- package/dist/{error-banner-CFPHFw4h.js → error-banner-DPc9eIw5.js} +87 -86
- package/dist/error-entries-CxN-Nq3V.js +2573 -0
- package/dist/{esm-AMFssoNp.js → esm-BDqqT86j.js} +329 -329
- package/dist/{esm-D2gTsXEV.js → esm-BgKjMvWw.js} +530 -529
- package/dist/feedback-button-7UVo7ro6.js +374 -0
- package/dist/{flowDiagram-HODETNUW-1IN-ih2E.js → flowDiagram-HODETNUW-Brg9MA6i.js} +2 -2
- package/dist/{formats-Buu00Z2q.js → formats-DbSvAPY-.js} +6 -6
- package/dist/{ganttDiagram-EL5Y4UJY-BQT16JSc.js → ganttDiagram-EL5Y4UJY-Dh_4cBJZ.js} +2 -2
- package/dist/{glide-data-editor-BBgCAPbk.js → glide-data-editor-DTtBwgEa.js} +6676 -2890
- package/dist/{glide-utils-DM9zBO4W.js → glide-utils-BSw3SSHF.js} +84 -45
- package/dist/{globals-DhH_o2tF.js → globals-CedpcDTN.js} +3 -3
- package/dist/{graphlib-BmY_CCV9.js → graphlib-nlwT3Wvy.js} +177 -220
- package/dist/hasIn-C20JsQ2m.js +28 -0
- package/dist/{highlighted-body-KPVGNVTW-zwjq21tf.js → highlighted-body-KPVGNVTW-3Ziwfz-V.js} +2 -2
- package/dist/{error-entries-Cz3yNs7w.js → html-to-image-Bc2AvXHG.js} +26695 -26850
- package/dist/{infoDiagram-27XIBGKW-CkB-zr4p.js → infoDiagram-27XIBGKW-E9e403YT.js} +1 -1
- package/dist/isArrayLikeObject-B-HXCsex.js +626 -0
- package/dist/isEmpty-CdX7MTE1.js +14 -0
- package/dist/{ishikawaDiagram-5VMMS53U-wlyLjBmM.js → ishikawaDiagram-5VMMS53U-C-RlYDRo.js} +1 -1
- package/dist/{kanban-definition-UXKFOSKX-C2usY0i-.js → kanban-definition-UXKFOSKX-CLFl_uqK.js} +1 -1
- package/dist/{linear-Cnaq3Tfb.js → linear-DWERh7eI.js} +121 -73
- package/dist/{loader-BQOfjTIJ.js → loader-BVEtQ4F3.js} +12 -12
- package/dist/main.js +10420 -8311
- package/dist/mermaid-HWGCJPDP-Du3-HPQA.js +2 -0
- package/dist/{mermaid-BA-Z6wGL.js → mermaid-rd09e1JL.js} +54 -54
- package/dist/{mindmap-definition-YA3MSWOX-nKOWS8RR.js → mindmap-definition-YA3MSWOX-BQ8TWtyB.js} +1 -1
- package/dist/{now-jM8-M7IR.js → now-BKZCLE5P.js} +12 -13
- package/dist/{number-overlay-editor-CvLhxHLb.js → number-overlay-editor-D_avDRZT.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-Dac9EIKD.js → pegDiagram-XKGWAZYB-CN8UHFPi.js} +2 -2
- package/dist/pencil-DcT8AE0h.js +10 -0
- package/dist/pick-tCszMUbm.js +35 -0
- package/dist/{pieDiagram-E7YTZNPT-B7S-DsW_.js → pieDiagram-E7YTZNPT-DGuJ0EBd.js} +1 -1
- package/dist/precisionRound-DhAzeRcm.js +49 -0
- package/dist/{process-output-B-KBO95p.js → process-output-qecD7Dk8.js} +4 -4
- package/dist/{quadrantDiagram-AXDQQJYC-Ccw0xMjN.js → quadrantDiagram-AXDQQJYC-Blrm7V7_.js} +1 -1
- package/dist/{railroadDiagram-O6MQD6OU-Cfa5Q-hr.js → railroadDiagram-O6MQD6OU-BPDA8wy8.js} +2 -2
- package/dist/{react-vega-BED0DjiB.js → react-vega-C4T9Rb_w.js} +12284 -12270
- package/dist/{requirementDiagram-BXWQKSXE-d8zXCJIk.js → requirementDiagram-BXWQKSXE-ChzLGbGW.js} +1 -1
- package/dist/{reveal-component-C9BavSsS.js → reveal-component-cJ717FaI.js} +1131 -1108
- package/dist/schemas-CdscWNy_.js +4747 -0
- package/dist/select-OxgozOC8.js +1200 -0
- package/dist/{sequenceDiagram-WJ2MYXX4-b6pei3lJ.js → sequenceDiagram-WJ2MYXX4-DFX-bE6O.js} +1 -1
- package/dist/{spec-BJzgmvVa.js → spec-oJn0RhpQ.js} +41 -11
- package/dist/{stateDiagram-D77RDMKH-Be-xSpaZ.js → stateDiagram-D77RDMKH-BR7cI3wK.js} +4 -4
- package/dist/{stateDiagram-v2-MP3YSRHH-5Y-FxesJ.js → stateDiagram-v2-MP3YSRHH-oGqB5UDI.js} +2 -2
- package/dist/static-state-CuV6Pc3a.js +72 -0
- package/dist/style.css +1 -1
- package/dist/{swimlanes-42K2YHIH-nRRl4NU0.js → swimlanes-42K2YHIH-DwSZl1yC.js} +1 -1
- package/dist/{swimlanesDiagram-VR7AAH4N-BS79510V.js → swimlanesDiagram-VR7AAH4N-BHlcd-Ri.js} +2 -2
- package/dist/{swiper-component-DKntyn8p.js → swiper-component-Dvj-goaD.js} +2 -2
- package/dist/{time-BieKwdYS.js → time-BaMbJs4z.js} +18 -18
- package/dist/{timeline-definition-24CTP7MA-Cfzf8WPf.js → timeline-definition-24CTP7MA-DMmP-G2O.js} +1 -1
- package/dist/toast-DbBHmxmB.js +491 -0
- package/dist/{tooltip-CtBZ5q8p.js → tooltip-BfcDQd2j.js} +329 -305
- package/dist/use-restore-focus-CmflVLWJ.js +1672 -0
- package/dist/{useAsyncData-BD534XmW.js → useAsyncData-BYIAoMqn.js} +15 -14
- package/dist/{useDateFormatter-DkajGwwd.js → useDateFormatter-ButWkdcK.js} +2 -2
- package/dist/{useDeepCompareMemoize-C72_RNsu.js → useDeepCompareMemoize-Bz0c5GN6.js} +1 -1
- package/dist/{useIframeCapabilities-DBETvvPl.js → useIframeCapabilities-BIolGsF1.js} +1 -1
- package/dist/useLifecycle-JjxCC7Vu.js +112 -0
- package/dist/useTheme-DPLobSFs.js +339 -0
- package/dist/utils-EBklsC5R.js +813 -0
- package/dist/{vega-component-nB1kV6sb.js → vega-component-rxIxAAh9.js} +171 -170
- 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-CeY6Z1xZ.js → vennDiagram-4TSXK5OY-Db8GtH1G.js} +1 -1
- package/dist/{wardleyDiagram-VM6X3IG4-Cxs9NtVi.js → wardleyDiagram-VM6X3IG4-DmIiC3FU.js} +1 -1
- package/dist/{xychartDiagram-S5SC5T6Z-DNWbvPK7.js → xychartDiagram-S5SC5T6Z-DS9OPrNq.js} +2 -2
- package/package.json +17 -20
- package/src/__mocks__/document.tsx +63 -0
- package/src/__mocks__/executions.ts +195 -0
- package/src/__mocks__/notebook.ts +3 -5
- package/src/__mocks__/partial.ts +26 -0
- package/src/__mocks__/requests.ts +1 -4
- package/src/__tests__/CellStatus.test.tsx +19 -20
- package/src/__tests__/fixtures/pair-config.ts +1 -1
- 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/__tests__/state.test.tsx +50 -0
- package/src/components/app-config/ai-config.tsx +156 -150
- package/src/components/app-config/app-config-button.tsx +6 -5
- package/src/components/app-config/app-config-form.tsx +5 -9
- package/src/components/app-config/common.tsx +7 -9
- package/src/components/app-config/data-form.tsx +112 -113
- package/src/components/app-config/mcp-config.tsx +30 -53
- package/src/components/app-config/user-config-form.css +91 -0
- package/src/components/app-config/user-config-form.tsx +266 -349
- package/src/components/chat/__tests__/capabilities-popover.test.tsx +0 -1
- package/src/components/chat/__tests__/chat-display.test.tsx +196 -0
- package/src/components/chat/__tests__/chat-markdown.test.tsx +43 -0
- package/src/components/chat/__tests__/chat-utils.test.ts +93 -1
- 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/__tests__/turn-timing.test.ts +84 -0
- package/src/components/chat/__tests__/use-assistant-turn.test.ts +95 -0
- package/src/components/chat/__tests__/user-message.test.tsx +27 -0
- package/src/components/chat/acp/__tests__/agent-panel-initialization.test.tsx +1 -1
- package/src/components/chat/acp/__tests__/agent-panel.test.tsx +22 -17
- 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/agent-panel.tsx +6 -3
- package/src/components/chat/acp/blocks.tsx +13 -12
- package/src/components/chat/acp/common.tsx +4 -5
- package/src/components/chat/acp/model-selector.tsx +1 -1
- package/src/components/chat/acp/session-tabs.tsx +8 -3
- package/src/components/chat/agent-activity.tsx +132 -0
- package/src/components/chat/capabilities-popover.tsx +2 -11
- package/src/components/chat/chat-components.tsx +9 -4
- package/src/components/chat/chat-display.tsx +131 -57
- package/src/components/chat/chat-history-popover.tsx +6 -1
- package/src/components/chat/chat-markdown.tsx +15 -0
- package/src/components/chat/chat-panel.tsx +216 -294
- package/src/components/chat/chat-typography.css +108 -0
- package/src/components/chat/chat-utils.ts +24 -35
- package/src/components/chat/notebook-access-selector.stories.tsx +22 -0
- package/src/components/chat/notebook-access-selector.tsx +100 -0
- package/src/components/chat/tool-call/__tests__/tool-labels.test.ts +76 -0
- package/src/components/chat/tool-call/__tests__/tool-step.test.tsx +57 -0
- package/src/components/chat/tool-call/shared.ts +6 -0
- package/src/components/chat/tool-call/tool-call-view.tsx +22 -76
- package/src/components/chat/tool-call/tool-labels.ts +172 -0
- package/src/components/chat/tool-call/tool-result.tsx +1 -1
- package/src/components/chat/tool-call/tool-step.tsx +145 -0
- package/src/components/chat/turn-timing.ts +73 -0
- package/src/components/chat/use-assistant-turn.ts +65 -0
- package/src/components/chat/use-chat-generation.ts +20 -0
- package/src/components/chat/user-message.tsx +145 -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-formatting/types.ts +1 -2
- 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 +15 -20
- 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 -21
- package/src/components/editor/__tests__/dynamic-favicon.test.tsx +111 -18
- package/src/components/editor/__tests__/output-persistence.test.tsx +8 -4
- 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-commands.test.ts +21 -40
- package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +41 -14
- package/src/components/editor/actions/__tests__/pair-with-agent.test.tsx +6 -24
- package/src/components/editor/actions/__tests__/useCellActionButton.test.tsx +131 -0
- package/src/components/editor/actions/name-cell-input.tsx +4 -3
- package/src/components/editor/actions/pair-with-agent-commands.ts +10 -7
- package/src/components/editor/actions/pair-with-agent-modal.tsx +29 -30
- package/src/components/editor/actions/pair-with-agent.tsx +16 -39
- package/src/components/editor/actions/useCellActionButton.tsx +47 -35
- package/src/components/editor/actions/useConfigActions.tsx +3 -4
- package/src/components/editor/actions/useNotebookActions.tsx +25 -29
- 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/ai-completion-editor.tsx +2 -1
- package/src/components/editor/ai/completion-handlers.tsx +0 -37
- package/src/components/editor/ai/prompt-input.tsx +189 -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 +4 -2
- 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/CellStatus.tsx +23 -19
- 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 +18 -8
- package/src/components/editor/cell/StagedAICell.tsx +52 -137
- package/src/components/editor/cell/StopButton.tsx +2 -2
- package/src/components/editor/cell/__tests__/useRunCells.test.ts +45 -0
- 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/cell-context-menu.tsx +4 -1
- package/src/components/editor/cell/code/__tests__/language-toggle.test.tsx +54 -0
- package/src/components/editor/cell/code/cell-editor.tsx +65 -169
- package/src/components/editor/cell/code/language-toggle.tsx +8 -7
- package/src/components/editor/cell/collapse.tsx +15 -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 +20 -8
- 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/panel-header.tsx +13 -0
- 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/types.ts +1 -1
- package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +7 -9
- 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 +265 -37
- package/src/components/editor/chrome/wrapper/__tests__/useAskAiAboutCell.test.tsx +58 -0
- package/src/components/editor/chrome/wrapper/app-chrome.tsx +91 -74
- 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 -2
- package/src/components/editor/chrome/wrapper/minimap-state.ts +1 -1
- package/src/components/editor/chrome/wrapper/pending-ai-cells.tsx +143 -72
- package/src/components/editor/chrome/wrapper/pending-ai-changes-badge.tsx +25 -0
- package/src/components/editor/chrome/wrapper/sidebar.tsx +8 -4
- package/src/components/editor/chrome/wrapper/storage.ts +1 -1
- package/src/components/editor/chrome/wrapper/useAskAiAboutCell.ts +36 -0
- 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 +25 -20
- 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__/filename-form.test.tsx +1 -4
- 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/filename-form.tsx +1 -1
- package/src/components/editor/header/status.tsx +44 -22
- 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 +21 -11
- 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 +74 -72
- 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 +114 -126
- 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/VideoOutput.tsx +6 -0
- package/src/components/editor/output/__tests__/VideoOutput.test.tsx +131 -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/__tests__/add-cell-buttons.test.tsx +18 -0
- package/src/components/editor/renderers/cell-array.tsx +32 -100
- package/src/components/editor/renderers/grid-layout/__tests__/grid-layout.test.tsx +75 -0
- package/src/components/editor/renderers/grid-layout/grid-layout.tsx +34 -22
- package/src/components/editor/renderers/slides-layout/__tests__/plugin.test.ts +0 -2
- 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 +13 -34
- package/src/components/editor/stdin-blocking-alert.tsx +4 -2
- 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 +39 -28
- package/src/components/mcp/mcp-status-indicator.tsx +18 -14
- 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 +89 -0
- package/src/components/scratchpad/scratchpad.tsx +47 -33
- package/src/components/scratchpad/state.ts +7 -0
- 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 +21 -14
- package/src/components/slides/reveal-component.tsx +72 -36
- package/src/components/slides/reveal-slides.css +5 -4
- package/src/components/slides/slide-cell-view.tsx +32 -29
- package/src/components/slides/slide-form.tsx +1 -0
- package/src/components/slides/slide-scroll-hint.tsx +3 -1
- package/src/components/slides/slide.tsx +3 -4
- 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__/fullscreen.test.tsx +44 -2
- 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/fullscreen.tsx +25 -1
- package/src/components/ui/range-slider.tsx +22 -9
- package/src/components/ui/toast.tsx +1 -1
- package/src/core/MarimoApp.tsx +3 -1
- package/src/core/ai/__tests__/staged-cells.test.ts +89 -608
- package/src/core/ai/context/context.ts +2 -0
- package/src/core/ai/context/providers/__tests__/cell-output.test.ts +8 -12
- package/src/core/ai/context/providers/__tests__/cell.test.ts +49 -0
- package/src/core/ai/context/providers/cell-output.ts +2 -2
- package/src/core/ai/context/providers/cell.ts +54 -0
- package/src/core/ai/context/providers/datasource.ts +6 -2
- package/src/core/ai/staged-cells.ts +56 -323
- package/src/core/ai/state.ts +3 -0
- 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 -426
- 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 +85 -151
- package/src/core/cells/__tests__/cells.test.ts +269 -2667
- package/src/core/cells/__tests__/collapseConsoleOutputs.test.ts +54 -0
- package/src/core/cells/__tests__/document.test.ts +164 -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 +122 -0
- package/src/core/cells/__tests__/outputs.test.tsx +71 -53
- package/src/core/cells/__tests__/pending-delete-service.test.tsx +11 -20
- package/src/core/cells/__tests__/session.test.ts +445 -211
- package/src/core/cells/__tests__/utils.test.ts +76 -347
- package/src/core/cells/actions.ts +678 -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 +45 -136
- package/src/core/cells/cells.ts +133 -1385
- package/src/core/cells/columns.ts +38 -0
- package/src/core/cells/document.ts +178 -0
- package/src/core/cells/edit.ts +30 -0
- package/src/core/cells/editor.ts +82 -0
- package/src/core/cells/focus.ts +27 -6
- package/src/core/cells/layout.ts +221 -0
- package/src/core/cells/outputs.ts +12 -28
- package/src/core/cells/pending-delete-service.ts +8 -14
- package/src/core/cells/scrollCellIntoView.ts +6 -0
- package/src/core/cells/session.ts +76 -53
- package/src/core/cells/types.ts +34 -41
- package/src/core/cells/utils.ts +46 -75
- package/src/core/codemirror/__tests__/format.test.ts +0 -34
- package/src/core/codemirror/__tests__/setup.test.ts +61 -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 +2 -6
- package/src/core/codemirror/cells/extensions.ts +30 -28
- package/src/core/codemirror/cells/state.ts +2 -6
- package/src/core/codemirror/cm.ts +5 -1
- package/src/core/codemirror/config/types.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 -23
- 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 +19 -13
- 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/misc/__tests__/paste.test.ts +3 -1
- package/src/core/codemirror/misc/paste.ts +1 -1
- package/src/core/codemirror/placeholder/__tests__/extensions.test.ts +75 -0
- package/src/core/codemirror/placeholder/extensions.ts +80 -19
- package/src/core/codemirror/readonly/__tests__/extension.test.ts +1 -1
- package/src/core/codemirror/readonly/extension.ts +2 -2
- 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 +56 -6
- package/src/core/config/config-schema.ts +17 -4
- package/src/core/config/config.ts +65 -20
- package/src/core/config/feature-flag.tsx +0 -2
- 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 +22 -28
- package/src/core/errors/error-entries.ts +4 -1
- package/src/core/executions/__tests__/atoms.test.ts +99 -0
- package/src/core/executions/__tests__/select.test.ts +268 -0
- package/src/core/executions/atoms.ts +206 -0
- package/src/core/executions/hooks.ts +31 -0
- package/src/core/executions/index.ts +31 -0
- package/src/core/executions/select.ts +175 -0
- package/src/core/export/__tests__/hooks.test.ts +0 -5
- 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/hotkeys/hotkeys.ts +2 -1
- package/src/core/islands/__tests__/bootstrap.test.ts +69 -17
- package/src/core/islands/__tests__/bridge.test.ts +85 -27
- package/src/core/islands/bootstrap.ts +50 -54
- package/src/core/islands/bridge.ts +84 -25
- package/src/core/islands/components/__tests__/IslandControls.test.tsx +7 -5
- package/src/core/islands/components/output-wrapper.tsx +5 -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 +17 -79
- package/src/core/kernel/state.ts +0 -15
- package/src/core/lifecycle/render-policy.ts +12 -26
- 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 +6 -8
- package/src/core/network/connection-notice.ts +1 -1
- package/src/core/network/connection.ts +1 -1
- package/src/core/network/requests-lazy.ts +5 -8
- package/src/core/network/requests-network.ts +5 -31
- package/src/core/network/requests-static.ts +1 -4
- package/src/core/network/requests-toasting.tsx +1 -4
- package/src/core/network/types.ts +6 -14
- 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 +759 -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__/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 +102 -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 +369 -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/{host → notebook/replica}/__tests__/attachments.test.ts +9 -10
- package/src/core/{host → notebook/replica}/__tests__/document.test.ts +18 -16
- package/src/core/notebook/replica/__tests__/executions.test.ts +200 -0
- package/src/core/notebook/replica/__tests__/fixtures.ts +79 -0
- package/src/core/notebook/replica/__tests__/replica.test.ts +559 -0
- package/src/core/notebook/replica/__tests__/results.test.ts +193 -0
- package/src/core/notebook/replica/__tests__/runtime.test.ts +78 -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 +20 -0
- package/src/core/notebook/replica/replica.ts +336 -0
- package/src/core/{host → notebook/replica}/resources/attachments.ts +6 -3
- package/src/core/{host → notebook/replica}/resources/document.ts +3 -3
- package/src/core/notebook/replica/resources/executions.ts +157 -0
- package/src/core/notebook/replica/resources/results.ts +48 -0
- package/src/core/notebook/replica/resources/runtime.ts +14 -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 +49 -51
- package/src/core/runtime/__tests__/adapter.test.ts +4 -4
- package/src/core/runtime/__tests__/createWsUrl.test.ts +77 -7
- package/src/core/runtime/__tests__/runtime.test.ts +32 -74
- package/src/core/runtime/adapter.ts +5 -5
- package/src/core/runtime/config.ts +2 -2
- package/src/core/runtime/runtime.ts +33 -46
- package/src/core/runtime/types.ts +0 -2
- package/src/core/saving/__tests__/document.test.tsx +123 -0
- package/src/core/saving/__tests__/state.test.ts +111 -0
- package/src/core/saving/__tests__/useAutoSave.test.tsx +67 -0
- package/src/core/saving/document.ts +33 -0
- package/src/core/saving/filename.ts +1 -1
- package/src/core/saving/save-component.tsx +39 -60
- 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 +186 -0
- package/src/core/{websocket → session}/__tests__/useNotebookSession.hook.test.tsx +247 -262
- package/src/core/session/active-notebook.ts +28 -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 +85 -67
- 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/vscode/__tests__/vscode-bindings.test.ts +125 -0
- package/src/core/vscode/vscode-bindings.ts +14 -2
- package/src/core/wasm/__tests__/bridge.test.ts +210 -64
- package/src/core/wasm/__tests__/worker.test.ts +188 -0
- package/src/core/wasm/bridge.ts +120 -73
- 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 +30 -18
- package/src/core/wasm/worker/worker.ts +118 -72
- package/src/css/app/Cell.css +7 -36
- package/src/css/app/codemirror.css +1 -1
- package/src/css/common.css +1 -1
- 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/RenderHTML.tsx +3 -5
- package/src/plugins/core/__test__/RenderHTML-copy.test.tsx +60 -0
- package/src/plugins/core/registerReactComponent.tsx +5 -14
- package/src/plugins/impl/CodeEditorPlugin.tsx +1 -1
- package/src/plugins/impl/DataEditorPlugin.tsx +34 -9
- 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__/DataEditorPlugin.test.ts +93 -4
- 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 +17 -15
- package/src/plugins/impl/code/any-language-editor.tsx +2 -2
- package/src/plugins/impl/common/error-banner.tsx +5 -3
- package/src/plugins/impl/data-editor/__tests__/column-types.test.ts +37 -0
- package/src/plugins/impl/data-editor/__tests__/editor-state.test.ts +52 -1
- package/src/plugins/impl/data-editor/__tests__/glide-data-editor.test.tsx +113 -1
- package/src/plugins/impl/data-editor/__tests__/glide-utils.test.ts +116 -0
- package/src/plugins/impl/data-editor/column-types.ts +52 -0
- package/src/plugins/impl/data-editor/editor-state.ts +18 -2
- package/src/plugins/impl/data-editor/glide-data-editor.tsx +73 -12
- package/src/plugins/impl/data-editor/glide-utils.ts +33 -10
- package/src/plugins/impl/data-editor/types.ts +7 -0
- 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 +44 -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/DownloadPlugin.tsx +7 -11
- package/src/plugins/layout/RoutesPlugin.tsx +1 -1
- package/src/plugins/layout/__test__/DownloadPlugin.test.tsx +205 -0
- package/src/stories/__tests__/editor-story.test.tsx +33 -0
- package/src/stories/button.stories.tsx +1 -0
- package/src/stories/cell-status.stories.tsx +17 -10
- package/src/stories/cell.stories.tsx +2 -36
- 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 +28 -32
- 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} +127 -1
- package/src/utils/__tests__/download.test.tsx +183 -23
- 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 +115 -79
- package/src/utils/download.ts +67 -6
- package/src/utils/maps.ts +0 -3
- package/src/utils/numbers.ts +79 -18
- package/src/utils/objects.ts +4 -9
- package/src/utils/time.ts +10 -0
- package/src/utils/tracer.ts +9 -5
- package/src/utils/zod-utils.ts +1 -3
- package/dist/_baseMerge-BiGRwuqv.js +0 -165
- package/dist/any-language-editor-D4q-IFcM.js +0 -76
- package/dist/assets/__vite-browser-external-BXYkiFsk.js +0 -1
- package/dist/assets/worker-HPY9fbhz.js +0 -114
- package/dist/dropdown-menu-DHP9P4VE.js +0 -4678
- package/dist/feedback-button-B2JRVQRd.js +0 -379
- package/dist/hasIn-o2Us0uVx.js +0 -132
- package/dist/html-to-image-DWTzN7q3.js +0 -3251
- package/dist/isArrayLikeObject-BZl-15ir.js +0 -450
- package/dist/isEmpty-BSZ1LaUo.js +0 -35
- package/dist/isSymbol-kQcfYbNX.js +0 -8
- package/dist/label-DoysmLMW.js +0 -281
- package/dist/mermaid-HWGCJPDP-ByCsZnYF.js +0 -2
- package/dist/pick-CJeoxYOS.js +0 -51
- package/dist/precisionRound-DMIVQrt2.js +0 -97
- package/dist/select-DemoKT4G.js +0 -1344
- package/dist/toDate-CJWU9dyl.js +0 -1185
- package/dist/toast-PkdZraM8.js +0 -1830
- package/dist/use-restore-focus-CKjfFjLM.js +0 -6369
- package/dist/useLifecycle-BGWzTwHK.js +0 -169
- package/dist/useTheme-BhQvec43.js +0 -904
- package/src/components/chat/reasoning-accordion.tsx +0 -61
- package/src/components/chat/tool-call/tool-error-card.tsx +0 -81
- package/src/components/chat/tool-call/tool-history-row.tsx +0 -174
- package/src/components/editor/ai/__tests__/staged-cell-submission.test.ts +0 -106
- package/src/components/editor/ai/add-cell-with-ai.tsx +0 -506
- package/src/components/editor/ai/staged-cell-submission.ts +0 -46
- package/src/components/editor/cell/__tests__/StagedAICell.test.tsx +0 -64
- package/src/components/editor/cell/code/__tests__/rtc-editor-config.test.ts +0 -50
- package/src/components/editor/chrome/wrapper/footer-items/rtc-status.tsx +0 -65
- package/src/components/editor/navigation/__tests__/state.test.ts +0 -81
- package/src/components/editor/navigation/state.ts +0 -65
- package/src/components/icons/loading-ellipsis.tsx +0 -46
- 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 -18
- 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/codemirror/rtc/extension.ts +0 -453
- package/src/core/codemirror/rtc/loro/__tests__/sync.test.ts +0 -52
- package/src/core/codemirror/rtc/loro/awareness.ts +0 -531
- package/src/core/codemirror/rtc/loro/colors.ts +0 -38
- package/src/core/codemirror/rtc/loro/sync.ts +0 -168
- 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 -433
- package/src/core/host/__tests__/protocol.test.ts +0 -214
- package/src/core/host/__tests__/stream.test.ts +0 -209
- package/src/core/host/connection.ts +0 -81
- package/src/core/host/events.ts +0 -61
- package/src/core/host/notebook-client.ts +0 -290
- package/src/core/host/protocol.ts +0 -1945
- package/src/core/host/resources/resource.ts +0 -47
- package/src/core/host/stream.ts +0 -211
- package/src/core/rtc/state.ts +0 -34
- 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 -170
- package/src/core/websocket/state.ts +0 -7
- 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 -88
- 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
|
@@ -4,29 +4,32 @@ import { python } from "@codemirror/lang-python";
|
|
|
4
4
|
import { EditorState } from "@codemirror/state";
|
|
5
5
|
import { EditorView } from "@codemirror/view";
|
|
6
6
|
import { createStore } from "jotai";
|
|
7
|
+
import { createRef } from "react";
|
|
7
8
|
import {
|
|
8
9
|
afterAll,
|
|
9
|
-
afterEach,
|
|
10
10
|
beforeAll,
|
|
11
|
+
afterEach,
|
|
11
12
|
beforeEach,
|
|
12
13
|
describe,
|
|
13
14
|
expect,
|
|
14
15
|
it,
|
|
15
16
|
vi,
|
|
16
17
|
} from "vitest";
|
|
18
|
+
import { MockExecutions } from "@/__mocks__/executions";
|
|
19
|
+
import {
|
|
20
|
+
notebookQueuedOrRunningCountAtom,
|
|
21
|
+
submitCells,
|
|
22
|
+
} from "@/core/executions";
|
|
23
|
+
import { store } from "@/core/state/jotai";
|
|
17
24
|
import { MockNotebook } from "@/__mocks__/notebook";
|
|
18
25
|
import { cellId } from "@/__tests__/branded";
|
|
19
26
|
import type { CellHandle } from "@/components/editor/notebook-cell";
|
|
20
|
-
import { CellId, SCRATCH_CELL_ID
|
|
27
|
+
import { CellId, SCRATCH_CELL_ID } from "@/core/cells/ids";
|
|
21
28
|
import { foldAllBulk, unfoldAllBulk } from "@/core/codemirror/editing/commands";
|
|
22
29
|
import { adaptiveLanguageConfiguration } from "@/core/codemirror/language/extension";
|
|
23
30
|
import { OverridingHotkeyProvider } from "@/core/hotkeys/hotkeys";
|
|
24
31
|
import type { OutputMessage } from "@/core/kernel/messages";
|
|
25
|
-
import {
|
|
26
|
-
type CollapsibleTree,
|
|
27
|
-
MultiColumn,
|
|
28
|
-
type CellColumnId,
|
|
29
|
-
} from "@/utils/id-tree";
|
|
32
|
+
import type { CellConfig } from "@/core/network/types";
|
|
30
33
|
import type { Seconds } from "@/utils/time";
|
|
31
34
|
import {
|
|
32
35
|
exportedForTesting,
|
|
@@ -34,13 +37,12 @@ import {
|
|
|
34
37
|
type NotebookState,
|
|
35
38
|
notebookAtom,
|
|
36
39
|
} from "../cells";
|
|
37
|
-
import { exportedForTesting as documentTransactionTestExports } from "../document-changes";
|
|
38
40
|
import {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
type CellData,
|
|
42
|
+
createCell,
|
|
43
|
+
createCellConfig,
|
|
44
|
+
createCellRuntimeState,
|
|
45
|
+
} from "../types";
|
|
44
46
|
|
|
45
47
|
vi.mock("@/core/codemirror/editing/commands", () => ({
|
|
46
48
|
foldAllBulk: vi.fn(),
|
|
@@ -60,9 +62,6 @@ vi.mock("../scrollCellIntoView", async (importOriginal) => {
|
|
|
60
62
|
};
|
|
61
63
|
});
|
|
62
64
|
|
|
63
|
-
const FIRST_COLUMN = 0;
|
|
64
|
-
const SECOND_COLUMN = 1;
|
|
65
|
-
|
|
66
65
|
const { initialNotebookState, reducer, createActions } = exportedForTesting;
|
|
67
66
|
|
|
68
67
|
function formatCells(notebook: NotebookState) {
|
|
@@ -78,14 +77,7 @@ function formatCells(notebook: NotebookState) {
|
|
|
78
77
|
const cells = cellsId.map((cellId) => cellData[cellId]);
|
|
79
78
|
return cells.map((cell) => printCell(cell)).join("\n\n");
|
|
80
79
|
};
|
|
81
|
-
|
|
82
|
-
return `> col ${columnIdx}\n${printCells(column.inOrderIds)}`;
|
|
83
|
-
};
|
|
84
|
-
const columns = cellIds.getColumns();
|
|
85
|
-
if (columns.length > 1) {
|
|
86
|
-
return wrap(columns.map(printColumn).join("\n\n"));
|
|
87
|
-
}
|
|
88
|
-
return wrap(printCells(cellIds.inOrderIds));
|
|
80
|
+
return wrap(printCells(cellIds));
|
|
89
81
|
}
|
|
90
82
|
|
|
91
83
|
function createEditor(content: string) {
|
|
@@ -121,8 +113,7 @@ describe("cell reducer", () => {
|
|
|
121
113
|
let cells: ReturnType<typeof flattenTopLevelNotebookCells>;
|
|
122
114
|
let firstCellId: CellId;
|
|
123
115
|
|
|
124
|
-
const
|
|
125
|
-
state = reducer(state, action);
|
|
116
|
+
const mountEditors = () => {
|
|
126
117
|
for (const [cellIdString, handle] of Object.entries(state.cellHandles)) {
|
|
127
118
|
// @ts-expect-error - Typescript Object.entries doesn't know that keys are CellId
|
|
128
119
|
const cid: CellId = cellIdString;
|
|
@@ -136,7 +127,94 @@ describe("cell reducer", () => {
|
|
|
136
127
|
}
|
|
137
128
|
}
|
|
138
129
|
cells = flattenTopLevelNotebookCells(state);
|
|
139
|
-
}
|
|
130
|
+
};
|
|
131
|
+
const actions = createActions((action) => {
|
|
132
|
+
state = reducer(state, action);
|
|
133
|
+
mountEditors();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
/** Put a cell into the notebook the way the document would. */
|
|
137
|
+
function seedCell({
|
|
138
|
+
cellId,
|
|
139
|
+
before,
|
|
140
|
+
code = "",
|
|
141
|
+
name,
|
|
142
|
+
config,
|
|
143
|
+
newCellId,
|
|
144
|
+
hideCode,
|
|
145
|
+
autoFocus = true,
|
|
146
|
+
}: {
|
|
147
|
+
cellId: CellId | "__end__";
|
|
148
|
+
before: boolean;
|
|
149
|
+
code?: string;
|
|
150
|
+
name?: string;
|
|
151
|
+
config?: CellConfig;
|
|
152
|
+
newCellId?: CellId;
|
|
153
|
+
hideCode?: boolean;
|
|
154
|
+
autoFocus?: boolean;
|
|
155
|
+
}): CellId {
|
|
156
|
+
const cellIndex =
|
|
157
|
+
cellId === "__end__"
|
|
158
|
+
? state.cellIds.length
|
|
159
|
+
: state.cellIds.indexOf(cellId);
|
|
160
|
+
const id = newCellId ?? CellId.create();
|
|
161
|
+
const cellIds = [...state.cellIds];
|
|
162
|
+
cellIds.splice(before ? cellIndex : cellIndex + 1, 0, id);
|
|
163
|
+
state = {
|
|
164
|
+
...state,
|
|
165
|
+
cellIds,
|
|
166
|
+
cellData: {
|
|
167
|
+
...state.cellData,
|
|
168
|
+
[id]: createCell({
|
|
169
|
+
id,
|
|
170
|
+
code,
|
|
171
|
+
name,
|
|
172
|
+
config: createCellConfig({
|
|
173
|
+
...config,
|
|
174
|
+
...(hideCode != null && { hide_code: hideCode }),
|
|
175
|
+
}),
|
|
176
|
+
edited: Boolean(code),
|
|
177
|
+
}),
|
|
178
|
+
},
|
|
179
|
+
cellRuntime: { ...state.cellRuntime, [id]: createCellRuntimeState() },
|
|
180
|
+
cellHandles: { ...state.cellHandles, [id]: createRef() },
|
|
181
|
+
scrollKey: autoFocus ? id : null,
|
|
182
|
+
};
|
|
183
|
+
mountEditors();
|
|
184
|
+
return id;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** Edit a cell's code as the document delivers it. */
|
|
188
|
+
function editCode(cellId: CellId, code: string) {
|
|
189
|
+
const cells = state.cellIds.map((id) => {
|
|
190
|
+
const cell = state.cellData[id];
|
|
191
|
+
return {
|
|
192
|
+
id,
|
|
193
|
+
code: id === cellId ? code : cell.code,
|
|
194
|
+
name: cell.name,
|
|
195
|
+
config: {
|
|
196
|
+
column: cell.config.column ?? null,
|
|
197
|
+
disabled: cell.config.disabled ?? false,
|
|
198
|
+
hide_code: cell.config.hide_code ?? false,
|
|
199
|
+
expand_output: cell.config.expand_output ?? false,
|
|
200
|
+
},
|
|
201
|
+
};
|
|
202
|
+
});
|
|
203
|
+
actions.reconcileDocument({
|
|
204
|
+
order: state.cellIds,
|
|
205
|
+
cells: new Map(cells.map((cell) => [cell.id, cell])),
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** Format a cell's code, as the editor reports it. */
|
|
210
|
+
function formatCode(cellId: CellId, code: string) {
|
|
211
|
+
actions.codeFormatted({
|
|
212
|
+
cellId,
|
|
213
|
+
before: state.cellData[cellId].code,
|
|
214
|
+
after: code,
|
|
215
|
+
});
|
|
216
|
+
editCode(cellId, code);
|
|
217
|
+
}
|
|
140
218
|
|
|
141
219
|
let i = 0;
|
|
142
220
|
const originalCreate = CellId.create.bind(CellId);
|
|
@@ -151,200 +229,16 @@ describe("cell reducer", () => {
|
|
|
151
229
|
i = 0;
|
|
152
230
|
|
|
153
231
|
state = initialNotebookState();
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
firstCellId = state.cellIds.inOrderIds[0];
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
afterEach(() => {
|
|
160
|
-
documentTransactionTestExports.cancelPendingChanges();
|
|
232
|
+
seedCell({ cellId: "__end__", before: false });
|
|
233
|
+
firstCellId = state.cellIds[0];
|
|
161
234
|
});
|
|
162
235
|
|
|
163
236
|
afterAll(() => {
|
|
164
237
|
CellId.create = originalCreate;
|
|
165
238
|
});
|
|
166
239
|
|
|
167
|
-
it("can add a cell after another cell", () => {
|
|
168
|
-
actions.createNewCell({
|
|
169
|
-
cellId: firstCellId,
|
|
170
|
-
before: false,
|
|
171
|
-
});
|
|
172
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
173
|
-
"
|
|
174
|
-
[0] ''
|
|
175
|
-
|
|
176
|
-
[1] ''
|
|
177
|
-
"
|
|
178
|
-
`);
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
it("can add a cell to the end with code", () => {
|
|
182
|
-
actions.createNewCell({
|
|
183
|
-
cellId: "__end__",
|
|
184
|
-
code: "import numpy as np",
|
|
185
|
-
before: false,
|
|
186
|
-
});
|
|
187
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
188
|
-
"
|
|
189
|
-
[0] ''
|
|
190
|
-
|
|
191
|
-
[1] 'import numpy as np'
|
|
192
|
-
"
|
|
193
|
-
`);
|
|
194
|
-
|
|
195
|
-
// Cell should be added to the end and edited
|
|
196
|
-
expect(cells[1].code).toBe("import numpy as np");
|
|
197
|
-
expect(cells[1].edited).toBe(true);
|
|
198
|
-
expect(cells[1].lastCodeRun).toBe(null);
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
it("can add a cell before another cell", () => {
|
|
202
|
-
actions.createNewCell({
|
|
203
|
-
cellId: firstCellId,
|
|
204
|
-
before: true,
|
|
205
|
-
});
|
|
206
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
207
|
-
"
|
|
208
|
-
[1] ''
|
|
209
|
-
|
|
210
|
-
[0] ''
|
|
211
|
-
"
|
|
212
|
-
`);
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
it("can add a cell with name and config", () => {
|
|
216
|
-
actions.createNewCell({
|
|
217
|
-
cellId: firstCellId,
|
|
218
|
-
before: false,
|
|
219
|
-
code: "x = 1",
|
|
220
|
-
name: "My Cell",
|
|
221
|
-
config: { hide_code: true, disabled: false },
|
|
222
|
-
});
|
|
223
|
-
const newCellId = state.cellIds.inOrderIds[1];
|
|
224
|
-
expect(state.cellData[newCellId].name).toBe("My Cell");
|
|
225
|
-
expect(state.cellData[newCellId].config.hide_code).toBe(true);
|
|
226
|
-
expect(state.cellData[newCellId].config.disabled).toBe(false);
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
it("can delete a Python cell and undo delete", () => {
|
|
230
|
-
actions.createNewCell({
|
|
231
|
-
cellId: firstCellId,
|
|
232
|
-
before: false,
|
|
233
|
-
});
|
|
234
|
-
actions.deleteCell({
|
|
235
|
-
cellId: firstCellId,
|
|
236
|
-
});
|
|
237
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
238
|
-
"
|
|
239
|
-
[1] ''
|
|
240
|
-
"
|
|
241
|
-
`);
|
|
242
|
-
|
|
243
|
-
// undo
|
|
244
|
-
actions.undoDeleteCell();
|
|
245
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
246
|
-
"
|
|
247
|
-
[2] ''
|
|
248
|
-
|
|
249
|
-
[1] ''
|
|
250
|
-
"
|
|
251
|
-
`);
|
|
252
|
-
|
|
253
|
-
// Verify scrollKey is set to the restored cell
|
|
254
|
-
expect(state.scrollKey).toBe("2");
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
it("can delete a SQL cell and undo delete", () => {
|
|
258
|
-
actions.createNewCell({
|
|
259
|
-
cellId: firstCellId,
|
|
260
|
-
before: false,
|
|
261
|
-
code: `df = mo.sql("""SELECT * FROM table""")`,
|
|
262
|
-
});
|
|
263
|
-
const newCellId = state.cellIds.getColumns()[0].atOrThrow(1);
|
|
264
|
-
actions.deleteCell({
|
|
265
|
-
cellId: newCellId,
|
|
266
|
-
});
|
|
267
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
268
|
-
"
|
|
269
|
-
[0] ''
|
|
270
|
-
"
|
|
271
|
-
`);
|
|
272
|
-
|
|
273
|
-
// undo
|
|
274
|
-
actions.undoDeleteCell();
|
|
275
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
276
|
-
"
|
|
277
|
-
[0] ''
|
|
278
|
-
|
|
279
|
-
[2] 'df = mo.sql("""SELECT * FROM table""")'
|
|
280
|
-
"
|
|
281
|
-
`);
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
it("can delete a Markdown cell and undo delete", () => {
|
|
285
|
-
const text = "The quick brown fox jumps over the lazy dog.";
|
|
286
|
-
actions.createNewCell({
|
|
287
|
-
cellId: firstCellId,
|
|
288
|
-
before: false,
|
|
289
|
-
code: `mo.md(r"""${text}""")`,
|
|
290
|
-
});
|
|
291
|
-
const newCellId = state.cellIds.getColumns()[0].atOrThrow(1);
|
|
292
|
-
actions.deleteCell({
|
|
293
|
-
cellId: newCellId,
|
|
294
|
-
});
|
|
295
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
296
|
-
"
|
|
297
|
-
[0] ''
|
|
298
|
-
"
|
|
299
|
-
`);
|
|
300
|
-
|
|
301
|
-
// undo
|
|
302
|
-
actions.undoDeleteCell();
|
|
303
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
304
|
-
"
|
|
305
|
-
[0] ''
|
|
306
|
-
|
|
307
|
-
[2] 'mo.md(r"""The quick brown fox jumps over the lazy dog.""")'
|
|
308
|
-
"
|
|
309
|
-
`);
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
it("undo delete restores cell config (hide_code, disabled)", () => {
|
|
313
|
-
actions.createNewCell({
|
|
314
|
-
cellId: firstCellId,
|
|
315
|
-
before: false,
|
|
316
|
-
code: "x = 1",
|
|
317
|
-
});
|
|
318
|
-
const newCellId = state.cellIds.getColumns()[0].atOrThrow(1);
|
|
319
|
-
|
|
320
|
-
// Set config to hidden and disabled
|
|
321
|
-
actions.updateCellConfig({
|
|
322
|
-
cellId: newCellId,
|
|
323
|
-
config: { hide_code: true, disabled: true },
|
|
324
|
-
});
|
|
325
|
-
expect(state.cellData[newCellId].config.hide_code).toBe(true);
|
|
326
|
-
expect(state.cellData[newCellId].config.disabled).toBe(true);
|
|
327
|
-
|
|
328
|
-
// Delete the cell
|
|
329
|
-
actions.deleteCell({ cellId: newCellId });
|
|
330
|
-
expect(state.cellIds.inOrderIds).not.toContain(newCellId);
|
|
331
|
-
|
|
332
|
-
// Undo delete
|
|
333
|
-
actions.undoDeleteCell();
|
|
334
|
-
const restoredCellId =
|
|
335
|
-
state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
|
|
336
|
-
|
|
337
|
-
// Config should be preserved
|
|
338
|
-
expect(state.cellData[restoredCellId].config.hide_code).toBe(true);
|
|
339
|
-
expect(state.cellData[restoredCellId].config.disabled).toBe(true);
|
|
340
|
-
});
|
|
341
|
-
|
|
342
240
|
it("can update a cell", () => {
|
|
343
|
-
|
|
344
|
-
cellId: firstCellId,
|
|
345
|
-
code: "import numpy as np",
|
|
346
|
-
formattingChange: false,
|
|
347
|
-
});
|
|
241
|
+
editCode(firstCellId, "import numpy as np");
|
|
348
242
|
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
349
243
|
"
|
|
350
244
|
[0] 'import numpy as np'
|
|
@@ -352,443 +246,6 @@ describe("cell reducer", () => {
|
|
|
352
246
|
`);
|
|
353
247
|
});
|
|
354
248
|
|
|
355
|
-
it("can move a cell", () => {
|
|
356
|
-
actions.createNewCell({
|
|
357
|
-
cellId: firstCellId,
|
|
358
|
-
before: false,
|
|
359
|
-
});
|
|
360
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
361
|
-
"
|
|
362
|
-
[0] ''
|
|
363
|
-
|
|
364
|
-
[1] ''
|
|
365
|
-
"
|
|
366
|
-
`);
|
|
367
|
-
|
|
368
|
-
// move first cell to the end
|
|
369
|
-
actions.moveCell({
|
|
370
|
-
cellId: firstCellId,
|
|
371
|
-
before: false,
|
|
372
|
-
});
|
|
373
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
374
|
-
"
|
|
375
|
-
[1] ''
|
|
376
|
-
|
|
377
|
-
[0] ''
|
|
378
|
-
"
|
|
379
|
-
`);
|
|
380
|
-
|
|
381
|
-
// move it back
|
|
382
|
-
actions.moveCell({
|
|
383
|
-
cellId: firstCellId,
|
|
384
|
-
before: true,
|
|
385
|
-
});
|
|
386
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
387
|
-
"
|
|
388
|
-
[0] ''
|
|
389
|
-
|
|
390
|
-
[1] ''
|
|
391
|
-
"
|
|
392
|
-
`);
|
|
393
|
-
|
|
394
|
-
// Add a column breakpoint to test left/right movement
|
|
395
|
-
actions.addColumnBreakpoint({ cellId: cellId("1") });
|
|
396
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
397
|
-
"
|
|
398
|
-
> col 0
|
|
399
|
-
[0] ''
|
|
400
|
-
|
|
401
|
-
> col 1
|
|
402
|
-
[1] ''
|
|
403
|
-
"
|
|
404
|
-
`);
|
|
405
|
-
|
|
406
|
-
// Move cell right
|
|
407
|
-
actions.moveCell({
|
|
408
|
-
cellId: firstCellId,
|
|
409
|
-
direction: "right",
|
|
410
|
-
});
|
|
411
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
412
|
-
"
|
|
413
|
-
> col 0
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
> col 1
|
|
417
|
-
[0] ''
|
|
418
|
-
|
|
419
|
-
[1] ''
|
|
420
|
-
"
|
|
421
|
-
`);
|
|
422
|
-
|
|
423
|
-
// Move cell left
|
|
424
|
-
actions.moveCell({
|
|
425
|
-
cellId: firstCellId,
|
|
426
|
-
direction: "left",
|
|
427
|
-
});
|
|
428
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
429
|
-
"
|
|
430
|
-
> col 0
|
|
431
|
-
[0] ''
|
|
432
|
-
|
|
433
|
-
> col 1
|
|
434
|
-
[1] ''
|
|
435
|
-
"
|
|
436
|
-
`);
|
|
437
|
-
|
|
438
|
-
// Try to move cell left when it's already in leftmost column (should noop)
|
|
439
|
-
actions.moveCell({
|
|
440
|
-
cellId: firstCellId,
|
|
441
|
-
direction: "left",
|
|
442
|
-
});
|
|
443
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
444
|
-
"
|
|
445
|
-
> col 0
|
|
446
|
-
[0] ''
|
|
447
|
-
|
|
448
|
-
> col 1
|
|
449
|
-
[1] ''
|
|
450
|
-
"
|
|
451
|
-
`);
|
|
452
|
-
|
|
453
|
-
// Try to move cell right when it's already in rightmost column (should noop)
|
|
454
|
-
actions.moveCell({
|
|
455
|
-
cellId: cellId("1"),
|
|
456
|
-
direction: "right",
|
|
457
|
-
});
|
|
458
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
459
|
-
"
|
|
460
|
-
> col 0
|
|
461
|
-
[0] ''
|
|
462
|
-
|
|
463
|
-
> col 1
|
|
464
|
-
[1] ''
|
|
465
|
-
"
|
|
466
|
-
`);
|
|
467
|
-
});
|
|
468
|
-
|
|
469
|
-
it("can drag and drop a cell", () => {
|
|
470
|
-
actions.createNewCell({
|
|
471
|
-
cellId: firstCellId,
|
|
472
|
-
before: false,
|
|
473
|
-
});
|
|
474
|
-
actions.createNewCell({
|
|
475
|
-
cellId: cellId("1"),
|
|
476
|
-
before: false,
|
|
477
|
-
});
|
|
478
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
479
|
-
"
|
|
480
|
-
[0] ''
|
|
481
|
-
|
|
482
|
-
[1] ''
|
|
483
|
-
|
|
484
|
-
[2] ''
|
|
485
|
-
"
|
|
486
|
-
`);
|
|
487
|
-
|
|
488
|
-
// drag first cell to the end
|
|
489
|
-
actions.dropCellOverCell({
|
|
490
|
-
cellId: firstCellId,
|
|
491
|
-
overCellId: cellId("2"),
|
|
492
|
-
});
|
|
493
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
494
|
-
"
|
|
495
|
-
[1] ''
|
|
496
|
-
|
|
497
|
-
[2] ''
|
|
498
|
-
|
|
499
|
-
[0] ''
|
|
500
|
-
"
|
|
501
|
-
`);
|
|
502
|
-
|
|
503
|
-
// drag it back to the middle
|
|
504
|
-
actions.dropCellOverCell({
|
|
505
|
-
cellId: firstCellId,
|
|
506
|
-
overCellId: cellId("2"),
|
|
507
|
-
});
|
|
508
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
509
|
-
"
|
|
510
|
-
[1] ''
|
|
511
|
-
|
|
512
|
-
[0] ''
|
|
513
|
-
|
|
514
|
-
[2] ''
|
|
515
|
-
"
|
|
516
|
-
`);
|
|
517
|
-
});
|
|
518
|
-
|
|
519
|
-
it("can move a cell to an exact index within a column", () => {
|
|
520
|
-
actions.createNewCell({
|
|
521
|
-
cellId: firstCellId,
|
|
522
|
-
before: false,
|
|
523
|
-
});
|
|
524
|
-
actions.createNewCell({
|
|
525
|
-
cellId: cellId("1"),
|
|
526
|
-
before: false,
|
|
527
|
-
});
|
|
528
|
-
|
|
529
|
-
const columnId = state.cellIds.atOrThrow(0).id;
|
|
530
|
-
actions.moveCellToIndex({
|
|
531
|
-
cellId: firstCellId,
|
|
532
|
-
columnId,
|
|
533
|
-
index: 3,
|
|
534
|
-
});
|
|
535
|
-
|
|
536
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
537
|
-
"
|
|
538
|
-
[1] ''
|
|
539
|
-
|
|
540
|
-
[2] ''
|
|
541
|
-
|
|
542
|
-
[0] ''
|
|
543
|
-
"
|
|
544
|
-
`);
|
|
545
|
-
});
|
|
546
|
-
|
|
547
|
-
it("can move a cell to an exact index across columns", () => {
|
|
548
|
-
actions.createNewCell({
|
|
549
|
-
cellId: firstCellId,
|
|
550
|
-
before: false,
|
|
551
|
-
});
|
|
552
|
-
actions.createNewCell({
|
|
553
|
-
cellId: cellId("1"),
|
|
554
|
-
before: false,
|
|
555
|
-
});
|
|
556
|
-
actions.addColumnBreakpoint({ cellId: cellId("1") });
|
|
557
|
-
|
|
558
|
-
const secondColumnId = state.cellIds.atOrThrow(1).id;
|
|
559
|
-
actions.moveCellToIndex({
|
|
560
|
-
cellId: firstCellId,
|
|
561
|
-
columnId: secondColumnId,
|
|
562
|
-
index: 1,
|
|
563
|
-
});
|
|
564
|
-
|
|
565
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
566
|
-
"
|
|
567
|
-
> col 0
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
> col 1
|
|
571
|
-
[1] ''
|
|
572
|
-
|
|
573
|
-
[0] ''
|
|
574
|
-
|
|
575
|
-
[2] ''
|
|
576
|
-
"
|
|
577
|
-
`);
|
|
578
|
-
});
|
|
579
|
-
|
|
580
|
-
it("moveCellToIndex is a no-op when moving to the same position", () => {
|
|
581
|
-
actions.createNewCell({
|
|
582
|
-
cellId: firstCellId,
|
|
583
|
-
before: false,
|
|
584
|
-
});
|
|
585
|
-
actions.createNewCell({
|
|
586
|
-
cellId: cellId("1"),
|
|
587
|
-
before: false,
|
|
588
|
-
});
|
|
589
|
-
|
|
590
|
-
const columnId = state.cellIds.atOrThrow(0).id;
|
|
591
|
-
const before = formatCells(state);
|
|
592
|
-
|
|
593
|
-
actions.moveCellToIndex({
|
|
594
|
-
cellId: firstCellId,
|
|
595
|
-
columnId,
|
|
596
|
-
index: 0,
|
|
597
|
-
});
|
|
598
|
-
|
|
599
|
-
expect(formatCells(state)).toBe(before);
|
|
600
|
-
});
|
|
601
|
-
|
|
602
|
-
it("moveCellToIndex is a no-op for an invalid columnId", () => {
|
|
603
|
-
actions.createNewCell({
|
|
604
|
-
cellId: firstCellId,
|
|
605
|
-
before: false,
|
|
606
|
-
});
|
|
607
|
-
|
|
608
|
-
const before = formatCells(state);
|
|
609
|
-
|
|
610
|
-
actions.moveCellToIndex({
|
|
611
|
-
cellId: firstCellId,
|
|
612
|
-
columnId: "nonexistent-column" as CellColumnId,
|
|
613
|
-
index: 0,
|
|
614
|
-
});
|
|
615
|
-
|
|
616
|
-
expect(formatCells(state)).toBe(before);
|
|
617
|
-
});
|
|
618
|
-
|
|
619
|
-
it("can move multiple cells relative to target", () => {
|
|
620
|
-
actions.createNewCell({
|
|
621
|
-
cellId: firstCellId,
|
|
622
|
-
before: false,
|
|
623
|
-
});
|
|
624
|
-
actions.createNewCell({
|
|
625
|
-
cellId: cellId("1"),
|
|
626
|
-
before: false,
|
|
627
|
-
});
|
|
628
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
629
|
-
"
|
|
630
|
-
[0] ''
|
|
631
|
-
|
|
632
|
-
[1] ''
|
|
633
|
-
|
|
634
|
-
[2] ''
|
|
635
|
-
"
|
|
636
|
-
`);
|
|
637
|
-
|
|
638
|
-
// Move first two cells after the third
|
|
639
|
-
actions.moveCellsRelativeTo({
|
|
640
|
-
cellIds: [firstCellId, cellId("1")],
|
|
641
|
-
targetCellId: cellId("2"),
|
|
642
|
-
position: "after",
|
|
643
|
-
});
|
|
644
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
645
|
-
"
|
|
646
|
-
[2] ''
|
|
647
|
-
|
|
648
|
-
[0] ''
|
|
649
|
-
|
|
650
|
-
[1] ''
|
|
651
|
-
"
|
|
652
|
-
`);
|
|
653
|
-
});
|
|
654
|
-
|
|
655
|
-
it("can undo cut-paste (move with previousPlacements)", () => {
|
|
656
|
-
actions.createNewCell({
|
|
657
|
-
cellId: firstCellId,
|
|
658
|
-
before: false,
|
|
659
|
-
});
|
|
660
|
-
actions.createNewCell({
|
|
661
|
-
cellId: cellId("1"),
|
|
662
|
-
before: false,
|
|
663
|
-
});
|
|
664
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
665
|
-
"
|
|
666
|
-
[0] ''
|
|
667
|
-
|
|
668
|
-
[1] ''
|
|
669
|
-
|
|
670
|
-
[2] ''
|
|
671
|
-
"
|
|
672
|
-
`);
|
|
673
|
-
|
|
674
|
-
const col = state.cellIds.findWithId(firstCellId);
|
|
675
|
-
const previousPlacements = [
|
|
676
|
-
{
|
|
677
|
-
columnId: col.id,
|
|
678
|
-
index: col.indexOfOrThrow(
|
|
679
|
-
firstCellId,
|
|
680
|
-
) as import("@/utils/id-tree").CellIndex,
|
|
681
|
-
},
|
|
682
|
-
{
|
|
683
|
-
columnId: col.id,
|
|
684
|
-
index: col.indexOfOrThrow(
|
|
685
|
-
cellId("1"),
|
|
686
|
-
) as import("@/utils/id-tree").CellIndex,
|
|
687
|
-
},
|
|
688
|
-
];
|
|
689
|
-
|
|
690
|
-
actions.moveCellsRelativeTo({
|
|
691
|
-
cellIds: [firstCellId, cellId("1")],
|
|
692
|
-
targetCellId: cellId("2"),
|
|
693
|
-
position: "after",
|
|
694
|
-
previousPlacements,
|
|
695
|
-
});
|
|
696
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
697
|
-
"
|
|
698
|
-
[2] ''
|
|
699
|
-
|
|
700
|
-
[0] ''
|
|
701
|
-
|
|
702
|
-
[1] ''
|
|
703
|
-
"
|
|
704
|
-
`);
|
|
705
|
-
|
|
706
|
-
actions.undoDeleteCell();
|
|
707
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
708
|
-
"
|
|
709
|
-
[0] ''
|
|
710
|
-
|
|
711
|
-
[1] ''
|
|
712
|
-
|
|
713
|
-
[2] ''
|
|
714
|
-
"
|
|
715
|
-
`);
|
|
716
|
-
});
|
|
717
|
-
|
|
718
|
-
it("undo order: cut-paste then delete — first undo restores delete, second undo undoes move", () => {
|
|
719
|
-
actions.createNewCell({
|
|
720
|
-
cellId: firstCellId,
|
|
721
|
-
before: false,
|
|
722
|
-
});
|
|
723
|
-
actions.createNewCell({
|
|
724
|
-
cellId: cellId("1"),
|
|
725
|
-
before: false,
|
|
726
|
-
});
|
|
727
|
-
|
|
728
|
-
const col = state.cellIds.findWithId(firstCellId);
|
|
729
|
-
const previousPlacements = [
|
|
730
|
-
{
|
|
731
|
-
columnId: col.id,
|
|
732
|
-
index: col.indexOfOrThrow(
|
|
733
|
-
firstCellId,
|
|
734
|
-
) as import("@/utils/id-tree").CellIndex,
|
|
735
|
-
},
|
|
736
|
-
{
|
|
737
|
-
columnId: col.id,
|
|
738
|
-
index: col.indexOfOrThrow(
|
|
739
|
-
cellId("1"),
|
|
740
|
-
) as import("@/utils/id-tree").CellIndex,
|
|
741
|
-
},
|
|
742
|
-
];
|
|
743
|
-
|
|
744
|
-
actions.moveCellsRelativeTo({
|
|
745
|
-
cellIds: [firstCellId, cellId("1")],
|
|
746
|
-
targetCellId: cellId("2"),
|
|
747
|
-
position: "after",
|
|
748
|
-
previousPlacements,
|
|
749
|
-
});
|
|
750
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
751
|
-
"
|
|
752
|
-
[2] ''
|
|
753
|
-
|
|
754
|
-
[0] ''
|
|
755
|
-
|
|
756
|
-
[1] ''
|
|
757
|
-
"
|
|
758
|
-
`);
|
|
759
|
-
|
|
760
|
-
actions.deleteCell({ cellId: cellId("2") });
|
|
761
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
762
|
-
"
|
|
763
|
-
[0] ''
|
|
764
|
-
|
|
765
|
-
[1] ''
|
|
766
|
-
"
|
|
767
|
-
`);
|
|
768
|
-
|
|
769
|
-
actions.undoDeleteCell();
|
|
770
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
771
|
-
"
|
|
772
|
-
[3] ''
|
|
773
|
-
|
|
774
|
-
[0] ''
|
|
775
|
-
|
|
776
|
-
[1] ''
|
|
777
|
-
"
|
|
778
|
-
`);
|
|
779
|
-
|
|
780
|
-
actions.undoDeleteCell();
|
|
781
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
782
|
-
"
|
|
783
|
-
[0] ''
|
|
784
|
-
|
|
785
|
-
[1] ''
|
|
786
|
-
|
|
787
|
-
[3] ''
|
|
788
|
-
"
|
|
789
|
-
`);
|
|
790
|
-
});
|
|
791
|
-
|
|
792
249
|
it("keeps error evidence after an edit and a new queued run", () => {
|
|
793
250
|
const code = "raise ValueError('old failure')";
|
|
794
251
|
const output: OutputMessage = {
|
|
@@ -820,11 +277,7 @@ describe("cell reducer", () => {
|
|
|
820
277
|
stale_inputs: null,
|
|
821
278
|
timestamp: 1,
|
|
822
279
|
});
|
|
823
|
-
|
|
824
|
-
cellId: firstCellId,
|
|
825
|
-
code: "print('new code')",
|
|
826
|
-
formattingChange: false,
|
|
827
|
-
});
|
|
280
|
+
editCode(firstCellId, "print('new code')");
|
|
828
281
|
actions.prepareForRun({ cellId: firstCellId });
|
|
829
282
|
actions.handleCellMessage({
|
|
830
283
|
cell_id: firstCellId,
|
|
@@ -846,19 +299,13 @@ describe("cell reducer", () => {
|
|
|
846
299
|
/////////////////
|
|
847
300
|
// Initial state
|
|
848
301
|
let cell = cells[0];
|
|
849
|
-
expect(cell.status).toBe("idle");
|
|
850
302
|
expect(cell.lastCodeRun).toBe(null);
|
|
851
303
|
expect(cell.edited).toBe(false);
|
|
852
304
|
expect(cell).toMatchSnapshot("initial state");
|
|
853
305
|
|
|
854
306
|
// Update code
|
|
855
|
-
|
|
856
|
-
cellId: firstCellId,
|
|
857
|
-
code: "import marimo as mo",
|
|
858
|
-
formattingChange: false,
|
|
859
|
-
});
|
|
307
|
+
editCode(firstCellId, "import marimo as mo");
|
|
860
308
|
cell = cells[0];
|
|
861
|
-
expect(cell.status).toBe("idle");
|
|
862
309
|
expect(cell.lastCodeRun).toBe(null);
|
|
863
310
|
expect(cell.edited).toBe(true);
|
|
864
311
|
expect(cell).toMatchSnapshot("code edited");
|
|
@@ -868,7 +315,6 @@ describe("cell reducer", () => {
|
|
|
868
315
|
cellId: firstCellId,
|
|
869
316
|
});
|
|
870
317
|
cell = cells[0];
|
|
871
|
-
expect(cell.status).toBe("queued");
|
|
872
318
|
expect(cell.lastCodeRun).toBe("import marimo as mo");
|
|
873
319
|
expect(cell.edited).toBe(false);
|
|
874
320
|
expect(cell).toMatchSnapshot("prepared for run");
|
|
@@ -883,12 +329,8 @@ describe("cell reducer", () => {
|
|
|
883
329
|
timestamp: new Date(10).getTime() as Seconds,
|
|
884
330
|
});
|
|
885
331
|
cell = cells[0];
|
|
886
|
-
expect(cell.status).toBe("queued");
|
|
887
332
|
expect(cell.lastCodeRun).toBe("import marimo as mo");
|
|
888
333
|
expect(cell.edited).toBe(false);
|
|
889
|
-
expect(cell.runElapsedTimeMs).toBe(null);
|
|
890
|
-
expect(cell.runStartTimestamp).toBe(null);
|
|
891
|
-
expect(cell.lastRunStartTimestamp).toBe(null);
|
|
892
334
|
expect(cell).toMatchSnapshot("queued message received");
|
|
893
335
|
|
|
894
336
|
// Receive running messages
|
|
@@ -901,12 +343,8 @@ describe("cell reducer", () => {
|
|
|
901
343
|
timestamp: new Date(20).getTime() as Seconds,
|
|
902
344
|
});
|
|
903
345
|
cell = cells[0];
|
|
904
|
-
expect(cell.status).toBe("running");
|
|
905
346
|
expect(cell.lastCodeRun).toBe("import marimo as mo");
|
|
906
347
|
expect(cell.edited).toBe(false);
|
|
907
|
-
expect(cell.runElapsedTimeMs).toBe(null);
|
|
908
|
-
expect(cell.runStartTimestamp).toBe(20);
|
|
909
|
-
expect(cell.lastRunStartTimestamp).toBe(20);
|
|
910
348
|
expect(cell).toMatchSnapshot("running message received");
|
|
911
349
|
|
|
912
350
|
// Console messages shouldn't transition status
|
|
@@ -924,11 +362,7 @@ describe("cell reducer", () => {
|
|
|
924
362
|
timestamp: new Date(22).getTime() as Seconds,
|
|
925
363
|
});
|
|
926
364
|
cell = cells[0];
|
|
927
|
-
expect(cell.status).toBe("running");
|
|
928
365
|
expect(cell.edited).toBe(false);
|
|
929
|
-
expect(cell.runElapsedTimeMs).toBe(null);
|
|
930
|
-
expect(cell.runStartTimestamp).toBe(20);
|
|
931
|
-
expect(cell.lastRunStartTimestamp).toBe(20);
|
|
932
366
|
expect(cell).toMatchSnapshot("console message received");
|
|
933
367
|
|
|
934
368
|
// Receive output messages
|
|
@@ -951,44 +385,26 @@ describe("cell reducer", () => {
|
|
|
951
385
|
timestamp: new Date(33).getTime() as Seconds,
|
|
952
386
|
});
|
|
953
387
|
cell = cells[0];
|
|
954
|
-
expect(cell.status).toBe("idle");
|
|
955
388
|
expect(cell.edited).toBe(false);
|
|
956
|
-
expect(cell.runElapsedTimeMs).toBe(13_000);
|
|
957
|
-
expect(cell.runStartTimestamp).toBe(null);
|
|
958
|
-
expect(cell.lastRunStartTimestamp).toBe(20);
|
|
959
389
|
expect(cell).toMatchSnapshot("output received");
|
|
960
390
|
|
|
961
391
|
// EDITING BACK AND FORTH
|
|
962
392
|
/////////////////
|
|
963
393
|
// Update code again
|
|
964
|
-
|
|
965
|
-
cellId: firstCellId,
|
|
966
|
-
code: "import marimo as mo\nimport numpy",
|
|
967
|
-
formattingChange: false,
|
|
968
|
-
});
|
|
394
|
+
editCode(firstCellId, "import marimo as mo\nimport numpy");
|
|
969
395
|
cell = cells[0];
|
|
970
|
-
expect(cell.status).toBe("idle");
|
|
971
396
|
expect(cell.edited).toBe(true);
|
|
972
397
|
expect(cell).toMatchSnapshot("code edited again");
|
|
973
398
|
|
|
974
399
|
// Update code should be unedited
|
|
975
|
-
|
|
976
|
-
cellId: firstCellId,
|
|
977
|
-
code: "import marimo as mo",
|
|
978
|
-
formattingChange: false,
|
|
979
|
-
});
|
|
400
|
+
editCode(firstCellId, "import marimo as mo");
|
|
980
401
|
cell = cells[0];
|
|
981
402
|
expect(cell.edited).toBe(false);
|
|
982
403
|
expect(cell).toMatchSnapshot("code reverted");
|
|
983
404
|
|
|
984
405
|
// Update code should be edited again
|
|
985
|
-
|
|
986
|
-
cellId: firstCellId,
|
|
987
|
-
code: "import marimo as mo\nimport numpy",
|
|
988
|
-
formattingChange: false,
|
|
989
|
-
});
|
|
406
|
+
editCode(firstCellId, "import marimo as mo\nimport numpy");
|
|
990
407
|
cell = cells[0];
|
|
991
|
-
expect(cell.status).toBe("idle");
|
|
992
408
|
expect(cell.lastCodeRun).toBe("import marimo as mo");
|
|
993
409
|
expect(cell.edited).toBe(true);
|
|
994
410
|
expect(cell).toMatchSnapshot("code changed after revert");
|
|
@@ -1041,11 +457,8 @@ describe("cell reducer", () => {
|
|
|
1041
457
|
});
|
|
1042
458
|
cell = cells[0];
|
|
1043
459
|
expect(cell.output).not.toBe(null); // keep old output
|
|
1044
|
-
expect(cell.status).toBe("idle");
|
|
1045
460
|
expect(cell.lastCodeRun).toBe("import marimo as mo\nimport numpy");
|
|
1046
461
|
expect(cell.edited).toBe(false);
|
|
1047
|
-
expect(cell.runElapsedTimeMs).toBe(11_000);
|
|
1048
|
-
expect(cell.runStartTimestamp).toBe(null);
|
|
1049
462
|
expect(cell).toMatchSnapshot("error response received");
|
|
1050
463
|
|
|
1051
464
|
// INTERRUPT RESPONSE
|
|
@@ -1062,127 +475,53 @@ describe("cell reducer", () => {
|
|
|
1062
475
|
output: undefined,
|
|
1063
476
|
console: null,
|
|
1064
477
|
status: "queued",
|
|
1065
|
-
stale_inputs: null,
|
|
1066
|
-
timestamp: new Date(40).getTime() as Seconds,
|
|
1067
|
-
});
|
|
1068
|
-
cell = cells[0];
|
|
1069
|
-
expect(cell.output).not.toBe(null); // keep old output
|
|
1070
|
-
// Running
|
|
1071
|
-
actions.handleCellMessage({
|
|
1072
|
-
cell_id: firstCellId,
|
|
1073
|
-
output: undefined,
|
|
1074
|
-
console: null,
|
|
1075
|
-
status: "running",
|
|
1076
|
-
stale_inputs: null,
|
|
1077
|
-
timestamp: new Date(50).getTime() as Seconds,
|
|
1078
|
-
});
|
|
1079
|
-
cell = cells[0];
|
|
1080
|
-
expect(cell.output).not.toBe(null); // keep old output
|
|
1081
|
-
// Receive error
|
|
1082
|
-
actions.handleCellMessage({
|
|
1083
|
-
cell_id: firstCellId,
|
|
1084
|
-
output: {
|
|
1085
|
-
channel: "marimo-error",
|
|
1086
|
-
mimetype: "application/vnd.marimo+error",
|
|
1087
|
-
data: [{ type: "interruption" }],
|
|
1088
|
-
timestamp: 0,
|
|
1089
|
-
},
|
|
1090
|
-
console: null,
|
|
1091
|
-
status: "idle",
|
|
1092
|
-
stale_inputs: null,
|
|
1093
|
-
timestamp: new Date(61).getTime() as Seconds,
|
|
1094
|
-
});
|
|
1095
|
-
cell = cells[0];
|
|
1096
|
-
expect(cell.status).toBe("idle");
|
|
1097
|
-
expect(cell.lastCodeRun).toBe(cell.code);
|
|
1098
|
-
expect(cell.edited).toBe(false);
|
|
1099
|
-
expect(cell.runElapsedTimeMs).toBe(11_000);
|
|
1100
|
-
expect(cell.runStartTimestamp).toBe(null);
|
|
1101
|
-
expect(cell).toMatchSnapshot("interruption response received");
|
|
1102
|
-
});
|
|
1103
|
-
|
|
1104
|
-
it("errors reset status to idle", () => {
|
|
1105
|
-
// Initial state
|
|
1106
|
-
let cell = cells[0];
|
|
1107
|
-
expect(cell.status).toBe("idle");
|
|
1108
|
-
expect(cell.lastCodeRun).toBe(null);
|
|
1109
|
-
expect(cell.edited).toBe(false);
|
|
1110
|
-
expect(cell).toMatchSnapshot("initial state");
|
|
1111
|
-
|
|
1112
|
-
// Update code
|
|
1113
|
-
actions.updateCellCode({
|
|
1114
|
-
cellId: firstCellId,
|
|
1115
|
-
code: "import marimo as mo",
|
|
1116
|
-
formattingChange: false,
|
|
1117
|
-
});
|
|
1118
|
-
cell = cells[0];
|
|
1119
|
-
expect(cell.status).toBe("idle");
|
|
1120
|
-
expect(cell.lastCodeRun).toBe(null);
|
|
1121
|
-
expect(cell.edited).toBe(true);
|
|
1122
|
-
expect(cell).toMatchSnapshot("code edited");
|
|
1123
|
-
|
|
1124
|
-
// Prepare for run
|
|
1125
|
-
actions.prepareForRun({
|
|
1126
|
-
cellId: firstCellId,
|
|
478
|
+
stale_inputs: null,
|
|
479
|
+
timestamp: new Date(40).getTime() as Seconds,
|
|
1127
480
|
});
|
|
1128
481
|
cell = cells[0];
|
|
1129
|
-
expect(cell.
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
// Prepare for run
|
|
1139
|
-
actions.prepareForRun({
|
|
1140
|
-
cellId: firstCellId,
|
|
482
|
+
expect(cell.output).not.toBe(null); // keep old output
|
|
483
|
+
// Running
|
|
484
|
+
actions.handleCellMessage({
|
|
485
|
+
cell_id: firstCellId,
|
|
486
|
+
output: undefined,
|
|
487
|
+
console: null,
|
|
488
|
+
status: "running",
|
|
489
|
+
stale_inputs: null,
|
|
490
|
+
timestamp: new Date(50).getTime() as Seconds,
|
|
1141
491
|
});
|
|
1142
492
|
cell = cells[0];
|
|
493
|
+
expect(cell.output).not.toBe(null); // keep old output
|
|
1143
494
|
// Receive error
|
|
1144
495
|
actions.handleCellMessage({
|
|
1145
496
|
cell_id: firstCellId,
|
|
1146
497
|
output: {
|
|
1147
498
|
channel: "marimo-error",
|
|
1148
499
|
mimetype: "application/vnd.marimo+error",
|
|
1149
|
-
data: [
|
|
1150
|
-
{ type: "exception", exception_type: "SyntaxError", msg: "Oh no!" },
|
|
1151
|
-
],
|
|
500
|
+
data: [{ type: "interruption" }],
|
|
1152
501
|
timestamp: 0,
|
|
1153
502
|
},
|
|
1154
503
|
console: null,
|
|
504
|
+
status: "idle",
|
|
1155
505
|
stale_inputs: null,
|
|
1156
506
|
timestamp: new Date(61).getTime() as Seconds,
|
|
1157
507
|
});
|
|
1158
508
|
cell = cells[0];
|
|
1159
|
-
expect(cell.
|
|
1160
|
-
expect(cell.lastCodeRun).toBe("import marimo as mo");
|
|
509
|
+
expect(cell.lastCodeRun).toBe(cell.code);
|
|
1161
510
|
expect(cell.edited).toBe(false);
|
|
1162
|
-
expect(cell
|
|
1163
|
-
expect(cell.runStartTimestamp).toBe(null);
|
|
1164
|
-
expect(cell).toMatchSnapshot("error response received");
|
|
511
|
+
expect(cell).toMatchSnapshot("interruption response received");
|
|
1165
512
|
});
|
|
1166
513
|
|
|
1167
514
|
it("can run a stale cell", () => {
|
|
1168
515
|
// Update code of first cell
|
|
1169
|
-
|
|
1170
|
-
cellId: firstCellId,
|
|
1171
|
-
code: "import marimo as mo",
|
|
1172
|
-
formattingChange: false,
|
|
1173
|
-
});
|
|
516
|
+
editCode(firstCellId, "import marimo as mo");
|
|
1174
517
|
// Add cell
|
|
1175
|
-
|
|
518
|
+
seedCell({
|
|
1176
519
|
cellId: firstCellId,
|
|
1177
520
|
before: false,
|
|
1178
521
|
});
|
|
1179
522
|
const secondCell = cells[1].id;
|
|
1180
523
|
// Update code
|
|
1181
|
-
|
|
1182
|
-
code: "mo.slider()",
|
|
1183
|
-
cellId: secondCell,
|
|
1184
|
-
formattingChange: false,
|
|
1185
|
-
});
|
|
524
|
+
editCode(secondCell, "mo.slider()");
|
|
1186
525
|
|
|
1187
526
|
// Prepare for run
|
|
1188
527
|
actions.prepareForRun({
|
|
@@ -1199,11 +538,8 @@ describe("cell reducer", () => {
|
|
|
1199
538
|
timestamp: new Date(10).getTime() as Seconds,
|
|
1200
539
|
});
|
|
1201
540
|
let cell = cells[1];
|
|
1202
|
-
expect(cell.status).toBe("queued");
|
|
1203
541
|
expect(cell.lastCodeRun).toBe("mo.slider()");
|
|
1204
542
|
expect(cell.edited).toBe(false);
|
|
1205
|
-
expect(cell.runElapsedTimeMs).toBe(null);
|
|
1206
|
-
expect(cell.runStartTimestamp).toBe(null);
|
|
1207
543
|
expect(cell).toMatchSnapshot("queued message received");
|
|
1208
544
|
|
|
1209
545
|
// Receive stale message
|
|
@@ -1216,23 +552,15 @@ describe("cell reducer", () => {
|
|
|
1216
552
|
timestamp: new Date(20).getTime() as Seconds,
|
|
1217
553
|
});
|
|
1218
554
|
cell = cells[1];
|
|
1219
|
-
expect(cell.status).toBe("queued");
|
|
1220
555
|
expect(cell.lastCodeRun).toBe("mo.slider()");
|
|
1221
556
|
expect(cell.edited).toBe(false);
|
|
1222
|
-
expect(cell.runElapsedTimeMs).toBe(null);
|
|
1223
|
-
expect(cell.runStartTimestamp).toBe(null);
|
|
1224
557
|
expect(cell).toMatchSnapshot("stale inputs received");
|
|
1225
558
|
});
|
|
1226
559
|
|
|
1227
560
|
it("can format code and update cell", () => {
|
|
1228
561
|
const firstCellId = cells[0].id;
|
|
1229
|
-
|
|
1230
|
-
cellId: firstCellId,
|
|
1231
|
-
code: "import marimo as mo",
|
|
1232
|
-
formattingChange: false,
|
|
1233
|
-
});
|
|
562
|
+
editCode(firstCellId, "import marimo as mo");
|
|
1234
563
|
let cell = cells[0];
|
|
1235
|
-
expect(cell.status).toBe("idle");
|
|
1236
564
|
expect(cell.lastCodeRun).toBe(null);
|
|
1237
565
|
expect(cell.edited).toBe(true);
|
|
1238
566
|
|
|
@@ -1261,56 +589,19 @@ describe("cell reducer", () => {
|
|
|
1261
589
|
|
|
1262
590
|
// Check steady state
|
|
1263
591
|
cell = cells[0];
|
|
1264
|
-
expect(cell.status).toBe("idle");
|
|
1265
592
|
expect(cell.edited).toBe(false);
|
|
1266
593
|
expect(cell.lastCodeRun).toBe("import marimo as mo");
|
|
1267
594
|
|
|
1268
595
|
// Format code
|
|
1269
|
-
|
|
1270
|
-
cellId: firstCellId,
|
|
1271
|
-
code: "import marimo as mo",
|
|
1272
|
-
formattingChange: true,
|
|
1273
|
-
});
|
|
596
|
+
formatCode(firstCellId, "import marimo as mo");
|
|
1274
597
|
cell = cells[0];
|
|
1275
|
-
expect(cell.status).toBe("idle");
|
|
1276
598
|
expect(cell.lastCodeRun).toBe("import marimo as mo");
|
|
1277
599
|
expect(cell.edited).toBe(false);
|
|
1278
600
|
});
|
|
1279
601
|
|
|
1280
|
-
it("can update a cells config", () => {
|
|
1281
|
-
const firstCellId = cells[0].id;
|
|
1282
|
-
let cell = cells[0];
|
|
1283
|
-
// Starts empty
|
|
1284
|
-
expect(cell.config).toEqual({
|
|
1285
|
-
disabled: false,
|
|
1286
|
-
hide_code: false,
|
|
1287
|
-
column: null,
|
|
1288
|
-
expand_output: false,
|
|
1289
|
-
});
|
|
1290
|
-
|
|
1291
|
-
actions.updateCellConfig({
|
|
1292
|
-
cellId: firstCellId,
|
|
1293
|
-
config: { disabled: true },
|
|
1294
|
-
});
|
|
1295
|
-
cell = cells[0];
|
|
1296
|
-
expect(cell.config.disabled).toBe(true);
|
|
1297
|
-
|
|
1298
|
-
// Revert
|
|
1299
|
-
actions.updateCellConfig({
|
|
1300
|
-
cellId: firstCellId,
|
|
1301
|
-
config: { disabled: false },
|
|
1302
|
-
});
|
|
1303
|
-
cell = cells[0];
|
|
1304
|
-
expect(cell.config.disabled).toBe(false);
|
|
1305
|
-
});
|
|
1306
|
-
|
|
1307
602
|
it("can run a stopped cell", () => {
|
|
1308
603
|
// Update code of first cell
|
|
1309
|
-
|
|
1310
|
-
cellId: firstCellId,
|
|
1311
|
-
code: "mo.md('This has an ancestor that was stopped')",
|
|
1312
|
-
formattingChange: false,
|
|
1313
|
-
});
|
|
604
|
+
editCode(firstCellId, "mo.md('This has an ancestor that was stopped')");
|
|
1314
605
|
|
|
1315
606
|
// Prepare for run
|
|
1316
607
|
actions.prepareForRun({
|
|
@@ -1327,13 +618,10 @@ describe("cell reducer", () => {
|
|
|
1327
618
|
timestamp: new Date(10).getTime() as Seconds,
|
|
1328
619
|
});
|
|
1329
620
|
let cell = cells[0];
|
|
1330
|
-
expect(cell.status).toBe("queued");
|
|
1331
621
|
expect(cell.lastCodeRun).toBe(
|
|
1332
622
|
"mo.md('This has an ancestor that was stopped')",
|
|
1333
623
|
);
|
|
1334
624
|
expect(cell.edited).toBe(false);
|
|
1335
|
-
expect(cell.runElapsedTimeMs).toBe(null);
|
|
1336
|
-
expect(cell.runStartTimestamp).toBe(null);
|
|
1337
625
|
expect(cell).toMatchSnapshot("queued message received");
|
|
1338
626
|
|
|
1339
627
|
// Receive idle message
|
|
@@ -1369,12 +657,10 @@ describe("cell reducer", () => {
|
|
|
1369
657
|
timestamp: new Date(20).getTime() as Seconds,
|
|
1370
658
|
});
|
|
1371
659
|
cell = cells[0];
|
|
1372
|
-
expect(cell.status).toBe("idle");
|
|
1373
660
|
// oxlint-disable-next-line typescript/no-explicit-any no-unsafe-optional-chaining
|
|
1374
661
|
expect((cell.output?.data as any)[0].msg).toBe(
|
|
1375
662
|
"This cell wasn't run because an ancestor was stopped with `mo.stop`: ",
|
|
1376
663
|
);
|
|
1377
|
-
expect(cell.stopped).toBe(true);
|
|
1378
664
|
expect(cell).toMatchSnapshot("ancestor stopped");
|
|
1379
665
|
|
|
1380
666
|
// Receive queued message
|
|
@@ -1387,8 +673,6 @@ describe("cell reducer", () => {
|
|
|
1387
673
|
timestamp: new Date(30).getTime() as Seconds,
|
|
1388
674
|
});
|
|
1389
675
|
cell = cells[0];
|
|
1390
|
-
expect(cell.status).toBe("queued");
|
|
1391
|
-
expect(cell.stopped).toBe(true);
|
|
1392
676
|
expect(cell).toMatchSnapshot("queued after stop");
|
|
1393
677
|
|
|
1394
678
|
// Receive running message
|
|
@@ -1401,8 +685,6 @@ describe("cell reducer", () => {
|
|
|
1401
685
|
timestamp: new Date(40).getTime() as Seconds,
|
|
1402
686
|
});
|
|
1403
687
|
cell = cells[0];
|
|
1404
|
-
expect(cell.status).toBe("running");
|
|
1405
|
-
expect(cell.stopped).toBe(false);
|
|
1406
688
|
expect(cell.output).toBe(null);
|
|
1407
689
|
expect(cell).toMatchSnapshot("running after stop");
|
|
1408
690
|
});
|
|
@@ -1429,7 +711,6 @@ describe("cell reducer", () => {
|
|
|
1429
711
|
|
|
1430
712
|
// Initial state
|
|
1431
713
|
let cell = cells[0];
|
|
1432
|
-
expect(cell.status).toBe("idle");
|
|
1433
714
|
expect(cell.consoleOutputs).toEqual([]);
|
|
1434
715
|
expect(cell).toMatchSnapshot("initial state");
|
|
1435
716
|
|
|
@@ -1438,7 +719,6 @@ describe("cell reducer", () => {
|
|
|
1438
719
|
cellId: firstCellId,
|
|
1439
720
|
});
|
|
1440
721
|
cell = cells[0];
|
|
1441
|
-
expect(cell.status).toBe("queued");
|
|
1442
722
|
expect(cell.consoleOutputs).toEqual([]);
|
|
1443
723
|
expect(cell).toMatchSnapshot("prepared for run");
|
|
1444
724
|
|
|
@@ -1452,7 +732,6 @@ describe("cell reducer", () => {
|
|
|
1452
732
|
timestamp: new Date(10).getTime() as Seconds,
|
|
1453
733
|
});
|
|
1454
734
|
cell = cells[0];
|
|
1455
|
-
expect(cell.status).toBe("queued");
|
|
1456
735
|
expect(cell).toMatchSnapshot("queued message received");
|
|
1457
736
|
|
|
1458
737
|
// Receive running messages
|
|
@@ -1465,7 +744,6 @@ describe("cell reducer", () => {
|
|
|
1465
744
|
timestamp: new Date(20).getTime() as Seconds,
|
|
1466
745
|
});
|
|
1467
746
|
cell = cells[0];
|
|
1468
|
-
expect(cell.status).toBe("running");
|
|
1469
747
|
expect(cell).toMatchSnapshot("running message received");
|
|
1470
748
|
|
|
1471
749
|
// Add console
|
|
@@ -1478,12 +756,10 @@ describe("cell reducer", () => {
|
|
|
1478
756
|
timestamp: new Date(22).getTime() as Seconds,
|
|
1479
757
|
});
|
|
1480
758
|
cell = cells[0];
|
|
1481
|
-
expect(cell.status).toBe("running");
|
|
1482
759
|
expect(cell).toMatchSnapshot("stdout received");
|
|
1483
760
|
|
|
1484
761
|
cell = cells[0];
|
|
1485
762
|
expect(cell.consoleOutputs[0]).toMatchObject(STDOUT);
|
|
1486
|
-
expect(cell.status).toBe("running");
|
|
1487
763
|
expect(cell).toMatchSnapshot("stdout verified");
|
|
1488
764
|
|
|
1489
765
|
// Ask via stdin
|
|
@@ -1498,7 +774,6 @@ describe("cell reducer", () => {
|
|
|
1498
774
|
cell = cells[0];
|
|
1499
775
|
expect(cell.consoleOutputs[0]).toMatchObject(STDOUT);
|
|
1500
776
|
expect(cell.consoleOutputs[1]).toMatchObject(STD_IN_1);
|
|
1501
|
-
expect(cell.status).toBe("running");
|
|
1502
777
|
expect(cell).toMatchSnapshot("stdin requested");
|
|
1503
778
|
|
|
1504
779
|
// Response to stdin
|
|
@@ -1512,7 +787,6 @@ describe("cell reducer", () => {
|
|
|
1512
787
|
...STD_IN_1,
|
|
1513
788
|
response: "Marimo!",
|
|
1514
789
|
});
|
|
1515
|
-
expect(cell.status).toBe("running");
|
|
1516
790
|
expect(cell).toMatchSnapshot("stdin answered");
|
|
1517
791
|
|
|
1518
792
|
// Ask via stdin, again
|
|
@@ -1530,7 +804,6 @@ describe("cell reducer", () => {
|
|
|
1530
804
|
{ ...STD_IN_1, response: "Marimo!" },
|
|
1531
805
|
STD_IN_2,
|
|
1532
806
|
]);
|
|
1533
|
-
expect(cell.status).toBe("running");
|
|
1534
807
|
expect(cell).toMatchSnapshot("second stdin requested");
|
|
1535
808
|
|
|
1536
809
|
// Interrupt, so we respond with ""
|
|
@@ -1548,7 +821,6 @@ describe("cell reducer", () => {
|
|
|
1548
821
|
timestamp: new Date(61).getTime() as Seconds,
|
|
1549
822
|
});
|
|
1550
823
|
cell = cells[0];
|
|
1551
|
-
expect(cell.status).toBe("idle");
|
|
1552
824
|
expect(cell.consoleOutputs).toMatchObject([
|
|
1553
825
|
STDOUT,
|
|
1554
826
|
{ ...STD_IN_1, response: "Marimo!" },
|
|
@@ -1579,7 +851,9 @@ describe("cell reducer", () => {
|
|
|
1579
851
|
response: secret,
|
|
1580
852
|
});
|
|
1581
853
|
|
|
1582
|
-
expect(cells[0].consoleOutputs).toEqual([
|
|
854
|
+
expect(cells[0].consoleOutputs).toEqual([
|
|
855
|
+
{ ...prompt, id: 1, response: "" },
|
|
856
|
+
]);
|
|
1583
857
|
expect(JSON.stringify(cells[0].consoleOutputs)).not.toContain(secret);
|
|
1584
858
|
});
|
|
1585
859
|
|
|
@@ -1640,7 +914,6 @@ describe("cell reducer", () => {
|
|
|
1640
914
|
|
|
1641
915
|
// Initial state
|
|
1642
916
|
let cell = cells[0];
|
|
1643
|
-
expect(cell.status).toBe("idle");
|
|
1644
917
|
expect(cell.consoleOutputs).toEqual([]);
|
|
1645
918
|
expect(cell).toMatchSnapshot("initial state");
|
|
1646
919
|
|
|
@@ -1659,7 +932,6 @@ describe("cell reducer", () => {
|
|
|
1659
932
|
cellId: firstCellId,
|
|
1660
933
|
});
|
|
1661
934
|
cell = cells[0];
|
|
1662
|
-
expect(cell.status).toBe("queued");
|
|
1663
935
|
expect(cell.consoleOutputs).toMatchObject([OLD_STDOUT]); // Old stays there until it starts running
|
|
1664
936
|
expect(cell).toMatchSnapshot("prepared for run");
|
|
1665
937
|
|
|
@@ -1673,7 +945,6 @@ describe("cell reducer", () => {
|
|
|
1673
945
|
timestamp: new Date(10).getTime() as Seconds,
|
|
1674
946
|
});
|
|
1675
947
|
cell = cells[0];
|
|
1676
|
-
expect(cell.status).toBe("queued");
|
|
1677
948
|
expect(cell.consoleOutputs).toMatchObject([OLD_STDOUT]); // Old stays there until it starts running
|
|
1678
949
|
expect(cell).toMatchSnapshot("queued message received");
|
|
1679
950
|
|
|
@@ -1687,620 +958,48 @@ describe("cell reducer", () => {
|
|
|
1687
958
|
timestamp: new Date(20).getTime() as Seconds,
|
|
1688
959
|
});
|
|
1689
960
|
cell = cells[0];
|
|
1690
|
-
expect(cell.status).toBe("running");
|
|
1691
961
|
expect(cell.consoleOutputs).toMatchObject([]);
|
|
1692
962
|
expect(cell).toMatchSnapshot("console cleared while running");
|
|
1693
963
|
|
|
1694
|
-
// Add console
|
|
1695
|
-
actions.handleCellMessage({
|
|
1696
|
-
cell_id: firstCellId,
|
|
1697
|
-
output: undefined,
|
|
1698
|
-
console: [STDOUT],
|
|
1699
|
-
status: "idle",
|
|
1700
|
-
stale_inputs: null,
|
|
1701
|
-
timestamp: new Date(22).getTime() as Seconds,
|
|
1702
|
-
});
|
|
1703
|
-
cell = cells[0];
|
|
1704
|
-
expect(cell.consoleOutputs).toMatchObject([STDOUT]);
|
|
1705
|
-
expect(cell
|
|
1706
|
-
expect(cell).toMatchSnapshot("console received while idle");
|
|
1707
|
-
});
|
|
1708
|
-
|
|
1709
|
-
it("can send a cell to the top", () => {
|
|
1710
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
1711
|
-
actions.createNewCell({ cellId: cellId("1"), before: false });
|
|
1712
|
-
actions.sendToTop({ cellId: cellId("2") });
|
|
1713
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
1714
|
-
"
|
|
1715
|
-
[2] ''
|
|
1716
|
-
|
|
1717
|
-
[0] ''
|
|
1718
|
-
|
|
1719
|
-
[1] ''
|
|
1720
|
-
"
|
|
1721
|
-
`);
|
|
1722
|
-
});
|
|
1723
|
-
|
|
1724
|
-
it("can send a cell to the bottom", () => {
|
|
1725
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
1726
|
-
actions.createNewCell({ cellId: cellId("1"), before: false });
|
|
1727
|
-
actions.sendToBottom({ cellId: firstCellId });
|
|
1728
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
1729
|
-
"
|
|
1730
|
-
[1] ''
|
|
1731
|
-
|
|
1732
|
-
[2] ''
|
|
1733
|
-
|
|
1734
|
-
[0] ''
|
|
1735
|
-
"
|
|
1736
|
-
`);
|
|
1737
|
-
});
|
|
1738
|
-
|
|
1739
|
-
it("can focus cells", () => {
|
|
1740
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
1741
|
-
actions.createNewCell({ cellId: cellId("1"), before: false });
|
|
1742
|
-
|
|
1743
|
-
actions.focusCell({ cellId: cellId("1"), where: "before" });
|
|
1744
|
-
expect(focusAndScrollCellIntoView).toHaveBeenCalledWith(
|
|
1745
|
-
expect.objectContaining({
|
|
1746
|
-
cellId: cellId("0"),
|
|
1747
|
-
}),
|
|
1748
|
-
);
|
|
1749
|
-
|
|
1750
|
-
actions.focusTopCell();
|
|
1751
|
-
expect(scrollToTop).toHaveBeenCalled();
|
|
1752
|
-
|
|
1753
|
-
actions.focusBottomCell();
|
|
1754
|
-
expect(scrollToBottom).toHaveBeenCalled();
|
|
1755
|
-
});
|
|
1756
|
-
|
|
1757
|
-
it("can update cell name", () => {
|
|
1758
|
-
actions.updateCellName({ cellId: firstCellId, name: "Test Cell" });
|
|
1759
|
-
expect(state.cellData[firstCellId].name).toBe("Test Cell");
|
|
1760
|
-
});
|
|
1761
|
-
|
|
1762
|
-
it("can set cell IDs and codes", () => {
|
|
1763
|
-
const newIds = [cellId("3"), cellId("4"), cellId("5")];
|
|
1764
|
-
const newCodes = ["code1", "code2", "code3"];
|
|
1765
|
-
|
|
1766
|
-
actions.setCellIds({ cellIds: newIds });
|
|
1767
|
-
expect(state.cellIds.atOrThrow(FIRST_COLUMN).topLevelIds).toEqual(newIds);
|
|
1768
|
-
|
|
1769
|
-
// When codeIsStale is false, lastCodeRun should match code
|
|
1770
|
-
actions.setCellCodes({
|
|
1771
|
-
codes: newCodes,
|
|
1772
|
-
ids: newIds,
|
|
1773
|
-
codeIsStale: false,
|
|
1774
|
-
});
|
|
1775
|
-
newIds.forEach((id, index) => {
|
|
1776
|
-
expect(state.cellData[id].code).toBe(newCodes[index]);
|
|
1777
|
-
expect(state.cellData[id].lastCodeRun).toBe(newCodes[index]);
|
|
1778
|
-
expect(state.cellData[id].edited).toBe(false);
|
|
1779
|
-
});
|
|
1780
|
-
|
|
1781
|
-
// When codeIsStale is true, lastCodeRun should not change
|
|
1782
|
-
const staleCodes = ["stale1", "stale2", "stale3"];
|
|
1783
|
-
actions.setCellCodes({
|
|
1784
|
-
codes: staleCodes,
|
|
1785
|
-
ids: newIds,
|
|
1786
|
-
codeIsStale: true,
|
|
1787
|
-
});
|
|
1788
|
-
newIds.forEach((id, index) => {
|
|
1789
|
-
expect(state.cellData[id].code).toBe(staleCodes[index]);
|
|
1790
|
-
expect(state.cellData[id].lastCodeRun).toBe(newCodes[index]);
|
|
1791
|
-
expect(state.cellData[id].edited).toBe(true);
|
|
1792
|
-
});
|
|
1793
|
-
});
|
|
1794
|
-
|
|
1795
|
-
it("can can add a new cell with/without stale code", () => {
|
|
1796
|
-
actions.setCellCodes({
|
|
1797
|
-
codes: ["new code"],
|
|
1798
|
-
ids: [cellId("2")],
|
|
1799
|
-
codeIsStale: false,
|
|
1800
|
-
});
|
|
1801
|
-
|
|
1802
|
-
expect(state.cellData[cellId("2")].code).toBe("new code");
|
|
1803
|
-
expect(state.cellData[cellId("2")].edited).toBe(false);
|
|
1804
|
-
expect(state.cellData[cellId("2")].lastCodeRun).toBe("new code");
|
|
1805
|
-
|
|
1806
|
-
actions.setCellCodes({
|
|
1807
|
-
codes: ["new code 2"],
|
|
1808
|
-
ids: [cellId("9")],
|
|
1809
|
-
codeIsStale: true,
|
|
1810
|
-
});
|
|
1811
|
-
|
|
1812
|
-
expect(state.cellData[cellId("9")].code).toBe("new code 2");
|
|
1813
|
-
expect(state.cellData[cellId("9")].edited).toBe(true);
|
|
1814
|
-
expect(state.cellData[cellId("9")].lastCodeRun).toBe(null);
|
|
1815
|
-
});
|
|
1816
|
-
|
|
1817
|
-
it("can partial update cell codes", () => {
|
|
1818
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
1819
|
-
actions.createNewCell({ cellId: cellId("1"), before: false });
|
|
1820
|
-
|
|
1821
|
-
expect(state.cellIds.inOrderIds).toEqual([
|
|
1822
|
-
cellId("0"),
|
|
1823
|
-
cellId("1"),
|
|
1824
|
-
cellId("2"),
|
|
1825
|
-
]);
|
|
1826
|
-
expect(state.cellData[cellId("0")].code).toBe("");
|
|
1827
|
-
expect(state.cellData[cellId("1")].code).toBe("");
|
|
1828
|
-
expect(state.cellData[cellId("2")].code).toBe("");
|
|
1829
|
-
|
|
1830
|
-
// Update cell 1
|
|
1831
|
-
actions.setCellCodes({
|
|
1832
|
-
codes: ["new code 2"],
|
|
1833
|
-
ids: [cellId("1")],
|
|
1834
|
-
codeIsStale: false,
|
|
1835
|
-
});
|
|
1836
|
-
|
|
1837
|
-
expect(state.cellIds.inOrderIds).toEqual([
|
|
1838
|
-
cellId("0"),
|
|
1839
|
-
cellId("1"),
|
|
1840
|
-
cellId("2"),
|
|
1841
|
-
]);
|
|
1842
|
-
expect(state.cellData[cellId("0")].code).toBe("");
|
|
1843
|
-
expect(state.cellData[cellId("1")].code).toBe("new code 2");
|
|
1844
|
-
expect(state.cellData[cellId("1")].edited).toBe(false);
|
|
1845
|
-
expect(state.cellData[cellId("2")].code).toBe("");
|
|
1846
|
-
});
|
|
1847
|
-
|
|
1848
|
-
it("can set cell codes with new cell ids, while preserving the old cell data", () => {
|
|
1849
|
-
actions.setCellCodes({
|
|
1850
|
-
codes: ["code1", "code2", "code3"],
|
|
1851
|
-
ids: [cellId("3"), cellId("4"), cellId("5")],
|
|
1852
|
-
codeIsStale: false,
|
|
1853
|
-
});
|
|
1854
|
-
expect(state.cellData[cellId("3")].code).toBe("code1");
|
|
1855
|
-
expect(state.cellData[cellId("4")].code).toBe("code2");
|
|
1856
|
-
expect(state.cellData[cellId("5")].code).toBe("code3");
|
|
1857
|
-
|
|
1858
|
-
// Update with some new cell ids and some old cell ids
|
|
1859
|
-
actions.setCellIds({
|
|
1860
|
-
cellIds: [cellId("1"), cellId("2"), cellId("3"), cellId("4")],
|
|
1861
|
-
});
|
|
1862
|
-
actions.setCellCodes({
|
|
1863
|
-
codes: ["new1", "new2", "code1", "code2"],
|
|
1864
|
-
ids: [cellId("1"), cellId("2"), cellId("3"), cellId("4")],
|
|
1865
|
-
codeIsStale: false,
|
|
1866
|
-
});
|
|
1867
|
-
expect(state.cellData[cellId("1")].code).toBe("new1");
|
|
1868
|
-
expect(state.cellData[cellId("2")].code).toBe("new2");
|
|
1869
|
-
expect(state.cellData[cellId("3")].code).toBe("code1");
|
|
1870
|
-
expect(state.cellData[cellId("4")].code).toBe("code2");
|
|
1871
|
-
expect(state.cellIds.inOrderIds).toEqual([
|
|
1872
|
-
cellId("1"),
|
|
1873
|
-
cellId("2"),
|
|
1874
|
-
cellId("3"),
|
|
1875
|
-
cellId("4"),
|
|
1876
|
-
]);
|
|
1877
|
-
// Cell 5 data is preserved (possibly used for tracing), but it's not in the cellIds
|
|
1878
|
-
expect(state.cellData[cellId("5")]).not.toBeUndefined();
|
|
1879
|
-
});
|
|
1880
|
-
|
|
1881
|
-
it("can set cell codes with names and configs", () => {
|
|
1882
|
-
const newIds = [cellId("3"), cellId("4")];
|
|
1883
|
-
actions.setCellIds({ cellIds: newIds });
|
|
1884
|
-
actions.setCellCodes({
|
|
1885
|
-
codes: ["code1", "code2"],
|
|
1886
|
-
ids: newIds,
|
|
1887
|
-
codeIsStale: false,
|
|
1888
|
-
names: ["setup_cell", "analysis"],
|
|
1889
|
-
configs: [
|
|
1890
|
-
{ hide_code: true, disabled: false, column: null },
|
|
1891
|
-
{ hide_code: false, disabled: true, column: null },
|
|
1892
|
-
],
|
|
1893
|
-
});
|
|
1894
|
-
|
|
1895
|
-
expect(state.cellData[cellId("3")].name).toBe("setup_cell");
|
|
1896
|
-
expect(state.cellData[cellId("3")].config.hide_code).toBe(true);
|
|
1897
|
-
expect(state.cellData[cellId("3")].config.disabled).toBe(false);
|
|
1898
|
-
|
|
1899
|
-
expect(state.cellData[cellId("4")].name).toBe("analysis");
|
|
1900
|
-
expect(state.cellData[cellId("4")].config.hide_code).toBe(false);
|
|
1901
|
-
expect(state.cellData[cellId("4")].config.disabled).toBe(true);
|
|
1902
|
-
});
|
|
1903
|
-
|
|
1904
|
-
it("can set cell codes without names/configs (backward compat)", () => {
|
|
1905
|
-
const newIds = [cellId("3")];
|
|
1906
|
-
actions.setCellIds({ cellIds: newIds });
|
|
1907
|
-
actions.setCellCodes({
|
|
1908
|
-
codes: ["code1"],
|
|
1909
|
-
ids: newIds,
|
|
1910
|
-
codeIsStale: false,
|
|
1911
|
-
});
|
|
1912
|
-
|
|
1913
|
-
// Should use defaults when names/configs not provided
|
|
1914
|
-
expect(state.cellData[cellId("3")].code).toBe("code1");
|
|
1915
|
-
expect(state.cellData[cellId("3")].config.hide_code).toBe(false);
|
|
1916
|
-
expect(state.cellData[cellId("3")].config.disabled).toBe(false);
|
|
1917
|
-
});
|
|
1918
|
-
|
|
1919
|
-
it("can update names and configs on existing cells via setCellCodes", () => {
|
|
1920
|
-
// Set initial state
|
|
1921
|
-
actions.setCellCodes({
|
|
1922
|
-
codes: ["x = 1"],
|
|
1923
|
-
ids: [firstCellId],
|
|
1924
|
-
codeIsStale: false,
|
|
1925
|
-
names: ["old_name"],
|
|
1926
|
-
configs: [{ hide_code: false, disabled: false, column: null }],
|
|
1927
|
-
});
|
|
1928
|
-
expect(state.cellData[firstCellId].name).toBe("old_name");
|
|
1929
|
-
expect(state.cellData[firstCellId].config.hide_code).toBe(false);
|
|
1930
|
-
|
|
1931
|
-
// Update with new name and config (same code)
|
|
1932
|
-
actions.setCellCodes({
|
|
1933
|
-
codes: ["x = 1"],
|
|
1934
|
-
ids: [firstCellId],
|
|
1935
|
-
codeIsStale: true,
|
|
1936
|
-
names: ["new_name"],
|
|
1937
|
-
configs: [{ hide_code: true, disabled: false, column: null }],
|
|
1938
|
-
});
|
|
1939
|
-
expect(state.cellData[firstCellId].name).toBe("new_name");
|
|
1940
|
-
expect(state.cellData[firstCellId].config.hide_code).toBe(true);
|
|
1941
|
-
});
|
|
1942
|
-
|
|
1943
|
-
it("can fold and unfold all cells", () => {
|
|
1944
|
-
actions.foldAll();
|
|
1945
|
-
expect(foldAllBulk).toHaveBeenCalled();
|
|
1946
|
-
|
|
1947
|
-
actions.unfoldAll();
|
|
1948
|
-
expect(unfoldAllBulk).toHaveBeenCalled();
|
|
1949
|
-
});
|
|
1950
|
-
|
|
1951
|
-
it("can clear logs", () => {
|
|
1952
|
-
state.cellLogs = [
|
|
1953
|
-
{
|
|
1954
|
-
level: "stderr",
|
|
1955
|
-
message: "log1",
|
|
1956
|
-
timestamp: 0,
|
|
1957
|
-
source: { type: "cell", cellId: firstCellId },
|
|
1958
|
-
},
|
|
1959
|
-
{
|
|
1960
|
-
level: "stderr",
|
|
1961
|
-
message: "log1",
|
|
1962
|
-
timestamp: 0,
|
|
1963
|
-
source: { type: "cell", cellId: firstCellId },
|
|
1964
|
-
},
|
|
1965
|
-
];
|
|
1966
|
-
actions.clearLogs();
|
|
1967
|
-
expect(state.cellLogs).toEqual([]);
|
|
1968
|
-
});
|
|
1969
|
-
|
|
1970
|
-
it("can collapse and expand cells", () => {
|
|
1971
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
1972
|
-
actions.createNewCell({
|
|
1973
|
-
cellId: cellId("1"),
|
|
1974
|
-
before: false,
|
|
1975
|
-
code: "# Header",
|
|
1976
|
-
});
|
|
1977
|
-
actions.createNewCell({
|
|
1978
|
-
cellId: cellId("2"),
|
|
1979
|
-
before: false,
|
|
1980
|
-
code: "## Subheader",
|
|
1981
|
-
});
|
|
1982
|
-
|
|
1983
|
-
const id = state.cellIds.atOrThrow(FIRST_COLUMN).atOrThrow(1);
|
|
1984
|
-
state.cellRuntime[id] = {
|
|
1985
|
-
...state.cellRuntime[id],
|
|
1986
|
-
outline: {
|
|
1987
|
-
items: [{ name: "Header", level: 1, by: { id: "header" } }],
|
|
1988
|
-
},
|
|
1989
|
-
};
|
|
1990
|
-
|
|
1991
|
-
actions.collapseCell({ cellId: id });
|
|
1992
|
-
expect(state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(id)).toBe(true);
|
|
1993
|
-
|
|
1994
|
-
actions.expandCell({ cellId: id });
|
|
1995
|
-
expect(state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(id)).toBe(false);
|
|
1996
|
-
});
|
|
1997
|
-
|
|
1998
|
-
it("can collapse and expand all cells in multiple columns", () => {
|
|
1999
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
2000
|
-
actions.createNewCell({
|
|
2001
|
-
cellId: cellId("1"),
|
|
2002
|
-
before: false,
|
|
2003
|
-
code: "# First Column Header",
|
|
2004
|
-
});
|
|
2005
|
-
actions.createNewCell({
|
|
2006
|
-
cellId: cellId("2"),
|
|
2007
|
-
before: false,
|
|
2008
|
-
code: "## First Column Subheader",
|
|
2009
|
-
});
|
|
2010
|
-
|
|
2011
|
-
actions.addColumnBreakpoint({ cellId: cellId("2") });
|
|
2012
|
-
|
|
2013
|
-
actions.createNewCell({
|
|
2014
|
-
cellId: cellId("3"),
|
|
2015
|
-
before: false,
|
|
2016
|
-
code: "# Second Column Header",
|
|
2017
|
-
});
|
|
2018
|
-
actions.createNewCell({
|
|
2019
|
-
cellId: cellId("4"),
|
|
2020
|
-
before: false,
|
|
2021
|
-
code: "## Second Column Subheader",
|
|
2022
|
-
});
|
|
2023
|
-
|
|
2024
|
-
const firstColumnHeaderId = state.cellIds
|
|
2025
|
-
.atOrThrow(FIRST_COLUMN)
|
|
2026
|
-
.atOrThrow(1);
|
|
2027
|
-
state.cellRuntime[firstColumnHeaderId] = {
|
|
2028
|
-
...state.cellRuntime[firstColumnHeaderId],
|
|
2029
|
-
outline: {
|
|
2030
|
-
items: [
|
|
2031
|
-
{ name: "First Column Header", level: 1, by: { id: "header" } },
|
|
2032
|
-
],
|
|
2033
|
-
},
|
|
2034
|
-
};
|
|
2035
|
-
|
|
2036
|
-
const secondColumnHeaderId = state.cellIds
|
|
2037
|
-
.atOrThrow(SECOND_COLUMN)
|
|
2038
|
-
.atOrThrow(0);
|
|
2039
|
-
state.cellRuntime[secondColumnHeaderId] = {
|
|
2040
|
-
...state.cellRuntime[secondColumnHeaderId],
|
|
2041
|
-
outline: {
|
|
2042
|
-
items: [
|
|
2043
|
-
{ name: "Second Column Header", level: 1, by: { id: "header" } },
|
|
2044
|
-
],
|
|
2045
|
-
},
|
|
2046
|
-
};
|
|
2047
|
-
|
|
2048
|
-
actions.collapseAllCells();
|
|
2049
|
-
expect(
|
|
2050
|
-
state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(firstColumnHeaderId),
|
|
2051
|
-
).toBe(true);
|
|
2052
|
-
expect(
|
|
2053
|
-
state.cellIds.atOrThrow(SECOND_COLUMN).isCollapsed(secondColumnHeaderId),
|
|
2054
|
-
).toBe(true);
|
|
2055
|
-
|
|
2056
|
-
actions.expandAllCells();
|
|
2057
|
-
expect(
|
|
2058
|
-
state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(firstColumnHeaderId),
|
|
2059
|
-
).toBe(false);
|
|
2060
|
-
expect(
|
|
2061
|
-
state.cellIds.atOrThrow(SECOND_COLUMN).isCollapsed(secondColumnHeaderId),
|
|
2062
|
-
).toBe(false);
|
|
2063
|
-
});
|
|
2064
|
-
|
|
2065
|
-
it("can collapse and expand nested cells in one call", () => {
|
|
2066
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
2067
|
-
actions.createNewCell({
|
|
2068
|
-
cellId: cellId("1"),
|
|
2069
|
-
before: false,
|
|
2070
|
-
code: "# Header",
|
|
2071
|
-
});
|
|
2072
|
-
actions.createNewCell({
|
|
2073
|
-
cellId: cellId("2"),
|
|
2074
|
-
before: false,
|
|
2075
|
-
code: "## Subheader",
|
|
2076
|
-
});
|
|
2077
|
-
actions.createNewCell({
|
|
2078
|
-
cellId: cellId("3"),
|
|
2079
|
-
before: false,
|
|
2080
|
-
code: "### Subsubheader",
|
|
2081
|
-
});
|
|
2082
|
-
|
|
2083
|
-
const headerId = state.cellIds.atOrThrow(FIRST_COLUMN).atOrThrow(1);
|
|
2084
|
-
state.cellRuntime[headerId] = {
|
|
2085
|
-
...state.cellRuntime[headerId],
|
|
2086
|
-
outline: {
|
|
2087
|
-
items: [{ name: "Header", level: 1, by: { id: "header" } }],
|
|
2088
|
-
},
|
|
2089
|
-
};
|
|
2090
|
-
|
|
2091
|
-
const subheaderId = state.cellIds.atOrThrow(FIRST_COLUMN).atOrThrow(2);
|
|
2092
|
-
state.cellRuntime[subheaderId] = {
|
|
2093
|
-
...state.cellRuntime[subheaderId],
|
|
2094
|
-
outline: {
|
|
2095
|
-
items: [{ name: "Subheader", level: 2, by: { id: "subheader" } }],
|
|
2096
|
-
},
|
|
2097
|
-
};
|
|
2098
|
-
|
|
2099
|
-
// Check if both the parent and child are collapsed
|
|
2100
|
-
actions.collapseAllCells();
|
|
2101
|
-
expect(state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(headerId)).toBe(
|
|
2102
|
-
true,
|
|
2103
|
-
);
|
|
2104
|
-
actions.expandCell({ cellId: headerId });
|
|
2105
|
-
expect(state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(subheaderId)).toBe(
|
|
2106
|
-
true,
|
|
2107
|
-
);
|
|
2108
|
-
actions.collapseCell({ cellId: headerId });
|
|
2109
|
-
|
|
2110
|
-
// Check if both the parent and child are expanded
|
|
2111
|
-
actions.expandAllCells();
|
|
2112
|
-
expect(state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(headerId)).toBe(
|
|
2113
|
-
false,
|
|
2114
|
-
);
|
|
2115
|
-
expect(state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(subheaderId)).toBe(
|
|
2116
|
-
false,
|
|
2117
|
-
);
|
|
2118
|
-
});
|
|
2119
|
-
|
|
2120
|
-
it("can collapse all cells when nested heading ranges end at the same cell", () => {
|
|
2121
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
2122
|
-
actions.createNewCell({
|
|
2123
|
-
cellId: cellId("1"),
|
|
2124
|
-
before: false,
|
|
2125
|
-
code: "# Header",
|
|
2126
|
-
});
|
|
2127
|
-
actions.createNewCell({
|
|
2128
|
-
cellId: cellId("2"),
|
|
2129
|
-
before: false,
|
|
2130
|
-
code: "## Subheader",
|
|
2131
|
-
});
|
|
2132
|
-
actions.createNewCell({
|
|
2133
|
-
cellId: cellId("3"),
|
|
2134
|
-
before: false,
|
|
2135
|
-
code: "### Subsubheader",
|
|
2136
|
-
});
|
|
2137
|
-
|
|
2138
|
-
const headerId = state.cellIds.atOrThrow(FIRST_COLUMN).atOrThrow(1);
|
|
2139
|
-
state.cellRuntime[headerId] = {
|
|
2140
|
-
...state.cellRuntime[headerId],
|
|
2141
|
-
outline: {
|
|
2142
|
-
items: [{ name: "Header", level: 1, by: { id: "header" } }],
|
|
2143
|
-
},
|
|
2144
|
-
};
|
|
2145
|
-
|
|
2146
|
-
const subheaderId = state.cellIds.atOrThrow(FIRST_COLUMN).atOrThrow(2);
|
|
2147
|
-
state.cellRuntime[subheaderId] = {
|
|
2148
|
-
...state.cellRuntime[subheaderId],
|
|
2149
|
-
outline: {
|
|
2150
|
-
items: [{ name: "Subheader", level: 2, by: { id: "subheader" } }],
|
|
2151
|
-
},
|
|
2152
|
-
};
|
|
2153
|
-
|
|
2154
|
-
const subsubheaderId = state.cellIds.atOrThrow(FIRST_COLUMN).atOrThrow(3);
|
|
2155
|
-
state.cellRuntime[subsubheaderId] = {
|
|
2156
|
-
...state.cellRuntime[subsubheaderId],
|
|
2157
|
-
outline: {
|
|
2158
|
-
items: [{ name: "Subsubheader", level: 3, by: { id: "subsubheader" } }],
|
|
2159
|
-
},
|
|
2160
|
-
};
|
|
2161
|
-
|
|
2162
|
-
// The header, subheader and subsubheader ranges all end at the last
|
|
2163
|
-
// cell. Collapsing all used to throw "Node ... not found in tree" and
|
|
2164
|
-
// leave the whole notebook unchanged.
|
|
2165
|
-
actions.collapseAllCells();
|
|
2166
|
-
expect(state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(headerId)).toBe(
|
|
2167
|
-
true,
|
|
2168
|
-
);
|
|
2169
|
-
expect(state.cellIds.atOrThrow(FIRST_COLUMN).topLevelIds).toEqual([
|
|
2170
|
-
firstCellId,
|
|
2171
|
-
headerId,
|
|
2172
|
-
]);
|
|
2173
|
-
|
|
2174
|
-
// Each level is nested under its parent, collapsed
|
|
2175
|
-
actions.expandCell({ cellId: headerId });
|
|
2176
|
-
expect(state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(subheaderId)).toBe(
|
|
2177
|
-
true,
|
|
2178
|
-
);
|
|
2179
|
-
actions.expandCell({ cellId: subheaderId });
|
|
2180
|
-
expect(
|
|
2181
|
-
state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(subsubheaderId),
|
|
2182
|
-
).toBe(true);
|
|
2183
|
-
});
|
|
2184
|
-
|
|
2185
|
-
it("can collapse all cells when a deep chain is followed by a later section", () => {
|
|
2186
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
2187
|
-
actions.createNewCell({
|
|
2188
|
-
cellId: cellId("1"),
|
|
2189
|
-
before: false,
|
|
2190
|
-
code: "# Header",
|
|
2191
|
-
});
|
|
2192
|
-
actions.createNewCell({
|
|
2193
|
-
cellId: cellId("2"),
|
|
2194
|
-
before: false,
|
|
2195
|
-
code: "## Section A",
|
|
2196
|
-
});
|
|
2197
|
-
actions.createNewCell({
|
|
2198
|
-
cellId: cellId("3"),
|
|
2199
|
-
before: false,
|
|
2200
|
-
code: "### Sub of A",
|
|
2201
|
-
});
|
|
2202
|
-
actions.createNewCell({
|
|
2203
|
-
cellId: cellId("4"),
|
|
2204
|
-
before: false,
|
|
2205
|
-
code: "## Section B",
|
|
2206
|
-
});
|
|
2207
|
-
|
|
2208
|
-
const headerId = state.cellIds.atOrThrow(FIRST_COLUMN).atOrThrow(1);
|
|
2209
|
-
state.cellRuntime[headerId] = {
|
|
2210
|
-
...state.cellRuntime[headerId],
|
|
2211
|
-
outline: {
|
|
2212
|
-
items: [{ name: "Header", level: 1, by: { id: "header" } }],
|
|
2213
|
-
},
|
|
2214
|
-
};
|
|
2215
|
-
|
|
2216
|
-
const sectionAId = state.cellIds.atOrThrow(FIRST_COLUMN).atOrThrow(2);
|
|
2217
|
-
state.cellRuntime[sectionAId] = {
|
|
2218
|
-
...state.cellRuntime[sectionAId],
|
|
2219
|
-
outline: {
|
|
2220
|
-
items: [{ name: "Section A", level: 2, by: { id: "section-a" } }],
|
|
2221
|
-
},
|
|
2222
|
-
};
|
|
2223
|
-
|
|
2224
|
-
const subsectionId = state.cellIds.atOrThrow(FIRST_COLUMN).atOrThrow(3);
|
|
2225
|
-
state.cellRuntime[subsectionId] = {
|
|
2226
|
-
...state.cellRuntime[subsectionId],
|
|
2227
|
-
outline: {
|
|
2228
|
-
items: [{ name: "Sub of A", level: 3, by: { id: "sub-of-a" } }],
|
|
2229
|
-
},
|
|
2230
|
-
};
|
|
2231
|
-
|
|
2232
|
-
const sectionBId = state.cellIds.atOrThrow(FIRST_COLUMN).atOrThrow(4);
|
|
2233
|
-
state.cellRuntime[sectionBId] = {
|
|
2234
|
-
...state.cellRuntime[sectionBId],
|
|
2235
|
-
outline: {
|
|
2236
|
-
items: [{ name: "Section B", level: 2, by: { id: "section-b" } }],
|
|
2237
|
-
},
|
|
2238
|
-
};
|
|
2239
|
-
|
|
2240
|
-
actions.collapseAllCells();
|
|
2241
|
-
expect(state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(headerId)).toBe(
|
|
2242
|
-
true,
|
|
2243
|
-
);
|
|
2244
|
-
expect(state.cellIds.atOrThrow(FIRST_COLUMN).topLevelIds).toEqual([
|
|
2245
|
-
firstCellId,
|
|
2246
|
-
headerId,
|
|
2247
|
-
]);
|
|
2248
|
-
|
|
2249
|
-
// Both sections sit under the header, collapsed, with the subsection
|
|
2250
|
-
// nested under section A
|
|
2251
|
-
actions.expandCell({ cellId: headerId });
|
|
2252
|
-
expect(state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(sectionAId)).toBe(
|
|
2253
|
-
true,
|
|
2254
|
-
);
|
|
2255
|
-
expect(state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(sectionBId)).toBe(
|
|
2256
|
-
true,
|
|
2257
|
-
);
|
|
2258
|
-
actions.expandCell({ cellId: sectionAId });
|
|
2259
|
-
expect(
|
|
2260
|
-
state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(subsectionId),
|
|
2261
|
-
).toBe(true);
|
|
964
|
+
// Add console
|
|
965
|
+
actions.handleCellMessage({
|
|
966
|
+
cell_id: firstCellId,
|
|
967
|
+
output: undefined,
|
|
968
|
+
console: [STDOUT],
|
|
969
|
+
status: "idle",
|
|
970
|
+
stale_inputs: null,
|
|
971
|
+
timestamp: new Date(22).getTime() as Seconds,
|
|
972
|
+
});
|
|
973
|
+
cell = cells[0];
|
|
974
|
+
expect(cell.consoleOutputs).toMatchObject([STDOUT]);
|
|
975
|
+
expect(cell).toMatchSnapshot("console received while idle");
|
|
2262
976
|
});
|
|
2263
977
|
|
|
2264
|
-
it("can
|
|
2265
|
-
actions.
|
|
2266
|
-
|
|
2267
|
-
actions.collapseCell({ cellId: firstCellId });
|
|
978
|
+
it("can fold and unfold all cells", () => {
|
|
979
|
+
actions.foldAll();
|
|
980
|
+
expect(foldAllBulk).toHaveBeenCalled();
|
|
2268
981
|
|
|
2269
|
-
actions.
|
|
2270
|
-
expect(
|
|
2271
|
-
false,
|
|
2272
|
-
);
|
|
982
|
+
actions.unfoldAll();
|
|
983
|
+
expect(unfoldAllBulk).toHaveBeenCalled();
|
|
2273
984
|
});
|
|
2274
985
|
|
|
2275
|
-
it("can
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
actions.
|
|
2291
|
-
expect(state.
|
|
2292
|
-
originalCellCount + 1,
|
|
2293
|
-
);
|
|
2294
|
-
expect(state.cellData[nextCellId].code).toBe("line1");
|
|
2295
|
-
expect(
|
|
2296
|
-
state.cellData[state.cellIds.atOrThrow(FIRST_COLUMN).atOrThrow(2)].code,
|
|
2297
|
-
).toBe("line2");
|
|
2298
|
-
|
|
2299
|
-
actions.undoSplitCell({ cellId: nextCellId, snapshot: "line1\nline2" });
|
|
2300
|
-
expect(state.cellIds.atOrThrow(FIRST_COLUMN).length).toBe(
|
|
2301
|
-
originalCellCount,
|
|
2302
|
-
);
|
|
2303
|
-
expect(state.cellData[nextCellId].code).toBe("line1\nline2");
|
|
986
|
+
it("can clear logs", () => {
|
|
987
|
+
state.cellLogs = [
|
|
988
|
+
{
|
|
989
|
+
level: "stderr",
|
|
990
|
+
message: "log1",
|
|
991
|
+
timestamp: 0,
|
|
992
|
+
source: { type: "cell", cellId: firstCellId },
|
|
993
|
+
},
|
|
994
|
+
{
|
|
995
|
+
level: "stderr",
|
|
996
|
+
message: "log1",
|
|
997
|
+
timestamp: 0,
|
|
998
|
+
source: { type: "cell", cellId: firstCellId },
|
|
999
|
+
},
|
|
1000
|
+
];
|
|
1001
|
+
actions.clearLogs();
|
|
1002
|
+
expect(state.cellLogs).toEqual([]);
|
|
2304
1003
|
});
|
|
2305
1004
|
|
|
2306
1005
|
it("can handle multiple console outputs", () => {
|
|
@@ -2339,407 +1038,6 @@ describe("cell reducer", () => {
|
|
|
2339
1038
|
`);
|
|
2340
1039
|
});
|
|
2341
1040
|
|
|
2342
|
-
it("can add a column breakpoint", () => {
|
|
2343
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
2344
|
-
actions.createNewCell({ cellId: cellId("1"), before: false });
|
|
2345
|
-
actions.createNewCell({ cellId: cellId("2"), before: false });
|
|
2346
|
-
|
|
2347
|
-
expect(state.cellIds.getColumns().length).toBe(1);
|
|
2348
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
2349
|
-
"
|
|
2350
|
-
[0] ''
|
|
2351
|
-
|
|
2352
|
-
[1] ''
|
|
2353
|
-
|
|
2354
|
-
[2] ''
|
|
2355
|
-
|
|
2356
|
-
[3] ''
|
|
2357
|
-
"
|
|
2358
|
-
`);
|
|
2359
|
-
|
|
2360
|
-
actions.addColumnBreakpoint({ cellId: cellId("2") });
|
|
2361
|
-
|
|
2362
|
-
expect(state.cellIds.getColumns().length).toBe(2);
|
|
2363
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
2364
|
-
"
|
|
2365
|
-
> col 0
|
|
2366
|
-
[0] ''
|
|
2367
|
-
|
|
2368
|
-
[1] ''
|
|
2369
|
-
|
|
2370
|
-
> col 1
|
|
2371
|
-
[2] ''
|
|
2372
|
-
|
|
2373
|
-
[3] ''
|
|
2374
|
-
"
|
|
2375
|
-
`);
|
|
2376
|
-
|
|
2377
|
-
// Check that the cells are in the correct columns
|
|
2378
|
-
expect(state.cellIds.getColumns()[0].topLevelIds).toEqual([
|
|
2379
|
-
cellId("0"),
|
|
2380
|
-
cellId("1"),
|
|
2381
|
-
]);
|
|
2382
|
-
expect(state.cellIds.getColumns()[1].topLevelIds).toEqual([
|
|
2383
|
-
cellId("2"),
|
|
2384
|
-
cellId("3"),
|
|
2385
|
-
]);
|
|
2386
|
-
});
|
|
2387
|
-
|
|
2388
|
-
it("cannot add a column breakpoint before the first cell", () => {
|
|
2389
|
-
expect(state.cellIds.getColumns().length).toBe(1);
|
|
2390
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
2391
|
-
actions.createNewCell({ cellId: cellId("1"), before: false });
|
|
2392
|
-
actions.addColumnBreakpoint({ cellId: firstCellId });
|
|
2393
|
-
expect(state.cellIds.getColumns().length).toBe(1);
|
|
2394
|
-
});
|
|
2395
|
-
|
|
2396
|
-
it("can delete a column", () => {
|
|
2397
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
2398
|
-
actions.createNewCell({ cellId: cellId("1"), before: false });
|
|
2399
|
-
actions.createNewCell({ cellId: cellId("2"), before: false });
|
|
2400
|
-
actions.addColumnBreakpoint({ cellId: cellId("2") });
|
|
2401
|
-
|
|
2402
|
-
expect(state.cellIds.getColumns().length).toBe(2);
|
|
2403
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
2404
|
-
"
|
|
2405
|
-
> col 0
|
|
2406
|
-
[0] ''
|
|
2407
|
-
|
|
2408
|
-
[1] ''
|
|
2409
|
-
|
|
2410
|
-
> col 1
|
|
2411
|
-
[2] ''
|
|
2412
|
-
|
|
2413
|
-
[3] ''
|
|
2414
|
-
"
|
|
2415
|
-
`);
|
|
2416
|
-
|
|
2417
|
-
const columnId = state.cellIds.atOrThrow(0).id;
|
|
2418
|
-
actions.deleteColumn({ columnId: columnId });
|
|
2419
|
-
|
|
2420
|
-
expect(state.cellIds.getColumns().length).toBe(1);
|
|
2421
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
2422
|
-
"
|
|
2423
|
-
[2] ''
|
|
2424
|
-
|
|
2425
|
-
[3] ''
|
|
2426
|
-
|
|
2427
|
-
[0] ''
|
|
2428
|
-
|
|
2429
|
-
[1] ''
|
|
2430
|
-
"
|
|
2431
|
-
`);
|
|
2432
|
-
|
|
2433
|
-
// Check that all cells are now in the remaining column
|
|
2434
|
-
expect(state.cellIds.getColumns()[0].topLevelIds).toEqual([
|
|
2435
|
-
"2",
|
|
2436
|
-
"3",
|
|
2437
|
-
"0",
|
|
2438
|
-
"1",
|
|
2439
|
-
]);
|
|
2440
|
-
});
|
|
2441
|
-
|
|
2442
|
-
it("deleting the last column does nothing", () => {
|
|
2443
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
2444
|
-
actions.createNewCell({ cellId: cellId("1"), before: false });
|
|
2445
|
-
|
|
2446
|
-
const initialState = { ...state };
|
|
2447
|
-
|
|
2448
|
-
actions.deleteColumn({ columnId: initialState.cellIds.atOrThrow(0).id });
|
|
2449
|
-
|
|
2450
|
-
// State should not change
|
|
2451
|
-
expect(state).toEqual(initialState);
|
|
2452
|
-
});
|
|
2453
|
-
|
|
2454
|
-
it("can drop a cell over another cell", () => {
|
|
2455
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
2456
|
-
actions.createNewCell({ cellId: cellId("1"), before: false });
|
|
2457
|
-
actions.createNewCell({ cellId: cellId("2"), before: false });
|
|
2458
|
-
|
|
2459
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
2460
|
-
"
|
|
2461
|
-
[0] ''
|
|
2462
|
-
|
|
2463
|
-
[1] ''
|
|
2464
|
-
|
|
2465
|
-
[2] ''
|
|
2466
|
-
|
|
2467
|
-
[3] ''
|
|
2468
|
-
"
|
|
2469
|
-
`);
|
|
2470
|
-
|
|
2471
|
-
actions.dropCellOverCell({
|
|
2472
|
-
cellId: cellId("0"),
|
|
2473
|
-
overCellId: cellId("3"),
|
|
2474
|
-
});
|
|
2475
|
-
|
|
2476
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
2477
|
-
"
|
|
2478
|
-
[1] ''
|
|
2479
|
-
|
|
2480
|
-
[2] ''
|
|
2481
|
-
|
|
2482
|
-
[3] ''
|
|
2483
|
-
|
|
2484
|
-
[0] ''
|
|
2485
|
-
"
|
|
2486
|
-
`);
|
|
2487
|
-
});
|
|
2488
|
-
|
|
2489
|
-
it("can drop a cell over a new column", () => {
|
|
2490
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
2491
|
-
actions.createNewCell({ cellId: cellId("1"), before: false });
|
|
2492
|
-
|
|
2493
|
-
expect(state.cellIds.getColumns().length).toBe(1);
|
|
2494
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
2495
|
-
"
|
|
2496
|
-
[0] ''
|
|
2497
|
-
|
|
2498
|
-
[1] ''
|
|
2499
|
-
|
|
2500
|
-
[2] ''
|
|
2501
|
-
"
|
|
2502
|
-
`);
|
|
2503
|
-
|
|
2504
|
-
actions.dropOverNewColumn({ cellId: cellId("1") });
|
|
2505
|
-
|
|
2506
|
-
expect(state.cellIds.getColumns().length).toBe(2);
|
|
2507
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
2508
|
-
"
|
|
2509
|
-
> col 0
|
|
2510
|
-
[0] ''
|
|
2511
|
-
|
|
2512
|
-
[2] ''
|
|
2513
|
-
|
|
2514
|
-
> col 1
|
|
2515
|
-
[1] ''
|
|
2516
|
-
"
|
|
2517
|
-
`);
|
|
2518
|
-
|
|
2519
|
-
// Check that the cells are in the correct columns
|
|
2520
|
-
expect(state.cellIds.getColumns()[0].topLevelIds).toEqual([
|
|
2521
|
-
cellId("0"),
|
|
2522
|
-
cellId("2"),
|
|
2523
|
-
]);
|
|
2524
|
-
expect(state.cellIds.getColumns()[1].topLevelIds).toEqual([cellId("1")]);
|
|
2525
|
-
});
|
|
2526
|
-
|
|
2527
|
-
it("can drop a column over another column", () => {
|
|
2528
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
2529
|
-
actions.createNewCell({ cellId: cellId("1"), before: false });
|
|
2530
|
-
actions.createNewCell({ cellId: cellId("2"), before: false });
|
|
2531
|
-
actions.addColumnBreakpoint({ cellId: cellId("2") });
|
|
2532
|
-
|
|
2533
|
-
expect(state.cellIds.getColumns().length).toBe(2);
|
|
2534
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
2535
|
-
"
|
|
2536
|
-
> col 0
|
|
2537
|
-
[0] ''
|
|
2538
|
-
|
|
2539
|
-
[1] ''
|
|
2540
|
-
|
|
2541
|
-
> col 1
|
|
2542
|
-
[2] ''
|
|
2543
|
-
|
|
2544
|
-
[3] ''
|
|
2545
|
-
"
|
|
2546
|
-
`);
|
|
2547
|
-
|
|
2548
|
-
const columnId0 = state.cellIds.atOrThrow(0).id;
|
|
2549
|
-
const columnId1 = state.cellIds.atOrThrow(1).id;
|
|
2550
|
-
expect(columnId0).not.toBe(columnId1);
|
|
2551
|
-
actions.moveColumn({ column: columnId1, overColumn: columnId0 });
|
|
2552
|
-
|
|
2553
|
-
expect(state.cellIds.getColumns().length).toBe(2);
|
|
2554
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
2555
|
-
"
|
|
2556
|
-
> col 0
|
|
2557
|
-
[2] ''
|
|
2558
|
-
|
|
2559
|
-
[3] ''
|
|
2560
|
-
|
|
2561
|
-
> col 1
|
|
2562
|
-
[0] ''
|
|
2563
|
-
|
|
2564
|
-
[1] ''
|
|
2565
|
-
"
|
|
2566
|
-
`);
|
|
2567
|
-
|
|
2568
|
-
// Check that the columns have swapped positions
|
|
2569
|
-
expect(state.cellIds.getColumns()[0].topLevelIds).toEqual([
|
|
2570
|
-
cellId("2"),
|
|
2571
|
-
cellId("3"),
|
|
2572
|
-
]);
|
|
2573
|
-
expect(state.cellIds.getColumns()[1].topLevelIds).toEqual([
|
|
2574
|
-
cellId("0"),
|
|
2575
|
-
cellId("1"),
|
|
2576
|
-
]);
|
|
2577
|
-
});
|
|
2578
|
-
|
|
2579
|
-
it("can compact columns", () => {
|
|
2580
|
-
// Create initial state with 3 columns, including an empty one
|
|
2581
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
2582
|
-
actions.createNewCell({ cellId: cellId("1"), before: false });
|
|
2583
|
-
actions.addColumnBreakpoint({ cellId: cellId("1") });
|
|
2584
|
-
actions.addColumnBreakpoint({ cellId: cellId("2") });
|
|
2585
|
-
actions.dropOverNewColumn({ cellId: cellId("2") });
|
|
2586
|
-
|
|
2587
|
-
expect(state.cellIds.getColumns().length).toBe(4);
|
|
2588
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
2589
|
-
"
|
|
2590
|
-
> col 0
|
|
2591
|
-
[0] ''
|
|
2592
|
-
|
|
2593
|
-
> col 1
|
|
2594
|
-
[1] ''
|
|
2595
|
-
|
|
2596
|
-
> col 2
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
> col 3
|
|
2600
|
-
[2] ''
|
|
2601
|
-
"
|
|
2602
|
-
`);
|
|
2603
|
-
|
|
2604
|
-
// Check initial column structure
|
|
2605
|
-
expect(state.cellIds.getColumns()[0].topLevelIds).toEqual([cellId("0")]);
|
|
2606
|
-
expect(state.cellIds.getColumns()[1].topLevelIds).toEqual([cellId("1")]);
|
|
2607
|
-
expect(state.cellIds.getColumns()[2].topLevelIds).toEqual([]);
|
|
2608
|
-
expect(state.cellIds.getColumns()[3].topLevelIds).toEqual([cellId("2")]);
|
|
2609
|
-
|
|
2610
|
-
// Compact columns
|
|
2611
|
-
actions.compactColumns();
|
|
2612
|
-
|
|
2613
|
-
expect(state.cellIds.getColumns().length).toBe(3);
|
|
2614
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
2615
|
-
"
|
|
2616
|
-
> col 0
|
|
2617
|
-
[0] ''
|
|
2618
|
-
|
|
2619
|
-
> col 1
|
|
2620
|
-
[1] ''
|
|
2621
|
-
|
|
2622
|
-
> col 2
|
|
2623
|
-
[2] ''
|
|
2624
|
-
"
|
|
2625
|
-
`);
|
|
2626
|
-
|
|
2627
|
-
// Check compacted column structure
|
|
2628
|
-
expect(state.cellIds.getColumns()[0].topLevelIds).toEqual([cellId("0")]);
|
|
2629
|
-
expect(state.cellIds.getColumns()[1].topLevelIds).toEqual([cellId("1")]);
|
|
2630
|
-
expect(state.cellIds.getColumns()[2].topLevelIds).toEqual([cellId("2")]);
|
|
2631
|
-
});
|
|
2632
|
-
|
|
2633
|
-
it("rebuildCellColumns regroups cells by config.column", () => {
|
|
2634
|
-
// Create four cells in a single column.
|
|
2635
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
2636
|
-
actions.createNewCell({ cellId: cellId("1"), before: false });
|
|
2637
|
-
actions.createNewCell({ cellId: cellId("2"), before: false });
|
|
2638
|
-
expect(state.cellIds.getColumns().length).toBe(1);
|
|
2639
|
-
|
|
2640
|
-
// Explicitly set config.column on each cell. This ONLY updates metadata —
|
|
2641
|
-
// updateCellConfig does not touch the MultiColumn tree.
|
|
2642
|
-
actions.updateCellConfig({
|
|
2643
|
-
cellId: cellId("0"),
|
|
2644
|
-
config: { column: 0 },
|
|
2645
|
-
});
|
|
2646
|
-
actions.updateCellConfig({
|
|
2647
|
-
cellId: cellId("2"),
|
|
2648
|
-
config: { column: 1 },
|
|
2649
|
-
});
|
|
2650
|
-
|
|
2651
|
-
// Tree is still a single column at this point.
|
|
2652
|
-
expect(state.cellIds.getColumns().length).toBe(1);
|
|
2653
|
-
|
|
2654
|
-
// Now rebuild the column tree from metadata.
|
|
2655
|
-
actions.rebuildCellColumns({
|
|
2656
|
-
cellIds: [cellId("0"), cellId("1"), cellId("2"), cellId("3")],
|
|
2657
|
-
});
|
|
2658
|
-
|
|
2659
|
-
expect(state.cellIds.getColumns().length).toBe(2);
|
|
2660
|
-
// Cell 1 inherits from 0 (col 0), cell 3 inherits from 2 (col 1).
|
|
2661
|
-
expect(state.cellIds.getColumns()[0].topLevelIds).toEqual([
|
|
2662
|
-
cellId("0"),
|
|
2663
|
-
cellId("1"),
|
|
2664
|
-
]);
|
|
2665
|
-
expect(state.cellIds.getColumns()[1].topLevelIds).toEqual([
|
|
2666
|
-
cellId("2"),
|
|
2667
|
-
cellId("3"),
|
|
2668
|
-
]);
|
|
2669
|
-
});
|
|
2670
|
-
|
|
2671
|
-
it("rebuildCellColumns with explicit column on every cell", () => {
|
|
2672
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
2673
|
-
actions.createNewCell({ cellId: cellId("1"), before: false });
|
|
2674
|
-
actions.createNewCell({ cellId: cellId("2"), before: false });
|
|
2675
|
-
|
|
2676
|
-
for (const [id, col] of [
|
|
2677
|
-
["0", 1],
|
|
2678
|
-
["1", 0],
|
|
2679
|
-
["2", 1],
|
|
2680
|
-
["3", 0],
|
|
2681
|
-
] as const) {
|
|
2682
|
-
actions.updateCellConfig({ cellId: cellId(id), config: { column: col } });
|
|
2683
|
-
}
|
|
2684
|
-
|
|
2685
|
-
actions.rebuildCellColumns({
|
|
2686
|
-
cellIds: [cellId("0"), cellId("1"), cellId("2"), cellId("3")],
|
|
2687
|
-
});
|
|
2688
|
-
|
|
2689
|
-
expect(state.cellIds.getColumns().length).toBe(2);
|
|
2690
|
-
expect(state.cellIds.getColumns()[0].topLevelIds).toEqual([
|
|
2691
|
-
cellId("1"),
|
|
2692
|
-
cellId("3"),
|
|
2693
|
-
]);
|
|
2694
|
-
expect(state.cellIds.getColumns()[1].topLevelIds).toEqual([
|
|
2695
|
-
cellId("0"),
|
|
2696
|
-
cellId("2"),
|
|
2697
|
-
]);
|
|
2698
|
-
});
|
|
2699
|
-
|
|
2700
|
-
it("rebuildCellColumns collapses to one column when all cells are col 0", () => {
|
|
2701
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
2702
|
-
actions.addColumnBreakpoint({ cellId: cellId("1") });
|
|
2703
|
-
expect(state.cellIds.getColumns().length).toBe(2);
|
|
2704
|
-
|
|
2705
|
-
// Wipe column metadata back to 0 for both cells.
|
|
2706
|
-
actions.updateCellConfig({ cellId: cellId("0"), config: { column: 0 } });
|
|
2707
|
-
actions.updateCellConfig({ cellId: cellId("1"), config: { column: 0 } });
|
|
2708
|
-
|
|
2709
|
-
actions.rebuildCellColumns({
|
|
2710
|
-
cellIds: [cellId("0"), cellId("1")],
|
|
2711
|
-
});
|
|
2712
|
-
|
|
2713
|
-
expect(state.cellIds.getColumns().length).toBe(1);
|
|
2714
|
-
expect(state.cellIds.getColumns()[0].topLevelIds).toEqual([
|
|
2715
|
-
cellId("0"),
|
|
2716
|
-
cellId("1"),
|
|
2717
|
-
]);
|
|
2718
|
-
});
|
|
2719
|
-
|
|
2720
|
-
it("rebuildCellColumns follows the provided order, not current tree order", () => {
|
|
2721
|
-
// Start with a single column.
|
|
2722
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
2723
|
-
actions.createNewCell({ cellId: cellId("1"), before: false });
|
|
2724
|
-
actions.updateCellConfig({ cellId: cellId("0"), config: { column: 0 } });
|
|
2725
|
-
actions.updateCellConfig({ cellId: cellId("2"), config: { column: 1 } });
|
|
2726
|
-
|
|
2727
|
-
// Pass the reversed order. The rebuild should honor it.
|
|
2728
|
-
actions.rebuildCellColumns({
|
|
2729
|
-
cellIds: [cellId("2"), cellId("1"), cellId("0")],
|
|
2730
|
-
});
|
|
2731
|
-
|
|
2732
|
-
// cellIds iteration:
|
|
2733
|
-
// 2 → col=1, pushed to col1, prev=1
|
|
2734
|
-
// 1 → col=null, pushed to col[prev=1] = col1
|
|
2735
|
-
// 0 → col=0, pushed to col0
|
|
2736
|
-
expect(state.cellIds.getColumns()[0].topLevelIds).toEqual([cellId("0")]);
|
|
2737
|
-
expect(state.cellIds.getColumns()[1].topLevelIds).toEqual([
|
|
2738
|
-
cellId("2"),
|
|
2739
|
-
cellId("1"),
|
|
2740
|
-
]);
|
|
2741
|
-
});
|
|
2742
|
-
|
|
2743
1041
|
it("can clear output of a single cell", () => {
|
|
2744
1042
|
// Set up initial state with output
|
|
2745
1043
|
actions.handleCellMessage({
|
|
@@ -2828,8 +1126,8 @@ describe("cell reducer", () => {
|
|
|
2828
1126
|
|
|
2829
1127
|
it("can clear output of all cells", () => {
|
|
2830
1128
|
// Create multiple cells with output
|
|
2831
|
-
|
|
2832
|
-
const secondCellId = state.cellIds
|
|
1129
|
+
seedCell({ cellId: firstCellId, before: false });
|
|
1130
|
+
const secondCellId = state.cellIds[1];
|
|
2833
1131
|
|
|
2834
1132
|
// Add output to both cells
|
|
2835
1133
|
const outputMessage = {
|
|
@@ -2861,135 +1159,23 @@ describe("cell reducer", () => {
|
|
|
2861
1159
|
|
|
2862
1160
|
// Verify initial state has output
|
|
2863
1161
|
expect(state.cellRuntime[firstCellId].output).not.toBeNull();
|
|
2864
|
-
expect(state.cellRuntime[firstCellId].consoleOutputs.length).toBe(1);
|
|
2865
|
-
expect(state.cellRuntime[secondCellId].output).not.toBeNull();
|
|
2866
|
-
expect(state.cellRuntime[secondCellId].consoleOutputs.length).toBe(1);
|
|
2867
|
-
|
|
2868
|
-
// Clear all outputs
|
|
2869
|
-
actions.clearAllCellOutputs();
|
|
2870
|
-
|
|
2871
|
-
// Verify all outputs are cleared
|
|
2872
|
-
expect(state.cellRuntime[firstCellId].output).toBeNull();
|
|
2873
|
-
expect(state.cellRuntime[firstCellId].consoleOutputs).toEqual([]);
|
|
2874
|
-
expect(state.cellRuntime[secondCellId].output).toBeNull();
|
|
2875
|
-
expect(state.cellRuntime[secondCellId].consoleOutputs).toEqual([]);
|
|
2876
|
-
});
|
|
2877
|
-
|
|
2878
|
-
it("skips creating new cell if code exists and skipIfCodeExists is true", () => {
|
|
2879
|
-
// Add initial cell with code
|
|
2880
|
-
actions.updateCellCode({
|
|
2881
|
-
cellId: firstCellId,
|
|
2882
|
-
code: "import numpy as np",
|
|
2883
|
-
formattingChange: false,
|
|
2884
|
-
});
|
|
2885
|
-
|
|
2886
|
-
// Try to create new cell with same code and skipIfCodeExists
|
|
2887
|
-
actions.createNewCell({
|
|
2888
|
-
cellId: "__end__",
|
|
2889
|
-
code: "import numpy as np",
|
|
2890
|
-
before: false,
|
|
2891
|
-
skipIfCodeExists: true,
|
|
2892
|
-
});
|
|
2893
|
-
|
|
2894
|
-
// Should still only have one cell
|
|
2895
|
-
expect(state.cellIds.inOrderIds.length).toBe(1);
|
|
2896
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
2897
|
-
"
|
|
2898
|
-
[0] 'import numpy as np'
|
|
2899
|
-
"
|
|
2900
|
-
`);
|
|
2901
|
-
|
|
2902
|
-
// Verify we can still add cell with different code
|
|
2903
|
-
actions.createNewCell({
|
|
2904
|
-
cellId: "__end__",
|
|
2905
|
-
code: "import pandas as pd",
|
|
2906
|
-
before: false,
|
|
2907
|
-
skipIfCodeExists: true,
|
|
2908
|
-
});
|
|
2909
|
-
|
|
2910
|
-
expect(state.cellIds.inOrderIds.length).toBe(2);
|
|
2911
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
2912
|
-
"
|
|
2913
|
-
[0] 'import numpy as np'
|
|
2914
|
-
|
|
2915
|
-
[1] 'import pandas as pd'
|
|
2916
|
-
"
|
|
2917
|
-
`);
|
|
2918
|
-
});
|
|
2919
|
-
|
|
2920
|
-
it("can create and noop-update a setup cell", () => {
|
|
2921
|
-
// Create the setup cell
|
|
2922
|
-
actions.addSetupCellIfDoesntExist({ code: "# Setup code" });
|
|
2923
|
-
|
|
2924
|
-
// Check that setup cell was created
|
|
2925
|
-
expect(state.cellData[SETUP_CELL_ID].id).toBe(SETUP_CELL_ID);
|
|
2926
|
-
expect(state.cellData[SETUP_CELL_ID].name).toBe("setup");
|
|
2927
|
-
expect(state.cellData[SETUP_CELL_ID].code).toBe("# Setup code");
|
|
2928
|
-
expect(state.cellData[SETUP_CELL_ID].edited).toBe(true);
|
|
2929
|
-
expect(state.cellIds.inOrderIds).toContain(SETUP_CELL_ID);
|
|
2930
|
-
|
|
2931
|
-
// Update the setup cell
|
|
2932
|
-
actions.addSetupCellIfDoesntExist({ code: "# Updated setup code" });
|
|
2933
|
-
|
|
2934
|
-
// Check that the setup cell did not change, since it already exists
|
|
2935
|
-
expect(state.cellData[SETUP_CELL_ID].code).toBe("# Setup code");
|
|
2936
|
-
expect(state.cellData[SETUP_CELL_ID].edited).toBe(true);
|
|
2937
|
-
expect(state.cellIds.inOrderIds).toContain(SETUP_CELL_ID);
|
|
2938
|
-
});
|
|
2939
|
-
|
|
2940
|
-
it("can delete and undelete the setup cell", () => {
|
|
2941
|
-
// Create the setup cell
|
|
2942
|
-
actions.addSetupCellIfDoesntExist({ code: "# Setup code" });
|
|
2943
|
-
|
|
2944
|
-
// Check that setup cell was created
|
|
2945
|
-
expect(state.cellData[SETUP_CELL_ID].id).toBe(SETUP_CELL_ID);
|
|
2946
|
-
expect(state.cellData[SETUP_CELL_ID].name).toBe("setup");
|
|
2947
|
-
expect(state.cellData[SETUP_CELL_ID].code).toBe("# Setup code");
|
|
2948
|
-
expect(state.cellData[SETUP_CELL_ID].edited).toBe(true);
|
|
2949
|
-
expect(state.cellIds.inOrderIds).toContain(SETUP_CELL_ID);
|
|
2950
|
-
|
|
2951
|
-
// Delete the setup cell
|
|
2952
|
-
actions.deleteCell({ cellId: SETUP_CELL_ID });
|
|
2953
|
-
|
|
2954
|
-
// Check that setup cell was deleted
|
|
2955
|
-
expect(state.cellData[SETUP_CELL_ID]).toBeDefined(); // we keep old state
|
|
2956
|
-
expect(state.cellIds.inOrderIds).not.toContain(SETUP_CELL_ID);
|
|
2957
|
-
|
|
2958
|
-
// Undo delete the setup cell
|
|
2959
|
-
actions.undoDeleteCell();
|
|
2960
|
-
|
|
2961
|
-
// Check that setup cell was restored
|
|
2962
|
-
expect(state.cellData[SETUP_CELL_ID].id).toBe(SETUP_CELL_ID);
|
|
2963
|
-
expect(state.cellData[SETUP_CELL_ID].name).toBe("setup");
|
|
2964
|
-
expect(state.cellData[SETUP_CELL_ID].code).toBe("# Setup code");
|
|
2965
|
-
expect(state.cellData[SETUP_CELL_ID].edited).toBe(true);
|
|
2966
|
-
expect(state.cellIds.inOrderIds).toContain(SETUP_CELL_ID);
|
|
2967
|
-
|
|
2968
|
-
// Verify scrollKey is set to the restored setup cell
|
|
2969
|
-
expect(state.scrollKey).toBe(SETUP_CELL_ID);
|
|
2970
|
-
});
|
|
2971
|
-
|
|
2972
|
-
it("can delete and then create a new setup cell", () => {
|
|
2973
|
-
// Create the setup cell
|
|
2974
|
-
actions.addSetupCellIfDoesntExist({ code: "# Setup code" });
|
|
2975
|
-
|
|
2976
|
-
// Delete the setup cell
|
|
2977
|
-
actions.deleteCell({ cellId: SETUP_CELL_ID });
|
|
1162
|
+
expect(state.cellRuntime[firstCellId].consoleOutputs.length).toBe(1);
|
|
1163
|
+
expect(state.cellRuntime[secondCellId].output).not.toBeNull();
|
|
1164
|
+
expect(state.cellRuntime[secondCellId].consoleOutputs.length).toBe(1);
|
|
2978
1165
|
|
|
2979
|
-
//
|
|
2980
|
-
actions.
|
|
1166
|
+
// Clear all outputs
|
|
1167
|
+
actions.clearAllCellOutputs();
|
|
2981
1168
|
|
|
2982
|
-
//
|
|
2983
|
-
expect(state.
|
|
2984
|
-
expect(state.
|
|
2985
|
-
expect(state.
|
|
2986
|
-
expect(state.
|
|
2987
|
-
expect(state.cellIds.inOrderIds).toContain(SETUP_CELL_ID);
|
|
1169
|
+
// Verify all outputs are cleared
|
|
1170
|
+
expect(state.cellRuntime[firstCellId].output).toBeNull();
|
|
1171
|
+
expect(state.cellRuntime[firstCellId].consoleOutputs).toEqual([]);
|
|
1172
|
+
expect(state.cellRuntime[secondCellId].output).toBeNull();
|
|
1173
|
+
expect(state.cellRuntime[secondCellId].consoleOutputs).toEqual([]);
|
|
2988
1174
|
});
|
|
2989
1175
|
|
|
2990
1176
|
it("can clear all outputs", () => {
|
|
2991
1177
|
// Add a cell and give it output
|
|
2992
|
-
|
|
1178
|
+
seedCell({
|
|
2993
1179
|
cellId: firstCellId,
|
|
2994
1180
|
before: false,
|
|
2995
1181
|
});
|
|
@@ -3017,522 +1203,13 @@ describe("cell reducer", () => {
|
|
|
3017
1203
|
expect(state.cellRuntime[cell2Id].output).toBeNull();
|
|
3018
1204
|
});
|
|
3019
1205
|
|
|
3020
|
-
describe("moveToNextCell", () => {
|
|
3021
|
-
let cell1Id: CellId;
|
|
3022
|
-
let cell2Id: CellId;
|
|
3023
|
-
let cell3Id: CellId;
|
|
3024
|
-
|
|
3025
|
-
beforeEach(() => {
|
|
3026
|
-
// Create a few cells to work with
|
|
3027
|
-
actions.createNewCell({ cellId: "__end__", before: false });
|
|
3028
|
-
actions.createNewCell({ cellId: "__end__", before: false });
|
|
3029
|
-
|
|
3030
|
-
cell1Id = state.cellIds.inOrderIds[0];
|
|
3031
|
-
cell2Id = state.cellIds.inOrderIds[1];
|
|
3032
|
-
cell3Id = state.cellIds.inOrderIds[2];
|
|
3033
|
-
});
|
|
3034
|
-
|
|
3035
|
-
it("creates new cell when moving after last cell with noCreate=false", () => {
|
|
3036
|
-
const initialCellCount = state.cellIds.inOrderIds.length;
|
|
3037
|
-
|
|
3038
|
-
actions.moveToNextCell({
|
|
3039
|
-
cellId: cell3Id,
|
|
3040
|
-
before: false,
|
|
3041
|
-
noCreate: false,
|
|
3042
|
-
});
|
|
3043
|
-
|
|
3044
|
-
expect(state.cellIds.inOrderIds.length).toBe(initialCellCount + 1);
|
|
3045
|
-
});
|
|
3046
|
-
|
|
3047
|
-
it("creates new cell when moving before first cell with noCreate=false", () => {
|
|
3048
|
-
const initialCellCount = state.cellIds.inOrderIds.length;
|
|
3049
|
-
|
|
3050
|
-
actions.moveToNextCell({
|
|
3051
|
-
cellId: cell1Id,
|
|
3052
|
-
before: true,
|
|
3053
|
-
noCreate: false,
|
|
3054
|
-
});
|
|
3055
|
-
|
|
3056
|
-
expect(state.cellIds.inOrderIds.length).toBe(initialCellCount + 1);
|
|
3057
|
-
});
|
|
3058
|
-
|
|
3059
|
-
it("does not create new cell when moving after last cell with noCreate=true", () => {
|
|
3060
|
-
const initialCellCount = state.cellIds.inOrderIds.length;
|
|
3061
|
-
const initialState = { ...state };
|
|
3062
|
-
|
|
3063
|
-
actions.moveToNextCell({
|
|
3064
|
-
cellId: cell3Id,
|
|
3065
|
-
before: false,
|
|
3066
|
-
noCreate: true,
|
|
3067
|
-
});
|
|
3068
|
-
|
|
3069
|
-
// Should not create a new cell
|
|
3070
|
-
expect(state.cellIds.inOrderIds.length).toBe(initialCellCount);
|
|
3071
|
-
// Should not crash or throw an error
|
|
3072
|
-
expect(state.cellIds.inOrderIds).toEqual(initialState.cellIds.inOrderIds);
|
|
3073
|
-
});
|
|
3074
|
-
|
|
3075
|
-
it("does not create new cell when moving before first cell with noCreate=true", () => {
|
|
3076
|
-
const initialCellCount = state.cellIds.inOrderIds.length;
|
|
3077
|
-
const initialState = { ...state };
|
|
3078
|
-
|
|
3079
|
-
actions.moveToNextCell({ cellId: cell1Id, before: true, noCreate: true });
|
|
3080
|
-
|
|
3081
|
-
// Should not create a new cell
|
|
3082
|
-
expect(state.cellIds.inOrderIds.length).toBe(initialCellCount);
|
|
3083
|
-
// Should not crash or throw an error
|
|
3084
|
-
expect(state.cellIds.inOrderIds).toEqual(initialState.cellIds.inOrderIds);
|
|
3085
|
-
});
|
|
3086
|
-
|
|
3087
|
-
it("focuses next cell when moving within bounds", () => {
|
|
3088
|
-
const focusSpy = vi.mocked(focusAndScrollCellIntoView);
|
|
3089
|
-
focusSpy.mockClear();
|
|
3090
|
-
|
|
3091
|
-
actions.moveToNextCell({
|
|
3092
|
-
cellId: cell1Id,
|
|
3093
|
-
before: false,
|
|
3094
|
-
noCreate: true,
|
|
3095
|
-
});
|
|
3096
|
-
|
|
3097
|
-
expect(focusSpy).toHaveBeenCalledWith({
|
|
3098
|
-
cellId: cell2Id,
|
|
3099
|
-
cell: state.cellHandles[cell2Id],
|
|
3100
|
-
isCodeHidden: false,
|
|
3101
|
-
codeFocus: "top",
|
|
3102
|
-
variableName: undefined,
|
|
3103
|
-
});
|
|
3104
|
-
});
|
|
3105
|
-
|
|
3106
|
-
it("focuses previous cell when moving backward within bounds", () => {
|
|
3107
|
-
const focusSpy = vi.mocked(focusAndScrollCellIntoView);
|
|
3108
|
-
focusSpy.mockClear();
|
|
3109
|
-
|
|
3110
|
-
actions.moveToNextCell({ cellId: cell2Id, before: true, noCreate: true });
|
|
3111
|
-
|
|
3112
|
-
expect(focusSpy).toHaveBeenCalledWith({
|
|
3113
|
-
cellId: cell1Id,
|
|
3114
|
-
cell: state.cellHandles[cell1Id],
|
|
3115
|
-
isCodeHidden: false,
|
|
3116
|
-
codeFocus: "bottom",
|
|
3117
|
-
variableName: undefined,
|
|
3118
|
-
});
|
|
3119
|
-
});
|
|
3120
|
-
|
|
3121
|
-
it("does not move focus from scratch cell", () => {
|
|
3122
|
-
const focusSpy = vi.mocked(focusAndScrollCellIntoView);
|
|
3123
|
-
focusSpy.mockClear();
|
|
3124
|
-
const initialState = { ...state };
|
|
3125
|
-
|
|
3126
|
-
actions.moveToNextCell({
|
|
3127
|
-
cellId: SCRATCH_CELL_ID,
|
|
3128
|
-
before: false,
|
|
3129
|
-
noCreate: false,
|
|
3130
|
-
});
|
|
3131
|
-
|
|
3132
|
-
expect(focusSpy).not.toHaveBeenCalled();
|
|
3133
|
-
expect(state.cellIds.inOrderIds).toEqual(initialState.cellIds.inOrderIds);
|
|
3134
|
-
});
|
|
3135
|
-
});
|
|
3136
|
-
|
|
3137
|
-
describe("untouched cells functionality", () => {
|
|
3138
|
-
it("starts with empty untouchedNewCells set", () => {
|
|
3139
|
-
expect(state.untouchedNewCells).toEqual(new Set());
|
|
3140
|
-
});
|
|
3141
|
-
|
|
3142
|
-
it("can create a new cell with hideCode option", () => {
|
|
3143
|
-
const initialCellCount = state.cellIds.inOrderIds.length;
|
|
3144
|
-
|
|
3145
|
-
actions.createNewCell({
|
|
3146
|
-
cellId: "__end__",
|
|
3147
|
-
before: false,
|
|
3148
|
-
hideCode: true,
|
|
3149
|
-
});
|
|
3150
|
-
|
|
3151
|
-
expect(state.cellIds.inOrderIds.length).toBe(initialCellCount + 1);
|
|
3152
|
-
const newCellId =
|
|
3153
|
-
state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
|
|
3154
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(true);
|
|
3155
|
-
});
|
|
3156
|
-
|
|
3157
|
-
it("adds interactively-created hidden cell with boilerplate code to untouchedNewCells", () => {
|
|
3158
|
-
// Markdown cells are created with hideCode and non-empty default code
|
|
3159
|
-
// (e.g. `mo.md(r"""\n""")`). They are user-initiated, so their editor
|
|
3160
|
-
// should be shown until first blur.
|
|
3161
|
-
actions.createNewCell({
|
|
3162
|
-
cellId: "__end__",
|
|
3163
|
-
before: false,
|
|
3164
|
-
code: 'mo.md(r"""\n""")',
|
|
3165
|
-
hideCode: true,
|
|
3166
|
-
autoFocus: true,
|
|
3167
|
-
});
|
|
3168
|
-
|
|
3169
|
-
const newCellId =
|
|
3170
|
-
state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
|
|
3171
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(true);
|
|
3172
|
-
});
|
|
3173
|
-
|
|
3174
|
-
it("does not add programmatically-created hidden cell with code to untouchedNewCells", () => {
|
|
3175
|
-
// Cells created by the kernel (e.g. via code_mode) carry code and
|
|
3176
|
-
// autoFocus=false; their hide_code must take effect immediately.
|
|
3177
|
-
actions.createNewCell({
|
|
3178
|
-
cellId: "__end__",
|
|
3179
|
-
before: false,
|
|
3180
|
-
code: "x = 1",
|
|
3181
|
-
hideCode: true,
|
|
3182
|
-
autoFocus: false,
|
|
3183
|
-
});
|
|
3184
|
-
|
|
3185
|
-
const newCellId =
|
|
3186
|
-
state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
|
|
3187
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(false);
|
|
3188
|
-
});
|
|
3189
|
-
|
|
3190
|
-
it("does not add cell to untouchedNewCells when hideCode is false", () => {
|
|
3191
|
-
actions.createNewCell({
|
|
3192
|
-
cellId: "__end__",
|
|
3193
|
-
before: false,
|
|
3194
|
-
hideCode: false,
|
|
3195
|
-
});
|
|
3196
|
-
|
|
3197
|
-
const newCellId =
|
|
3198
|
-
state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
|
|
3199
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(false);
|
|
3200
|
-
});
|
|
3201
|
-
|
|
3202
|
-
it("does not add cell to untouchedNewCells when hideCode is undefined", () => {
|
|
3203
|
-
actions.createNewCell({
|
|
3204
|
-
cellId: "__end__",
|
|
3205
|
-
before: false,
|
|
3206
|
-
});
|
|
3207
|
-
|
|
3208
|
-
const newCellId =
|
|
3209
|
-
state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
|
|
3210
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(false);
|
|
3211
|
-
});
|
|
3212
|
-
|
|
3213
|
-
it("can mark a cell as touched", () => {
|
|
3214
|
-
// Create a cell with hideCode
|
|
3215
|
-
actions.createNewCell({
|
|
3216
|
-
cellId: "__end__",
|
|
3217
|
-
before: false,
|
|
3218
|
-
hideCode: true,
|
|
3219
|
-
});
|
|
3220
|
-
|
|
3221
|
-
const newCellId =
|
|
3222
|
-
state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
|
|
3223
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(true);
|
|
3224
|
-
|
|
3225
|
-
// Mark it as touched
|
|
3226
|
-
actions.markTouched({ cellId: newCellId });
|
|
3227
|
-
|
|
3228
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(false);
|
|
3229
|
-
});
|
|
3230
|
-
|
|
3231
|
-
it("markTouched is idempotent", () => {
|
|
3232
|
-
// Create a cell with hideCode
|
|
3233
|
-
actions.createNewCell({
|
|
3234
|
-
cellId: "__end__",
|
|
3235
|
-
before: false,
|
|
3236
|
-
hideCode: true,
|
|
3237
|
-
});
|
|
3238
|
-
|
|
3239
|
-
const newCellId =
|
|
3240
|
-
state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
|
|
3241
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(true);
|
|
3242
|
-
|
|
3243
|
-
// Mark it as touched multiple times
|
|
3244
|
-
actions.markTouched({ cellId: newCellId });
|
|
3245
|
-
actions.markTouched({ cellId: newCellId });
|
|
3246
|
-
actions.markTouched({ cellId: newCellId });
|
|
3247
|
-
|
|
3248
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(false);
|
|
3249
|
-
});
|
|
3250
|
-
|
|
3251
|
-
it("can mark a non-existent cell as touched without error", () => {
|
|
3252
|
-
const nonExistentCellId = cellId("non-existent");
|
|
3253
|
-
|
|
3254
|
-
expect(() => {
|
|
3255
|
-
actions.markTouched({ cellId: nonExistentCellId });
|
|
3256
|
-
}).not.toThrow();
|
|
3257
|
-
|
|
3258
|
-
expect(state.untouchedNewCells.has(nonExistentCellId)).toBe(false);
|
|
3259
|
-
});
|
|
3260
|
-
|
|
3261
|
-
it("can handle multiple untouched cells", () => {
|
|
3262
|
-
// Create multiple cells with hideCode
|
|
3263
|
-
actions.createNewCell({
|
|
3264
|
-
cellId: "__end__",
|
|
3265
|
-
before: false,
|
|
3266
|
-
hideCode: true,
|
|
3267
|
-
});
|
|
3268
|
-
actions.createNewCell({
|
|
3269
|
-
cellId: "__end__",
|
|
3270
|
-
before: false,
|
|
3271
|
-
hideCode: true,
|
|
3272
|
-
});
|
|
3273
|
-
actions.createNewCell({
|
|
3274
|
-
cellId: "__end__",
|
|
3275
|
-
before: false,
|
|
3276
|
-
hideCode: false,
|
|
3277
|
-
});
|
|
3278
|
-
|
|
3279
|
-
const cellIds = state.cellIds.inOrderIds;
|
|
3280
|
-
const cell1Id = cellIds[cellIds.length - 3];
|
|
3281
|
-
const cell2Id = cellIds[cellIds.length - 2];
|
|
3282
|
-
const cell3Id = cellIds[cellIds.length - 1];
|
|
3283
|
-
|
|
3284
|
-
expect(state.untouchedNewCells.has(cell1Id)).toBe(true);
|
|
3285
|
-
expect(state.untouchedNewCells.has(cell2Id)).toBe(true);
|
|
3286
|
-
expect(state.untouchedNewCells.has(cell3Id)).toBe(false);
|
|
3287
|
-
|
|
3288
|
-
// Mark one as touched
|
|
3289
|
-
actions.markTouched({ cellId: cell1Id });
|
|
3290
|
-
|
|
3291
|
-
expect(state.untouchedNewCells.has(cell1Id)).toBe(false);
|
|
3292
|
-
expect(state.untouchedNewCells.has(cell2Id)).toBe(true);
|
|
3293
|
-
expect(state.untouchedNewCells.has(cell3Id)).toBe(false);
|
|
3294
|
-
});
|
|
3295
|
-
|
|
3296
|
-
it("preserves untouched state when cells are moved", () => {
|
|
3297
|
-
// Create cells with hideCode
|
|
3298
|
-
actions.createNewCell({
|
|
3299
|
-
cellId: "__end__",
|
|
3300
|
-
before: false,
|
|
3301
|
-
hideCode: true,
|
|
3302
|
-
});
|
|
3303
|
-
actions.createNewCell({
|
|
3304
|
-
cellId: "__end__",
|
|
3305
|
-
before: false,
|
|
3306
|
-
hideCode: true,
|
|
3307
|
-
});
|
|
3308
|
-
|
|
3309
|
-
const cellIds = state.cellIds.inOrderIds;
|
|
3310
|
-
const cell1Id = cellIds[cellIds.length - 2];
|
|
3311
|
-
const cell2Id = cellIds[cellIds.length - 1];
|
|
3312
|
-
|
|
3313
|
-
expect(state.untouchedNewCells.has(cell1Id)).toBe(true);
|
|
3314
|
-
expect(state.untouchedNewCells.has(cell2Id)).toBe(true);
|
|
3315
|
-
|
|
3316
|
-
// Move cell1 to the end
|
|
3317
|
-
actions.moveCell({
|
|
3318
|
-
cellId: cell1Id,
|
|
3319
|
-
before: false,
|
|
3320
|
-
});
|
|
3321
|
-
|
|
3322
|
-
// Both cells should still be untouched
|
|
3323
|
-
expect(state.untouchedNewCells.has(cell1Id)).toBe(true);
|
|
3324
|
-
expect(state.untouchedNewCells.has(cell2Id)).toBe(true);
|
|
3325
|
-
});
|
|
3326
|
-
|
|
3327
|
-
it("does not remove untouched state when cell is deleted", () => {
|
|
3328
|
-
// We could implement this, but it doesn't actually affect downstream behavior.
|
|
3329
|
-
// It is easier to leave it, so `undo` works as expected.
|
|
3330
|
-
|
|
3331
|
-
// Create a cell with hideCode
|
|
3332
|
-
actions.createNewCell({
|
|
3333
|
-
cellId: "__end__",
|
|
3334
|
-
before: false,
|
|
3335
|
-
hideCode: true,
|
|
3336
|
-
});
|
|
3337
|
-
|
|
3338
|
-
const newCellId =
|
|
3339
|
-
state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
|
|
3340
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(true);
|
|
3341
|
-
|
|
3342
|
-
// Delete the cell
|
|
3343
|
-
actions.deleteCell({ cellId: newCellId });
|
|
3344
|
-
|
|
3345
|
-
// Does not actually remove untouched state
|
|
3346
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(true);
|
|
3347
|
-
});
|
|
3348
|
-
|
|
3349
|
-
it("restores untouched state when cell deletion is undone", () => {
|
|
3350
|
-
// Create a cell with hideCode
|
|
3351
|
-
actions.createNewCell({
|
|
3352
|
-
cellId: "__end__",
|
|
3353
|
-
before: false,
|
|
3354
|
-
hideCode: true,
|
|
3355
|
-
});
|
|
3356
|
-
|
|
3357
|
-
const newCellId =
|
|
3358
|
-
state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
|
|
3359
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(true);
|
|
3360
|
-
|
|
3361
|
-
// Delete the cell
|
|
3362
|
-
actions.deleteCell({ cellId: newCellId });
|
|
3363
|
-
// Still exists in untouchedNewCells
|
|
3364
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(true);
|
|
3365
|
-
|
|
3366
|
-
// Undo the deletion
|
|
3367
|
-
actions.undoDeleteCell();
|
|
3368
|
-
|
|
3369
|
-
// The cell should be restored but no longer untouched
|
|
3370
|
-
// (this is expected behavior - undoing doesn't restore untouched state)
|
|
3371
|
-
const restoredCellId =
|
|
3372
|
-
state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
|
|
3373
|
-
expect(state.untouchedNewCells.has(restoredCellId)).toBe(false);
|
|
3374
|
-
});
|
|
3375
|
-
|
|
3376
|
-
it("untouched cell behavior with hide_code config", () => {
|
|
3377
|
-
// Create a cell with hideCode and hide_code config
|
|
3378
|
-
actions.createNewCell({
|
|
3379
|
-
cellId: "__end__",
|
|
3380
|
-
before: false,
|
|
3381
|
-
hideCode: true,
|
|
3382
|
-
});
|
|
3383
|
-
|
|
3384
|
-
const newCellId =
|
|
3385
|
-
state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
|
|
3386
|
-
|
|
3387
|
-
// Update the cell config to hide code
|
|
3388
|
-
actions.updateCellConfig({
|
|
3389
|
-
cellId: newCellId,
|
|
3390
|
-
config: { hide_code: true },
|
|
3391
|
-
});
|
|
3392
|
-
|
|
3393
|
-
// Cell should be untouched and have hide_code config
|
|
3394
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(true);
|
|
3395
|
-
expect(state.cellData[newCellId].config.hide_code).toBe(true);
|
|
3396
|
-
|
|
3397
|
-
// Code should not be hidden because cell is untouched
|
|
3398
|
-
expect(exportedForTesting.isCellCodeHidden(state, newCellId)).toBe(false);
|
|
3399
|
-
|
|
3400
|
-
// Mark as touched
|
|
3401
|
-
actions.markTouched({ cellId: newCellId });
|
|
3402
|
-
|
|
3403
|
-
// Now code should be hidden
|
|
3404
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(false);
|
|
3405
|
-
expect(exportedForTesting.isCellCodeHidden(state, newCellId)).toBe(true);
|
|
3406
|
-
});
|
|
3407
|
-
|
|
3408
|
-
it("can mark an existing cell as untouched", () => {
|
|
3409
|
-
// Create a cell without hideCode (not in untouchedNewCells)
|
|
3410
|
-
actions.createNewCell({
|
|
3411
|
-
cellId: "__end__",
|
|
3412
|
-
before: false,
|
|
3413
|
-
hideCode: false,
|
|
3414
|
-
});
|
|
3415
|
-
|
|
3416
|
-
const newCellId =
|
|
3417
|
-
state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
|
|
3418
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(false);
|
|
3419
|
-
|
|
3420
|
-
// Mark it as untouched
|
|
3421
|
-
actions.markUntouched({ cellId: newCellId });
|
|
3422
|
-
|
|
3423
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(true);
|
|
3424
|
-
});
|
|
3425
|
-
|
|
3426
|
-
it("markUntouched is idempotent", () => {
|
|
3427
|
-
// Create a cell without hideCode
|
|
3428
|
-
actions.createNewCell({
|
|
3429
|
-
cellId: "__end__",
|
|
3430
|
-
before: false,
|
|
3431
|
-
hideCode: false,
|
|
3432
|
-
});
|
|
3433
|
-
|
|
3434
|
-
const newCellId =
|
|
3435
|
-
state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
|
|
3436
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(false);
|
|
3437
|
-
|
|
3438
|
-
// Mark as untouched multiple times
|
|
3439
|
-
actions.markUntouched({ cellId: newCellId });
|
|
3440
|
-
actions.markUntouched({ cellId: newCellId });
|
|
3441
|
-
actions.markUntouched({ cellId: newCellId });
|
|
3442
|
-
|
|
3443
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(true);
|
|
3444
|
-
});
|
|
3445
|
-
|
|
3446
|
-
it("markUntouched does not affect already untouched cells", () => {
|
|
3447
|
-
// Create a cell with hideCode (already in untouchedNewCells)
|
|
3448
|
-
actions.createNewCell({
|
|
3449
|
-
cellId: "__end__",
|
|
3450
|
-
before: false,
|
|
3451
|
-
hideCode: true,
|
|
3452
|
-
});
|
|
3453
|
-
|
|
3454
|
-
const newCellId =
|
|
3455
|
-
state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
|
|
3456
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(true);
|
|
3457
|
-
|
|
3458
|
-
// Calling markUntouched should not change anything
|
|
3459
|
-
actions.markUntouched({ cellId: newCellId });
|
|
3460
|
-
|
|
3461
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(true);
|
|
3462
|
-
});
|
|
3463
|
-
|
|
3464
|
-
it("markTouched and markUntouched can toggle cell state", () => {
|
|
3465
|
-
// Create a cell without hideCode
|
|
3466
|
-
actions.createNewCell({
|
|
3467
|
-
cellId: "__end__",
|
|
3468
|
-
before: false,
|
|
3469
|
-
hideCode: false,
|
|
3470
|
-
});
|
|
3471
|
-
|
|
3472
|
-
const newCellId =
|
|
3473
|
-
state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
|
|
3474
|
-
|
|
3475
|
-
// Initially not untouched
|
|
3476
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(false);
|
|
3477
|
-
|
|
3478
|
-
// Mark as untouched
|
|
3479
|
-
actions.markUntouched({ cellId: newCellId });
|
|
3480
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(true);
|
|
3481
|
-
|
|
3482
|
-
// Mark as touched
|
|
3483
|
-
actions.markTouched({ cellId: newCellId });
|
|
3484
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(false);
|
|
3485
|
-
|
|
3486
|
-
// Mark as untouched again
|
|
3487
|
-
actions.markUntouched({ cellId: newCellId });
|
|
3488
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(true);
|
|
3489
|
-
});
|
|
3490
|
-
|
|
3491
|
-
it("markUntouched works for markdown cell conversion scenario", () => {
|
|
3492
|
-
// Simulates converting a Python cell to Markdown
|
|
3493
|
-
// 1. Create a regular cell (no hideCode)
|
|
3494
|
-
actions.createNewCell({
|
|
3495
|
-
cellId: "__end__",
|
|
3496
|
-
before: false,
|
|
3497
|
-
hideCode: false,
|
|
3498
|
-
});
|
|
3499
|
-
|
|
3500
|
-
const cellId =
|
|
3501
|
-
state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
|
|
3502
|
-
|
|
3503
|
-
// Cell starts without hide_code and not in untouchedNewCells
|
|
3504
|
-
expect(state.cellData[cellId].config.hide_code).toBe(false);
|
|
3505
|
-
expect(state.untouchedNewCells.has(cellId)).toBe(false);
|
|
3506
|
-
expect(exportedForTesting.isCellCodeHidden(state, cellId)).toBe(false);
|
|
3507
|
-
|
|
3508
|
-
// 2. Convert to markdown: set hide_code and mark as untouched
|
|
3509
|
-
actions.updateCellConfig({
|
|
3510
|
-
cellId,
|
|
3511
|
-
config: { hide_code: true },
|
|
3512
|
-
});
|
|
3513
|
-
actions.markUntouched({ cellId });
|
|
3514
|
-
|
|
3515
|
-
// Code should NOT be hidden because cell is untouched (user can edit)
|
|
3516
|
-
expect(state.cellData[cellId].config.hide_code).toBe(true);
|
|
3517
|
-
expect(state.untouchedNewCells.has(cellId)).toBe(true);
|
|
3518
|
-
expect(exportedForTesting.isCellCodeHidden(state, cellId)).toBe(false);
|
|
3519
|
-
|
|
3520
|
-
// 3. User blurs the cell (markTouched)
|
|
3521
|
-
actions.markTouched({ cellId });
|
|
3522
|
-
|
|
3523
|
-
// Now code should be hidden
|
|
3524
|
-
expect(state.untouchedNewCells.has(cellId)).toBe(false);
|
|
3525
|
-
expect(exportedForTesting.isCellCodeHidden(state, cellId)).toBe(true);
|
|
3526
|
-
});
|
|
3527
|
-
});
|
|
3528
|
-
|
|
3529
1206
|
describe("releaseCellAtoms", () => {
|
|
3530
1207
|
it("atom families cache atoms until explicitly removed", () => {
|
|
3531
1208
|
const { cellDataAtom, cellRuntimeAtom, cellHandleAtom } =
|
|
3532
1209
|
exportedForTesting;
|
|
3533
1210
|
|
|
3534
|
-
|
|
3535
|
-
const newCellId = state.cellIds
|
|
1211
|
+
seedCell({ cellId: firstCellId, before: false });
|
|
1212
|
+
const newCellId = state.cellIds[1];
|
|
3536
1213
|
|
|
3537
1214
|
const dataAtom1 = cellDataAtom(newCellId);
|
|
3538
1215
|
const runtimeAtom1 = cellRuntimeAtom(newCellId);
|
|
@@ -3547,116 +1224,15 @@ describe("cell reducer", () => {
|
|
|
3547
1224
|
expect(runtimeAtom2).toBe(runtimeAtom1);
|
|
3548
1225
|
expect(handleAtom2).toBe(handleAtom1);
|
|
3549
1226
|
});
|
|
3550
|
-
|
|
3551
|
-
it("cleans up atom family cache when cells are deleted", () => {
|
|
3552
|
-
const { cellDataAtom, cellRuntimeAtom, cellHandleAtom } =
|
|
3553
|
-
exportedForTesting;
|
|
3554
|
-
|
|
3555
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
3556
|
-
const newCellId = state.cellIds.inOrderIds[1];
|
|
3557
|
-
|
|
3558
|
-
// Access to create cache entries
|
|
3559
|
-
const dataAtom1 = cellDataAtom(newCellId);
|
|
3560
|
-
const runtimeAtom1 = cellRuntimeAtom(newCellId);
|
|
3561
|
-
const handleAtom1 = cellHandleAtom(newCellId);
|
|
3562
|
-
|
|
3563
|
-
// Triggers purge
|
|
3564
|
-
actions.deleteCell({ cellId: newCellId });
|
|
3565
|
-
|
|
3566
|
-
// Access again (should be new instances after cleanup)
|
|
3567
|
-
const dataAtom2 = cellDataAtom(newCellId);
|
|
3568
|
-
const runtimeAtom2 = cellRuntimeAtom(newCellId);
|
|
3569
|
-
const handleAtom2 = cellHandleAtom(newCellId);
|
|
3570
|
-
|
|
3571
|
-
expect(dataAtom2).not.toBe(dataAtom1);
|
|
3572
|
-
expect(runtimeAtom2).not.toBe(runtimeAtom1);
|
|
3573
|
-
expect(handleAtom2).not.toBe(handleAtom1);
|
|
3574
|
-
});
|
|
3575
|
-
});
|
|
3576
|
-
});
|
|
3577
|
-
|
|
3578
|
-
describe("isCellCodeHidden", () => {
|
|
3579
|
-
const state = initialNotebookState();
|
|
3580
|
-
const firstCellId = state.cellIds.inOrderIds[0];
|
|
3581
|
-
|
|
3582
|
-
it("returns false when hide_code is false and cell is not untouched", () => {
|
|
3583
|
-
const testCellId = cellId("test-cell");
|
|
3584
|
-
const testState: NotebookState = {
|
|
3585
|
-
...state,
|
|
3586
|
-
cellData: {
|
|
3587
|
-
...state.cellData,
|
|
3588
|
-
[testCellId]: {
|
|
3589
|
-
...state.cellData[firstCellId],
|
|
3590
|
-
id: testCellId,
|
|
3591
|
-
config: { hide_code: false, disabled: false, column: null },
|
|
3592
|
-
},
|
|
3593
|
-
},
|
|
3594
|
-
untouchedNewCells: new Set(),
|
|
3595
|
-
};
|
|
3596
|
-
|
|
3597
|
-
expect(exportedForTesting.isCellCodeHidden(testState, testCellId)).toBe(
|
|
3598
|
-
false,
|
|
3599
|
-
);
|
|
3600
|
-
});
|
|
3601
|
-
|
|
3602
|
-
it("returns true when hide_code is true and cell is not untouched", () => {
|
|
3603
|
-
const testCellId = cellId("test-cell");
|
|
3604
|
-
const testState: NotebookState = {
|
|
3605
|
-
...state,
|
|
3606
|
-
cellData: {
|
|
3607
|
-
...state.cellData,
|
|
3608
|
-
[testCellId]: {
|
|
3609
|
-
...state.cellData[firstCellId],
|
|
3610
|
-
id: testCellId,
|
|
3611
|
-
config: { hide_code: true, disabled: false, column: null },
|
|
3612
|
-
},
|
|
3613
|
-
},
|
|
3614
|
-
untouchedNewCells: new Set(),
|
|
3615
|
-
};
|
|
3616
|
-
|
|
3617
|
-
expect(exportedForTesting.isCellCodeHidden(testState, testCellId)).toBe(
|
|
3618
|
-
true,
|
|
3619
|
-
);
|
|
3620
|
-
});
|
|
3621
|
-
|
|
3622
|
-
it("returns false when hide_code is true but cell is untouched", () => {
|
|
3623
|
-
const testCellId = cellId("test-cell");
|
|
3624
|
-
const testState: NotebookState = {
|
|
3625
|
-
...state,
|
|
3626
|
-
cellData: {
|
|
3627
|
-
...state.cellData,
|
|
3628
|
-
[testCellId]: {
|
|
3629
|
-
...state.cellData[firstCellId],
|
|
3630
|
-
id: testCellId,
|
|
3631
|
-
config: { hide_code: true, disabled: false, column: null },
|
|
3632
|
-
},
|
|
3633
|
-
},
|
|
3634
|
-
untouchedNewCells: new Set([testCellId]),
|
|
3635
|
-
};
|
|
3636
|
-
|
|
3637
|
-
expect(exportedForTesting.isCellCodeHidden(testState, testCellId)).toBe(
|
|
3638
|
-
false,
|
|
3639
|
-
);
|
|
3640
1227
|
});
|
|
1228
|
+
it("withdraws the submission of a cell that leaves the document", () => {
|
|
1229
|
+
MockExecutions.seed(store, []);
|
|
1230
|
+
submitCells(store, [firstCellId]);
|
|
1231
|
+
expect(store.get(notebookQueuedOrRunningCountAtom)).toBe(1);
|
|
3641
1232
|
|
|
3642
|
-
|
|
3643
|
-
const testCellId = cellId("test-cell");
|
|
3644
|
-
const testState: NotebookState = {
|
|
3645
|
-
...state,
|
|
3646
|
-
cellData: {
|
|
3647
|
-
...state.cellData,
|
|
3648
|
-
[testCellId]: {
|
|
3649
|
-
...state.cellData[firstCellId],
|
|
3650
|
-
id: testCellId,
|
|
3651
|
-
config: { hide_code: false, disabled: false, column: null },
|
|
3652
|
-
},
|
|
3653
|
-
},
|
|
3654
|
-
untouchedNewCells: new Set([testCellId]),
|
|
3655
|
-
};
|
|
1233
|
+
actions.reconcileDocument({ order: [], cells: new Map() });
|
|
3656
1234
|
|
|
3657
|
-
expect(
|
|
3658
|
-
false,
|
|
3659
|
-
);
|
|
1235
|
+
expect(store.get(notebookQueuedOrRunningCountAtom)).toBe(0);
|
|
3660
1236
|
});
|
|
3661
1237
|
});
|
|
3662
1238
|
|
|
@@ -3839,11 +1415,11 @@ describe("createTracebackInfoAtom", () => {
|
|
|
3839
1415
|
data: [{ type: "syntax", msg: "Syntax error", lineno: 1 }],
|
|
3840
1416
|
mimetype: "application/vnd.marimo+error",
|
|
3841
1417
|
},
|
|
3842
|
-
status: "queued",
|
|
3843
1418
|
},
|
|
3844
1419
|
},
|
|
3845
1420
|
}),
|
|
3846
1421
|
);
|
|
1422
|
+
MockExecutions.seed(store, [MockExecutions.queued(cellId("cell1"))]);
|
|
3847
1423
|
|
|
3848
1424
|
const tracebackAtom = exportedForTesting.createTracebackInfoAtom(
|
|
3849
1425
|
cellId("cell1"),
|
|
@@ -3871,11 +1447,11 @@ describe("createTracebackInfoAtom", () => {
|
|
|
3871
1447
|
data: [{ type: "syntax", msg: "Syntax error", lineno: 1 }],
|
|
3872
1448
|
mimetype: "application/vnd.marimo+error",
|
|
3873
1449
|
},
|
|
3874
|
-
status: "running",
|
|
3875
1450
|
},
|
|
3876
1451
|
},
|
|
3877
1452
|
}),
|
|
3878
1453
|
);
|
|
1454
|
+
MockExecutions.seed(store, [MockExecutions.running(cellId("cell1"))]);
|
|
3879
1455
|
|
|
3880
1456
|
const tracebackAtom = exportedForTesting.createTracebackInfoAtom(
|
|
3881
1457
|
cellId("cell1"),
|
|
@@ -3886,31 +1462,9 @@ describe("createTracebackInfoAtom", () => {
|
|
|
3886
1462
|
});
|
|
3887
1463
|
});
|
|
3888
1464
|
|
|
3889
|
-
describe("
|
|
1465
|
+
describe("kernel-ready", () => {
|
|
3890
1466
|
const CELL_A = cellId("A");
|
|
3891
1467
|
const CELL_B = cellId("B");
|
|
3892
|
-
const newCells: CellData[] = [
|
|
3893
|
-
{
|
|
3894
|
-
id: CELL_A,
|
|
3895
|
-
name: "a",
|
|
3896
|
-
code: "1",
|
|
3897
|
-
edited: false,
|
|
3898
|
-
lastCodeRun: null,
|
|
3899
|
-
lastExecutionTime: null,
|
|
3900
|
-
config: { hide_code: false, disabled: false, column: null },
|
|
3901
|
-
serializedEditorState: null,
|
|
3902
|
-
},
|
|
3903
|
-
{
|
|
3904
|
-
id: CELL_B,
|
|
3905
|
-
name: "b",
|
|
3906
|
-
code: "2",
|
|
3907
|
-
edited: false,
|
|
3908
|
-
lastCodeRun: null,
|
|
3909
|
-
lastExecutionTime: null,
|
|
3910
|
-
config: { hide_code: false, disabled: false, column: null },
|
|
3911
|
-
serializedEditorState: null,
|
|
3912
|
-
},
|
|
3913
|
-
];
|
|
3914
1468
|
|
|
3915
1469
|
const hydratedState = () =>
|
|
3916
1470
|
MockNotebook.notebookState({
|
|
@@ -3940,44 +1494,92 @@ describe("setCells snapshot preservation", () => {
|
|
|
3940
1494
|
},
|
|
3941
1495
|
});
|
|
3942
1496
|
|
|
3943
|
-
|
|
3944
|
-
const
|
|
3945
|
-
|
|
1497
|
+
it("setLastRun records what the kernel last ran", () => {
|
|
1498
|
+
const state = hydratedState();
|
|
1499
|
+
const next = exportedForTesting.reducer(state, {
|
|
1500
|
+
type: "setLastRun",
|
|
1501
|
+
payload: { lastCodeRun: { [CELL_A]: "0" } },
|
|
1502
|
+
});
|
|
1503
|
+
|
|
1504
|
+
expect(next.cellData[CELL_A]).toMatchObject({
|
|
1505
|
+
lastCodeRun: "0",
|
|
1506
|
+
edited: true,
|
|
1507
|
+
});
|
|
1508
|
+
expect(next.cellData[CELL_B]).toMatchObject({
|
|
1509
|
+
lastCodeRun: null,
|
|
1510
|
+
edited: false,
|
|
1511
|
+
});
|
|
1512
|
+
expect(next.cellRuntime).toBe(state.cellRuntime);
|
|
3946
1513
|
});
|
|
3947
1514
|
|
|
3948
|
-
it("
|
|
1515
|
+
it("resetCellRuntimes starts every cell's runtime over", () => {
|
|
3949
1516
|
const next = exportedForTesting.reducer(hydratedState(), {
|
|
3950
|
-
type: "
|
|
3951
|
-
payload:
|
|
1517
|
+
type: "resetCellRuntimes",
|
|
1518
|
+
payload: undefined,
|
|
3952
1519
|
});
|
|
3953
1520
|
|
|
3954
|
-
expect(next.cellRuntime[CELL_A].output).
|
|
3955
|
-
|
|
1521
|
+
expect(next.cellRuntime[CELL_A].output).toBeNull();
|
|
1522
|
+
expect(next.cellRuntime[CELL_B].consoleOutputs).toEqual([]);
|
|
1523
|
+
});
|
|
1524
|
+
|
|
1525
|
+
it("resetCellRuntimes starts the scratch cell over too", () => {
|
|
1526
|
+
const state = MockNotebook.notebookState({
|
|
1527
|
+
cellData: { [CELL_A]: { id: CELL_A, code: "1" } },
|
|
1528
|
+
cellRuntime: {
|
|
1529
|
+
[SCRATCH_CELL_ID]: {
|
|
1530
|
+
output: {
|
|
1531
|
+
channel: "output",
|
|
1532
|
+
mimetype: "text/plain",
|
|
1533
|
+
data: "scratch",
|
|
1534
|
+
timestamp: 0,
|
|
1535
|
+
},
|
|
1536
|
+
},
|
|
1537
|
+
},
|
|
3956
1538
|
});
|
|
1539
|
+
const next = exportedForTesting.reducer(state, {
|
|
1540
|
+
type: "resetCellRuntimes",
|
|
1541
|
+
payload: undefined,
|
|
1542
|
+
});
|
|
1543
|
+
|
|
1544
|
+
expect(next.cellRuntime[SCRATCH_CELL_ID].output).toBeNull();
|
|
3957
1545
|
});
|
|
3958
1546
|
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
1547
|
+
describe("resetCellRuntimes in WASM", () => {
|
|
1548
|
+
beforeEach(async () => {
|
|
1549
|
+
const { isWasm } = await import("@/core/wasm/utils");
|
|
1550
|
+
vi.mocked(isWasm).mockReturnValue(true);
|
|
3963
1551
|
});
|
|
3964
1552
|
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
1553
|
+
afterEach(async () => {
|
|
1554
|
+
const { isWasm } = await import("@/core/wasm/utils");
|
|
1555
|
+
vi.mocked(isWasm).mockReturnValue(false);
|
|
3968
1556
|
});
|
|
3969
|
-
});
|
|
3970
1557
|
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
1558
|
+
it("keeps hydrated outputs and console", () => {
|
|
1559
|
+
const next = exportedForTesting.reducer(hydratedState(), {
|
|
1560
|
+
type: "resetCellRuntimes",
|
|
1561
|
+
payload: undefined,
|
|
1562
|
+
});
|
|
1563
|
+
|
|
1564
|
+
expect(next.cellRuntime[CELL_A].output).toMatchObject({
|
|
1565
|
+
data: "hydrated-A",
|
|
1566
|
+
});
|
|
1567
|
+
expect(next.cellRuntime[CELL_B].consoleOutputs[0]).toMatchObject({
|
|
1568
|
+
data: "hydrated-B-stdout",
|
|
1569
|
+
});
|
|
3976
1570
|
});
|
|
3977
1571
|
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
1572
|
+
it("starts cells with no prior runtime over", () => {
|
|
1573
|
+
const state = MockNotebook.notebookState({
|
|
1574
|
+
cellData: { [CELL_A]: { id: CELL_A, code: "1" } },
|
|
1575
|
+
});
|
|
1576
|
+
const next = exportedForTesting.reducer(state, {
|
|
1577
|
+
type: "resetCellRuntimes",
|
|
1578
|
+
payload: undefined,
|
|
1579
|
+
});
|
|
1580
|
+
|
|
1581
|
+
expect(next.cellRuntime[CELL_A].output).toBeNull();
|
|
1582
|
+
expect(next.cellRuntime[CELL_A].consoleOutputs).toEqual([]);
|
|
1583
|
+
});
|
|
3982
1584
|
});
|
|
3983
1585
|
});
|