@marimo-team/islands 0.25.3-dev267 → 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-C4yCN38h.js → ConnectedDataExplorerComponent-eLHPh6uu.js} +8 -8
- package/dist/{ErrorBoundary-CewXPyL5.js → ErrorBoundary-CQYAoD7X.js} +4 -4
- package/dist/{abnfDiagram-VCTEODGH-o6Ay6Q63.js → abnfDiagram-VCTEODGH-hqd0IBtX.js} +2 -2
- package/dist/{architectureDiagram-5GKGNRK7-BhiwwlVg.js → architectureDiagram-5GKGNRK7-BzLAFcc0.js} +1 -1
- package/dist/{strings-BCKpLFNU.js → assertNever-DIU3JJbt.js} +14 -14
- package/dist/{chat-ui-CbgR5ivy.js → chat-ui-BAnLgpTe.js} +17 -17
- package/dist/{chunk-IMKFNOWR-WMSMf4Nj.js → chunk-IMKFNOWR-DOeVvuyE.js} +1 -1
- package/dist/{chunk-SHT3W25Y-BRfADrzD.js → chunk-SHT3W25Y-DZoFI8wR.js} +1 -1
- package/dist/{chunk-SVP7TREG-CuK5QFre.js → chunk-SVP7TREG-CWu86PSW.js} +1 -1
- package/dist/{chunk-TICWLB2K-Bcun_jhJ.js → chunk-TICWLB2K-BrM0GoP3.js} +1 -1
- package/dist/{classDiagram-ZZMXUADV-CgWDbUDT.js → classDiagram-ZZMXUADV-DYi4V2bB.js} +2 -2
- package/dist/{classDiagram-v2-VYDZK3BY-DYmc9SFU.js → classDiagram-v2-VYDZK3BY-tN7FIk-m.js} +2 -2
- package/dist/{code-visibility-BKBl9Rh6.js → code-visibility-DcZ_RwaH.js} +44 -47
- package/dist/{common-N3Jd_6m9.js → common-BGeQilvh.js} +4866 -4857
- package/dist/{config-CmeG_Pb6.js → config-BsPruWMR.js} +2 -2
- package/dist/{createReducer-C_syF8Td.js → createReducer-DdtT_7ji.js} +1 -1
- package/dist/{cynefinDiagram-5FMLGOSQ-PPKWipjT.js → cynefinDiagram-5FMLGOSQ-D5nCnDc-.js} +1 -1
- package/dist/{dagre-GXQ25YYZ-niRBZh--.js → dagre-GXQ25YYZ-BHkd7f_i.js} +1 -1
- package/dist/{data-grid-overlay-editor-Cp59CrqZ.js → data-grid-overlay-editor-Dc4lW4A3.js} +1 -1
- package/dist/{diagram-S7CK7UJ4-DPiP9rot.js → diagram-S7CK7UJ4-D1ALppT2.js} +1 -1
- package/dist/{diagram-UQ7AKVKN-B0WZuaxV.js → diagram-UQ7AKVKN-DLv0b83L.js} +1 -1
- package/dist/{diagram-VX7I27RA-Rn42WBuN.js → diagram-VX7I27RA-7XJ7J6qg.js} +1 -1
- package/dist/{diagram-Z3DM3KII-BB93--ry.js → diagram-Z3DM3KII-7M7zbHp_.js} +1 -1
- package/dist/{dialog-BtprQCUw.js → dialog-DzCj1qQv.js} +1 -1
- package/dist/{dropdown-menu-BpImEfn3.js → dropdown-menu-CdXE15an.js} +3 -3
- package/dist/{ebnfDiagram-PWID7BFC-V4t5h6mZ.js → ebnfDiagram-PWID7BFC-DHH1qlmV.js} +2 -2
- package/dist/{erDiagram-RLTQ6QDP-C1MsAKXd.js → erDiagram-RLTQ6QDP-fI_0dGef.js} +1 -1
- package/dist/{error-entries-CJGdD2ky.js → error-entries-CxN-Nq3V.js} +519 -458
- package/dist/{feedback-button-Qc1f9iPq.js → feedback-button-BqaeQcvX.js} +22 -22
- package/dist/{flowDiagram-HODETNUW-DHjeJP96.js → flowDiagram-HODETNUW-Brg9MA6i.js} +2 -2
- package/dist/{formats-gGospKWJ.js → formats-DbSvAPY-.js} +1 -1
- package/dist/{glide-data-editor-hCq3K0sd.js → glide-data-editor-CiijPXA7.js} +11 -11
- package/dist/{glide-utils-DC3sk6EX.js → glide-utils-BSw3SSHF.js} +1 -1
- package/dist/{globals-B2_uMYDY.js → globals-CpmSWKI_.js} +2 -2
- package/dist/{html-to-image-S7spRQCi.js → html-to-image-8sywF4my.js} +5120 -5117
- package/dist/{infoDiagram-27XIBGKW-Dovv6iKx.js → infoDiagram-27XIBGKW-E9e403YT.js} +1 -1
- package/dist/{ishikawaDiagram-5VMMS53U-D2nyygsc.js → ishikawaDiagram-5VMMS53U-C-RlYDRo.js} +1 -1
- package/dist/{kanban-definition-UXKFOSKX-KyVx0uTY.js → kanban-definition-UXKFOSKX-CLFl_uqK.js} +1 -1
- package/dist/main.js +5972 -6003
- package/dist/{mermaid-B-jKSwGt.js → mermaid-rd09e1JL.js} +34 -34
- package/dist/{mindmap-definition-YA3MSWOX-Bz9tKQFl.js → mindmap-definition-YA3MSWOX-BQ8TWtyB.js} +1 -1
- package/dist/{number-overlay-editor-CfNYvbdz.js → number-overlay-editor-Ft4NiAEN.js} +1 -1
- package/dist/{pegDiagram-XKGWAZYB-C_qpq6Tv.js → pegDiagram-XKGWAZYB-CN8UHFPi.js} +2 -2
- package/dist/{pieDiagram-E7YTZNPT-Bp9ucgI4.js → pieDiagram-E7YTZNPT-DGuJ0EBd.js} +1 -1
- package/dist/{process-output-f34bkXTm.js → process-output-Dku5POvm.js} +3 -3
- package/dist/{railroadDiagram-O6MQD6OU-tq-VEH6j.js → railroadDiagram-O6MQD6OU-BPDA8wy8.js} +2 -2
- package/dist/{requirementDiagram-BXWQKSXE-DW-vHadb.js → requirementDiagram-BXWQKSXE-ChzLGbGW.js} +1 -1
- package/dist/{reveal-component-CUxv-kkP.js → reveal-component-D0cfwbbl.js} +1094 -1094
- package/dist/{select-C33FVPPN.js → select-OxgozOC8.js} +1 -1
- package/dist/{sequenceDiagram-WJ2MYXX4-Dv9U9qRP.js → sequenceDiagram-WJ2MYXX4-DFX-bE6O.js} +1 -1
- package/dist/{spec-DH1lmF7Q.js → spec-oJn0RhpQ.js} +2 -2
- package/dist/{stateDiagram-D77RDMKH-DTmPQ0EB.js → stateDiagram-D77RDMKH-BR7cI3wK.js} +2 -2
- package/dist/{stateDiagram-v2-MP3YSRHH-CkCif0Iw.js → stateDiagram-v2-MP3YSRHH-oGqB5UDI.js} +2 -2
- package/dist/{static-state-BldGd99T.js → static-state-CuV6Pc3a.js} +1 -1
- package/dist/{swimlanes-42K2YHIH-21kCq6l8.js → swimlanes-42K2YHIH-DwSZl1yC.js} +1 -1
- package/dist/{swimlanesDiagram-VR7AAH4N-BpRJP6KU.js → swimlanesDiagram-VR7AAH4N-BHlcd-Ri.js} +2 -2
- package/dist/{timeline-definition-24CTP7MA-Ckgh4lS4.js → timeline-definition-24CTP7MA-DMmP-G2O.js} +1 -1
- package/dist/{toast-DpczUryP.js → toast-DbBHmxmB.js} +1 -1
- package/dist/{useAsyncData-DsZMvNXn.js → useAsyncData-BYIAoMqn.js} +1 -1
- package/dist/{useDateFormatter-Dk_xFci4.js → useDateFormatter-ButWkdcK.js} +1 -1
- package/dist/{useDeepCompareMemoize-KMBBux8C.js → useDeepCompareMemoize-Bz0c5GN6.js} +1 -1
- package/dist/{useTheme-BZ2GGLgm.js → useTheme-DPLobSFs.js} +7 -7
- package/dist/{utils-DXkj82sK.js → utils-EBklsC5R.js} +31 -31
- package/dist/{vega-component-CF9kI6C4.js → vega-component-rxIxAAh9.js} +6 -6
- package/dist/{vennDiagram-4TSXK5OY-CDei2Bt3.js → vennDiagram-4TSXK5OY-Db8GtH1G.js} +1 -1
- package/dist/{wardleyDiagram-VM6X3IG4-CXavT601.js → wardleyDiagram-VM6X3IG4-DmIiC3FU.js} +1 -1
- package/dist/{xychartDiagram-S5SC5T6Z-CvJhcZri.js → xychartDiagram-S5SC5T6Z-DS9OPrNq.js} +1 -1
- package/package.json +1 -1
- package/src/__mocks__/executions.ts +195 -0
- package/src/__mocks__/notebook.ts +0 -1
- package/src/__tests__/CellStatus.test.tsx +19 -20
- package/src/components/dependency-graph/minimap-content.tsx +3 -2
- package/src/components/editor/__tests__/data-attributes.test.tsx +0 -7
- package/src/components/editor/__tests__/output-persistence.test.tsx +2 -2
- package/src/components/editor/actions/__tests__/useCellActionButton.test.tsx +1 -0
- package/src/components/editor/actions/useCellActionButton.tsx +7 -3
- package/src/components/editor/alerts/stdin-blocking-alert.tsx +3 -1
- package/src/components/editor/cell/CellStatus.tsx +23 -19
- package/src/components/editor/cell/RunButton.tsx +16 -6
- package/src/components/editor/cell/__tests__/useRunCells.test.ts +45 -0
- package/src/components/editor/cell/cell-context-menu.tsx +4 -1
- package/src/components/editor/cell/code/cell-editor.tsx +7 -5
- package/src/components/editor/cell/collapse.tsx +4 -0
- package/src/components/editor/cell/useRunCells.ts +18 -3
- package/src/components/editor/chrome/wrapper/sidebar.tsx +2 -4
- package/src/components/editor/header/status.tsx +4 -2
- package/src/components/editor/navigation/navigation.ts +3 -2
- package/src/components/editor/notebook-cell.tsx +82 -83
- package/src/components/editor/renderers/grid-layout/__tests__/grid-layout.test.tsx +75 -0
- package/src/components/editor/renderers/grid-layout/grid-layout.tsx +22 -16
- package/src/components/editor/renderers/slides-layout/__tests__/plugin.test.ts +0 -1
- package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +12 -33
- package/src/components/editor/stdin-blocking-alert.tsx +3 -1
- package/src/components/scratchpad/scratchpad.tsx +4 -1
- package/src/components/slides/minimap.tsx +4 -2
- package/src/components/slides/reveal-component.tsx +14 -10
- package/src/components/slides/slide-cell-view.tsx +28 -24
- package/src/components/slides/slide.tsx +3 -4
- package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +0 -389
- package/src/core/cells/__tests__/cell.test.ts +49 -154
- package/src/core/cells/__tests__/cells.test.ts +18 -127
- package/src/core/cells/__tests__/pending-delete-service.test.tsx +11 -20
- package/src/core/cells/__tests__/utils.test.ts +75 -247
- package/src/core/cells/actions.ts +5 -2
- package/src/core/cells/cell.ts +31 -133
- package/src/core/cells/cells.ts +14 -29
- package/src/core/cells/document.ts +3 -3
- package/src/core/cells/focus.ts +27 -6
- package/src/core/cells/layout.ts +1 -1
- package/src/core/cells/pending-delete-service.ts +5 -7
- package/src/core/cells/types.ts +13 -36
- package/src/core/cells/utils.ts +37 -68
- package/src/core/edit-app.tsx +2 -1
- package/src/core/executions/__tests__/atoms.test.ts +99 -0
- package/src/core/executions/__tests__/select.test.ts +268 -0
- package/src/core/executions/atoms.ts +206 -0
- package/src/core/executions/hooks.ts +31 -0
- package/src/core/executions/index.ts +31 -0
- package/src/core/executions/select.ts +175 -0
- package/src/core/export/__tests__/hooks.test.ts +0 -5
- package/src/core/islands/bootstrap.ts +4 -1
- package/src/core/islands/components/output-wrapper.tsx +5 -5
- package/src/core/kernel/handlers.ts +2 -11
- package/src/core/kernel/state.ts +0 -15
- package/src/core/lifecycle/render-policy.ts +12 -26
- package/src/core/network/__tests__/requests-lazy.test.ts +5 -5
- package/src/core/network/requests-lazy.ts +3 -3
- package/src/core/notebook/operations.ts +1 -1
- package/src/core/run-app.tsx +2 -1
- package/src/core/runtime/__tests__/adapter.test.ts +3 -3
- package/src/core/runtime/adapter.ts +4 -4
- package/src/core/session/useNotebookSession.tsx +3 -3
- package/src/core/wasm/bridge.ts +1 -1
- package/src/plugins/core/registerReactComponent.tsx +4 -13
- package/src/stories/cell-status.stories.tsx +17 -10
- package/src/stories/cell.stories.tsx +0 -31
- package/src/stories/layout/vertical/one-column.stories.tsx +0 -9
- package/src/utils/time.ts +10 -0
|
@@ -37,10 +37,14 @@ import { isErrorMime } from "@/core/mime";
|
|
|
37
37
|
import type { AppMode } from "@/core/mode";
|
|
38
38
|
import { connectionAtom } from "@/core/network/connection";
|
|
39
39
|
import { useRequestClient } from "@/core/network/requests";
|
|
40
|
-
import
|
|
40
|
+
import {
|
|
41
|
+
type CellExecutionView,
|
|
42
|
+
type CellRunStatus,
|
|
43
|
+
useCellExecution,
|
|
44
|
+
} from "@/core/executions";
|
|
45
|
+
import type { CellConfig } from "@/core/network/types";
|
|
41
46
|
import { useResizeObserver } from "@/hooks/useResizeObserver";
|
|
42
47
|
import { cn } from "@/utils/cn";
|
|
43
|
-
import type { Milliseconds, Seconds } from "@/utils/time";
|
|
44
48
|
import {
|
|
45
49
|
useCellData,
|
|
46
50
|
useCellHandle,
|
|
@@ -315,6 +319,10 @@ const ReadonlyCellComponent = forwardRef(
|
|
|
315
319
|
const { cellId } = props;
|
|
316
320
|
const cellData = useCellData(cellId);
|
|
317
321
|
const cellRuntime = useCellRuntime(cellId);
|
|
322
|
+
const execution = useCellExecution(cellId);
|
|
323
|
+
const errored = execution.outcome === "error";
|
|
324
|
+
const interrupted = execution.outcome === "interrupted";
|
|
325
|
+
const stopped = execution.outcome === "skipped";
|
|
318
326
|
|
|
319
327
|
const className = clsx("marimo-cell", "hover-actions-parent z-10", {
|
|
320
328
|
published: true,
|
|
@@ -322,9 +330,9 @@ const ReadonlyCellComponent = forwardRef(
|
|
|
322
330
|
|
|
323
331
|
// Hide the output if it's an error or stopped.
|
|
324
332
|
const hidden = shouldHidePublishedCell({
|
|
325
|
-
errored
|
|
326
|
-
interrupted
|
|
327
|
-
stopped
|
|
333
|
+
errored,
|
|
334
|
+
interrupted,
|
|
335
|
+
stopped,
|
|
328
336
|
output: cellRuntime.output,
|
|
329
337
|
showErrorTracebacks: false,
|
|
330
338
|
});
|
|
@@ -346,8 +354,12 @@ const ReadonlyCellComponent = forwardRef(
|
|
|
346
354
|
className={CSSClasses.outputArea}
|
|
347
355
|
cellId={cellId}
|
|
348
356
|
output={cellRuntime.output}
|
|
349
|
-
stale={outputIsStale(
|
|
350
|
-
|
|
357
|
+
stale={outputIsStale(
|
|
358
|
+
execution,
|
|
359
|
+
cellRuntime.staleInputs,
|
|
360
|
+
cellData.edited,
|
|
361
|
+
)}
|
|
362
|
+
loading={outputIsLoading(execution.status)}
|
|
351
363
|
/>
|
|
352
364
|
</div>
|
|
353
365
|
);
|
|
@@ -374,6 +386,10 @@ const EditableCellComponent = ({
|
|
|
374
386
|
const cellRef = useRef<HTMLDivElement>(null);
|
|
375
387
|
const cellData = useCellData(cellId);
|
|
376
388
|
const cellRuntime = useCellRuntime(cellId);
|
|
389
|
+
const execution = useCellExecution(cellId);
|
|
390
|
+
const errored = execution.outcome === "error";
|
|
391
|
+
const interrupted = execution.outcome === "interrupted";
|
|
392
|
+
const stopped = execution.outcome === "skipped";
|
|
377
393
|
const cellActionDropdownRef = useRef<CellActionsDropdownHandle>(null);
|
|
378
394
|
// DOM node where the editorView will be mounted
|
|
379
395
|
const editorViewParentRef = useRef<HTMLDivElement>(null);
|
|
@@ -389,23 +405,17 @@ const EditableCellComponent = ({
|
|
|
389
405
|
|
|
390
406
|
const [languageAdapter, setLanguageAdapter] = useState<LanguageAdapterType>();
|
|
391
407
|
|
|
392
|
-
const uninstantiated = isUninstantiated(
|
|
393
|
-
executionTime: cellRuntime.runElapsedTimeMs ?? cellData.lastExecutionTime,
|
|
394
|
-
status: cellRuntime.status,
|
|
395
|
-
errored: cellRuntime.errored,
|
|
396
|
-
interrupted: cellRuntime.interrupted,
|
|
397
|
-
stopped: cellRuntime.stopped,
|
|
398
|
-
});
|
|
408
|
+
const uninstantiated = isUninstantiated(execution);
|
|
399
409
|
|
|
400
410
|
const needsRun = cellNeedsRun({
|
|
401
411
|
edited: cellData.edited,
|
|
402
|
-
interrupted
|
|
412
|
+
interrupted,
|
|
403
413
|
staleInputs: cellRuntime.staleInputs,
|
|
404
414
|
disabled: cellData.config.disabled,
|
|
405
|
-
|
|
415
|
+
blocked: execution.blocked,
|
|
406
416
|
});
|
|
407
417
|
|
|
408
|
-
const loading = outputIsLoading(
|
|
418
|
+
const loading = outputIsLoading(execution.status);
|
|
409
419
|
|
|
410
420
|
const isPresenting = mode === "present";
|
|
411
421
|
|
|
@@ -414,9 +424,9 @@ const EditableCellComponent = ({
|
|
|
414
424
|
const presentHidden =
|
|
415
425
|
isPresenting &&
|
|
416
426
|
shouldHidePublishedCell({
|
|
417
|
-
errored
|
|
418
|
-
interrupted
|
|
419
|
-
stopped
|
|
427
|
+
errored,
|
|
428
|
+
interrupted,
|
|
429
|
+
stopped,
|
|
420
430
|
output: cellRuntime.output,
|
|
421
431
|
showErrorTracebacks: userConfig.runtime.show_tracebacks ?? false,
|
|
422
432
|
});
|
|
@@ -432,10 +442,10 @@ const EditableCellComponent = ({
|
|
|
432
442
|
// console output is cleared immediately on run, so check for queued instead
|
|
433
443
|
// of loading to determine staleness
|
|
434
444
|
const consoleOutputStale =
|
|
435
|
-
(
|
|
445
|
+
(execution.status === "queued" ||
|
|
436
446
|
cellData.edited ||
|
|
437
447
|
cellRuntime.staleInputs) &&
|
|
438
|
-
!
|
|
448
|
+
!interrupted;
|
|
439
449
|
|
|
440
450
|
// This getter is called during render, so useEvent cannot be used here.
|
|
441
451
|
// oxlint-disable-next-line react/preserve-manual-memoization
|
|
@@ -472,7 +482,8 @@ const EditableCellComponent = ({
|
|
|
472
482
|
// While presenting, pending edits do not dim the output as stale,
|
|
473
483
|
// matching the read view.
|
|
474
484
|
const isStaleCell = outputIsStale(
|
|
475
|
-
|
|
485
|
+
execution,
|
|
486
|
+
cellRuntime.staleInputs,
|
|
476
487
|
cellData.edited && !isPresenting,
|
|
477
488
|
);
|
|
478
489
|
const hasConsoleOutput = cellRuntime.consoleOutputs.length > 0;
|
|
@@ -568,7 +579,7 @@ const EditableCellComponent = ({
|
|
|
568
579
|
cellId={cellId}
|
|
569
580
|
output={cellRuntime.output}
|
|
570
581
|
stale={isStaleCell}
|
|
571
|
-
loading={outputIsLoading(
|
|
582
|
+
loading={outputIsLoading(execution.status)}
|
|
572
583
|
/>
|
|
573
584
|
</div>
|
|
574
585
|
);
|
|
@@ -579,18 +590,18 @@ const EditableCellComponent = ({
|
|
|
579
590
|
isPresenting
|
|
580
591
|
? // Match the published styling of the read view
|
|
581
592
|
publishedCellClasses({
|
|
582
|
-
errored
|
|
583
|
-
stopped
|
|
593
|
+
errored,
|
|
594
|
+
stopped,
|
|
584
595
|
})
|
|
585
596
|
: {
|
|
586
597
|
"z-10": true,
|
|
587
598
|
interactive: true,
|
|
588
599
|
...cellStatusClasses({
|
|
589
600
|
needsRun,
|
|
590
|
-
errored
|
|
591
|
-
stopped
|
|
601
|
+
errored,
|
|
602
|
+
stopped,
|
|
592
603
|
disabled: cellData.config.disabled,
|
|
593
|
-
|
|
604
|
+
blocked: execution.blocked,
|
|
594
605
|
}),
|
|
595
606
|
borderless:
|
|
596
607
|
isMarkdownCodeHidden &&
|
|
@@ -617,7 +628,7 @@ const EditableCellComponent = ({
|
|
|
617
628
|
if (cellData.config.disabled) {
|
|
618
629
|
return "This cell is disabled";
|
|
619
630
|
}
|
|
620
|
-
if (
|
|
631
|
+
if (execution.blocked) {
|
|
621
632
|
return "This cell has a disabled ancestor";
|
|
622
633
|
}
|
|
623
634
|
return undefined;
|
|
@@ -635,7 +646,7 @@ const EditableCellComponent = ({
|
|
|
635
646
|
<SortableCell
|
|
636
647
|
tabIndex={-1}
|
|
637
648
|
ref={cellRef}
|
|
638
|
-
data-status={
|
|
649
|
+
data-status={execution.status}
|
|
639
650
|
onBlur={closeCompletionHandler}
|
|
640
651
|
onKeyDown={resumeCompletionHandler}
|
|
641
652
|
cellId={cellId}
|
|
@@ -675,7 +686,8 @@ const EditableCellComponent = ({
|
|
|
675
686
|
<div className="absolute right-2 -top-4 z-10">
|
|
676
687
|
<CellToolbar
|
|
677
688
|
edited={cellData.edited}
|
|
678
|
-
status={
|
|
689
|
+
status={execution.status}
|
|
690
|
+
blocked={execution.blocked}
|
|
679
691
|
cellConfig={cellData.config}
|
|
680
692
|
needsRun={needsRun}
|
|
681
693
|
hasOutput={hasOutput}
|
|
@@ -693,7 +705,7 @@ const EditableCellComponent = ({
|
|
|
693
705
|
id={cellId}
|
|
694
706
|
code={cellData.code}
|
|
695
707
|
config={cellData.config}
|
|
696
|
-
status={
|
|
708
|
+
status={execution.status}
|
|
697
709
|
runCell={runCell}
|
|
698
710
|
setEditorView={setEditorView}
|
|
699
711
|
userConfig={userConfig}
|
|
@@ -714,21 +726,17 @@ const EditableCellComponent = ({
|
|
|
714
726
|
isMarkdownCodeHidden && cellOutput === "below" && "top-14",
|
|
715
727
|
)}
|
|
716
728
|
edited={cellData.edited}
|
|
717
|
-
|
|
729
|
+
execution={execution}
|
|
718
730
|
isCellStatusInline={isCellStatusInline}
|
|
719
731
|
uninstantiated={uninstantiated}
|
|
720
732
|
disabled={cellData.config.disabled}
|
|
721
|
-
runElapsedTimeMs={cellRuntime.runElapsedTimeMs}
|
|
722
|
-
runStartTimestamp={cellRuntime.runStartTimestamp}
|
|
723
|
-
lastRunStartTimestamp={cellRuntime.lastRunStartTimestamp}
|
|
724
733
|
staleInputs={cellRuntime.staleInputs}
|
|
725
|
-
interrupted={cellRuntime.interrupted}
|
|
726
734
|
/>
|
|
727
735
|
)}
|
|
728
736
|
<div className="shoulder-bottom hover-action">
|
|
729
737
|
{canDelete && isCellCodeShown && (
|
|
730
738
|
<DeleteButton
|
|
731
|
-
status={
|
|
739
|
+
status={execution.status}
|
|
732
740
|
connectionState={connection.state}
|
|
733
741
|
onClick={() => {
|
|
734
742
|
if (
|
|
@@ -745,7 +753,7 @@ const EditableCellComponent = ({
|
|
|
745
753
|
{!isPresenting && (
|
|
746
754
|
<SqlValidationErrorBanner
|
|
747
755
|
cellId={cellId}
|
|
748
|
-
hide={
|
|
756
|
+
hide={errored && !isStaleCell}
|
|
749
757
|
/>
|
|
750
758
|
)}
|
|
751
759
|
{cellOutput === "below" && (outputArea || emptyMarkdownPlaceholder)}
|
|
@@ -811,7 +819,7 @@ const EditableCellComponent = ({
|
|
|
811
819
|
<ConsoleOutput
|
|
812
820
|
consoleOutputs={cellRuntime.consoleOutputs}
|
|
813
821
|
stale={consoleOutputStale}
|
|
814
|
-
interrupted={
|
|
822
|
+
interrupted={interrupted}
|
|
815
823
|
// Empty name if serialization triggered
|
|
816
824
|
cellName={cellRuntime.serialization ? "_" : cellData.name}
|
|
817
825
|
onRefactorWithAI={handleRefactorWithAI}
|
|
@@ -853,41 +861,33 @@ const CellRightSideActions = memo(
|
|
|
853
861
|
className?: string;
|
|
854
862
|
disabled: boolean | undefined;
|
|
855
863
|
edited: boolean;
|
|
856
|
-
|
|
864
|
+
execution: CellExecutionView;
|
|
857
865
|
isCellStatusInline: boolean;
|
|
858
|
-
lastRunStartTimestamp: Seconds | null;
|
|
859
|
-
runElapsedTimeMs: Milliseconds | null;
|
|
860
|
-
runStartTimestamp: Seconds | null;
|
|
861
866
|
staleInputs: boolean;
|
|
862
|
-
status: RuntimeState;
|
|
863
867
|
uninstantiated: boolean;
|
|
864
868
|
}) => {
|
|
865
869
|
const {
|
|
866
870
|
className,
|
|
867
871
|
disabled = false,
|
|
868
872
|
edited,
|
|
869
|
-
|
|
873
|
+
execution,
|
|
870
874
|
isCellStatusInline,
|
|
871
|
-
lastRunStartTimestamp,
|
|
872
|
-
runElapsedTimeMs,
|
|
873
|
-
runStartTimestamp,
|
|
874
875
|
staleInputs,
|
|
875
|
-
status,
|
|
876
876
|
uninstantiated,
|
|
877
877
|
} = props;
|
|
878
878
|
|
|
879
879
|
const cellStatusComponent = (
|
|
880
880
|
<CellStatusComponent
|
|
881
|
-
status={status}
|
|
881
|
+
status={execution.status}
|
|
882
882
|
staleInputs={staleInputs}
|
|
883
|
-
interrupted={interrupted}
|
|
883
|
+
interrupted={execution.outcome === "interrupted"}
|
|
884
884
|
editing={true}
|
|
885
885
|
edited={edited}
|
|
886
886
|
disabled={disabled}
|
|
887
|
-
elapsedTime={
|
|
888
|
-
|
|
887
|
+
elapsedTime={execution.duration}
|
|
888
|
+
startedAt={execution.startedAt}
|
|
889
|
+
blocked={execution.blocked}
|
|
889
890
|
uninstantiated={uninstantiated}
|
|
890
|
-
lastRunStartTimestamp={lastRunStartTimestamp}
|
|
891
891
|
/>
|
|
892
892
|
);
|
|
893
893
|
|
|
@@ -960,7 +960,8 @@ CellLeftSideActions.displayName = "CellLeftSideActions";
|
|
|
960
960
|
|
|
961
961
|
interface CellToolbarProps {
|
|
962
962
|
edited: boolean;
|
|
963
|
-
status:
|
|
963
|
+
status: CellRunStatus;
|
|
964
|
+
blocked: boolean;
|
|
964
965
|
cellConfig: CellConfig;
|
|
965
966
|
needsRun: boolean;
|
|
966
967
|
hasOutput: boolean;
|
|
@@ -977,6 +978,7 @@ const CellToolbar = memo(
|
|
|
977
978
|
({
|
|
978
979
|
edited,
|
|
979
980
|
status,
|
|
981
|
+
blocked,
|
|
980
982
|
cellConfig,
|
|
981
983
|
needsRun,
|
|
982
984
|
hasOutput,
|
|
@@ -1002,6 +1004,7 @@ const CellToolbar = memo(
|
|
|
1002
1004
|
onClick={onRun}
|
|
1003
1005
|
connectionState={connection.state}
|
|
1004
1006
|
status={status}
|
|
1007
|
+
blocked={blocked}
|
|
1005
1008
|
config={cellConfig}
|
|
1006
1009
|
needsRun={needsRun}
|
|
1007
1010
|
/>
|
|
@@ -1011,6 +1014,7 @@ const CellToolbar = memo(
|
|
|
1011
1014
|
ref={cellActionDropdownRef}
|
|
1012
1015
|
cellId={cellId}
|
|
1013
1016
|
status={status}
|
|
1017
|
+
blocked={blocked}
|
|
1014
1018
|
getEditorView={getEditorView}
|
|
1015
1019
|
name={name}
|
|
1016
1020
|
config={cellConfig}
|
|
@@ -1054,6 +1058,10 @@ const SetupCellComponent = ({
|
|
|
1054
1058
|
const cellRef = useRef<HTMLDivElement>(null);
|
|
1055
1059
|
const cellData = useCellData(cellId);
|
|
1056
1060
|
const cellRuntime = useCellRuntime(cellId);
|
|
1061
|
+
const execution = useCellExecution(cellId);
|
|
1062
|
+
const errored = execution.outcome === "error";
|
|
1063
|
+
const interrupted = execution.outcome === "interrupted";
|
|
1064
|
+
const stopped = execution.outcome === "skipped";
|
|
1057
1065
|
const cellActionDropdownRef = useRef<CellActionsDropdownHandle>(null);
|
|
1058
1066
|
// DOM node where the editorView will be mounted
|
|
1059
1067
|
const editorViewParentRef = useRef<HTMLDivElement>(null);
|
|
@@ -1065,31 +1073,25 @@ const SetupCellComponent = ({
|
|
|
1065
1073
|
const setAiCompletionCell = useSetAtom(aiCompletionCellAtom);
|
|
1066
1074
|
const runCell = useRunCell(cellId);
|
|
1067
1075
|
|
|
1068
|
-
const uninstantiated = isUninstantiated(
|
|
1069
|
-
executionTime: cellRuntime.runElapsedTimeMs ?? cellData.lastExecutionTime,
|
|
1070
|
-
status: cellRuntime.status,
|
|
1071
|
-
errored: cellRuntime.errored,
|
|
1072
|
-
interrupted: cellRuntime.interrupted,
|
|
1073
|
-
stopped: cellRuntime.stopped,
|
|
1074
|
-
});
|
|
1076
|
+
const uninstantiated = isUninstantiated(execution);
|
|
1075
1077
|
|
|
1076
1078
|
const needsRun = cellNeedsRun({
|
|
1077
1079
|
edited: cellData.edited,
|
|
1078
|
-
interrupted
|
|
1080
|
+
interrupted,
|
|
1079
1081
|
staleInputs: cellRuntime.staleInputs,
|
|
1080
1082
|
disabled: cellData.config.disabled,
|
|
1081
|
-
|
|
1083
|
+
blocked: execution.blocked,
|
|
1082
1084
|
});
|
|
1083
1085
|
const loading =
|
|
1084
|
-
|
|
1086
|
+
execution.status === "running" || execution.status === "queued";
|
|
1085
1087
|
|
|
1086
1088
|
// console output is cleared immediately on run, so check for queued instead
|
|
1087
1089
|
// of loading to determine staleness
|
|
1088
1090
|
const consoleOutputStale =
|
|
1089
|
-
(
|
|
1091
|
+
(execution.status === "queued" ||
|
|
1090
1092
|
cellData.edited ||
|
|
1091
1093
|
cellRuntime.staleInputs) &&
|
|
1092
|
-
!
|
|
1094
|
+
!interrupted;
|
|
1093
1095
|
|
|
1094
1096
|
// This getter is called during render, so useEvent cannot be used here.
|
|
1095
1097
|
// oxlint-disable-next-line react/preserve-manual-memoization
|
|
@@ -1123,10 +1125,10 @@ const SetupCellComponent = ({
|
|
|
1123
1125
|
interactive: true,
|
|
1124
1126
|
...cellStatusClasses({
|
|
1125
1127
|
needsRun,
|
|
1126
|
-
errored
|
|
1127
|
-
stopped
|
|
1128
|
+
errored,
|
|
1129
|
+
stopped,
|
|
1128
1130
|
disabled: cellData.config.disabled,
|
|
1129
|
-
|
|
1131
|
+
blocked: execution.blocked,
|
|
1130
1132
|
}),
|
|
1131
1133
|
});
|
|
1132
1134
|
|
|
@@ -1147,7 +1149,7 @@ const SetupCellComponent = ({
|
|
|
1147
1149
|
if (cellData.config.disabled) {
|
|
1148
1150
|
return "This cell is disabled";
|
|
1149
1151
|
}
|
|
1150
|
-
if (
|
|
1152
|
+
if (execution.blocked) {
|
|
1151
1153
|
return "This cell has a disabled ancestor";
|
|
1152
1154
|
}
|
|
1153
1155
|
return undefined;
|
|
@@ -1174,7 +1176,7 @@ const SetupCellComponent = ({
|
|
|
1174
1176
|
mounted) while presenting. */}
|
|
1175
1177
|
<div hidden={isPresenting}>
|
|
1176
1178
|
<div
|
|
1177
|
-
data-status={
|
|
1179
|
+
data-status={execution.status}
|
|
1178
1180
|
ref={cellRef}
|
|
1179
1181
|
{...mergeProps(navigationProps, {
|
|
1180
1182
|
className: cn(
|
|
@@ -1198,7 +1200,8 @@ const SetupCellComponent = ({
|
|
|
1198
1200
|
<div className="absolute right-2 -top-4 z-10">
|
|
1199
1201
|
<CellToolbar
|
|
1200
1202
|
edited={cellData.edited}
|
|
1201
|
-
status={
|
|
1203
|
+
status={execution.status}
|
|
1204
|
+
blocked={execution.blocked}
|
|
1202
1205
|
cellConfig={cellData.config}
|
|
1203
1206
|
needsRun={needsRun}
|
|
1204
1207
|
hasOutput={hasOutput}
|
|
@@ -1217,7 +1220,7 @@ const SetupCellComponent = ({
|
|
|
1217
1220
|
id={cellId}
|
|
1218
1221
|
code={cellData.code}
|
|
1219
1222
|
config={cellData.config}
|
|
1220
|
-
status={
|
|
1223
|
+
status={execution.status}
|
|
1221
1224
|
runCell={runCell}
|
|
1222
1225
|
setEditorView={setEditorView}
|
|
1223
1226
|
userConfig={userConfig}
|
|
@@ -1235,22 +1238,18 @@ const SetupCellComponent = ({
|
|
|
1235
1238
|
{!isPresenting && (
|
|
1236
1239
|
<CellRightSideActions
|
|
1237
1240
|
edited={cellData.edited}
|
|
1238
|
-
|
|
1241
|
+
execution={execution}
|
|
1239
1242
|
isCellStatusInline={false}
|
|
1240
1243
|
uninstantiated={uninstantiated}
|
|
1241
1244
|
disabled={cellData.config.disabled}
|
|
1242
|
-
runElapsedTimeMs={cellRuntime.runElapsedTimeMs}
|
|
1243
|
-
runStartTimestamp={cellRuntime.runStartTimestamp}
|
|
1244
|
-
lastRunStartTimestamp={cellRuntime.lastRunStartTimestamp}
|
|
1245
1245
|
staleInputs={cellRuntime.staleInputs}
|
|
1246
|
-
interrupted={cellRuntime.interrupted}
|
|
1247
1246
|
/>
|
|
1248
1247
|
)}
|
|
1249
1248
|
<div className="shoulder-bottom hover-action">
|
|
1250
1249
|
{canDelete && (
|
|
1251
1250
|
<DeleteButton
|
|
1252
1251
|
connectionState={connection.state}
|
|
1253
|
-
status={
|
|
1252
|
+
status={execution.status}
|
|
1254
1253
|
onClick={() => {
|
|
1255
1254
|
if (
|
|
1256
1255
|
!loading &&
|
|
@@ -1298,7 +1297,7 @@ const SetupCellComponent = ({
|
|
|
1298
1297
|
<ConsoleOutput
|
|
1299
1298
|
consoleOutputs={cellRuntime.consoleOutputs}
|
|
1300
1299
|
stale={consoleOutputStale}
|
|
1301
|
-
interrupted={
|
|
1300
|
+
interrupted={interrupted}
|
|
1302
1301
|
// Don't show name
|
|
1303
1302
|
cellName={"_"}
|
|
1304
1303
|
onRefactorWithAI={handleRefactorWithAI}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { render, screen } from "@testing-library/react";
|
|
4
|
+
import { createStore, Provider } from "jotai";
|
|
5
|
+
import { describe, expect, it, vi } from "vitest";
|
|
6
|
+
import { MockExecutions } from "@/__mocks__/executions";
|
|
7
|
+
import { MockNotebook } from "@/__mocks__/notebook";
|
|
8
|
+
import { cellId } from "@/__tests__/branded";
|
|
9
|
+
import { parseAppConfig } from "@/core/config/config-schema";
|
|
10
|
+
import { GridLayoutRenderer } from "../grid-layout";
|
|
11
|
+
import type { GridLayout } from "../types";
|
|
12
|
+
|
|
13
|
+
vi.mock("@/components/editor/Output", () => ({
|
|
14
|
+
OutputArea: ({ cellId }: { cellId: string }) => (
|
|
15
|
+
<div data-testid={`output-${cellId}`} />
|
|
16
|
+
),
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
const ERRORED = cellId("errored");
|
|
20
|
+
|
|
21
|
+
function renderErroredCell(cells: GridLayout["cells"]) {
|
|
22
|
+
const store = createStore();
|
|
23
|
+
MockExecutions.seed(store, [MockExecutions.errored(ERRORED)]);
|
|
24
|
+
const notebook = MockNotebook.notebookState({
|
|
25
|
+
cellData: { [ERRORED]: { code: "raise ValueError('boom')" } },
|
|
26
|
+
cellRuntime: {
|
|
27
|
+
[ERRORED]: {
|
|
28
|
+
output: {
|
|
29
|
+
channel: "marimo-error",
|
|
30
|
+
mimetype: "application/vnd.marimo+error",
|
|
31
|
+
data: [MockNotebook.errors.exception("boom")],
|
|
32
|
+
timestamp: 1,
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
render(
|
|
38
|
+
<Provider store={store}>
|
|
39
|
+
<GridLayoutRenderer
|
|
40
|
+
appConfig={parseAppConfig({})}
|
|
41
|
+
mode="edit"
|
|
42
|
+
layout={{
|
|
43
|
+
columns: 12,
|
|
44
|
+
rowHeight: 20,
|
|
45
|
+
cells,
|
|
46
|
+
scrollableCells: new Set(),
|
|
47
|
+
cellSide: new Map(),
|
|
48
|
+
}}
|
|
49
|
+
setLayout={vi.fn()}
|
|
50
|
+
cells={[
|
|
51
|
+
{ ...notebook.cellData[ERRORED], ...notebook.cellRuntime[ERRORED] },
|
|
52
|
+
]}
|
|
53
|
+
/>
|
|
54
|
+
</Provider>,
|
|
55
|
+
);
|
|
56
|
+
return screen.getAllByTestId(`output-${ERRORED}`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
describe("GridLayoutRenderer", () => {
|
|
60
|
+
it("hides an errored cell placed in the grid", () => {
|
|
61
|
+
const [placed] = renderErroredCell([
|
|
62
|
+
{ i: ERRORED, x: 0, y: 0, w: 4, h: 4 },
|
|
63
|
+
]);
|
|
64
|
+
expect(placed.closest(".invisible")).not.toBeNull();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("keeps an errored cell visible in the Outputs tray", () => {
|
|
68
|
+
const outputs = renderErroredCell([]);
|
|
69
|
+
expect(screen.getByText("Outputs")).toBeTruthy();
|
|
70
|
+
expect(outputs.length).toBeGreaterThan(0);
|
|
71
|
+
for (const output of outputs) {
|
|
72
|
+
expect(output.closest(".invisible")).toBeNull();
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
});
|
|
@@ -37,6 +37,7 @@ import { NumberField } from "@/components/ui/number-field";
|
|
|
37
37
|
import { Switch } from "@/components/ui/switch";
|
|
38
38
|
import { outputIsLoading, outputIsStale } from "@/core/cells/cell";
|
|
39
39
|
import type { CellId } from "@/core/cells/ids";
|
|
40
|
+
import { useCellExecution } from "@/core/executions";
|
|
40
41
|
import type { AppMode } from "@/core/mode";
|
|
41
42
|
import { useIsDragging } from "@/hooks/useIsDragging";
|
|
42
43
|
import { cn } from "@/utils/cn";
|
|
@@ -227,11 +228,10 @@ export const GridLayoutRenderer: React.FC<Props> = ({
|
|
|
227
228
|
mode={mode}
|
|
228
229
|
cellId={cell.id}
|
|
229
230
|
output={cell.output}
|
|
230
|
-
|
|
231
|
-
|
|
231
|
+
staleInputs={cell.staleInputs}
|
|
232
|
+
hideWithoutValue={true}
|
|
232
233
|
isScrollable={isScrollable}
|
|
233
234
|
side={side}
|
|
234
|
-
hidden={cell.errored || cell.interrupted || cell.stopped}
|
|
235
235
|
/>
|
|
236
236
|
);
|
|
237
237
|
|
|
@@ -294,10 +294,9 @@ export const GridLayoutRenderer: React.FC<Props> = ({
|
|
|
294
294
|
mode={mode}
|
|
295
295
|
cellId={cell.id}
|
|
296
296
|
output={cell.output}
|
|
297
|
-
|
|
298
|
-
|
|
297
|
+
staleInputs={cell.staleInputs}
|
|
298
|
+
hideWithoutValue={false}
|
|
299
299
|
isScrollable={false}
|
|
300
|
-
hidden={false}
|
|
301
300
|
/>
|
|
302
301
|
);
|
|
303
302
|
})}
|
|
@@ -364,10 +363,9 @@ export const GridLayoutRenderer: React.FC<Props> = ({
|
|
|
364
363
|
mode={mode}
|
|
365
364
|
cellId={cell.id}
|
|
366
365
|
output={cell.output}
|
|
366
|
+
staleInputs={cell.staleInputs}
|
|
367
|
+
hideWithoutValue={false}
|
|
367
368
|
isScrollable={false}
|
|
368
|
-
status={cell.status}
|
|
369
|
-
stale={outputIsStale(cell, false)}
|
|
370
|
-
hidden={false}
|
|
371
369
|
/>
|
|
372
370
|
</div>
|
|
373
371
|
))}
|
|
@@ -377,31 +375,39 @@ export const GridLayoutRenderer: React.FC<Props> = ({
|
|
|
377
375
|
);
|
|
378
376
|
};
|
|
379
377
|
|
|
380
|
-
interface GridCellProps extends Pick<
|
|
378
|
+
interface GridCellProps extends Pick<
|
|
379
|
+
CellRuntimeState,
|
|
380
|
+
"output" | "staleInputs"
|
|
381
|
+
> {
|
|
381
382
|
className?: string;
|
|
382
383
|
code: string;
|
|
383
384
|
cellId: CellId;
|
|
384
385
|
mode: AppMode;
|
|
385
|
-
|
|
386
|
+
/** Whether a cell whose last run ended without a value keeps its place but shows nothing. */
|
|
387
|
+
hideWithoutValue: boolean;
|
|
386
388
|
isScrollable: boolean;
|
|
387
389
|
side?: GridLayoutCellSide;
|
|
388
|
-
stale: boolean;
|
|
389
390
|
}
|
|
390
391
|
|
|
391
392
|
const GridCell = memo(
|
|
392
393
|
({
|
|
393
394
|
output,
|
|
395
|
+
staleInputs,
|
|
394
396
|
cellId,
|
|
395
|
-
status,
|
|
396
397
|
mode,
|
|
397
398
|
code,
|
|
398
|
-
|
|
399
|
+
hideWithoutValue,
|
|
399
400
|
isScrollable,
|
|
400
401
|
side,
|
|
401
402
|
className,
|
|
402
|
-
stale,
|
|
403
403
|
}: GridCellProps) => {
|
|
404
|
-
const
|
|
404
|
+
const execution = useCellExecution(cellId);
|
|
405
|
+
const loading = outputIsLoading(execution.status);
|
|
406
|
+
const stale = outputIsStale(execution, staleInputs, false);
|
|
407
|
+
const hidden =
|
|
408
|
+
hideWithoutValue &&
|
|
409
|
+
execution.outcome !== null &&
|
|
410
|
+
execution.outcome !== "value";
|
|
405
411
|
|
|
406
412
|
const isOutputEmpty = output == null || output.data === "";
|
|
407
413
|
// If not reading, show code when there is no output
|