@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
@@ -1,7 +1,8 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
  import { HardDriveDownloadIcon, PlayIcon } from "lucide-react";
3
3
  import type { JSX } from "react";
4
- import type { CellConfig, RuntimeState } from "@/core/network/types";
4
+ import type { CellRunStatus } from "@/core/executions";
5
+ import type { CellConfig } from "@/core/network/types";
5
6
  import {
6
7
  getConnectionTooltip,
7
8
  isAppInteractionDisabled,
@@ -35,20 +36,29 @@ function computeColor({
35
36
 
36
37
  export const RunButton = (props: {
37
38
  edited: boolean;
38
- status: RuntimeState;
39
+ status: CellRunStatus;
40
+ /** Whether a cell it depends on is disabled. */
41
+ blocked: boolean;
39
42
  needsRun: boolean;
40
43
  connectionState: ConnectionState;
41
44
  config: CellConfig;
42
45
  onClick?: () => void;
43
46
  }): JSX.Element => {
44
- const { onClick, connectionState, needsRun, status, config, edited } = props;
47
+ const {
48
+ onClick,
49
+ connectionState,
50
+ needsRun,
51
+ status,
52
+ blocked,
53
+ config,
54
+ edited,
55
+ } = props;
45
56
 
46
- const blockedStatus = status === "disabled-transitively";
47
57
  const loading = status === "running" || status === "queued";
48
58
  const inactive =
49
59
  isAppInteractionDisabled(connectionState) ||
50
60
  loading ||
51
- (!config.disabled && blockedStatus && !edited);
61
+ (!config.disabled && blocked && !edited);
52
62
  const variant = computeColor({
53
63
  connectionState,
54
64
  needsRun,
@@ -69,7 +79,7 @@ export const RunButton = (props: {
69
79
  </ToolbarItem>
70
80
  );
71
81
  }
72
- if (!config.disabled && blockedStatus && !edited) {
82
+ if (!config.disabled && blocked && !edited) {
73
83
  return (
74
84
  <ToolbarItem
75
85
  disabled={inactive}
@@ -1,24 +1,17 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
- import { useAtomValue, useStore } from "jotai";
4
- import { SparklesIcon } from "lucide-react";
5
- import { Tooltip } from "@/components/ui/tooltip";
3
+ import { atom, useAtomValue, useStore } from "jotai";
4
+ import { useMemo } from "react";
6
5
  import {
7
6
  type Edit,
7
+ stagedAICellsAtom,
8
8
  useStagedAICell,
9
9
  useStagedCells,
10
10
  } from "@/core/ai/staged-cells";
11
11
  import { chatGenerationInProgressAtom } from "@/core/ai/state";
12
- import { getCellEditorView } from "@/core/cells/cells";
13
12
  import type { CellId } from "@/core/cells/ids";
14
- import { prepareCellCodeUpdate } from "@/core/codemirror/language/cell-code";
15
- import { cn } from "@/utils/cn";
16
- import { Logger } from "@/utils/Logger";
17
- import {
18
- AI_EDIT_ACTION_LABELS,
19
- CompletionActionsCellFooter,
20
- } from "../ai/completion-handlers";
21
- import { useRunCell } from "./useRunCells";
13
+ import { AI_EDIT_ACTION_LABELS } from "../ai/completion-handlers";
14
+ import type { ActionButton } from "../actions/types";
22
15
 
23
16
  const actionLabels: Record<Edit["type"], { accept: string; decline: string }> =
24
17
  {
@@ -27,142 +20,56 @@ const actionLabels: Record<Edit["type"], { accept: string; decline: string }> =
27
20
  delete_cell: { accept: "Delete cell", decline: "Keep cell" },
28
21
  };
29
22
 
30
- export const StagedAICellBackground: React.FC<{
31
- cellId: CellId;
32
- className?: string;
33
- }> = ({ cellId, className }) => {
34
- const stagedCell = useStagedAICell(cellId);
35
-
36
- if (!stagedCell) {
37
- return null;
38
- }
39
-
40
- const cellClass =
41
- stagedCell.type === "delete_cell"
42
- ? "mo-ai-deleted-cell"
43
- : "mo-ai-generated-cell";
44
-
45
- return <div className={cn(cellClass, className)} />;
46
- };
47
-
48
- export const StagedAICellFooter: React.FC<{ cellId: CellId }> = ({
23
+ export const StagedAIDeletionMarker: React.FC<{ cellId: CellId }> = ({
49
24
  cellId,
50
- }) => {
25
+ }) =>
26
+ useStagedAICell(cellId)?.type === "delete_cell" ? (
27
+ <div className="mo-ai-deleted-cell" />
28
+ ) : null;
29
+
30
+ export function useStagedAICellActions(
31
+ cellId: CellId | undefined,
32
+ ): ActionButton[] {
51
33
  const store = useStore();
52
- const stagedAiCell = useStagedAICell(cellId);
53
- const isGenerating = useAtomValue(chatGenerationInProgressAtom);
54
- const runCell = useRunCell(cellId);
55
-
56
- const { deleteStagedCell, removeStagedCell } = useStagedCells(store);
57
-
58
- if (!stagedAiCell) {
59
- return null;
60
- }
61
-
62
- const isDeletion = stagedAiCell.type === "delete_cell";
63
- const labels = actionLabels[stagedAiCell.type];
64
-
65
- const handleCompletion = (type: "accept" | "reject") => {
66
- const completionFunc =
67
- type === "accept" ? acceptStagedCell : rejectStagedCell;
68
- completionFunc(cellId, stagedAiCell, removeStagedCell, deleteStagedCell);
69
- };
70
-
71
- const tooltipContent =
72
- stagedAiCell.type === "add_cell"
73
- ? "AI-generated cell"
74
- : isDeletion
75
- ? "AI suggests deleting this cell"
76
- : "AI changed this cell";
77
-
78
- return (
79
- <div className="mo-ai-cell-footer">
80
- <div className="flex items-center gap-1.5 text-xs text-muted-foreground">
81
- <Tooltip content={tooltipContent}>
82
- <span
83
- className="inline-flex items-center gap-1.5"
84
- aria-label={tooltipContent}
85
- >
86
- <SparklesIcon
87
- aria-hidden="true"
88
- className="size-3.5 text-(--blue-11)"
89
- />
90
- <span>AI</span>
91
- </span>
92
- </Tooltip>
93
- </div>
94
- <div className="flex items-center gap-1.5">
95
- <CompletionActionsCellFooter
96
- isLoading={isGenerating}
97
- onAccept={() => handleCompletion("accept")}
98
- onDecline={() => handleCompletion("reject")}
99
- size="xs"
100
- runCell={isDeletion ? undefined : runCell}
101
- acceptLabel={labels.accept}
102
- declineLabel={labels.decline}
103
- />
104
- </div>
105
- </div>
34
+ const edit = useStagedAICell(cellId);
35
+ // Every cell calls this; only cells with a pending edit care about the lock.
36
+ const isLocked = useAtomValue(
37
+ useMemo(
38
+ () =>
39
+ atom(
40
+ (get) =>
41
+ cellId !== undefined &&
42
+ get(stagedAICellsAtom).has(cellId) &&
43
+ get(chatGenerationInProgressAtom),
44
+ ),
45
+ [cellId],
46
+ ),
106
47
  );
107
- };
48
+ const { acceptStagedCell, rejectStagedCell } = useStagedCells();
108
49
 
109
- /**
110
- * Accept a staged cell and apply the changes.
111
- */
112
- export function acceptStagedCell(
113
- cellId: CellId,
114
- edit: Edit,
115
- removeStagedCell: (cellId: CellId) => void,
116
- deleteStagedCell: (cellId: CellId) => void,
117
- ): void {
118
- switch (edit.type) {
119
- case "delete_cell":
120
- // For delete cells, the cell is deleted when the completion is accepted
121
- deleteStagedCell(cellId);
122
- break;
123
- default:
124
- removeStagedCell(cellId);
125
- break;
50
+ if (!edit || cellId === undefined) {
51
+ return [];
126
52
  }
127
- }
128
-
129
- /**
130
- * Reject a staged cell and revert the changes.
131
- */
132
- export function rejectStagedCell(
133
- cellId: CellId,
134
- edit: Edit,
135
- removeStagedCell: (cellId: CellId) => void,
136
- deleteStagedCell: (cellId: CellId) => void,
137
- ): void {
138
- switch (edit.type) {
139
- case "update_cell": {
140
- // Revert cell code
141
- const editorView = getCellEditorView(cellId);
142
- if (!editorView) {
143
- Logger.error("Editor for this cell not found", { cellId });
144
- break;
145
- }
146
53
 
147
- const update = prepareCellCodeUpdate(
148
- editorView.state,
149
- edit.previousCode,
150
- { initialize: true },
151
- );
152
-
153
- if (update) {
154
- editorView.dispatch(update);
155
- }
156
- removeStagedCell(cellId);
157
- break;
158
- }
159
- case "add_cell":
160
- // Delete the cell since it's newly created
161
- deleteStagedCell(cellId);
162
- break;
163
- case "delete_cell":
164
- // Just remove the deletion marker - cell stays in notebook
165
- removeStagedCell(cellId);
166
- break;
167
- }
54
+ const labels = actionLabels[edit.type];
55
+ return [
56
+ {
57
+ label: labels.accept,
58
+ disabled: isLocked,
59
+ handle: () => {
60
+ if (!store.get(chatGenerationInProgressAtom)) {
61
+ acceptStagedCell(cellId, edit);
62
+ }
63
+ },
64
+ },
65
+ {
66
+ label: labels.decline,
67
+ disabled: isLocked,
68
+ handle: () => {
69
+ if (!store.get(chatGenerationInProgressAtom)) {
70
+ rejectStagedCell(cellId, edit);
71
+ }
72
+ },
73
+ },
74
+ ];
168
75
  }
@@ -0,0 +1,45 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { describe, expect, it, vi } from "vitest";
4
+ import { MockExecutions } from "@/__mocks__/executions";
5
+ import { MockNotebook } from "@/__mocks__/notebook";
6
+ import { cellId } from "@/__tests__/branded";
7
+ import {
8
+ cellExecutionInputsAtom,
9
+ cellExecutionView,
10
+ notebookQueuedOrRunningCountAtom,
11
+ } from "@/core/executions";
12
+ import { store } from "@/core/state/jotai";
13
+ import { runCells } from "../useRunCells";
14
+
15
+ const ENABLED = cellId("enabled");
16
+ const DISABLED = cellId("disabled");
17
+
18
+ describe("runCells", () => {
19
+ it("sends a disabled cell to the kernel without reading it as queued", async () => {
20
+ MockExecutions.seed(store, []);
21
+ const notebook = MockNotebook.notebookState({
22
+ cellData: {
23
+ [ENABLED]: { code: "x = 1" },
24
+ [DISABLED]: { code: "y = 2", config: { disabled: true } },
25
+ },
26
+ });
27
+ const sendRun = vi.fn(async () => null);
28
+
29
+ await runCells({
30
+ cellIds: [ENABLED, DISABLED],
31
+ sendRun,
32
+ prepareForRun: vi.fn(),
33
+ notebook,
34
+ });
35
+
36
+ expect(sendRun).toHaveBeenCalledWith({
37
+ cellIds: [ENABLED, DISABLED],
38
+ codes: ["x = 1", "y = 2"],
39
+ });
40
+ const inputs = store.get(cellExecutionInputsAtom);
41
+ expect(cellExecutionView(inputs, ENABLED).status).toBe("queued");
42
+ expect(cellExecutionView(inputs, DISABLED).status).toBe("idle");
43
+ expect(store.get(notebookQueuedOrRunningCountAtom)).toBe(1);
44
+ });
45
+ });
@@ -23,6 +23,7 @@ import { toast } from "@/components/ui/use-toast";
23
23
  import { useCellData, useCellRuntime } from "@/core/cells/cells";
24
24
  import { CellOutputId } from "@/core/cells/ids";
25
25
  import { isOutputEmpty } from "@/core/cells/outputs";
26
+ import { useCellExecution } from "@/core/executions";
26
27
  import { goToDefinitionWithLspFallback } from "@/core/codemirror/go-to-definition/utils";
27
28
  import { sendToPanelManager } from "@/core/vscode/vscode-bindings";
28
29
  import { copyImageToClipboard, copyToClipboard } from "@/utils/copy";
@@ -54,12 +55,14 @@ export const CellActionsContextMenu = ({
54
55
  }: Props) => {
55
56
  const cellData = useCellData(cellId);
56
57
  const cellRuntime = useCellRuntime(cellId);
58
+ const execution = useCellExecution(cellId);
57
59
  const actions = useCellActionButtons({
58
60
  cell: {
59
61
  cellId: cellId,
60
62
  name: cellData.name,
61
63
  config: cellData.config,
62
- status: cellRuntime.status,
64
+ status: execution.status,
65
+ blocked: execution.blocked,
63
66
  hasOutput: !isOutputEmpty(cellRuntime.output),
64
67
  hasConsoleOutput: cellRuntime.consoleOutputs.length > 0,
65
68
  getEditorView,
@@ -19,7 +19,8 @@ import {
19
19
  useNotebookDocumentEdit,
20
20
  } from "@/core/notebook/document";
21
21
  import { usePendingDeleteService } from "@/core/cells/pending-delete-service";
22
- import type { CellData, CellRuntimeState } from "@/core/cells/types";
22
+ import type { CellData } from "@/core/cells/types";
23
+ import type { CellRunStatus } from "@/core/executions";
23
24
  import { setupCodeMirror } from "@/core/codemirror/cm";
24
25
  import { acceptCompletionOnEnterAtom } from "@/core/codemirror/completion/accept-on-enter-atom";
25
26
  import { editorMountScheduler } from "@/core/codemirror/editor-mount-scheduler";
@@ -54,10 +55,11 @@ import { useSplitCellCallback } from "../useSplitCell";
54
55
  import { CodePlaceholder } from "./code-placeholder";
55
56
  import { LanguageToggles } from "./language-toggle";
56
57
 
57
- export interface CellEditorProps
58
- extends
59
- Pick<CellRuntimeState, "status">,
60
- Pick<CellData, "id" | "code" | "config"> {
58
+ export interface CellEditorProps extends Pick<
59
+ CellData,
60
+ "id" | "code" | "config"
61
+ > {
62
+ status: CellRunStatus;
61
63
  runCell: () => void;
62
64
  theme: Theme;
63
65
  showPlaceholder: boolean;
@@ -9,6 +9,7 @@ import {
9
9
  RefreshCcw,
10
10
  } from "lucide-react";
11
11
  import type React from "react";
12
+ import { useAtomValue } from "jotai";
12
13
  import { memo } from "react";
13
14
  import { Toolbar } from "@/components/layout/toolbar";
14
15
  import { Button } from "@/components/ui/button";
@@ -17,6 +18,7 @@ import { useNotebook } from "@/core/cells/cells";
17
18
  import { columnOf, useLayout } from "@/core/cells/layout";
18
19
  import type { CellId } from "@/core/cells/ids";
19
20
  import { getDescendantsStatus } from "@/core/cells/utils";
21
+ import { cellExecutionInputsAtom } from "@/core/executions";
20
22
  import { cn } from "@/utils/cn";
21
23
 
22
24
  interface Props {
@@ -65,9 +67,11 @@ export const CollapsedCellBanner: React.FC<{
65
67
  count: number;
66
68
  }> = memo(({ onClick, count, cellId }) => {
67
69
  const notebook = useNotebook();
70
+ const executions = useAtomValue(cellExecutionInputsAtom);
68
71
  const layout = useLayout();
69
72
  const states = getDescendantsStatus(
70
73
  notebook,
74
+ executions,
71
75
  columnOf(layout, cellId).folds.get(cellId) ?? [],
72
76
  );
73
77
 
@@ -7,10 +7,16 @@ import { getNotebook, type NotebookState } from "@/core/cells/cells";
7
7
  import { useCellActions } from "@/core/cells/actions";
8
8
  import type { CellId } from "@/core/cells/ids";
9
9
  import { enabledCellIds, staleCellIds } from "@/core/cells/utils";
10
+ import {
11
+ cellExecutionInputsAtom,
12
+ submitCells,
13
+ withdrawCells,
14
+ } from "@/core/executions";
10
15
  import { getCurrentLanguageAdapter } from "@/core/codemirror/language/commands";
11
16
  import { getEditorCodeAsPython } from "@/core/codemirror/language/utils";
12
17
  import { useRequestClient } from "@/core/network/requests";
13
18
  import type { ExecuteCellsRequest } from "@/core/network/types";
19
+ import { store } from "@/core/state/jotai";
14
20
  import { Logger } from "@/utils/Logger";
15
21
 
16
22
  export const hasRunAnyCellAtom = atom<boolean>(false);
@@ -20,7 +26,9 @@ export const hasRunAnyCellAtom = atom<boolean>(false);
20
26
  */
21
27
  export function useRunStaleCells() {
22
28
  const runCells = useRunCells();
23
- const runStaleCells = useEvent(() => runCells(staleCellIds(getNotebook())));
29
+ const runStaleCells = useEvent(() =>
30
+ runCells(staleCellIds(getNotebook(), store.get(cellExecutionInputsAtom))),
31
+ );
24
32
  return runStaleCells;
25
33
  }
26
34
 
@@ -107,9 +115,16 @@ export async function runCells({
107
115
  codes.push(code);
108
116
  prepareForRun({ cellId });
109
117
  }
110
-
111
- // Send the run request to the Kernel
118
+ // The kernel sets a disabled cell aside without planning an execution,
119
+ // so a submission for it would never be superseded.
120
+ submitCells(
121
+ store,
122
+ cellIds.filter((cellId) => !cellData[cellId]?.config.disabled),
123
+ );
124
+
125
+ // TODO(MO-8346): Submit through the host's `runtime.run`.
112
126
  await sendRun({ cellIds: cellIds, codes: codes }).catch((error) => {
127
+ withdrawCells(store, cellIds);
113
128
  Logger.error(error);
114
129
  });
115
130
  }