@marimo-team/islands 0.25.3-dev266 → 0.25.3-dev268

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. package/dist/{ConnectedDataExplorerComponent-C_hj7Poi.js → ConnectedDataExplorerComponent-eLHPh6uu.js} +9 -9
  2. package/dist/{ErrorBoundary-CvxlC3F1.js → ErrorBoundary-CQYAoD7X.js} +64 -69
  3. package/dist/{abnfDiagram-VCTEODGH-DzrZZXRD.js → abnfDiagram-VCTEODGH-hqd0IBtX.js} +2 -2
  4. package/dist/{any-language-editor-CFeWfC3o.js → any-language-editor-DnG26QXh.js} +1 -1
  5. package/dist/{architectureDiagram-5GKGNRK7-iF_snyFL.js → architectureDiagram-5GKGNRK7-BzLAFcc0.js} +1 -1
  6. package/dist/{strings-BCKpLFNU.js → assertNever-DIU3JJbt.js} +14 -14
  7. package/dist/{chat-ui-DFxNgzXF.js → chat-ui-BAnLgpTe.js} +1964 -1740
  8. package/dist/{chunk-IMKFNOWR-B7xytD4B.js → chunk-IMKFNOWR-DOeVvuyE.js} +1 -1
  9. package/dist/{chunk-SHT3W25Y-I8ZFP5L1.js → chunk-SHT3W25Y-DZoFI8wR.js} +1 -1
  10. package/dist/{chunk-SVP7TREG-Di-uAPdT.js → chunk-SVP7TREG-CWu86PSW.js} +1 -1
  11. package/dist/{chunk-TICWLB2K-BFpUy0ud.js → chunk-TICWLB2K-BrM0GoP3.js} +1 -1
  12. package/dist/{classDiagram-ZZMXUADV-Cqm4JGpx.js → classDiagram-ZZMXUADV-DYi4V2bB.js} +2 -2
  13. package/dist/{classDiagram-v2-VYDZK3BY-DFHRJpN3.js → classDiagram-v2-VYDZK3BY-tN7FIk-m.js} +2 -2
  14. package/dist/{code-visibility-SRM1JiD_.js → code-visibility-DcZ_RwaH.js} +44 -47
  15. package/dist/{common-yiRhZ6ZZ.js → common-BGeQilvh.js} +5846 -5519
  16. package/dist/{config-CmeG_Pb6.js → config-BsPruWMR.js} +2 -2
  17. package/dist/{createReducer-C_syF8Td.js → createReducer-DdtT_7ji.js} +1 -1
  18. package/dist/{cynefinDiagram-5FMLGOSQ-cuGOeTBC.js → cynefinDiagram-5FMLGOSQ-D5nCnDc-.js} +1 -1
  19. package/dist/{dagre-GXQ25YYZ-DJ76aC4k.js → dagre-GXQ25YYZ-BHkd7f_i.js} +1 -1
  20. package/dist/{data-grid-overlay-editor-DLTzcQ4O.js → data-grid-overlay-editor-Dc4lW4A3.js} +1 -1
  21. package/dist/{diagram-S7CK7UJ4-DLLc0sw7.js → diagram-S7CK7UJ4-D1ALppT2.js} +1 -1
  22. package/dist/{diagram-UQ7AKVKN-Tz_d6qmk.js → diagram-UQ7AKVKN-DLv0b83L.js} +1 -1
  23. package/dist/{diagram-VX7I27RA-D7SzPrDN.js → diagram-VX7I27RA-7XJ7J6qg.js} +1 -1
  24. package/dist/{diagram-Z3DM3KII-qr37XV58.js → diagram-Z3DM3KII-7M7zbHp_.js} +1 -1
  25. package/dist/{dialog-BtprQCUw.js → dialog-DzCj1qQv.js} +1 -1
  26. package/dist/{dropdown-menu-CkJ7ErY6.js → dropdown-menu-CdXE15an.js} +1214 -1208
  27. package/dist/{ebnfDiagram-PWID7BFC-BKOQb4J0.js → ebnfDiagram-PWID7BFC-DHH1qlmV.js} +2 -2
  28. package/dist/{erDiagram-RLTQ6QDP-CM15Vq68.js → erDiagram-RLTQ6QDP-fI_0dGef.js} +1 -1
  29. package/dist/{error-banner-DbRFOd2O.js → error-banner-DPc9eIw5.js} +3 -3
  30. package/dist/{error-entries-CMoaUX9V.js → error-entries-CxN-Nq3V.js} +524 -456
  31. package/dist/{feedback-button-DBUrE4OL.js → feedback-button-BqaeQcvX.js} +77 -77
  32. package/dist/{flowDiagram-HODETNUW-Dt3N5yFT.js → flowDiagram-HODETNUW-Brg9MA6i.js} +2 -2
  33. package/dist/{formats-gGospKWJ.js → formats-DbSvAPY-.js} +1 -1
  34. package/dist/{glide-data-editor-Cfv4HeOh.js → glide-data-editor-CiijPXA7.js} +2042 -2041
  35. package/dist/{glide-utils-Ba9WD2IN.js → glide-utils-BSw3SSHF.js} +29 -35
  36. package/dist/{globals-dIbBIR5-.js → globals-CpmSWKI_.js} +2 -2
  37. package/dist/{html-to-image-D_5CMreP.js → html-to-image-8sywF4my.js} +5452 -5751
  38. package/dist/{infoDiagram-27XIBGKW-C7axJMCl.js → infoDiagram-27XIBGKW-E9e403YT.js} +1 -1
  39. package/dist/{ishikawaDiagram-5VMMS53U-CdC9Eur7.js → ishikawaDiagram-5VMMS53U-C-RlYDRo.js} +1 -1
  40. package/dist/{kanban-definition-UXKFOSKX-BHnMIgD7.js → kanban-definition-UXKFOSKX-CLFl_uqK.js} +1 -1
  41. package/dist/main.js +6294 -6324
  42. package/dist/{mermaid-DFYN_PXd.js → mermaid-rd09e1JL.js} +35 -35
  43. package/dist/{mindmap-definition-YA3MSWOX-BagHoCGU.js → mindmap-definition-YA3MSWOX-BQ8TWtyB.js} +1 -1
  44. package/dist/{number-overlay-editor-CTiJp5Mj.js → number-overlay-editor-Ft4NiAEN.js} +1 -1
  45. package/dist/{pegDiagram-XKGWAZYB-8IIZczip.js → pegDiagram-XKGWAZYB-CN8UHFPi.js} +2 -2
  46. package/dist/pencil-DcT8AE0h.js +10 -0
  47. package/dist/{pieDiagram-E7YTZNPT-CE6dKBIr.js → pieDiagram-E7YTZNPT-DGuJ0EBd.js} +1 -1
  48. package/dist/{process-output-CGFNVC60.js → process-output-Dku5POvm.js} +3 -3
  49. package/dist/{railroadDiagram-O6MQD6OU-CkexPq3b.js → railroadDiagram-O6MQD6OU-BPDA8wy8.js} +2 -2
  50. package/dist/{requirementDiagram-BXWQKSXE-B0-OkuAe.js → requirementDiagram-BXWQKSXE-ChzLGbGW.js} +1 -1
  51. package/dist/{reveal-component-DH09ygHo.js → reveal-component-D0cfwbbl.js} +1094 -1094
  52. package/dist/{select-C33FVPPN.js → select-OxgozOC8.js} +1 -1
  53. package/dist/{sequenceDiagram-WJ2MYXX4-Dxysg8DX.js → sequenceDiagram-WJ2MYXX4-DFX-bE6O.js} +1 -1
  54. package/dist/{spec-DH1lmF7Q.js → spec-oJn0RhpQ.js} +2 -2
  55. package/dist/{stateDiagram-D77RDMKH-DTEN3-OJ.js → stateDiagram-D77RDMKH-BR7cI3wK.js} +2 -2
  56. package/dist/{stateDiagram-v2-MP3YSRHH-vktk3Xc5.js → stateDiagram-v2-MP3YSRHH-oGqB5UDI.js} +2 -2
  57. package/dist/{static-state-BldGd99T.js → static-state-CuV6Pc3a.js} +1 -1
  58. package/dist/style.css +1 -1
  59. package/dist/{swimlanes-42K2YHIH-Cjveh9OM.js → swimlanes-42K2YHIH-DwSZl1yC.js} +1 -1
  60. package/dist/{swimlanesDiagram-VR7AAH4N-Bdr7Ev_2.js → swimlanesDiagram-VR7AAH4N-BHlcd-Ri.js} +2 -2
  61. package/dist/{timeline-definition-24CTP7MA-KIuDcefn.js → timeline-definition-24CTP7MA-DMmP-G2O.js} +1 -1
  62. package/dist/{toast-CY1_Qv41.js → toast-DbBHmxmB.js} +2 -2
  63. package/dist/{useAsyncData-DsZMvNXn.js → useAsyncData-BYIAoMqn.js} +1 -1
  64. package/dist/{useDateFormatter-Dk_xFci4.js → useDateFormatter-ButWkdcK.js} +1 -1
  65. package/dist/{useDeepCompareMemoize-KMBBux8C.js → useDeepCompareMemoize-Bz0c5GN6.js} +1 -1
  66. package/dist/{useTheme-BZ2GGLgm.js → useTheme-DPLobSFs.js} +7 -7
  67. package/dist/{utils-DXkj82sK.js → utils-EBklsC5R.js} +31 -31
  68. package/dist/{vega-component-C-lqjV0q.js → vega-component-rxIxAAh9.js} +7 -7
  69. package/dist/{vennDiagram-4TSXK5OY-DrIOUHfb.js → vennDiagram-4TSXK5OY-Db8GtH1G.js} +1 -1
  70. package/dist/{wardleyDiagram-VM6X3IG4-CJNjf4OH.js → wardleyDiagram-VM6X3IG4-DmIiC3FU.js} +1 -1
  71. package/dist/{xychartDiagram-S5SC5T6Z-DEshFho-.js → xychartDiagram-S5SC5T6Z-DS9OPrNq.js} +1 -1
  72. package/package.json +1 -1
  73. package/src/__mocks__/executions.ts +195 -0
  74. package/src/__mocks__/notebook.ts +0 -1
  75. package/src/__tests__/CellStatus.test.tsx +19 -20
  76. package/src/components/chat/__tests__/chat-display.test.tsx +157 -86
  77. package/src/components/chat/__tests__/chat-utils.test.ts +42 -0
  78. package/src/components/chat/__tests__/turn-timing.test.ts +84 -0
  79. package/src/components/chat/__tests__/use-assistant-turn.test.ts +95 -0
  80. package/src/components/chat/__tests__/user-message.test.tsx +27 -0
  81. package/src/components/chat/acp/agent-panel.tsx +2 -0
  82. package/src/components/chat/agent-activity.tsx +132 -0
  83. package/src/components/chat/chat-display.tsx +125 -96
  84. package/src/components/chat/chat-panel.tsx +123 -106
  85. package/src/components/chat/chat-utils.ts +12 -24
  86. package/src/components/chat/tool-call/__tests__/tool-labels.test.ts +76 -0
  87. package/src/components/chat/tool-call/__tests__/tool-step.test.tsx +57 -0
  88. package/src/components/chat/tool-call/shared.ts +6 -0
  89. package/src/components/chat/tool-call/tool-call-view.tsx +22 -76
  90. package/src/components/chat/tool-call/tool-labels.ts +172 -0
  91. package/src/components/chat/tool-call/tool-step.tsx +145 -0
  92. package/src/components/chat/turn-timing.ts +73 -0
  93. package/src/components/chat/use-assistant-turn.ts +65 -0
  94. package/src/components/chat/use-chat-generation.ts +5 -4
  95. package/src/components/chat/user-message.tsx +145 -0
  96. package/src/components/dependency-graph/minimap-content.tsx +3 -2
  97. package/src/components/editor/__tests__/data-attributes.test.tsx +0 -7
  98. package/src/components/editor/__tests__/output-persistence.test.tsx +2 -2
  99. package/src/components/editor/actions/__tests__/useCellActionButton.test.tsx +19 -1
  100. package/src/components/editor/actions/useCellActionButton.tsx +10 -3
  101. package/src/components/editor/ai/completion-handlers.tsx +0 -37
  102. package/src/components/editor/alerts/stdin-blocking-alert.tsx +3 -1
  103. package/src/components/editor/cell/CellStatus.tsx +23 -19
  104. package/src/components/editor/cell/RunButton.tsx +16 -6
  105. package/src/components/editor/cell/StagedAICell.tsx +51 -144
  106. package/src/components/editor/cell/__tests__/useRunCells.test.ts +45 -0
  107. package/src/components/editor/cell/cell-context-menu.tsx +4 -1
  108. package/src/components/editor/cell/code/cell-editor.tsx +7 -5
  109. package/src/components/editor/cell/collapse.tsx +4 -0
  110. package/src/components/editor/cell/useRunCells.ts +18 -3
  111. package/src/components/editor/chrome/wrapper/__tests__/pending-ai-cells.test.tsx +193 -32
  112. package/src/components/editor/chrome/wrapper/app-chrome.tsx +2 -2
  113. package/src/components/editor/chrome/wrapper/pending-ai-cells.tsx +145 -81
  114. package/src/components/editor/chrome/wrapper/pending-ai-changes-badge.tsx +25 -0
  115. package/src/components/editor/chrome/wrapper/sidebar.tsx +8 -4
  116. package/src/components/editor/header/status.tsx +4 -2
  117. package/src/components/editor/links/cell-link.tsx +4 -4
  118. package/src/components/editor/navigation/navigation.ts +3 -2
  119. package/src/components/editor/notebook-cell.tsx +85 -94
  120. package/src/components/editor/renderers/grid-layout/__tests__/grid-layout.test.tsx +75 -0
  121. package/src/components/editor/renderers/grid-layout/grid-layout.tsx +22 -16
  122. package/src/components/editor/renderers/slides-layout/__tests__/plugin.test.ts +0 -1
  123. package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +12 -33
  124. package/src/components/editor/stdin-blocking-alert.tsx +3 -1
  125. package/src/components/scratchpad/scratchpad.tsx +4 -1
  126. package/src/components/slides/minimap.tsx +4 -2
  127. package/src/components/slides/reveal-component.tsx +14 -10
  128. package/src/components/slides/slide-cell-view.tsx +28 -24
  129. package/src/components/slides/slide.tsx +3 -4
  130. package/src/components/ui/toast.tsx +1 -1
  131. package/src/core/ai/__tests__/staged-cells.test.ts +74 -126
  132. package/src/core/ai/staged-cells.ts +48 -38
  133. package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +0 -389
  134. package/src/core/cells/__tests__/cell.test.ts +49 -154
  135. package/src/core/cells/__tests__/cells.test.ts +18 -127
  136. package/src/core/cells/__tests__/pending-delete-service.test.tsx +11 -20
  137. package/src/core/cells/__tests__/utils.test.ts +75 -247
  138. package/src/core/cells/actions.ts +5 -2
  139. package/src/core/cells/cell.ts +31 -133
  140. package/src/core/cells/cells.ts +14 -29
  141. package/src/core/cells/document.ts +3 -3
  142. package/src/core/cells/focus.ts +27 -6
  143. package/src/core/cells/layout.ts +1 -1
  144. package/src/core/cells/pending-delete-service.ts +5 -7
  145. package/src/core/cells/scrollCellIntoView.ts +6 -0
  146. package/src/core/cells/types.ts +13 -36
  147. package/src/core/cells/utils.ts +37 -68
  148. package/src/core/codemirror/placeholder/__tests__/extensions.test.ts +59 -0
  149. package/src/core/codemirror/placeholder/extensions.ts +23 -0
  150. package/src/core/edit-app.tsx +2 -1
  151. package/src/core/executions/__tests__/atoms.test.ts +99 -0
  152. package/src/core/executions/__tests__/select.test.ts +268 -0
  153. package/src/core/executions/atoms.ts +206 -0
  154. package/src/core/executions/hooks.ts +31 -0
  155. package/src/core/executions/index.ts +31 -0
  156. package/src/core/executions/select.ts +175 -0
  157. package/src/core/export/__tests__/hooks.test.ts +0 -5
  158. package/src/core/islands/bootstrap.ts +4 -1
  159. package/src/core/islands/components/output-wrapper.tsx +5 -5
  160. package/src/core/kernel/handlers.ts +2 -11
  161. package/src/core/kernel/state.ts +0 -15
  162. package/src/core/lifecycle/render-policy.ts +12 -26
  163. package/src/core/network/__tests__/requests-lazy.test.ts +5 -5
  164. package/src/core/network/requests-lazy.ts +3 -3
  165. package/src/core/notebook/operations.ts +1 -1
  166. package/src/core/run-app.tsx +2 -1
  167. package/src/core/runtime/__tests__/adapter.test.ts +3 -3
  168. package/src/core/runtime/adapter.ts +4 -4
  169. package/src/core/session/__tests__/useNotebookSession.hook.test.tsx +34 -0
  170. package/src/core/session/useNotebookSession.tsx +10 -5
  171. package/src/core/wasm/bridge.ts +1 -1
  172. package/src/css/app/Cell.css +7 -36
  173. package/src/plugins/core/registerReactComponent.tsx +4 -13
  174. package/src/plugins/impl/chat/chat-ui.tsx +2 -3
  175. package/src/plugins/impl/common/error-banner.tsx +5 -3
  176. package/src/stories/cell-status.stories.tsx +17 -10
  177. package/src/stories/cell.stories.tsx +0 -31
  178. package/src/stories/layout/vertical/one-column.stories.tsx +0 -9
  179. package/src/utils/time.ts +10 -0
  180. package/src/components/chat/reasoning-accordion.tsx +0 -61
  181. package/src/components/chat/tool-call/tool-activity-group.tsx +0 -49
  182. package/src/components/chat/tool-call/tool-error-card.tsx +0 -81
  183. package/src/components/chat/tool-call/tool-history-row.tsx +0 -147
  184. package/src/components/icons/loading-ellipsis.tsx +0 -46
