@marimo-team/islands 0.25.3-dev266 → 0.25.3-dev268
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-C_hj7Poi.js → ConnectedDataExplorerComponent-eLHPh6uu.js} +9 -9
- package/dist/{ErrorBoundary-CvxlC3F1.js → ErrorBoundary-CQYAoD7X.js} +64 -69
- package/dist/{abnfDiagram-VCTEODGH-DzrZZXRD.js → abnfDiagram-VCTEODGH-hqd0IBtX.js} +2 -2
- package/dist/{any-language-editor-CFeWfC3o.js → any-language-editor-DnG26QXh.js} +1 -1
- package/dist/{architectureDiagram-5GKGNRK7-iF_snyFL.js → architectureDiagram-5GKGNRK7-BzLAFcc0.js} +1 -1
- package/dist/{strings-BCKpLFNU.js → assertNever-DIU3JJbt.js} +14 -14
- package/dist/{chat-ui-DFxNgzXF.js → chat-ui-BAnLgpTe.js} +1964 -1740
- package/dist/{chunk-IMKFNOWR-B7xytD4B.js → chunk-IMKFNOWR-DOeVvuyE.js} +1 -1
- package/dist/{chunk-SHT3W25Y-I8ZFP5L1.js → chunk-SHT3W25Y-DZoFI8wR.js} +1 -1
- package/dist/{chunk-SVP7TREG-Di-uAPdT.js → chunk-SVP7TREG-CWu86PSW.js} +1 -1
- package/dist/{chunk-TICWLB2K-BFpUy0ud.js → chunk-TICWLB2K-BrM0GoP3.js} +1 -1
- package/dist/{classDiagram-ZZMXUADV-Cqm4JGpx.js → classDiagram-ZZMXUADV-DYi4V2bB.js} +2 -2
- package/dist/{classDiagram-v2-VYDZK3BY-DFHRJpN3.js → classDiagram-v2-VYDZK3BY-tN7FIk-m.js} +2 -2
- package/dist/{code-visibility-SRM1JiD_.js → code-visibility-DcZ_RwaH.js} +44 -47
- package/dist/{common-yiRhZ6ZZ.js → common-BGeQilvh.js} +5846 -5519
- 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-cuGOeTBC.js → cynefinDiagram-5FMLGOSQ-D5nCnDc-.js} +1 -1
- package/dist/{dagre-GXQ25YYZ-DJ76aC4k.js → dagre-GXQ25YYZ-BHkd7f_i.js} +1 -1
- package/dist/{data-grid-overlay-editor-DLTzcQ4O.js → data-grid-overlay-editor-Dc4lW4A3.js} +1 -1
- package/dist/{diagram-S7CK7UJ4-DLLc0sw7.js → diagram-S7CK7UJ4-D1ALppT2.js} +1 -1
- package/dist/{diagram-UQ7AKVKN-Tz_d6qmk.js → diagram-UQ7AKVKN-DLv0b83L.js} +1 -1
- package/dist/{diagram-VX7I27RA-D7SzPrDN.js → diagram-VX7I27RA-7XJ7J6qg.js} +1 -1
- package/dist/{diagram-Z3DM3KII-qr37XV58.js → diagram-Z3DM3KII-7M7zbHp_.js} +1 -1
- package/dist/{dialog-BtprQCUw.js → dialog-DzCj1qQv.js} +1 -1
- package/dist/{dropdown-menu-CkJ7ErY6.js → dropdown-menu-CdXE15an.js} +1214 -1208
- package/dist/{ebnfDiagram-PWID7BFC-BKOQb4J0.js → ebnfDiagram-PWID7BFC-DHH1qlmV.js} +2 -2
- package/dist/{erDiagram-RLTQ6QDP-CM15Vq68.js → erDiagram-RLTQ6QDP-fI_0dGef.js} +1 -1
- package/dist/{error-banner-DbRFOd2O.js → error-banner-DPc9eIw5.js} +3 -3
- package/dist/{error-entries-CMoaUX9V.js → error-entries-CxN-Nq3V.js} +524 -456
- package/dist/{feedback-button-DBUrE4OL.js → feedback-button-BqaeQcvX.js} +77 -77
- package/dist/{flowDiagram-HODETNUW-Dt3N5yFT.js → flowDiagram-HODETNUW-Brg9MA6i.js} +2 -2
- package/dist/{formats-gGospKWJ.js → formats-DbSvAPY-.js} +1 -1
- package/dist/{glide-data-editor-Cfv4HeOh.js → glide-data-editor-CiijPXA7.js} +2042 -2041
- package/dist/{glide-utils-Ba9WD2IN.js → glide-utils-BSw3SSHF.js} +29 -35
- package/dist/{globals-dIbBIR5-.js → globals-CpmSWKI_.js} +2 -2
- package/dist/{html-to-image-D_5CMreP.js → html-to-image-8sywF4my.js} +5452 -5751
- package/dist/{infoDiagram-27XIBGKW-C7axJMCl.js → infoDiagram-27XIBGKW-E9e403YT.js} +1 -1
- package/dist/{ishikawaDiagram-5VMMS53U-CdC9Eur7.js → ishikawaDiagram-5VMMS53U-C-RlYDRo.js} +1 -1
- package/dist/{kanban-definition-UXKFOSKX-BHnMIgD7.js → kanban-definition-UXKFOSKX-CLFl_uqK.js} +1 -1
- package/dist/main.js +6294 -6324
- package/dist/{mermaid-DFYN_PXd.js → mermaid-rd09e1JL.js} +35 -35
- package/dist/{mindmap-definition-YA3MSWOX-BagHoCGU.js → mindmap-definition-YA3MSWOX-BQ8TWtyB.js} +1 -1
- package/dist/{number-overlay-editor-CTiJp5Mj.js → number-overlay-editor-Ft4NiAEN.js} +1 -1
- package/dist/{pegDiagram-XKGWAZYB-8IIZczip.js → pegDiagram-XKGWAZYB-CN8UHFPi.js} +2 -2
- package/dist/pencil-DcT8AE0h.js +10 -0
- package/dist/{pieDiagram-E7YTZNPT-CE6dKBIr.js → pieDiagram-E7YTZNPT-DGuJ0EBd.js} +1 -1
- package/dist/{process-output-CGFNVC60.js → process-output-Dku5POvm.js} +3 -3
- package/dist/{railroadDiagram-O6MQD6OU-CkexPq3b.js → railroadDiagram-O6MQD6OU-BPDA8wy8.js} +2 -2
- package/dist/{requirementDiagram-BXWQKSXE-B0-OkuAe.js → requirementDiagram-BXWQKSXE-ChzLGbGW.js} +1 -1
- package/dist/{reveal-component-DH09ygHo.js → reveal-component-D0cfwbbl.js} +1094 -1094
- package/dist/{select-C33FVPPN.js → select-OxgozOC8.js} +1 -1
- package/dist/{sequenceDiagram-WJ2MYXX4-Dxysg8DX.js → sequenceDiagram-WJ2MYXX4-DFX-bE6O.js} +1 -1
- package/dist/{spec-DH1lmF7Q.js → spec-oJn0RhpQ.js} +2 -2
- package/dist/{stateDiagram-D77RDMKH-DTEN3-OJ.js → stateDiagram-D77RDMKH-BR7cI3wK.js} +2 -2
- package/dist/{stateDiagram-v2-MP3YSRHH-vktk3Xc5.js → stateDiagram-v2-MP3YSRHH-oGqB5UDI.js} +2 -2
- package/dist/{static-state-BldGd99T.js → static-state-CuV6Pc3a.js} +1 -1
- package/dist/style.css +1 -1
- package/dist/{swimlanes-42K2YHIH-Cjveh9OM.js → swimlanes-42K2YHIH-DwSZl1yC.js} +1 -1
- package/dist/{swimlanesDiagram-VR7AAH4N-Bdr7Ev_2.js → swimlanesDiagram-VR7AAH4N-BHlcd-Ri.js} +2 -2
- package/dist/{timeline-definition-24CTP7MA-KIuDcefn.js → timeline-definition-24CTP7MA-DMmP-G2O.js} +1 -1
- package/dist/{toast-CY1_Qv41.js → toast-DbBHmxmB.js} +2 -2
- 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-C-lqjV0q.js → vega-component-rxIxAAh9.js} +7 -7
- package/dist/{vennDiagram-4TSXK5OY-DrIOUHfb.js → vennDiagram-4TSXK5OY-Db8GtH1G.js} +1 -1
- package/dist/{wardleyDiagram-VM6X3IG4-CJNjf4OH.js → wardleyDiagram-VM6X3IG4-DmIiC3FU.js} +1 -1
- package/dist/{xychartDiagram-S5SC5T6Z-DEshFho-.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/chat/__tests__/chat-display.test.tsx +157 -86
- package/src/components/chat/__tests__/chat-utils.test.ts +42 -0
- package/src/components/chat/__tests__/turn-timing.test.ts +84 -0
- package/src/components/chat/__tests__/use-assistant-turn.test.ts +95 -0
- package/src/components/chat/__tests__/user-message.test.tsx +27 -0
- package/src/components/chat/acp/agent-panel.tsx +2 -0
- package/src/components/chat/agent-activity.tsx +132 -0
- package/src/components/chat/chat-display.tsx +125 -96
- package/src/components/chat/chat-panel.tsx +123 -106
- package/src/components/chat/chat-utils.ts +12 -24
- package/src/components/chat/tool-call/__tests__/tool-labels.test.ts +76 -0
- package/src/components/chat/tool-call/__tests__/tool-step.test.tsx +57 -0
- package/src/components/chat/tool-call/shared.ts +6 -0
- package/src/components/chat/tool-call/tool-call-view.tsx +22 -76
- package/src/components/chat/tool-call/tool-labels.ts +172 -0
- package/src/components/chat/tool-call/tool-step.tsx +145 -0
- package/src/components/chat/turn-timing.ts +73 -0
- package/src/components/chat/use-assistant-turn.ts +65 -0
- package/src/components/chat/use-chat-generation.ts +5 -4
- package/src/components/chat/user-message.tsx +145 -0
- 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 +19 -1
- package/src/components/editor/actions/useCellActionButton.tsx +10 -3
- package/src/components/editor/ai/completion-handlers.tsx +0 -37
- 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/StagedAICell.tsx +51 -144
- 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/__tests__/pending-ai-cells.test.tsx +193 -32
- package/src/components/editor/chrome/wrapper/app-chrome.tsx +2 -2
- package/src/components/editor/chrome/wrapper/pending-ai-cells.tsx +145 -81
- package/src/components/editor/chrome/wrapper/pending-ai-changes-badge.tsx +25 -0
- package/src/components/editor/chrome/wrapper/sidebar.tsx +8 -4
- package/src/components/editor/header/status.tsx +4 -2
- package/src/components/editor/links/cell-link.tsx +4 -4
- package/src/components/editor/navigation/navigation.ts +3 -2
- package/src/components/editor/notebook-cell.tsx +85 -94
- 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/components/ui/toast.tsx +1 -1
- package/src/core/ai/__tests__/staged-cells.test.ts +74 -126
- package/src/core/ai/staged-cells.ts +48 -38
- 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/scrollCellIntoView.ts +6 -0
- package/src/core/cells/types.ts +13 -36
- package/src/core/cells/utils.ts +37 -68
- package/src/core/codemirror/placeholder/__tests__/extensions.test.ts +59 -0
- package/src/core/codemirror/placeholder/extensions.ts +23 -0
- 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/__tests__/useNotebookSession.hook.test.tsx +34 -0
- package/src/core/session/useNotebookSession.tsx +10 -5
- package/src/core/wasm/bridge.ts +1 -1
- package/src/css/app/Cell.css +7 -36
- package/src/plugins/core/registerReactComponent.tsx +4 -13
- package/src/plugins/impl/chat/chat-ui.tsx +2 -3
- package/src/plugins/impl/common/error-banner.tsx +5 -3
- 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
- package/src/components/chat/reasoning-accordion.tsx +0 -61
- package/src/components/chat/tool-call/tool-activity-group.tsx +0 -49
- package/src/components/chat/tool-call/tool-error-card.tsx +0 -81
- package/src/components/chat/tool-call/tool-history-row.tsx +0 -147
- package/src/components/icons/loading-ellipsis.tsx +0 -46
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { createStore } from "jotai";
|
|
4
|
+
import { describe, expect, it } from "vitest";
|
|
5
|
+
import { MockExecutions } from "@/__mocks__/executions";
|
|
6
|
+
import { cellId } from "@/__tests__/branded";
|
|
7
|
+
import type { CellMessage } from "@/core/kernel/messages";
|
|
8
|
+
import {
|
|
9
|
+
cellExecutionInputsAtom,
|
|
10
|
+
notebookQueuedOrRunningCountAtom,
|
|
11
|
+
observeCellMessage,
|
|
12
|
+
submitCells,
|
|
13
|
+
} from "../atoms";
|
|
14
|
+
import { cellExecutionView } from "../select";
|
|
15
|
+
|
|
16
|
+
const C1 = cellId("c1");
|
|
17
|
+
|
|
18
|
+
function cellOp(status: CellMessage["status"]): CellMessage {
|
|
19
|
+
return {
|
|
20
|
+
cell_id: C1,
|
|
21
|
+
output: null,
|
|
22
|
+
console: null,
|
|
23
|
+
status,
|
|
24
|
+
stale_inputs: null,
|
|
25
|
+
timestamp: 1,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
describe("observeCellMessage", () => {
|
|
30
|
+
it("withdraws a submission once the kernel reports the cell blocked", () => {
|
|
31
|
+
const store = createStore();
|
|
32
|
+
MockExecutions.seed(store, []);
|
|
33
|
+
submitCells(store, [C1]);
|
|
34
|
+
expect(
|
|
35
|
+
cellExecutionView(store.get(cellExecutionInputsAtom), C1).status,
|
|
36
|
+
).toBe("queued");
|
|
37
|
+
|
|
38
|
+
observeCellMessage(store, cellOp("disabled-transitively"));
|
|
39
|
+
|
|
40
|
+
const view = cellExecutionView(store.get(cellExecutionInputsAtom), C1);
|
|
41
|
+
expect(view.status).toBe("idle");
|
|
42
|
+
expect(view.blocked).toBe(true);
|
|
43
|
+
expect(store.get(notebookQueuedOrRunningCountAtom)).toBe(0);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("forgets submissions and blocked cells with the replica they were made for", () => {
|
|
47
|
+
const store = createStore();
|
|
48
|
+
MockExecutions.seed(store, []);
|
|
49
|
+
submitCells(store, [C1]);
|
|
50
|
+
observeCellMessage(store, cellOp("disabled-transitively"));
|
|
51
|
+
|
|
52
|
+
MockExecutions.seed(store, []);
|
|
53
|
+
|
|
54
|
+
const view = cellExecutionView(store.get(cellExecutionInputsAtom), C1);
|
|
55
|
+
expect(view.status).toBe("idle");
|
|
56
|
+
expect(view.blocked).toBe(false);
|
|
57
|
+
expect(store.get(notebookQueuedOrRunningCountAtom)).toBe(0);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("keeps a submission while the cell is merely queued", () => {
|
|
61
|
+
const store = createStore();
|
|
62
|
+
MockExecutions.seed(store, []);
|
|
63
|
+
submitCells(store, [C1]);
|
|
64
|
+
|
|
65
|
+
observeCellMessage(store, cellOp("queued"));
|
|
66
|
+
|
|
67
|
+
expect(
|
|
68
|
+
cellExecutionView(store.get(cellExecutionInputsAtom), C1).status,
|
|
69
|
+
).toBe("queued");
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("forgets acknowledged submissions and blocked cells when the replica follows a new notebook", () => {
|
|
73
|
+
const store = createStore();
|
|
74
|
+
const replica = MockExecutions.seed(store, []);
|
|
75
|
+
submitCells(store, [C1]);
|
|
76
|
+
observeCellMessage(store, {
|
|
77
|
+
...cellOp("disabled-transitively"),
|
|
78
|
+
cell_id: cellId("c2"),
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const completed = MockExecutions.inputs([MockExecutions.completed(C1)]);
|
|
82
|
+
store.set(replica.executionsAtom, completed.executions);
|
|
83
|
+
store.set(replica.resultsAtom, completed.results);
|
|
84
|
+
expect(
|
|
85
|
+
cellExecutionView(store.get(cellExecutionInputsAtom), C1).status,
|
|
86
|
+
).toBe("idle");
|
|
87
|
+
|
|
88
|
+
const empty = MockExecutions.inputs([]);
|
|
89
|
+
store.set(replica.executionsAtom, empty.executions);
|
|
90
|
+
store.set(replica.resultsAtom, empty.results);
|
|
91
|
+
store.set(replica.notebookAtom, {
|
|
92
|
+
...store.get(replica.notebookAtom),
|
|
93
|
+
id: "n2",
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
expect(store.get(cellExecutionInputsAtom)).toEqual(empty);
|
|
97
|
+
expect(store.get(notebookQueuedOrRunningCountAtom)).toBe(0);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import type * as Protocol from "@marimo-team/client/protocol";
|
|
4
|
+
import { describe, expect, it } from "vitest";
|
|
5
|
+
import type { Execution, Executions, Results } from "@/core/notebook/replica";
|
|
6
|
+
import {
|
|
7
|
+
anyRunning,
|
|
8
|
+
type CellExecutionInputs,
|
|
9
|
+
cellExecutionView,
|
|
10
|
+
onlyScratchRunning,
|
|
11
|
+
queuedOrRunningCount,
|
|
12
|
+
runningCellIds,
|
|
13
|
+
} from "../select";
|
|
14
|
+
|
|
15
|
+
const T0 = "2026-10-01T09:20:11Z";
|
|
16
|
+
const T1 = "2026-10-01T09:20:12.500Z";
|
|
17
|
+
const T2 = "2026-10-01T09:20:20Z";
|
|
18
|
+
|
|
19
|
+
function record(
|
|
20
|
+
overrides: Partial<Protocol.Execution> & { id: string },
|
|
21
|
+
): Protocol.Execution {
|
|
22
|
+
return {
|
|
23
|
+
run_id: "run-1",
|
|
24
|
+
runtime_id: "r1",
|
|
25
|
+
caused_by: null,
|
|
26
|
+
cell_id: "c1",
|
|
27
|
+
code: null,
|
|
28
|
+
code_fingerprint: "0f71456db2ea83f4",
|
|
29
|
+
depends_on: {},
|
|
30
|
+
status: "completed",
|
|
31
|
+
outcome: { kind: "value", display: null },
|
|
32
|
+
started_at: T0,
|
|
33
|
+
completed_at: T1,
|
|
34
|
+
...overrides,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function execution(overrides: Partial<Protocol.Execution> & { id: string }) {
|
|
39
|
+
return { record: record(overrides), items: new Map(), variables: new Map() };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function inputs({
|
|
43
|
+
executions = [],
|
|
44
|
+
results = new Map(),
|
|
45
|
+
submissions = new Map(),
|
|
46
|
+
blocked = new Set(),
|
|
47
|
+
}: {
|
|
48
|
+
executions?: Execution[];
|
|
49
|
+
results?: Results;
|
|
50
|
+
submissions?: ReadonlyMap<string, string | null>;
|
|
51
|
+
blocked?: ReadonlySet<string>;
|
|
52
|
+
}): CellExecutionInputs {
|
|
53
|
+
const stream: Executions = {
|
|
54
|
+
runs: new Map(),
|
|
55
|
+
executions: new Map(executions.map((e) => [e.record.id, e])),
|
|
56
|
+
latest: new Map(
|
|
57
|
+
executions.flatMap((e) =>
|
|
58
|
+
e.record.cell_id === null ? [] : [[e.record.cell_id, e.record.id]],
|
|
59
|
+
),
|
|
60
|
+
),
|
|
61
|
+
};
|
|
62
|
+
return { executions: stream, results, submissions, blocked };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
describe("cellExecutionView", () => {
|
|
66
|
+
it("is idle with no outcome before a cell runs", () => {
|
|
67
|
+
expect(cellExecutionView(inputs({}), "c1")).toEqual({
|
|
68
|
+
status: "idle",
|
|
69
|
+
blocked: false,
|
|
70
|
+
outcome: null,
|
|
71
|
+
startedAt: null,
|
|
72
|
+
duration: null,
|
|
73
|
+
rendered: false,
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("follows the execution on the stream while it is unfinished", () => {
|
|
78
|
+
const queued = inputs({
|
|
79
|
+
executions: [
|
|
80
|
+
execution({
|
|
81
|
+
id: "e1",
|
|
82
|
+
status: "queued",
|
|
83
|
+
outcome: null,
|
|
84
|
+
started_at: null,
|
|
85
|
+
completed_at: null,
|
|
86
|
+
}),
|
|
87
|
+
],
|
|
88
|
+
});
|
|
89
|
+
expect(cellExecutionView(queued, "c1").status).toBe("queued");
|
|
90
|
+
const running = inputs({
|
|
91
|
+
executions: [
|
|
92
|
+
execution({
|
|
93
|
+
id: "e1",
|
|
94
|
+
status: "running",
|
|
95
|
+
outcome: null,
|
|
96
|
+
started_at: T2,
|
|
97
|
+
completed_at: null,
|
|
98
|
+
}),
|
|
99
|
+
],
|
|
100
|
+
results: new Map([["c1", record({ id: "e0" })]]),
|
|
101
|
+
});
|
|
102
|
+
expect(cellExecutionView(running, "c1")).toEqual({
|
|
103
|
+
status: "running",
|
|
104
|
+
blocked: false,
|
|
105
|
+
outcome: null,
|
|
106
|
+
startedAt: Date.parse(T2),
|
|
107
|
+
duration: null,
|
|
108
|
+
rendered: false,
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("notes when a running execution has shown a value so far", () => {
|
|
113
|
+
const showing = inputs({
|
|
114
|
+
executions: [
|
|
115
|
+
execution({
|
|
116
|
+
id: "e1",
|
|
117
|
+
status: "running",
|
|
118
|
+
outcome: { kind: "value", display: null },
|
|
119
|
+
completed_at: null,
|
|
120
|
+
}),
|
|
121
|
+
],
|
|
122
|
+
});
|
|
123
|
+
expect(cellExecutionView(showing, "c1").rendered).toBe(true);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("reports how the last completed execution ended, with its timing", () => {
|
|
127
|
+
const ended = (outcome: Protocol.Outcome) =>
|
|
128
|
+
cellExecutionView(
|
|
129
|
+
inputs({
|
|
130
|
+
executions: [execution({ id: "e1", outcome })],
|
|
131
|
+
results: new Map([["c1", record({ id: "e1", outcome })]]),
|
|
132
|
+
}),
|
|
133
|
+
"c1",
|
|
134
|
+
);
|
|
135
|
+
const value = ended({ kind: "value", display: null });
|
|
136
|
+
expect(value).toEqual({
|
|
137
|
+
status: "idle",
|
|
138
|
+
blocked: false,
|
|
139
|
+
outcome: "value",
|
|
140
|
+
startedAt: Date.parse(T0),
|
|
141
|
+
duration: 1500,
|
|
142
|
+
rendered: false,
|
|
143
|
+
});
|
|
144
|
+
const error = (kind: string): Protocol.Outcome => ({
|
|
145
|
+
kind: "error",
|
|
146
|
+
error: {
|
|
147
|
+
kind,
|
|
148
|
+
message: "",
|
|
149
|
+
name: null,
|
|
150
|
+
traceback: null,
|
|
151
|
+
source: "marimo",
|
|
152
|
+
severity: "error",
|
|
153
|
+
range: null,
|
|
154
|
+
related: [],
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
expect(ended(error("exception")).outcome).toBe("error");
|
|
158
|
+
const skipped: Protocol.Outcome = {
|
|
159
|
+
kind: "skipped",
|
|
160
|
+
reason: "ancestor-stopped",
|
|
161
|
+
message: "",
|
|
162
|
+
related: [],
|
|
163
|
+
};
|
|
164
|
+
expect(ended(skipped).outcome).toBe("skipped");
|
|
165
|
+
expect(ended({ kind: "interrupted" }).outcome).toBe("interrupted");
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("reads as queued from a submission until the host shows a newer execution", () => {
|
|
169
|
+
const before = inputs({
|
|
170
|
+
executions: [execution({ id: "e1" })],
|
|
171
|
+
results: new Map([["c1", record({ id: "e1" })]]),
|
|
172
|
+
submissions: new Map([["c1", "e1"]]),
|
|
173
|
+
});
|
|
174
|
+
expect(cellExecutionView(before, "c1").status).toBe("queued");
|
|
175
|
+
expect(cellExecutionView(before, "c1").outcome).toBeNull();
|
|
176
|
+
|
|
177
|
+
// The stream let e1 go while the request was in flight; its result remains.
|
|
178
|
+
const forgotten = inputs({
|
|
179
|
+
results: new Map([["c1", record({ id: "e1" })]]),
|
|
180
|
+
submissions: new Map([["c1", "e1"]]),
|
|
181
|
+
});
|
|
182
|
+
expect(cellExecutionView(forgotten, "c1").status).toBe("queued");
|
|
183
|
+
|
|
184
|
+
const shown = inputs({
|
|
185
|
+
executions: [
|
|
186
|
+
execution({ id: "e2", status: "running", completed_at: null }),
|
|
187
|
+
],
|
|
188
|
+
results: new Map([["c1", record({ id: "e1" })]]),
|
|
189
|
+
submissions: new Map([["c1", "e1"]]),
|
|
190
|
+
});
|
|
191
|
+
expect(cellExecutionView(shown, "c1").status).toBe("running");
|
|
192
|
+
|
|
193
|
+
const done = inputs({
|
|
194
|
+
executions: [execution({ id: "e2" })],
|
|
195
|
+
results: new Map([["c1", record({ id: "e2" })]]),
|
|
196
|
+
submissions: new Map([["c1", "e1"]]),
|
|
197
|
+
});
|
|
198
|
+
expect(cellExecutionView(done, "c1").status).toBe("idle");
|
|
199
|
+
|
|
200
|
+
const fresh = inputs({ submissions: new Map([["c1", null]]) });
|
|
201
|
+
expect(cellExecutionView(fresh, "c1").status).toBe("queued");
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it("shows a blocked cell as disabled transitively, with its last outcome", () => {
|
|
205
|
+
const view = cellExecutionView(
|
|
206
|
+
inputs({
|
|
207
|
+
results: new Map([["c1", record({ id: "e1" })]]),
|
|
208
|
+
blocked: new Set(["c1"]),
|
|
209
|
+
}),
|
|
210
|
+
"c1",
|
|
211
|
+
);
|
|
212
|
+
expect(view.status).toBe("idle");
|
|
213
|
+
expect(view.blocked).toBe(true);
|
|
214
|
+
expect(view.outcome).toBe("value");
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
describe("notebook-level views", () => {
|
|
219
|
+
const stream = inputs({
|
|
220
|
+
executions: [
|
|
221
|
+
execution({ id: "e1", status: "running", completed_at: null }),
|
|
222
|
+
execution({
|
|
223
|
+
id: "e2",
|
|
224
|
+
cell_id: "c2",
|
|
225
|
+
status: "queued",
|
|
226
|
+
started_at: null,
|
|
227
|
+
completed_at: null,
|
|
228
|
+
}),
|
|
229
|
+
execution({ id: "e3", cell_id: "c3" }),
|
|
230
|
+
execution({
|
|
231
|
+
id: "s1",
|
|
232
|
+
cell_id: null,
|
|
233
|
+
status: "running",
|
|
234
|
+
completed_at: null,
|
|
235
|
+
}),
|
|
236
|
+
],
|
|
237
|
+
results: new Map([["c5", record({ id: "e5", cell_id: "c5" })]]),
|
|
238
|
+
submissions: new Map([
|
|
239
|
+
["c2", "e2"],
|
|
240
|
+
["c4", null],
|
|
241
|
+
["c5", "e5"],
|
|
242
|
+
]),
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it("counts unfinished executions and submissions the host has not shown", () => {
|
|
246
|
+
expect(anyRunning(stream.executions)).toBe(true);
|
|
247
|
+
expect(queuedOrRunningCount(stream)).toBe(5);
|
|
248
|
+
expect(runningCellIds(stream.executions)).toEqual(["c1"]);
|
|
249
|
+
expect(onlyScratchRunning(stream.executions)).toBe(false);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it("tells scratch work apart from cells", () => {
|
|
253
|
+
const scratch = inputs({
|
|
254
|
+
executions: [
|
|
255
|
+
execution({
|
|
256
|
+
id: "s1",
|
|
257
|
+
cell_id: null,
|
|
258
|
+
status: "running",
|
|
259
|
+
completed_at: null,
|
|
260
|
+
}),
|
|
261
|
+
execution({ id: "e3", cell_id: "c3" }),
|
|
262
|
+
],
|
|
263
|
+
});
|
|
264
|
+
expect(anyRunning(scratch.executions)).toBe(true);
|
|
265
|
+
expect(onlyScratchRunning(scratch.executions)).toBe(true);
|
|
266
|
+
expect(queuedOrRunningCount(scratch)).toBe(1);
|
|
267
|
+
});
|
|
268
|
+
});
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
/** The active notebook's executions and runtime as the editor reads them. */
|
|
4
|
+
|
|
5
|
+
import type * as Protocol from "@marimo-team/client/protocol";
|
|
6
|
+
import { atom, type createStore } from "jotai";
|
|
7
|
+
import type { CellMessage } from "@/core/kernel/messages";
|
|
8
|
+
import type {
|
|
9
|
+
Executions,
|
|
10
|
+
NotebookReplica,
|
|
11
|
+
Results,
|
|
12
|
+
} from "@/core/notebook/replica";
|
|
13
|
+
import { activeNotebookReplicaAtom } from "@/core/session/active-notebook";
|
|
14
|
+
import { waitFor } from "@/core/state/jotai";
|
|
15
|
+
import {
|
|
16
|
+
anyRunning,
|
|
17
|
+
type CellExecutionInputs,
|
|
18
|
+
latestExecutionId,
|
|
19
|
+
queuedOrRunningCount,
|
|
20
|
+
} from "./select";
|
|
21
|
+
|
|
22
|
+
type Store = ReturnType<typeof createStore>;
|
|
23
|
+
|
|
24
|
+
const NO_EXECUTIONS: Executions = {
|
|
25
|
+
runs: new Map(),
|
|
26
|
+
executions: new Map(),
|
|
27
|
+
latest: new Map(),
|
|
28
|
+
};
|
|
29
|
+
const NO_RESULTS: Results = new Map();
|
|
30
|
+
const NO_SUBMISSIONS: ReadonlyMap<
|
|
31
|
+
Protocol.CellId,
|
|
32
|
+
Protocol.ExecutionId | null
|
|
33
|
+
> = new Map();
|
|
34
|
+
const NO_BLOCKED: ReadonlySet<Protocol.CellId> = new Set();
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* What the editor marks about cells of one replica's notebook.
|
|
38
|
+
*
|
|
39
|
+
* The marks are keyed by cell id, which another notebook, or the same one
|
|
40
|
+
* after a reconnect, reuses with different executions behind it. They
|
|
41
|
+
* therefore count only for the replica and host notebook they belong to.
|
|
42
|
+
*/
|
|
43
|
+
type Marks<T> = {
|
|
44
|
+
readonly notebook: {
|
|
45
|
+
readonly replica: NotebookReplica;
|
|
46
|
+
readonly id: Protocol.NotebookId;
|
|
47
|
+
};
|
|
48
|
+
readonly value: T;
|
|
49
|
+
} | null;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Cells a run was requested for, with the execution each had at the time.
|
|
53
|
+
*
|
|
54
|
+
* The host shows a queued execution once the kernel plans the run. Until
|
|
55
|
+
* then the cell reads as queued from this record. The host's newer
|
|
56
|
+
* execution supersedes it; a rejected request, a cell the kernel will not
|
|
57
|
+
* run, or the cell leaving the document withdraws it. Written only by
|
|
58
|
+
* `submitCells`, `withdrawCells`, and `observeCellMessage`.
|
|
59
|
+
*/
|
|
60
|
+
const submissionsAtom =
|
|
61
|
+
atom<Marks<ReadonlyMap<Protocol.CellId, Protocol.ExecutionId | null>>>(null);
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Cells the kernel reports as blocked by a disabled ancestor. Written only
|
|
65
|
+
* by `observeCellMessage`.
|
|
66
|
+
*
|
|
67
|
+
* TODO(MO-8463): Derive from the document and the analysis stream.
|
|
68
|
+
*/
|
|
69
|
+
const blockedCellsAtom = atom<Marks<ReadonlySet<Protocol.CellId>>>(null);
|
|
70
|
+
|
|
71
|
+
function current<T>(
|
|
72
|
+
marks: Marks<T>,
|
|
73
|
+
replica: NotebookReplica | null,
|
|
74
|
+
notebookId: Protocol.NotebookId | null,
|
|
75
|
+
empty: T,
|
|
76
|
+
): T {
|
|
77
|
+
return marks !== null &&
|
|
78
|
+
marks.notebook.replica === replica &&
|
|
79
|
+
marks.notebook.id === notebookId
|
|
80
|
+
? marks.value
|
|
81
|
+
: empty;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function mark<T>(store: Store, value: T): Marks<T> {
|
|
85
|
+
const replica = store.get(activeNotebookReplicaAtom);
|
|
86
|
+
const notebook = replica === null ? null : store.get(replica.notebookAtom);
|
|
87
|
+
return replica === null || notebook === null
|
|
88
|
+
? null
|
|
89
|
+
: { notebook: { replica, id: notebook.id }, value };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** What every cell's view derives from. Pair with `cellExecutionView`. */
|
|
93
|
+
export const cellExecutionInputsAtom = atom((get): CellExecutionInputs => {
|
|
94
|
+
const replica = get(activeNotebookReplicaAtom);
|
|
95
|
+
const notebookId =
|
|
96
|
+
replica === null ? null : (get(replica.notebookAtom)?.id ?? null);
|
|
97
|
+
return {
|
|
98
|
+
executions: replica === null ? NO_EXECUTIONS : get(replica.executionsAtom),
|
|
99
|
+
results: replica === null ? NO_RESULTS : get(replica.resultsAtom),
|
|
100
|
+
submissions: current(
|
|
101
|
+
get(submissionsAtom),
|
|
102
|
+
replica,
|
|
103
|
+
notebookId,
|
|
104
|
+
NO_SUBMISSIONS,
|
|
105
|
+
),
|
|
106
|
+
blocked: current(get(blockedCellsAtom), replica, notebookId, NO_BLOCKED),
|
|
107
|
+
};
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
/** The runtime's status, null while the notebook has none or the stream is not ready. */
|
|
111
|
+
export const runtimeStatusAtom = atom((get) => {
|
|
112
|
+
const replica = get(activeNotebookReplicaAtom);
|
|
113
|
+
return replica === null ? null : (get(replica.runtimeAtom)?.status ?? null);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
export const notebookIsRunningAtom = atom((get) =>
|
|
117
|
+
anyRunning(get(cellExecutionInputsAtom).executions),
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
export const notebookQueuedOrRunningCountAtom = atom((get) =>
|
|
121
|
+
queuedOrRunningCount(get(cellExecutionInputsAtom)),
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
/** Resolve once the host reports the runtime running. */
|
|
125
|
+
export async function waitForRuntimeRunning(): Promise<void> {
|
|
126
|
+
await waitFor(runtimeStatusAtom, (status) => status === "running");
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Record a run request for `cellIds` so they read as queued at once.
|
|
131
|
+
*
|
|
132
|
+
* Cells blocked by a disabled ancestor stay blocked: the kernel will not
|
|
133
|
+
* queue them. A disabled cell is not queued either; the caller leaves it
|
|
134
|
+
* out, since its configuration lives in the notebook state.
|
|
135
|
+
*/
|
|
136
|
+
export function submitCells(
|
|
137
|
+
store: Store,
|
|
138
|
+
cellIds: readonly Protocol.CellId[],
|
|
139
|
+
): void {
|
|
140
|
+
const inputs = store.get(cellExecutionInputsAtom);
|
|
141
|
+
const next = new Map(inputs.submissions);
|
|
142
|
+
for (const cellId of cellIds) {
|
|
143
|
+
if (!inputs.blocked.has(cellId)) {
|
|
144
|
+
next.set(cellId, latestExecutionId(inputs, cellId));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
setSubmissions(store, next);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function setSubmissions(
|
|
151
|
+
store: Store,
|
|
152
|
+
value: ReadonlyMap<Protocol.CellId, Protocol.ExecutionId | null>,
|
|
153
|
+
): void {
|
|
154
|
+
store.set(submissionsAtom, mark(store, value));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** Forget a run request the host rejected. */
|
|
158
|
+
export function withdrawCells(
|
|
159
|
+
store: Store,
|
|
160
|
+
cellIds: readonly Protocol.CellId[],
|
|
161
|
+
): void {
|
|
162
|
+
const next = new Map(store.get(cellExecutionInputsAtom).submissions);
|
|
163
|
+
for (const cellId of cellIds) {
|
|
164
|
+
next.delete(cellId);
|
|
165
|
+
}
|
|
166
|
+
setSubmissions(store, next);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function setCellBlocked(
|
|
170
|
+
store: Store,
|
|
171
|
+
cellId: Protocol.CellId,
|
|
172
|
+
blocked: boolean,
|
|
173
|
+
): void {
|
|
174
|
+
const known = store.get(cellExecutionInputsAtom).blocked;
|
|
175
|
+
if (known.has(cellId) === blocked) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
const next = new Set(known);
|
|
179
|
+
if (blocked) {
|
|
180
|
+
next.add(cellId);
|
|
181
|
+
} else {
|
|
182
|
+
next.delete(cellId);
|
|
183
|
+
}
|
|
184
|
+
store.set(blockedCellsAtom, mark(store, next));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Keep the blocked set and the submissions in step with a cell-op.
|
|
189
|
+
*
|
|
190
|
+
* A status names whether the cell is blocked. A blocked cell and one whose
|
|
191
|
+
* output is an error will not run, so a submission that never became an
|
|
192
|
+
* execution is withdrawn, as it was when the editor set status itself.
|
|
193
|
+
*
|
|
194
|
+
* TODO(MO-8407): Delete with the cell-op case. The blocked read goes with
|
|
195
|
+
* MO-8463, the error read with MO-8346.
|
|
196
|
+
*/
|
|
197
|
+
export function observeCellMessage(store: Store, message: CellMessage): void {
|
|
198
|
+
const cellId = message.cell_id;
|
|
199
|
+
const blocked = message.status === "disabled-transitively";
|
|
200
|
+
if (message.status != null) {
|
|
201
|
+
setCellBlocked(store, cellId, blocked);
|
|
202
|
+
}
|
|
203
|
+
if (blocked || message.output?.mimetype === "application/vnd.marimo+error") {
|
|
204
|
+
withdrawCells(store, [cellId]);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import type * as Protocol from "@marimo-team/client/protocol";
|
|
4
|
+
import { dequal } from "dequal";
|
|
5
|
+
import { useAtomValue } from "jotai";
|
|
6
|
+
import { selectAtom } from "jotai/utils";
|
|
7
|
+
import { useMemo } from "react";
|
|
8
|
+
import { cellExecutionInputsAtom } from "./atoms";
|
|
9
|
+
import { type CellExecutionView, cellExecutionView } from "./select";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A cell's status, timing, and how it last ended.
|
|
13
|
+
*
|
|
14
|
+
* Read from the host's executions, with the editor's own marks for a run
|
|
15
|
+
* it has requested and the kernel has not planned yet, and for a cell the
|
|
16
|
+
* kernel reports blocked by a disabled ancestor.
|
|
17
|
+
*/
|
|
18
|
+
export function useCellExecution(id: Protocol.CellId): CellExecutionView {
|
|
19
|
+
// One atom per mounted cell, kept for its subscription and compared by
|
|
20
|
+
// value so the cell renders only when its own view changes.
|
|
21
|
+
const cellAtom = useMemo(
|
|
22
|
+
() =>
|
|
23
|
+
selectAtom(
|
|
24
|
+
cellExecutionInputsAtom,
|
|
25
|
+
(inputs) => cellExecutionView(inputs, id),
|
|
26
|
+
dequal,
|
|
27
|
+
),
|
|
28
|
+
[id],
|
|
29
|
+
);
|
|
30
|
+
return useAtomValue(cellAtom);
|
|
31
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Cell status and timing read from the host's executions.
|
|
5
|
+
*
|
|
6
|
+
* Every atom here is derived, so it can be read but not set. The state
|
|
7
|
+
* behind them changes only through `submitCells`, `withdrawCells`, and
|
|
8
|
+
* `observeCellMessage`.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export {
|
|
12
|
+
cellExecutionInputsAtom,
|
|
13
|
+
notebookIsRunningAtom,
|
|
14
|
+
notebookQueuedOrRunningCountAtom,
|
|
15
|
+
observeCellMessage,
|
|
16
|
+
runtimeStatusAtom,
|
|
17
|
+
submitCells,
|
|
18
|
+
waitForRuntimeRunning,
|
|
19
|
+
withdrawCells,
|
|
20
|
+
} from "./atoms";
|
|
21
|
+
export { useCellExecution } from "./hooks";
|
|
22
|
+
export {
|
|
23
|
+
type CellExecutionInputs,
|
|
24
|
+
type CellExecutionView,
|
|
25
|
+
type CellOutcome,
|
|
26
|
+
type CellRunStatus,
|
|
27
|
+
cellExecutionView,
|
|
28
|
+
onlyScratchRunning,
|
|
29
|
+
runningCellIds,
|
|
30
|
+
scratchStatus,
|
|
31
|
+
} from "./select";
|