@marimo-team/islands 0.25.3-dev267 → 0.25.3-dev269
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-C4yCN38h.js → ConnectedDataExplorerComponent-eLHPh6uu.js} +8 -8
- package/dist/{ErrorBoundary-CewXPyL5.js → ErrorBoundary-DCtynjHZ.js} +4 -4
- package/dist/{abnfDiagram-VCTEODGH-o6Ay6Q63.js → abnfDiagram-VCTEODGH-hqd0IBtX.js} +2 -2
- package/dist/{architectureDiagram-5GKGNRK7-BhiwwlVg.js → architectureDiagram-5GKGNRK7-BzLAFcc0.js} +1 -1
- package/dist/{strings-BCKpLFNU.js → assertNever-DIU3JJbt.js} +14 -14
- package/dist/{chat-ui-CbgR5ivy.js → chat-ui-BWIkve2R.js} +17 -17
- package/dist/{chunk-IMKFNOWR-WMSMf4Nj.js → chunk-IMKFNOWR-DOeVvuyE.js} +1 -1
- package/dist/{chunk-SHT3W25Y-BRfADrzD.js → chunk-SHT3W25Y-DZoFI8wR.js} +1 -1
- package/dist/{chunk-SVP7TREG-CuK5QFre.js → chunk-SVP7TREG-CWu86PSW.js} +1 -1
- package/dist/{chunk-TICWLB2K-Bcun_jhJ.js → chunk-TICWLB2K-BrM0GoP3.js} +1 -1
- package/dist/{classDiagram-ZZMXUADV-CgWDbUDT.js → classDiagram-ZZMXUADV-DYi4V2bB.js} +2 -2
- package/dist/{classDiagram-v2-VYDZK3BY-DYmc9SFU.js → classDiagram-v2-VYDZK3BY-tN7FIk-m.js} +2 -2
- package/dist/{code-visibility-BKBl9Rh6.js → code-visibility-CMF5tcNj.js} +44 -47
- package/dist/{common-N3Jd_6m9.js → common-DYAjQ1bK.js} +4866 -4857
- package/dist/{config-CmeG_Pb6.js → config-BsPruWMR.js} +2 -2
- package/dist/{createReducer-C_syF8Td.js → createReducer-DdtT_7ji.js} +1 -1
- package/dist/{cynefinDiagram-5FMLGOSQ-PPKWipjT.js → cynefinDiagram-5FMLGOSQ-D5nCnDc-.js} +1 -1
- package/dist/{dagre-GXQ25YYZ-niRBZh--.js → dagre-GXQ25YYZ-BHkd7f_i.js} +1 -1
- package/dist/{data-grid-overlay-editor-Cp59CrqZ.js → data-grid-overlay-editor-B6oND8pX.js} +1 -1
- package/dist/{diagram-S7CK7UJ4-DPiP9rot.js → diagram-S7CK7UJ4-D1ALppT2.js} +1 -1
- package/dist/{diagram-UQ7AKVKN-B0WZuaxV.js → diagram-UQ7AKVKN-DLv0b83L.js} +1 -1
- package/dist/{diagram-VX7I27RA-Rn42WBuN.js → diagram-VX7I27RA-7XJ7J6qg.js} +1 -1
- package/dist/{diagram-Z3DM3KII-BB93--ry.js → diagram-Z3DM3KII-7M7zbHp_.js} +1 -1
- package/dist/{dialog-BtprQCUw.js → dialog-DzCj1qQv.js} +1 -1
- package/dist/{dropdown-menu-BpImEfn3.js → dropdown-menu-CdXE15an.js} +3 -3
- package/dist/{ebnfDiagram-PWID7BFC-V4t5h6mZ.js → ebnfDiagram-PWID7BFC-DHH1qlmV.js} +2 -2
- package/dist/{erDiagram-RLTQ6QDP-C1MsAKXd.js → erDiagram-RLTQ6QDP-fI_0dGef.js} +1 -1
- package/dist/{error-entries-CJGdD2ky.js → error-entries-CxN-Nq3V.js} +519 -458
- package/dist/{feedback-button-Qc1f9iPq.js → feedback-button-BelKx6R5.js} +22 -22
- package/dist/{flowDiagram-HODETNUW-DHjeJP96.js → flowDiagram-HODETNUW-Brg9MA6i.js} +2 -2
- package/dist/{formats-gGospKWJ.js → formats-DbSvAPY-.js} +1 -1
- package/dist/{glide-data-editor-hCq3K0sd.js → glide-data-editor-CMZ-kv0S.js} +11 -11
- package/dist/{glide-utils-DC3sk6EX.js → glide-utils-BSw3SSHF.js} +1 -1
- package/dist/{globals-B2_uMYDY.js → globals-DV77npCG.js} +2 -2
- package/dist/{html-to-image-S7spRQCi.js → html-to-image-Bc2AvXHG.js} +5121 -5121
- package/dist/{infoDiagram-27XIBGKW-Dovv6iKx.js → infoDiagram-27XIBGKW-E9e403YT.js} +1 -1
- package/dist/{ishikawaDiagram-5VMMS53U-D2nyygsc.js → ishikawaDiagram-5VMMS53U-C-RlYDRo.js} +1 -1
- package/dist/{kanban-definition-UXKFOSKX-KyVx0uTY.js → kanban-definition-UXKFOSKX-CLFl_uqK.js} +1 -1
- package/dist/main.js +5972 -6003
- package/dist/{mermaid-B-jKSwGt.js → mermaid-rd09e1JL.js} +34 -34
- package/dist/{mindmap-definition-YA3MSWOX-Bz9tKQFl.js → mindmap-definition-YA3MSWOX-BQ8TWtyB.js} +1 -1
- package/dist/{number-overlay-editor-CfNYvbdz.js → number-overlay-editor-CFTuKnrY.js} +1 -1
- package/dist/{pegDiagram-XKGWAZYB-C_qpq6Tv.js → pegDiagram-XKGWAZYB-CN8UHFPi.js} +2 -2
- package/dist/{pieDiagram-E7YTZNPT-Bp9ucgI4.js → pieDiagram-E7YTZNPT-DGuJ0EBd.js} +1 -1
- package/dist/{process-output-f34bkXTm.js → process-output-qecD7Dk8.js} +3 -3
- package/dist/{railroadDiagram-O6MQD6OU-tq-VEH6j.js → railroadDiagram-O6MQD6OU-BPDA8wy8.js} +2 -2
- package/dist/{requirementDiagram-BXWQKSXE-DW-vHadb.js → requirementDiagram-BXWQKSXE-ChzLGbGW.js} +1 -1
- package/dist/{reveal-component-CUxv-kkP.js → reveal-component-Doix35eL.js} +1094 -1094
- package/dist/{select-C33FVPPN.js → select-OxgozOC8.js} +1 -1
- package/dist/{sequenceDiagram-WJ2MYXX4-Dv9U9qRP.js → sequenceDiagram-WJ2MYXX4-DFX-bE6O.js} +1 -1
- package/dist/{spec-DH1lmF7Q.js → spec-oJn0RhpQ.js} +2 -2
- package/dist/{stateDiagram-D77RDMKH-DTmPQ0EB.js → stateDiagram-D77RDMKH-BR7cI3wK.js} +2 -2
- package/dist/{stateDiagram-v2-MP3YSRHH-CkCif0Iw.js → stateDiagram-v2-MP3YSRHH-oGqB5UDI.js} +2 -2
- package/dist/{static-state-BldGd99T.js → static-state-CuV6Pc3a.js} +1 -1
- package/dist/{swimlanes-42K2YHIH-21kCq6l8.js → swimlanes-42K2YHIH-DwSZl1yC.js} +1 -1
- package/dist/{swimlanesDiagram-VR7AAH4N-BpRJP6KU.js → swimlanesDiagram-VR7AAH4N-BHlcd-Ri.js} +2 -2
- package/dist/{timeline-definition-24CTP7MA-Ckgh4lS4.js → timeline-definition-24CTP7MA-DMmP-G2O.js} +1 -1
- package/dist/{toast-DpczUryP.js → toast-DbBHmxmB.js} +1 -1
- package/dist/{useAsyncData-DsZMvNXn.js → useAsyncData-BYIAoMqn.js} +1 -1
- package/dist/{useDateFormatter-Dk_xFci4.js → useDateFormatter-ButWkdcK.js} +1 -1
- package/dist/{useDeepCompareMemoize-KMBBux8C.js → useDeepCompareMemoize-Bz0c5GN6.js} +1 -1
- package/dist/{useTheme-BZ2GGLgm.js → useTheme-DPLobSFs.js} +7 -7
- package/dist/{utils-DXkj82sK.js → utils-EBklsC5R.js} +31 -31
- package/dist/{vega-component-CF9kI6C4.js → vega-component-rxIxAAh9.js} +6 -6
- package/dist/{vennDiagram-4TSXK5OY-CDei2Bt3.js → vennDiagram-4TSXK5OY-Db8GtH1G.js} +1 -1
- package/dist/{wardleyDiagram-VM6X3IG4-CXavT601.js → wardleyDiagram-VM6X3IG4-DmIiC3FU.js} +1 -1
- package/dist/{xychartDiagram-S5SC5T6Z-CvJhcZri.js → xychartDiagram-S5SC5T6Z-DS9OPrNq.js} +1 -1
- package/package.json +1 -1
- package/src/__mocks__/executions.ts +195 -0
- package/src/__mocks__/notebook.ts +0 -1
- package/src/__tests__/CellStatus.test.tsx +19 -20
- package/src/components/dependency-graph/minimap-content.tsx +3 -2
- package/src/components/editor/__tests__/data-attributes.test.tsx +0 -7
- package/src/components/editor/__tests__/output-persistence.test.tsx +2 -2
- package/src/components/editor/actions/__tests__/useCellActionButton.test.tsx +1 -0
- package/src/components/editor/actions/useCellActionButton.tsx +7 -3
- package/src/components/editor/alerts/stdin-blocking-alert.tsx +3 -1
- package/src/components/editor/cell/CellStatus.tsx +23 -19
- package/src/components/editor/cell/RunButton.tsx +16 -6
- package/src/components/editor/cell/__tests__/useRunCells.test.ts +45 -0
- package/src/components/editor/cell/cell-context-menu.tsx +4 -1
- package/src/components/editor/cell/code/cell-editor.tsx +7 -5
- package/src/components/editor/cell/collapse.tsx +4 -0
- package/src/components/editor/cell/useRunCells.ts +18 -3
- package/src/components/editor/chrome/wrapper/sidebar.tsx +2 -4
- package/src/components/editor/header/status.tsx +4 -2
- package/src/components/editor/navigation/navigation.ts +3 -2
- package/src/components/editor/notebook-cell.tsx +82 -83
- package/src/components/editor/renderers/grid-layout/__tests__/grid-layout.test.tsx +75 -0
- package/src/components/editor/renderers/grid-layout/grid-layout.tsx +22 -16
- package/src/components/editor/renderers/slides-layout/__tests__/plugin.test.ts +0 -1
- package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +12 -33
- package/src/components/editor/stdin-blocking-alert.tsx +3 -1
- package/src/components/scratchpad/scratchpad.tsx +4 -1
- package/src/components/slides/minimap.tsx +4 -2
- package/src/components/slides/reveal-component.tsx +14 -10
- package/src/components/slides/slide-cell-view.tsx +28 -24
- package/src/components/slides/slide.tsx +3 -4
- package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +0 -389
- package/src/core/cells/__tests__/cell.test.ts +49 -154
- package/src/core/cells/__tests__/cells.test.ts +18 -127
- package/src/core/cells/__tests__/pending-delete-service.test.tsx +11 -20
- package/src/core/cells/__tests__/utils.test.ts +75 -247
- package/src/core/cells/actions.ts +5 -2
- package/src/core/cells/cell.ts +31 -133
- package/src/core/cells/cells.ts +14 -29
- package/src/core/cells/document.ts +3 -3
- package/src/core/cells/focus.ts +27 -6
- package/src/core/cells/layout.ts +1 -1
- package/src/core/cells/pending-delete-service.ts +5 -7
- package/src/core/cells/types.ts +13 -36
- package/src/core/cells/utils.ts +37 -68
- package/src/core/edit-app.tsx +2 -1
- package/src/core/executions/__tests__/atoms.test.ts +99 -0
- package/src/core/executions/__tests__/select.test.ts +268 -0
- package/src/core/executions/atoms.ts +206 -0
- package/src/core/executions/hooks.ts +31 -0
- package/src/core/executions/index.ts +31 -0
- package/src/core/executions/select.ts +175 -0
- package/src/core/export/__tests__/hooks.test.ts +0 -5
- package/src/core/islands/bootstrap.ts +4 -1
- package/src/core/islands/components/output-wrapper.tsx +5 -5
- package/src/core/kernel/handlers.ts +2 -11
- package/src/core/kernel/state.ts +0 -15
- package/src/core/lifecycle/render-policy.ts +12 -26
- package/src/core/network/__tests__/requests-lazy.test.ts +5 -5
- package/src/core/network/requests-lazy.ts +3 -3
- package/src/core/notebook/operations.ts +1 -1
- package/src/core/run-app.tsx +2 -1
- package/src/core/runtime/__tests__/adapter.test.ts +3 -3
- package/src/core/runtime/adapter.ts +4 -4
- package/src/core/session/useNotebookSession.tsx +3 -3
- package/src/core/wasm/bridge.ts +1 -1
- package/src/plugins/core/RenderHTML.tsx +3 -5
- package/src/plugins/core/__test__/RenderHTML-copy.test.tsx +60 -0
- package/src/plugins/core/registerReactComponent.tsx +4 -13
- package/src/stories/cell-status.stories.tsx +17 -10
- package/src/stories/cell.stories.tsx +0 -31
- package/src/stories/layout/vertical/one-column.stories.tsx +0 -9
- package/src/utils/time.ts +10 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
/* oxlint-disable typescript/no-explicit-any */
|
|
3
3
|
import { describe, expect, it } from "vitest";
|
|
4
|
+
import { MockExecutions } from "@/__mocks__/executions";
|
|
4
5
|
import { initialNotebookState, type NotebookState } from "@/core/cells/cells";
|
|
5
6
|
import { CellId } from "@/core/cells/ids";
|
|
6
7
|
import type { CellData } from "@/core/cells/types";
|
|
@@ -13,278 +14,105 @@ import {
|
|
|
13
14
|
staleCellIds,
|
|
14
15
|
} from "../utils";
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
stopped: false,
|
|
39
|
-
staleInputs: false,
|
|
40
|
-
},
|
|
41
|
-
cell2: {
|
|
42
|
-
runElapsedTimeMs: 456,
|
|
43
|
-
status: "idle",
|
|
44
|
-
errored: false,
|
|
45
|
-
interrupted: false,
|
|
46
|
-
stopped: false,
|
|
47
|
-
staleInputs: false,
|
|
48
|
-
},
|
|
49
|
-
},
|
|
50
|
-
} as any;
|
|
51
|
-
|
|
52
|
-
const result = staleCellIds(state);
|
|
53
|
-
|
|
54
|
-
expect(result).toEqual(["cell1"]);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it("should use lastExecutionTime when runElapsedTimeMs is null", () => {
|
|
58
|
-
const state: NotebookState = {
|
|
59
|
-
cellIds: ["cell1", "cell2"],
|
|
60
|
-
cellData: {
|
|
61
|
-
cell1: {
|
|
62
|
-
lastExecutionTime: null,
|
|
63
|
-
edited: false,
|
|
64
|
-
config: { disabled: false },
|
|
65
|
-
},
|
|
66
|
-
cell2: {
|
|
67
|
-
lastExecutionTime: 123,
|
|
68
|
-
edited: false,
|
|
69
|
-
config: { disabled: false },
|
|
70
|
-
},
|
|
71
|
-
},
|
|
72
|
-
cellRuntime: {
|
|
73
|
-
cell1: {
|
|
74
|
-
runElapsedTimeMs: null,
|
|
75
|
-
status: "idle",
|
|
76
|
-
errored: false,
|
|
77
|
-
interrupted: false,
|
|
78
|
-
stopped: false,
|
|
79
|
-
staleInputs: false,
|
|
80
|
-
},
|
|
81
|
-
cell2: {
|
|
82
|
-
runElapsedTimeMs: null,
|
|
83
|
-
status: "idle",
|
|
84
|
-
errored: false,
|
|
85
|
-
interrupted: false,
|
|
86
|
-
stopped: false,
|
|
87
|
-
staleInputs: false,
|
|
88
|
-
},
|
|
89
|
-
},
|
|
90
|
-
} as any;
|
|
17
|
+
function notebook(
|
|
18
|
+
cells: Record<
|
|
19
|
+
string,
|
|
20
|
+
{ edited?: boolean; disabled?: boolean; staleInputs?: boolean }
|
|
21
|
+
>,
|
|
22
|
+
): NotebookState {
|
|
23
|
+
return {
|
|
24
|
+
cellIds: Object.keys(cells),
|
|
25
|
+
cellData: Object.fromEntries(
|
|
26
|
+
Object.entries(cells).map(([id, cell]) => [
|
|
27
|
+
id,
|
|
28
|
+
{ edited: cell.edited ?? false, config: { disabled: cell.disabled } },
|
|
29
|
+
]),
|
|
30
|
+
),
|
|
31
|
+
cellRuntime: Object.fromEntries(
|
|
32
|
+
Object.entries(cells).map(([id, cell]) => [
|
|
33
|
+
id,
|
|
34
|
+
{ staleInputs: cell.staleInputs ?? false },
|
|
35
|
+
]),
|
|
36
|
+
),
|
|
37
|
+
} as any;
|
|
38
|
+
}
|
|
91
39
|
|
|
92
|
-
|
|
40
|
+
describe("staleCellIds", () => {
|
|
41
|
+
it("should return cell IDs that have never run", () => {
|
|
42
|
+
const state = notebook({ cell1: {}, cell2: {} });
|
|
43
|
+
const executions = MockExecutions.inputs([
|
|
44
|
+
MockExecutions.completed("cell2"),
|
|
45
|
+
]);
|
|
93
46
|
|
|
94
|
-
expect(
|
|
47
|
+
expect(staleCellIds(state, executions)).toEqual(["cell1"]);
|
|
95
48
|
});
|
|
96
49
|
|
|
97
50
|
it("should return cell IDs that are edited", () => {
|
|
98
|
-
const state:
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
edited: true,
|
|
104
|
-
config: { disabled: false },
|
|
105
|
-
},
|
|
106
|
-
cell2: {
|
|
107
|
-
lastExecutionTime: 123,
|
|
108
|
-
edited: false,
|
|
109
|
-
config: { disabled: false },
|
|
110
|
-
},
|
|
111
|
-
},
|
|
112
|
-
cellRuntime: {
|
|
113
|
-
cell1: {
|
|
114
|
-
runElapsedTimeMs: 456,
|
|
115
|
-
status: "idle",
|
|
116
|
-
errored: false,
|
|
117
|
-
interrupted: false,
|
|
118
|
-
stopped: false,
|
|
119
|
-
staleInputs: false,
|
|
120
|
-
},
|
|
121
|
-
cell2: {
|
|
122
|
-
runElapsedTimeMs: 456,
|
|
123
|
-
status: "idle",
|
|
124
|
-
errored: false,
|
|
125
|
-
interrupted: false,
|
|
126
|
-
stopped: false,
|
|
127
|
-
staleInputs: false,
|
|
128
|
-
},
|
|
129
|
-
},
|
|
130
|
-
} as any;
|
|
131
|
-
|
|
132
|
-
const result = staleCellIds(state);
|
|
51
|
+
const state = notebook({ cell1: { edited: true }, cell2: {} });
|
|
52
|
+
const executions = MockExecutions.inputs([
|
|
53
|
+
MockExecutions.completed("cell1"),
|
|
54
|
+
MockExecutions.completed("cell2"),
|
|
55
|
+
]);
|
|
133
56
|
|
|
134
|
-
expect(
|
|
57
|
+
expect(staleCellIds(state, executions)).toEqual(["cell1"]);
|
|
135
58
|
});
|
|
136
59
|
|
|
137
60
|
it("should return cell IDs that are interrupted", () => {
|
|
138
|
-
const state:
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
cell2: {
|
|
147
|
-
lastExecutionTime: 123,
|
|
148
|
-
edited: false,
|
|
149
|
-
config: { disabled: false },
|
|
150
|
-
},
|
|
151
|
-
},
|
|
152
|
-
cellRuntime: {
|
|
153
|
-
cell1: {
|
|
154
|
-
runElapsedTimeMs: 456,
|
|
155
|
-
status: "idle",
|
|
156
|
-
errored: false,
|
|
157
|
-
interrupted: true,
|
|
158
|
-
stopped: false,
|
|
159
|
-
staleInputs: false,
|
|
160
|
-
},
|
|
161
|
-
cell2: {
|
|
162
|
-
runElapsedTimeMs: 456,
|
|
163
|
-
status: "idle",
|
|
164
|
-
errored: false,
|
|
165
|
-
interrupted: false,
|
|
166
|
-
stopped: false,
|
|
167
|
-
staleInputs: false,
|
|
168
|
-
},
|
|
169
|
-
},
|
|
170
|
-
} as any;
|
|
61
|
+
const state = notebook({ cell1: {}, cell2: {} });
|
|
62
|
+
const executions = MockExecutions.inputs([
|
|
63
|
+
MockExecutions.interrupted("cell1"),
|
|
64
|
+
MockExecutions.completed("cell2"),
|
|
65
|
+
]);
|
|
66
|
+
|
|
67
|
+
expect(staleCellIds(state, executions)).toEqual(["cell1"]);
|
|
68
|
+
});
|
|
171
69
|
|
|
172
|
-
|
|
70
|
+
it("should return cell IDs with stale inputs", () => {
|
|
71
|
+
const state = notebook({ cell1: { staleInputs: true }, cell2: {} });
|
|
72
|
+
const executions = MockExecutions.inputs([
|
|
73
|
+
MockExecutions.completed("cell1"),
|
|
74
|
+
MockExecutions.completed("cell2"),
|
|
75
|
+
]);
|
|
173
76
|
|
|
174
|
-
expect(
|
|
77
|
+
expect(staleCellIds(state, executions)).toEqual(["cell1"]);
|
|
175
78
|
});
|
|
176
79
|
|
|
177
80
|
it("should not return cell IDs that are disabled", () => {
|
|
178
|
-
const state
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
cell2: {
|
|
187
|
-
lastExecutionTime: 123,
|
|
188
|
-
edited: false,
|
|
189
|
-
config: { disabled: false },
|
|
190
|
-
},
|
|
191
|
-
},
|
|
192
|
-
cellRuntime: {
|
|
193
|
-
cell1: {
|
|
194
|
-
runElapsedTimeMs: 456,
|
|
195
|
-
status: "disabled-transitively",
|
|
196
|
-
errored: false,
|
|
197
|
-
interrupted: false,
|
|
198
|
-
stopped: false,
|
|
199
|
-
staleInputs: true,
|
|
200
|
-
},
|
|
201
|
-
cell2: {
|
|
202
|
-
runElapsedTimeMs: 456,
|
|
203
|
-
status: "idle",
|
|
204
|
-
errored: false,
|
|
205
|
-
interrupted: false,
|
|
206
|
-
stopped: false,
|
|
207
|
-
staleInputs: false,
|
|
208
|
-
},
|
|
209
|
-
},
|
|
210
|
-
} as any;
|
|
211
|
-
|
|
212
|
-
const result = staleCellIds(state);
|
|
81
|
+
const state = notebook({
|
|
82
|
+
cell1: { disabled: true, staleInputs: true },
|
|
83
|
+
cell2: { staleInputs: true },
|
|
84
|
+
});
|
|
85
|
+
const executions = MockExecutions.inputs(
|
|
86
|
+
[MockExecutions.completed("cell1"), MockExecutions.completed("cell2")],
|
|
87
|
+
{ blocked: new Set(["cell2"]) },
|
|
88
|
+
);
|
|
213
89
|
|
|
214
|
-
expect(
|
|
90
|
+
expect(staleCellIds(state, executions)).toEqual([]);
|
|
215
91
|
});
|
|
216
92
|
});
|
|
217
93
|
|
|
218
94
|
describe("isUninstantiated", () => {
|
|
219
|
-
it("should return true if
|
|
220
|
-
|
|
221
|
-
executionTime: null,
|
|
222
|
-
status: "idle",
|
|
223
|
-
errored: false,
|
|
224
|
-
interrupted: false,
|
|
225
|
-
stopped: false,
|
|
226
|
-
});
|
|
227
|
-
expect(result).toBe(true);
|
|
95
|
+
it("should return true if the cell has not run", () => {
|
|
96
|
+
expect(isUninstantiated({ status: "idle", outcome: null })).toBe(true);
|
|
228
97
|
});
|
|
229
98
|
|
|
230
|
-
it("should return false if cell has run", () => {
|
|
231
|
-
|
|
232
|
-
executionTime: 123,
|
|
233
|
-
status: "idle",
|
|
234
|
-
errored: false,
|
|
235
|
-
interrupted: false,
|
|
236
|
-
stopped: false,
|
|
237
|
-
});
|
|
238
|
-
expect(result).toBe(false);
|
|
99
|
+
it("should return false if the cell has run", () => {
|
|
100
|
+
expect(isUninstantiated({ status: "idle", outcome: "value" })).toBe(false);
|
|
239
101
|
});
|
|
240
102
|
|
|
241
|
-
it("should return false if cell is currently queued or running", () => {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
status: "queued",
|
|
245
|
-
errored: false,
|
|
246
|
-
interrupted: false,
|
|
247
|
-
stopped: false,
|
|
248
|
-
});
|
|
249
|
-
expect(result).toBe(false);
|
|
250
|
-
|
|
251
|
-
result = isUninstantiated({
|
|
252
|
-
executionTime: null,
|
|
253
|
-
status: "running",
|
|
254
|
-
errored: false,
|
|
255
|
-
interrupted: false,
|
|
256
|
-
stopped: false,
|
|
257
|
-
});
|
|
258
|
-
expect(result).toBe(false);
|
|
103
|
+
it("should return false if the cell is currently queued or running", () => {
|
|
104
|
+
expect(isUninstantiated({ status: "queued", outcome: null })).toBe(false);
|
|
105
|
+
expect(isUninstantiated({ status: "running", outcome: null })).toBe(false);
|
|
259
106
|
});
|
|
260
107
|
|
|
261
|
-
it("should return false if cell
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
expect(result).toBe(false);
|
|
270
|
-
|
|
271
|
-
result = isUninstantiated({
|
|
272
|
-
executionTime: null,
|
|
273
|
-
status: "idle",
|
|
274
|
-
errored: false,
|
|
275
|
-
interrupted: true,
|
|
276
|
-
stopped: false,
|
|
277
|
-
});
|
|
278
|
-
expect(result).toBe(false);
|
|
279
|
-
|
|
280
|
-
result = isUninstantiated({
|
|
281
|
-
executionTime: null,
|
|
282
|
-
status: "idle",
|
|
283
|
-
errored: false,
|
|
284
|
-
interrupted: false,
|
|
285
|
-
stopped: true,
|
|
286
|
-
});
|
|
287
|
-
expect(result).toBe(false);
|
|
108
|
+
it("should return false if the cell ended without a value", () => {
|
|
109
|
+
expect(isUninstantiated({ status: "idle", outcome: "error" })).toBe(false);
|
|
110
|
+
expect(isUninstantiated({ status: "idle", outcome: "interrupted" })).toBe(
|
|
111
|
+
false,
|
|
112
|
+
);
|
|
113
|
+
expect(isUninstantiated({ status: "idle", outcome: "skipped" })).toBe(
|
|
114
|
+
false,
|
|
115
|
+
);
|
|
288
116
|
});
|
|
289
117
|
});
|
|
290
118
|
|
|
@@ -6,6 +6,7 @@ import { historyField } from "@codemirror/commands";
|
|
|
6
6
|
import { atom, type Getter, type Setter, useSetAtom } from "jotai";
|
|
7
7
|
import { useMemo } from "react";
|
|
8
8
|
import { scrollAndHighlightCell } from "@/components/editor/links/cell-link";
|
|
9
|
+
import { cellExecutionInputsAtom, runningCellIds } from "@/core/executions";
|
|
9
10
|
import type { EditableDocument } from "@/core/notebook/document";
|
|
10
11
|
import { Logger } from "@/utils/Logger";
|
|
11
12
|
import { clamp } from "@/utils/math";
|
|
@@ -680,8 +681,10 @@ export type CellActions = ReturnType<typeof useCellActions>;
|
|
|
680
681
|
|
|
681
682
|
/** Scroll to the first cell that is currently running. */
|
|
682
683
|
export function notebookScrollToRunning() {
|
|
683
|
-
const
|
|
684
|
-
|
|
684
|
+
const running = new Set(
|
|
685
|
+
runningCellIds(store.get(cellExecutionInputsAtom).executions),
|
|
686
|
+
);
|
|
687
|
+
const cellId = store.get(notebookAtom).cellIds.find((id) => running.has(id));
|
|
685
688
|
if (!cellId) {
|
|
686
689
|
return;
|
|
687
690
|
}
|
package/src/core/cells/cell.ts
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
-
import {
|
|
3
|
-
import { invariant } from "@/utils/invariant";
|
|
4
|
-
import { type Seconds, Time } from "@/utils/time";
|
|
2
|
+
import type { CellExecutionView, CellRunStatus } from "@/core/executions";
|
|
5
3
|
import { extractAllTracebackInfo, type TracebackInfo } from "@/utils/traceback";
|
|
6
4
|
import { parseOutline } from "../dom/outline";
|
|
7
5
|
import type { CellMessage, OutputMessage } from "../kernel/messages";
|
|
8
|
-
import type { RuntimeState } from "../network/types";
|
|
9
6
|
import { collapseConsoleOutputs } from "./collapseConsoleOutputs";
|
|
10
7
|
import type { CellRuntimeState, ConsoleOutputMessage } from "./types";
|
|
11
8
|
|
|
@@ -14,111 +11,44 @@ function nextConsoleOutputId(outputs: ConsoleOutputMessage[]): number {
|
|
|
14
11
|
return (outputs.at(-1)?.id ?? 0) + 1;
|
|
15
12
|
}
|
|
16
13
|
|
|
14
|
+
function errorTypes(output: OutputMessage | null | undefined): string[] {
|
|
15
|
+
if (
|
|
16
|
+
output == null ||
|
|
17
|
+
output.mimetype !== "application/vnd.marimo+error" ||
|
|
18
|
+
!Array.isArray(output.data)
|
|
19
|
+
) {
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
22
|
+
return output.data.map((error) => error.type);
|
|
23
|
+
}
|
|
24
|
+
|
|
17
25
|
export function transitionCell(
|
|
18
26
|
cell: CellRuntimeState,
|
|
19
27
|
message: CellMessage,
|
|
20
28
|
): CellRuntimeState {
|
|
21
29
|
const nextCell = { ...cell };
|
|
22
30
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
// the cell's current code if the user modified it.
|
|
34
|
-
break;
|
|
35
|
-
case "running":
|
|
36
|
-
// Clear interrupted here in case we start as "running"
|
|
37
|
-
// This can happen on a resumed session
|
|
38
|
-
nextCell.interrupted = false;
|
|
39
|
-
// If was previously stopped, clear the outputs
|
|
40
|
-
if (cell.stopped) {
|
|
41
|
-
nextCell.output = null;
|
|
42
|
-
}
|
|
43
|
-
// If it transitioned from queued to running, remove previous console outputs
|
|
44
|
-
if (nextCell.status === "queued") {
|
|
45
|
-
nextCell.consoleOutputs = [];
|
|
46
|
-
}
|
|
47
|
-
nextCell.stopped = false;
|
|
48
|
-
nextCell.runStartTimestamp = message.timestamp as Seconds;
|
|
49
|
-
// Store the last run timestamp, since this gets cleared once idle
|
|
50
|
-
nextCell.lastRunStartTimestamp = message.timestamp as Seconds;
|
|
51
|
-
break;
|
|
52
|
-
case "idle":
|
|
53
|
-
if (cell.runStartTimestamp) {
|
|
54
|
-
nextCell.runElapsedTimeMs = Time.fromSeconds(
|
|
55
|
-
((message.timestamp ?? 0) - cell.runStartTimestamp) as Seconds,
|
|
56
|
-
).toMilliseconds();
|
|
57
|
-
nextCell.runStartTimestamp = null;
|
|
58
|
-
nextCell.staleInputs = false;
|
|
59
|
-
}
|
|
60
|
-
// If last run start timestamp is not set, set it to the current timestamp
|
|
61
|
-
// This happens on a resumed session
|
|
62
|
-
if (!cell.lastRunStartTimestamp && message.timestamp) {
|
|
63
|
-
nextCell.lastRunStartTimestamp = message.timestamp as Seconds;
|
|
64
|
-
}
|
|
65
|
-
nextCell.debuggerActive = false;
|
|
66
|
-
break;
|
|
67
|
-
case null:
|
|
68
|
-
break;
|
|
69
|
-
case "disabled-transitively":
|
|
70
|
-
// Everything should already be up to date from prepareCellForExecution
|
|
71
|
-
break;
|
|
72
|
-
case undefined:
|
|
73
|
-
break;
|
|
74
|
-
default:
|
|
75
|
-
logNever(message.status);
|
|
31
|
+
if (message.status === "queued") {
|
|
32
|
+
nextCell.debuggerActive = false;
|
|
33
|
+
}
|
|
34
|
+
// A skipped cell's output is the notice that an ancestor stopped it, which
|
|
35
|
+
// has nothing to do with the run that is starting.
|
|
36
|
+
if (
|
|
37
|
+
message.status === "running" &&
|
|
38
|
+
errorTypes(cell.output).some((type) => type.includes("ancestor"))
|
|
39
|
+
) {
|
|
40
|
+
nextCell.output = null;
|
|
76
41
|
}
|
|
77
42
|
|
|
78
43
|
nextCell.output = message.output ?? nextCell.output;
|
|
79
44
|
nextCell.staleInputs = message.stale_inputs ?? nextCell.staleInputs;
|
|
80
|
-
nextCell.status = message.status ?? nextCell.status;
|
|
81
45
|
// Tri-state partial update: an omitted field (undefined) leaves the hint
|
|
82
46
|
// unchanged; null explicitly clears it; a string sets it.
|
|
83
47
|
if (message.serialization !== undefined) {
|
|
84
48
|
nextCell.serialization = message.serialization;
|
|
85
49
|
}
|
|
86
50
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
// Handle errors: marimo includes an error output when a cell is interrupted
|
|
90
|
-
// or errored
|
|
91
|
-
if (
|
|
92
|
-
message.output != null &&
|
|
93
|
-
message.output.mimetype === "application/vnd.marimo+error"
|
|
94
|
-
) {
|
|
95
|
-
// The frontend manually sets status to queued when a user runs a cell,
|
|
96
|
-
// to give immediate feedback, but the kernel doesn't know that.
|
|
97
|
-
//
|
|
98
|
-
// TODO(akshayka): Move all status management to the backend.
|
|
99
|
-
if (nextCell.status === "queued" || nextCell.status === "running") {
|
|
100
|
-
nextCell.status = "idle";
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
invariant(
|
|
104
|
-
Array.isArray(message.output.data),
|
|
105
|
-
"Expected error output data to be an array",
|
|
106
|
-
);
|
|
107
|
-
if (message.output.data.some((error) => error.type === "interruption")) {
|
|
108
|
-
// Interrupted helps distinguish that the cell is stale
|
|
109
|
-
nextCell.interrupted = true;
|
|
110
|
-
didInterruptFromThisMessage = true;
|
|
111
|
-
} else if (
|
|
112
|
-
message.output.data.some((error) => error.type.includes("ancestor"))
|
|
113
|
-
) {
|
|
114
|
-
// The cell didn't run, but it was intentional, so don't count as
|
|
115
|
-
// errored.
|
|
116
|
-
nextCell.stopped = true;
|
|
117
|
-
} else {
|
|
118
|
-
// Communicate that the cell errored (e.g., an exception was raised)
|
|
119
|
-
nextCell.errored = true;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
51
|
+
const interrupted = errorTypes(message.output).includes("interruption");
|
|
122
52
|
|
|
123
53
|
// Coalesce console outputs, which are streamed during execution.
|
|
124
54
|
let consoleOutputs = cell.consoleOutputs;
|
|
@@ -127,7 +57,7 @@ export function transitionCell(
|
|
|
127
57
|
// stdin/pdb session, so resolve any dangling stdin prompt. This removes the
|
|
128
58
|
// debugger box — e.g. after quitting pdb with `q`, which stops the cell
|
|
129
59
|
// (idle) rather than interrupting it.
|
|
130
|
-
if (
|
|
60
|
+
if (interrupted || message.status === "idle") {
|
|
131
61
|
nextCell.debuggerActive = false;
|
|
132
62
|
consoleOutputs = consoleOutputs.map((output) => {
|
|
133
63
|
if (output.channel === "stdin") {
|
|
@@ -177,30 +107,10 @@ export function transitionCell(
|
|
|
177
107
|
return nextCell;
|
|
178
108
|
}
|
|
179
109
|
|
|
180
|
-
// Should be called when a cell's code is registered with the kernel for
|
|
181
|
-
// execution.
|
|
182
|
-
export function prepareCellForExecution(
|
|
183
|
-
cell: CellRuntimeState,
|
|
184
|
-
): CellRuntimeState {
|
|
185
|
-
const nextCell = { ...cell };
|
|
186
|
-
|
|
187
|
-
if (cell.status !== "disabled-transitively") {
|
|
188
|
-
// TODO(akshayka): Move this to the backend. It's in the FE right now
|
|
189
|
-
// to give the user immediate feedback.
|
|
190
|
-
nextCell.status = "queued";
|
|
191
|
-
}
|
|
192
|
-
nextCell.interrupted = false;
|
|
193
|
-
nextCell.errored = false;
|
|
194
|
-
nextCell.runElapsedTimeMs = null;
|
|
195
|
-
nextCell.debuggerActive = false;
|
|
196
|
-
|
|
197
|
-
return nextCell;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
110
|
/**
|
|
201
111
|
* A cell's output is loading if it is running or queued.
|
|
202
112
|
*/
|
|
203
|
-
export function outputIsLoading(status:
|
|
113
|
+
export function outputIsLoading(status: CellRunStatus): boolean {
|
|
204
114
|
return status === "running" || status === "queued";
|
|
205
115
|
}
|
|
206
116
|
|
|
@@ -208,16 +118,14 @@ export function outputIsLoading(status: RuntimeState): boolean {
|
|
|
208
118
|
* A cell's output is stale if it has been edited, is loading, or has errored.
|
|
209
119
|
*/
|
|
210
120
|
export function outputIsStale(
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
"status" | "output" | "runStartTimestamp" | "interrupted" | "staleInputs"
|
|
214
|
-
>,
|
|
121
|
+
execution: Pick<CellExecutionView, "status" | "outcome" | "rendered">,
|
|
122
|
+
staleInputs: boolean,
|
|
215
123
|
edited: boolean,
|
|
216
124
|
): boolean {
|
|
217
|
-
const { status,
|
|
125
|
+
const { status, outcome, rendered } = execution;
|
|
218
126
|
|
|
219
127
|
// If interrupted, the output is not stale
|
|
220
|
-
if (interrupted) {
|
|
128
|
+
if (outcome === "interrupted") {
|
|
221
129
|
return false;
|
|
222
130
|
}
|
|
223
131
|
|
|
@@ -226,18 +134,8 @@ export function outputIsStale(
|
|
|
226
134
|
return true;
|
|
227
135
|
}
|
|
228
136
|
|
|
229
|
-
//
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
// Output is received while the cell is running (e.g. mo.output.append())
|
|
233
|
-
const outputReceivedWhileRunning =
|
|
234
|
-
status === "running" &&
|
|
235
|
-
output !== null &&
|
|
236
|
-
runStartTimestamp !== null &&
|
|
237
|
-
(output.timestamp ?? 0) > runStartTimestamp;
|
|
238
|
-
|
|
239
|
-
// If loading and output has not been received while running
|
|
240
|
-
if (loading && !outputReceivedWhileRunning) {
|
|
137
|
+
// Loading, unless the running cell has shown output (e.g. mo.output.append())
|
|
138
|
+
if (outputIsLoading(status) && !(status === "running" && rendered)) {
|
|
241
139
|
return true;
|
|
242
140
|
}
|
|
243
141
|
|