@@ -5,7 +5,7 @@ import { HourglassIcon, UnlinkIcon } from "lucide-react";
5
5
  import React from "react";
6
6
  import { Tooltip } from "@/components/ui/tooltip";
7
7
  import { notebookScrollToRunning } from "@/core/cells/actions";
8
- import { onlyScratchpadIsRunningAtom } from "@/core/cells/cells";
8
+ import { cellExecutionInputsAtom, onlyScratchRunning } from "@/core/executions";
9
9
  import { viewStateAtom } from "@/core/mode";
10
10
  import {
11
11
  type ConnectionStatus,
@@ -77,7 +77,9 @@ const DisconnectedIcon: React.FC<{ onReconnect?: () => void }> = ({
77
77
  };
78
78
 
79
79
  const RunningIcon = () => {
80
- const scratchpadOnly = useAtomValue(onlyScratchpadIsRunningAtom);
80
+ const scratchpadOnly = onlyScratchRunning(
81
+ useAtomValue(cellExecutionInputsAtom).executions,
82
+ );
81
83
  const tooltip = scratchpadOnly
82
84
  ? "Scratchpad is running"
83
85
  : "Jump to running cell";
@@ -5,6 +5,7 @@ import { useCellIds, useCellNames } from "@/core/cells/cells";
5
5
  import { useCellActions } from "@/core/cells/actions";
6
6
  import { type CellId, HTMLCellId, SCRATCH_CELL_ID } from "@/core/cells/ids";
7
7
  import { displayCellName } from "@/core/cells/names";
8
+ import { scrollToCell } from "@/core/cells/scrollCellIntoView";
8
9
  import { goToCellLine } from "@/core/codemirror/go-to-definition/utils";
9
10
  import { useFilename } from "@/core/saving/filename";
10
11
  import { cn } from "@/utils/cn";
@@ -106,16 +107,15 @@ export function scrollAndHighlightCell(
106
107
  skipScroll?: boolean,
107
108
  ): boolean {
108
109
  const cellHtmlId = HTMLCellId.create(cellId);
109
- const cell: HTMLElement | null = document.getElementById(cellHtmlId);
110
+ const cell = skipScroll
111
+ ? document.getElementById(cellHtmlId)
112
+ : scrollToCell(cellId);
110
113
  const isCellErrored = cell?.classList.contains("has-error");
111
114
 
112
115
  if (cell === null) {
113
116
  Logger.error(`Cell ${cellHtmlId} not found on page.`);
114
117
  return false;
115
118
  }
116
- if (!skipScroll) {
117
- cell.scrollIntoView({ behavior: "smooth", block: "center" });
118
- }
119
119
 
120
120
  if (variant === "destructive" || (isCellErrored && variant === undefined)) {
121
121
  cell.classList.add("error-outline");
@@ -22,6 +22,7 @@ import {
22
22
  visibleNeighbor,
23
23
  } from "@/core/cells/layout";
24
24
  import { useCellActions } from "@/core/cells/actions";
25
+ import { cellExecutionInputsAtom, cellExecutionView } from "@/core/executions";
25
26
  import { useNotebookDocumentEdit } from "@/core/notebook/document";
26
27
  import { useCellFocusActions } from "@/core/cells/focus";
27
28
  import type { CellId } from "@/core/cells/ids";
@@ -582,9 +583,9 @@ export function useCellNavigationProps(
582
583
  selectedCells.size >= 2 ? [...selectedCells] : [cellId];
583
584
 
584
585
  // Cannot delete running cells
585
- const notebook = store.get(notebookAtom);
586
+ const executions = store.get(cellExecutionInputsAtom);
586
587
  const hasRunningCell = cellIds.some((id) => {
587
- const { status } = notebook.cellRuntime[id];
588
+ const { status } = cellExecutionView(executions, id);
588
589
  return status === "running" || status === "queued";
589
590
  });
590
591
 
@@ -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 type { CellConfig, RuntimeState } from "@/core/network/types";
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,
@@ -78,10 +82,7 @@ import { CollapsedCellBanner, CollapseToggle } from "./cell/collapse";
78
82
  import { DeleteButton } from "./cell/DeleteButton";
79
83
  import { PendingDeleteConfirmation } from "./cell/PendingDeleteConfirmation";
80
84
  import { RunButton } from "./cell/RunButton";
81
- import {
82
- StagedAICellBackground,
83
- StagedAICellFooter,
84
- } from "./cell/StagedAICell";
85
+ import { StagedAIDeletionMarker } from "./cell/StagedAICell";
85
86
  import { useDeleteCellCallback } from "./cell/useDeleteCell";
86
87
  import { useRunCell } from "./cell/useRunCells";
87
88
  import { cellDomProps } from "./common";
@@ -318,6 +319,10 @@ const ReadonlyCellComponent = forwardRef(
318
319
  const { cellId } = props;
319
320
  const cellData = useCellData(cellId);
320
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";
321
326
 
322
327
  const className = clsx("marimo-cell", "hover-actions-parent z-10", {
323
328
  published: true,
@@ -325,9 +330,9 @@ const ReadonlyCellComponent = forwardRef(
325
330
 
326
331
  // Hide the output if it's an error or stopped.
327
332
  const hidden = shouldHidePublishedCell({
328
- errored: cellRuntime.errored,
329
- interrupted: cellRuntime.interrupted,
330
- stopped: cellRuntime.stopped,
333
+ errored,
334
+ interrupted,
335
+ stopped,
331
336
  output: cellRuntime.output,
332
337
  showErrorTracebacks: false,
333
338
  });
@@ -349,8 +354,12 @@ const ReadonlyCellComponent = forwardRef(
349
354
  className={CSSClasses.outputArea}
350
355
  cellId={cellId}
351
356
  output={cellRuntime.output}
352
- stale={outputIsStale(cellRuntime, cellData.edited)}
353
- loading={outputIsLoading(cellRuntime.status)}
357
+ stale={outputIsStale(
358
+ execution,
359
+ cellRuntime.staleInputs,
360
+ cellData.edited,
361
+ )}
362
+ loading={outputIsLoading(execution.status)}
354
363
  />
355
364
  </div>
356
365
  );
@@ -377,6 +386,10 @@ const EditableCellComponent = ({
377
386
  const cellRef = useRef<HTMLDivElement>(null);
378
387
  const cellData = useCellData(cellId);
379
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";
380
393
  const cellActionDropdownRef = useRef<CellActionsDropdownHandle>(null);
381
394
  // DOM node where the editorView will be mounted
382
395
  const editorViewParentRef = useRef<HTMLDivElement>(null);
@@ -392,23 +405,17 @@ const EditableCellComponent = ({
392
405
 
393
406
  const [languageAdapter, setLanguageAdapter] = useState<LanguageAdapterType>();
394
407
 
395
- const uninstantiated = isUninstantiated({
396
- executionTime: cellRuntime.runElapsedTimeMs ?? cellData.lastExecutionTime,
397
- status: cellRuntime.status,
398
- errored: cellRuntime.errored,
399
- interrupted: cellRuntime.interrupted,
400
- stopped: cellRuntime.stopped,
401
- });
408
+ const uninstantiated = isUninstantiated(execution);
402
409
 
403
410
  const needsRun = cellNeedsRun({
404
411
  edited: cellData.edited,
405
- interrupted: cellRuntime.interrupted,
412
+ interrupted,
406
413
  staleInputs: cellRuntime.staleInputs,
407
414
  disabled: cellData.config.disabled,
408
- status: cellRuntime.status,
415
+ blocked: execution.blocked,
409
416
  });
410
417
 
411
- const loading = outputIsLoading(cellRuntime.status);
418
+ const loading = outputIsLoading(execution.status);
412
419
 
413
420
  const isPresenting = mode === "present";
414
421
 
@@ -417,9 +424,9 @@ const EditableCellComponent = ({
417
424
  const presentHidden =
418
425
  isPresenting &&
419
426
  shouldHidePublishedCell({
420
- errored: cellRuntime.errored,
421
- interrupted: cellRuntime.interrupted,
422
- stopped: cellRuntime.stopped,
427
+ errored,
428
+ interrupted,
429
+ stopped,
423
430
  output: cellRuntime.output,
424
431
  showErrorTracebacks: userConfig.runtime.show_tracebacks ?? false,
425
432
  });
@@ -435,10 +442,10 @@ const EditableCellComponent = ({
435
442
  // console output is cleared immediately on run, so check for queued instead
436
443
  // of loading to determine staleness
437
444
  const consoleOutputStale =
438
- (cellRuntime.status === "queued" ||
445
+ (execution.status === "queued" ||
439
446
  cellData.edited ||
440
447
  cellRuntime.staleInputs) &&
441
- !cellRuntime.interrupted;
448
+ !interrupted;
442
449
 
443
450
  // This getter is called during render, so useEvent cannot be used here.
444
451
  // oxlint-disable-next-line react/preserve-manual-memoization
@@ -475,7 +482,8 @@ const EditableCellComponent = ({
475
482
  // While presenting, pending edits do not dim the output as stale,
476
483
  // matching the read view.
477
484
  const isStaleCell = outputIsStale(
478
- cellRuntime,
485
+ execution,
486
+ cellRuntime.staleInputs,
479
487
  cellData.edited && !isPresenting,
480
488
  );
481
489
  const hasConsoleOutput = cellRuntime.consoleOutputs.length > 0;
@@ -571,7 +579,7 @@ const EditableCellComponent = ({
571
579
  cellId={cellId}
572
580
  output={cellRuntime.output}
573
581
  stale={isStaleCell}
574
- loading={outputIsLoading(cellRuntime.status)}
582
+ loading={outputIsLoading(execution.status)}
575
583
  />
576
584
  </div>
577
585
  );
@@ -582,18 +590,18 @@ const EditableCellComponent = ({
582
590
  isPresenting
583
591
  ? // Match the published styling of the read view
584
592
  publishedCellClasses({
585
- errored: cellRuntime.errored,
586
- stopped: cellRuntime.stopped,
593
+ errored,
594
+ stopped,
587
595
  })
588
596
  : {
589
597
  "z-10": true,
590
598
  interactive: true,
591
599
  ...cellStatusClasses({
592
600
  needsRun,
593
- errored: cellRuntime.errored,
594
- stopped: cellRuntime.stopped,
601
+ errored,
602
+ stopped,
595
603
  disabled: cellData.config.disabled,
596
- status: cellRuntime.status,
604
+ blocked: execution.blocked,
597
605
  }),
598
606
  borderless:
599
607
  isMarkdownCodeHidden &&
@@ -620,7 +628,7 @@ const EditableCellComponent = ({
620
628
  if (cellData.config.disabled) {
621
629
  return "This cell is disabled";
622
630
  }
623
- if (cellRuntime.status === "disabled-transitively") {
631
+ if (execution.blocked) {
624
632
  return "This cell has a disabled ancestor";
625
633
  }
626
634
  return undefined;
@@ -638,7 +646,7 @@ const EditableCellComponent = ({
638
646
  <SortableCell
639
647
  tabIndex={-1}
640
648
  ref={cellRef}
641
- data-status={cellRuntime.status}
649
+ data-status={execution.status}
642
650
  onBlur={closeCompletionHandler}
643
651
  onKeyDown={resumeCompletionHandler}
644
652
  cellId={cellId}
@@ -674,11 +682,12 @@ const EditableCellComponent = ({
674
682
  data-has-output-above={hasOutputAbove}
675
683
  data-hidden={isMarkdownCodeHidden}
676
684
  >
677
- <StagedAICellBackground cellId={cellId} />
685
+ <StagedAIDeletionMarker cellId={cellId} />
678
686
  <div className="absolute right-2 -top-4 z-10">
679
687
  <CellToolbar
680
688
  edited={cellData.edited}
681
- status={cellRuntime.status}
689
+ status={execution.status}
690
+ blocked={execution.blocked}
682
691
  cellConfig={cellData.config}
683
692
  needsRun={needsRun}
684
693
  hasOutput={hasOutput}
@@ -696,7 +705,7 @@ const EditableCellComponent = ({
696
705
  id={cellId}
697
706
  code={cellData.code}
698
707
  config={cellData.config}
699
- status={cellRuntime.status}
708
+ status={execution.status}
700
709
  runCell={runCell}
701
710
  setEditorView={setEditorView}
702
711
  userConfig={userConfig}
@@ -717,21 +726,17 @@ const EditableCellComponent = ({
717
726
  isMarkdownCodeHidden && cellOutput === "below" && "top-14",
718
727
  )}
719
728
  edited={cellData.edited}
720
- status={cellRuntime.status}
729
+ execution={execution}
721
730
  isCellStatusInline={isCellStatusInline}
722
731
  uninstantiated={uninstantiated}
723
732
  disabled={cellData.config.disabled}
724
- runElapsedTimeMs={cellRuntime.runElapsedTimeMs}
725
- runStartTimestamp={cellRuntime.runStartTimestamp}
726
- lastRunStartTimestamp={cellRuntime.lastRunStartTimestamp}
727
733
  staleInputs={cellRuntime.staleInputs}
728
- interrupted={cellRuntime.interrupted}
729
734
  />
730
735
  )}
731
736
  <div className="shoulder-bottom hover-action">
732
737
  {canDelete && isCellCodeShown && (
733
738
  <DeleteButton
734
- status={cellRuntime.status}
739
+ status={execution.status}
735
740
  connectionState={connection.state}
736
741
  onClick={() => {
737
742
  if (
@@ -748,7 +753,7 @@ const EditableCellComponent = ({
748
753
  {!isPresenting && (
749
754
  <SqlValidationErrorBanner
750
755
  cellId={cellId}
751
- hide={cellRuntime.errored && !isStaleCell}
756
+ hide={errored && !isStaleCell}
752
757
  />
753
758
  )}
754
759
  {cellOutput === "below" && (outputArea || emptyMarkdownPlaceholder)}
@@ -814,7 +819,7 @@ const EditableCellComponent = ({
814
819
  <ConsoleOutput
815
820
  consoleOutputs={cellRuntime.consoleOutputs}
816
821
  stale={consoleOutputStale}
817
- interrupted={cellRuntime.interrupted}
822
+ interrupted={interrupted}
818
823
  // Empty name if serialization triggered
819
824
  cellName={cellRuntime.serialization ? "_" : cellData.name}
820
825
  onRefactorWithAI={handleRefactorWithAI}
@@ -837,7 +842,6 @@ const EditableCellComponent = ({
837
842
  />
838
843
  )}
839
844
  <PendingDeleteConfirmation cellId={cellId} />
840
- {!isPresenting && <StagedAICellFooter cellId={cellId} />}
841
845
  </div>
842
846
  {isCollapsed && !isPresenting && (
843
847
  <CollapsedCellBanner
@@ -857,41 +861,33 @@ const CellRightSideActions = memo(
857
861
  className?: string;
858
862
  disabled: boolean | undefined;
859
863
  edited: boolean;
860
- interrupted: boolean;
864
+ execution: CellExecutionView;
861
865
  isCellStatusInline: boolean;
862
- lastRunStartTimestamp: Seconds | null;
863
- runElapsedTimeMs: Milliseconds | null;
864
- runStartTimestamp: Seconds | null;
865
866
  staleInputs: boolean;
866
- status: RuntimeState;
867
867
  uninstantiated: boolean;
868
868
  }) => {
869
869
  const {
870
870
  className,
871
871
  disabled = false,
872
872
  edited,
873
- interrupted,
873
+ execution,
874
874
  isCellStatusInline,
875
- lastRunStartTimestamp,
876
- runElapsedTimeMs,
877
- runStartTimestamp,
878
875
  staleInputs,
879
- status,
880
876
  uninstantiated,
881
877
  } = props;
882
878
 
883
879
  const cellStatusComponent = (
884
880
  <CellStatusComponent
885
- status={status}
881
+ status={execution.status}
886
882
  staleInputs={staleInputs}
887
- interrupted={interrupted}
883
+ interrupted={execution.outcome === "interrupted"}
888
884
  editing={true}
889
885
  edited={edited}
890
886
  disabled={disabled}
891
- elapsedTime={runElapsedTimeMs}
892
- runStartTimestamp={runStartTimestamp}
887
+ elapsedTime={execution.duration}
888
+ startedAt={execution.startedAt}
889
+ blocked={execution.blocked}
893
890
  uninstantiated={uninstantiated}
894
- lastRunStartTimestamp={lastRunStartTimestamp}
895
891
  />
896
892
  );
897
893
 
@@ -964,7 +960,8 @@ CellLeftSideActions.displayName = "CellLeftSideActions";
964
960
 
965
961
  interface CellToolbarProps {
966
962
  edited: boolean;
967
- status: RuntimeState;
963
+ status: CellRunStatus;
964
+ blocked: boolean;
968
965
  cellConfig: CellConfig;
969
966
  needsRun: boolean;
970
967
  hasOutput: boolean;
@@ -981,6 +978,7 @@ const CellToolbar = memo(
981
978
  ({
982
979
  edited,
983
980
  status,
981
+ blocked,
984
982
  cellConfig,
985
983
  needsRun,
986
984
  hasOutput,
@@ -1006,6 +1004,7 @@ const CellToolbar = memo(
1006
1004
  onClick={onRun}
1007
1005
  connectionState={connection.state}
1008
1006
  status={status}
1007
+ blocked={blocked}
1009
1008
  config={cellConfig}
1010
1009
  needsRun={needsRun}
1011
1010
  />
@@ -1015,6 +1014,7 @@ const CellToolbar = memo(
1015
1014
  ref={cellActionDropdownRef}
1016
1015
  cellId={cellId}
1017
1016
  status={status}
1017
+ blocked={blocked}
1018
1018
  getEditorView={getEditorView}
1019
1019
  name={name}
1020
1020
  config={cellConfig}
@@ -1058,6 +1058,10 @@ const SetupCellComponent = ({
1058
1058
  const cellRef = useRef<HTMLDivElement>(null);
1059
1059
  const cellData = useCellData(cellId);
1060
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";
1061
1065
  const cellActionDropdownRef = useRef<CellActionsDropdownHandle>(null);
1062
1066
  // DOM node where the editorView will be mounted
1063
1067
  const editorViewParentRef = useRef<HTMLDivElement>(null);
@@ -1069,31 +1073,25 @@ const SetupCellComponent = ({
1069
1073
  const setAiCompletionCell = useSetAtom(aiCompletionCellAtom);
1070
1074
  const runCell = useRunCell(cellId);
1071
1075
 
1072
- const uninstantiated = isUninstantiated({
1073
- executionTime: cellRuntime.runElapsedTimeMs ?? cellData.lastExecutionTime,
1074
- status: cellRuntime.status,
1075
- errored: cellRuntime.errored,
1076
- interrupted: cellRuntime.interrupted,
1077
- stopped: cellRuntime.stopped,
1078
- });
1076
+ const uninstantiated = isUninstantiated(execution);
1079
1077
 
1080
1078
  const needsRun = cellNeedsRun({
1081
1079
  edited: cellData.edited,
1082
- interrupted: cellRuntime.interrupted,
1080
+ interrupted,
1083
1081
  staleInputs: cellRuntime.staleInputs,
1084
1082
  disabled: cellData.config.disabled,
1085
- status: cellRuntime.status,
1083
+ blocked: execution.blocked,
1086
1084
  });
1087
1085
  const loading =
1088
- cellRuntime.status === "running" || cellRuntime.status === "queued";
1086
+ execution.status === "running" || execution.status === "queued";
1089
1087
 
1090
1088
  // console output is cleared immediately on run, so check for queued instead
1091
1089
  // of loading to determine staleness
1092
1090
  const consoleOutputStale =
1093
- (cellRuntime.status === "queued" ||
1091
+ (execution.status === "queued" ||
1094
1092
  cellData.edited ||
1095
1093
  cellRuntime.staleInputs) &&
1096
- !cellRuntime.interrupted;
1094
+ !interrupted;
1097
1095
 
1098
1096
  // This getter is called during render, so useEvent cannot be used here.
1099
1097
  // oxlint-disable-next-line react/preserve-manual-memoization
@@ -1127,10 +1125,10 @@ const SetupCellComponent = ({
1127
1125
  interactive: true,
1128
1126
  ...cellStatusClasses({
1129
1127
  needsRun,
1130
- errored: cellRuntime.errored,
1131
- stopped: cellRuntime.stopped,
1128
+ errored,
1129
+ stopped,
1132
1130
  disabled: cellData.config.disabled,
1133
- status: cellRuntime.status,
1131
+ blocked: execution.blocked,
1134
1132
  }),
1135
1133
  });
1136
1134
 
@@ -1151,7 +1149,7 @@ const SetupCellComponent = ({
1151
1149
  if (cellData.config.disabled) {
1152
1150
  return "This cell is disabled";
1153
1151
  }
1154
- if (cellRuntime.status === "disabled-transitively") {
1152
+ if (execution.blocked) {
1155
1153
  return "This cell has a disabled ancestor";
1156
1154
  }
1157
1155
  return undefined;
@@ -1178,7 +1176,7 @@ const SetupCellComponent = ({
1178
1176
  mounted) while presenting. */}
1179
1177
  <div hidden={isPresenting}>
1180
1178
  <div
1181
- data-status={cellRuntime.status}
1179
+ data-status={execution.status}
1182
1180
  ref={cellRef}
1183
1181
  {...mergeProps(navigationProps, {
1184
1182
  className: cn(
@@ -1198,14 +1196,12 @@ const SetupCellComponent = ({
1198
1196
  data-has-output-above={false}
1199
1197
  data-hidden={!isCellCodeShown}
1200
1198
  >
1201
- <StagedAICellBackground
1202
- cellId={cellId}
1203
- className="mo-ai-setup-cell"
1204
- />
1199
+ <StagedAIDeletionMarker cellId={cellId} />
1205
1200
  <div className="absolute right-2 -top-4 z-10">
1206
1201
  <CellToolbar
1207
1202
  edited={cellData.edited}
1208
- status={cellRuntime.status}
1203
+ status={execution.status}
1204
+ blocked={execution.blocked}
1209
1205
  cellConfig={cellData.config}
1210
1206
  needsRun={needsRun}
1211
1207
  hasOutput={hasOutput}
@@ -1224,7 +1220,7 @@ const SetupCellComponent = ({
1224
1220
  id={cellId}
1225
1221
  code={cellData.code}
1226
1222
  config={cellData.config}
1227
- status={cellRuntime.status}
1223
+ status={execution.status}
1228
1224
  runCell={runCell}
1229
1225
  setEditorView={setEditorView}
1230
1226
  userConfig={userConfig}
@@ -1242,22 +1238,18 @@ const SetupCellComponent = ({
1242
1238
  {!isPresenting && (
1243
1239
  <CellRightSideActions
1244
1240
  edited={cellData.edited}
1245
- status={cellRuntime.status}
1241
+ execution={execution}
1246
1242
  isCellStatusInline={false}
1247
1243
  uninstantiated={uninstantiated}
1248
1244
  disabled={cellData.config.disabled}
1249
- runElapsedTimeMs={cellRuntime.runElapsedTimeMs}
1250
- runStartTimestamp={cellRuntime.runStartTimestamp}
1251
- lastRunStartTimestamp={cellRuntime.lastRunStartTimestamp}
1252
1245
  staleInputs={cellRuntime.staleInputs}
1253
- interrupted={cellRuntime.interrupted}
1254
1246
  />
1255
1247
  )}
1256
1248
  <div className="shoulder-bottom hover-action">
1257
1249
  {canDelete && (
1258
1250
  <DeleteButton
1259
1251
  connectionState={connection.state}
1260
- status={cellRuntime.status}
1252
+ status={execution.status}
1261
1253
  onClick={() => {
1262
1254
  if (
1263
1255
  !loading &&
@@ -1305,7 +1297,7 @@ const SetupCellComponent = ({
1305
1297
  <ConsoleOutput
1306
1298
  consoleOutputs={cellRuntime.consoleOutputs}
1307
1299
  stale={consoleOutputStale}
1308
- interrupted={cellRuntime.interrupted}
1300
+ interrupted={interrupted}
1309
1301
  // Don't show name
1310
1302
  cellName={"_"}
1311
1303
  onRefactorWithAI={handleRefactorWithAI}
@@ -1326,7 +1318,6 @@ const SetupCellComponent = ({
1326
1318
  cellId={cellId}
1327
1319
  debuggerActive={cellRuntime.debuggerActive}
1328
1320
  />
1329
- <StagedAICellFooter cellId={cellId} />
1330
1321
  </div>
1331
1322
  </div>
1332
1323
  </CellActionsContextMenu>
@@ -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
+ });