@marimo-team/islands 0.25.3-dev21 → 0.25.3-dev211
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-D6RHJV8D.js} +120 -116
- package/dist/{ErrorBoundary-ttSr7_91.js → ErrorBoundary-BpGAot_X.js} +16 -16
- package/dist/{abnfDiagram-VCTEODGH-B3w-XWX8.js → abnfDiagram-VCTEODGH-0q7Lbmii.js} +2 -2
- package/dist/any-language-editor-jGaR0EAo.js +75 -0
- package/dist/{architectureDiagram-5GKGNRK7-C18U9A6k.js → architectureDiagram-5GKGNRK7-1PydKPmE.js} +1 -1
- package/dist/{arrays-CN1djC4k.js → arrays-DoQ-AILx.js} +15 -27
- package/dist/assets/__vite-browser-external-CBRgnYY0.js +1 -0
- package/dist/assets/worker-DiPNP__U.js +113 -0
- package/dist/{button-CvjkwdSk.js → button-B6RjDKJx.js} +227 -217
- package/dist/{capabilities-CQ5IjhzL.js → capabilities-CkVzWJEX.js} +2 -2
- package/dist/{chat-ui-w8SVynkn.js → chat-ui-B5pH_aXk.js} +1277 -1275
- package/dist/{check-BMKBBrdc.js → check-Z_vtI3OL.js} +1 -1
- package/dist/{chunk-IMKFNOWR-CrS5bzqW.js → chunk-IMKFNOWR-DEzZZ1Zc.js} +1 -1
- package/dist/{chunk-SHT3W25Y-StpvPvUG.js → chunk-SHT3W25Y-CuHj-hnn.js} +1 -1
- package/dist/{chunk-SVP7TREG-CBXjbYZC.js → chunk-SVP7TREG-BPiZK2bA.js} +1 -1
- package/dist/{chunk-TICWLB2K-Dp6bIb-q.js → chunk-TICWLB2K-DF6XcIjM.js} +1 -1
- package/dist/{chunk-YOKDWASO-DPZk8vhS.js → chunk-YOKDWASO-DKplTrBY.js} +20 -20
- package/dist/{classDiagram-ZZMXUADV-BFYuxV5g.js → classDiagram-ZZMXUADV-CMdR-cP4.js} +2 -2
- package/dist/{classDiagram-v2-VYDZK3BY-D_tjXVMl.js → classDiagram-v2-VYDZK3BY-DRvc3_7Y.js} +2 -2
- package/dist/code-visibility-B5trOyTh.js +134 -0
- package/dist/{common-CeebhhhO.js → common-BiM3Xm_d.js} +9837 -9658
- package/dist/config-DbjzjwxX.js +636 -0
- package/dist/{copy-icon-BgUO8jsi.js → copy-icon-CG57cxwb.js} +3 -3
- package/dist/createReducer-DePYJ0F3.js +63 -0
- package/dist/{cynefinDiagram-5FMLGOSQ-DSgYrHB2.js → cynefinDiagram-5FMLGOSQ-BkBjEY9l.js} +1 -1
- package/dist/{dagre-GXQ25YYZ-GYlruCBf.js → dagre-GXQ25YYZ-D-HEaW4I.js} +1 -1
- package/dist/{data-grid-overlay-editor-vxQ4E6k1.js → data-grid-overlay-editor-GxGkM-Al.js} +5 -5
- package/dist/{data-grid-types-BH_r7g7l.js → data-grid-types-0PJ-9N5n.js} +1 -1
- package/dist/{diagram-S7CK7UJ4-DRu5LX5_.js → diagram-S7CK7UJ4-Bn-oF-rB.js} +1 -1
- package/dist/{diagram-UQ7AKVKN-DD7uiejP.js → diagram-UQ7AKVKN-BpzzXWBj.js} +1 -1
- package/dist/{diagram-VX7I27RA-ByH9KAJJ.js → diagram-VX7I27RA-COOtvfBP.js} +1 -1
- package/dist/{diagram-Z3DM3KII-BsQHPRke.js → diagram-Z3DM3KII-K5cXvbrl.js} +1 -1
- package/dist/{dialog-Cc479ZEH.js → dialog-DsZ49mmU.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-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-CSNdPSnS.js +6083 -0
- package/dist/{ebnfDiagram-PWID7BFC-CQRK5_eT.js → ebnfDiagram-PWID7BFC-BI1ZmjVm.js} +2 -2
- package/dist/{erDiagram-RLTQ6QDP-BKtWrDVh.js → erDiagram-RLTQ6QDP-BtWjj9xy.js} +1 -1
- package/dist/{error-banner-CFPHFw4h.js → error-banner-IsonnvA0.js} +84 -83
- package/dist/error-entries-CRPfxMqm.js +2505 -0
- package/dist/{esm-D2gTsXEV.js → esm-BgKjMvWw.js} +530 -529
- package/dist/{esm-AMFssoNp.js → esm-CHjVSexr.js} +328 -329
- package/dist/feedback-button-CAxVqhvl.js +374 -0
- package/dist/{flowDiagram-HODETNUW-1IN-ih2E.js → flowDiagram-HODETNUW-DQvZh96A.js} +2 -2
- package/dist/{formats-X4mMQWzX.js → formats-BK4VefeF.js} +6 -6
- package/dist/{ganttDiagram-EL5Y4UJY-BQT16JSc.js → ganttDiagram-EL5Y4UJY-Dh_4cBJZ.js} +2 -2
- package/dist/{glide-data-editor-CSYHMRDu.js → glide-data-editor-CBX2wLtn.js} +417 -415
- package/dist/{glide-utils-DM9zBO4W.js → glide-utils-CQddGpa-.js} +16 -16
- package/dist/{globals-Fpiwkv4D.js → globals-DuCA4LSq.js} +3 -3
- package/dist/{highlighted-body-KPVGNVTW-zwjq21tf.js → highlighted-body-KPVGNVTW-3Ziwfz-V.js} +2 -2
- package/dist/{error-entries-Damuuclv.js → html-to-image-CgAnxFi8.js} +26911 -26769
- package/dist/{infoDiagram-27XIBGKW-CkB-zr4p.js → infoDiagram-27XIBGKW-BdAObnc_.js} +1 -1
- package/dist/{ishikawaDiagram-5VMMS53U-wlyLjBmM.js → ishikawaDiagram-5VMMS53U-DP-jE8dw.js} +1 -1
- package/dist/{kanban-definition-UXKFOSKX-C2usY0i-.js → kanban-definition-UXKFOSKX-BkMgPjfm.js} +1 -1
- package/dist/{linear-Cnaq3Tfb.js → linear-DWERh7eI.js} +121 -73
- package/dist/{loader-BQOfjTIJ.js → loader-BisjOLiW.js} +12 -12
- package/dist/main.js +10128 -8127
- package/dist/{mermaid-BA-Z6wGL.js → mermaid-B-OFqheA.js} +52 -52
- package/dist/mermaid-HWGCJPDP-Du3-HPQA.js +2 -0
- package/dist/{mindmap-definition-YA3MSWOX-nKOWS8RR.js → mindmap-definition-YA3MSWOX-Du7toAZX.js} +1 -1
- package/dist/{number-overlay-editor-BOjqsq7D.js → number-overlay-editor-KvS7Rgw9.js} +3 -3
- package/dist/{dist-CN8zI-XC.js → objectWithoutPropertiesLoose-ai5NUqGa.js} +262 -240
- 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-CX_729j0.js} +2 -2
- package/dist/{pieDiagram-E7YTZNPT-B7S-DsW_.js → pieDiagram-E7YTZNPT-dtTFJcVI.js} +1 -1
- package/dist/precisionRound-DhAzeRcm.js +49 -0
- package/dist/{process-output-CTPTtxXB.js → process-output-Bmev5azU.js} +4 -4
- package/dist/{quadrantDiagram-AXDQQJYC-Ccw0xMjN.js → quadrantDiagram-AXDQQJYC-Blrm7V7_.js} +1 -1
- package/dist/{railroadDiagram-O6MQD6OU-Cfa5Q-hr.js → railroadDiagram-O6MQD6OU-Dsc2Cf4c.js} +2 -2
- package/dist/{react-vega-BED0DjiB.js → react-vega-C4T9Rb_w.js} +12284 -12270
- package/dist/{requirementDiagram-BXWQKSXE-d8zXCJIk.js → requirementDiagram-BXWQKSXE-D3g0c60I.js} +1 -1
- package/dist/{reveal-component-Brpo7CP8.js → reveal-component-CqBvRKJz.js} +426 -403
- package/dist/schemas-CdscWNy_.js +4747 -0
- package/dist/select-BSxlITBo.js +1200 -0
- package/dist/{sequenceDiagram-WJ2MYXX4-b6pei3lJ.js → sequenceDiagram-WJ2MYXX4-w1pWbIpk.js} +1 -1
- package/dist/{spec-BJzgmvVa.js → spec-Dq8RNplp.js} +41 -11
- package/dist/{stateDiagram-D77RDMKH-Be-xSpaZ.js → stateDiagram-D77RDMKH-BCv7kP4H.js} +2 -2
- package/dist/{stateDiagram-v2-MP3YSRHH-5Y-FxesJ.js → stateDiagram-v2-MP3YSRHH-D1cTOt0x.js} +2 -2
- package/dist/static-state-BldGd99T.js +72 -0
- package/dist/strings-BGW-fXVJ.js +507 -0
- package/dist/style.css +1 -1
- package/dist/{swimlanes-42K2YHIH-nRRl4NU0.js → swimlanes-42K2YHIH-DNO6jDnd.js} +1 -1
- package/dist/{swimlanesDiagram-VR7AAH4N-BS79510V.js → swimlanesDiagram-VR7AAH4N-CkSWsxYJ.js} +2 -2
- package/dist/{swiper-component-DKntyn8p.js → swiper-component-Dir1o8ml.js} +2 -2
- package/dist/{time-BieKwdYS.js → time-BaMbJs4z.js} +18 -18
- package/dist/{timeline-definition-24CTP7MA-Cfzf8WPf.js → timeline-definition-24CTP7MA-DQvqGFM9.js} +1 -1
- package/dist/toast-BWIPi3aF.js +491 -0
- package/dist/{tooltip-CtBZ5q8p.js → tooltip-C3UnvsRe.js} +329 -305
- package/dist/use-restore-focus-BWO0z1lo.js +1672 -0
- package/dist/{useAsyncData-BD534XmW.js → useAsyncData-DsZMvNXn.js} +15 -14
- package/dist/{useDateFormatter-DkajGwwd.js → useDateFormatter-ChB4eyim.js} +2 -2
- package/dist/{useDeepCompareMemoize-C72_RNsu.js → useDeepCompareMemoize-KMBBux8C.js} +1 -1
- package/dist/{useIframeCapabilities-DBETvvPl.js → useIframeCapabilities-cMNQ80CK.js} +1 -1
- package/dist/useLifecycle-BquH9ET-.js +112 -0
- package/dist/useTheme-B9C1JIy9.js +307 -0
- package/dist/utils-DXkj82sK.js +813 -0
- package/dist/{vega-component-DfcYgj-Y.js → vega-component-9NOEITbg.js} +170 -169
- 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-BfzXp4SH.js} +1 -1
- package/dist/{wardleyDiagram-VM6X3IG4-Cxs9NtVi.js → wardleyDiagram-VM6X3IG4-CiqEE8eq.js} +1 -1
- package/dist/{xychartDiagram-S5SC5T6Z-DNWbvPK7.js → xychartDiagram-S5SC5T6Z-BTIHCVOI.js} +2 -2
- package/package.json +16 -20
- package/src/__mocks__/document.tsx +63 -0
- package/src/__mocks__/notebook.ts +3 -4
- package/src/__mocks__/partial.ts +26 -0
- package/src/__mocks__/requests.ts +1 -4
- package/src/__tests__/main.test.tsx +24 -1
- package/src/__tests__/module-entry.test.ts +44 -0
- package/src/__tests__/mount.test.ts +1 -21
- package/src/__tests__/{host-imports.test.ts → replica-imports.test.ts} +12 -11
- package/src/components/ai/__tests__/ai-model-dropdown.test.tsx +63 -0
- package/src/components/ai/ai-model-dropdown.tsx +11 -6
- package/src/components/app-config/ai-config.tsx +7 -2
- package/src/components/app-config/app-config-form.tsx +5 -9
- package/src/components/app-config/user-config-form.tsx +35 -56
- package/src/components/chat/__tests__/message-queue.test.tsx +17 -0
- package/src/components/chat/__tests__/notebook-access-selector.test.tsx +64 -0
- package/src/components/chat/acp/__tests__/session-tabs.test.tsx +43 -0
- package/src/components/chat/acp/__tests__/state.test.ts +2 -2
- package/src/components/chat/acp/blocks.tsx +3 -2
- package/src/components/chat/acp/session-tabs.tsx +8 -3
- package/src/components/chat/chat-components.tsx +2 -0
- package/src/components/chat/chat-panel.tsx +20 -101
- package/src/components/chat/chat-utils.ts +4 -8
- package/src/components/chat/notebook-access-selector.stories.tsx +22 -0
- package/src/components/chat/notebook-access-selector.tsx +100 -0
- package/src/components/data-table/TableBottomBar.tsx +4 -1
- package/src/components/data-table/TableTopBar.tsx +2 -0
- package/src/components/data-table/__tests__/chart-spec-model.test.ts +195 -195
- package/src/components/data-table/__tests__/column-explorer.test.tsx +27 -1
- package/src/components/data-table/__tests__/columns.test.tsx +105 -0
- package/src/components/data-table/__tests__/context-menu-interaction.test.tsx +130 -0
- package/src/components/data-table/__tests__/data-table.test.tsx +1 -0
- package/src/components/data-table/__tests__/date-popover.test.tsx +72 -0
- package/src/components/data-table/__tests__/export-actions.test.tsx +105 -0
- package/src/components/data-table/__tests__/keyboard-selection.test.tsx +188 -0
- package/src/components/data-table/__tests__/pagination.test.tsx +28 -1
- package/src/components/data-table/__tests__/search-highlight.test.tsx +109 -0
- package/src/components/data-table/__tests__/url-detector.test.tsx +74 -0
- package/src/components/data-table/cell-selection/feature.ts +2 -2
- package/src/components/data-table/cell-selection/types.ts +2 -2
- package/src/components/data-table/charts/__tests__/chart-panel.test.tsx +74 -1
- package/src/components/data-table/charts/__tests__/spec-snapshot.test.ts +20 -20
- package/src/components/data-table/charts/__tests__/spec.test.ts +78 -52
- package/src/components/data-table/charts/__tests__/storage.test.ts +4 -1
- package/src/components/data-table/charts/chart-spec/spec.ts +68 -43
- package/src/components/data-table/charts/charts.tsx +40 -30
- package/src/components/data-table/charts/storage.ts +16 -5
- package/src/components/data-table/column-explorer-panel/column-explorer.tsx +1 -1
- package/src/components/data-table/column-summary/chart-spec-model.tsx +23 -16
- package/src/components/data-table/column-summary/column-summary.tsx +2 -4
- package/src/components/data-table/column-visibility-dropdown.tsx +8 -13
- package/src/components/data-table/columns.tsx +45 -46
- package/src/components/data-table/context-menu.tsx +36 -20
- package/src/components/data-table/data-table.tsx +17 -1
- package/src/components/data-table/date-popover.tsx +6 -4
- package/src/components/data-table/export-actions.tsx +76 -48
- package/src/components/data-table/pagination.tsx +44 -33
- package/src/components/data-table/range-focus/atoms.ts +4 -6
- package/src/components/data-table/range-focus/use-cell-range-selection.ts +65 -38
- package/src/components/data-table/range-focus/use-scroll-into-view.ts +21 -2
- package/src/components/data-table/renderers.tsx +16 -7
- package/src/components/data-table/row-viewer-panel/__tests__/row-viewer.test.tsx +24 -1
- package/src/components/data-table/row-viewer-panel/row-viewer.tsx +4 -4
- package/src/components/data-table/search-highlight.tsx +79 -0
- package/src/components/data-table/types.ts +1 -0
- package/src/components/data-table/url-detector.tsx +49 -9
- package/src/components/datasources/__tests__/column-actions.test.tsx +97 -0
- package/src/components/datasources/__tests__/column-preview.test.tsx +96 -5
- package/src/components/datasources/__tests__/datasources-interactions.test.tsx +181 -0
- package/src/components/datasources/__tests__/table-list.test.tsx +118 -0
- package/src/components/datasources/__tests__/utils.test.ts +57 -6
- package/src/components/datasources/column-preview.tsx +17 -12
- package/src/components/datasources/datasources.tsx +56 -43
- package/src/components/datasources/utils.ts +32 -23
- package/src/components/debugger/debugger-code.tsx +5 -0
- package/src/components/dependency-graph/custom-node.tsx +1 -1
- package/src/components/dependency-graph/dependency-graph-tree.tsx +8 -8
- package/src/components/dependency-graph/elements.ts +24 -21
- package/src/components/dependency-graph/minimap-content.tsx +13 -19
- package/src/components/dependency-graph/panels.tsx +4 -2
- package/src/components/dependency-graph/utils/__tests__/layout.test.ts +18 -11
- package/src/components/editor/SortableCell.tsx +1 -3
- package/src/components/editor/__tests__/Output.test.tsx +50 -32
- package/src/{core/attachments/__tests__/toast.test.tsx → components/editor/__tests__/agent-toast.test.tsx} +1 -1
- package/src/components/editor/__tests__/data-attributes.test.tsx +28 -14
- package/src/components/editor/__tests__/dynamic-favicon.test.tsx +111 -18
- package/src/components/editor/__tests__/output-persistence.test.tsx +6 -2
- package/src/components/editor/__tests__/package-alert.test.tsx +22 -1
- package/src/components/editor/actions/__tests__/name-cell-input.test.tsx +69 -0
- package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +34 -8
- package/src/components/editor/actions/__tests__/pair-with-agent.test.tsx +2 -6
- package/src/components/editor/actions/__tests__/useCellActionButton.test.tsx +113 -0
- package/src/components/editor/actions/name-cell-input.tsx +4 -3
- package/src/components/editor/actions/pair-with-agent-commands.ts +2 -2
- package/src/components/editor/actions/pair-with-agent-modal.tsx +19 -16
- package/src/components/editor/actions/useCellActionButton.tsx +36 -31
- package/src/components/editor/actions/useConfigActions.tsx +3 -4
- package/src/components/editor/actions/useNotebookActions.tsx +19 -23
- package/src/components/editor/actions/useRestartKernel.tsx +1 -1
- package/src/components/editor/actions/useSetCodeVisibility.ts +11 -12
- package/src/components/editor/actions/useSetOutputsExpanded.ts +14 -19
- package/src/components/editor/ai/add-cell-with-ai.tsx +3 -3
- package/src/components/editor/ai/ai-completion-editor.tsx +1 -0
- package/src/components/editor/alerts/__tests__/connection-notice.test.tsx +1 -1
- package/src/components/editor/alerts/connection-notice.tsx +1 -1
- package/src/components/editor/alerts/startup-logs-alert.tsx +1 -0
- package/src/components/editor/alerts/stdin-blocking-alert.tsx +1 -1
- package/src/components/editor/app-container.tsx +1 -1
- package/src/components/editor/boundary/__tests__/ErrorBoundary.test.tsx +1 -1
- package/src/components/editor/cell/CreateCellButton.tsx +15 -21
- package/src/components/editor/cell/DeleteButton.tsx +3 -2
- package/src/components/editor/cell/RunButton.tsx +2 -2
- package/src/components/editor/cell/StagedAICell.tsx +10 -2
- package/src/components/editor/cell/StopButton.tsx +2 -2
- package/src/components/editor/cell/__tests__/StagedAICell.test.tsx +4 -2
- package/src/components/editor/cell/__tests__/useShouldShowInterrupt.test.ts +39 -0
- package/src/components/editor/cell/cell-actions.tsx +12 -1
- package/src/components/editor/cell/code/__tests__/language-toggle.test.tsx +54 -0
- package/src/components/editor/cell/code/cell-editor.tsx +46 -165
- package/src/components/editor/cell/code/language-toggle.tsx +8 -7
- package/src/components/editor/cell/collapse.tsx +11 -4
- package/src/components/editor/cell/useAddCell.ts +4 -8
- package/src/components/editor/cell/useDeleteCell.tsx +10 -36
- package/src/components/editor/cell/useRunCells.ts +2 -5
- package/src/components/editor/cell/useShouldShowInterrupt.ts +2 -12
- package/src/components/editor/cell/useSplitCell.tsx +10 -6
- package/src/components/editor/chrome/components/__tests__/feedback-button.test.tsx +1 -1
- package/src/components/editor/chrome/components/feedback-button.tsx +2 -8
- package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +31 -2
- package/src/components/editor/chrome/panels/__tests__/runtime-details.test.tsx +1 -1
- package/src/components/editor/chrome/panels/__tests__/sandbox-recovery.test.tsx +1 -1
- package/src/components/editor/chrome/panels/cache-panel.tsx +1 -0
- package/src/components/editor/chrome/panels/dependency-graph-panel.tsx +1 -1
- package/src/components/editor/chrome/panels/logs-panel.tsx +2 -1
- package/src/components/editor/chrome/panels/outline/__tests__/outline-keyboard.test.tsx +84 -0
- package/src/components/editor/chrome/panels/outline/floating-outline.tsx +39 -14
- package/src/components/editor/chrome/panels/outline/useActiveOutline.tsx +3 -2
- package/src/components/editor/chrome/panels/packages-panel.tsx +4 -7
- package/src/components/editor/chrome/panels/secrets-panel.tsx +1 -0
- package/src/components/editor/chrome/panels/session-panel.tsx +1 -1
- package/src/components/editor/chrome/panels/snippets-panel.tsx +10 -12
- package/src/components/editor/chrome/panels/variable-panel.tsx +1 -5
- package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +6 -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 +62 -3
- 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 +12 -2
- package/src/components/editor/chrome/wrapper/storage.ts +1 -1
- package/src/components/editor/columns/__tests__/cell-column.test.tsx +1 -1
- package/src/components/editor/columns/cell-column.tsx +1 -1
- package/src/components/editor/columns/sortable-column.tsx +2 -2
- package/src/components/editor/connections/components.tsx +5 -10
- package/src/components/editor/controls/Controls.tsx +4 -4
- package/src/components/editor/controls/__tests__/command-palette.test.tsx +53 -0
- package/src/components/editor/controls/__tests__/keyboard-shortcuts.test.tsx +61 -0
- package/src/components/editor/controls/__tests__/notebook-menu-dropdown.test.tsx +10 -4
- package/src/components/editor/controls/command-palette.tsx +22 -10
- package/src/components/editor/controls/keyboard-shortcuts.tsx +14 -5
- package/src/components/editor/documentation.css +34 -11
- package/src/components/editor/dynamic-favicon.tsx +21 -40
- package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +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 +40 -20
- package/src/components/editor/links/__tests__/cell-link.test.tsx +148 -0
- package/src/components/editor/links/cell-link-list.tsx +1 -1
- package/src/components/editor/links/cell-link.tsx +17 -7
- package/src/components/editor/navigation/__tests__/clipboard.test.ts +60 -38
- package/src/components/editor/navigation/__tests__/navigation.test.ts +108 -85
- package/src/components/editor/navigation/__tests__/selection.test.ts +43 -21
- package/src/components/editor/navigation/clipboard.ts +17 -14
- package/src/components/editor/navigation/focus-utils.ts +23 -4
- package/src/components/editor/navigation/multi-cell-action-toolbar.tsx +17 -33
- package/src/components/editor/navigation/navigation.ts +71 -70
- package/src/components/editor/navigation/selection.ts +30 -29
- package/src/components/editor/notebook-banner.tsx +1 -0
- package/src/components/editor/notebook-cell.tsx +29 -32
- package/src/components/editor/output/MarimoErrorOutput.tsx +2 -1
- package/src/components/editor/output/MarimoTracebackOutput.tsx +19 -17
- package/src/components/editor/output/Outputs.css +8 -0
- package/src/components/editor/output/__tests__/traceback.test.tsx +27 -1
- package/src/components/editor/output/ansi-reduce.ts +33 -20
- package/src/components/editor/output/console/ConsoleOutput.tsx +19 -25
- package/src/components/editor/output/console/__tests__/ConsoleOutput.test.tsx +125 -69
- package/src/components/editor/package-alert.tsx +14 -7
- package/src/components/editor/renderers/cell-array.tsx +22 -33
- package/src/components/editor/renderers/grid-layout/grid-layout.tsx +12 -6
- package/src/components/editor/renderers/slides-layout/__tests__/plugin.test.ts +0 -1
- package/src/components/editor/renderers/slides-layout/slides-layout.tsx +1 -1
- package/src/components/editor/renderers/vertical-layout/__tests__/useFocusFirstEditor.test.ts +4 -4
- package/src/components/editor/renderers/vertical-layout/sidebar/toggle.tsx +7 -1
- package/src/components/editor/renderers/vertical-layout/useFocusFirstEditor.ts +1 -1
- package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +1 -1
- package/src/components/editor/stdin-blocking-alert.tsx +1 -1
- package/src/components/home/components.tsx +17 -31
- package/src/components/icons/__tests__/large-spinner.test.tsx +39 -0
- package/src/components/icons/large-spinner.tsx +3 -14
- package/src/components/icons/spinner.tsx +1 -1
- package/src/components/markdown/markdown-renderer.tsx +4 -8
- package/src/components/mcp/mcp-status-indicator.tsx +1 -1
- package/src/components/pages/__tests__/home-page.test.tsx +116 -0
- package/src/components/pages/edit-page.tsx +19 -17
- package/src/components/pages/home-page.tsx +164 -91
- package/src/components/scratchpad/scratchpad.test.tsx +89 -0
- package/src/components/scratchpad/scratchpad.tsx +43 -32
- 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 +17 -12
- package/src/components/slides/reveal-component.tsx +58 -26
- package/src/components/slides/reveal-slides.css +5 -4
- package/src/components/slides/slide-cell-view.tsx +4 -5
- package/src/components/slides/slide-form.tsx +1 -0
- package/src/components/slides/slide-scroll-hint.tsx +3 -1
- package/src/components/sort/SortableCellsProvider.tsx +8 -25
- package/src/components/tracing/tracing-keyboard.test.tsx +85 -0
- package/src/components/tracing/tracing.tsx +46 -42
- package/src/components/ui/__tests__/draggable-popover.test.tsx +24 -0
- package/src/components/ui/__tests__/range-slider.test.tsx +165 -0
- package/src/components/ui/alert-dialog.tsx +1 -1
- package/src/components/ui/draggable-popover.tsx +24 -2
- package/src/components/ui/range-slider.tsx +22 -9
- package/src/core/MarimoApp.tsx +1 -1
- package/src/core/ai/__tests__/staged-cells.test.ts +32 -33
- package/src/core/ai/context/providers/__tests__/cell-output.test.ts +8 -12
- package/src/core/ai/context/providers/cell-output.ts +2 -2
- package/src/core/ai/context/providers/datasource.ts +6 -2
- package/src/core/ai/staged-cells.ts +18 -19
- package/src/core/ai/tools/__tests__/edit-notebook-tool.test.ts +40 -88
- package/src/core/ai/tools/__tests__/registry.test.ts +17 -0
- package/src/core/ai/tools/base.ts +18 -15
- package/src/core/ai/tools/edit-notebook-tool.ts +86 -80
- package/src/core/ai/tools/registry.ts +13 -10
- package/src/core/ai/tools/sample-tool.ts +6 -7
- package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +4 -37
- package/src/core/cells/__tests__/actions.test.ts +624 -35
- package/src/core/cells/__tests__/add-missing-import.test.ts +7 -8
- package/src/core/cells/__tests__/arrange.test.ts +432 -0
- package/src/core/cells/__tests__/cell.test.ts +39 -0
- package/src/core/cells/__tests__/cells.test.ts +251 -2540
- 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__/session.test.ts +445 -211
- package/src/core/cells/__tests__/utils.test.ts +9 -108
- package/src/core/cells/actions.ts +675 -6
- package/src/core/cells/add-missing-import.ts +47 -43
- package/src/core/cells/arrange.ts +529 -0
- package/src/core/cells/cell.ts +14 -3
- package/src/core/cells/cells.ts +126 -1363
- 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/layout.ts +221 -0
- package/src/core/cells/outputs.ts +12 -28
- package/src/core/cells/pending-delete-service.ts +3 -7
- package/src/core/cells/session.ts +76 -53
- package/src/core/cells/types.ts +22 -6
- package/src/core/cells/utils.ts +10 -8
- package/src/core/codemirror/__tests__/format.test.ts +0 -34
- package/src/core/codemirror/__tests__/setup.test.ts +29 -29
- package/src/core/codemirror/ai/__tests__/resources.test.ts +117 -0
- package/src/core/codemirror/ai/resources.ts +1 -1
- package/src/core/codemirror/cells/__tests__/extensions.test.ts +2 -6
- package/src/core/codemirror/cells/extensions.ts +30 -28
- package/src/core/codemirror/cells/state.ts +1 -6
- 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 +16 -10
- package/src/core/codemirror/language/languages/sql/sql.ts +16 -24
- package/src/core/codemirror/language/panel/panel.tsx +1 -0
- package/src/core/codemirror/language/types.ts +7 -7
- package/src/core/codemirror/language/utils.ts +9 -39
- package/src/core/codemirror/lsp/__tests__/notebook-lsp.test.ts +1 -2
- package/src/core/codemirror/lsp/notebook-lsp.ts +6 -5
- package/src/core/codemirror/lsp/utils.ts +1 -1
- package/src/core/codemirror/misc/__tests__/paste.test.ts +3 -1
- package/src/core/codemirror/misc/paste.ts +1 -1
- package/src/core/codemirror/readonly/__tests__/extension.test.ts +1 -1
- package/src/core/codemirror/readonly/extension.ts +2 -2
- package/src/core/codemirror/utils.ts +1 -0
- package/src/core/config/__tests__/app-config.test.ts +64 -0
- package/src/core/config/__tests__/config-schema.test.ts +13 -6
- package/src/core/config/config-schema.ts +13 -4
- package/src/core/config/config.ts +26 -12
- 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 +21 -28
- package/src/core/errors/error-entries.ts +4 -1
- package/src/core/export/hooks.ts +1 -1
- package/src/core/export/layout.ts +2 -2
- package/src/core/hotkeys/actions.ts +2 -4
- package/src/core/islands/__tests__/bootstrap.test.ts +69 -17
- package/src/core/islands/__tests__/bridge.test.ts +72 -25
- package/src/core/islands/bootstrap.ts +46 -53
- package/src/core/islands/bridge.ts +75 -24
- package/src/core/islands/components/__tests__/IslandControls.test.tsx +7 -5
- package/src/core/islands/components/web-components.tsx +1 -1
- package/src/core/islands/worker/__tests__/controller.test.ts +62 -4
- package/src/core/islands/worker/controller.ts +4 -10
- package/src/core/islands/worker/worker.tsx +33 -33
- package/src/core/kernel/__tests__/RuntimeState.test.ts +1 -1
- package/src/core/kernel/__tests__/handlers.test.ts +7 -153
- package/src/core/kernel/handlers.ts +19 -72
- package/src/core/meta/__tests__/code-visibility.test.tsx +78 -70
- package/src/core/meta/code-visibility.ts +18 -9
- package/src/core/network/__tests__/connection.test.ts +1 -1
- package/src/core/network/__tests__/requests-lazy.test.ts +1 -3
- package/src/core/network/connection-notice.ts +1 -1
- package/src/core/network/connection.ts +1 -1
- package/src/core/network/requests-lazy.ts +2 -5
- 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 +758 -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__/fixtures.ts +79 -0
- package/src/core/notebook/replica/__tests__/replica.test.ts +550 -0
- package/src/core/notebook/replica/backend.ts +19 -0
- package/src/core/notebook/replica/connection.ts +42 -0
- package/src/core/notebook/replica/index.ts +13 -0
- package/src/core/notebook/replica/replica.ts +286 -0
- package/src/core/{host → notebook/replica}/resources/attachments.ts +6 -3
- package/src/core/{host → notebook/replica}/resources/document.ts +4 -4
- package/src/core/packages/__tests__/useSandboxController.test.tsx +1 -1
- package/src/core/packages/useSandboxController.ts +1 -1
- package/src/core/run-app.tsx +48 -51
- package/src/core/runtime/__tests__/adapter.test.ts +1 -1
- package/src/core/runtime/__tests__/createWsUrl.test.ts +77 -7
- package/src/core/runtime/__tests__/runtime.test.ts +32 -74
- package/src/core/runtime/adapter.ts +1 -1
- 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 +184 -0
- package/src/core/{websocket → session}/__tests__/useNotebookSession.hook.test.tsx +213 -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 +77 -64
- package/src/core/state/__tests__/jotai.test.tsx +108 -0
- package/src/core/state/jotai.ts +1 -1
- package/src/core/static/__tests__/files.test.ts +38 -10
- package/src/core/static/files.ts +2 -0
- package/src/core/{websocket → transports}/__tests__/connection-state.test.ts +1 -13
- package/src/core/transports/__tests__/server.test.ts +408 -0
- package/src/core/transports/__tests__/static.test.ts +306 -0
- package/src/core/transports/__tests__/wasm.test.ts +166 -0
- package/src/core/{websocket → transports}/connection-state.ts +9 -16
- package/src/core/transports/server.ts +152 -0
- package/src/core/transports/static.ts +166 -0
- package/src/core/transports/wasm.ts +97 -0
- package/src/core/wasm/__tests__/bridge.test.ts +105 -65
- package/src/core/wasm/__tests__/worker.test.ts +187 -0
- package/src/core/wasm/bridge.ts +60 -71
- package/src/core/wasm/rpc.ts +15 -3
- package/src/core/wasm/store.ts +3 -3
- package/src/core/wasm/worker/bootstrap.ts +55 -51
- package/src/core/wasm/worker/getMarimoWheel.ts +1 -1
- package/src/core/wasm/worker/save-worker.ts +9 -10
- package/src/core/wasm/worker/types.ts +29 -18
- package/src/core/wasm/worker/worker.ts +89 -45
- package/src/css/app/codemirror.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/registerReactComponent.tsx +1 -1
- package/src/plugins/impl/CodeEditorPlugin.tsx +1 -1
- package/src/plugins/impl/DataEditorPlugin.tsx +1 -1
- package/src/plugins/impl/DataTablePlugin.tsx +69 -37
- package/src/plugins/impl/DropdownPlugin.tsx +2 -6
- package/src/plugins/impl/FileBrowserPlugin.tsx +4 -16
- package/src/plugins/impl/FormPlugin.tsx +1 -1
- package/src/plugins/impl/RangeSliderPlugin.tsx +16 -2
- package/src/plugins/impl/RefreshPlugin.tsx +1 -0
- package/src/plugins/impl/SliderPlugin.tsx +6 -8
- package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +387 -1
- package/src/plugins/impl/__tests__/FileBrowserPlugin.test.tsx +45 -0
- package/src/plugins/impl/__tests__/RangeSliderPlugin.test.tsx +109 -0
- package/src/plugins/impl/__tests__/SliderPlugin.test.tsx +24 -3
- package/src/plugins/impl/anywidget/__tests__/registry.test.ts +153 -0
- package/src/plugins/impl/anywidget/__tests__/widget-binding.test.ts +241 -2
- package/src/plugins/impl/anywidget/model.ts +3 -1
- package/src/plugins/impl/anywidget/registry.ts +18 -14
- package/src/plugins/impl/anywidget/runtime.ts +21 -9
- package/src/plugins/impl/anywidget/widget-binding.ts +160 -82
- package/src/plugins/impl/chat/__tests__/chat-prompts.test.tsx +93 -0
- package/src/plugins/impl/chat/chat-ui.tsx +14 -11
- package/src/plugins/impl/code/any-language-editor.tsx +2 -2
- package/src/plugins/impl/data-editor/glide-data-editor.tsx +8 -5
- package/src/plugins/impl/data-editor/glide-utils.ts +3 -1
- package/src/plugins/impl/data-explorer/ConnectedDataExplorerComponent.tsx +1 -1
- package/src/plugins/impl/data-explorer/components/column-summary.tsx +4 -2
- package/src/plugins/impl/data-explorer/state/reducer.ts +1 -1
- package/src/plugins/impl/data-frames/DataFramePlugin.tsx +1 -2
- package/src/plugins/impl/data-frames/__tests__/sidebar.test.tsx +160 -0
- package/src/plugins/impl/data-frames/forms/__tests__/__snapshots__/form.test.tsx.snap +66 -66
- package/src/plugins/impl/data-frames/panel.tsx +43 -16
- package/src/plugins/impl/mpl-interactive/MplInteractivePlugin.tsx +5 -3
- package/src/plugins/impl/panel/PanelPlugin.tsx +16 -17
- package/src/plugins/impl/panel/__tests__/utils.test.ts +39 -1
- package/src/plugins/impl/panel/utils.ts +17 -0
- package/src/plugins/impl/plotly/PlotlyPlugin.tsx +2 -5
- package/src/plugins/impl/plotly/selection.ts +25 -19
- package/src/plugins/impl/vega/VegaPlugin.tsx +2 -3
- package/src/plugins/impl/vega/make-selectable.ts +4 -7
- package/src/plugins/layout/RoutesPlugin.tsx +1 -1
- package/src/stories/__tests__/editor-story.test.tsx +33 -0
- package/src/stories/button.stories.tsx +1 -0
- package/src/stories/cell.stories.tsx +2 -5
- package/src/stories/data-table.stories.tsx +31 -0
- package/src/stories/editor.stories.tsx +24 -17
- package/src/stories/form-wrapper.stories.tsx +2 -2
- package/src/stories/layout/vertical/one-column.stories.tsx +28 -23
- package/src/stories/runtime-story.tsx +23 -17
- package/src/stories/select.stories.tsx +2 -2
- package/src/stories/switchable-multi-select.stories.tsx +1 -1
- package/src/utils/__tests__/arrays.test.ts +35 -0
- package/src/utils/__tests__/batch-requests.test.ts +3 -3
- package/src/utils/__tests__/{createReducer.test.ts → createReducer.test.tsx} +29 -0
- package/src/utils/__tests__/numbers.test.ts +105 -1
- package/src/utils/__tests__/objects.test.ts +21 -0
- package/src/utils/arrays.ts +11 -2
- package/src/utils/blob.ts +1 -1
- package/src/utils/createReducer.ts +53 -26
- package/src/utils/maps.ts +0 -3
- package/src/utils/numbers.ts +79 -18
- package/src/utils/objects.ts +4 -9
- package/src/utils/once.ts +19 -19
- package/src/utils/tracer.ts +9 -5
- package/src/utils/zod-utils.ts +1 -3
- package/dist/any-language-editor-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/code-visibility-DPm4oWK1.js +0 -138
- package/dist/dropdown-menu-DHP9P4VE.js +0 -4678
- package/dist/feedback-button-DVgw4Y7F.js +0 -379
- package/dist/html-to-image-BdA59IUD.js +0 -3251
- package/dist/label-DoysmLMW.js +0 -281
- package/dist/mermaid-HWGCJPDP-ByCsZnYF.js +0 -2
- package/dist/objectWithoutPropertiesLoose-BRDcTsfB.js +0 -23
- package/dist/precisionRound-DMIVQrt2.js +0 -97
- package/dist/select-DemoKT4G.js +0 -1344
- package/dist/toDate-B5mIgxwZ.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/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/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 -211
- 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 -1943
- package/src/core/host/resources/resource.ts +0 -59
- package/src/core/host/stream.ts +0 -213
- 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,10 +4,11 @@ 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,
|
|
@@ -17,16 +18,12 @@ import {
|
|
|
17
18
|
import { MockNotebook } from "@/__mocks__/notebook";
|
|
18
19
|
import { cellId } from "@/__tests__/branded";
|
|
19
20
|
import type { CellHandle } from "@/components/editor/notebook-cell";
|
|
20
|
-
import { CellId, SCRATCH_CELL_ID
|
|
21
|
+
import { CellId, SCRATCH_CELL_ID } from "@/core/cells/ids";
|
|
21
22
|
import { foldAllBulk, unfoldAllBulk } from "@/core/codemirror/editing/commands";
|
|
22
23
|
import { adaptiveLanguageConfiguration } from "@/core/codemirror/language/extension";
|
|
23
24
|
import { OverridingHotkeyProvider } from "@/core/hotkeys/hotkeys";
|
|
24
25
|
import type { OutputMessage } from "@/core/kernel/messages";
|
|
25
|
-
import {
|
|
26
|
-
type CollapsibleTree,
|
|
27
|
-
MultiColumn,
|
|
28
|
-
type CellColumnId,
|
|
29
|
-
} from "@/utils/id-tree";
|
|
26
|
+
import type { CellConfig } from "@/core/network/types";
|
|
30
27
|
import type { Seconds } from "@/utils/time";
|
|
31
28
|
import {
|
|
32
29
|
exportedForTesting,
|
|
@@ -34,13 +31,12 @@ import {
|
|
|
34
31
|
type NotebookState,
|
|
35
32
|
notebookAtom,
|
|
36
33
|
} from "../cells";
|
|
37
|
-
import { exportedForTesting as documentTransactionTestExports } from "../document-changes";
|
|
38
34
|
import {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
35
|
+
type CellData,
|
|
36
|
+
createCell,
|
|
37
|
+
createCellConfig,
|
|
38
|
+
createCellRuntimeState,
|
|
39
|
+
} from "../types";
|
|
44
40
|
|
|
45
41
|
vi.mock("@/core/codemirror/editing/commands", () => ({
|
|
46
42
|
foldAllBulk: vi.fn(),
|
|
@@ -60,9 +56,6 @@ vi.mock("../scrollCellIntoView", async (importOriginal) => {
|
|
|
60
56
|
};
|
|
61
57
|
});
|
|
62
58
|
|
|
63
|
-
const FIRST_COLUMN = 0;
|
|
64
|
-
const SECOND_COLUMN = 1;
|
|
65
|
-
|
|
66
59
|
const { initialNotebookState, reducer, createActions } = exportedForTesting;
|
|
67
60
|
|
|
68
61
|
function formatCells(notebook: NotebookState) {
|
|
@@ -78,14 +71,7 @@ function formatCells(notebook: NotebookState) {
|
|
|
78
71
|
const cells = cellsId.map((cellId) => cellData[cellId]);
|
|
79
72
|
return cells.map((cell) => printCell(cell)).join("\n\n");
|
|
80
73
|
};
|
|
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));
|
|
74
|
+
return wrap(printCells(cellIds));
|
|
89
75
|
}
|
|
90
76
|
|
|
91
77
|
function createEditor(content: string) {
|
|
@@ -121,8 +107,7 @@ describe("cell reducer", () => {
|
|
|
121
107
|
let cells: ReturnType<typeof flattenTopLevelNotebookCells>;
|
|
122
108
|
let firstCellId: CellId;
|
|
123
109
|
|
|
124
|
-
const
|
|
125
|
-
state = reducer(state, action);
|
|
110
|
+
const mountEditors = () => {
|
|
126
111
|
for (const [cellIdString, handle] of Object.entries(state.cellHandles)) {
|
|
127
112
|
// @ts-expect-error - Typescript Object.entries doesn't know that keys are CellId
|
|
128
113
|
const cid: CellId = cellIdString;
|
|
@@ -136,7 +121,94 @@ describe("cell reducer", () => {
|
|
|
136
121
|
}
|
|
137
122
|
}
|
|
138
123
|
cells = flattenTopLevelNotebookCells(state);
|
|
139
|
-
}
|
|
124
|
+
};
|
|
125
|
+
const actions = createActions((action) => {
|
|
126
|
+
state = reducer(state, action);
|
|
127
|
+
mountEditors();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
/** Put a cell into the notebook the way the document would. */
|
|
131
|
+
function seedCell({
|
|
132
|
+
cellId,
|
|
133
|
+
before,
|
|
134
|
+
code = "",
|
|
135
|
+
name,
|
|
136
|
+
config,
|
|
137
|
+
newCellId,
|
|
138
|
+
hideCode,
|
|
139
|
+
autoFocus = true,
|
|
140
|
+
}: {
|
|
141
|
+
cellId: CellId | "__end__";
|
|
142
|
+
before: boolean;
|
|
143
|
+
code?: string;
|
|
144
|
+
name?: string;
|
|
145
|
+
config?: CellConfig;
|
|
146
|
+
newCellId?: CellId;
|
|
147
|
+
hideCode?: boolean;
|
|
148
|
+
autoFocus?: boolean;
|
|
149
|
+
}): CellId {
|
|
150
|
+
const cellIndex =
|
|
151
|
+
cellId === "__end__"
|
|
152
|
+
? state.cellIds.length
|
|
153
|
+
: state.cellIds.indexOf(cellId);
|
|
154
|
+
const id = newCellId ?? CellId.create();
|
|
155
|
+
const cellIds = [...state.cellIds];
|
|
156
|
+
cellIds.splice(before ? cellIndex : cellIndex + 1, 0, id);
|
|
157
|
+
state = {
|
|
158
|
+
...state,
|
|
159
|
+
cellIds,
|
|
160
|
+
cellData: {
|
|
161
|
+
...state.cellData,
|
|
162
|
+
[id]: createCell({
|
|
163
|
+
id,
|
|
164
|
+
code,
|
|
165
|
+
name,
|
|
166
|
+
config: createCellConfig({
|
|
167
|
+
...config,
|
|
168
|
+
...(hideCode != null && { hide_code: hideCode }),
|
|
169
|
+
}),
|
|
170
|
+
edited: Boolean(code),
|
|
171
|
+
}),
|
|
172
|
+
},
|
|
173
|
+
cellRuntime: { ...state.cellRuntime, [id]: createCellRuntimeState() },
|
|
174
|
+
cellHandles: { ...state.cellHandles, [id]: createRef() },
|
|
175
|
+
scrollKey: autoFocus ? id : null,
|
|
176
|
+
};
|
|
177
|
+
mountEditors();
|
|
178
|
+
return id;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** Edit a cell's code as the document delivers it. */
|
|
182
|
+
function editCode(cellId: CellId, code: string) {
|
|
183
|
+
const cells = state.cellIds.map((id) => {
|
|
184
|
+
const cell = state.cellData[id];
|
|
185
|
+
return {
|
|
186
|
+
id,
|
|
187
|
+
code: id === cellId ? code : cell.code,
|
|
188
|
+
name: cell.name,
|
|
189
|
+
config: {
|
|
190
|
+
column: cell.config.column ?? null,
|
|
191
|
+
disabled: cell.config.disabled ?? false,
|
|
192
|
+
hide_code: cell.config.hide_code ?? false,
|
|
193
|
+
expand_output: cell.config.expand_output ?? false,
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
});
|
|
197
|
+
actions.reconcileDocument({
|
|
198
|
+
order: state.cellIds,
|
|
199
|
+
cells: new Map(cells.map((cell) => [cell.id, cell])),
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** Format a cell's code, as the editor reports it. */
|
|
204
|
+
function formatCode(cellId: CellId, code: string) {
|
|
205
|
+
actions.codeFormatted({
|
|
206
|
+
cellId,
|
|
207
|
+
before: state.cellData[cellId].code,
|
|
208
|
+
after: code,
|
|
209
|
+
});
|
|
210
|
+
editCode(cellId, code);
|
|
211
|
+
}
|
|
140
212
|
|
|
141
213
|
let i = 0;
|
|
142
214
|
const originalCreate = CellId.create.bind(CellId);
|
|
@@ -151,640 +223,19 @@ describe("cell reducer", () => {
|
|
|
151
223
|
i = 0;
|
|
152
224
|
|
|
153
225
|
state = initialNotebookState();
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
afterEach(() => {
|
|
160
|
-
documentTransactionTestExports.cancelPendingChanges();
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
afterAll(() => {
|
|
164
|
-
CellId.create = originalCreate;
|
|
165
|
-
});
|
|
166
|
-
|
|
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
|
-
it("can update a cell", () => {
|
|
343
|
-
actions.updateCellCode({
|
|
344
|
-
cellId: firstCellId,
|
|
345
|
-
code: "import numpy as np",
|
|
346
|
-
formattingChange: false,
|
|
347
|
-
});
|
|
348
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
349
|
-
"
|
|
350
|
-
[0] 'import numpy as np'
|
|
351
|
-
"
|
|
352
|
-
`);
|
|
353
|
-
});
|
|
354
|
-
|
|
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] ''
|
|
226
|
+
seedCell({ cellId: "__end__", before: false });
|
|
227
|
+
firstCellId = state.cellIds[0];
|
|
228
|
+
});
|
|
775
229
|
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
230
|
+
afterAll(() => {
|
|
231
|
+
CellId.create = originalCreate;
|
|
232
|
+
});
|
|
779
233
|
|
|
780
|
-
|
|
234
|
+
it("can update a cell", () => {
|
|
235
|
+
editCode(firstCellId, "import numpy as np");
|
|
781
236
|
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
782
237
|
"
|
|
783
|
-
[0] ''
|
|
784
|
-
|
|
785
|
-
[1] ''
|
|
786
|
-
|
|
787
|
-
[3] ''
|
|
238
|
+
[0] 'import numpy as np'
|
|
788
239
|
"
|
|
789
240
|
`);
|
|
790
241
|
});
|
|
@@ -820,11 +271,7 @@ describe("cell reducer", () => {
|
|
|
820
271
|
stale_inputs: null,
|
|
821
272
|
timestamp: 1,
|
|
822
273
|
});
|
|
823
|
-
|
|
824
|
-
cellId: firstCellId,
|
|
825
|
-
code: "print('new code')",
|
|
826
|
-
formattingChange: false,
|
|
827
|
-
});
|
|
274
|
+
editCode(firstCellId, "print('new code')");
|
|
828
275
|
actions.prepareForRun({ cellId: firstCellId });
|
|
829
276
|
actions.handleCellMessage({
|
|
830
277
|
cell_id: firstCellId,
|
|
@@ -852,11 +299,7 @@ describe("cell reducer", () => {
|
|
|
852
299
|
expect(cell).toMatchSnapshot("initial state");
|
|
853
300
|
|
|
854
301
|
// Update code
|
|
855
|
-
|
|
856
|
-
cellId: firstCellId,
|
|
857
|
-
code: "import marimo as mo",
|
|
858
|
-
formattingChange: false,
|
|
859
|
-
});
|
|
302
|
+
editCode(firstCellId, "import marimo as mo");
|
|
860
303
|
cell = cells[0];
|
|
861
304
|
expect(cell.status).toBe("idle");
|
|
862
305
|
expect(cell.lastCodeRun).toBe(null);
|
|
@@ -961,32 +404,20 @@ describe("cell reducer", () => {
|
|
|
961
404
|
// EDITING BACK AND FORTH
|
|
962
405
|
/////////////////
|
|
963
406
|
// Update code again
|
|
964
|
-
|
|
965
|
-
cellId: firstCellId,
|
|
966
|
-
code: "import marimo as mo\nimport numpy",
|
|
967
|
-
formattingChange: false,
|
|
968
|
-
});
|
|
407
|
+
editCode(firstCellId, "import marimo as mo\nimport numpy");
|
|
969
408
|
cell = cells[0];
|
|
970
409
|
expect(cell.status).toBe("idle");
|
|
971
410
|
expect(cell.edited).toBe(true);
|
|
972
411
|
expect(cell).toMatchSnapshot("code edited again");
|
|
973
412
|
|
|
974
413
|
// Update code should be unedited
|
|
975
|
-
|
|
976
|
-
cellId: firstCellId,
|
|
977
|
-
code: "import marimo as mo",
|
|
978
|
-
formattingChange: false,
|
|
979
|
-
});
|
|
414
|
+
editCode(firstCellId, "import marimo as mo");
|
|
980
415
|
cell = cells[0];
|
|
981
416
|
expect(cell.edited).toBe(false);
|
|
982
417
|
expect(cell).toMatchSnapshot("code reverted");
|
|
983
418
|
|
|
984
419
|
// Update code should be edited again
|
|
985
|
-
|
|
986
|
-
cellId: firstCellId,
|
|
987
|
-
code: "import marimo as mo\nimport numpy",
|
|
988
|
-
formattingChange: false,
|
|
989
|
-
});
|
|
420
|
+
editCode(firstCellId, "import marimo as mo\nimport numpy");
|
|
990
421
|
cell = cells[0];
|
|
991
422
|
expect(cell.status).toBe("idle");
|
|
992
423
|
expect(cell.lastCodeRun).toBe("import marimo as mo");
|
|
@@ -1110,11 +541,7 @@ describe("cell reducer", () => {
|
|
|
1110
541
|
expect(cell).toMatchSnapshot("initial state");
|
|
1111
542
|
|
|
1112
543
|
// Update code
|
|
1113
|
-
|
|
1114
|
-
cellId: firstCellId,
|
|
1115
|
-
code: "import marimo as mo",
|
|
1116
|
-
formattingChange: false,
|
|
1117
|
-
});
|
|
544
|
+
editCode(firstCellId, "import marimo as mo");
|
|
1118
545
|
cell = cells[0];
|
|
1119
546
|
expect(cell.status).toBe("idle");
|
|
1120
547
|
expect(cell.lastCodeRun).toBe(null);
|
|
@@ -1166,23 +593,15 @@ describe("cell reducer", () => {
|
|
|
1166
593
|
|
|
1167
594
|
it("can run a stale cell", () => {
|
|
1168
595
|
// Update code of first cell
|
|
1169
|
-
|
|
1170
|
-
cellId: firstCellId,
|
|
1171
|
-
code: "import marimo as mo",
|
|
1172
|
-
formattingChange: false,
|
|
1173
|
-
});
|
|
596
|
+
editCode(firstCellId, "import marimo as mo");
|
|
1174
597
|
// Add cell
|
|
1175
|
-
|
|
598
|
+
seedCell({
|
|
1176
599
|
cellId: firstCellId,
|
|
1177
600
|
before: false,
|
|
1178
601
|
});
|
|
1179
602
|
const secondCell = cells[1].id;
|
|
1180
603
|
// Update code
|
|
1181
|
-
|
|
1182
|
-
code: "mo.slider()",
|
|
1183
|
-
cellId: secondCell,
|
|
1184
|
-
formattingChange: false,
|
|
1185
|
-
});
|
|
604
|
+
editCode(secondCell, "mo.slider()");
|
|
1186
605
|
|
|
1187
606
|
// Prepare for run
|
|
1188
607
|
actions.prepareForRun({
|
|
@@ -1226,11 +645,7 @@ describe("cell reducer", () => {
|
|
|
1226
645
|
|
|
1227
646
|
it("can format code and update cell", () => {
|
|
1228
647
|
const firstCellId = cells[0].id;
|
|
1229
|
-
|
|
1230
|
-
cellId: firstCellId,
|
|
1231
|
-
code: "import marimo as mo",
|
|
1232
|
-
formattingChange: false,
|
|
1233
|
-
});
|
|
648
|
+
editCode(firstCellId, "import marimo as mo");
|
|
1234
649
|
let cell = cells[0];
|
|
1235
650
|
expect(cell.status).toBe("idle");
|
|
1236
651
|
expect(cell.lastCodeRun).toBe(null);
|
|
@@ -1266,51 +681,16 @@ describe("cell reducer", () => {
|
|
|
1266
681
|
expect(cell.lastCodeRun).toBe("import marimo as mo");
|
|
1267
682
|
|
|
1268
683
|
// Format code
|
|
1269
|
-
|
|
1270
|
-
cellId: firstCellId,
|
|
1271
|
-
code: "import marimo as mo",
|
|
1272
|
-
formattingChange: true,
|
|
1273
|
-
});
|
|
684
|
+
formatCode(firstCellId, "import marimo as mo");
|
|
1274
685
|
cell = cells[0];
|
|
1275
686
|
expect(cell.status).toBe("idle");
|
|
1276
687
|
expect(cell.lastCodeRun).toBe("import marimo as mo");
|
|
1277
688
|
expect(cell.edited).toBe(false);
|
|
1278
689
|
});
|
|
1279
690
|
|
|
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
691
|
it("can run a stopped cell", () => {
|
|
1308
692
|
// Update code of first cell
|
|
1309
|
-
|
|
1310
|
-
cellId: firstCellId,
|
|
1311
|
-
code: "mo.md('This has an ancestor that was stopped')",
|
|
1312
|
-
formattingChange: false,
|
|
1313
|
-
});
|
|
693
|
+
editCode(firstCellId, "mo.md('This has an ancestor that was stopped')");
|
|
1314
694
|
|
|
1315
695
|
// Prepare for run
|
|
1316
696
|
actions.prepareForRun({
|
|
@@ -1579,7 +959,9 @@ describe("cell reducer", () => {
|
|
|
1579
959
|
response: secret,
|
|
1580
960
|
});
|
|
1581
961
|
|
|
1582
|
-
expect(cells[0].consoleOutputs).toEqual([
|
|
962
|
+
expect(cells[0].consoleOutputs).toEqual([
|
|
963
|
+
{ ...prompt, id: 1, response: "" },
|
|
964
|
+
]);
|
|
1583
965
|
expect(JSON.stringify(cells[0].consoleOutputs)).not.toContain(secret);
|
|
1584
966
|
});
|
|
1585
967
|
|
|
@@ -1684,623 +1066,53 @@ describe("cell reducer", () => {
|
|
|
1684
1066
|
console: [], // Backend sends an empty array to clearu
|
|
1685
1067
|
status: "running",
|
|
1686
1068
|
stale_inputs: null,
|
|
1687
|
-
timestamp: new Date(20).getTime() as Seconds,
|
|
1688
|
-
});
|
|
1689
|
-
cell = cells[0];
|
|
1690
|
-
expect(cell.status).toBe("running");
|
|
1691
|
-
expect(cell.consoleOutputs).toMatchObject([]);
|
|
1692
|
-
expect(cell).toMatchSnapshot("console cleared while running");
|
|
1693
|
-
|
|
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.status).toBe("idle");
|
|
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
|
-
]);
|
|
1069
|
+
timestamp: new Date(20).getTime() as Seconds,
|
|
1070
|
+
});
|
|
1071
|
+
cell = cells[0];
|
|
1072
|
+
expect(cell.status).toBe("running");
|
|
1073
|
+
expect(cell.consoleOutputs).toMatchObject([]);
|
|
1074
|
+
expect(cell).toMatchSnapshot("console cleared while running");
|
|
2248
1075
|
|
|
2249
|
-
//
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
);
|
|
2258
|
-
|
|
2259
|
-
expect(
|
|
2260
|
-
|
|
2261
|
-
).
|
|
1076
|
+
// Add console
|
|
1077
|
+
actions.handleCellMessage({
|
|
1078
|
+
cell_id: firstCellId,
|
|
1079
|
+
output: undefined,
|
|
1080
|
+
console: [STDOUT],
|
|
1081
|
+
status: "idle",
|
|
1082
|
+
stale_inputs: null,
|
|
1083
|
+
timestamp: new Date(22).getTime() as Seconds,
|
|
1084
|
+
});
|
|
1085
|
+
cell = cells[0];
|
|
1086
|
+
expect(cell.consoleOutputs).toMatchObject([STDOUT]);
|
|
1087
|
+
expect(cell.status).toBe("idle");
|
|
1088
|
+
expect(cell).toMatchSnapshot("console received while idle");
|
|
2262
1089
|
});
|
|
2263
1090
|
|
|
2264
|
-
it("can
|
|
2265
|
-
actions.
|
|
2266
|
-
|
|
2267
|
-
actions.collapseCell({ cellId: firstCellId });
|
|
1091
|
+
it("can fold and unfold all cells", () => {
|
|
1092
|
+
actions.foldAll();
|
|
1093
|
+
expect(foldAllBulk).toHaveBeenCalled();
|
|
2268
1094
|
|
|
2269
|
-
actions.
|
|
2270
|
-
expect(
|
|
2271
|
-
false,
|
|
2272
|
-
);
|
|
1095
|
+
actions.unfoldAll();
|
|
1096
|
+
expect(unfoldAllBulk).toHaveBeenCalled();
|
|
2273
1097
|
});
|
|
2274
1098
|
|
|
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");
|
|
1099
|
+
it("can clear logs", () => {
|
|
1100
|
+
state.cellLogs = [
|
|
1101
|
+
{
|
|
1102
|
+
level: "stderr",
|
|
1103
|
+
message: "log1",
|
|
1104
|
+
timestamp: 0,
|
|
1105
|
+
source: { type: "cell", cellId: firstCellId },
|
|
1106
|
+
},
|
|
1107
|
+
{
|
|
1108
|
+
level: "stderr",
|
|
1109
|
+
message: "log1",
|
|
1110
|
+
timestamp: 0,
|
|
1111
|
+
source: { type: "cell", cellId: firstCellId },
|
|
1112
|
+
},
|
|
1113
|
+
];
|
|
1114
|
+
actions.clearLogs();
|
|
1115
|
+
expect(state.cellLogs).toEqual([]);
|
|
2304
1116
|
});
|
|
2305
1117
|
|
|
2306
1118
|
it("can handle multiple console outputs", () => {
|
|
@@ -2339,407 +1151,6 @@ describe("cell reducer", () => {
|
|
|
2339
1151
|
`);
|
|
2340
1152
|
});
|
|
2341
1153
|
|
|
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
1154
|
it("can clear output of a single cell", () => {
|
|
2744
1155
|
// Set up initial state with output
|
|
2745
1156
|
actions.handleCellMessage({
|
|
@@ -2828,8 +1239,8 @@ describe("cell reducer", () => {
|
|
|
2828
1239
|
|
|
2829
1240
|
it("can clear output of all cells", () => {
|
|
2830
1241
|
// Create multiple cells with output
|
|
2831
|
-
|
|
2832
|
-
const secondCellId = state.cellIds
|
|
1242
|
+
seedCell({ cellId: firstCellId, before: false });
|
|
1243
|
+
const secondCellId = state.cellIds[1];
|
|
2833
1244
|
|
|
2834
1245
|
// Add output to both cells
|
|
2835
1246
|
const outputMessage = {
|
|
@@ -2870,126 +1281,14 @@ describe("cell reducer", () => {
|
|
|
2870
1281
|
|
|
2871
1282
|
// Verify all outputs are cleared
|
|
2872
1283
|
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 });
|
|
2978
|
-
|
|
2979
|
-
// Create a new setup cell
|
|
2980
|
-
actions.addSetupCellIfDoesntExist({ code: "# New code" });
|
|
2981
|
-
|
|
2982
|
-
// Check that the new setup cell was created
|
|
2983
|
-
expect(state.cellData[SETUP_CELL_ID].id).toBe(SETUP_CELL_ID);
|
|
2984
|
-
expect(state.cellData[SETUP_CELL_ID].name).toBe("setup");
|
|
2985
|
-
expect(state.cellData[SETUP_CELL_ID].code).toBe("# New code");
|
|
2986
|
-
expect(state.cellData[SETUP_CELL_ID].edited).toBe(true);
|
|
2987
|
-
expect(state.cellIds.inOrderIds).toContain(SETUP_CELL_ID);
|
|
1284
|
+
expect(state.cellRuntime[firstCellId].consoleOutputs).toEqual([]);
|
|
1285
|
+
expect(state.cellRuntime[secondCellId].output).toBeNull();
|
|
1286
|
+
expect(state.cellRuntime[secondCellId].consoleOutputs).toEqual([]);
|
|
2988
1287
|
});
|
|
2989
1288
|
|
|
2990
1289
|
it("can clear all outputs", () => {
|
|
2991
1290
|
// Add a cell and give it output
|
|
2992
|
-
|
|
1291
|
+
seedCell({
|
|
2993
1292
|
cellId: firstCellId,
|
|
2994
1293
|
before: false,
|
|
2995
1294
|
});
|
|
@@ -3017,522 +1316,13 @@ describe("cell reducer", () => {
|
|
|
3017
1316
|
expect(state.cellRuntime[cell2Id].output).toBeNull();
|
|
3018
1317
|
});
|
|
3019
1318
|
|
|
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
1319
|
describe("releaseCellAtoms", () => {
|
|
3530
1320
|
it("atom families cache atoms until explicitly removed", () => {
|
|
3531
1321
|
const { cellDataAtom, cellRuntimeAtom, cellHandleAtom } =
|
|
3532
1322
|
exportedForTesting;
|
|
3533
1323
|
|
|
3534
|
-
|
|
3535
|
-
const newCellId = state.cellIds
|
|
1324
|
+
seedCell({ cellId: firstCellId, before: false });
|
|
1325
|
+
const newCellId = state.cellIds[1];
|
|
3536
1326
|
|
|
3537
1327
|
const dataAtom1 = cellDataAtom(newCellId);
|
|
3538
1328
|
const runtimeAtom1 = cellRuntimeAtom(newCellId);
|
|
@@ -3547,116 +1337,6 @@ describe("cell reducer", () => {
|
|
|
3547
1337
|
expect(runtimeAtom2).toBe(runtimeAtom1);
|
|
3548
1338
|
expect(handleAtom2).toBe(handleAtom1);
|
|
3549
1339
|
});
|
|
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
|
-
});
|
|
3641
|
-
|
|
3642
|
-
it("returns false when hide_code is false and cell is untouched", () => {
|
|
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
|
-
};
|
|
3656
|
-
|
|
3657
|
-
expect(exportedForTesting.isCellCodeHidden(testState, testCellId)).toBe(
|
|
3658
|
-
false,
|
|
3659
|
-
);
|
|
3660
1340
|
});
|
|
3661
1341
|
});
|
|
3662
1342
|
|
|
@@ -3886,31 +1566,9 @@ describe("createTracebackInfoAtom", () => {
|
|
|
3886
1566
|
});
|
|
3887
1567
|
});
|
|
3888
1568
|
|
|
3889
|
-
describe("
|
|
1569
|
+
describe("kernel-ready", () => {
|
|
3890
1570
|
const CELL_A = cellId("A");
|
|
3891
1571
|
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
1572
|
|
|
3915
1573
|
const hydratedState = () =>
|
|
3916
1574
|
MockNotebook.notebookState({
|
|
@@ -3940,44 +1598,97 @@ describe("setCells snapshot preservation", () => {
|
|
|
3940
1598
|
},
|
|
3941
1599
|
});
|
|
3942
1600
|
|
|
3943
|
-
|
|
3944
|
-
const
|
|
3945
|
-
|
|
1601
|
+
it("setLastRun records what the kernel last ran", () => {
|
|
1602
|
+
const state = hydratedState();
|
|
1603
|
+
const next = exportedForTesting.reducer(state, {
|
|
1604
|
+
type: "setLastRun",
|
|
1605
|
+
payload: {
|
|
1606
|
+
lastCodeRun: { [CELL_A]: "0" },
|
|
1607
|
+
lastExecutionTime: { [CELL_A]: 5 },
|
|
1608
|
+
},
|
|
1609
|
+
});
|
|
1610
|
+
|
|
1611
|
+
expect(next.cellData[CELL_A]).toMatchObject({
|
|
1612
|
+
lastCodeRun: "0",
|
|
1613
|
+
lastExecutionTime: 5,
|
|
1614
|
+
edited: true,
|
|
1615
|
+
});
|
|
1616
|
+
expect(next.cellData[CELL_B]).toMatchObject({
|
|
1617
|
+
lastCodeRun: null,
|
|
1618
|
+
lastExecutionTime: null,
|
|
1619
|
+
edited: false,
|
|
1620
|
+
});
|
|
1621
|
+
expect(next.cellRuntime).toBe(state.cellRuntime);
|
|
3946
1622
|
});
|
|
3947
1623
|
|
|
3948
|
-
it("
|
|
1624
|
+
it("resetCellRuntimes starts every cell's runtime over", () => {
|
|
3949
1625
|
const next = exportedForTesting.reducer(hydratedState(), {
|
|
3950
|
-
type: "
|
|
3951
|
-
payload:
|
|
1626
|
+
type: "resetCellRuntimes",
|
|
1627
|
+
payload: undefined,
|
|
3952
1628
|
});
|
|
3953
1629
|
|
|
3954
|
-
expect(next.cellRuntime[CELL_A].output).
|
|
3955
|
-
|
|
1630
|
+
expect(next.cellRuntime[CELL_A].output).toBeNull();
|
|
1631
|
+
expect(next.cellRuntime[CELL_B].consoleOutputs).toEqual([]);
|
|
1632
|
+
});
|
|
1633
|
+
|
|
1634
|
+
it("resetCellRuntimes starts the scratch cell over too", () => {
|
|
1635
|
+
const state = MockNotebook.notebookState({
|
|
1636
|
+
cellData: { [CELL_A]: { id: CELL_A, code: "1" } },
|
|
1637
|
+
cellRuntime: {
|
|
1638
|
+
[SCRATCH_CELL_ID]: {
|
|
1639
|
+
output: {
|
|
1640
|
+
channel: "output",
|
|
1641
|
+
mimetype: "text/plain",
|
|
1642
|
+
data: "scratch",
|
|
1643
|
+
timestamp: 0,
|
|
1644
|
+
},
|
|
1645
|
+
},
|
|
1646
|
+
},
|
|
3956
1647
|
});
|
|
1648
|
+
const next = exportedForTesting.reducer(state, {
|
|
1649
|
+
type: "resetCellRuntimes",
|
|
1650
|
+
payload: undefined,
|
|
1651
|
+
});
|
|
1652
|
+
|
|
1653
|
+
expect(next.cellRuntime[SCRATCH_CELL_ID].output).toBeNull();
|
|
3957
1654
|
});
|
|
3958
1655
|
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
1656
|
+
describe("resetCellRuntimes in WASM", () => {
|
|
1657
|
+
beforeEach(async () => {
|
|
1658
|
+
const { isWasm } = await import("@/core/wasm/utils");
|
|
1659
|
+
vi.mocked(isWasm).mockReturnValue(true);
|
|
3963
1660
|
});
|
|
3964
1661
|
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
1662
|
+
afterEach(async () => {
|
|
1663
|
+
const { isWasm } = await import("@/core/wasm/utils");
|
|
1664
|
+
vi.mocked(isWasm).mockReturnValue(false);
|
|
3968
1665
|
});
|
|
3969
|
-
});
|
|
3970
1666
|
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
1667
|
+
it("keeps hydrated outputs and console", () => {
|
|
1668
|
+
const next = exportedForTesting.reducer(hydratedState(), {
|
|
1669
|
+
type: "resetCellRuntimes",
|
|
1670
|
+
payload: undefined,
|
|
1671
|
+
});
|
|
1672
|
+
|
|
1673
|
+
expect(next.cellRuntime[CELL_A].output).toMatchObject({
|
|
1674
|
+
data: "hydrated-A",
|
|
1675
|
+
});
|
|
1676
|
+
expect(next.cellRuntime[CELL_B].consoleOutputs[0]).toMatchObject({
|
|
1677
|
+
data: "hydrated-B-stdout",
|
|
1678
|
+
});
|
|
3976
1679
|
});
|
|
3977
1680
|
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
1681
|
+
it("starts cells with no prior runtime over", () => {
|
|
1682
|
+
const state = MockNotebook.notebookState({
|
|
1683
|
+
cellData: { [CELL_A]: { id: CELL_A, code: "1" } },
|
|
1684
|
+
});
|
|
1685
|
+
const next = exportedForTesting.reducer(state, {
|
|
1686
|
+
type: "resetCellRuntimes",
|
|
1687
|
+
payload: undefined,
|
|
1688
|
+
});
|
|
1689
|
+
|
|
1690
|
+
expect(next.cellRuntime[CELL_A].output).toBeNull();
|
|
1691
|
+
expect(next.cellRuntime[CELL_A].consoleOutputs).toEqual([]);
|
|
1692
|
+
});
|
|
3982
1693
|
});
|
|
3983
1694
|
});
|