@marimo-team/frontend 0.25.3-dev4 → 0.25.3-dev40
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/assets/CellStatus-BGIWF94J.js +1 -0
- package/dist/assets/{ConnectedDataExplorerComponent-t8fH_TNc.js → ConnectedDataExplorerComponent-D9UK8o9A.js} +1 -1
- package/dist/assets/{ErrorBoundary-Gl5nCA8C.js → ErrorBoundary-hliG40Pk.js} +2 -2
- package/dist/assets/JsonOutput-9tmC-p6h.js +11 -0
- package/dist/assets/{LazyAnyLanguageCodeMirror-CKcCbG2G.js → LazyAnyLanguageCodeMirror-BPYE76jR.js} +2 -2
- package/dist/assets/MarimoErrorOutput-BaHdbtck.js +10 -0
- package/dist/assets/{RenderHTML-BbOqmHy3.js → RenderHTML-B1FzB5NY.js} +1 -1
- package/dist/assets/{RunButton-CK3LhnP5.js → RunButton-DpGokdF1.js} +1 -1
- package/dist/assets/{add-cell-with-ai-8UBC0xwR.js → add-cell-with-ai-BYWG0iKO.js} +13 -13
- package/dist/assets/{add-connection-dialog-DXQnd6eB.js → add-connection-dialog-BWbUVPJk.js} +2 -2
- package/dist/assets/{agent-panel-DdkVL-Kx.js → agent-panel-DKNiQrAl.js} +5 -5
- package/dist/assets/ai-model-dropdown-CIg9eu47.js +13 -0
- package/dist/assets/{alert-dialog-BaPGzeOK.js → alert-dialog-C-sWmKva.js} +1 -1
- package/dist/assets/any-language-editor-DUMe1UCk.js +1 -0
- package/dist/assets/{app-config-button-BKEG4x9u.js → app-config-button-BvkL2eSY.js} +1 -1
- package/dist/assets/arrays-0R2reF2y.js +1 -0
- package/dist/assets/{blob-D5nfNHOo.js → blob-BQfLiwKr.js} +1 -1
- package/dist/assets/{button-COI-NGrx.js → button-D8gVKwe-.js} +1 -1
- package/dist/assets/{cache-panel-DTY1dqvi.js → cache-panel-Byolv2ht.js} +1 -1
- package/dist/assets/{capabilities-BJvf-MD4.js → capabilities-Pq1YNPlK.js} +1 -1
- package/dist/assets/{cell-editor-32lVhib7.js → cell-editor-BKKUv4d5.js} +9 -9
- package/dist/assets/{cell-link-LSth_8hT.js → cell-link-B_QkRkm-.js} +1 -1
- package/dist/assets/{cells-BRkCaSsh.js → cells-CdH98VSn.js} +31 -31
- package/dist/assets/{chat-display-Dnagy4Nv.js → chat-display-CNbRSebo.js} +1 -1
- package/dist/assets/{chat-panel-DN9vsbgS.js → chat-panel-HbEKDf0c.js} +2 -2
- package/dist/assets/{chat-ui-Df8Vcj6n.js → chat-ui-BwMev8Bf.js} +1 -1
- package/dist/assets/{circle-DXf-VWZG.js → circle-B6D_6D8w.js} +1 -1
- package/dist/assets/code-visibility-sQ37CBNo.js +1 -0
- package/dist/assets/coerce-DWjFeXOY.js +1 -0
- package/dist/assets/{column-preview-CHK_DNrs.js → column-preview-CsEBzIUE.js} +1 -1
- package/dist/assets/command-palette-BzZ86pUz.js +1 -0
- package/dist/assets/{common-Cu6CDbuw.js → common-S2v8dsEy.js} +1 -1
- package/dist/assets/{components-CGw16R_N.js → components-D-TnFWjo.js} +1 -1
- package/dist/assets/{components-CP_UoFtT.js → components-DrU8dOzy.js} +1 -1
- package/dist/assets/{components-CXfO-F47.js → components-lDm0qi2t.js} +1 -1
- package/dist/assets/config-BoBwH7CU.js +1 -0
- package/dist/assets/config-CYsbZpwa.js +104 -0
- package/dist/assets/config-DGX7JYor.js +1 -0
- package/dist/assets/{copy-D8O_CYCG.js → copy-KlhazlhB.js} +1 -1
- package/dist/assets/{copy-icon-BIiRXh_3.js → copy-icon-8-a5vovc.js} +1 -1
- package/dist/assets/{createReducer-DgRODTW6.js → createReducer-HKXUbqyz.js} +1 -1
- package/dist/assets/{datasource-Cq6x_5ML.js → datasource-BA7YF4eo.js} +1 -1
- package/dist/assets/{dates-CfmdTL6B.js → dates-CQMwFko2.js} +1 -1
- package/dist/assets/{dependency-graph-panel--Kgtktcw.js → dependency-graph-panel-DsjI61Qa.js} +1 -1
- package/dist/assets/{dist-iaThVDkA.js → dist-jlhMTKo_.js} +1 -1
- package/dist/assets/{documentation-panel-BHmbeznw.js → documentation-panel-hk3IdeXy.js} +1 -1
- package/dist/assets/{download-DCJXy66H.js → download-CCZkq0y4.js} +1 -1
- package/dist/assets/{dropdown-menu-C1rnl-0o.js → dropdown-menu-DS6umAnH.js} +1 -1
- package/dist/assets/edit-page-BgS6tjth.js +9 -0
- package/dist/assets/{error-banner-Cd69vvyX.js → error-banner-B5kzVf_0.js} +1 -1
- package/dist/assets/{error-entries-Dk3VZ-OK.js → error-entries-BVXrGOTZ.js} +1 -1
- package/dist/assets/{error-panel-BPRYmC5J.js → error-panel-DYethUUR.js} +1 -1
- package/dist/assets/{errors-DCrsa1nH.js → errors-CNsgAw6m.js} +1 -1
- package/dist/assets/{es-Cqst59WX.js → es-N9SHpmG7.js} +1 -1
- package/dist/assets/feedback-button-DDzObtF2.js +1 -0
- package/dist/assets/{feedback-button-Bik2-e33.js → feedback-button-DaNc3uJa.js} +2 -2
- package/dist/assets/{file-explorer-panel-BmekFu7T.js → file-explorer-panel-CqnzSSYc.js} +1 -1
- package/dist/assets/{file-name-input-Cdc-TVqO.js → file-name-input-D4AvwZV9.js} +1 -1
- package/dist/assets/{floating-outline-XD9FAV-f.js → floating-outline-CTy-Iakk.js} +1 -1
- package/dist/assets/{formats-yZ06h1Pl.js → formats-zylfwejq.js} +1 -1
- package/dist/assets/{formatting-BqrP-n49.js → formatting-uom1es4b.js} +1 -1
- package/dist/assets/{fullscreen-B9tHcfpV.js → fullscreen-BWTs8pYZ.js} +1 -1
- package/dist/assets/{gallery-page-Dm6mGwBi.js → gallery-page-ByjlvM6k.js} +1 -1
- package/dist/assets/{ganttDiagram-EL5Y4UJY-2n7Ig5Jm.js → ganttDiagram-EL5Y4UJY-CYqkCfFG.js} +1 -1
- package/dist/assets/{glide-data-editor-J9CAb70d.js → glide-data-editor-CABBj2b0.js} +2 -2
- package/dist/assets/{glide-utils-CIrAxdjv.js → glide-utils-HFqhvWHI.js} +2 -2
- package/dist/assets/{home-page-s3LUizSn.js → home-page-DejRSjeJ.js} +3 -3
- package/dist/assets/hooks-BJgCcVXZ.js +1 -0
- package/dist/assets/{html-to-image-DK9Zl1lU.js → html-to-image-D_0rd3_B.js} +1 -1
- package/dist/assets/index-ChfDZJzP.css +2 -0
- package/dist/assets/index-PDJQd7X6.js +30 -0
- package/dist/assets/{input-BoBxR50a.js → input-ByHt6aoV.js} +1 -1
- package/dist/assets/{kiosk-mode-BZECfg9k.js → kiosk-mode-BGmMSc7A.js} +1 -1
- package/dist/assets/{layout-B_GMOySr.js → layout-Ct0rjb-5.js} +5 -5
- package/dist/assets/linear-Bma8J3iM.js +1 -0
- package/dist/assets/links-CXcpNIM0.js +1 -0
- package/dist/assets/{logs-panel-ABM16htT.js → logs-panel-TyeqEvOY.js} +1 -1
- package/dist/assets/{markdown-renderer-BG3sp2rs.js → markdown-renderer-BFyD6Jhs.js} +1 -1
- package/dist/assets/{menu-items-BrwnTgyK.js → menu-items-BgUyNUpu.js} +1 -1
- package/dist/assets/{mermaid-a3xYymTy.js → mermaid-B6mqMm5_.js} +2 -2
- package/dist/assets/{name-cell-input-BgEBmqyL.js → name-cell-input-D-0VF3qd.js} +1 -1
- package/dist/assets/numbers-CsZVw3Dc.js +1 -0
- package/dist/assets/{objects-snlMn4yk.js → objects-rLPlQ3X_.js} +1 -1
- package/dist/assets/{once-Haz_uxEL.js → once-6KNTRW0u.js} +1 -1
- package/dist/assets/{outline-panel-DCXQ3_F6.js → outline-panel-DMTjk2WC.js} +1 -1
- package/dist/assets/{packages-panel-BMGOmZkR.js → packages-panel-DrT69k9L.js} +1 -1
- package/dist/assets/{panel-accordion-state-C1sdPp0m.js → panel-accordion-state-Ban6j---.js} +1 -1
- package/dist/assets/panels-BLYQRkEc.js +4 -0
- package/dist/assets/precisionRound-BxlUs8G4.js +1 -0
- package/dist/assets/process-output-DzpQYlpY.js +1 -0
- package/dist/assets/{quadrantDiagram-AXDQQJYC-yBhCDI7E.js → quadrantDiagram-AXDQQJYC-D9KH5Zge.js} +1 -1
- package/dist/assets/react-vega-BTNeboR2.js +189 -0
- package/dist/assets/{readonly-code-display-Do50PHI8.js → readonly-code-display-Cyn3k5TX.js} +1 -1
- package/dist/assets/{readonly-python-code-DCSMIsTX.js → readonly-python-code-DrXDDAnA.js} +1 -1
- package/dist/assets/{renderShortcut-BkVJQ_Zh.js → renderShortcut-H60uiHBp.js} +1 -1
- package/dist/assets/{reveal-component-CLE6vB1Z.js → reveal-component-_lKImPWP.js} +3 -3
- package/dist/assets/{run-page-Depd6lEE.js → run-page-7UNg--Rj.js} +1 -1
- package/dist/assets/{runs-BLp5jezv.js → runs-6lJxKV1w.js} +1 -1
- package/dist/assets/{runtime-details-state-CSPobEdR.js → runtime-details-state-CKOKhMhv.js} +1 -1
- package/dist/assets/{schemas-Dv42wLFq.js → schemas-ChRqMOTL.js} +1 -1
- package/dist/assets/{scratchpad-panel-lc6jmpjv.js → scratchpad-panel-C7h4MgqN.js} +1 -1
- package/dist/assets/{scroll-to-bottom-button-1FyowpWV.js → scroll-to-bottom-button-CIkON2WJ.js} +1 -1
- package/dist/assets/{secrets-panel-Bctbtkrp.js → secrets-panel-CmLZsNfp.js} +1 -1
- package/dist/assets/{select-DTBn5eTb.js → select-3CVGlYrC.js} +1 -1
- package/dist/assets/{session-DqoWmb7X.js → session-B8Y-GJnS.js} +1 -1
- package/dist/assets/session-panel-CLiWXc5y.js +1 -0
- package/dist/assets/{snippets-panel-Bh1oTw4m.js → snippets-panel-Wrqaorxa.js} +1 -1
- package/dist/assets/{spec-BfSBcO5B.js → spec-uCpVvaAR.js} +1 -1
- package/dist/assets/{state-uwRvJLb8.js → state-BY4_P6ko.js} +1 -1
- package/dist/assets/{state-BAJYRhmO.js → state-C0irhlOZ.js} +1 -1
- package/dist/assets/{state-DxQnC_1I.js → state-Ck7FMD5A.js} +1 -1
- package/dist/assets/{state-C3oDoyFe.js → state-DaWgt-rn.js} +1 -1
- package/dist/assets/state-TVREh4u4.js +3 -0
- package/dist/assets/{state-XsPO8hNi.js → state-_uXVmwuN.js} +1 -1
- package/dist/assets/{strings-B2vv-ENv.js → strings-C25fhMR_.js} +1 -1
- package/dist/assets/{swiper-component-a07domrZ.js → swiper-component-CRGRy5_O.js} +1 -1
- package/dist/assets/{switch-CTaiAjrz.js → switch-1h011wlH.js} +1 -1
- package/dist/assets/{tabs-BuQlZFbt.js → tabs-CY8E4r9U.js} +1 -1
- package/dist/assets/{terminal-Ck_vK-34.js → terminal-Dp5gyJ7l.js} +3 -3
- package/dist/assets/{time-MgToa8VM.js → time-DswSXBDk.js} +1 -1
- package/dist/assets/{toast-BGzLGnvw.js → toast-Gzxi-le_.js} +1 -1
- package/dist/assets/tooltip-Brya4Ghn.js +61 -0
- package/dist/assets/{tooltip-C9TuY18o.js → tooltip-CkGm8X7m.js} +1 -1
- package/dist/assets/tracing-fUNLyKuj.js +1 -0
- package/dist/assets/{tracing-panel-PeRXHD8_.js → tracing-panel-Cx4OqOoP.js} +2 -2
- package/dist/assets/{tree-actions-GM5E_bUs.js → tree-actions-B1vsdenY.js} +1 -1
- package/dist/assets/{use-restore-focus-BxEGwIbk.js → use-restore-focus-DdS7Tl0F.js} +1 -1
- package/dist/assets/{useAddCell-GR7eEQrT.js → useAddCell-DZ-50qEG.js} +1 -1
- package/dist/assets/useAsyncData-DhhdOGGd.js +1 -0
- package/dist/assets/{useBoolean-BDD6_xQQ.js → useBoolean-5g-bBaYF.js} +1 -1
- package/dist/assets/{useCellActionButton-B-9eG5z_.js → useCellActionButton-DINnRstv.js} +1 -1
- package/dist/assets/{useDeleteCell-2_bMClHd.js → useDeleteCell-CREJXVNp.js} +1 -1
- package/dist/assets/{useDependencyPanelTab-k3C3M18S.js → useDependencyPanelTab-Cw5VT16Q.js} +1 -1
- package/dist/assets/{useEventListener-D9cftcHj.js → useEventListener-CjDeLqFC.js} +1 -1
- package/dist/assets/{useIframeCapabilities-CwTkczVN.js → useIframeCapabilities-Czr9e4uG.js} +1 -1
- package/dist/assets/{useInstallPackage-iX1eufdl.js → useInstallPackage-Cw0dVELh.js} +1 -1
- package/dist/assets/{useInterval--x3s1VpI.js → useInterval-DJdQmXnq.js} +1 -1
- package/dist/assets/useNotebookActions-B4av4mLc.js +1 -0
- package/dist/assets/{useRunCells-B2vUhREg.js → useRunCells-BHBMk9zW.js} +1 -1
- package/dist/assets/{useSplitCell-DEb03htp.js → useSplitCell-DAgwZV0q.js} +1 -1
- package/dist/assets/{useTheme-COhSHkCj.js → useTheme-DLtPc-39.js} +1 -1
- package/dist/assets/{utils-CaQYvazl.js → utils-DfElfBpz.js} +1 -1
- package/dist/assets/vega-component-pQE2mKc1.js +1 -0
- package/dist/assets/{vega-embed-container-DJPNkHPb.js → vega-embed-container-DtArSJz1.js} +1 -1
- package/dist/assets/vega-loader.browser-Bu25TKax.js +6 -0
- package/dist/assets/{write-secret-modal-DzKAInL2.js → write-secret-modal-usRSi1bb.js} +1 -1
- package/dist/assets/{xychartDiagram-S5SC5T6Z-KbGzG6-u.js → xychartDiagram-S5SC5T6Z-CeRnBS5e.js} +1 -1
- package/dist/index.html +76 -77
- package/package.json +4 -2
- package/src/__tests__/fixtures/{pair-preview.ts → pair-config.ts} +3 -2
- package/src/__tests__/mount.test.ts +28 -8
- package/src/components/app-config/ai-config.tsx +5 -1
- package/src/components/data-table/__tests__/date-popover.test.tsx +72 -0
- package/src/components/data-table/__tests__/export-actions.test.tsx +329 -6
- package/src/components/data-table/column-visibility-dropdown.tsx +8 -13
- package/src/components/data-table/columns.tsx +2 -1
- package/src/components/data-table/date-popover.tsx +6 -4
- package/src/components/data-table/export-actions.tsx +192 -116
- package/src/components/data-table/row-viewer-panel/row-viewer.tsx +2 -2
- package/src/components/data-table/schemas.ts +14 -36
- package/src/components/editor/Output.tsx +9 -2
- package/src/components/editor/__tests__/dynamic-favicon.test.tsx +111 -18
- package/src/components/editor/actions/__tests__/pair-with-agent-commands.test.ts +80 -252
- package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +186 -34
- package/src/components/editor/actions/pair-with-agent-commands.ts +42 -51
- package/src/components/editor/actions/pair-with-agent-modal.tsx +96 -36
- package/src/components/editor/ai/add-cell-with-ai.tsx +3 -3
- package/src/components/editor/chrome/panels/error-panel.tsx +1 -0
- package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +366 -0
- package/src/components/editor/chrome/wrapper/footer-item.tsx +32 -24
- package/src/components/editor/chrome/wrapper/footer-items/attachment-status.tsx +237 -0
- package/src/components/editor/chrome/wrapper/footer.tsx +2 -0
- package/src/components/editor/connections/components.tsx +1 -1
- package/src/components/editor/controls/__tests__/command-palette.test.tsx +53 -0
- package/src/components/editor/controls/command-palette.tsx +22 -10
- package/src/components/editor/dynamic-favicon.tsx +21 -40
- package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +537 -0
- package/src/components/editor/errors/send-error-report-button.tsx +69 -0
- package/src/components/editor/output/MarimoErrorOutput.tsx +21 -5
- package/src/components/editor/output/MarimoTracebackOutput.tsx +36 -21
- package/src/components/editor/output/__tests__/ansi-reduce.test.ts +18 -0
- package/src/components/editor/output/ansi-reduce.ts +6 -0
- package/src/components/pages/home-page.tsx +4 -4
- package/src/components/tracing/tracing.tsx +2 -2
- package/src/core/attachments/__tests__/toast.test.tsx +58 -0
- package/src/core/attachments/state.ts +18 -0
- package/src/core/attachments/toast.ts +15 -0
- package/src/core/cells/__tests__/cells.test.ts +52 -0
- package/src/core/cells/__tests__/error-report.test.ts +139 -0
- package/src/core/cells/__tests__/notebook-outline.test.ts +113 -0
- package/src/core/cells/__tests__/session.test.ts +50 -1
- package/src/core/cells/cells.ts +2 -1
- package/src/core/cells/error-report.ts +63 -0
- package/src/core/cells/pending-delete-service.ts +2 -6
- package/src/core/cells/session.ts +28 -1
- package/src/core/config/pair.ts +5 -3
- package/src/core/datasets/__tests__/data-source-discovery.test.ts +4 -4
- package/src/core/datasets/data-source-discovery.ts +4 -6
- package/src/core/edit-app.tsx +2 -2
- package/src/core/host/AGENTS.md +5 -0
- package/src/core/host/__tests__/attachments.test.ts +57 -0
- package/src/core/host/__tests__/document.test.ts +124 -0
- package/src/core/host/__tests__/events.test.ts +46 -0
- package/src/core/host/__tests__/fixtures.ts +39 -0
- package/src/core/host/__tests__/notebook-client.test.ts +433 -0
- package/src/core/host/__tests__/protocol.test.ts +15 -0
- package/src/core/host/__tests__/stream.test.ts +209 -0
- package/src/core/host/connection.ts +81 -0
- package/src/core/host/events.ts +61 -0
- package/src/core/host/notebook-client.ts +290 -0
- package/src/core/host/protocol.ts +11 -6
- package/src/core/host/resources/attachments.ts +19 -0
- package/src/core/host/resources/document.ts +101 -0
- package/src/core/host/resources/resource.ts +47 -0
- package/src/core/host/stream.ts +211 -0
- package/src/core/hotkeys/actions.ts +2 -4
- package/src/core/network/connection.ts +4 -2
- package/src/core/run-app.tsx +2 -2
- package/src/core/runtime/__tests__/runtime.test.ts +55 -11
- package/src/core/runtime/runtime.ts +18 -5
- package/src/core/runtime/types.ts +2 -0
- package/src/core/websocket/__tests__/{useMarimoKernelConnection.test.ts → connection-state.test.ts} +15 -17
- package/src/core/websocket/__tests__/{useWebSocket.test.ts → createConnectionTransport.test.ts} +1 -1
- package/src/core/websocket/__tests__/useNotebookClient.hook.test.tsx +170 -0
- package/src/core/websocket/__tests__/{useMarimoKernelConnection.hook.test.tsx → useNotebookSession.hook.test.tsx} +181 -65
- package/src/core/websocket/connection-state.ts +70 -0
- package/src/core/websocket/state.ts +7 -0
- package/src/core/websocket/transports/__tests__/sse.test.ts +1 -4
- package/src/core/websocket/transports/create.ts +31 -0
- package/src/core/websocket/transports/transport.ts +12 -23
- package/src/core/websocket/types.ts +9 -39
- package/src/core/websocket/useNotebookClient.ts +88 -0
- package/src/core/websocket/{useMarimoKernelConnection.tsx → useNotebookSession.tsx} +59 -225
- package/src/hooks/__tests__/useAsyncData.test.tsx +148 -0
- package/src/hooks/useAsyncData.ts +3 -3
- package/src/hooks/useDataSourceDiscovery.ts +6 -4
- package/src/hooks/useElapsedTime.ts +3 -3
- package/src/mount.tsx +7 -3
- package/src/plugins/core/registerReactComponent.tsx +1 -1
- package/src/plugins/impl/DropdownPlugin.tsx +2 -6
- package/src/plugins/impl/FormPlugin.tsx +1 -1
- package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +84 -0
- 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/vega/make-selectable.ts +3 -6
- package/src/utils/__tests__/arrays.test.ts +35 -0
- package/src/utils/__tests__/numbers.test.ts +48 -1
- package/src/utils/__tests__/objects.test.ts +21 -0
- package/src/utils/arrays.ts +10 -0
- package/src/utils/numbers.ts +28 -17
- package/src/utils/objects.ts +3 -7
- package/src/utils/once.ts +19 -19
- package/dist/assets/CellStatus-BP2mavAq.js +0 -1
- package/dist/assets/JsonOutput-DxHKOpPG.js +0 -11
- package/dist/assets/MarimoErrorOutput-D60XkCny.js +0 -6
- package/dist/assets/ai-model-dropdown-B0z6-zhp.js +0 -5
- package/dist/assets/any-language-editor-BLCAD3yx.js +0 -1
- package/dist/assets/api-BeC_VSGv.js +0 -1
- package/dist/assets/arrays-BqUx5USx.js +0 -1
- package/dist/assets/code-visibility-B-_GvwR5.js +0 -1
- package/dist/assets/coerce-CpE6kUdX.js +0 -1
- package/dist/assets/command-palette-Cf7WX-nr.js +0 -1
- package/dist/assets/config-BOKzA3tF.js +0 -1
- package/dist/assets/config-DX8Y2WbJ.js +0 -1
- package/dist/assets/config-VPSq73n8.js +0 -1
- package/dist/assets/edit-page-BT74CF4G.js +0 -9
- package/dist/assets/feedback-button-B2gKC5cn.js +0 -1
- package/dist/assets/hooks-RnjuFmEx.js +0 -1
- package/dist/assets/index-Bcdzex-3.css +0 -2
- package/dist/assets/index-D-kY3FU1.js +0 -29
- package/dist/assets/linear-0eij_aAG.js +0 -1
- package/dist/assets/links-DkkL9DVa.js +0 -1
- package/dist/assets/numbers-Mk3-CcoU.js +0 -1
- package/dist/assets/pair-CaQyK6En.js +0 -1
- package/dist/assets/panels-Dn59o8ay.js +0 -4
- package/dist/assets/precisionRound-Bq_nmf5u.js +0 -1
- package/dist/assets/process-output-D0KGq04R.js +0 -1
- package/dist/assets/react-vega-C3rYGtP6.js +0 -189
- package/dist/assets/session-panel-BxS5hiG2.js +0 -1
- package/dist/assets/state-_Qai_QyX.js +0 -3
- package/dist/assets/tooltip-mT3CGest.js +0 -61
- package/dist/assets/tracing-Di6pUmX8.js +0 -1
- package/dist/assets/useAsyncData-C7aJJz0Y.js +0 -1
- package/dist/assets/useNotebookActions-DIc4xTzY.js +0 -1
- package/dist/assets/vega-component-CnadTgoE.js +0 -1
- package/dist/assets/vega-loader.browser-CLRkNgm7.js +0 -6
- package/src/core/websocket/useWebSocket.tsx +0 -88
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
import { Kbd } from "@/components/ui/kbd";
|
|
23
23
|
import { Tooltip } from "@/components/ui/tooltip";
|
|
24
24
|
import { getCellEditorView } from "@/core/cells/cells";
|
|
25
|
+
import { buildErrorReport } from "@/core/cells/error-report";
|
|
25
26
|
import type { CellId } from "@/core/cells/ids";
|
|
26
27
|
import { SCRATCH_CELL_ID } from "@/core/cells/ids";
|
|
27
28
|
import { toggleBreakpoint } from "@/core/codemirror/cells/debugger-state";
|
|
@@ -34,6 +35,7 @@ import { isWasm } from "@/core/wasm/utils";
|
|
|
34
35
|
import { renderHTML } from "@/plugins/core/RenderHTML";
|
|
35
36
|
import { sanitizeHtml } from "@/plugins/core/sanitize-html";
|
|
36
37
|
import { copyToClipboard } from "@/utils/copy";
|
|
38
|
+
import { parseHtmlContent } from "@/utils/dom";
|
|
37
39
|
import {
|
|
38
40
|
containsMangledLocal,
|
|
39
41
|
splitMangledLocals,
|
|
@@ -46,33 +48,45 @@ import {
|
|
|
46
48
|
import { useOpenAiAssistant } from "../chrome/wrapper/useOpenAiAssistant";
|
|
47
49
|
import { AIFixButton, buildFixInChatPrompt } from "../errors/auto-fix";
|
|
48
50
|
import { MangledSegments } from "../errors/mangled-local-chip";
|
|
51
|
+
import { SendErrorReportButton } from "../errors/send-error-report-button";
|
|
49
52
|
import { CellLinkTraceback } from "../links/cell-link";
|
|
50
53
|
import type { OnRefactorWithAI } from "../Output";
|
|
51
54
|
|
|
52
55
|
interface Props {
|
|
53
56
|
cellId: CellId | undefined;
|
|
54
57
|
traceback: string;
|
|
58
|
+
tracebackFormat?: "html" | "text";
|
|
59
|
+
code?: string;
|
|
55
60
|
onRefactorWithAI?: OnRefactorWithAI;
|
|
56
61
|
}
|
|
57
62
|
|
|
58
|
-
/**
|
|
59
|
-
* List of errors due to violations of Marimo semantics.
|
|
60
|
-
*/
|
|
63
|
+
/** Render a traceback and actions for its captured execution. */
|
|
61
64
|
export const MarimoTracebackOutput = ({
|
|
62
65
|
onRefactorWithAI,
|
|
63
66
|
traceback,
|
|
67
|
+
tracebackFormat = "html",
|
|
64
68
|
cellId,
|
|
69
|
+
code,
|
|
65
70
|
}: Props): JSX.Element => {
|
|
66
|
-
const htmlTraceback =
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
const htmlTraceback =
|
|
72
|
+
tracebackFormat === "text" ? (
|
|
73
|
+
<pre>{traceback}</pre>
|
|
74
|
+
) : (
|
|
75
|
+
renderHTML({
|
|
76
|
+
html: traceback,
|
|
77
|
+
additionalReplacements: [
|
|
78
|
+
replaceTracebackFilenames,
|
|
79
|
+
replaceTracebackPrefix,
|
|
80
|
+
replaceMangledLocal,
|
|
81
|
+
],
|
|
82
|
+
})
|
|
83
|
+
);
|
|
74
84
|
|
|
75
|
-
const
|
|
85
|
+
const textTraceback =
|
|
86
|
+
tracebackFormat === "html"
|
|
87
|
+
? parseHtmlContent(sanitizeHtml(traceback))
|
|
88
|
+
: traceback;
|
|
89
|
+
const lastTracebackLine = lastLine(textTraceback);
|
|
76
90
|
const aiFeaturesEnabled = useAtomValue(aiFeaturesEnabledAtom);
|
|
77
91
|
const openAiAssistant = useOpenAiAssistant();
|
|
78
92
|
|
|
@@ -131,6 +145,13 @@ export const MarimoTracebackOutput = ({
|
|
|
131
145
|
</div>
|
|
132
146
|
)}
|
|
133
147
|
<div className="flex gap-2">
|
|
148
|
+
{cellId && lastTracebackLine && (
|
|
149
|
+
<SendErrorReportButton
|
|
150
|
+
getContent={() =>
|
|
151
|
+
buildErrorReport(cellId, { code, traceback, tracebackFormat })
|
|
152
|
+
}
|
|
153
|
+
/>
|
|
154
|
+
)}
|
|
134
155
|
{showAIFix && (
|
|
135
156
|
<AIFixButton
|
|
136
157
|
tooltip="Fix with AI"
|
|
@@ -186,11 +207,7 @@ export const MarimoTracebackOutput = ({
|
|
|
186
207
|
</DropdownMenuItem>
|
|
187
208
|
<DropdownMenuItem
|
|
188
209
|
onClick={() => {
|
|
189
|
-
|
|
190
|
-
const div = document.createElement("div");
|
|
191
|
-
div.innerHTML = sanitizeHtml(traceback);
|
|
192
|
-
const textContent = div.textContent || "";
|
|
193
|
-
copyToClipboard(textContent);
|
|
210
|
+
copyToClipboard(textTraceback);
|
|
194
211
|
}}
|
|
195
212
|
>
|
|
196
213
|
<CopyIcon className="h-4 w-4 mr-2" />
|
|
@@ -205,9 +222,7 @@ export const MarimoTracebackOutput = ({
|
|
|
205
222
|
};
|
|
206
223
|
|
|
207
224
|
function lastLine(text: string): string {
|
|
208
|
-
const
|
|
209
|
-
el.innerHTML = sanitizeHtml(text);
|
|
210
|
-
const lines = el.textContent?.split("\n").filter(Boolean);
|
|
225
|
+
const lines = text.split("\n").filter(Boolean);
|
|
211
226
|
return lines?.at(-1) || "";
|
|
212
227
|
}
|
|
213
228
|
|
|
@@ -233,7 +248,7 @@ export const replaceTracebackFilenames = (domNode: DOMNode) => {
|
|
|
233
248
|
<Tooltip content={tooltipContent}>
|
|
234
249
|
<button
|
|
235
250
|
type="button"
|
|
236
|
-
className="ml-1 p-1 rounded-sm hover:bg-muted transition-
|
|
251
|
+
className="ml-1 p-1 rounded-sm hover:bg-muted transition-colors inline"
|
|
237
252
|
>
|
|
238
253
|
<BugPlayIcon
|
|
239
254
|
onClick={() => {
|
|
@@ -7,6 +7,24 @@ import {
|
|
|
7
7
|
TerminalBuffer,
|
|
8
8
|
} from "../ansi-reduce";
|
|
9
9
|
|
|
10
|
+
test("preserves source evidence through stateful output and serialization", () => {
|
|
11
|
+
const output = StatefulOutputMessage.create({
|
|
12
|
+
channel: "stderr",
|
|
13
|
+
mimetype: "application/vnd.marimo+traceback",
|
|
14
|
+
timestamp: 1,
|
|
15
|
+
data: "<pre>ValueError:",
|
|
16
|
+
code: "raise ValueError('failed code')",
|
|
17
|
+
}).appendData(" failed code</pre>");
|
|
18
|
+
expect(output.code).toBe("raise ValueError('failed code')");
|
|
19
|
+
expect(output.toJSON()).toEqual({
|
|
20
|
+
channel: "stderr",
|
|
21
|
+
mimetype: "application/vnd.marimo+traceback",
|
|
22
|
+
timestamp: 1,
|
|
23
|
+
data: "<pre>ValueError: failed code</pre>",
|
|
24
|
+
code: "raise ValueError('failed code')",
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
10
28
|
describe("TerminalBuffer", () => {
|
|
11
29
|
test("writeChar writes single character", () => {
|
|
12
30
|
const buffer = new TerminalBuffer();
|
|
@@ -368,6 +368,7 @@ export class StatefulOutputMessage implements OutputMessage {
|
|
|
368
368
|
public readonly mimetype: OutputMessage["mimetype"];
|
|
369
369
|
public readonly channel: OutputMessage["channel"];
|
|
370
370
|
public readonly timestamp: OutputMessage["timestamp"];
|
|
371
|
+
public readonly code: OutputMessage["code"];
|
|
371
372
|
private ansiReducer = new AnsiReducer();
|
|
372
373
|
private _data: string;
|
|
373
374
|
|
|
@@ -383,6 +384,7 @@ export class StatefulOutputMessage implements OutputMessage {
|
|
|
383
384
|
message.channel,
|
|
384
385
|
message.timestamp,
|
|
385
386
|
ansiReducer,
|
|
387
|
+
message.code,
|
|
386
388
|
);
|
|
387
389
|
}
|
|
388
390
|
|
|
@@ -391,10 +393,12 @@ export class StatefulOutputMessage implements OutputMessage {
|
|
|
391
393
|
channel: OutputMessage["channel"],
|
|
392
394
|
timestamp: OutputMessage["timestamp"],
|
|
393
395
|
ansiReducer: AnsiReducer,
|
|
396
|
+
code: OutputMessage["code"],
|
|
394
397
|
) {
|
|
395
398
|
this.mimetype = mimetype;
|
|
396
399
|
this.channel = channel;
|
|
397
400
|
this.timestamp = timestamp;
|
|
401
|
+
this.code = code;
|
|
398
402
|
this.ansiReducer = ansiReducer;
|
|
399
403
|
this._data = this.ansiReducer.render();
|
|
400
404
|
}
|
|
@@ -406,6 +410,7 @@ export class StatefulOutputMessage implements OutputMessage {
|
|
|
406
410
|
this.channel,
|
|
407
411
|
this.timestamp,
|
|
408
412
|
this.ansiReducer,
|
|
413
|
+
this.code,
|
|
409
414
|
);
|
|
410
415
|
}
|
|
411
416
|
|
|
@@ -415,6 +420,7 @@ export class StatefulOutputMessage implements OutputMessage {
|
|
|
415
420
|
channel: this.channel,
|
|
416
421
|
timestamp: this.timestamp,
|
|
417
422
|
data: this.data,
|
|
423
|
+
...(this.code === undefined ? {} : { code: this.code }),
|
|
418
424
|
};
|
|
419
425
|
}
|
|
420
426
|
}
|
|
@@ -514,7 +514,7 @@ const MarimoFileComponent = ({ file }: { file: MarimoFile }) => {
|
|
|
514
514
|
|
|
515
515
|
return (
|
|
516
516
|
<a
|
|
517
|
-
className="py-1.5 px-4 hover:bg-(--blue-2) hover:text-primary transition-
|
|
517
|
+
className="py-1.5 px-4 hover:bg-(--blue-2) hover:text-primary transition-colors duration-300 cursor-pointer group relative flex gap-4 items-center"
|
|
518
518
|
key={file.path}
|
|
519
519
|
href={href.toString()}
|
|
520
520
|
target={tabTarget(file.initializationId || file.path)}
|
|
@@ -542,7 +542,7 @@ const MarimoFileComponent = ({ file }: { file: MarimoFile }) => {
|
|
|
542
542
|
</div>
|
|
543
543
|
<ExternalLinkIcon
|
|
544
544
|
size={20}
|
|
545
|
-
className="group-hover:opacity-100 opacity-0 transition-
|
|
545
|
+
className="group-hover:opacity-100 opacity-0 transition-opacity duration-300 text-primary"
|
|
546
546
|
/>
|
|
547
547
|
</div>
|
|
548
548
|
{!!file.lastModified && (
|
|
@@ -617,14 +617,14 @@ const CreateNewNotebook: React.FC = () => {
|
|
|
617
617
|
<a
|
|
618
618
|
className="relative rounded-lg p-6 group
|
|
619
619
|
text-primary hover:bg-(--blue-2) shadow-md-solid shadow-accent border bg-(--blue-1)
|
|
620
|
-
transition-
|
|
620
|
+
transition-colors duration-300 cursor-pointer
|
|
621
621
|
"
|
|
622
622
|
href={url}
|
|
623
623
|
target="_blank"
|
|
624
624
|
rel="noreferrer"
|
|
625
625
|
>
|
|
626
626
|
<h2 className="text-lg font-semibold">Create a new notebook</h2>
|
|
627
|
-
<div className="group-hover:opacity-100 opacity-0 absolute right-5 top-0 bottom-0 rounded-lg flex items-center justify-center transition-
|
|
627
|
+
<div className="group-hover:opacity-100 opacity-0 absolute right-5 top-0 bottom-0 rounded-lg flex items-center justify-center transition-opacity duration-300">
|
|
628
628
|
<ExternalLinkIcon size={24} />
|
|
629
629
|
</div>
|
|
630
630
|
</a>
|
|
@@ -145,11 +145,11 @@ const TraceBlock: React.FC<{
|
|
|
145
145
|
isMostRecentRun: boolean;
|
|
146
146
|
chartPosition: ChartPosition;
|
|
147
147
|
theme: ResolvedTheme;
|
|
148
|
-
}> = ({ run, isMostRecentRun, chartPosition, isExpanded, theme }) => {
|
|
148
|
+
}> = ({ run, isMostRecentRun, chartPosition, isExpanded: expanded, theme }) => {
|
|
149
149
|
const setExpandedRuns = useSetAtom(expandedRunsAtom);
|
|
150
150
|
// We prefer the user's last click, but if they haven't clicked on this run,
|
|
151
151
|
// we expand the most recent run by default, otherwise we collapse it.
|
|
152
|
-
isExpanded =
|
|
152
|
+
const isExpanded = expanded ?? isMostRecentRun;
|
|
153
153
|
|
|
154
154
|
const onToggleExpanded = () => {
|
|
155
155
|
setExpandedRuns((prev) => {
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
// @vitest-environment jsdom
|
|
3
|
+
|
|
4
|
+
import { act, render, renderHook } from "@testing-library/react";
|
|
5
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
6
|
+
import { Toaster } from "@/components/ui/toaster";
|
|
7
|
+
import { clearToasts, toast, useToast } from "@/components/ui/use-toast";
|
|
8
|
+
import { agentToast } from "../toast";
|
|
9
|
+
|
|
10
|
+
describe("agent notifications", () => {
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
vi.useFakeTimers();
|
|
13
|
+
clearToasts();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
act(() => {
|
|
18
|
+
clearToasts();
|
|
19
|
+
vi.runOnlyPendingTimers();
|
|
20
|
+
});
|
|
21
|
+
vi.useRealTimers();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it.each([
|
|
25
|
+
{ variant: undefined, duration: 3000 },
|
|
26
|
+
{ variant: "danger" as const, duration: 5000 },
|
|
27
|
+
])("expires while the window is blurred: %j", ({ variant, duration }) => {
|
|
28
|
+
const { result } = renderHook(useToast);
|
|
29
|
+
render(<Toaster />);
|
|
30
|
+
act(() => agentToast({ title: "Agent notice", variant }));
|
|
31
|
+
act(() => window.dispatchEvent(new Event("blur")));
|
|
32
|
+
act(() => vi.advanceTimersByTime(duration - 1));
|
|
33
|
+
expect(result.current.toasts[0].open).toBe(true);
|
|
34
|
+
act(() => vi.advanceTimersByTime(1));
|
|
35
|
+
expect(result.current.toasts[0].open).toBe(false);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("preserves pause behavior for ordinary notifications", () => {
|
|
39
|
+
const { result } = renderHook(useToast);
|
|
40
|
+
render(<Toaster />);
|
|
41
|
+
act(() => toast({ title: "Ordinary notice", duration: 3000 }));
|
|
42
|
+
act(() => window.dispatchEvent(new Event("blur")));
|
|
43
|
+
act(() => vi.advanceTimersByTime(5000));
|
|
44
|
+
expect(result.current.toasts[0].open).toBe(true);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("does not let an older timer dismiss a newer notification", () => {
|
|
48
|
+
const { result } = renderHook(useToast);
|
|
49
|
+
act(() => agentToast({ title: "First" }));
|
|
50
|
+
act(() => vi.advanceTimersByTime(1000));
|
|
51
|
+
act(() => agentToast({ title: "Second" }));
|
|
52
|
+
act(() => vi.advanceTimersByTime(2000));
|
|
53
|
+
expect(result.current.toasts[0].title).toBe("Second");
|
|
54
|
+
expect(result.current.toasts[0].open).toBe(true);
|
|
55
|
+
act(() => vi.advanceTimersByTime(1000));
|
|
56
|
+
expect(result.current.toasts[0].open).toBe(false);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { atom } from "jotai";
|
|
4
|
+
import type { Attachment } from "@/core/host/protocol";
|
|
5
|
+
import { notebookClientAtom } from "@/core/websocket/state";
|
|
6
|
+
|
|
7
|
+
export type { Attachment } from "@/core/host/protocol";
|
|
8
|
+
|
|
9
|
+
export const agentAttachmentsAtom = atom((get) => {
|
|
10
|
+
const notebook = get(notebookClientAtom);
|
|
11
|
+
return (notebook ? get(notebook.attachmentsAtom) : []).filter(
|
|
12
|
+
(attachment) => attachment.kind === "agent",
|
|
13
|
+
);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export function attachmentLabel(attachment: Attachment): string {
|
|
17
|
+
return attachment.name || "Agent";
|
|
18
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { toast } from "@/components/ui/use-toast";
|
|
4
|
+
|
|
5
|
+
export function agentToast(
|
|
6
|
+
options: Pick<
|
|
7
|
+
Parameters<typeof toast>[0],
|
|
8
|
+
"title" | "variant" | "description"
|
|
9
|
+
>,
|
|
10
|
+
) {
|
|
11
|
+
const duration = options.variant === "danger" ? 5000 : 3000;
|
|
12
|
+
const { dismiss } = toast({ ...options, duration });
|
|
13
|
+
// Agent notices must also expire while the user focuses the agent terminal.
|
|
14
|
+
window.setTimeout(dismiss, duration);
|
|
15
|
+
}
|
|
@@ -789,6 +789,58 @@ describe("cell reducer", () => {
|
|
|
789
789
|
`);
|
|
790
790
|
});
|
|
791
791
|
|
|
792
|
+
it("keeps error evidence after an edit and a new queued run", () => {
|
|
793
|
+
const code = "raise ValueError('old failure')";
|
|
794
|
+
const output: OutputMessage = {
|
|
795
|
+
channel: "marimo-error",
|
|
796
|
+
mimetype: "application/vnd.marimo+error",
|
|
797
|
+
data: [
|
|
798
|
+
{
|
|
799
|
+
type: "exception",
|
|
800
|
+
msg: "old failure",
|
|
801
|
+
exception_type: "ValueError",
|
|
802
|
+
raising_cell: null,
|
|
803
|
+
traceback: "<pre>ValueError: old failure</pre>",
|
|
804
|
+
},
|
|
805
|
+
],
|
|
806
|
+
code,
|
|
807
|
+
timestamp: 1,
|
|
808
|
+
};
|
|
809
|
+
actions.handleCellMessage({
|
|
810
|
+
cell_id: firstCellId,
|
|
811
|
+
output,
|
|
812
|
+
console: {
|
|
813
|
+
channel: "stderr",
|
|
814
|
+
mimetype: "application/vnd.marimo+traceback",
|
|
815
|
+
data: "<pre>ValueError: old failure</pre>",
|
|
816
|
+
code,
|
|
817
|
+
timestamp: 1,
|
|
818
|
+
},
|
|
819
|
+
status: "idle",
|
|
820
|
+
stale_inputs: null,
|
|
821
|
+
timestamp: 1,
|
|
822
|
+
});
|
|
823
|
+
actions.updateCellCode({
|
|
824
|
+
cellId: firstCellId,
|
|
825
|
+
code: "print('new code')",
|
|
826
|
+
formattingChange: false,
|
|
827
|
+
});
|
|
828
|
+
actions.prepareForRun({ cellId: firstCellId });
|
|
829
|
+
actions.handleCellMessage({
|
|
830
|
+
cell_id: firstCellId,
|
|
831
|
+
output: null,
|
|
832
|
+
console: null,
|
|
833
|
+
status: "queued",
|
|
834
|
+
stale_inputs: null,
|
|
835
|
+
timestamp: 2,
|
|
836
|
+
});
|
|
837
|
+
const runtime = state.cellRuntime[firstCellId];
|
|
838
|
+
expect(state.cellData[firstCellId].lastCodeRun).toBe("print('new code')");
|
|
839
|
+
expect(runtime.output).toEqual(output);
|
|
840
|
+
expect(runtime.consoleOutputs[0].code).toBe(code);
|
|
841
|
+
expect(runtime.consoleOutputs[0].data).toContain("old failure");
|
|
842
|
+
});
|
|
843
|
+
|
|
792
844
|
it("can run cell and receive cell messages", () => {
|
|
793
845
|
// HAPPY PATH
|
|
794
846
|
/////////////////
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
// @vitest-environment jsdom
|
|
3
|
+
|
|
4
|
+
import { describe, expect, it } from "vitest";
|
|
5
|
+
import { cellId } from "@/__tests__/branded";
|
|
6
|
+
import type { MarimoError } from "@/core/kernel/messages";
|
|
7
|
+
import { buildErrorReport, ERROR_MESSAGE_INSTRUCTIONS } from "../error-report";
|
|
8
|
+
|
|
9
|
+
const CELL_ID = cellId("failed-cell");
|
|
10
|
+
|
|
11
|
+
describe("buildErrorReport", () => {
|
|
12
|
+
it("preserves plain traceback text without treating angle brackets as HTML", () => {
|
|
13
|
+
const traceback =
|
|
14
|
+
' File "<cell>", line 1\nValueError: expected <value> & got <other>';
|
|
15
|
+
expect(
|
|
16
|
+
buildErrorReport(CELL_ID, { traceback, tracebackFormat: "text" }).output,
|
|
17
|
+
).toEqual({
|
|
18
|
+
type: "error",
|
|
19
|
+
instructions: ERROR_MESSAGE_INSTRUCTIONS,
|
|
20
|
+
message: "ValueError: expected <value> & got <other>",
|
|
21
|
+
traceback,
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
it("owns the instruction to repair the live notebook", () => {
|
|
25
|
+
const report = buildErrorReport(CELL_ID, {
|
|
26
|
+
errors: [{ type: "syntax", msg: "invalid code" }],
|
|
27
|
+
});
|
|
28
|
+
expect(report.output.instructions).toEqual("Fix this notebook error.");
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("keeps repair intent visible before oversized evidence", () => {
|
|
32
|
+
const report = buildErrorReport(CELL_ID, {
|
|
33
|
+
code: "# long code\n".repeat(10_000),
|
|
34
|
+
errors: [
|
|
35
|
+
{
|
|
36
|
+
type: "exception",
|
|
37
|
+
exception_type: "ValueError",
|
|
38
|
+
msg: "long error ".repeat(10_000),
|
|
39
|
+
raising_cell: null,
|
|
40
|
+
traceback: `<pre>Traceback\n${"long frame\n".repeat(10_000)}</pre>`,
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
});
|
|
44
|
+
const serialized = JSON.stringify(report);
|
|
45
|
+
|
|
46
|
+
expect(serialized.length).toBeGreaterThan(8192);
|
|
47
|
+
expect(serialized.slice(0, 256)).toContain(
|
|
48
|
+
'"instructions":"Fix this notebook error."',
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("retains every message and complete traceback without display limits", () => {
|
|
53
|
+
const traceback = `Traceback\n${"long frame\n".repeat(10_000)}ValueError: bad <value>`;
|
|
54
|
+
const errors: MarimoError[] = [
|
|
55
|
+
{
|
|
56
|
+
type: "exception",
|
|
57
|
+
msg: "bad <value>",
|
|
58
|
+
exception_type: "ValueError",
|
|
59
|
+
raising_cell: null,
|
|
60
|
+
traceback: `<pre>${traceback.replaceAll("<", "<").replaceAll(">", ">")}</pre>`,
|
|
61
|
+
},
|
|
62
|
+
{ type: "syntax", msg: "another error" },
|
|
63
|
+
];
|
|
64
|
+
expect(buildErrorReport(CELL_ID, { code: "failed()", errors })).toEqual({
|
|
65
|
+
type: "cell-output",
|
|
66
|
+
cell_id: CELL_ID,
|
|
67
|
+
code: "failed()",
|
|
68
|
+
output: {
|
|
69
|
+
type: "error",
|
|
70
|
+
instructions: ERROR_MESSAGE_INSTRUCTIONS,
|
|
71
|
+
message: "bad <value>\nanother error",
|
|
72
|
+
traceback,
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("leaves source unknown for a restored legacy exception", () => {
|
|
78
|
+
expect(
|
|
79
|
+
buildErrorReport(CELL_ID, {
|
|
80
|
+
errors: [
|
|
81
|
+
{
|
|
82
|
+
type: "exception",
|
|
83
|
+
exception_type: "exception",
|
|
84
|
+
msg: "legacy error",
|
|
85
|
+
raising_cell: null,
|
|
86
|
+
traceback: null,
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
}),
|
|
90
|
+
).toEqual({
|
|
91
|
+
type: "cell-output",
|
|
92
|
+
cell_id: CELL_ID,
|
|
93
|
+
output: {
|
|
94
|
+
type: "error",
|
|
95
|
+
instructions: ERROR_MESSAGE_INSTRUCTIONS,
|
|
96
|
+
message: "legacy error",
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("preserves empty captured code", () => {
|
|
102
|
+
expect(
|
|
103
|
+
buildErrorReport(CELL_ID, {
|
|
104
|
+
code: "",
|
|
105
|
+
errors: [{ type: "syntax", msg: "invalid code" }],
|
|
106
|
+
}),
|
|
107
|
+
).toEqual({
|
|
108
|
+
type: "cell-output",
|
|
109
|
+
cell_id: CELL_ID,
|
|
110
|
+
code: "",
|
|
111
|
+
output: {
|
|
112
|
+
type: "error",
|
|
113
|
+
instructions: ERROR_MESSAGE_INSTRUCTIONS,
|
|
114
|
+
message: "invalid code",
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("keeps separate structured tracebacks in order", () => {
|
|
120
|
+
const errors: MarimoError[] = ["first", "second"].map((message) => ({
|
|
121
|
+
type: "exception",
|
|
122
|
+
exception_type: "ValueError",
|
|
123
|
+
raising_cell: null,
|
|
124
|
+
msg: message,
|
|
125
|
+
traceback: `<pre>Traceback\nValueError: ${message}</pre>`,
|
|
126
|
+
}));
|
|
127
|
+
expect(buildErrorReport(CELL_ID, { errors })).toEqual({
|
|
128
|
+
type: "cell-output",
|
|
129
|
+
cell_id: CELL_ID,
|
|
130
|
+
output: {
|
|
131
|
+
type: "error",
|
|
132
|
+
instructions: ERROR_MESSAGE_INSTRUCTIONS,
|
|
133
|
+
message: "first\nsecond",
|
|
134
|
+
traceback:
|
|
135
|
+
"Traceback\nValueError: first\n\nTraceback\nValueError: second",
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
});
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { createStore } from "jotai";
|
|
4
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
import { MockNotebook } from "@/__mocks__/notebook";
|
|
6
|
+
import { cellId } from "@/__tests__/branded";
|
|
7
|
+
import {
|
|
8
|
+
createNotebookActions,
|
|
9
|
+
notebookAtom,
|
|
10
|
+
notebookOutline,
|
|
11
|
+
notebookReducer,
|
|
12
|
+
} from "../cells";
|
|
13
|
+
import { exportedForTesting } from "../document-changes";
|
|
14
|
+
import type { Outline } from "../outline";
|
|
15
|
+
|
|
16
|
+
const first = cellId("first");
|
|
17
|
+
const second = cellId("second");
|
|
18
|
+
const firstOutline: Outline = {
|
|
19
|
+
items: [{ name: "First", level: 1, by: { id: "first" } }],
|
|
20
|
+
};
|
|
21
|
+
const secondOutline: Outline = {
|
|
22
|
+
items: [{ name: "Second", level: 2, by: { id: "second" } }],
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
function setup() {
|
|
26
|
+
const store = createStore();
|
|
27
|
+
store.set(
|
|
28
|
+
notebookAtom,
|
|
29
|
+
MockNotebook.notebookState({
|
|
30
|
+
cellData: { [first]: { code: "x = 1" }, [second]: {} },
|
|
31
|
+
cellRuntime: {
|
|
32
|
+
[first]: { outline: firstOutline },
|
|
33
|
+
[second]: { outline: secondOutline },
|
|
34
|
+
},
|
|
35
|
+
}),
|
|
36
|
+
);
|
|
37
|
+
const actions = createNotebookActions((action) =>
|
|
38
|
+
store.set(notebookAtom, (state) => notebookReducer(state, action)),
|
|
39
|
+
);
|
|
40
|
+
return { store, actions };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
afterEach(() => {
|
|
44
|
+
exportedForTesting.cancelPendingChanges();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe("notebookOutline", () => {
|
|
48
|
+
it("does not notify outline subscribers when cell code changes", () => {
|
|
49
|
+
const { store, actions } = setup();
|
|
50
|
+
const initial = store.get(notebookOutline);
|
|
51
|
+
const listener = vi.fn();
|
|
52
|
+
const unsubscribe = store.sub(notebookOutline, listener);
|
|
53
|
+
|
|
54
|
+
actions.updateCellCode({
|
|
55
|
+
cellId: first,
|
|
56
|
+
code: "x = 2",
|
|
57
|
+
formattingChange: false,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
expect(store.get(notebookOutline)).toBe(initial);
|
|
61
|
+
expect(listener).not.toHaveBeenCalled();
|
|
62
|
+
unsubscribe();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("updates headings when runtime outlines change or are removed", () => {
|
|
66
|
+
const { store } = setup();
|
|
67
|
+
const updated: Outline = {
|
|
68
|
+
items: [{ name: "Updated", level: 1, by: { id: "updated" } }],
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
for (const outline of [updated, null]) {
|
|
72
|
+
store.set(notebookAtom, (state) => ({
|
|
73
|
+
...state,
|
|
74
|
+
cellRuntime: {
|
|
75
|
+
...state.cellRuntime,
|
|
76
|
+
[first]: { ...state.cellRuntime[first], outline },
|
|
77
|
+
},
|
|
78
|
+
}));
|
|
79
|
+
expect(store.get(notebookOutline)).toEqual({
|
|
80
|
+
items: [...(outline?.items ?? []), ...secondOutline.items],
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("keeps headings in cell order after moving and deleting cells", () => {
|
|
86
|
+
const { store, actions } = setup();
|
|
87
|
+
actions.moveCell({ cellId: second, before: true });
|
|
88
|
+
expect(store.get(notebookOutline)).toEqual({
|
|
89
|
+
items: [...secondOutline.items, ...firstOutline.items],
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
actions.deleteCell({ cellId: second });
|
|
93
|
+
expect(store.get(notebookOutline)).toEqual(firstOutline);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("notifies subscribers when output is replaced with the same headings", () => {
|
|
97
|
+
const { store } = setup();
|
|
98
|
+
const listener = vi.fn();
|
|
99
|
+
const unsubscribe = store.sub(notebookOutline, listener);
|
|
100
|
+
|
|
101
|
+
store.set(notebookAtom, (state) => ({
|
|
102
|
+
...state,
|
|
103
|
+
cellRuntime: {
|
|
104
|
+
...state.cellRuntime,
|
|
105
|
+
[first]: { ...state.cellRuntime[first], outline: { ...firstOutline } },
|
|
106
|
+
},
|
|
107
|
+
}));
|
|
108
|
+
|
|
109
|
+
// Consumers must reconnect to replacement heading elements in the DOM.
|
|
110
|
+
expect(listener).toHaveBeenCalledOnce();
|
|
111
|
+
unsubscribe();
|
|
112
|
+
});
|
|
113
|
+
});
|