@marimo-team/islands 0.25.3-dev17 → 0.25.3-dev173
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{ConnectedDataExplorerComponent-CNMliVzw.js → ConnectedDataExplorerComponent-Le_OBfmt.js} +21 -19
- package/dist/{ErrorBoundary-jaGao4LA.js → ErrorBoundary-wT3f3iJ1.js} +19 -19
- package/dist/{abnfDiagram-VCTEODGH-D-Ez91gV.js → abnfDiagram-VCTEODGH-Tf1ghyQQ.js} +2 -2
- package/dist/any-language-editor-B92WMzoC.js +75 -0
- package/dist/{architectureDiagram-5GKGNRK7-CTpcNtO8.js → architectureDiagram-5GKGNRK7-xlnbTG0T.js} +1 -1
- package/dist/{arrays-CN1djC4k.js → arrays-R6JsImiT.js} +8 -0
- package/dist/assets/__vite-browser-external-DVeFf2e7.js +1 -0
- package/dist/assets/worker-C1pfNBsx.js +113 -0
- package/dist/{button-CvjkwdSk.js → button-BQRdqQi1.js} +199 -192
- package/dist/{capabilities-CQ5IjhzL.js → capabilities-CI4xUPsp.js} +2 -2
- package/dist/{chat-ui-DaHynk8g.js → chat-ui-CuORr6fL.js} +1142 -1141
- package/dist/{check-EDwSwP89.js → check-C9Mp9eCK.js} +1 -1
- package/dist/{chunk-IMKFNOWR-DGxi-zeo.js → chunk-IMKFNOWR-Cmd4a5jy.js} +1 -1
- package/dist/{chunk-SHT3W25Y-CV9J3rAB.js → chunk-SHT3W25Y-mSNp-WpO.js} +1 -1
- package/dist/{chunk-SVP7TREG-BL-9Fqgg.js → chunk-SVP7TREG-CWC49D8X.js} +1 -1
- package/dist/{chunk-TICWLB2K-Cu_XMjDM.js → chunk-TICWLB2K-De9FINtG.js} +1 -1
- package/dist/{chunk-YOKDWASO-DPZk8vhS.js → chunk-YOKDWASO-DKplTrBY.js} +20 -20
- package/dist/{classDiagram-ZZMXUADV-BXXV13Hq.js → classDiagram-ZZMXUADV-DweTkYdp.js} +2 -2
- package/dist/{classDiagram-v2-VYDZK3BY-B7FXJ1Cp.js → classDiagram-v2-VYDZK3BY-A6MR2TiE.js} +2 -2
- package/dist/{code-visibility-Bub8tnaA.js → code-visibility-ZReaeMaF.js} +25 -25
- package/dist/{common-CdT0nyN1.js → common-DCevHg1m.js} +6917 -6738
- package/dist/{copy-icon-Ft7t9bqz.js → copy-icon-De584pZ4.js} +3 -3
- package/dist/{cynefinDiagram-5FMLGOSQ-CH96rwba.js → cynefinDiagram-5FMLGOSQ-BSvo5le-.js} +1 -1
- package/dist/{dagre-GXQ25YYZ-BD3PmpFY.js → dagre-GXQ25YYZ-tXRQslO6.js} +1 -1
- package/dist/{data-grid-overlay-editor-Dbcss6_2.js → data-grid-overlay-editor-CTC-bJus.js} +5 -5
- package/dist/{data-grid-types-BH_r7g7l.js → data-grid-types-0PJ-9N5n.js} +1 -1
- package/dist/{diagram-S7CK7UJ4-BsSJJldB.js → diagram-S7CK7UJ4-r3t5VtwI.js} +1 -1
- package/dist/{diagram-UQ7AKVKN-muQtNZc4.js → diagram-UQ7AKVKN-qgbC8YnT.js} +1 -1
- package/dist/{diagram-VX7I27RA-CdhuZAqR.js → diagram-VX7I27RA-CiHxEwtp.js} +1 -1
- package/dist/{diagram-Z3DM3KII-BD8mXjOj.js → diagram-Z3DM3KII-D95u15Ev.js} +1 -1
- package/dist/{dialog-CEBFZNyc.js → dialog-DVRfQuhD.js} +25 -25
- package/dist/{dist-DpBpA_n1.js → dist-Lm-D-Vr6.js} +3 -3
- package/dist/{dropdown-menu-CP_qCYHC.js → dropdown-menu-DCMbbQ35.js} +1481 -1433
- package/dist/{ebnfDiagram-PWID7BFC-BIN1sweE.js → ebnfDiagram-PWID7BFC-P7e4bryK.js} +2 -2
- package/dist/{erDiagram-RLTQ6QDP-Ctp5cfoN.js → erDiagram-RLTQ6QDP-CUHZcmRj.js} +1 -1
- package/dist/{error-banner-D-WCIiMt.js → error-banner-DxJ3s8YO.js} +86 -86
- package/dist/{error-entries-Bv6bZPF3.js → error-entries-CmWMEsY_.js} +5756 -5643
- package/dist/{feedback-button-B3Wrnsf_.js → feedback-button-DwXkmewo.js} +35 -35
- package/dist/{flowDiagram-HODETNUW-3EfLrlFb.js → flowDiagram-HODETNUW-BGZ95AzI.js} +2 -2
- package/dist/{formats-27CW_UP4.js → formats-DI7HEVv7.js} +3 -3
- package/dist/{ganttDiagram-EL5Y4UJY-BQT16JSc.js → ganttDiagram-EL5Y4UJY-Dh_4cBJZ.js} +2 -2
- package/dist/{glide-data-editor-BDYKJdqK.js → glide-data-editor-BlmcP-lw.js} +370 -368
- package/dist/{glide-utils-DJyqh0EM.js → glide-utils-D98laidb.js} +16 -16
- package/dist/{globals-DGTE1S0_.js → globals-BdRitZ4p.js} +3 -3
- package/dist/{highlighted-body-KPVGNVTW-zwjq21tf.js → highlighted-body-KPVGNVTW-3Ziwfz-V.js} +2 -2
- package/dist/{html-to-image-Bf8QMGi4.js → html-to-image-bpiQh7ND.js} +639 -602
- package/dist/{infoDiagram-27XIBGKW-DipQoHxl.js → infoDiagram-27XIBGKW-BF0YX70D.js} +1 -1
- package/dist/{ishikawaDiagram-5VMMS53U-DgFMjSGz.js → ishikawaDiagram-5VMMS53U-_itsAAn-.js} +1 -1
- package/dist/{kanban-definition-UXKFOSKX-w1ACVjgl.js → kanban-definition-UXKFOSKX-VNW2Beqh.js} +1 -1
- package/dist/{label-BDKF3Afc.js → label-Qvw1E97W.js} +3 -3
- package/dist/{linear-Cnaq3Tfb.js → linear-DWERh7eI.js} +121 -73
- package/dist/{loader-BQOfjTIJ.js → loader-DOVB0Syl.js} +12 -12
- package/dist/main.js +9672 -7674
- package/dist/{mermaid-CHo49Yv-.js → mermaid-CZ6FrClS.js} +38 -38
- package/dist/mermaid-HWGCJPDP-Du3-HPQA.js +2 -0
- package/dist/{mindmap-definition-YA3MSWOX-CrT0KSfX.js → mindmap-definition-YA3MSWOX-DR4ripvd.js} +1 -1
- package/dist/{number-overlay-editor-S8UJY0i5.js → number-overlay-editor-DQG-hFHT.js} +3 -3
- package/dist/{objects-CzUgoMIF.js → objects-DCmMvZtn.js} +1 -3
- package/dist/{once-BtE-wKRL.js → once-eG8Vmvjd.js} +1 -1
- package/dist/{pegDiagram-XKGWAZYB-BqvywNJA.js → pegDiagram-XKGWAZYB-CFr3XlGl.js} +2 -2
- package/dist/{pieDiagram-E7YTZNPT-BfxJ_XCc.js → pieDiagram-E7YTZNPT-j91XYSLY.js} +1 -1
- package/dist/precisionRound-DhAzeRcm.js +49 -0
- package/dist/{process-output-BZg56CCb.js → process-output-BzloOI7F.js} +4 -4
- package/dist/{quadrantDiagram-AXDQQJYC-Ccw0xMjN.js → quadrantDiagram-AXDQQJYC-Blrm7V7_.js} +1 -1
- package/dist/{railroadDiagram-O6MQD6OU-8vFLVTPu.js → railroadDiagram-O6MQD6OU-Cd47ZV9E.js} +2 -2
- package/dist/{react-vega-BED0DjiB.js → react-vega-C4T9Rb_w.js} +12284 -12270
- package/dist/{requirementDiagram-BXWQKSXE-a_MxGvxe.js → requirementDiagram-BXWQKSXE-eJH6gLtX.js} +1 -1
- package/dist/{reveal-component-De4Pvf4V.js → reveal-component-D3r3vSTK.js} +14 -14
- package/dist/{select-BButFi-d.js → select-2KOiReNY.js} +536 -457
- package/dist/{sequenceDiagram-WJ2MYXX4-CXFMnxGh.js → sequenceDiagram-WJ2MYXX4-DdfFEIrm.js} +1 -1
- package/dist/{spec-BwarBpUJ.js → spec-h69nC3SI.js} +41 -11
- package/dist/{stateDiagram-D77RDMKH-DQeA_CwI.js → stateDiagram-D77RDMKH-DkjDvip8.js} +2 -2
- package/dist/{stateDiagram-v2-MP3YSRHH-BxxQucm1.js → stateDiagram-v2-MP3YSRHH-DBHACnOo.js} +2 -2
- package/dist/style.css +1 -1
- package/dist/{swimlanes-42K2YHIH-Do6L-cYg.js → swimlanes-42K2YHIH-ogImlOmp.js} +1 -1
- package/dist/{swimlanesDiagram-VR7AAH4N-DUzPvSVU.js → swimlanesDiagram-VR7AAH4N-D6x-zsci.js} +2 -2
- package/dist/{swiper-component-DKntyn8p.js → swiper-component-BkCm-kLg.js} +2 -2
- package/dist/{time-BieKwdYS.js → time-BaMbJs4z.js} +18 -18
- package/dist/{timeline-definition-24CTP7MA-D3c46WM5.js → timeline-definition-24CTP7MA-DQpLtsHD.js} +1 -1
- package/dist/{toDate-UtiwmnH2.js → toDate-CDzoofJo.js} +37 -36
- package/dist/{toast-B9UMEi8u.js → toast-B5t9o6s_.js} +219 -201
- package/dist/{tooltip-BO1REbqs.js → tooltip-s4zZxwKe.js} +325 -301
- package/dist/{use-restore-focus-BEM7sFWl.js → use-restore-focus-BgDTd2tc.js} +1482 -1433
- package/dist/{useAsyncData-Deh5kszT.js → useAsyncData-Bn8SxUqM.js} +15 -14
- package/dist/{useDateFormatter-D8XruNGG.js → useDateFormatter-D3Tkfzej.js} +2 -2
- package/dist/{useDeepCompareMemoize-DXwzRINI.js → useDeepCompareMemoize-CC2tnPCk.js} +1 -1
- package/dist/{useIframeCapabilities-DBETvvPl.js → useIframeCapabilities-CSdWn6NP.js} +1 -1
- package/dist/{useLifecycle-j7D9uD1o.js → useLifecycle-BJlxHqWa.js} +28 -25
- package/dist/{useTheme-CRnjkku0.js → useTheme-DMfQYmlG.js} +301 -297
- package/dist/{vega-component-BeF9dfyO.js → vega-component-CovWHi60.js} +148 -148
- package/dist/{vega-embed-container-BNHE18G9.js → vega-embed-container--LYZqguO.js} +2 -2
- package/dist/{vega-loader.browser-DNST6uVf.js → vega-loader.browser-B9Pz0EGP.js} +298 -507
- package/dist/{vennDiagram-4TSXK5OY-BSZD8JcZ.js → vennDiagram-4TSXK5OY-BD-SL_vq.js} +1 -1
- package/dist/{wardleyDiagram-VM6X3IG4-48cUWkfA.js → wardleyDiagram-VM6X3IG4-BDgJV4ah.js} +1 -1
- package/dist/{xychartDiagram-S5SC5T6Z-CswYgYeu.js → xychartDiagram-S5SC5T6Z-1hIHN6Rl.js} +2 -2
- package/package.json +16 -16
- package/src/__mocks__/requests.ts +0 -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/user-config-form.tsx +35 -0
- package/src/components/chat/__tests__/message-queue.test.tsx +17 -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 +9 -8
- package/src/components/chat/chat-utils.ts +4 -8
- 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 +98 -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 +13 -4
- package/src/components/datasources/datasources.tsx +51 -39
- package/src/components/datasources/utils.ts +32 -23
- package/src/components/debugger/debugger-code.tsx +5 -0
- package/src/components/dependency-graph/dependency-graph-tree.tsx +8 -8
- package/src/components/dependency-graph/elements.ts +24 -21
- package/src/components/dependency-graph/panels.tsx +2 -1
- package/src/components/dependency-graph/utils/__tests__/layout.test.ts +18 -11
- package/src/components/editor/SortableCell.tsx +1 -3
- package/src/{core/attachments/__tests__/toast.test.tsx → components/editor/__tests__/agent-toast.test.tsx} +1 -1
- package/src/components/editor/__tests__/data-attributes.test.tsx +6 -2
- package/src/components/editor/__tests__/dynamic-favicon.test.tsx +111 -18
- package/src/components/editor/__tests__/package-alert.test.tsx +22 -1
- package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +24 -0
- package/src/components/editor/actions/pair-with-agent-modal.tsx +18 -15
- package/src/components/editor/actions/useRestartKernel.tsx +1 -1
- package/src/components/editor/ai/add-cell-with-ai.tsx +3 -3
- package/src/components/editor/ai/ai-completion-editor.tsx +1 -0
- package/src/components/editor/alerts/__tests__/connection-notice.test.tsx +1 -1
- package/src/components/editor/alerts/connection-notice.tsx +1 -1
- package/src/components/editor/alerts/startup-logs-alert.tsx +1 -0
- package/src/components/editor/app-container.tsx +1 -1
- package/src/components/editor/boundary/__tests__/ErrorBoundary.test.tsx +1 -1
- package/src/components/editor/cell/CreateCellButton.tsx +10 -12
- 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__/useShouldShowInterrupt.test.ts +39 -0
- package/src/components/editor/cell/code/__tests__/language-toggle.test.tsx +54 -0
- package/src/components/editor/cell/code/cell-editor.tsx +9 -16
- package/src/components/editor/cell/code/language-toggle.tsx +8 -7
- package/src/components/editor/cell/collapse.tsx +5 -3
- package/src/components/editor/cell/useShouldShowInterrupt.ts +2 -12
- package/src/components/editor/chrome/components/__tests__/feedback-button.test.tsx +1 -1
- package/src/components/editor/chrome/components/feedback-button.tsx +1 -1
- package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +31 -2
- package/src/components/editor/chrome/panels/__tests__/runtime-details.test.tsx +1 -1
- package/src/components/editor/chrome/panels/__tests__/sandbox-recovery.test.tsx +1 -1
- package/src/components/editor/chrome/panels/cache-panel.tsx +1 -0
- package/src/components/editor/chrome/panels/outline/__tests__/outline-keyboard.test.tsx +84 -0
- package/src/components/editor/chrome/panels/outline/floating-outline.tsx +39 -14
- package/src/components/editor/chrome/panels/outline/useActiveOutline.tsx +3 -2
- package/src/components/editor/chrome/panels/packages-panel.tsx +4 -7
- package/src/components/editor/chrome/panels/secrets-panel.tsx +1 -0
- package/src/components/editor/chrome/panels/snippets-panel.tsx +5 -1
- package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +9 -18
- package/src/components/editor/chrome/wrapper/__tests__/document-sync-status.test.tsx +92 -0
- package/src/components/editor/chrome/wrapper/__tests__/pending-ai-cells.test.tsx +58 -1
- package/src/components/editor/chrome/wrapper/footer-items/attachment-status.tsx +4 -4
- package/src/components/editor/chrome/wrapper/footer-items/backend-status.tsx +1 -1
- package/src/components/editor/chrome/wrapper/footer-items/document-sync-status.tsx +54 -0
- package/src/components/editor/chrome/wrapper/footer-items/lsp-status.tsx +1 -1
- package/src/components/editor/chrome/wrapper/footer-items/machine-stats.tsx +1 -1
- package/src/components/editor/chrome/wrapper/footer-items/runtime-settings.tsx +1 -7
- package/src/components/editor/chrome/wrapper/footer.tsx +2 -0
- package/src/components/editor/chrome/wrapper/pending-ai-cells.tsx +12 -2
- package/src/components/editor/chrome/wrapper/storage.ts +1 -1
- package/src/components/editor/connections/components.tsx +1 -1
- package/src/components/editor/controls/Controls.tsx +2 -2
- 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 +1 -1
- package/src/components/editor/controls/command-palette.tsx +22 -10
- package/src/components/editor/controls/keyboard-shortcuts.tsx +14 -5
- package/src/components/editor/dynamic-favicon.tsx +21 -40
- package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +9 -23
- package/src/components/editor/errors/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 +45 -8
- package/src/components/editor/file-tree/file-name-input.tsx +1 -0
- package/src/components/editor/file-tree/file-viewer.tsx +4 -2
- package/src/components/editor/file-tree/use-file-search.ts +35 -21
- package/src/components/editor/header/__tests__/status.test.tsx +5 -3
- package/src/components/editor/header/app-header.tsx +1 -1
- package/src/components/editor/header/status.tsx +40 -20
- package/src/components/editor/links/__tests__/cell-link.test.tsx +146 -0
- package/src/components/editor/links/cell-link.tsx +14 -5
- package/src/components/editor/navigation/focus-utils.ts +4 -4
- package/src/components/editor/notebook-banner.tsx +1 -0
- package/src/components/editor/notebook-cell.tsx +12 -3
- 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/package-alert.tsx +14 -7
- package/src/components/editor/renderers/grid-layout/grid-layout.tsx +12 -6
- package/src/components/editor/renderers/vertical-layout/sidebar/toggle.tsx +7 -1
- package/src/components/home/components.tsx +17 -31
- package/src/components/icons/__tests__/large-spinner.test.tsx +39 -0
- package/src/components/icons/large-spinner.tsx +3 -14
- package/src/components/icons/spinner.tsx +1 -1
- package/src/components/mcp/mcp-status-indicator.tsx +1 -1
- package/src/components/pages/__tests__/home-page.test.tsx +116 -0
- package/src/components/pages/home-page.tsx +164 -91
- package/src/components/scratchpad/scratchpad.test.tsx +95 -0
- package/src/components/scratchpad/scratchpad.tsx +16 -3
- package/src/components/slides/reveal-component.tsx +4 -2
- package/src/components/slides/slide-cell-view.tsx +1 -1
- package/src/components/slides/slide-form.tsx +1 -0
- package/src/components/tracing/tracing-keyboard.test.tsx +85 -0
- package/src/components/tracing/tracing.tsx +45 -41
- package/src/components/ui/__tests__/draggable-popover.test.tsx +24 -0
- package/src/components/ui/__tests__/range-slider.test.tsx +165 -0
- package/src/components/ui/alert-dialog.tsx +1 -1
- package/src/components/ui/draggable-popover.tsx +24 -2
- package/src/components/ui/range-slider.tsx +22 -9
- package/src/core/ai/__tests__/staged-cells.test.ts +8 -5
- package/src/core/ai/context/providers/datasource.ts +6 -2
- package/src/core/ai/staged-cells.ts +7 -2
- package/src/core/ai/tools/__tests__/edit-notebook-tool.test.ts +3 -15
- package/src/core/ai/tools/base.ts +16 -7
- package/src/core/ai/tools/edit-notebook-tool.ts +61 -48
- package/src/core/ai/tools/registry.ts +13 -8
- package/src/core/ai/tools/sample-tool.ts +6 -7
- package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +4 -0
- package/src/core/cells/__tests__/cell.test.ts +39 -0
- package/src/core/cells/__tests__/cells.test.ts +83 -346
- package/src/core/cells/__tests__/collapseConsoleOutputs.test.ts +54 -0
- package/src/core/cells/__tests__/document.test.ts +444 -0
- package/src/core/cells/__tests__/flush-document.test.ts +40 -0
- package/src/core/cells/__tests__/notebook-outline.test.ts +108 -0
- package/src/core/cells/__tests__/session.test.ts +117 -36
- package/src/core/cells/__tests__/with-document-save.test.ts +67 -0
- package/src/core/cells/cell.ts +14 -3
- package/src/core/cells/cells.ts +65 -190
- package/src/core/cells/columns.ts +38 -0
- package/src/core/cells/document.ts +389 -0
- package/src/core/cells/pending-delete-service.ts +2 -6
- package/src/core/cells/session.ts +1 -1
- package/src/core/cells/types.ts +22 -1
- package/src/core/codemirror/__tests__/setup.test.ts +29 -29
- package/src/core/codemirror/ai/__tests__/resources.test.ts +117 -0
- package/src/core/codemirror/ai/resources.ts +1 -1
- package/src/core/codemirror/cells/__tests__/extensions.test.ts +42 -1
- package/src/core/codemirror/cells/extensions.ts +21 -3
- package/src/core/codemirror/format.ts +6 -5
- package/src/core/codemirror/keymaps/vim.ts +2 -6
- package/src/core/codemirror/language/__tests__/sql.test.ts +22 -20
- package/src/core/codemirror/language/__tests__/utils.test.ts +187 -15
- package/src/core/codemirror/language/cell-code.ts +114 -0
- package/src/core/codemirror/language/extension.ts +52 -35
- package/src/core/codemirror/language/languages/markdown.ts +8 -6
- package/src/core/codemirror/language/languages/python.ts +16 -10
- package/src/core/codemirror/language/languages/sql/sql.ts +16 -24
- package/src/core/codemirror/language/panel/panel.tsx +1 -0
- package/src/core/codemirror/language/types.ts +7 -7
- package/src/core/codemirror/language/utils.ts +9 -39
- package/src/core/codemirror/lsp/notebook-lsp.ts +6 -5
- package/src/core/codemirror/readonly/__tests__/extension.test.ts +1 -1
- package/src/core/codemirror/readonly/extension.ts +2 -2
- package/src/core/codemirror/rtc/extension.ts +24 -13
- package/src/core/codemirror/rtc/loro/awareness.ts +34 -19
- package/src/core/codemirror/rtc/loro/sync.ts +1 -0
- package/src/core/codemirror/utils.ts +1 -0
- package/src/core/config/__tests__/config-schema.test.ts +5 -3
- package/src/core/config/config-schema.ts +3 -0
- package/src/core/datasets/__tests__/data-source-discovery.test.ts +5 -5
- package/src/core/datasets/data-source-discovery.ts +4 -6
- package/src/core/edit-app.tsx +13 -9
- package/src/core/errors/error-entries.ts +3 -0
- package/src/core/export/hooks.ts +1 -1
- package/src/core/export/layout.ts +2 -2
- package/src/core/hotkeys/actions.ts +2 -4
- package/src/core/islands/__tests__/bootstrap.test.ts +73 -43
- package/src/core/islands/__tests__/bridge.test.ts +72 -25
- package/src/core/islands/bootstrap.ts +46 -53
- package/src/core/islands/bridge.ts +74 -21
- package/src/core/islands/components/__tests__/IslandControls.test.tsx +7 -5
- package/src/core/islands/worker/__tests__/controller.test.ts +62 -4
- package/src/core/islands/worker/controller.ts +4 -10
- package/src/core/islands/worker/worker.tsx +33 -33
- package/src/core/kernel/__tests__/RuntimeState.test.ts +1 -1
- package/src/core/kernel/__tests__/handlers.test.ts +7 -153
- package/src/core/kernel/handlers.ts +18 -59
- package/src/core/network/__tests__/connection.test.ts +1 -1
- package/src/core/network/connection-notice.ts +1 -1
- package/src/core/network/connection.ts +5 -3
- package/src/core/network/requests-lazy.ts +1 -2
- package/src/core/network/requests-network.ts +0 -9
- package/src/core/network/requests-static.ts +0 -1
- package/src/core/network/requests-toasting.tsx +0 -1
- package/src/core/network/types.ts +0 -5
- package/src/core/network/useConnectionNotice.ts +8 -2
- package/src/core/notebook/attachments.ts +38 -0
- package/src/core/notebook/document/__tests__/codemirror.test.ts +547 -0
- package/src/core/notebook/document/__tests__/draft.test.ts +322 -0
- package/src/core/notebook/document/__tests__/fixtures.ts +92 -0
- package/src/core/notebook/document/__tests__/hold.test.ts +88 -0
- package/src/core/notebook/document/__tests__/hooks.test.tsx +391 -0
- package/src/core/notebook/document/__tests__/lifecycle-scenarios.test.ts +61 -0
- package/src/core/notebook/document/__tests__/model.test.ts +247 -0
- package/src/core/notebook/document/__tests__/protocol-fixtures.ts +38 -0
- package/src/core/notebook/document/__tests__/recovery-scenarios.test.ts +230 -0
- package/src/core/notebook/document/__tests__/scenario-fixture.ts +43 -0
- package/src/core/notebook/document/__tests__/scenarios.test.ts +205 -0
- package/src/core/notebook/document/__tests__/state.test.ts +438 -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 +472 -0
- package/src/core/notebook/document/hooks.tsx +118 -0
- package/src/core/notebook/document/index.ts +23 -0
- package/src/core/notebook/document/model.ts +105 -0
- package/src/core/notebook/document/select.ts +41 -0
- package/src/core/notebook/document/state.ts +374 -0
- package/src/core/notebook/document/sync.ts +418 -0
- package/src/core/notebook/index.ts +18 -0
- package/src/core/notebook/replica/AGENTS.md +11 -0
- package/src/core/notebook/replica/__tests__/attachments.test.ts +56 -0
- package/src/core/notebook/replica/__tests__/document.test.ts +126 -0
- package/src/core/notebook/replica/__tests__/fixtures.ts +79 -0
- package/src/core/notebook/replica/__tests__/replica.test.ts +550 -0
- package/src/core/notebook/replica/backend.ts +19 -0
- package/src/core/notebook/replica/connection.ts +42 -0
- package/src/core/notebook/replica/index.ts +13 -0
- package/src/core/notebook/replica/replica.ts +286 -0
- package/src/core/notebook/replica/resources/attachments.ts +22 -0
- package/src/core/notebook/replica/resources/document.ts +101 -0
- package/src/core/packages/__tests__/useSandboxController.test.tsx +1 -1
- package/src/core/packages/useSandboxController.ts +1 -1
- package/src/core/run-app.tsx +48 -51
- package/src/core/runtime/__tests__/adapter.test.ts +1 -1
- package/src/core/runtime/__tests__/createWsUrl.test.ts +77 -7
- package/src/core/runtime/__tests__/runtime.test.ts +32 -58
- package/src/core/runtime/adapter.ts +1 -1
- package/src/core/runtime/config.ts +2 -2
- package/src/core/runtime/runtime.ts +38 -42
- package/src/core/runtime/types.ts +0 -2
- package/src/core/saving/filename.ts +1 -1
- package/src/core/saving/save-component.tsx +10 -4
- package/src/core/saving/useAutoSave.ts +1 -1
- package/src/core/session/__tests__/backend.test.ts +235 -0
- package/src/core/session/__tests__/useNotebookReplica.hook.test.tsx +183 -0
- package/src/core/session/__tests__/useNotebookSession.hook.test.tsx +663 -0
- package/src/core/session/active-notebook.ts +11 -0
- package/src/core/session/backend.ts +125 -0
- package/src/core/{websocket → session}/types.ts +2 -2
- package/src/core/session/useNotebookReplica.ts +72 -0
- package/src/core/{websocket → session}/useNotebookSession.tsx +70 -68
- package/src/core/state/__tests__/jotai.test.tsx +108 -0
- package/src/core/state/jotai.ts +1 -1
- package/src/core/static/__tests__/files.test.ts +38 -10
- package/src/core/static/files.ts +2 -0
- package/src/core/{websocket → transports}/__tests__/connection-state.test.ts +1 -13
- package/src/core/transports/__tests__/server.test.ts +408 -0
- package/src/core/transports/__tests__/static.test.ts +175 -0
- package/src/core/transports/__tests__/wasm.test.ts +166 -0
- package/src/core/{websocket → transports}/connection-state.ts +9 -16
- package/src/core/transports/server.ts +152 -0
- package/src/core/transports/static.ts +84 -0
- package/src/core/transports/wasm.ts +97 -0
- package/src/core/wasm/__tests__/bridge.test.ts +213 -50
- package/src/core/wasm/__tests__/worker.test.ts +187 -0
- package/src/core/wasm/bridge.ts +75 -53
- package/src/core/wasm/rpc.ts +15 -3
- package/src/core/wasm/store.ts +3 -3
- package/src/core/wasm/worker/bootstrap.ts +55 -51
- package/src/core/wasm/worker/getMarimoWheel.ts +1 -1
- package/src/core/wasm/worker/save-worker.ts +9 -10
- package/src/core/wasm/worker/types.ts +29 -18
- package/src/core/wasm/worker/worker.ts +88 -44
- package/src/css/globals.css +10 -0
- package/src/hooks/__tests__/useAsyncData.test.tsx +148 -0
- package/src/hooks/__tests__/useDebounce.test.tsx +51 -0
- package/src/hooks/__tests__/useDeepCompareMemoize.test.tsx +39 -0
- package/src/hooks/__tests__/useDelayElapsed.test.tsx +28 -0
- package/src/hooks/__tests__/useInternalStateWithSync.test.tsx +20 -0
- package/src/hooks/__tests__/useScript.test.tsx +77 -0
- package/src/hooks/__tests__/useStateForKey.test.tsx +257 -0
- package/src/hooks/useAsyncData.ts +3 -3
- package/src/hooks/useDataSourceDiscovery.ts +6 -4
- package/src/hooks/useDebounce.ts +6 -10
- package/src/hooks/useDeepCompareMemoize.ts +1 -1
- package/src/hooks/useDelayElapsed.ts +14 -10
- package/src/hooks/useElapsedTime.ts +3 -3
- package/src/hooks/useEventListener.ts +1 -2
- package/src/hooks/useInputHistory.ts +4 -7
- package/src/hooks/useInternalStateWithSync.ts +6 -6
- package/src/hooks/useResizeObserver.ts +4 -7
- package/src/hooks/useScript.ts +67 -70
- package/src/hooks/useStateForKey.ts +93 -0
- package/src/mount.tsx +1 -5
- package/src/plugins/__tests__/plugin-object-schemas.test.ts +104 -0
- package/src/plugins/core/registerReactComponent.tsx +1 -1
- package/src/plugins/impl/CodeEditorPlugin.tsx +1 -1
- package/src/plugins/impl/DataEditorPlugin.tsx +1 -1
- package/src/plugins/impl/DataTablePlugin.tsx +69 -37
- package/src/plugins/impl/DropdownPlugin.tsx +2 -6
- package/src/plugins/impl/FileBrowserPlugin.tsx +4 -16
- package/src/plugins/impl/FormPlugin.tsx +1 -1
- package/src/plugins/impl/RangeSliderPlugin.tsx +16 -2
- package/src/plugins/impl/RefreshPlugin.tsx +1 -0
- package/src/plugins/impl/SliderPlugin.tsx +6 -8
- package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +387 -1
- package/src/plugins/impl/__tests__/FileBrowserPlugin.test.tsx +45 -0
- package/src/plugins/impl/__tests__/RangeSliderPlugin.test.tsx +109 -0
- package/src/plugins/impl/__tests__/SliderPlugin.test.tsx +24 -3
- package/src/plugins/impl/anywidget/__tests__/registry.test.ts +153 -0
- package/src/plugins/impl/anywidget/__tests__/widget-binding.test.ts +241 -2
- package/src/plugins/impl/anywidget/model.ts +3 -1
- package/src/plugins/impl/anywidget/registry.ts +18 -14
- package/src/plugins/impl/anywidget/runtime.ts +21 -9
- package/src/plugins/impl/anywidget/widget-binding.ts +160 -82
- package/src/plugins/impl/chat/__tests__/chat-prompts.test.tsx +93 -0
- package/src/plugins/impl/chat/chat-ui.tsx +14 -11
- package/src/plugins/impl/code/any-language-editor.tsx +2 -2
- package/src/plugins/impl/data-editor/glide-data-editor.tsx +8 -5
- package/src/plugins/impl/data-editor/glide-utils.ts +3 -1
- package/src/plugins/impl/data-explorer/ConnectedDataExplorerComponent.tsx +1 -1
- package/src/plugins/impl/data-explorer/components/column-summary.tsx +4 -2
- package/src/plugins/impl/data-explorer/state/reducer.ts +1 -1
- package/src/plugins/impl/data-frames/DataFramePlugin.tsx +1 -2
- package/src/plugins/impl/data-frames/__tests__/sidebar.test.tsx +160 -0
- package/src/plugins/impl/data-frames/forms/__tests__/__snapshots__/form.test.tsx.snap +66 -66
- package/src/plugins/impl/data-frames/panel.tsx +43 -16
- package/src/plugins/impl/mpl-interactive/MplInteractivePlugin.tsx +5 -3
- package/src/plugins/impl/panel/PanelPlugin.tsx +16 -17
- package/src/plugins/impl/panel/__tests__/utils.test.ts +39 -1
- package/src/plugins/impl/panel/utils.ts +17 -0
- package/src/plugins/impl/plotly/PlotlyPlugin.tsx +2 -5
- package/src/plugins/impl/plotly/selection.ts +25 -19
- package/src/plugins/impl/vega/VegaPlugin.tsx +2 -3
- package/src/plugins/impl/vega/make-selectable.ts +4 -7
- package/src/plugins/layout/RoutesPlugin.tsx +1 -1
- package/src/stories/__tests__/editor-story.test.tsx +33 -0
- package/src/stories/button.stories.tsx +1 -0
- package/src/stories/cell.stories.tsx +1 -1
- package/src/stories/data-table.stories.tsx +31 -0
- package/src/stories/editor.stories.tsx +24 -17
- package/src/stories/form-wrapper.stories.tsx +2 -2
- package/src/stories/layout/vertical/one-column.stories.tsx +1 -1
- package/src/stories/runtime-story.tsx +1 -1
- package/src/stories/select.stories.tsx +2 -2
- package/src/stories/switchable-multi-select.stories.tsx +1 -1
- package/src/utils/__tests__/arrays.test.ts +35 -0
- package/src/utils/__tests__/batch-requests.test.ts +3 -3
- package/src/utils/__tests__/{createReducer.test.ts → createReducer.test.tsx} +29 -0
- package/src/utils/__tests__/numbers.test.ts +105 -1
- package/src/utils/__tests__/objects.test.ts +21 -0
- package/src/utils/arrays.ts +11 -2
- package/src/utils/blob.ts +1 -1
- package/src/utils/createReducer.ts +53 -26
- package/src/utils/maps.ts +0 -3
- package/src/utils/numbers.ts +79 -18
- package/src/utils/objects.ts +4 -9
- package/src/utils/once.ts +19 -19
- package/src/utils/tracer.ts +9 -5
- package/src/utils/zod-utils.ts +1 -3
- package/dist/any-language-editor-DZ_YTVus.js +0 -76
- package/dist/assets/__vite-browser-external-BXYkiFsk.js +0 -1
- package/dist/assets/worker-HPY9fbhz.js +0 -114
- package/dist/mermaid-HWGCJPDP-ByCsZnYF.js +0 -2
- package/dist/precisionRound-DMIVQrt2.js +0 -97
- package/src/core/attachments/state.ts +0 -21
- package/src/core/cells/__tests__/apply-transaction.test.ts +0 -991
- package/src/core/cells/__tests__/document-changes.test.ts +0 -1140
- package/src/core/cells/__tests__/document-roundtrip.test.ts +0 -392
- package/src/core/cells/document-changes.ts +0 -944
- package/src/core/host/AGENTS.md +0 -6
- package/src/core/host/__tests__/events.test.ts +0 -46
- package/src/core/host/__tests__/notebook-client.test.ts +0 -191
- package/src/core/host/__tests__/protocol.test.ts +0 -214
- package/src/core/host/connection.ts +0 -81
- package/src/core/host/events.ts +0 -60
- package/src/core/host/notebook-client.ts +0 -226
- package/src/core/host/protocol.ts +0 -1943
- package/src/core/state/__tests__/jotai.test.ts +0 -25
- package/src/core/wasm/worker/message-buffer.ts +0 -33
- package/src/core/websocket/__tests__/createConnectionTransport.test.ts +0 -49
- package/src/core/websocket/__tests__/useNotebookClient.hook.test.tsx +0 -140
- package/src/core/websocket/__tests__/useNotebookSession.hook.test.tsx +0 -883
- package/src/core/websocket/transports/__tests__/basic.test.ts +0 -275
- package/src/core/websocket/transports/__tests__/sse.test.ts +0 -335
- package/src/core/websocket/transports/__tests__/transport.test.ts +0 -308
- package/src/core/websocket/transports/__tests__/ws.test.ts +0 -125
- package/src/core/websocket/transports/basic.ts +0 -102
- package/src/core/websocket/transports/create.ts +0 -31
- package/src/core/websocket/transports/sse.ts +0 -308
- package/src/core/websocket/transports/transport.ts +0 -50
- package/src/core/websocket/transports/ws.ts +0 -98
- package/src/core/websocket/useNotebookClient.ts +0 -80
- /package/src/{core/attachments/toast.ts → components/editor/agent-toast.ts} +0 -0
- /package/src/core/{websocket → session}/connection-utils.ts +0 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
/** How a document's cells fall into columns. */
|
|
4
|
+
|
|
5
|
+
import type { EditableDocument } from "@/core/notebook/document";
|
|
6
|
+
import type { CellId } from "./ids";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Each cell's column: its `column` config, or the column of the cell before
|
|
10
|
+
* it. The first cell starts in column 0.
|
|
11
|
+
*/
|
|
12
|
+
export function resolveColumns(
|
|
13
|
+
document: EditableDocument,
|
|
14
|
+
): Map<CellId, number> {
|
|
15
|
+
const columns = new Map<CellId, number>();
|
|
16
|
+
let current = 0;
|
|
17
|
+
for (const id of document.order) {
|
|
18
|
+
const column = document.cells.get(id)?.config.column;
|
|
19
|
+
if (column != null && column >= 0) {
|
|
20
|
+
current = column;
|
|
21
|
+
}
|
|
22
|
+
// The protocol's ids are plain strings; the notebook brands them.
|
|
23
|
+
columns.set(id as CellId, current);
|
|
24
|
+
}
|
|
25
|
+
return columns;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** The cells of each column in order, with empty columns for gaps. */
|
|
29
|
+
export function groupByColumn(document: EditableDocument): CellId[][] {
|
|
30
|
+
const columns: CellId[][] = [[]];
|
|
31
|
+
for (const [id, column] of resolveColumns(document)) {
|
|
32
|
+
while (columns.length <= column) {
|
|
33
|
+
columns.push([]);
|
|
34
|
+
}
|
|
35
|
+
columns[column].push(id);
|
|
36
|
+
}
|
|
37
|
+
return columns;
|
|
38
|
+
}
|
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Keep the notebook state and the host document in step while cells live
|
|
5
|
+
* in both.
|
|
6
|
+
*
|
|
7
|
+
* TODO(MO-8028): Delete this module once components read cells from the
|
|
8
|
+
* document model and `notebookAtom` holds only runtime and editor state.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type * as Protocol from "@marimo-team/client/protocol";
|
|
12
|
+
import { dequal as isEqual } from "dequal";
|
|
13
|
+
import type { createStore } from "jotai";
|
|
14
|
+
import { createRef, type RefObject } from "react";
|
|
15
|
+
import type { CellHandle } from "@/components/editor/notebook-cell";
|
|
16
|
+
import type {
|
|
17
|
+
DocumentEdit,
|
|
18
|
+
DocumentEditor,
|
|
19
|
+
DocumentModel,
|
|
20
|
+
EditableCell,
|
|
21
|
+
EditableDocument,
|
|
22
|
+
} from "@/core/notebook/document";
|
|
23
|
+
import type { DispatchedActionOf } from "@/utils/createReducer";
|
|
24
|
+
import { CollapsibleTree, MultiColumn } from "@/utils/id-tree";
|
|
25
|
+
import { prepareCellCodeUpdate } from "../codemirror/language/cell-code";
|
|
26
|
+
import type { CellConfig } from "../network/types";
|
|
27
|
+
import { canUseRtc, isRtcEnabled } from "../rtc/state";
|
|
28
|
+
import { store } from "../state/jotai";
|
|
29
|
+
import { activeDocumentModelAtom } from "../session/active-notebook";
|
|
30
|
+
import type { CellActions, NotebookState } from "./cells";
|
|
31
|
+
import { groupByColumn } from "./columns";
|
|
32
|
+
import type { CellId } from "./ids";
|
|
33
|
+
import {
|
|
34
|
+
type CellData,
|
|
35
|
+
createCell,
|
|
36
|
+
createCellConfig,
|
|
37
|
+
createCellRuntimeState,
|
|
38
|
+
isEdited,
|
|
39
|
+
} from "./types";
|
|
40
|
+
|
|
41
|
+
type CellAction = DispatchedActionOf<CellActions>;
|
|
42
|
+
|
|
43
|
+
// Following the document ----------------------------------------------------
|
|
44
|
+
|
|
45
|
+
/** Rebuild the tree for `columns`, keeping column ids and collapsed sections. */
|
|
46
|
+
function reconcileColumns(
|
|
47
|
+
current: MultiColumn<CellId>,
|
|
48
|
+
columns: CellId[][],
|
|
49
|
+
): MultiColumn<CellId> {
|
|
50
|
+
const previous = current.getColumns();
|
|
51
|
+
// The document cannot hold an empty column, so keep the ones this client has.
|
|
52
|
+
while (previous[columns.length]?.length === 0) {
|
|
53
|
+
columns.push([]);
|
|
54
|
+
}
|
|
55
|
+
if (
|
|
56
|
+
columns.length === previous.length &&
|
|
57
|
+
columns.every((ids, index) => isEqual(ids, previous[index].inOrderIds))
|
|
58
|
+
) {
|
|
59
|
+
return current;
|
|
60
|
+
}
|
|
61
|
+
return new MultiColumn(
|
|
62
|
+
columns.map((ids, index) =>
|
|
63
|
+
CollapsibleTree.fromWithPreviousShape(ids, previous[index]),
|
|
64
|
+
),
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** The cell's data with the document's fields, and its editor brought along. */
|
|
69
|
+
function followCell(
|
|
70
|
+
current: CellData,
|
|
71
|
+
cell: EditableCell,
|
|
72
|
+
handle: RefObject<CellHandle | null> | undefined,
|
|
73
|
+
): CellData {
|
|
74
|
+
// Withheld source keeps the code this client has.
|
|
75
|
+
const code = cell.code ?? current.code;
|
|
76
|
+
const config = isEqual(current.config, cell.config)
|
|
77
|
+
? current.config
|
|
78
|
+
: createCellConfig(cell.config);
|
|
79
|
+
if (
|
|
80
|
+
code === current.code &&
|
|
81
|
+
cell.name === current.name &&
|
|
82
|
+
config === current.config
|
|
83
|
+
) {
|
|
84
|
+
return current;
|
|
85
|
+
}
|
|
86
|
+
if (code === current.code) {
|
|
87
|
+
return { ...current, name: cell.name, config };
|
|
88
|
+
}
|
|
89
|
+
// RTC cells follow Loro. Other editors take the host's code; one that
|
|
90
|
+
// shows it already, as the editor this client types in does, is left alone.
|
|
91
|
+
if (!isRtcEnabled() || !canUseRtc(current.id)) {
|
|
92
|
+
const view = handle?.current?.editorViewOrNull;
|
|
93
|
+
const update = view && prepareCellCodeUpdate(view.state, code);
|
|
94
|
+
if (update) {
|
|
95
|
+
view.dispatch(update);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
...current,
|
|
100
|
+
code,
|
|
101
|
+
name: cell.name,
|
|
102
|
+
config,
|
|
103
|
+
edited: isEdited(current.lastCodeRun, code),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Make the notebook's cells and order follow the document.
|
|
109
|
+
*
|
|
110
|
+
* Returns the same state when nothing differs, so a local edit that the
|
|
111
|
+
* document already reflects changes nothing.
|
|
112
|
+
*
|
|
113
|
+
* TODO(MO-8028): Delete with `reconcileDocument` once cells are read from
|
|
114
|
+
* the document model.
|
|
115
|
+
*/
|
|
116
|
+
export function applyDocument(
|
|
117
|
+
state: NotebookState,
|
|
118
|
+
document: EditableDocument,
|
|
119
|
+
): NotebookState {
|
|
120
|
+
const cellIds = reconcileColumns(state.cellIds, groupByColumn(document));
|
|
121
|
+
let { cellData, cellRuntime, cellHandles } = state;
|
|
122
|
+
for (const id of cellIds.inOrderIds) {
|
|
123
|
+
const cell = document.cells.get(id);
|
|
124
|
+
const current = state.cellData[id];
|
|
125
|
+
if (cell === undefined) {
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (current === undefined) {
|
|
129
|
+
// A new cell has no run to be edited from.
|
|
130
|
+
cellData = {
|
|
131
|
+
...cellData,
|
|
132
|
+
[id]: createCell({
|
|
133
|
+
id,
|
|
134
|
+
code: cell.code ?? "",
|
|
135
|
+
name: cell.name,
|
|
136
|
+
config: createCellConfig(cell.config),
|
|
137
|
+
}),
|
|
138
|
+
};
|
|
139
|
+
cellRuntime = { ...cellRuntime, [id]: createCellRuntimeState() };
|
|
140
|
+
cellHandles = { ...cellHandles, [id]: createRef() };
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
const next = followCell(current, cell, state.cellHandles[id]);
|
|
144
|
+
if (next !== current) {
|
|
145
|
+
cellData = { ...cellData, [id]: next };
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const present = new Set(cellIds.inOrderIds);
|
|
149
|
+
const removed = state.cellIds.inOrderIds.filter((id) => !present.has(id));
|
|
150
|
+
if (removed.length > 0) {
|
|
151
|
+
const gone = new Set(removed);
|
|
152
|
+
const keep = <V>(record: Record<CellId, V>): Record<CellId, V> =>
|
|
153
|
+
Object.fromEntries(
|
|
154
|
+
Object.entries(record).filter(([id]) => !gone.has(id as CellId)),
|
|
155
|
+
) as Record<CellId, V>;
|
|
156
|
+
cellData = keep(cellData);
|
|
157
|
+
cellRuntime = keep(cellRuntime);
|
|
158
|
+
cellHandles = keep(cellHandles);
|
|
159
|
+
}
|
|
160
|
+
if (
|
|
161
|
+
cellIds === state.cellIds &&
|
|
162
|
+
cellData === state.cellData &&
|
|
163
|
+
removed.length === 0
|
|
164
|
+
) {
|
|
165
|
+
return state;
|
|
166
|
+
}
|
|
167
|
+
return { ...state, cellIds, cellData, cellRuntime, cellHandles };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Reconcile the model's document now and on each change, until the signal aborts.
|
|
172
|
+
*
|
|
173
|
+
* TODO(MO-8028): Delete with `applyDocument`.
|
|
174
|
+
*/
|
|
175
|
+
export function followDocument({
|
|
176
|
+
store,
|
|
177
|
+
model,
|
|
178
|
+
reconcile,
|
|
179
|
+
signal,
|
|
180
|
+
}: {
|
|
181
|
+
store: ReturnType<typeof createStore>;
|
|
182
|
+
model: Pick<DocumentModel, "documentAtom">;
|
|
183
|
+
reconcile: (document: EditableDocument) => void;
|
|
184
|
+
signal: AbortSignal;
|
|
185
|
+
}): void {
|
|
186
|
+
const follow = () => {
|
|
187
|
+
const document = store.get(model.documentAtom);
|
|
188
|
+
if (document !== null) {
|
|
189
|
+
reconcile(document);
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
const unsubscribe = store.sub(model.documentAtom, follow);
|
|
193
|
+
signal.addEventListener("abort", unsubscribe, { once: true });
|
|
194
|
+
follow();
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Editing the document ------------------------------------------------------
|
|
198
|
+
|
|
199
|
+
const CONFIG_FIELDS = ["disabled", "hide_code", "expand_output"] as const;
|
|
200
|
+
|
|
201
|
+
/** The fields of `after` that differ from `before`, leaving `column` to the tree. */
|
|
202
|
+
function configPatch(
|
|
203
|
+
before: CellConfig,
|
|
204
|
+
after: CellConfig,
|
|
205
|
+
): Partial<Protocol.CellConfig> | null {
|
|
206
|
+
const patch: Partial<Protocol.CellConfig> = {};
|
|
207
|
+
for (const field of CONFIG_FIELDS) {
|
|
208
|
+
if (before[field] !== after[field]) {
|
|
209
|
+
patch[field] = after[field] ?? false;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return Object.keys(patch).length === 0 ? null : patch;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* The `column` configs that make the document's columns match the tree's.
|
|
217
|
+
*
|
|
218
|
+
* A cell without a column follows the cell before it, so only cells whose
|
|
219
|
+
* resolved column differs from their position need one.
|
|
220
|
+
*/
|
|
221
|
+
function columnFixes(state: NotebookState): Array<[CellId, number | null]> {
|
|
222
|
+
const fixes: Array<[CellId, number | null]> = [];
|
|
223
|
+
const columns = state.cellIds.getColumns();
|
|
224
|
+
if (columns.length === 1) {
|
|
225
|
+
for (const id of columns[0].inOrderIds) {
|
|
226
|
+
const column = state.cellData[id].config.column;
|
|
227
|
+
if (column != null && column !== 0) {
|
|
228
|
+
fixes.push([id, null]);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return fixes;
|
|
232
|
+
}
|
|
233
|
+
let inherited = 0;
|
|
234
|
+
columns.forEach((column, target) => {
|
|
235
|
+
for (const id of column.inOrderIds) {
|
|
236
|
+
const configured = state.cellData[id].config.column;
|
|
237
|
+
const resolved =
|
|
238
|
+
configured != null && configured >= 0 ? configured : inherited;
|
|
239
|
+
if (resolved !== target) {
|
|
240
|
+
fixes.push([id, target]);
|
|
241
|
+
}
|
|
242
|
+
inherited = target;
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
return fixes;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** The edit that makes the document match `next`, or null when it already does. */
|
|
249
|
+
function diffNotebook(
|
|
250
|
+
prev: NotebookState,
|
|
251
|
+
next: NotebookState,
|
|
252
|
+
): DocumentEdit | null {
|
|
253
|
+
const steps: Array<(tx: DocumentEditor) => void> = [];
|
|
254
|
+
const prevOrder = prev.cellIds.inOrderIds;
|
|
255
|
+
const nextOrder = next.cellIds.inOrderIds;
|
|
256
|
+
const prevIds = new Set(prevOrder);
|
|
257
|
+
const nextIds = new Set(nextOrder);
|
|
258
|
+
for (const id of prevOrder) {
|
|
259
|
+
if (!nextIds.has(id)) {
|
|
260
|
+
steps.push((tx) => tx.deleteCell(id));
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
const created: CellId[] = [];
|
|
264
|
+
for (const id of nextOrder) {
|
|
265
|
+
const cell = next.cellData[id];
|
|
266
|
+
const before = prev.cellData[id];
|
|
267
|
+
if (!prevIds.has(id) || before === undefined) {
|
|
268
|
+
created.push(id);
|
|
269
|
+
steps.push((tx) =>
|
|
270
|
+
tx.createCell({
|
|
271
|
+
id,
|
|
272
|
+
code: cell.code,
|
|
273
|
+
name: cell.name,
|
|
274
|
+
config: cell.config,
|
|
275
|
+
}),
|
|
276
|
+
);
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
if (before === cell) {
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
if (before.code !== cell.code) {
|
|
283
|
+
steps.push((tx) => tx.setCode(id, cell.code));
|
|
284
|
+
}
|
|
285
|
+
if (before.name !== cell.name) {
|
|
286
|
+
steps.push((tx) => tx.setName(id, cell.name));
|
|
287
|
+
}
|
|
288
|
+
const patch = configPatch(before.config, cell.config);
|
|
289
|
+
if (patch !== null) {
|
|
290
|
+
steps.push((tx) => tx.updateConfig(id, patch));
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
for (const [id, column] of columnFixes(next)) {
|
|
294
|
+
steps.push((tx) => tx.updateConfig(id, { column }));
|
|
295
|
+
}
|
|
296
|
+
// Created cells go to the end, so the order is set when they belong elsewhere.
|
|
297
|
+
const survivors = prevOrder.filter((id) => nextIds.has(id));
|
|
298
|
+
if (!isEqual(nextOrder, [...survivors, ...created])) {
|
|
299
|
+
steps.push((tx) => tx.setOrder(nextOrder));
|
|
300
|
+
}
|
|
301
|
+
if (steps.length === 0) {
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
return (tx) => {
|
|
305
|
+
for (const step of steps) {
|
|
306
|
+
step(tx);
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Middleware for the notebook reducer. Writes what an action changed about
|
|
313
|
+
* the cells and their order into the document model as one edit.
|
|
314
|
+
*
|
|
315
|
+
* TODO(MO-8028): Each action family that moves onto the model removes its
|
|
316
|
+
* part of `diffNotebook`. The middleware goes with the last one, code.
|
|
317
|
+
*/
|
|
318
|
+
export function documentEditMiddleware(
|
|
319
|
+
prev: NotebookState,
|
|
320
|
+
next: NotebookState,
|
|
321
|
+
action: CellAction,
|
|
322
|
+
): void {
|
|
323
|
+
// The document replaces the notebook's cells, so following it is no edit.
|
|
324
|
+
if (action.type === "reconcileDocument") {
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
if (prev.cellIds === next.cellIds && prev.cellData === next.cellData) {
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
const model = store.get(activeDocumentModelAtom);
|
|
331
|
+
if (model === null) {
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
const edit = diffNotebook(prev, next);
|
|
335
|
+
if (edit !== null) {
|
|
336
|
+
store.set(model.editAtom, edit);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Commands that read the document on the host -------------------------------
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Send this client's edits to the host before a command that reads them
|
|
344
|
+
* there. Without a document or a backend, as on a static page, there is
|
|
345
|
+
* nothing to send and the command proceeds.
|
|
346
|
+
*/
|
|
347
|
+
export async function flushDocument(): Promise<void> {
|
|
348
|
+
const model = store.get(activeDocumentModelAtom);
|
|
349
|
+
if (
|
|
350
|
+
model === null ||
|
|
351
|
+
store.get(model.syncStatusAtom).status === "unavailable"
|
|
352
|
+
) {
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
await model.flush();
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Run a legacy save after this client's edits reach the host, and keep
|
|
360
|
+
* later edits local until it finishes. The save carries the whole
|
|
361
|
+
* notebook and the host takes it as the document, so an edit sent
|
|
362
|
+
* meanwhile would be overwritten. When the edits cannot be sent the save
|
|
363
|
+
* still runs, and the model resumes against the saved document.
|
|
364
|
+
*
|
|
365
|
+
* TODO(MO-8028): Goes with the legacy save, along with the hold.
|
|
366
|
+
*/
|
|
367
|
+
export async function withDocumentSave<T>(save: () => Promise<T>): Promise<T> {
|
|
368
|
+
const model = store.get(activeDocumentModelAtom);
|
|
369
|
+
if (model === null) {
|
|
370
|
+
return save();
|
|
371
|
+
}
|
|
372
|
+
let flushed = true;
|
|
373
|
+
try {
|
|
374
|
+
await model.flush();
|
|
375
|
+
} catch {
|
|
376
|
+
flushed = false;
|
|
377
|
+
}
|
|
378
|
+
const release = model.hold();
|
|
379
|
+
let result: T;
|
|
380
|
+
try {
|
|
381
|
+
result = await save();
|
|
382
|
+
} finally {
|
|
383
|
+
release();
|
|
384
|
+
}
|
|
385
|
+
if (!flushed) {
|
|
386
|
+
void model.retry().catch(() => undefined);
|
|
387
|
+
}
|
|
388
|
+
return result;
|
|
389
|
+
}
|
|
@@ -134,12 +134,8 @@ export function usePendingDeleteService() {
|
|
|
134
134
|
submit({ cellIds, deleteCell, deleteManyCells, store });
|
|
135
135
|
},
|
|
136
136
|
clear,
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
},
|
|
140
|
-
get shouldConfirmDelete() {
|
|
141
|
-
return entries.size > 1;
|
|
142
|
-
},
|
|
137
|
+
idle: entries.size === 0,
|
|
138
|
+
shouldConfirmDelete: entries.size > 1,
|
|
143
139
|
};
|
|
144
140
|
}
|
|
145
141
|
|
|
@@ -56,7 +56,7 @@ function mergeSessionAndNotebookCells(
|
|
|
56
56
|
return {
|
|
57
57
|
cellIds,
|
|
58
58
|
sessionCellData: new Map(
|
|
59
|
-
cellIds.map((id
|
|
59
|
+
cellIds.map((id) => [id, null] as [CellId, SessionCell | null]),
|
|
60
60
|
),
|
|
61
61
|
notebookCellData: new Map(
|
|
62
62
|
cellIds.map(
|
package/src/core/cells/types.ts
CHANGED
|
@@ -8,6 +8,18 @@ import type { CellId } from "./ids";
|
|
|
8
8
|
import { DEFAULT_CELL_NAME } from "./names";
|
|
9
9
|
import type { Outline } from "./outline";
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Whether a cell's code has changed since it last ran. A cell that never ran
|
|
13
|
+
* is edited once it has code. Whitespace at the ends does not count.
|
|
14
|
+
*
|
|
15
|
+
* TODO(MO-8028): Temporary until staleness comes from the host's executions.
|
|
16
|
+
*/
|
|
17
|
+
export function isEdited(lastCodeRun: string | null, code: string): boolean {
|
|
18
|
+
return lastCodeRun === null
|
|
19
|
+
? code.trim().length > 0
|
|
20
|
+
: lastCodeRun.trim() !== code.trim();
|
|
21
|
+
}
|
|
22
|
+
|
|
11
23
|
/**
|
|
12
24
|
* Create a new cell with default state.
|
|
13
25
|
*/
|
|
@@ -95,7 +107,7 @@ export interface CellRuntimeState {
|
|
|
95
107
|
/** TOC outline */
|
|
96
108
|
outline: Outline | null;
|
|
97
109
|
/** messages encoding the cell's console outputs. */
|
|
98
|
-
consoleOutputs:
|
|
110
|
+
consoleOutputs: ConsoleOutputMessage[];
|
|
99
111
|
/** current status of the cell */
|
|
100
112
|
status: RuntimeState;
|
|
101
113
|
/** whether the cell has stale inputs*/
|
|
@@ -130,3 +142,12 @@ export type WithResponse<T> = T & {
|
|
|
130
142
|
*/
|
|
131
143
|
response?: string;
|
|
132
144
|
};
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* A console output as held by the frontend. The id is assigned when the
|
|
148
|
+
* output arrives and never changes, so it can identify the output while
|
|
149
|
+
* neighboring outputs collapse, truncate, or receive a stdin response.
|
|
150
|
+
*/
|
|
151
|
+
export type ConsoleOutputMessage = WithResponse<OutputMessage> & {
|
|
152
|
+
id?: number;
|
|
153
|
+
};
|
|
@@ -100,7 +100,7 @@ function getDuplicateKeymaps(state: EditorState) {
|
|
|
100
100
|
(keymap) => keymap,
|
|
101
101
|
);
|
|
102
102
|
const duplicates = Objects.fromEntries(
|
|
103
|
-
Object.entries(groupBy).filter(([
|
|
103
|
+
Object.entries(groupBy).filter(([_key, value]) => value.length > 1),
|
|
104
104
|
);
|
|
105
105
|
return duplicates;
|
|
106
106
|
}
|
|
@@ -198,22 +198,22 @@ test("go to definition falls through to lower-priority LSP keymap", () => {
|
|
|
198
198
|
test("placeholder adds another extension", () => {
|
|
199
199
|
const opts = getOpts();
|
|
200
200
|
const withAI = new PythonLanguageAdapter()
|
|
201
|
-
.getExtension(
|
|
202
|
-
opts.cellId,
|
|
203
|
-
opts.completionConfig,
|
|
204
|
-
opts.hotkeys,
|
|
205
|
-
"marimo-import",
|
|
206
|
-
opts.lspConfig,
|
|
207
|
-
)
|
|
201
|
+
.getExtension({
|
|
202
|
+
cellId: opts.cellId,
|
|
203
|
+
completionConfig: opts.completionConfig,
|
|
204
|
+
hotkeys: opts.hotkeys,
|
|
205
|
+
placeholderType: "marimo-import",
|
|
206
|
+
lspConfig: opts.lspConfig,
|
|
207
|
+
})
|
|
208
208
|
.flat();
|
|
209
209
|
const withoutAI = new PythonLanguageAdapter()
|
|
210
|
-
.getExtension(
|
|
211
|
-
opts.cellId,
|
|
212
|
-
opts.completionConfig,
|
|
213
|
-
opts.hotkeys,
|
|
214
|
-
"none",
|
|
215
|
-
opts.lspConfig,
|
|
216
|
-
)
|
|
210
|
+
.getExtension({
|
|
211
|
+
cellId: opts.cellId,
|
|
212
|
+
completionConfig: opts.completionConfig,
|
|
213
|
+
hotkeys: opts.hotkeys,
|
|
214
|
+
placeholderType: "none",
|
|
215
|
+
lspConfig: opts.lspConfig,
|
|
216
|
+
})
|
|
217
217
|
.flat();
|
|
218
218
|
expect(withAI.length - 1).toBe(withoutAI.length);
|
|
219
219
|
});
|
|
@@ -221,22 +221,22 @@ test("placeholder adds another extension", () => {
|
|
|
221
221
|
test("ai adds more extensions", () => {
|
|
222
222
|
const opts = getOpts();
|
|
223
223
|
const withAI = new PythonLanguageAdapter()
|
|
224
|
-
.getExtension(
|
|
225
|
-
opts.cellId,
|
|
226
|
-
opts.completionConfig,
|
|
227
|
-
opts.hotkeys,
|
|
228
|
-
"ai",
|
|
229
|
-
opts.lspConfig,
|
|
230
|
-
)
|
|
224
|
+
.getExtension({
|
|
225
|
+
cellId: opts.cellId,
|
|
226
|
+
completionConfig: opts.completionConfig,
|
|
227
|
+
hotkeys: opts.hotkeys,
|
|
228
|
+
placeholderType: "ai",
|
|
229
|
+
lspConfig: opts.lspConfig,
|
|
230
|
+
})
|
|
231
231
|
.flat();
|
|
232
232
|
const withoutAI = new PythonLanguageAdapter()
|
|
233
|
-
.getExtension(
|
|
234
|
-
opts.cellId,
|
|
235
|
-
opts.completionConfig,
|
|
236
|
-
opts.hotkeys,
|
|
237
|
-
"none",
|
|
238
|
-
opts.lspConfig,
|
|
239
|
-
)
|
|
233
|
+
.getExtension({
|
|
234
|
+
cellId: opts.cellId,
|
|
235
|
+
completionConfig: opts.completionConfig,
|
|
236
|
+
hotkeys: opts.hotkeys,
|
|
237
|
+
placeholderType: "none",
|
|
238
|
+
lspConfig: opts.lspConfig,
|
|
239
|
+
})
|
|
240
240
|
.flat();
|
|
241
241
|
expect(withAI.length - 2).toBe(withoutAI.length);
|
|
242
242
|
});
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { markdownLanguage } from "@codemirror/lang-markdown";
|
|
4
|
+
import { hoverResource, type Resource } from "@marimo-team/codemirror-mcp";
|
|
5
|
+
import { createStore } from "jotai";
|
|
6
|
+
import { assert, beforeEach, describe, expect, it, vi } from "vitest";
|
|
7
|
+
import { MockNotebook } from "@/__mocks__/notebook";
|
|
8
|
+
import { getAIContextRegistry } from "@/core/ai/context/context";
|
|
9
|
+
import { ErrorContextProvider } from "@/core/ai/context/providers/error";
|
|
10
|
+
import { AIContextRegistry } from "@/core/ai/context/registry";
|
|
11
|
+
import { notebookAtom } from "@/core/cells/cells";
|
|
12
|
+
import { CellId } from "@/core/cells/ids";
|
|
13
|
+
import { resourceExtension } from "../resources";
|
|
14
|
+
|
|
15
|
+
vi.mock("@marimo-team/codemirror-mcp", async (importOriginal) => ({
|
|
16
|
+
...(await importOriginal<typeof import("@marimo-team/codemirror-mcp")>()),
|
|
17
|
+
hoverResource: vi.fn(() => []),
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
vi.mock("@/core/ai/context/context", () => ({
|
|
21
|
+
getAIContextRegistry: vi.fn(),
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
describe("resource tooltips", () => {
|
|
25
|
+
const message = '<b>Unexpected response</b> & "details"';
|
|
26
|
+
let provider: ErrorContextProvider;
|
|
27
|
+
let resource: Resource;
|
|
28
|
+
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
vi.clearAllMocks();
|
|
31
|
+
const store = createStore();
|
|
32
|
+
store.set(
|
|
33
|
+
notebookAtom,
|
|
34
|
+
MockNotebook.notebookStateWithErrors([
|
|
35
|
+
{
|
|
36
|
+
cellId: CellId.create(),
|
|
37
|
+
cellName: "Cell 1",
|
|
38
|
+
errorData: [MockNotebook.errors.exception(message)],
|
|
39
|
+
},
|
|
40
|
+
]),
|
|
41
|
+
);
|
|
42
|
+
provider = new ErrorContextProvider(store);
|
|
43
|
+
resource = provider.getItems()[1];
|
|
44
|
+
vi.mocked(getAIContextRegistry).mockReturnValue(
|
|
45
|
+
new AIContextRegistry().register(provider),
|
|
46
|
+
);
|
|
47
|
+
resourceExtension({ language: markdownLanguage, store });
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
function renderTooltip(item = resource) {
|
|
51
|
+
const options = vi.mocked(hoverResource).mock.calls[0][0];
|
|
52
|
+
assert(options.createTooltip);
|
|
53
|
+
return options.createTooltip(item).dom;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
it("preserves literal text in cell error descriptions", () => {
|
|
57
|
+
const dom = renderTooltip();
|
|
58
|
+
expect(dom.textContent).toBe(message);
|
|
59
|
+
expect(dom.childElementCount).toBe(0);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("preserves literal descriptions when a provider is unavailable", () => {
|
|
63
|
+
const dom = renderTooltip({ ...resource, type: "unknown" });
|
|
64
|
+
expect(dom.textContent).toBe(message);
|
|
65
|
+
expect(dom.childElementCount).toBe(0);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("falls back to the literal resource name", () => {
|
|
69
|
+
const dom = renderTooltip({
|
|
70
|
+
...resource,
|
|
71
|
+
description: undefined,
|
|
72
|
+
name: message,
|
|
73
|
+
});
|
|
74
|
+
expect(dom.textContent).toBe(message);
|
|
75
|
+
expect(dom.childElementCount).toBe(0);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("preserves literal text in completion info strings", () => {
|
|
79
|
+
vi.spyOn(provider, "formatCompletion").mockReturnValue({
|
|
80
|
+
label: "Error",
|
|
81
|
+
info: message,
|
|
82
|
+
});
|
|
83
|
+
const dom = renderTooltip({ ...resource, description: "Fallback" });
|
|
84
|
+
expect(dom.textContent).toBe(message);
|
|
85
|
+
expect(dom.childElementCount).toBe(0);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("preserves literal fallback text when an info renderer returns null", () => {
|
|
89
|
+
vi.spyOn(provider, "formatCompletion").mockReturnValue({
|
|
90
|
+
label: "Error",
|
|
91
|
+
info: () => null,
|
|
92
|
+
});
|
|
93
|
+
const dom = renderTooltip();
|
|
94
|
+
expect(dom.textContent).toBe(message);
|
|
95
|
+
expect(dom.childElementCount).toBe(0);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("preserves literal fallback text for async info renderers", () => {
|
|
99
|
+
vi.spyOn(provider, "formatCompletion").mockReturnValue({
|
|
100
|
+
label: "Error",
|
|
101
|
+
info: async () => document.createElement("div"),
|
|
102
|
+
});
|
|
103
|
+
const dom = renderTooltip();
|
|
104
|
+
expect(dom.textContent).toBe(message);
|
|
105
|
+
expect(dom.childElementCount).toBe(0);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it.each(["element", "tooltip"])("preserves custom %s renderers", (kind) => {
|
|
109
|
+
const dom = document.createElement("strong");
|
|
110
|
+
dom.textContent = "Details";
|
|
111
|
+
vi.spyOn(provider, "formatCompletion").mockReturnValue({
|
|
112
|
+
label: "Error",
|
|
113
|
+
info: () => (kind === "element" ? dom : { dom }),
|
|
114
|
+
});
|
|
115
|
+
expect(renderTooltip()).toBe(dom);
|
|
116
|
+
});
|
|
117
|
+
});
|