@marimo-team/islands 0.25.3-dev308 → 0.25.3-dev310
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-DJnSxHJB.js → ConnectedDataExplorerComponent-DieU6L4x.js} +1 -1
- package/dist/{ErrorBoundary-flfhLFje.js → ErrorBoundary-DjoeT4kv.js} +1 -1
- package/dist/{abnfDiagram-VCTEODGH-CR0U5hup.js → abnfDiagram-VCTEODGH-LOVrjbdp.js} +2 -2
- package/dist/{any-language-editor-C5dsTpeB.js → any-language-editor-DMC2JE9_.js} +1 -1
- package/dist/{architectureDiagram-5GKGNRK7-Bc_fbI1Q.js → architectureDiagram-5GKGNRK7-RZsm312h.js} +1 -1
- package/dist/assets/__vite-browser-external-BSp9tJ_k.js +1 -0
- package/dist/assets/worker-BdfPA0g5.js +171 -0
- package/dist/{chat-ui-C4iLRzY0.js → chat-ui-DkuxGEDp.js} +418 -418
- package/dist/{chunk-IMKFNOWR-y9z5oeAY.js → chunk-IMKFNOWR-B6agbLjo.js} +1 -1
- package/dist/{chunk-SHT3W25Y-Tu-V-e_v.js → chunk-SHT3W25Y-CfULafYG.js} +1 -1
- package/dist/{chunk-SVP7TREG-CJFxs5R_.js → chunk-SVP7TREG-DEOpPMJp.js} +1 -1
- package/dist/{chunk-TICWLB2K-zkTP7ya7.js → chunk-TICWLB2K-DaH-gkX5.js} +1 -1
- package/dist/{classDiagram-ZZMXUADV-DxJAZI0N.js → classDiagram-ZZMXUADV-C0CyiWQ7.js} +2 -2
- package/dist/{classDiagram-v2-VYDZK3BY-TmHo2BpS.js → classDiagram-v2-VYDZK3BY-xfIc9e2Y.js} +2 -2
- package/dist/{code-visibility-BDs_r7jH.js → code-visibility-CduMVVFS.js} +32 -32
- package/dist/{common-CMTsL35V.js → common-CMcEcucH.js} +919 -919
- package/dist/{config-BG2qKzDg.js → config-BVYA1P0C.js} +103 -76
- package/dist/{cynefinDiagram-5FMLGOSQ-Eb40hmhD.js → cynefinDiagram-5FMLGOSQ-DwGYhKu0.js} +1 -1
- package/dist/{dagre-GXQ25YYZ-_paaVwit.js → dagre-GXQ25YYZ-DfMaWJOV.js} +1 -1
- package/dist/{data-grid-overlay-editor-es6V6qf-.js → data-grid-overlay-editor-BYrIVut3.js} +1 -1
- package/dist/{diagram-S7CK7UJ4-BtKJm1_4.js → diagram-S7CK7UJ4-Bw_L87GZ.js} +1 -1
- package/dist/{diagram-UQ7AKVKN-DQbxhEMd.js → diagram-UQ7AKVKN-nsrs3Fbz.js} +1 -1
- package/dist/{diagram-VX7I27RA-DITDIfGC.js → diagram-VX7I27RA-7ST9lEqP.js} +1 -1
- package/dist/{diagram-Z3DM3KII-Ct9_zo6F.js → diagram-Z3DM3KII-BnGte-HX.js} +1 -1
- package/dist/{ebnfDiagram-PWID7BFC-6cOntwfv.js → ebnfDiagram-PWID7BFC-zaKyJ1YL.js} +2 -2
- package/dist/{erDiagram-RLTQ6QDP-CJiOER41.js → erDiagram-RLTQ6QDP-Cx7g95FZ.js} +1 -1
- package/dist/{error-banner-DGOrmBk5.js → error-banner-DkQNU4bn.js} +128 -128
- package/dist/{error-entries-BWmleiL0.js → error-entries-istW7Xbs.js} +589 -544
- package/dist/{feedback-button-D4eVjeNs.js → feedback-button-Zvm26aOQ.js} +107 -106
- package/dist/{flowDiagram-HODETNUW-1lPBEUuW.js → flowDiagram-HODETNUW-C7nIqaHP.js} +2 -2
- package/dist/{formats-qhBmYb0l.js → formats-CMa7L3E8.js} +1 -1
- package/dist/{glide-data-editor-ahfaPdFW.js → glide-data-editor-BE-GgVp4.js} +3 -3
- package/dist/{globals-m8oswz--.js → globals-Dbru-WLP.js} +1 -1
- package/dist/{html-to-image-B8hE6HIH.js → html-to-image-pPbR2cDx.js} +4512 -4464
- package/dist/{infoDiagram-27XIBGKW-C6K4tri5.js → infoDiagram-27XIBGKW-BTM_NXsn.js} +1 -1
- package/dist/{ishikawaDiagram-5VMMS53U-DwcwgRNH.js → ishikawaDiagram-5VMMS53U-K0FZrlvv.js} +1 -1
- package/dist/{kanban-definition-UXKFOSKX-E489OMdc.js → kanban-definition-UXKFOSKX-ftznOyrz.js} +1 -1
- package/dist/main.js +6595 -6186
- package/dist/{mermaid-DMPMkVqY.js → mermaid-CNgH43ED.js} +33 -33
- package/dist/{mindmap-definition-YA3MSWOX-DWHjasOp.js → mindmap-definition-YA3MSWOX-CmzbxKfM.js} +1 -1
- package/dist/{number-overlay-editor-C6klKzEr.js → number-overlay-editor-qvlPZ-l-.js} +1 -1
- package/dist/{pegDiagram-XKGWAZYB-COjcPOZT.js → pegDiagram-XKGWAZYB-DfTAWfGE.js} +2 -2
- package/dist/{pieDiagram-E7YTZNPT-BHAVZ38h.js → pieDiagram-E7YTZNPT-_kGQstZj.js} +1 -1
- package/dist/{process-output-DZDL_j1a.js → process-output-BySO1nIs.js} +3 -3
- package/dist/{railroadDiagram-O6MQD6OU-94ThFyKX.js → railroadDiagram-O6MQD6OU-D41WmIff.js} +2 -2
- package/dist/{requirementDiagram-BXWQKSXE-CpF-Fxbb.js → requirementDiagram-BXWQKSXE-Bh53ZI3I.js} +1 -1
- package/dist/{reveal-component-DwnxPyVz.js → reveal-component-DQaegdcW.js} +4 -4
- package/dist/{sequenceDiagram-WJ2MYXX4-CvANK2x7.js → sequenceDiagram-WJ2MYXX4-DBX27dNm.js} +1 -1
- package/dist/{stateDiagram-D77RDMKH-WHuJ4mJL.js → stateDiagram-D77RDMKH-CSYz2IZi.js} +2 -2
- package/dist/{stateDiagram-v2-MP3YSRHH-DbO7lwYq.js → stateDiagram-v2-MP3YSRHH-DVLvtOqn.js} +2 -2
- package/dist/{swimlanes-42K2YHIH-CS2s29Vo.js → swimlanes-42K2YHIH-D59N0ktA.js} +1 -1
- package/dist/{swimlanesDiagram-VR7AAH4N-CXaA1vQg.js → swimlanesDiagram-VR7AAH4N-CRgpmVSW.js} +2 -2
- package/dist/{timeline-definition-24CTP7MA-B-SQMg5h.js → timeline-definition-24CTP7MA-C_xIIZDM.js} +1 -1
- package/dist/toDate-5qPtZ6HL.js +94 -0
- package/dist/{vega-component-DwqGfN3_.js → vega-component-Bly2lDhz.js} +10 -10
- package/dist/{vennDiagram-4TSXK5OY-etEFgvaG.js → vennDiagram-4TSXK5OY-DlOf0Mjs.js} +1 -1
- package/dist/{wardleyDiagram-VM6X3IG4-BEQN7g62.js → wardleyDiagram-VM6X3IG4-zD_2guVe.js} +1 -1
- package/dist/{xychartDiagram-S5SC5T6Z-D8-e5Z0R.js → xychartDiagram-S5SC5T6Z-CA0Dy1ZM.js} +1 -1
- package/package.json +1 -1
- package/src/__mocks__/executions.ts +35 -13
- package/src/__mocks__/requests.ts +0 -1
- package/src/components/editor/cell/__tests__/useRunCells.test.tsx +86 -101
- package/src/components/editor/cell/useRunCells.ts +38 -65
- package/src/components/editor/notebook-cell.tsx +32 -20
- package/src/components/editor/output/ansi-reduce.ts +7 -0
- package/src/core/ai/__tests__/staged-cells.test.ts +1 -1
- package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +2 -0
- package/src/core/cells/__tests__/cells.test.ts +0 -14
- package/src/core/cells/__tests__/collapseConsoleOutputs.test.ts +15 -0
- package/src/core/cells/__tests__/utils.test.ts +1 -1
- package/src/core/cells/add-missing-import.ts +2 -5
- package/src/core/cells/cell.ts +1 -0
- package/src/core/cells/cells.ts +1 -2
- package/src/core/executions/__tests__/atoms.test.ts +227 -43
- package/src/core/executions/__tests__/hooks.test.tsx +1 -2
- package/src/core/executions/__tests__/input.test.ts +86 -0
- package/src/core/executions/__tests__/run.test.ts +390 -0
- package/src/core/executions/__tests__/select.test.ts +105 -30
- package/src/core/executions/atoms.ts +186 -71
- package/src/core/executions/index.ts +7 -6
- package/src/core/executions/input.ts +55 -0
- package/src/core/executions/run.ts +120 -0
- package/src/core/executions/select.ts +59 -24
- package/src/core/islands/__tests__/bridge.test.ts +35 -54
- package/src/core/islands/bootstrap.ts +5 -1
- package/src/core/islands/bridge.ts +16 -28
- package/src/core/islands/components/IslandControls.tsx +15 -7
- package/src/core/islands/components/__tests__/IslandControls.test.tsx +36 -53
- package/src/core/islands/worker/worker.tsx +45 -19
- package/src/core/network/__tests__/requests-lazy.test.ts +14 -24
- package/src/core/network/__tests__/requests-toasting.test.tsx +7 -7
- package/src/core/network/requests-lazy.ts +2 -3
- package/src/core/network/requests-network.ts +0 -9
- package/src/core/network/requests-static.ts +0 -1
- package/src/core/network/requests-toasting.tsx +0 -1
- package/src/core/network/types.ts +0 -2
- package/src/core/notebook/replica/__tests__/fixtures.ts +17 -1
- package/src/core/notebook/replica/backend.ts +3 -1
- package/src/core/runtime/__tests__/runtime.test.ts +36 -0
- package/src/core/runtime/config.ts +36 -1
- package/src/core/runtime/runtime.ts +21 -1
- package/src/core/transports/static.ts +4 -3
- package/src/core/wasm/bridge.ts +0 -17
- package/dist/assets/__vite-browser-external-DzTYIHH_.js +0 -1
- package/dist/assets/worker-BH08I2Il.js +0 -171
- package/dist/static-state-Dtocd9UO.js +0 -68
|
@@ -2,7 +2,7 @@ import { i as e } from "./chunk-75Z2AOVW-9jQIEAxd.js";
|
|
|
2
2
|
import { n as t } from "./src-D5uZaZib.js";
|
|
3
3
|
import { n } from "./chunk-Y2CYZVJY-DKKS1VyV.js";
|
|
4
4
|
import { D as r, H as i, K as a, U as o, a as s, b as c, c as l, v as u, w as d, x as f, y as p } from "./chunk-DU6HZSFF-B20LZ-hE.js";
|
|
5
|
-
import { p as m } from "./mermaid-
|
|
5
|
+
import { p as m } from "./mermaid-CNgH43ED.js";
|
|
6
6
|
import { n as h } from "./mermaid-parser.core-DtOnl7m4.js";
|
|
7
7
|
import { t as g } from "./chunk-JWPE2WC7-DJvv1t8U.js";
|
|
8
8
|
//#region ../node_modules/.pnpm/mermaid@11.17.2/node_modules/mermaid/dist/chunks/mermaid.core/wardleyDiagram-VM6X3IG4.mjs
|
|
@@ -9,7 +9,7 @@ import { n as s } from "./chunk-Y2CYZVJY-DKKS1VyV.js";
|
|
|
9
9
|
import { D as c, H as l, K as u, U as d, a as f, b as p, c as m, f as h, v as g, w as _, y as v, z as y } from "./chunk-DU6HZSFF-B20LZ-hE.js";
|
|
10
10
|
import "./chunk-PWAF6VOD-oT_Qd2OU.js";
|
|
11
11
|
import { t as b } from "./chunk-GMAD6QVW-DokpSFdf.js";
|
|
12
|
-
import { p as x } from "./mermaid-
|
|
12
|
+
import { p as x } from "./mermaid-CNgH43ED.js";
|
|
13
13
|
//#region ../node_modules/.pnpm/d3-scale@4.0.2/node_modules/d3-scale/src/band.js
|
|
14
14
|
function S() {
|
|
15
15
|
var e = t().unknown(void 0), r = e.domain, i = e.range, o = 0, s = 1, c, l, u = !1, d = 0, f = 0, p = .5;
|
package/package.json
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
type Document,
|
|
12
12
|
type Execution,
|
|
13
13
|
type Executions,
|
|
14
|
+
type NotebookBackend,
|
|
14
15
|
type NotebookReplica,
|
|
15
16
|
type Results,
|
|
16
17
|
} from "@/core/notebook/replica";
|
|
@@ -18,6 +19,12 @@ import { activeNotebookReplicaAtom } from "@/core/session/active-notebook";
|
|
|
18
19
|
|
|
19
20
|
type Store = ReturnType<typeof createStore>;
|
|
20
21
|
|
|
22
|
+
const CONFIG: Protocol.CellConfig = {
|
|
23
|
+
column: null,
|
|
24
|
+
disabled: false,
|
|
25
|
+
hide_code: false,
|
|
26
|
+
expand_output: false,
|
|
27
|
+
};
|
|
21
28
|
const T0 = "2026-10-01T09:20:11Z";
|
|
22
29
|
const T1 = "2026-10-01T09:20:12.500Z";
|
|
23
30
|
|
|
@@ -138,6 +145,17 @@ export const MockExecutions = {
|
|
|
138
145
|
});
|
|
139
146
|
},
|
|
140
147
|
|
|
148
|
+
notebook(id: Protocol.NotebookId = "n1"): Protocol.Notebook {
|
|
149
|
+
return {
|
|
150
|
+
id,
|
|
151
|
+
project_id: "p1",
|
|
152
|
+
path: null,
|
|
153
|
+
name: "Notebook",
|
|
154
|
+
operations: [],
|
|
155
|
+
updated_at: null,
|
|
156
|
+
};
|
|
157
|
+
},
|
|
158
|
+
|
|
141
159
|
runtime(status: Protocol.RuntimeStatus): Protocol.Runtime {
|
|
142
160
|
return {
|
|
143
161
|
id: "r1",
|
|
@@ -149,15 +167,24 @@ export const MockExecutions = {
|
|
|
149
167
|
};
|
|
150
168
|
},
|
|
151
169
|
|
|
152
|
-
/**
|
|
170
|
+
/**
|
|
171
|
+
* What the selectors read. `latest` and the results are derived from
|
|
172
|
+
* `records`, and the document is taken to have every cell they name,
|
|
173
|
+
* with nothing keeping it from running.
|
|
174
|
+
*/
|
|
153
175
|
inputs(
|
|
154
176
|
records: Protocol.Execution[],
|
|
155
|
-
extra: Partial<Pick<CellExecutionInputs, "
|
|
177
|
+
extra: Partial<Pick<CellExecutionInputs, "requested" | "blocked">> = {},
|
|
156
178
|
): CellExecutionInputs {
|
|
157
179
|
return {
|
|
158
180
|
executions: mirror(records),
|
|
159
181
|
results: results(records),
|
|
160
|
-
|
|
182
|
+
cells: new Map(
|
|
183
|
+
records.flatMap((record) =>
|
|
184
|
+
record.cell_id === null ? [] : [[record.cell_id, { config: CONFIG }]],
|
|
185
|
+
),
|
|
186
|
+
),
|
|
187
|
+
requested: new Set(),
|
|
161
188
|
blocked: new Set(),
|
|
162
189
|
...extra,
|
|
163
190
|
};
|
|
@@ -172,20 +199,15 @@ export const MockExecutions = {
|
|
|
172
199
|
) {
|
|
173
200
|
const current = store.get(activeNotebookReplicaAtom);
|
|
174
201
|
const replica = {
|
|
175
|
-
connectionAtom: atom<ConnectionStatus>({ state: ConnectionState.OPEN }),
|
|
176
|
-
backendAtom: atom(null),
|
|
177
202
|
attachmentsAtom: atom([]),
|
|
178
203
|
connect: () => () => undefined,
|
|
179
204
|
reconnect: () => Promise.resolve(),
|
|
180
205
|
...current,
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
operations: [],
|
|
187
|
-
updated_at: null,
|
|
188
|
-
}),
|
|
206
|
+
// Settable, so a test can close the connection or give the replica a
|
|
207
|
+
// backend to command.
|
|
208
|
+
connectionAtom: atom<ConnectionStatus>({ state: ConnectionState.OPEN }),
|
|
209
|
+
backendAtom: atom<NotebookBackend | null>(null),
|
|
210
|
+
notebookAtom: atom<Protocol.Notebook | null>(MockExecutions.notebook()),
|
|
189
211
|
documentAtom: atom<Document | null>(document),
|
|
190
212
|
runtimeAtom: atom(runtime),
|
|
191
213
|
executionsAtom: atom(mirror(records)),
|
|
@@ -23,7 +23,6 @@ export const MockRequestClient = {
|
|
|
23
23
|
sendFormat: vi.fn().mockResolvedValue({ codes: {} }),
|
|
24
24
|
sendInterrupt: vi.fn().mockResolvedValue({}),
|
|
25
25
|
sendShutdown: vi.fn().mockResolvedValue({}),
|
|
26
|
-
sendRun: vi.fn().mockResolvedValue({}),
|
|
27
26
|
sendRunScratchpad: vi.fn().mockResolvedValue({}),
|
|
28
27
|
sendInstantiate: vi.fn().mockResolvedValue({}),
|
|
29
28
|
sendCodeCompletionRequest: vi.fn().mockResolvedValue({ items: [] }),
|
|
@@ -1,169 +1,154 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
|
+
import type * as Protocol from "@marimo-team/client/protocol";
|
|
3
4
|
import { act, renderHook } from "@testing-library/react";
|
|
4
|
-
import { Provider } from "jotai";
|
|
5
|
+
import { atom, Provider } from "jotai";
|
|
5
6
|
import type { ReactNode } from "react";
|
|
6
|
-
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
7
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
7
8
|
import { MockExecutions } from "@/__mocks__/executions";
|
|
8
9
|
import { MockNotebook } from "@/__mocks__/notebook";
|
|
9
|
-
import { MockRequestClient } from "@/__mocks__/requests";
|
|
10
10
|
import { cellId } from "@/__tests__/branded";
|
|
11
11
|
import { notebookAtom } from "@/core/cells/cells";
|
|
12
|
-
import {
|
|
13
|
-
cellExecutionInputsAtom,
|
|
14
|
-
cellExecutionView,
|
|
15
|
-
notebookQueuedOrRunningCountAtom,
|
|
16
|
-
} from "@/core/executions";
|
|
17
|
-
import { requestClientAtom } from "@/core/network/requests";
|
|
18
12
|
import type { DocumentModel } from "@/core/notebook/document";
|
|
19
13
|
import {
|
|
20
14
|
cell,
|
|
21
15
|
document as documentOf,
|
|
22
16
|
withCells,
|
|
23
17
|
} from "@/core/notebook/document/__tests__/fixtures";
|
|
18
|
+
import {
|
|
19
|
+
type FakeRun,
|
|
20
|
+
fakeBackend,
|
|
21
|
+
} from "@/core/notebook/replica/__tests__/fixtures";
|
|
24
22
|
import { activeDocumentModelAtom } from "@/core/session/active-notebook";
|
|
25
23
|
import { store } from "@/core/state/jotai";
|
|
26
|
-
import {
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
expect(cellExecutionView(inputs, DISABLED).status).toBe("idle");
|
|
56
|
-
expect(store.get(notebookQueuedOrRunningCountAtom)).toBe(1);
|
|
57
|
-
});
|
|
58
|
-
});
|
|
24
|
+
import { useRunStaleCells } from "../useRunCells";
|
|
25
|
+
|
|
26
|
+
const toast = vi.fn();
|
|
27
|
+
vi.mock("@/components/ui/use-toast", () => ({
|
|
28
|
+
toast: (...args: unknown[]) => toast(...args),
|
|
29
|
+
}));
|
|
30
|
+
vi.mock("@/core/runtime/config", () => ({
|
|
31
|
+
startRuntimeIfLazy: async () => undefined,
|
|
32
|
+
}));
|
|
33
|
+
|
|
34
|
+
const C1 = cellId("c1");
|
|
35
|
+
const C2 = cellId("c2");
|
|
36
|
+
|
|
37
|
+
const wrapper = ({ children }: { children: ReactNode }) => (
|
|
38
|
+
<Provider store={store}>{children}</Provider>
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
function accepted(cells: Protocol.CellId[]): Protocol.Run {
|
|
42
|
+
return {
|
|
43
|
+
id: "run-1",
|
|
44
|
+
notebook_id: "n1",
|
|
45
|
+
runtime_id: "r1",
|
|
46
|
+
cells,
|
|
47
|
+
status: "queued",
|
|
48
|
+
outcome: null,
|
|
49
|
+
started_at: null,
|
|
50
|
+
completed_at: null,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
59
53
|
|
|
60
54
|
describe("useRunStaleCells", () => {
|
|
61
|
-
|
|
62
|
-
|
|
55
|
+
let run: ReturnType<typeof vi.fn<FakeRun>>;
|
|
56
|
+
|
|
57
|
+
beforeEach(() => {
|
|
58
|
+
toast.mockClear();
|
|
59
|
+
run = vi.fn<FakeRun>(async (_notebookId, request) => ({
|
|
60
|
+
ok: true,
|
|
61
|
+
status: 202,
|
|
62
|
+
data: accepted(request.cells?.map((cell) => cell.id) ?? []),
|
|
63
|
+
}));
|
|
64
|
+
});
|
|
63
65
|
|
|
64
66
|
afterEach(() => {
|
|
65
67
|
store.set(activeDocumentModelAtom, null);
|
|
66
68
|
});
|
|
67
69
|
|
|
68
|
-
|
|
70
|
+
function seed(records: Protocol.Execution[]) {
|
|
69
71
|
const document = documentOf();
|
|
70
72
|
const replica = MockExecutions.seed(
|
|
71
73
|
store,
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
MockExecutions.completed(C2, { code_fingerprint: "c2-hash" }),
|
|
75
|
-
],
|
|
76
|
-
null,
|
|
74
|
+
records,
|
|
75
|
+
MockExecutions.runtime("running"),
|
|
77
76
|
document,
|
|
78
77
|
);
|
|
78
|
+
store.set(replica.backendAtom, fakeBackend({ run }));
|
|
79
|
+
return { replica, document };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function model(sync: () => Promise<Protocol.Document>) {
|
|
83
|
+
store.set(activeDocumentModelAtom, {
|
|
84
|
+
retry: sync,
|
|
85
|
+
documentAtom: atom(null),
|
|
86
|
+
} as unknown as DocumentModel);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
it("syncs pending edits before choosing the stale cells", async () => {
|
|
90
|
+
const { replica, document } = seed([
|
|
91
|
+
MockExecutions.completed(C1, { code_fingerprint: "c1-hash" }),
|
|
92
|
+
MockExecutions.completed(C2, { code_fingerprint: "c2-hash" }),
|
|
93
|
+
]);
|
|
79
94
|
store.set(
|
|
80
95
|
notebookAtom,
|
|
81
96
|
MockNotebook.notebookState({
|
|
82
97
|
cellData: { [C1]: { code: "x = 2" }, [C2]: { code: "y = 2" } },
|
|
83
98
|
}),
|
|
84
99
|
);
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
// The edit to c1 is still in flight: the host confirms it on flush.
|
|
88
|
-
const flush = vi.fn(async () => {
|
|
100
|
+
// The edit to c1 is still in flight: the host confirms it on sync.
|
|
101
|
+
const sync = vi.fn(async () => {
|
|
89
102
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
);
|
|
103
|
+
const confirmed = withCells(document, {
|
|
104
|
+
[C1]: cell(C1, "x = 2", { code_fingerprint: "c1-hash-2" }),
|
|
105
|
+
});
|
|
106
|
+
store.set(replica.documentAtom, confirmed);
|
|
107
|
+
return confirmed.revision;
|
|
96
108
|
});
|
|
97
|
-
|
|
98
|
-
const wrapper = ({ children }: { children: ReactNode }) => (
|
|
99
|
-
<Provider store={store}>{children}</Provider>
|
|
100
|
-
);
|
|
109
|
+
model(sync);
|
|
101
110
|
const { result } = renderHook(useRunStaleCells, { wrapper });
|
|
102
111
|
|
|
103
112
|
await act(() => result.current());
|
|
104
113
|
|
|
105
|
-
expect(
|
|
106
|
-
expect(
|
|
107
|
-
cellIds: [C1],
|
|
108
|
-
codes: ["x = 2"],
|
|
109
|
-
});
|
|
114
|
+
expect(sync).toHaveBeenCalled();
|
|
115
|
+
expect(run).toHaveBeenCalledWith("n1", { cells: [{ id: C1 }] });
|
|
110
116
|
});
|
|
111
117
|
|
|
112
118
|
it("does nothing when the notebook changed while the flush was pending", async () => {
|
|
113
|
-
MockExecutions.
|
|
114
|
-
store,
|
|
115
|
-
[MockExecutions.completed(C1, { code_fingerprint: "stale" })],
|
|
116
|
-
null,
|
|
117
|
-
documentOf(),
|
|
118
|
-
);
|
|
119
|
+
seed([MockExecutions.completed(C1, { code_fingerprint: "stale" })]);
|
|
119
120
|
store.set(
|
|
120
121
|
notebookAtom,
|
|
121
122
|
MockNotebook.notebookState({ cellData: { [C1]: { code: "x = 1" } } }),
|
|
122
123
|
);
|
|
123
|
-
|
|
124
|
-
store.set(requestClientAtom, client);
|
|
125
|
-
const flush = vi.fn(async () => {
|
|
124
|
+
model(async () => {
|
|
126
125
|
store.set(activeDocumentModelAtom, null);
|
|
127
126
|
throw new Error("aborted");
|
|
128
127
|
});
|
|
129
|
-
store.set(activeDocumentModelAtom, { flush } as unknown as DocumentModel);
|
|
130
|
-
const wrapper = ({ children }: { children: ReactNode }) => (
|
|
131
|
-
<Provider store={store}>{children}</Provider>
|
|
132
|
-
);
|
|
133
128
|
const { result } = renderHook(useRunStaleCells, { wrapper });
|
|
134
129
|
|
|
135
130
|
await act(() => result.current());
|
|
136
131
|
|
|
137
|
-
expect(
|
|
132
|
+
expect(run).not.toHaveBeenCalled();
|
|
133
|
+
expect(toast).not.toHaveBeenCalled();
|
|
138
134
|
});
|
|
139
135
|
|
|
140
|
-
it("
|
|
141
|
-
MockExecutions.
|
|
142
|
-
store,
|
|
143
|
-
[MockExecutions.completed(C1, { code_fingerprint: "stale" })],
|
|
144
|
-
null,
|
|
145
|
-
documentOf(),
|
|
146
|
-
);
|
|
136
|
+
it("runs nothing while edits cannot reach the host", async () => {
|
|
137
|
+
seed([MockExecutions.completed(C1, { code_fingerprint: "stale" })]);
|
|
147
138
|
store.set(
|
|
148
139
|
notebookAtom,
|
|
149
140
|
MockNotebook.notebookState({ cellData: { [C1]: { code: "x = 1" } } }),
|
|
150
141
|
);
|
|
151
|
-
|
|
152
|
-
store.set(requestClientAtom, client);
|
|
153
|
-
const flush = vi.fn(async () => {
|
|
142
|
+
model(async () => {
|
|
154
143
|
throw new Error("offline");
|
|
155
144
|
});
|
|
156
|
-
store.set(activeDocumentModelAtom, { flush } as unknown as DocumentModel);
|
|
157
|
-
const wrapper = ({ children }: { children: ReactNode }) => (
|
|
158
|
-
<Provider store={store}>{children}</Provider>
|
|
159
|
-
);
|
|
160
145
|
const { result } = renderHook(useRunStaleCells, { wrapper });
|
|
161
146
|
|
|
162
147
|
await act(() => result.current());
|
|
163
148
|
|
|
164
|
-
expect(
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
149
|
+
expect(run).not.toHaveBeenCalled();
|
|
150
|
+
expect(toast).toHaveBeenCalledWith(
|
|
151
|
+
expect.objectContaining({ title: "Edits not yet synced" }),
|
|
152
|
+
);
|
|
168
153
|
});
|
|
169
154
|
});
|
|
@@ -4,22 +4,21 @@ import { closeCompletion } from "@codemirror/autocomplete";
|
|
|
4
4
|
import { atom, useSetAtom } from "jotai";
|
|
5
5
|
import useEvent from "react-use-event-hook";
|
|
6
6
|
import { toast } from "@/components/ui/use-toast";
|
|
7
|
-
import { getNotebook
|
|
7
|
+
import { getNotebook } from "@/core/cells/cells";
|
|
8
8
|
import { useCellActions } from "@/core/cells/actions";
|
|
9
9
|
import type { CellId } from "@/core/cells/ids";
|
|
10
10
|
import { enabledCellIds, staleCellIds } from "@/core/cells/utils";
|
|
11
11
|
import {
|
|
12
12
|
cellExecutionInputsAtom,
|
|
13
13
|
cellFreshnessAtom,
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
runCells,
|
|
15
|
+
syncEdits,
|
|
16
|
+
waitForRuntime,
|
|
16
17
|
} from "@/core/executions";
|
|
17
18
|
import { getCurrentLanguageAdapter } from "@/core/codemirror/language/commands";
|
|
18
|
-
import { getEditorCodeAsPython } from "@/core/codemirror/language/utils";
|
|
19
|
-
import { useRequestClient } from "@/core/network/requests";
|
|
20
|
-
import type { ExecuteCellsRequest } from "@/core/network/types";
|
|
21
19
|
import { activeDocumentModelAtom } from "@/core/session/active-notebook";
|
|
22
20
|
import { store } from "@/core/state/jotai";
|
|
21
|
+
import { prettyError } from "@/utils/errors";
|
|
23
22
|
import { Logger } from "@/utils/Logger";
|
|
24
23
|
|
|
25
24
|
export const hasRunAnyCellAtom = atom<boolean>(false);
|
|
@@ -33,18 +32,36 @@ export function useRunStaleCells() {
|
|
|
33
32
|
// Freshness reads the confirmed document, so an edit still in flight
|
|
34
33
|
// reaches the host before the stale cells are chosen.
|
|
35
34
|
const model = store.get(activeDocumentModelAtom);
|
|
35
|
+
if (model === null) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
36
38
|
try {
|
|
37
|
-
await
|
|
39
|
+
await waitForRuntime(store);
|
|
38
40
|
} catch (error) {
|
|
39
41
|
if (store.get(activeDocumentModelAtom) !== model) {
|
|
40
42
|
return;
|
|
41
43
|
}
|
|
42
|
-
Logger.warn("
|
|
44
|
+
Logger.warn("Stale cells not run without a runtime", error);
|
|
45
|
+
toast({
|
|
46
|
+
title: "Failed to run",
|
|
47
|
+
description: prettyError(error),
|
|
48
|
+
variant: "danger",
|
|
49
|
+
});
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
await syncEdits(model);
|
|
54
|
+
} catch (error) {
|
|
55
|
+
if (store.get(activeDocumentModelAtom) !== model) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
Logger.warn("Stale cells not run with unsynced edits", error);
|
|
43
59
|
toast({
|
|
44
60
|
title: "Edits not yet synced",
|
|
45
|
-
description: "Stale cells were
|
|
61
|
+
description: "Stale cells were not run. Retry once the edits sync.",
|
|
46
62
|
variant: "danger",
|
|
47
63
|
});
|
|
64
|
+
return;
|
|
48
65
|
}
|
|
49
66
|
// The request was for the notebook that was open when it was made.
|
|
50
67
|
if (store.get(activeDocumentModelAtom) !== model) {
|
|
@@ -82,78 +99,34 @@ export function useRunAllCells() {
|
|
|
82
99
|
}
|
|
83
100
|
|
|
84
101
|
/**
|
|
85
|
-
* Creates a function that runs the given cells.
|
|
102
|
+
* Creates a function that runs the given cells through the host.
|
|
103
|
+
*
|
|
104
|
+
* The editor settles each cell first: an open completion closes and the
|
|
105
|
+
* debugger view clears, as they would on any run.
|
|
86
106
|
*/
|
|
87
107
|
export function useRunCells() {
|
|
88
108
|
const { prepareForRun } = useCellActions();
|
|
89
|
-
const { sendRun } = useRequestClient();
|
|
90
109
|
const setHasRunAnyCell = useSetAtom(hasRunAnyCellAtom);
|
|
91
110
|
|
|
92
111
|
const runCellsMemoized = useEvent(async (cellIds: CellId[]) => {
|
|
93
112
|
if (cellIds.length === 0) {
|
|
94
113
|
return;
|
|
95
114
|
}
|
|
96
|
-
|
|
97
|
-
const notebook = getNotebook();
|
|
98
|
-
|
|
99
115
|
// Set a flag that a user has manually run at least one cell.
|
|
100
116
|
setHasRunAnyCell(true);
|
|
101
117
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
return runCellsMemoized;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export async function runCells({
|
|
109
|
-
cellIds,
|
|
110
|
-
sendRun,
|
|
111
|
-
prepareForRun,
|
|
112
|
-
notebook,
|
|
113
|
-
}: {
|
|
114
|
-
cellIds: CellId[];
|
|
115
|
-
sendRun: (request: ExecuteCellsRequest) => Promise<null>;
|
|
116
|
-
prepareForRun: (action: { cellId: CellId }) => void;
|
|
117
|
-
notebook: NotebookState;
|
|
118
|
-
}) {
|
|
119
|
-
if (cellIds.length === 0) {
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const { cellHandles, cellData } = notebook;
|
|
124
|
-
|
|
125
|
-
const codes: string[] = [];
|
|
126
|
-
for (const cellId of cellIds) {
|
|
127
|
-
const ref = cellHandles[cellId];
|
|
128
|
-
const ev = ref?.current?.editorView;
|
|
129
|
-
let code: string;
|
|
130
|
-
// Performs side-effects that must run whenever the cell is run, but doesn't
|
|
131
|
-
// actually run the cell.
|
|
132
|
-
if (ev) {
|
|
118
|
+
const { cellHandles } = getNotebook();
|
|
119
|
+
for (const cellId of cellIds) {
|
|
120
|
+
const ev = cellHandles[cellId]?.current?.editorView;
|
|
133
121
|
// Skip close on markdown, since we autorun, otherwise we'll close the
|
|
134
122
|
// completion each time.
|
|
135
|
-
if (getCurrentLanguageAdapter(ev) !== "markdown") {
|
|
123
|
+
if (ev && getCurrentLanguageAdapter(ev) !== "markdown") {
|
|
136
124
|
closeCompletion(ev);
|
|
137
125
|
}
|
|
138
|
-
|
|
139
|
-
code = getEditorCodeAsPython(ev);
|
|
140
|
-
} else {
|
|
141
|
-
code = cellData[cellId]?.code || "";
|
|
126
|
+
prepareForRun({ cellId });
|
|
142
127
|
}
|
|
143
|
-
|
|
144
|
-
codes.push(code);
|
|
145
|
-
prepareForRun({ cellId });
|
|
146
|
-
}
|
|
147
|
-
// The kernel sets a disabled cell aside without planning an execution,
|
|
148
|
-
// so a submission for it would never be superseded.
|
|
149
|
-
submitCells(
|
|
150
|
-
store,
|
|
151
|
-
cellIds.filter((cellId) => !cellData[cellId]?.config.disabled),
|
|
152
|
-
);
|
|
153
|
-
|
|
154
|
-
// TODO(MO-8346): Submit through the host's `runtime.run`.
|
|
155
|
-
await sendRun({ cellIds: cellIds, codes: codes }).catch((error) => {
|
|
156
|
-
withdrawCells(store, cellIds);
|
|
157
|
-
Logger.error(error);
|
|
128
|
+
return runCells(store, cellIds);
|
|
158
129
|
});
|
|
130
|
+
|
|
131
|
+
return runCellsMemoized;
|
|
159
132
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { closeCompletion, completionStatus } from "@codemirror/autocomplete";
|
|
3
3
|
import type { EditorView } from "@codemirror/view";
|
|
4
4
|
import clsx from "clsx";
|
|
5
|
-
import { useAtomValue, useSetAtom } from "jotai";
|
|
5
|
+
import { useAtomValue, useSetAtom, useStore } from "jotai";
|
|
6
6
|
import {
|
|
7
7
|
HelpCircleIcon,
|
|
8
8
|
MoreHorizontalIcon,
|
|
@@ -40,14 +40,16 @@ import { canCollapseOutline } from "@/core/dom/outline";
|
|
|
40
40
|
import { isErrorMime } from "@/core/mime";
|
|
41
41
|
import type { AppMode } from "@/core/mode";
|
|
42
42
|
import { connectionAtom } from "@/core/network/connection";
|
|
43
|
-
import { useRequestClient } from "@/core/network/requests";
|
|
44
43
|
import {
|
|
45
44
|
type CellExecutionView,
|
|
46
45
|
type CellFreshness,
|
|
47
46
|
type CellRunStatus,
|
|
47
|
+
replyInput,
|
|
48
48
|
useCellExecution,
|
|
49
49
|
useCellFreshness,
|
|
50
50
|
} from "@/core/executions";
|
|
51
|
+
import type { WithResponse } from "@/core/cells/types";
|
|
52
|
+
import type { OutputMessage } from "@/core/kernel/messages";
|
|
51
53
|
import type { CellConfig } from "@/core/network/types";
|
|
52
54
|
import { useResizeObserver } from "@/hooks/useResizeObserver";
|
|
53
55
|
import { cn } from "@/utils/cn";
|
|
@@ -241,6 +243,30 @@ export interface CellHandle {
|
|
|
241
243
|
editorViewOrNull: EditorView | null;
|
|
242
244
|
}
|
|
243
245
|
|
|
246
|
+
/**
|
|
247
|
+
* Answer the prompt at `index` of the cell's console through the host.
|
|
248
|
+
*
|
|
249
|
+
* The console entry is marked answered right away, as before. The reply
|
|
250
|
+
* itself names the prompt, so the host can mark it answered and turn away
|
|
251
|
+
* a late reply meant for an earlier prompt.
|
|
252
|
+
*/
|
|
253
|
+
function useAnswerPrompt(
|
|
254
|
+
cellId: CellId,
|
|
255
|
+
consoleOutputs: readonly WithResponse<OutputMessage>[],
|
|
256
|
+
) {
|
|
257
|
+
const store = useStore();
|
|
258
|
+
const actions = useCellActions();
|
|
259
|
+
return useEvent((text: string, index: number) => {
|
|
260
|
+
actions.setStdinResponse({ cellId, response: text, outputIndex: index });
|
|
261
|
+
const prompt = consoleOutputs[index];
|
|
262
|
+
if (prompt?.input_id === undefined) {
|
|
263
|
+
Logger.error("The prompt has no id to answer it by", prompt);
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
void replyInput(store, prompt.input_id, text);
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
|
|
244
270
|
export interface CellProps {
|
|
245
271
|
cellId: CellId;
|
|
246
272
|
theme: Theme;
|
|
@@ -404,7 +430,7 @@ const EditableCellComponent = ({
|
|
|
404
430
|
const setAiCompletionCell = useSetAtom(aiCompletionCellAtom);
|
|
405
431
|
const deleteCell = useDeleteCellCallback();
|
|
406
432
|
const runCell = useRunCell(cellId);
|
|
407
|
-
const
|
|
433
|
+
const answerPrompt = useAnswerPrompt(cellId, cellRuntime.consoleOutputs);
|
|
408
434
|
const isPendingCut = useIsPendingCut(cellId);
|
|
409
435
|
|
|
410
436
|
const [languageAdapter, setLanguageAdapter] = useState<LanguageAdapterType>();
|
|
@@ -823,14 +849,7 @@ const EditableCellComponent = ({
|
|
|
823
849
|
// breakpoints; no-op when none are set.
|
|
824
850
|
clearCellBreakpoints(cellId);
|
|
825
851
|
}}
|
|
826
|
-
onSubmitDebugger={
|
|
827
|
-
actions.setStdinResponse({
|
|
828
|
-
cellId,
|
|
829
|
-
response: text,
|
|
830
|
-
outputIndex: index,
|
|
831
|
-
});
|
|
832
|
-
sendStdin({ text });
|
|
833
|
-
}}
|
|
852
|
+
onSubmitDebugger={answerPrompt}
|
|
834
853
|
cellId={cellId}
|
|
835
854
|
debuggerActive={cellRuntime.debuggerActive}
|
|
836
855
|
/>
|
|
@@ -1060,10 +1079,10 @@ const SetupCellComponent = ({
|
|
|
1060
1079
|
const connection = useAtomValue(connectionAtom);
|
|
1061
1080
|
|
|
1062
1081
|
const actions = useCellActions();
|
|
1063
|
-
const requestClient = useRequestClient();
|
|
1064
1082
|
const deleteCell = useDeleteCellCallback();
|
|
1065
1083
|
const setAiCompletionCell = useSetAtom(aiCompletionCellAtom);
|
|
1066
1084
|
const runCell = useRunCell(cellId);
|
|
1085
|
+
const answerPrompt = useAnswerPrompt(cellId, cellRuntime.consoleOutputs);
|
|
1067
1086
|
|
|
1068
1087
|
const uninstantiated = isUninstantiated(execution);
|
|
1069
1088
|
|
|
@@ -1293,14 +1312,7 @@ const SetupCellComponent = ({
|
|
|
1293
1312
|
// breakpoints; no-op when none are set.
|
|
1294
1313
|
clearCellBreakpoints(cellId);
|
|
1295
1314
|
}}
|
|
1296
|
-
onSubmitDebugger={
|
|
1297
|
-
actions.setStdinResponse({
|
|
1298
|
-
cellId,
|
|
1299
|
-
response: text,
|
|
1300
|
-
outputIndex: index,
|
|
1301
|
-
});
|
|
1302
|
-
requestClient.sendStdin({ text });
|
|
1303
|
-
}}
|
|
1315
|
+
onSubmitDebugger={answerPrompt}
|
|
1304
1316
|
cellId={cellId}
|
|
1305
1317
|
debuggerActive={cellRuntime.debuggerActive}
|
|
1306
1318
|
/>
|