@marimo-team/islands 0.25.3-dev273 → 0.25.3-dev279
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-eLHPh6uu.js → ConnectedDataExplorerComponent-CErm132k.js} +25 -26
- package/dist/{ErrorBoundary-DBh-wUT9.js → ErrorBoundary-DmTMuDPR.js} +43 -44
- package/dist/{abnfDiagram-VCTEODGH-hqd0IBtX.js → abnfDiagram-VCTEODGH-d-Qn2fwb.js} +2 -2
- package/dist/{any-language-editor-DnG26QXh.js → any-language-editor-6k6F12Z3.js} +3 -3
- package/dist/{architectureDiagram-5GKGNRK7-BzLAFcc0.js → architectureDiagram-5GKGNRK7-DxragHYv.js} +1 -1
- package/dist/{assertNever-DIU3JJbt.js → assertNever-B-YzWzbL.js} +14 -14
- package/dist/{button-0-RqY1DW.js → button-DZuNnIZO.js} +220 -223
- package/dist/{capabilities-B6yy_CBN.js → capabilities-D9FskwsK.js} +1 -1
- package/dist/{chat-ui-BWIkve2R.js → chat-ui-v1Fdh0NA.js} +332 -333
- package/dist/{check-gUSZbRsl.js → check-XFzZeH-y.js} +1 -1
- package/dist/{chunk-IMKFNOWR-DOeVvuyE.js → chunk-IMKFNOWR-C0x0feFg.js} +1 -1
- package/dist/{chunk-SHT3W25Y-DZoFI8wR.js → chunk-SHT3W25Y-D0ZdASqB.js} +1 -1
- package/dist/{chunk-SVP7TREG-CWu86PSW.js → chunk-SVP7TREG-BHjL96zx.js} +1 -1
- package/dist/{chunk-TICWLB2K-BrM0GoP3.js → chunk-TICWLB2K-CzcrwGfp.js} +1 -1
- package/dist/{classDiagram-ZZMXUADV-DYi4V2bB.js → classDiagram-ZZMXUADV-BTqFAZTP.js} +2 -2
- package/dist/{classDiagram-v2-VYDZK3BY-tN7FIk-m.js → classDiagram-v2-VYDZK3BY-EPXb0Elt.js} +2 -2
- package/dist/{code-visibility-M_dXDGp6.js → code-visibility-Dw4-TXN0.js} +20 -20
- package/dist/{common-DV0fvw_I.js → common-BXMFi4lr.js} +5399 -5397
- package/dist/{config-BsPruWMR.js → config-C8MFHQ2E.js} +23 -22
- package/dist/{copy-icon-C2I8sw4h.js → copy-icon-CtllOdmg.js} +3 -3
- package/dist/{createReducer-DdtT_7ji.js → createReducer-DRqwmaTc.js} +2 -2
- package/dist/{cynefinDiagram-5FMLGOSQ-D5nCnDc-.js → cynefinDiagram-5FMLGOSQ-CBSD344q.js} +1 -1
- package/dist/{dagre-GXQ25YYZ-BHkd7f_i.js → dagre-GXQ25YYZ-JItjh3Ge.js} +1 -1
- package/dist/{data-grid-overlay-editor-Cd1zGKBt.js → data-grid-overlay-editor-hZzwX7Ia.js} +1 -1
- package/dist/{diagram-S7CK7UJ4-D1ALppT2.js → diagram-S7CK7UJ4-CTD9xTVb.js} +1 -1
- package/dist/{diagram-UQ7AKVKN-DLv0b83L.js → diagram-UQ7AKVKN-DTfnFai8.js} +1 -1
- package/dist/{diagram-VX7I27RA-7XJ7J6qg.js → diagram-VX7I27RA-BcSnucV-.js} +1 -1
- package/dist/{diagram-Z3DM3KII-7M7zbHp_.js → diagram-Z3DM3KII-C2ATIE8A.js} +1 -1
- package/dist/{dialog-DzCj1qQv.js → dialog-CaYm5imE.js} +33 -33
- package/dist/{dropdown-menu-CdXE15an.js → dropdown-menu-Bb0bFOIk.js} +149 -149
- package/dist/{ebnfDiagram-PWID7BFC-DHH1qlmV.js → ebnfDiagram-PWID7BFC-B7B9JPxc.js} +2 -2
- package/dist/{erDiagram-RLTQ6QDP-fI_0dGef.js → erDiagram-RLTQ6QDP-v6hDno1O.js} +1 -1
- package/dist/{error-banner-DPc9eIw5.js → error-banner-CHCdHVFB.js} +44 -45
- package/dist/{error-entries-CxN-Nq3V.js → error-entries-C1S-Rmyt.js} +563 -554
- package/dist/{feedback-button-CE2gPi4-.js → feedback-button-6Ui2TDLR.js} +12 -12
- package/dist/{flowDiagram-HODETNUW-Brg9MA6i.js → flowDiagram-HODETNUW-aIFlAtOy.js} +2 -2
- package/dist/{formats-DbSvAPY-.js → formats-PzBQMUnr.js} +3 -3
- package/dist/{glide-data-editor-BycATbgF.js → glide-data-editor-PZNlrSDI.js} +14 -16
- package/dist/{glide-utils-BSw3SSHF.js → glide-utils-BSQr4d90.js} +51 -59
- package/dist/{globals-CW6OvnI5.js → globals-DX6FPZYq.js} +3 -3
- package/dist/{html-to-image-Bc2AvXHG.js → html-to-image-CqGUd58T.js} +14529 -14531
- package/dist/{infoDiagram-27XIBGKW-E9e403YT.js → infoDiagram-27XIBGKW-D6YHLeCP.js} +1 -1
- package/dist/{ishikawaDiagram-5VMMS53U-C-RlYDRo.js → ishikawaDiagram-5VMMS53U-D1ZvprOX.js} +1 -1
- package/dist/{kanban-definition-UXKFOSKX-CLFl_uqK.js → kanban-definition-UXKFOSKX-Bna_3BMr.js} +1 -1
- package/dist/{loader-BVEtQ4F3.js → loader-DZvtzQoD.js} +1 -1
- package/dist/main.js +7357 -7361
- package/dist/{mermaid-rd09e1JL.js → mermaid-A36unKXJ.js} +50 -50
- package/dist/{mindmap-definition-YA3MSWOX-BQ8TWtyB.js → mindmap-definition-YA3MSWOX-CyVCs__G.js} +1 -1
- package/dist/{number-overlay-editor-B_rR-LdM.js → number-overlay-editor-D0CsL3_R.js} +1 -1
- package/dist/{pegDiagram-XKGWAZYB-CN8UHFPi.js → pegDiagram-XKGWAZYB-9hNBD2wZ.js} +2 -2
- package/dist/{pencil-DcT8AE0h.js → pencil-DYUlSKxZ.js} +1 -1
- package/dist/{pieDiagram-E7YTZNPT-DGuJ0EBd.js → pieDiagram-E7YTZNPT-8tr-NYo7.js} +1 -1
- package/dist/{process-output-qecD7Dk8.js → process-output-DskX9jLa.js} +4 -4
- package/dist/{railroadDiagram-O6MQD6OU-BPDA8wy8.js → railroadDiagram-O6MQD6OU-Dzg1dyyp.js} +2 -2
- package/dist/{requirementDiagram-BXWQKSXE-ChzLGbGW.js → requirementDiagram-BXWQKSXE-B7sNqwGG.js} +1 -1
- package/dist/{reveal-component-CngVwOSy.js → reveal-component-D7xeI1QR.js} +1003 -1003
- package/dist/{select-OxgozOC8.js → select-DsoP1qe8.js} +290 -290
- package/dist/{sequenceDiagram-WJ2MYXX4-DFX-bE6O.js → sequenceDiagram-WJ2MYXX4-oP9shbaG.js} +1 -1
- package/dist/{spec-oJn0RhpQ.js → spec-BZMDxAu7.js} +4 -4
- package/dist/{stateDiagram-D77RDMKH-BR7cI3wK.js → stateDiagram-D77RDMKH-CjCv4ogv.js} +2 -2
- package/dist/{stateDiagram-v2-MP3YSRHH-oGqB5UDI.js → stateDiagram-v2-MP3YSRHH-BW9X162a.js} +2 -2
- package/dist/{static-state-CuV6Pc3a.js → static-state-D-Q-Ajaa.js} +8 -8
- package/dist/style.css +1 -1
- package/dist/{swimlanes-42K2YHIH-DwSZl1yC.js → swimlanes-42K2YHIH-Ckfs9Wrn.js} +1 -1
- package/dist/{swimlanesDiagram-VR7AAH4N-BHlcd-Ri.js → swimlanesDiagram-VR7AAH4N-DpTNG9YP.js} +2 -2
- package/dist/{swiper-component-Dvj-goaD.js → swiper-component-DQbK_ZWm.js} +7 -7
- package/dist/{timeline-definition-24CTP7MA-DMmP-G2O.js → timeline-definition-24CTP7MA-BXaBlV6U.js} +1 -1
- package/dist/{toast-DbBHmxmB.js → toast-BhjIy3Tq.js} +158 -158
- package/dist/{tooltip-BfcDQd2j.js → tooltip-DMMiOn2f.js} +64 -64
- package/dist/use-restore-focus-CosX7-gx.js +7568 -0
- package/dist/{useAsyncData-BYIAoMqn.js → useAsyncData-Ci4AvJff.js} +4 -4
- package/dist/{useDateFormatter-ButWkdcK.js → useDateFormatter-CPlava52.js} +2 -2
- package/dist/{useDeepCompareMemoize-Bz0c5GN6.js → useDeepCompareMemoize-NaaWoFlX.js} +1 -1
- package/dist/useEvent-BRH-aCqi.js +19 -0
- package/dist/{useIframeCapabilities-BIolGsF1.js → useIframeCapabilities-BXhDDoCG.js} +1 -1
- package/dist/{useLifecycle-JjxCC7Vu.js → useLifecycle-SniivYmC.js} +2 -2
- package/dist/{vega-component-rxIxAAh9.js → vega-component-DIxaDQNs.js} +40 -40
- package/dist/{vennDiagram-4TSXK5OY-Db8GtH1G.js → vennDiagram-4TSXK5OY-C-hQ4iNJ.js} +1 -1
- package/dist/{wardleyDiagram-VM6X3IG4-DmIiC3FU.js → wardleyDiagram-VM6X3IG4-C2MQ_Umv.js} +1 -1
- package/dist/{xychartDiagram-S5SC5T6Z-DS9OPrNq.js → xychartDiagram-S5SC5T6Z-BtTTBn7Q.js} +1 -1
- package/package.json +1 -1
- package/src/__mocks__/executions.ts +4 -2
- package/src/__mocks__/notebook.ts +0 -2
- package/src/__tests__/CellStatus.test.tsx +6 -6
- package/src/components/editor/__tests__/data-attributes.test.tsx +0 -2
- package/src/components/editor/__tests__/output-persistence.test.tsx +20 -5
- package/src/components/editor/cell/CellStatus.tsx +6 -5
- package/src/components/editor/cell/__tests__/useRunCells.test.tsx +169 -0
- package/src/components/editor/cell/collapse.tsx +3 -4
- package/src/components/editor/cell/useRunCells.ts +32 -3
- package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +2 -3
- package/src/components/editor/notebook-cell.tsx +23 -37
- package/src/components/editor/renderers/grid-layout/grid-layout.tsx +4 -10
- package/src/components/editor/renderers/slides-layout/__tests__/plugin.test.ts +0 -2
- package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +8 -5
- package/src/components/slides/minimap.tsx +3 -2
- package/src/components/slides/reveal-component.tsx +3 -2
- package/src/components/slides/slide-cell-view.tsx +8 -8
- package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +0 -101
- package/src/core/cells/__tests__/actions.test.ts +0 -1
- package/src/core/cells/__tests__/cell.test.ts +81 -17
- package/src/core/cells/__tests__/cells.test.ts +0 -100
- package/src/core/cells/__tests__/document.test.ts +2 -8
- package/src/core/cells/__tests__/utils.test.ts +93 -24
- package/src/core/cells/actions.ts +0 -1
- package/src/core/cells/cell.ts +37 -14
- package/src/core/cells/cells.ts +10 -99
- package/src/core/cells/document.ts +2 -13
- package/src/core/cells/types.ts +0 -23
- package/src/core/cells/utils.ts +44 -17
- package/src/core/codemirror/cells/extensions.ts +0 -14
- package/src/core/errors/__tests__/error-entries.test.ts +40 -1
- package/src/core/errors/error-entries.ts +2 -3
- package/src/core/executions/__tests__/freshness.test.ts +420 -0
- package/src/core/executions/__tests__/hooks.test.tsx +106 -0
- package/src/core/executions/atoms.ts +22 -0
- package/src/core/executions/freshness.ts +164 -0
- package/src/core/executions/hooks.ts +17 -1
- package/src/core/executions/index.ts +3 -1
- package/src/core/islands/bootstrap.ts +0 -1
- package/src/core/islands/components/__tests__/web-components.test.tsx +63 -0
- package/src/core/islands/components/web-components.tsx +11 -0
- package/src/core/kernel/handlers.ts +0 -4
- package/src/core/session/useNotebookSession.tsx +2 -7
- package/src/css/globals.css +6 -2
- package/src/plugins/impl/DataEditorPlugin.tsx +0 -4
- package/src/plugins/impl/data-editor/__tests__/column-types.test.ts +5 -0
- package/src/plugins/impl/data-editor/__tests__/glide-data-editor.test.tsx +9 -5
- package/src/plugins/impl/data-editor/column-types.ts +3 -0
- package/src/plugins/impl/data-editor/glide-data-editor.tsx +6 -5
- package/src/plugins/impl/data-editor/glide-utils.ts +10 -32
- package/src/plugins/impl/data-editor/types.ts +0 -2
- package/src/stories/cell-status.stories.tsx +12 -14
- package/src/stories/cell.stories.tsx +44 -10
- package/src/stories/layout/vertical/one-column.stories.tsx +0 -10
- package/src/theme/__tests__/islands-theme.test.tsx +276 -0
- package/src/theme/namespace.tsx +4 -3
- package/src/theme/useTheme.ts +71 -41
- package/dist/schemas-CdscWNy_.js +0 -4747
- package/dist/use-restore-focus-CmflVLWJ.js +0 -1672
- package/dist/useTheme-DPLobSFs.js +0 -339
- package/dist/utils-EBklsC5R.js +0 -813
- package/src/components/editor/cell/__tests__/useRunCells.test.ts +0 -45
|
@@ -173,7 +173,6 @@ describe("cell reducer", () => {
|
|
|
173
173
|
...config,
|
|
174
174
|
...(hideCode != null && { hide_code: hideCode }),
|
|
175
175
|
}),
|
|
176
|
-
edited: Boolean(code),
|
|
177
176
|
}),
|
|
178
177
|
},
|
|
179
178
|
cellRuntime: { ...state.cellRuntime, [id]: createCellRuntimeState() },
|
|
@@ -206,16 +205,6 @@ describe("cell reducer", () => {
|
|
|
206
205
|
});
|
|
207
206
|
}
|
|
208
207
|
|
|
209
|
-
/** Format a cell's code, as the editor reports it. */
|
|
210
|
-
function formatCode(cellId: CellId, code: string) {
|
|
211
|
-
actions.codeFormatted({
|
|
212
|
-
cellId,
|
|
213
|
-
before: state.cellData[cellId].code,
|
|
214
|
-
after: code,
|
|
215
|
-
});
|
|
216
|
-
editCode(cellId, code);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
208
|
let i = 0;
|
|
220
209
|
const originalCreate = CellId.create.bind(CellId);
|
|
221
210
|
|
|
@@ -288,7 +277,6 @@ describe("cell reducer", () => {
|
|
|
288
277
|
timestamp: 2,
|
|
289
278
|
});
|
|
290
279
|
const runtime = state.cellRuntime[firstCellId];
|
|
291
|
-
expect(state.cellData[firstCellId].lastCodeRun).toBe("print('new code')");
|
|
292
280
|
expect(runtime.output).toEqual(output);
|
|
293
281
|
expect(runtime.consoleOutputs[0].code).toBe(code);
|
|
294
282
|
expect(runtime.consoleOutputs[0].data).toContain("old failure");
|
|
@@ -299,15 +287,11 @@ describe("cell reducer", () => {
|
|
|
299
287
|
/////////////////
|
|
300
288
|
// Initial state
|
|
301
289
|
let cell = cells[0];
|
|
302
|
-
expect(cell.lastCodeRun).toBe(null);
|
|
303
|
-
expect(cell.edited).toBe(false);
|
|
304
290
|
expect(cell).toMatchSnapshot("initial state");
|
|
305
291
|
|
|
306
292
|
// Update code
|
|
307
293
|
editCode(firstCellId, "import marimo as mo");
|
|
308
294
|
cell = cells[0];
|
|
309
|
-
expect(cell.lastCodeRun).toBe(null);
|
|
310
|
-
expect(cell.edited).toBe(true);
|
|
311
295
|
expect(cell).toMatchSnapshot("code edited");
|
|
312
296
|
|
|
313
297
|
// Prepare for run
|
|
@@ -315,8 +299,6 @@ describe("cell reducer", () => {
|
|
|
315
299
|
cellId: firstCellId,
|
|
316
300
|
});
|
|
317
301
|
cell = cells[0];
|
|
318
|
-
expect(cell.lastCodeRun).toBe("import marimo as mo");
|
|
319
|
-
expect(cell.edited).toBe(false);
|
|
320
302
|
expect(cell).toMatchSnapshot("prepared for run");
|
|
321
303
|
|
|
322
304
|
// Receive queued messages
|
|
@@ -329,8 +311,6 @@ describe("cell reducer", () => {
|
|
|
329
311
|
timestamp: new Date(10).getTime() as Seconds,
|
|
330
312
|
});
|
|
331
313
|
cell = cells[0];
|
|
332
|
-
expect(cell.lastCodeRun).toBe("import marimo as mo");
|
|
333
|
-
expect(cell.edited).toBe(false);
|
|
334
314
|
expect(cell).toMatchSnapshot("queued message received");
|
|
335
315
|
|
|
336
316
|
// Receive running messages
|
|
@@ -343,8 +323,6 @@ describe("cell reducer", () => {
|
|
|
343
323
|
timestamp: new Date(20).getTime() as Seconds,
|
|
344
324
|
});
|
|
345
325
|
cell = cells[0];
|
|
346
|
-
expect(cell.lastCodeRun).toBe("import marimo as mo");
|
|
347
|
-
expect(cell.edited).toBe(false);
|
|
348
326
|
expect(cell).toMatchSnapshot("running message received");
|
|
349
327
|
|
|
350
328
|
// Console messages shouldn't transition status
|
|
@@ -362,7 +340,6 @@ describe("cell reducer", () => {
|
|
|
362
340
|
timestamp: new Date(22).getTime() as Seconds,
|
|
363
341
|
});
|
|
364
342
|
cell = cells[0];
|
|
365
|
-
expect(cell.edited).toBe(false);
|
|
366
343
|
expect(cell).toMatchSnapshot("console message received");
|
|
367
344
|
|
|
368
345
|
// Receive output messages
|
|
@@ -385,7 +362,6 @@ describe("cell reducer", () => {
|
|
|
385
362
|
timestamp: new Date(33).getTime() as Seconds,
|
|
386
363
|
});
|
|
387
364
|
cell = cells[0];
|
|
388
|
-
expect(cell.edited).toBe(false);
|
|
389
365
|
expect(cell).toMatchSnapshot("output received");
|
|
390
366
|
|
|
391
367
|
// EDITING BACK AND FORTH
|
|
@@ -393,20 +369,16 @@ describe("cell reducer", () => {
|
|
|
393
369
|
// Update code again
|
|
394
370
|
editCode(firstCellId, "import marimo as mo\nimport numpy");
|
|
395
371
|
cell = cells[0];
|
|
396
|
-
expect(cell.edited).toBe(true);
|
|
397
372
|
expect(cell).toMatchSnapshot("code edited again");
|
|
398
373
|
|
|
399
374
|
// Update code should be unedited
|
|
400
375
|
editCode(firstCellId, "import marimo as mo");
|
|
401
376
|
cell = cells[0];
|
|
402
|
-
expect(cell.edited).toBe(false);
|
|
403
377
|
expect(cell).toMatchSnapshot("code reverted");
|
|
404
378
|
|
|
405
379
|
// Update code should be edited again
|
|
406
380
|
editCode(firstCellId, "import marimo as mo\nimport numpy");
|
|
407
381
|
cell = cells[0];
|
|
408
|
-
expect(cell.lastCodeRun).toBe("import marimo as mo");
|
|
409
|
-
expect(cell.edited).toBe(true);
|
|
410
382
|
expect(cell).toMatchSnapshot("code changed after revert");
|
|
411
383
|
|
|
412
384
|
// ERROR RESPONSE
|
|
@@ -457,8 +429,6 @@ describe("cell reducer", () => {
|
|
|
457
429
|
});
|
|
458
430
|
cell = cells[0];
|
|
459
431
|
expect(cell.output).not.toBe(null); // keep old output
|
|
460
|
-
expect(cell.lastCodeRun).toBe("import marimo as mo\nimport numpy");
|
|
461
|
-
expect(cell.edited).toBe(false);
|
|
462
432
|
expect(cell).toMatchSnapshot("error response received");
|
|
463
433
|
|
|
464
434
|
// INTERRUPT RESPONSE
|
|
@@ -506,8 +476,6 @@ describe("cell reducer", () => {
|
|
|
506
476
|
timestamp: new Date(61).getTime() as Seconds,
|
|
507
477
|
});
|
|
508
478
|
cell = cells[0];
|
|
509
|
-
expect(cell.lastCodeRun).toBe(cell.code);
|
|
510
|
-
expect(cell.edited).toBe(false);
|
|
511
479
|
expect(cell).toMatchSnapshot("interruption response received");
|
|
512
480
|
});
|
|
513
481
|
|
|
@@ -538,8 +506,6 @@ describe("cell reducer", () => {
|
|
|
538
506
|
timestamp: new Date(10).getTime() as Seconds,
|
|
539
507
|
});
|
|
540
508
|
let cell = cells[1];
|
|
541
|
-
expect(cell.lastCodeRun).toBe("mo.slider()");
|
|
542
|
-
expect(cell.edited).toBe(false);
|
|
543
509
|
expect(cell).toMatchSnapshot("queued message received");
|
|
544
510
|
|
|
545
511
|
// Receive stale message
|
|
@@ -552,53 +518,9 @@ describe("cell reducer", () => {
|
|
|
552
518
|
timestamp: new Date(20).getTime() as Seconds,
|
|
553
519
|
});
|
|
554
520
|
cell = cells[1];
|
|
555
|
-
expect(cell.lastCodeRun).toBe("mo.slider()");
|
|
556
|
-
expect(cell.edited).toBe(false);
|
|
557
521
|
expect(cell).toMatchSnapshot("stale inputs received");
|
|
558
522
|
});
|
|
559
523
|
|
|
560
|
-
it("can format code and update cell", () => {
|
|
561
|
-
const firstCellId = cells[0].id;
|
|
562
|
-
editCode(firstCellId, "import marimo as mo");
|
|
563
|
-
let cell = cells[0];
|
|
564
|
-
expect(cell.lastCodeRun).toBe(null);
|
|
565
|
-
expect(cell.edited).toBe(true);
|
|
566
|
-
|
|
567
|
-
// Run
|
|
568
|
-
actions.prepareForRun({
|
|
569
|
-
cellId: firstCellId,
|
|
570
|
-
});
|
|
571
|
-
actions.handleCellMessage({
|
|
572
|
-
cell_id: firstCellId,
|
|
573
|
-
output: {
|
|
574
|
-
channel: "output",
|
|
575
|
-
mimetype: "text/plain",
|
|
576
|
-
data: "ok",
|
|
577
|
-
timestamp: 0,
|
|
578
|
-
},
|
|
579
|
-
console: {
|
|
580
|
-
channel: "stdout",
|
|
581
|
-
mimetype: "text/plain",
|
|
582
|
-
data: "hello!",
|
|
583
|
-
timestamp: 0,
|
|
584
|
-
},
|
|
585
|
-
status: "idle",
|
|
586
|
-
stale_inputs: null,
|
|
587
|
-
timestamp: new Date(33).getTime() as Seconds,
|
|
588
|
-
});
|
|
589
|
-
|
|
590
|
-
// Check steady state
|
|
591
|
-
cell = cells[0];
|
|
592
|
-
expect(cell.edited).toBe(false);
|
|
593
|
-
expect(cell.lastCodeRun).toBe("import marimo as mo");
|
|
594
|
-
|
|
595
|
-
// Format code
|
|
596
|
-
formatCode(firstCellId, "import marimo as mo");
|
|
597
|
-
cell = cells[0];
|
|
598
|
-
expect(cell.lastCodeRun).toBe("import marimo as mo");
|
|
599
|
-
expect(cell.edited).toBe(false);
|
|
600
|
-
});
|
|
601
|
-
|
|
602
524
|
it("can run a stopped cell", () => {
|
|
603
525
|
// Update code of first cell
|
|
604
526
|
editCode(firstCellId, "mo.md('This has an ancestor that was stopped')");
|
|
@@ -618,10 +540,6 @@ describe("cell reducer", () => {
|
|
|
618
540
|
timestamp: new Date(10).getTime() as Seconds,
|
|
619
541
|
});
|
|
620
542
|
let cell = cells[0];
|
|
621
|
-
expect(cell.lastCodeRun).toBe(
|
|
622
|
-
"mo.md('This has an ancestor that was stopped')",
|
|
623
|
-
);
|
|
624
|
-
expect(cell.edited).toBe(false);
|
|
625
543
|
expect(cell).toMatchSnapshot("queued message received");
|
|
626
544
|
|
|
627
545
|
// Receive idle message
|
|
@@ -1494,24 +1412,6 @@ describe("kernel-ready", () => {
|
|
|
1494
1412
|
},
|
|
1495
1413
|
});
|
|
1496
1414
|
|
|
1497
|
-
it("setLastRun records what the kernel last ran", () => {
|
|
1498
|
-
const state = hydratedState();
|
|
1499
|
-
const next = exportedForTesting.reducer(state, {
|
|
1500
|
-
type: "setLastRun",
|
|
1501
|
-
payload: { lastCodeRun: { [CELL_A]: "0" } },
|
|
1502
|
-
});
|
|
1503
|
-
|
|
1504
|
-
expect(next.cellData[CELL_A]).toMatchObject({
|
|
1505
|
-
lastCodeRun: "0",
|
|
1506
|
-
edited: true,
|
|
1507
|
-
});
|
|
1508
|
-
expect(next.cellData[CELL_B]).toMatchObject({
|
|
1509
|
-
lastCodeRun: null,
|
|
1510
|
-
edited: false,
|
|
1511
|
-
});
|
|
1512
|
-
expect(next.cellRuntime).toBe(state.cellRuntime);
|
|
1513
|
-
});
|
|
1514
|
-
|
|
1515
1415
|
it("resetCellRuntimes starts every cell's runtime over", () => {
|
|
1516
1416
|
const next = exportedForTesting.reducer(hydratedState(), {
|
|
1517
1417
|
type: "resetCellRuntimes",
|
|
@@ -53,13 +53,8 @@ describe("applyDocument", () => {
|
|
|
53
53
|
const state = notebookOf([cell(a), cell(b, { code: "" })]);
|
|
54
54
|
|
|
55
55
|
expect(state.cellIds).toEqual([a, b]);
|
|
56
|
-
expect(state.cellData[a]).toMatchObject({
|
|
57
|
-
|
|
58
|
-
name: "_",
|
|
59
|
-
edited: true,
|
|
60
|
-
lastCodeRun: null,
|
|
61
|
-
});
|
|
62
|
-
expect(state.cellData[b].edited).toBe(false);
|
|
56
|
+
expect(state.cellData[a]).toMatchObject({ code: "a = 1", name: "_" });
|
|
57
|
+
expect(state.cellData[b]).toMatchObject({ code: "", name: "_" });
|
|
63
58
|
expect(state.cellRuntime[a]).toBeDefined();
|
|
64
59
|
expect(state.cellHandles[a]).toBeDefined();
|
|
65
60
|
expect(state.cellData[SCRATCH_CELL_ID]).toBeDefined();
|
|
@@ -96,7 +91,6 @@ describe("applyDocument", () => {
|
|
|
96
91
|
expect(next.cellData[a]).toMatchObject({
|
|
97
92
|
name: "renamed",
|
|
98
93
|
config: { hide_code: true },
|
|
99
|
-
edited: true,
|
|
100
94
|
});
|
|
101
95
|
});
|
|
102
96
|
|
|
@@ -5,8 +5,10 @@ import { MockExecutions } from "@/__mocks__/executions";
|
|
|
5
5
|
import { initialNotebookState, type NotebookState } from "@/core/cells/cells";
|
|
6
6
|
import { CellId } from "@/core/cells/ids";
|
|
7
7
|
import type { CellData } from "@/core/cells/types";
|
|
8
|
+
import type { CellFreshnessLookup } from "@/core/executions";
|
|
8
9
|
import type { OutputMessage } from "@/core/kernel/messages";
|
|
9
10
|
import {
|
|
11
|
+
cellNeedsRun,
|
|
10
12
|
disabledCellIds,
|
|
11
13
|
enabledCellIds,
|
|
12
14
|
isUninstantiated,
|
|
@@ -14,80 +16,147 @@ import {
|
|
|
14
16
|
staleCellIds,
|
|
15
17
|
} from "../utils";
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
): NotebookState {
|
|
19
|
+
type Cells = Record<
|
|
20
|
+
string,
|
|
21
|
+
{ edited?: boolean; disabled?: boolean; staleInputs?: boolean }
|
|
22
|
+
>;
|
|
23
|
+
|
|
24
|
+
function notebook(cells: Cells): NotebookState {
|
|
23
25
|
return {
|
|
24
26
|
cellIds: Object.keys(cells),
|
|
25
27
|
cellData: Object.fromEntries(
|
|
26
28
|
Object.entries(cells).map(([id, cell]) => [
|
|
27
29
|
id,
|
|
28
|
-
{
|
|
29
|
-
]),
|
|
30
|
-
),
|
|
31
|
-
cellRuntime: Object.fromEntries(
|
|
32
|
-
Object.entries(cells).map(([id, cell]) => [
|
|
33
|
-
id,
|
|
34
|
-
{ staleInputs: cell.staleInputs ?? false },
|
|
30
|
+
{ config: { disabled: cell.disabled } },
|
|
35
31
|
]),
|
|
36
32
|
),
|
|
33
|
+
cellRuntime: Object.fromEntries(Object.keys(cells).map((id) => [id, {}])),
|
|
37
34
|
} as any;
|
|
38
35
|
}
|
|
39
36
|
|
|
37
|
+
/** Freshness as the host would report it for `cells`. */
|
|
38
|
+
function freshness(cells: Cells): CellFreshnessLookup {
|
|
39
|
+
return (id) => {
|
|
40
|
+
const cell = cells[id];
|
|
41
|
+
return {
|
|
42
|
+
sourceChanged: cell?.edited ?? false,
|
|
43
|
+
inputsChanged: cell?.staleInputs ?? false,
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
40
48
|
describe("staleCellIds", () => {
|
|
41
49
|
it("should return cell IDs that have never run", () => {
|
|
42
|
-
const
|
|
50
|
+
const cells: Cells = { cell1: {}, cell2: {} };
|
|
51
|
+
const state = notebook(cells);
|
|
43
52
|
const executions = MockExecutions.inputs([
|
|
44
53
|
MockExecutions.completed("cell2"),
|
|
45
54
|
]);
|
|
46
55
|
|
|
47
|
-
expect(staleCellIds(state, executions)).toEqual([
|
|
56
|
+
expect(staleCellIds(state, executions, freshness(cells))).toEqual([
|
|
57
|
+
"cell1",
|
|
58
|
+
]);
|
|
48
59
|
});
|
|
49
60
|
|
|
50
61
|
it("should return cell IDs that are edited", () => {
|
|
51
|
-
const
|
|
62
|
+
const cells: Cells = { cell1: { edited: true }, cell2: {} };
|
|
63
|
+
const state = notebook(cells);
|
|
52
64
|
const executions = MockExecutions.inputs([
|
|
53
65
|
MockExecutions.completed("cell1"),
|
|
54
66
|
MockExecutions.completed("cell2"),
|
|
55
67
|
]);
|
|
56
68
|
|
|
57
|
-
expect(staleCellIds(state, executions)).toEqual([
|
|
69
|
+
expect(staleCellIds(state, executions, freshness(cells))).toEqual([
|
|
70
|
+
"cell1",
|
|
71
|
+
]);
|
|
58
72
|
});
|
|
59
73
|
|
|
60
74
|
it("should return cell IDs that are interrupted", () => {
|
|
61
|
-
const
|
|
75
|
+
const cells: Cells = { cell1: {}, cell2: {} };
|
|
76
|
+
const state = notebook(cells);
|
|
62
77
|
const executions = MockExecutions.inputs([
|
|
63
78
|
MockExecutions.interrupted("cell1"),
|
|
64
79
|
MockExecutions.completed("cell2"),
|
|
65
80
|
]);
|
|
66
81
|
|
|
67
|
-
expect(staleCellIds(state, executions)).toEqual([
|
|
82
|
+
expect(staleCellIds(state, executions, freshness(cells))).toEqual([
|
|
83
|
+
"cell1",
|
|
84
|
+
]);
|
|
68
85
|
});
|
|
69
86
|
|
|
70
87
|
it("should return cell IDs with stale inputs", () => {
|
|
71
|
-
const
|
|
88
|
+
const cells: Cells = { cell1: { staleInputs: true }, cell2: {} };
|
|
89
|
+
const state = notebook(cells);
|
|
72
90
|
const executions = MockExecutions.inputs([
|
|
73
91
|
MockExecutions.completed("cell1"),
|
|
74
92
|
MockExecutions.completed("cell2"),
|
|
75
93
|
]);
|
|
76
94
|
|
|
77
|
-
expect(staleCellIds(state, executions)).toEqual([
|
|
95
|
+
expect(staleCellIds(state, executions, freshness(cells))).toEqual([
|
|
96
|
+
"cell1",
|
|
97
|
+
]);
|
|
78
98
|
});
|
|
79
99
|
|
|
80
100
|
it("should not return cell IDs that are disabled", () => {
|
|
81
|
-
const
|
|
101
|
+
const cells: Cells = {
|
|
82
102
|
cell1: { disabled: true, staleInputs: true },
|
|
83
103
|
cell2: { staleInputs: true },
|
|
84
|
-
}
|
|
104
|
+
};
|
|
105
|
+
const state = notebook(cells);
|
|
85
106
|
const executions = MockExecutions.inputs(
|
|
86
107
|
[MockExecutions.completed("cell1"), MockExecutions.completed("cell2")],
|
|
87
108
|
{ blocked: new Set(["cell2"]) },
|
|
88
109
|
);
|
|
89
110
|
|
|
90
|
-
expect(staleCellIds(state, executions)).toEqual([]);
|
|
111
|
+
expect(staleCellIds(state, executions, freshness(cells))).toEqual([]);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe("staleCellIds with a run in progress", () => {
|
|
116
|
+
it("leaves a running cell alone unless its source changed since", () => {
|
|
117
|
+
const cells: Cells = { cell1: {}, cell2: { edited: true } };
|
|
118
|
+
const state = notebook(cells);
|
|
119
|
+
const executions = MockExecutions.inputs([
|
|
120
|
+
MockExecutions.running("cell1"),
|
|
121
|
+
MockExecutions.running("cell2"),
|
|
122
|
+
]);
|
|
123
|
+
|
|
124
|
+
expect(staleCellIds(state, executions, freshness(cells))).toEqual([
|
|
125
|
+
"cell2",
|
|
126
|
+
]);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("leaves a submitted cell alone until the host has planned its run", () => {
|
|
130
|
+
const cells: Cells = { cell1: { edited: true } };
|
|
131
|
+
const state = notebook(cells);
|
|
132
|
+
const ran = MockExecutions.completed("cell1");
|
|
133
|
+
const executions = MockExecutions.inputs([ran], {
|
|
134
|
+
submissions: new Map([["cell1", ran.id]]),
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
expect(staleCellIds(state, executions, freshness(cells))).toEqual([]);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
describe("cellNeedsRun", () => {
|
|
142
|
+
const blocked = { interrupted: false, disabled: false, blocked: true };
|
|
143
|
+
|
|
144
|
+
it("flags a blocked cell whose source changed, whatever its inputs did", () => {
|
|
145
|
+
expect(
|
|
146
|
+
cellNeedsRun({
|
|
147
|
+
...blocked,
|
|
148
|
+
freshness: freshness({ c: { edited: true, staleInputs: true } })("c"),
|
|
149
|
+
}),
|
|
150
|
+
).toBe(true);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("does not flag a blocked cell for changed inputs alone", () => {
|
|
154
|
+
expect(
|
|
155
|
+
cellNeedsRun({
|
|
156
|
+
...blocked,
|
|
157
|
+
freshness: freshness({ c: { staleInputs: true } })("c"),
|
|
158
|
+
}),
|
|
159
|
+
).toBe(false);
|
|
91
160
|
});
|
|
92
161
|
});
|
|
93
162
|
|
|
@@ -603,7 +603,6 @@ export function useCellActions() {
|
|
|
603
603
|
handleCellMessage: _handleCellMessage,
|
|
604
604
|
addLogs: _addLogs,
|
|
605
605
|
reconcileDocument: _reconcileDocument,
|
|
606
|
-
setLastRun: _setLastRun,
|
|
607
606
|
resetCellRuntimes: _resetCellRuntimes,
|
|
608
607
|
...operations
|
|
609
608
|
} = useReducerActions();
|
package/src/core/cells/cell.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
CellExecutionView,
|
|
4
|
+
CellFreshness,
|
|
5
|
+
CellRunStatus,
|
|
6
|
+
} from "@/core/executions";
|
|
3
7
|
import { extractAllTracebackInfo, type TracebackInfo } from "@/utils/traceback";
|
|
4
8
|
import { parseOutline } from "../dom/outline";
|
|
5
9
|
import type { CellMessage, OutputMessage } from "../kernel/messages";
|
|
@@ -41,7 +45,6 @@ export function transitionCell(
|
|
|
41
45
|
}
|
|
42
46
|
|
|
43
47
|
nextCell.output = message.output ?? nextCell.output;
|
|
44
|
-
nextCell.staleInputs = message.stale_inputs ?? nextCell.staleInputs;
|
|
45
48
|
// Tri-state partial update: an omitted field (undefined) leaves the hint
|
|
46
49
|
// unchanged; null explicitly clears it; a string sets it.
|
|
47
50
|
if (message.serialization !== undefined) {
|
|
@@ -115,31 +118,51 @@ export function outputIsLoading(status: CellRunStatus): boolean {
|
|
|
115
118
|
}
|
|
116
119
|
|
|
117
120
|
/**
|
|
118
|
-
* A cell's output is stale if it
|
|
121
|
+
* A cell's output is stale if it is loading, an input it read has changed,
|
|
122
|
+
* or, while `editing`, its source has changed. A read view keeps showing
|
|
123
|
+
* the last result of edited source.
|
|
119
124
|
*/
|
|
120
125
|
export function outputIsStale(
|
|
121
126
|
execution: Pick<CellExecutionView, "status" | "outcome" | "rendered">,
|
|
122
|
-
|
|
123
|
-
|
|
127
|
+
freshness: CellFreshness,
|
|
128
|
+
editing: boolean,
|
|
124
129
|
): boolean {
|
|
125
130
|
const { status, outcome, rendered } = execution;
|
|
126
131
|
|
|
127
|
-
//
|
|
132
|
+
// A run in progress owns the output area: loading until the running cell
|
|
133
|
+
// has shown output (e.g. mo.output.append()), whatever the last result was.
|
|
134
|
+
if (outputIsLoading(status)) {
|
|
135
|
+
return !(status === "running" && rendered);
|
|
136
|
+
}
|
|
137
|
+
if (editing && freshness.sourceChanged) {
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
// An interrupted run's partial output stands as the record of that run.
|
|
128
141
|
if (outcome === "interrupted") {
|
|
129
142
|
return false;
|
|
130
143
|
}
|
|
144
|
+
return freshness.inputsChanged;
|
|
145
|
+
}
|
|
131
146
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
147
|
+
/**
|
|
148
|
+
* Console output is stale while a run is queued, since the run will clear
|
|
149
|
+
* it, and once a settled result is outdated. A run in progress owns it.
|
|
150
|
+
*/
|
|
151
|
+
export function consoleOutputIsStale(
|
|
152
|
+
execution: Pick<CellExecutionView, "status" | "outcome">,
|
|
153
|
+
freshness: CellFreshness,
|
|
154
|
+
): boolean {
|
|
155
|
+
const { status, outcome } = execution;
|
|
156
|
+
if (outcome === "interrupted") {
|
|
157
|
+
return false;
|
|
135
158
|
}
|
|
136
|
-
|
|
137
|
-
// Loading, unless the running cell has shown output (e.g. mo.output.append())
|
|
138
|
-
if (outputIsLoading(status) && !(status === "running" && rendered)) {
|
|
159
|
+
if (status === "queued") {
|
|
139
160
|
return true;
|
|
140
161
|
}
|
|
141
|
-
|
|
142
|
-
|
|
162
|
+
if (status === "running") {
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
return freshness.sourceChanged || freshness.inputsChanged;
|
|
143
166
|
}
|
|
144
167
|
|
|
145
168
|
/**
|
package/src/core/cells/cells.ts
CHANGED
|
@@ -9,6 +9,7 @@ import type { EditableDocument, Placement } from "@/core/notebook/document";
|
|
|
9
9
|
import {
|
|
10
10
|
cellExecutionInputsAtom,
|
|
11
11
|
cellExecutionView,
|
|
12
|
+
cellFreshnessAtom,
|
|
12
13
|
withdrawCells,
|
|
13
14
|
} from "@/core/executions";
|
|
14
15
|
import type { CellColumnId } from "./layout";
|
|
@@ -38,7 +39,6 @@ import {
|
|
|
38
39
|
type CellRuntimeState,
|
|
39
40
|
createCell,
|
|
40
41
|
createCellRuntimeState,
|
|
41
|
-
isEdited,
|
|
42
42
|
} from "./types";
|
|
43
43
|
import {
|
|
44
44
|
canUndoDeletes,
|
|
@@ -161,76 +161,21 @@ const {
|
|
|
161
161
|
useActions,
|
|
162
162
|
valueAtom: notebookAtom,
|
|
163
163
|
} = createReducerAndAtoms(initialNotebookState, {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
* its code from `before` to `after`. The code itself arrives through the
|
|
167
|
-
* document.
|
|
168
|
-
*/
|
|
169
|
-
codeFormatted: (
|
|
170
|
-
state,
|
|
171
|
-
action: { cellId: CellId; before: string; after: string },
|
|
172
|
-
) => {
|
|
173
|
-
const { cellId, before, after } = action;
|
|
174
|
-
if (!state.cellData[cellId]) {
|
|
175
|
-
return state;
|
|
176
|
-
}
|
|
177
|
-
return updateCellData({
|
|
178
|
-
state,
|
|
179
|
-
cellId,
|
|
180
|
-
cellReducer: (cell) => {
|
|
181
|
-
const lastCodeRun = isEdited(cell.lastCodeRun, before)
|
|
182
|
-
? cell.lastCodeRun
|
|
183
|
-
: after;
|
|
184
|
-
return {
|
|
185
|
-
...cell,
|
|
186
|
-
lastCodeRun,
|
|
187
|
-
edited: isEdited(lastCodeRun, cell.code),
|
|
188
|
-
};
|
|
189
|
-
},
|
|
190
|
-
});
|
|
191
|
-
},
|
|
192
|
-
prepareForRun: (state, action: { cellId: CellId }) => {
|
|
193
|
-
const newState = updateCellRuntimeState({
|
|
164
|
+
prepareForRun: (state, action: { cellId: CellId }) =>
|
|
165
|
+
updateCellRuntimeState({
|
|
194
166
|
state,
|
|
195
167
|
cellId: action.cellId,
|
|
196
168
|
cellReducer: (cell) => ({ ...cell, debuggerActive: false }),
|
|
197
|
-
})
|
|
198
|
-
return updateCellData({
|
|
199
|
-
state: newState,
|
|
200
|
-
cellId: action.cellId,
|
|
201
|
-
cellReducer: (cell) => {
|
|
202
|
-
return {
|
|
203
|
-
...cell,
|
|
204
|
-
edited: false,
|
|
205
|
-
lastCodeRun: cell.code.trim(),
|
|
206
|
-
};
|
|
207
|
-
},
|
|
208
|
-
});
|
|
209
|
-
},
|
|
169
|
+
}),
|
|
210
170
|
handleCellMessage: (state, message: CellMessage) => {
|
|
211
171
|
const cellId = message.cell_id;
|
|
212
|
-
|
|
172
|
+
const nextState = updateCellRuntimeState({
|
|
213
173
|
state,
|
|
214
174
|
cellId,
|
|
215
175
|
cellReducer: (cell) => {
|
|
216
176
|
return transitionCell(cell, message);
|
|
217
177
|
},
|
|
218
178
|
});
|
|
219
|
-
// When a cell is queued for execution, snapshot the current code
|
|
220
|
-
// as lastCodeRun. This clears staleness for cells executed by the
|
|
221
|
-
// kernel (e.g. via code_mode). If the user edits during execution,
|
|
222
|
-
// code !== lastCodeRun keeps the cell stale.
|
|
223
|
-
if (message.status === "queued") {
|
|
224
|
-
nextState = updateCellData({
|
|
225
|
-
state: nextState,
|
|
226
|
-
cellId,
|
|
227
|
-
cellReducer: (cell) => ({
|
|
228
|
-
...cell,
|
|
229
|
-
lastCodeRun: cell.code.trim(),
|
|
230
|
-
edited: false,
|
|
231
|
-
}),
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
179
|
return {
|
|
235
180
|
...nextState,
|
|
236
181
|
cellLogs: [...nextState.cellLogs, ...getCellLogsForMessage(message)],
|
|
@@ -286,21 +231,6 @@ const {
|
|
|
286
231
|
},
|
|
287
232
|
});
|
|
288
233
|
},
|
|
289
|
-
/** Record what the kernel last ran for each cell. */
|
|
290
|
-
setLastRun: (state, action: { lastCodeRun: Record<CellId, string> }) => {
|
|
291
|
-
const cellData = { ...state.cellData };
|
|
292
|
-
for (const cellId of state.cellIds) {
|
|
293
|
-
const cell = state.cellData[cellId];
|
|
294
|
-
const lastCodeRun = action.lastCodeRun[cellId] ?? null;
|
|
295
|
-
cellData[cellId] = {
|
|
296
|
-
...cell,
|
|
297
|
-
lastCodeRun,
|
|
298
|
-
// A cell the kernel never ran is not stale on load.
|
|
299
|
-
edited: lastCodeRun !== null && isEdited(lastCodeRun, cell.code),
|
|
300
|
-
};
|
|
301
|
-
}
|
|
302
|
-
return { ...state, cellData };
|
|
303
|
-
},
|
|
304
234
|
/**
|
|
305
235
|
* Start each cell's runtime over for a new kernel. WASM keeps the outputs
|
|
306
236
|
* its mount-time snapshot gave it.
|
|
@@ -429,29 +359,6 @@ function updateCellRuntimeState({
|
|
|
429
359
|
};
|
|
430
360
|
}
|
|
431
361
|
|
|
432
|
-
function updateCellData({
|
|
433
|
-
state,
|
|
434
|
-
cellId,
|
|
435
|
-
cellReducer,
|
|
436
|
-
}: {
|
|
437
|
-
state: NotebookState;
|
|
438
|
-
cellId: CellId;
|
|
439
|
-
cellReducer: ReducerWithoutAction<CellData>;
|
|
440
|
-
}) {
|
|
441
|
-
if (!(cellId in state.cellData)) {
|
|
442
|
-
Logger.warn(`Cell ${cellId} not found in state`);
|
|
443
|
-
return state;
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
return {
|
|
447
|
-
...state,
|
|
448
|
-
cellData: {
|
|
449
|
-
...state.cellData,
|
|
450
|
-
[cellId]: cellReducer(state.cellData[cellId]),
|
|
451
|
-
},
|
|
452
|
-
};
|
|
453
|
-
}
|
|
454
|
-
|
|
455
362
|
/** Each cell's config in order. The document's `column` configs are canonical. */
|
|
456
363
|
export function getCellConfigs(state: NotebookState): CellConfig[] {
|
|
457
364
|
return state.cellIds.map((cellId) => ({
|
|
@@ -492,7 +399,11 @@ export const canUndoDeletesAtom = atom((get) =>
|
|
|
492
399
|
export const undoLabelAtom = atom((get) => getUndoLabel(get(notebookAtom)));
|
|
493
400
|
|
|
494
401
|
export const needsRunAtom = atom((get) =>
|
|
495
|
-
notebookNeedsRun(
|
|
402
|
+
notebookNeedsRun(
|
|
403
|
+
get(notebookAtom),
|
|
404
|
+
get(cellExecutionInputsAtom),
|
|
405
|
+
get(cellFreshnessAtom),
|
|
406
|
+
),
|
|
496
407
|
);
|
|
497
408
|
|
|
498
409
|
export const cellErrorsAtom = atom((get) => {
|