@marimo-team/islands 0.25.3-dev170 → 0.25.3-dev175

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 (196) hide show
  1. package/dist/{ConnectedDataExplorerComponent-BbSXV-Xa.js → ConnectedDataExplorerComponent-Le_OBfmt.js} +9 -9
  2. package/dist/{ErrorBoundary-NE86qEWZ.js → ErrorBoundary-DNvcAIsP.js} +8 -8
  3. package/dist/{abnfDiagram-VCTEODGH-D7n07nXf.js → abnfDiagram-VCTEODGH-Tf1ghyQQ.js} +2 -2
  4. package/dist/{any-language-editor-3E24SnCV.js → any-language-editor-BVFHDDQ3.js} +4 -4
  5. package/dist/{architectureDiagram-5GKGNRK7-D6CjRib5.js → architectureDiagram-5GKGNRK7-xlnbTG0T.js} +1 -1
  6. package/dist/{chat-ui-CC7GekpG.js → chat-ui-X-cWrL9u.js} +1218 -1218
  7. package/dist/{check-SpOMoCLP.js → check-C9Mp9eCK.js} +1 -1
  8. package/dist/{chunk-IMKFNOWR-6dtPz7uS.js → chunk-IMKFNOWR-Cmd4a5jy.js} +1 -1
  9. package/dist/{chunk-SHT3W25Y-BwnFNKDA.js → chunk-SHT3W25Y-mSNp-WpO.js} +1 -1
  10. package/dist/{chunk-SVP7TREG-D2eV4vBJ.js → chunk-SVP7TREG-CWC49D8X.js} +1 -1
  11. package/dist/{chunk-TICWLB2K-BDs4-_-r.js → chunk-TICWLB2K-De9FINtG.js} +1 -1
  12. package/dist/{classDiagram-ZZMXUADV-DCJnf_8v.js → classDiagram-ZZMXUADV-DweTkYdp.js} +2 -2
  13. package/dist/{classDiagram-v2-VYDZK3BY-BOmSzPBD.js → classDiagram-v2-VYDZK3BY-A6MR2TiE.js} +2 -2
  14. package/dist/{code-visibility-Bzvc6ajl.js → code-visibility-CH1lmM1h.js} +5 -5
  15. package/dist/{common-Ciw4zkh2.js → common-DIrS1UR9.js} +1081 -1085
  16. package/dist/{copy-icon-C1trdvVt.js → copy-icon-De584pZ4.js} +2 -2
  17. package/dist/{cynefinDiagram-5FMLGOSQ-Bln_Soky.js → cynefinDiagram-5FMLGOSQ-BSvo5le-.js} +1 -1
  18. package/dist/{dagre-GXQ25YYZ-D-nf7WzW.js → dagre-GXQ25YYZ-tXRQslO6.js} +1 -1
  19. package/dist/{data-grid-overlay-editor-AQREmxI7.js → data-grid-overlay-editor-DFLZvFK0.js} +1 -1
  20. package/dist/{diagram-S7CK7UJ4-PsgSMkY2.js → diagram-S7CK7UJ4-r3t5VtwI.js} +1 -1
  21. package/dist/{diagram-UQ7AKVKN-DfwfUuSw.js → diagram-UQ7AKVKN-qgbC8YnT.js} +1 -1
  22. package/dist/{diagram-VX7I27RA-C9_e-oMc.js → diagram-VX7I27RA-CiHxEwtp.js} +1 -1
  23. package/dist/{diagram-Z3DM3KII-CWLa5tuY.js → diagram-Z3DM3KII-D95u15Ev.js} +1 -1
  24. package/dist/{dialog-DgeqyrEZ.js → dialog-DVRfQuhD.js} +24 -24
  25. package/dist/{dist-F8Ep36rk.js → dist-B1M0bGO3.js} +1 -1
  26. package/dist/{dist-D6d86eNJ.js → dist-BA13RJj5.js} +1 -1
  27. package/dist/{dist-Bvs24R_E.js → dist-BJtmSgcE.js} +48 -48
  28. package/dist/{dist-CN_BeG2I.js → dist-Ba2VIn1K.js} +53 -53
  29. package/dist/{dist-PXYI_yuP.js → dist-Bj4bmwUm.js} +12 -12
  30. package/dist/{dist-DoE4XJBC.js → dist-BmS-ZGjl.js} +8626 -8626
  31. package/dist/{dist-BorMD-uX.js → dist-Bzogh7CW.js} +87 -87
  32. package/dist/{dist-CN8zI-XC.js → dist-CThPVKaP.js} +1385 -1382
  33. package/dist/{dist-DiDibOYr.js → dist-CTi5FTiV.js} +87 -87
  34. package/dist/{dist-diIx5qJW.js → dist-Ck_C32ye.js} +2 -2
  35. package/dist/{dist-BLfKmR-l.js → dist-D1fnFp1J.js} +1 -1
  36. package/dist/{dist-OR1OnN-y.js → dist-D3x9__Fu.js} +46 -46
  37. package/dist/{dist-BMQf5zIS.js → dist-D7mrh5sO.js} +36 -36
  38. package/dist/{dist-CSJ0MDRw.js → dist-D9Q3xjiP.js} +1 -1
  39. package/dist/{dist-BuaRHxOc.js → dist-DP60Ubh8.js} +2 -2
  40. package/dist/{dist-CSbh1PNP.js → dist-DTv9KkpW.js} +12 -12
  41. package/dist/{dist-D-wwqlzJ.js → dist-DVVwOfrD.js} +109 -109
  42. package/dist/{dist-C8fAKSH1.js → dist-DYXM73-k.js} +36 -36
  43. package/dist/{dist-DBtSl3sf.js → dist-DjeybPOb.js} +1 -1
  44. package/dist/{dist-CrSP_H1M.js → dist-DtGn9N4h.js} +197 -197
  45. package/dist/{dist-CLdpGQVr.js → dist-KN80qWCP.js} +2 -2
  46. package/dist/{dist-Cg7gzw-a.js → dist-O0qFpEJh.js} +56 -56
  47. package/dist/{dist-CkTTIdBe.js → dist-OUle2RT7.js} +207 -207
  48. package/dist/{dist-BK4Oj99o.js → dist-T5rh6JvL.js} +1 -1
  49. package/dist/{dropdown-menu-B6ftyzOg.js → dropdown-menu-DCMbbQ35.js} +691 -691
  50. package/dist/{ebnfDiagram-PWID7BFC-B1QRM58v.js → ebnfDiagram-PWID7BFC-P7e4bryK.js} +2 -2
  51. package/dist/{erDiagram-RLTQ6QDP-DJK_FqvW.js → erDiagram-RLTQ6QDP-CUHZcmRj.js} +1 -1
  52. package/dist/{error-banner-D40pmfQQ.js → error-banner-DxJ3s8YO.js} +57 -57
  53. package/dist/{error-entries-DgK3wUJR.js → error-entries-mpoD5t79.js} +4985 -5006
  54. package/dist/{esm-D2gTsXEV.js → esm-Bbw_Njjx.js} +575 -575
  55. package/dist/{esm-AMFssoNp.js → esm-mDQNEFe_.js} +354 -354
  56. package/dist/{feedback-button-8a6SETmT.js → feedback-button-DL0DJ-LB.js} +30 -30
  57. package/dist/{flowDiagram-HODETNUW-Uxc25XSJ.js → flowDiagram-HODETNUW-BGZ95AzI.js} +2 -2
  58. package/dist/{formats-DQCyPd5Z.js → formats-DI7HEVv7.js} +1 -1
  59. package/dist/{glide-data-editor-B70oke_v.js → glide-data-editor-n4wnx19S.js} +10 -10
  60. package/dist/{glide-utils-Dz_F4pAY.js → glide-utils-D98laidb.js} +2 -2
  61. package/dist/{globals-Cxd5hNlI.js → globals-NCsYyjpB.js} +3 -3
  62. package/dist/{html-to-image-D4yG5Ru0.js → html-to-image-BoZthB4P.js} +467 -458
  63. package/dist/{infoDiagram-27XIBGKW-DmjxqwL0.js → infoDiagram-27XIBGKW-BF0YX70D.js} +1 -1
  64. package/dist/{ishikawaDiagram-5VMMS53U-NgCLHqQi.js → ishikawaDiagram-5VMMS53U-_itsAAn-.js} +1 -1
  65. package/dist/{kanban-definition-UXKFOSKX-CZaG-iam.js → kanban-definition-UXKFOSKX-VNW2Beqh.js} +1 -1
  66. package/dist/{label-D9_-zSg-.js → label-Qvw1E97W.js} +1 -1
  67. package/dist/main.js +8257 -6606
  68. package/dist/{mermaid-CSvw7c71.js → mermaid-CZ6FrClS.js} +35 -35
  69. package/dist/{mindmap-definition-YA3MSWOX-DJYQBDH_.js → mindmap-definition-YA3MSWOX-DR4ripvd.js} +1 -1
  70. package/dist/{number-overlay-editor-ChK-SKUk.js → number-overlay-editor-rlvo4mo3.js} +1 -1
  71. package/dist/{pegDiagram-XKGWAZYB-CHKO_dAe.js → pegDiagram-XKGWAZYB-CFr3XlGl.js} +2 -2
  72. package/dist/{pieDiagram-E7YTZNPT-DVevbMAo.js → pieDiagram-E7YTZNPT-j91XYSLY.js} +1 -1
  73. package/dist/{process-output-DxvBAeL3.js → process-output-DLS4Mqg_.js} +4 -4
  74. package/dist/{railroadDiagram-O6MQD6OU-Detj1kcB.js → railroadDiagram-O6MQD6OU-Cd47ZV9E.js} +2 -2
  75. package/dist/{requirementDiagram-BXWQKSXE-BLFD0T_Z.js → requirementDiagram-BXWQKSXE-eJH6gLtX.js} +1 -1
  76. package/dist/{reveal-component-CXMXeDZD.js → reveal-component-BJfmRUzb.js} +9 -9
  77. package/dist/{select-CkF_GBxO.js → select-2KOiReNY.js} +324 -324
  78. package/dist/{sequenceDiagram-WJ2MYXX4-BZBgatQV.js → sequenceDiagram-WJ2MYXX4-DdfFEIrm.js} +1 -1
  79. package/dist/{spec-Mnkn3XEr.js → spec-h69nC3SI.js} +4 -4
  80. package/dist/{stateDiagram-D77RDMKH-4BZW9-6_.js → stateDiagram-D77RDMKH-DkjDvip8.js} +2 -2
  81. package/dist/{stateDiagram-v2-MP3YSRHH-C4sKC8Iq.js → stateDiagram-v2-MP3YSRHH-DBHACnOo.js} +2 -2
  82. package/dist/{swimlanes-42K2YHIH-SL5yeGe2.js → swimlanes-42K2YHIH-ogImlOmp.js} +1 -1
  83. package/dist/{swimlanesDiagram-VR7AAH4N-DqPmAYUv.js → swimlanesDiagram-VR7AAH4N-D6x-zsci.js} +2 -2
  84. package/dist/{timeline-definition-24CTP7MA-BfXUMWkq.js → timeline-definition-24CTP7MA-DQpLtsHD.js} +1 -1
  85. package/dist/{toDate-TMubKIVL.js → toDate-CDzoofJo.js} +2 -2
  86. package/dist/{toast-Btbve3Lm.js → toast-B5t9o6s_.js} +47 -47
  87. package/dist/{tooltip-CJ4Rqasp.js → tooltip-s4zZxwKe.js} +67 -67
  88. package/dist/{use-restore-focus-CmGwRNId.js → use-restore-focus-BgDTd2tc.js} +1 -1
  89. package/dist/{useAsyncData-DjMGFzFy.js → useAsyncData-Bn8SxUqM.js} +1 -1
  90. package/dist/{useDateFormatter-eCL_SdeG.js → useDateFormatter-D3Tkfzej.js} +2 -2
  91. package/dist/{useDeepCompareMemoize-BQR5TJot.js → useDeepCompareMemoize-CC2tnPCk.js} +1 -1
  92. package/dist/{useLifecycle-C_GvKWbQ.js → useLifecycle-BJlxHqWa.js} +2 -2
  93. package/dist/{useTheme-Bns2gJZ6.js → useTheme-DMfQYmlG.js} +85 -85
  94. package/dist/{vega-component-BsVptuhz.js → vega-component-CovWHi60.js} +7 -7
  95. package/dist/{vennDiagram-4TSXK5OY-CH65znGa.js → vennDiagram-4TSXK5OY-BD-SL_vq.js} +1 -1
  96. package/dist/{wardleyDiagram-VM6X3IG4-z9pWKb2_.js → wardleyDiagram-VM6X3IG4-BDgJV4ah.js} +1 -1
  97. package/dist/{xychartDiagram-S5SC5T6Z-MxyaWjjq.js → xychartDiagram-S5SC5T6Z-1hIHN6Rl.js} +1 -1
  98. package/package.json +1 -1
  99. package/src/__mocks__/document.tsx +54 -0
  100. package/src/__mocks__/notebook.ts +2 -1
  101. package/src/__mocks__/requests.ts +0 -2
  102. package/src/components/chat/chat-panel.tsx +3 -3
  103. package/src/components/datasources/__tests__/column-preview.test.tsx +6 -8
  104. package/src/components/datasources/column-preview.tsx +4 -8
  105. package/src/components/datasources/datasources.tsx +4 -3
  106. package/src/components/dependency-graph/minimap-content.tsx +2 -6
  107. package/src/components/dependency-graph/panels.tsx +2 -1
  108. package/src/components/editor/__tests__/Output.test.tsx +50 -32
  109. package/src/components/editor/__tests__/data-attributes.test.tsx +22 -11
  110. package/src/components/editor/__tests__/output-persistence.test.tsx +6 -1
  111. package/src/components/editor/actions/__tests__/name-cell-input.test.tsx +69 -0
  112. package/src/components/editor/actions/name-cell-input.tsx +4 -3
  113. package/src/components/editor/actions/useCellActionButton.tsx +20 -26
  114. package/src/components/editor/actions/useNotebookActions.tsx +13 -18
  115. package/src/components/editor/actions/useSetCodeVisibility.ts +10 -11
  116. package/src/components/editor/actions/useSetOutputsExpanded.ts +13 -18
  117. package/src/components/editor/cell/CreateCellButton.tsx +5 -9
  118. package/src/components/editor/cell/__tests__/StagedAICell.test.tsx +3 -1
  119. package/src/components/editor/cell/code/cell-editor.tsx +13 -20
  120. package/src/components/editor/cell/useAddCell.ts +4 -8
  121. package/src/components/editor/cell/useDeleteCell.tsx +10 -36
  122. package/src/components/editor/cell/useRunCells.ts +2 -5
  123. package/src/components/editor/cell/useSplitCell.tsx +2 -1
  124. package/src/components/editor/chrome/panels/logs-panel.tsx +2 -1
  125. package/src/components/editor/chrome/panels/snippets-panel.tsx +5 -11
  126. package/src/components/editor/chrome/wrapper/__tests__/pending-ai-cells.test.tsx +3 -1
  127. package/src/components/editor/columns/sortable-column.tsx +1 -1
  128. package/src/components/editor/connections/components.tsx +4 -9
  129. package/src/components/editor/controls/Controls.tsx +1 -1
  130. package/src/components/editor/controls/__tests__/notebook-menu-dropdown.test.tsx +9 -3
  131. package/src/components/editor/documentation.css +34 -11
  132. package/src/components/editor/errors/auto-fix.tsx +4 -8
  133. package/src/components/editor/file-tree/file-explorer.tsx +3 -7
  134. package/src/components/editor/links/__tests__/cell-link.test.tsx +3 -1
  135. package/src/components/editor/links/cell-link.tsx +2 -1
  136. package/src/components/editor/navigation/__tests__/clipboard.test.ts +60 -38
  137. package/src/components/editor/navigation/__tests__/navigation.test.ts +73 -64
  138. package/src/components/editor/navigation/clipboard.ts +12 -11
  139. package/src/components/editor/navigation/multi-cell-action-toolbar.tsx +11 -20
  140. package/src/components/editor/navigation/navigation.ts +20 -28
  141. package/src/components/editor/notebook-cell.tsx +7 -10
  142. package/src/components/editor/output/console/__tests__/ConsoleOutput.test.tsx +125 -69
  143. package/src/components/editor/renderers/cell-array.tsx +10 -17
  144. package/src/components/markdown/markdown-renderer.tsx +4 -8
  145. package/src/components/pages/edit-page.tsx +32 -17
  146. package/src/components/scratchpad/scratchpad.test.tsx +3 -1
  147. package/src/components/scratchpad/scratchpad.tsx +4 -7
  148. package/src/components/slides/__tests__/minimap-actions.test.tsx +9 -25
  149. package/src/components/slides/minimap.tsx +7 -3
  150. package/src/components/slides/slide-cell-view.tsx +3 -3
  151. package/src/components/sort/SortableCellsProvider.tsx +2 -1
  152. package/src/core/ai/__tests__/staged-cells.test.ts +18 -20
  153. package/src/core/ai/staged-cells.ts +8 -16
  154. package/src/core/ai/tools/__tests__/edit-notebook-tool.test.ts +2 -2
  155. package/src/core/ai/tools/__tests__/run-cells-tool.test.ts +2 -2
  156. package/src/core/ai/tools/base.ts +2 -2
  157. package/src/core/ai/tools/edit-notebook-tool.ts +13 -21
  158. package/src/core/cells/__tests__/actions.test.ts +474 -34
  159. package/src/core/cells/__tests__/add-missing-import.test.ts +7 -8
  160. package/src/core/cells/__tests__/cells.test.ts +251 -861
  161. package/src/core/cells/__tests__/document.test.ts +42 -38
  162. package/src/core/cells/__tests__/notebook-outline.test.ts +7 -1
  163. package/src/core/cells/__tests__/outputs.test.tsx +71 -53
  164. package/src/core/cells/actions.ts +391 -3
  165. package/src/core/cells/add-missing-import.ts +46 -42
  166. package/src/core/cells/cells.ts +73 -444
  167. package/src/core/cells/document.ts +38 -6
  168. package/src/core/cells/outputs.ts +12 -28
  169. package/src/core/codemirror/cells/extensions.ts +6 -6
  170. package/src/core/codemirror/cells/state.ts +1 -1
  171. package/src/core/codemirror/keymaps/vim.ts +2 -2
  172. package/src/core/edit-app.tsx +20 -25
  173. package/src/core/islands/__tests__/bootstrap.test.ts +56 -0
  174. package/src/core/islands/__tests__/bridge.test.ts +42 -0
  175. package/src/core/islands/bootstrap.ts +24 -1
  176. package/src/core/islands/bridge.ts +46 -5
  177. package/src/core/kernel/__tests__/handlers.test.ts +7 -153
  178. package/src/core/kernel/handlers.ts +18 -59
  179. package/src/core/network/__tests__/requests-lazy.test.ts +1 -3
  180. package/src/core/network/requests-lazy.ts +0 -2
  181. package/src/core/network/requests-network.ts +0 -16
  182. package/src/core/network/requests-static.ts +0 -2
  183. package/src/core/network/requests-toasting.tsx +0 -2
  184. package/src/core/network/types.ts +0 -7
  185. package/src/core/notebook/document/__tests__/codemirror.test.ts +5 -0
  186. package/src/core/notebook/document/__tests__/draft.test.ts +41 -0
  187. package/src/core/notebook/document/__tests__/state.test.ts +21 -0
  188. package/src/core/notebook/document/draft.ts +35 -5
  189. package/src/core/notebook/document/index.ts +1 -0
  190. package/src/core/notebook/document/state.ts +6 -2
  191. package/src/core/run-app.tsx +1 -7
  192. package/src/core/session/__tests__/useNotebookSession.hook.test.tsx +0 -6
  193. package/src/core/session/useNotebookSession.tsx +28 -35
  194. package/src/core/wasm/bridge.ts +0 -16
  195. package/src/css/globals.css +56 -22
  196. package/src/stories/runtime-story.tsx +22 -15
@@ -1,12 +1,11 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
- import { historyField } from "@codemirror/commands";
3
+ import type * as Protocol from "@marimo-team/client/protocol";
4
4
  import { type Atom, atom, useAtom, useAtomValue } from "jotai";
5
5
  import { atomFamily, selectAtom, splitAtom } from "jotai/utils";
6
6
  import { createRef, type ReducerWithoutAction } from "react";
7
7
  import type { CellHandle } from "@/components/editor/notebook-cell";
8
- import type { EditableDocument } from "@/core/notebook/document";
9
- import type { CollapsibleTree } from "@/utils/id-tree";
8
+ import type { EditableDocument, Placement } from "@/core/notebook/document";
10
9
  import {
11
10
  type CellColumnId,
12
11
  type CellIndex,
@@ -31,7 +30,7 @@ import { createDeepEqualAtom, store } from "../state/jotai";
31
30
  import { isWasm } from "../wasm/utils";
32
31
  import { prepareCellForExecution, transitionCell } from "./cell";
33
32
  import { applyDocument, documentEditMiddleware } from "./document";
34
- import { CellId, SCRATCH_CELL_ID, SETUP_CELL_ID } from "./ids";
33
+ import { CellId, SCRATCH_CELL_ID } from "./ids";
35
34
  import { type CellLog, getCellLogsForMessage } from "./logs";
36
35
  import {
37
36
  focusAndScrollCellIntoView,
@@ -42,8 +41,8 @@ import {
42
41
  type CellData,
43
42
  type CellRuntimeState,
44
43
  createCell,
45
- createCellConfig,
46
44
  createCellRuntimeState,
45
+ isEdited,
47
46
  } from "./types";
48
47
  import {
49
48
  canUndoDeletes,
@@ -55,17 +54,21 @@ import {
55
54
  notebookQueueOrRunningCount,
56
55
  } from "./utils";
57
56
 
57
+ /** A deleted cell as it was, with where it went in the document. */
58
+ export interface DeletedCell {
59
+ cell: Protocol.CellInput;
60
+ placement: Placement;
61
+ /** The editor's state at deletion, so undo brings its history back. */
62
+ serializedEditorState: SerializedEditorState | null;
63
+ }
64
+
58
65
  /**
59
- * History entry for undoing a cell deletion.
66
+ * History entry for undoing one deletion, of one or more cells.
60
67
  */
61
68
  export interface UndoDeleteEntry {
62
69
  type: "delete";
63
- name: string;
64
- serializedEditorState: SerializedEditorState;
65
- column: CellColumnId;
66
- index: CellIndex;
67
- isSetupCell: boolean;
68
- config: CellConfig;
70
+ /** In document order. Each cell is placed after the one before it. */
71
+ cells: DeletedCell[];
69
72
  }
70
73
 
71
74
  /**
@@ -161,40 +164,6 @@ export function initialNotebookState(): NotebookState {
161
164
  });
162
165
  }
163
166
 
164
- /** The target cell ID to create a new cell relative to. Can be:
165
- * - A CellId string for an existing cell
166
- * - "__end__" to append at the end of the first column
167
- * - {type: "__end__", columnId} to append at the end of a specific column
168
- */
169
- export type CellPosition =
170
- | CellId
171
- | "__end__"
172
- | { type: "__end__"; columnId: CellColumnId };
173
-
174
- export interface CreateNewCellAction {
175
- cellId: CellPosition;
176
- /** Whether to insert before (true) or after (false) the target cell */
177
- before: boolean;
178
- /** Initial code content for the new cell */
179
- code?: string;
180
- /** Optional name for the new cell */
181
- name?: string;
182
- /** Optional cell configuration */
183
- config?: CellConfig;
184
- /** The last executed code for the new cell */
185
- lastCodeRun?: string;
186
- /** Timestamp of the last execution */
187
- lastExecutionTime?: number;
188
- /** Optional custom ID for the new cell. Auto-generated if not provided */
189
- newCellId?: CellId;
190
- /** Whether to focus the new cell after creation */
191
- autoFocus?: boolean;
192
- /** If true, skip creation if code already exists */
193
- skipIfCodeExists?: boolean;
194
- /** Hide the code in the new cell. This will be initially shown until the cell is blurred for the first time. */
195
- hideCode?: boolean;
196
- }
197
-
198
167
  /**
199
168
  * Actions and reducer for the notebook state.
200
169
  */
@@ -205,85 +174,6 @@ const {
205
174
  useActions,
206
175
  valueAtom: notebookAtom,
207
176
  } = createReducerAndAtoms(initialNotebookState, {
208
- createNewCell: (state, action: CreateNewCellAction) => {
209
- const {
210
- cellId,
211
- before,
212
- code,
213
- name,
214
- config,
215
- lastCodeRun = null,
216
- lastExecutionTime = null,
217
- autoFocus = true,
218
- skipIfCodeExists = false,
219
- hideCode = undefined,
220
- } = action;
221
-
222
- let columnId: CellColumnId;
223
- let cellIndex: number;
224
-
225
- // If skipIfCodeExists is true, check if the code already exists in the notebook
226
- if (skipIfCodeExists) {
227
- for (const cellId of state.cellIds.inOrderIds) {
228
- if (state.cellData[cellId]?.code === code) {
229
- return state;
230
- }
231
- }
232
- }
233
-
234
- if (cellId === "__end__") {
235
- const column = state.cellIds.atOrThrow(0);
236
- columnId = column.id;
237
- cellIndex = column.length;
238
- } else if (typeof cellId === "string") {
239
- const column = state.cellIds.findWithId(cellId);
240
- columnId = column.id;
241
- cellIndex = column.topLevelIds.indexOf(cellId);
242
- } else if (cellId.type === "__end__") {
243
- const column =
244
- state.cellIds.get(cellId.columnId) || state.cellIds.atOrThrow(0);
245
- columnId = column.id;
246
- cellIndex = column.length;
247
- } else {
248
- throw new Error("Invalid cellId");
249
- }
250
-
251
- const newCellId = action.newCellId || CellId.create();
252
- const insertionIndex = before ? cellIndex : cellIndex + 1;
253
-
254
- return {
255
- ...state,
256
- cellIds: state.cellIds.insertId(newCellId, columnId, insertionIndex),
257
- cellData: {
258
- ...state.cellData,
259
- [newCellId]: createCell({
260
- id: newCellId,
261
- code,
262
- name,
263
- lastCodeRun,
264
- config: createCellConfig({
265
- ...config,
266
- ...(hideCode != null && { hide_code: hideCode }),
267
- }),
268
- lastExecutionTime,
269
- edited: Boolean(code) && code !== lastCodeRun,
270
- }),
271
- },
272
- cellRuntime: {
273
- ...state.cellRuntime,
274
- [newCellId]: createCellRuntimeState(),
275
- },
276
- cellHandles: {
277
- ...state.cellHandles,
278
- [newCellId]: createRef(),
279
- },
280
- scrollKey: autoFocus ? newCellId : null,
281
- untouchedNewCells:
282
- hideCode && autoFocus
283
- ? new Set([...state.untouchedNewCells, newCellId])
284
- : state.untouchedNewCells,
285
- };
286
- },
287
177
  moveCell: (
288
178
  state,
289
179
  action: {
@@ -678,128 +568,29 @@ const {
678
568
  cellIds: state.cellIds.compact(),
679
569
  };
680
570
  },
681
- deleteCell: (state, action: { cellId: CellId }) => {
682
- const cellId = action.cellId;
683
-
684
- // Can't delete the last cell, across all columns
685
- if (state.cellIds.hasOnlyOneId()) {
686
- return state;
687
- }
688
-
689
- let column: CollapsibleTree<CellId>;
690
- try {
691
- column = state.cellIds.findWithId(cellId);
692
- } catch (error) {
693
- // Expected for kernel-only cells or out-of-order transactions.
694
- Logger.warn("Skipping delete for missing cellId", {
695
- cellId,
696
- error,
697
- });
698
- return state;
699
- }
700
-
701
- const cellIndex = column.indexOfOrThrow(cellId);
702
- const focusIndex = cellIndex === 0 ? 1 : cellIndex - 1;
703
- let scrollKey: CellId | null = null;
704
- if (column.length > 1) {
705
- scrollKey = column.atOrThrow(focusIndex);
706
- }
707
-
708
- const editorView = state.cellHandles[cellId].current?.editorView;
709
- const serializedEditorState = editorView?.state.toJSON({
710
- history: historyField,
711
- });
712
- if (serializedEditorState) {
713
- serializedEditorState.doc = state.cellData[cellId].code;
714
- }
715
-
716
- // release the granular atom(s) created for this cell
717
- releaseCellAtoms(cellId);
718
- const prevData = state.cellData[cellId];
719
- return {
720
- ...state,
721
- cellIds: state.cellIds.deleteById(cellId),
722
- history: [
723
- ...state.history,
724
- {
725
- type: "delete",
726
- name: prevData.name,
727
- serializedEditorState: serializedEditorState,
728
- column: column.id,
729
- index: cellIndex,
730
- isSetupCell: cellId === SETUP_CELL_ID,
731
- config: prevData.config,
732
- },
733
- ],
734
- scrollKey: scrollKey,
735
- };
736
- },
737
- undoDeleteCell: (state) => {
738
- if (state.history.length === 0) {
571
+ /**
572
+ * Put moved cells back where they were.
573
+ *
574
+ * TODO(MO-8028): Move to `undoDeleteCellAtom` with the order family.
575
+ */
576
+ undoMoveCells: (state, action: UndoMoveEntry) => {
577
+ const { cellIds, placements } = action;
578
+ if (
579
+ cellIds.length === 0 ||
580
+ placements.length !== cellIds.length ||
581
+ cellIds.some((id) => !state.cellData[id])
582
+ ) {
739
583
  return state;
740
584
  }
741
-
742
- const last = state.history[state.history.length - 1];
743
-
744
- if (last.type === "move") {
745
- const { cellIds, placements } = last;
746
- if (
747
- cellIds.length === 0 ||
748
- placements.length !== cellIds.length ||
749
- cellIds.some((id) => !state.cellData[id])
750
- ) {
751
- return { ...state, history: state.history.slice(0, -1) };
752
- }
753
- const toRestore = cellIds.map((id, i) => ({
754
- id,
755
- columnId: placements[i].columnId,
756
- index: placements[i].index,
757
- }));
758
- return {
759
- ...state,
760
- cellIds: state.cellIds.placeCells(toRestore),
761
- history: state.history.slice(0, -1),
762
- scrollKey: cellIds[0] ?? null,
763
- };
764
- }
765
-
766
- const {
767
- name,
768
- serializedEditorState = { doc: "" },
769
- column,
770
- index,
771
- isSetupCell,
772
- config,
773
- } = last;
774
-
775
- const cellId = isSetupCell ? SETUP_CELL_ID : CellId.create();
776
- const undoCell = createCell({
777
- id: cellId,
778
- name,
779
- code: serializedEditorState.doc,
780
- edited: serializedEditorState.doc.trim().length > 0,
781
- serializedEditorState,
782
- config,
783
- });
784
-
585
+ const toRestore = cellIds.map((id, i) => ({
586
+ id,
587
+ columnId: placements[i].columnId,
588
+ index: placements[i].index,
589
+ }));
785
590
  return {
786
591
  ...state,
787
- cellIds: state.cellIds.insertId(cellId, column, index),
788
- cellData: {
789
- ...state.cellData,
790
- [cellId]: undoCell,
791
- },
792
- cellRuntime: {
793
- ...state.cellRuntime,
794
- [cellId]: createCellRuntimeState(),
795
- },
796
- cellHandles: {
797
- ...state.cellHandles,
798
- [cellId]: createRef(),
799
- },
800
- history: state.history.slice(0, -1),
801
- // Scroll to the newly created cell
802
- scrollKey: cellId,
592
+ cellIds: state.cellIds.placeCells(toRestore),
593
+ scrollKey: cellIds[0] ?? null,
803
594
  };
804
595
  },
805
596
  clearSerializedEditorState: (state, action: { cellId: CellId }) => {
@@ -852,35 +643,6 @@ const {
852
643
  },
853
644
  });
854
645
  },
855
- updateCellName: (state, action: { cellId: CellId; name: string }) => {
856
- const { cellId, name } = action;
857
- return updateCellData({
858
- state,
859
- cellId,
860
- cellReducer: (cell) => {
861
- return {
862
- ...cell,
863
- name: name,
864
- };
865
- },
866
- });
867
- },
868
- updateCellConfig: (
869
- state,
870
- action: { cellId: CellId; config: Partial<CellConfig> },
871
- ) => {
872
- const { cellId, config } = action;
873
- return updateCellData({
874
- state,
875
- cellId,
876
- cellReducer: (cell) => {
877
- return {
878
- ...cell,
879
- config: createCellConfig({ ...cell.config, ...config }),
880
- };
881
- },
882
- });
883
- },
884
646
  prepareForRun: (state, action: { cellId: CellId }) => {
885
647
  const newState = updateCellRuntimeState({
886
648
  state,
@@ -936,12 +698,7 @@ const {
936
698
  cellLogs: [...state.cellLogs, ...action.logs],
937
699
  };
938
700
  },
939
- /**
940
- * Follow the host document's cells and order.
941
- *
942
- * Dispatched without middleware, since the document already has these
943
- * changes.
944
- */
701
+ /** Follow the host document's cells and order. */
945
702
  reconcileDocument: (state, document: EditableDocument) => {
946
703
  const nextState = applyDocument(state, document);
947
704
  for (const cellId of state.cellIds.inOrderIds) {
@@ -985,127 +742,44 @@ const {
985
742
  },
986
743
  });
987
744
  },
988
- setCells: (state, cells: CellData[]) => {
989
- const cellData = Object.fromEntries(cells.map((cell) => [cell.id, cell]));
990
-
991
- // WASM has no server-side SessionView to replay outputs, so the
992
- // snapshot hydrated by notebookStateFromSession is the only source.
993
- const preserveSnapshot = isWasm();
994
- const runtimeFor = (cellId: CellId): CellRuntimeState => {
995
- if (!preserveSnapshot) {
996
- return createCellRuntimeState();
997
- }
998
- const prev = state.cellRuntime[cellId];
999
- const hasSnapshot =
1000
- prev && (prev.output != null || prev.consoleOutputs.length > 0);
1001
- return hasSnapshot ? prev : createCellRuntimeState();
1002
- };
1003
- const cellRuntime = Object.fromEntries(
1004
- cells.map((cell) => [cell.id, runtimeFor(cell.id)]),
1005
- );
1006
-
1007
- return withScratchCell({
1008
- ...state,
1009
- cellIds: MultiColumn.fromIdsAndColumns(
1010
- cells.map((cell) => [cell.id, cell.config.column]),
1011
- ),
1012
- cellData: cellData,
1013
- cellRuntime: cellRuntime,
1014
- cellHandles: Object.fromEntries(
1015
- cells.map((cell) => [cell.id, createRef()]),
1016
- ),
1017
- });
1018
- },
1019
- /**
1020
- * Move focus to next cell
1021
- *
1022
- * Creates a new cell if the current cell is the last one in the array.
1023
- *
1024
- * If needed, scrolls newly created or focused cell into view.
1025
- *
1026
- * Replicates Shift+Enter functionality of Jupyter
1027
- */
1028
- moveToNextCell: (
745
+ /** Record what the kernel last ran for each cell. */
746
+ setLastRun: (
1029
747
  state,
1030
- action: { cellId: CellId; before: boolean; noCreate?: boolean },
748
+ action: {
749
+ lastCodeRun: Record<CellId, string>;
750
+ lastExecutionTime: Record<CellId, number>;
751
+ },
1031
752
  ) => {
1032
- const { cellId, before, noCreate = false } = action;
1033
-
1034
- // Can't move focus of scratch cell
1035
- if (cellId === SCRATCH_CELL_ID) {
1036
- return state;
1037
- }
1038
-
1039
- const column = state.cellIds.findWithId(cellId);
1040
- const index = column.indexOfOrThrow(cellId);
1041
- const nextCellIndex = before ? index - 1 : index + 1;
1042
-
1043
- const isPastLastCell = nextCellIndex === column.length;
1044
- const isBeforeFirstCell = nextCellIndex === -1;
1045
-
1046
- // Create a new cell at the end and set scrollKey to focus it
1047
- if (isPastLastCell && !noCreate) {
1048
- const newCellId = CellId.create();
1049
- return {
1050
- ...state,
1051
- cellIds: state.cellIds.insertId(newCellId, column.id, nextCellIndex),
1052
- cellData: {
1053
- ...state.cellData,
1054
- [newCellId]: createCell({ id: newCellId }),
1055
- },
1056
- cellRuntime: {
1057
- ...state.cellRuntime,
1058
- [newCellId]: createCellRuntimeState(),
1059
- },
1060
- cellHandles: {
1061
- ...state.cellHandles,
1062
- [newCellId]: createRef(),
1063
- },
1064
- scrollKey: newCellId,
1065
- };
1066
- }
1067
-
1068
- if (isBeforeFirstCell && !noCreate) {
1069
- const newCellId = CellId.create();
1070
- return {
1071
- ...state,
1072
- cellIds: state.cellIds.insertId(newCellId, column.id, 0),
1073
- cellData: {
1074
- ...state.cellData,
1075
- [newCellId]: createCell({ id: newCellId }),
1076
- },
1077
- cellRuntime: {
1078
- ...state.cellRuntime,
1079
- [newCellId]: createCellRuntimeState(),
1080
- },
1081
- cellHandles: {
1082
- ...state.cellHandles,
1083
- [newCellId]: createRef(),
1084
- },
1085
- scrollKey: newCellId,
753
+ const cellData = { ...state.cellData };
754
+ for (const cellId of state.cellIds.inOrderIds) {
755
+ const cell = state.cellData[cellId];
756
+ const lastCodeRun = action.lastCodeRun[cellId] ?? null;
757
+ cellData[cellId] = {
758
+ ...cell,
759
+ lastCodeRun,
760
+ lastExecutionTime: action.lastExecutionTime[cellId] ?? null,
761
+ // A cell the kernel never ran is not stale on load.
762
+ edited: lastCodeRun !== null && isEdited(lastCodeRun, cell.code),
1086
763
  };
1087
764
  }
1088
-
1089
- if ((isPastLastCell || isBeforeFirstCell) && noCreate) {
1090
- // Trying to move past the last cell or before the first cell
1091
- // with noCreate=true - do nothing
1092
- return state;
1093
- }
1094
-
1095
- if (nextCellIndex >= 0 && nextCellIndex < column.length) {
1096
- // Move to valid cell within bounds
1097
- const nextCellId = column.atOrThrow(nextCellIndex);
1098
- // Just focus, no state change
1099
- focusAndScrollCellIntoView({
1100
- cellId: nextCellId,
1101
- cell: state.cellHandles[nextCellId],
1102
- isCodeHidden: isCellCodeHidden(state, nextCellId),
1103
- codeFocus: before ? "bottom" : "top",
1104
- variableName: undefined,
1105
- });
765
+ return { ...state, cellData };
766
+ },
767
+ /**
768
+ * Start each cell's runtime over for a new kernel. WASM keeps the outputs
769
+ * its mount-time snapshot gave it.
770
+ */
771
+ resetCellRuntimes: (state) => {
772
+ const preserveSnapshot = isWasm();
773
+ const cellRuntime = { ...state.cellRuntime };
774
+ for (const cellId of [...state.cellIds.inOrderIds, SCRATCH_CELL_ID]) {
775
+ const previous = state.cellRuntime[cellId];
776
+ const hasSnapshot =
777
+ preserveSnapshot &&
778
+ previous !== undefined &&
779
+ (previous.output != null || previous.consoleOutputs.length > 0);
780
+ cellRuntime[cellId] = hasSnapshot ? previous : createCellRuntimeState();
1106
781
  }
1107
-
1108
- return state;
782
+ return { ...state, cellRuntime };
1109
783
  },
1110
784
  markTouched: (state, action: { cellId: CellId }) => {
1111
785
  const { cellId } = action;
@@ -1427,44 +1101,6 @@ const {
1427
1101
  cellRuntime: newCellRuntime,
1428
1102
  };
1429
1103
  },
1430
- addSetupCellIfDoesntExist: (state, action: { code?: string }) => {
1431
- const { code } = action;
1432
-
1433
- if (state.cellIds.setupCellExists()) {
1434
- // Just focus on the existing setup cell
1435
- return {
1436
- ...state,
1437
- scrollKey: SETUP_CELL_ID,
1438
- };
1439
- }
1440
-
1441
- return {
1442
- ...state,
1443
- cellIds: state.cellIds.insertId(
1444
- SETUP_CELL_ID,
1445
- state.cellIds.atOrThrow(0).id,
1446
- 0,
1447
- ),
1448
- cellData: {
1449
- ...state.cellData,
1450
- [SETUP_CELL_ID]: createCell({
1451
- id: SETUP_CELL_ID,
1452
- name: SETUP_CELL_ID,
1453
- code,
1454
- edited: Boolean(code),
1455
- }),
1456
- },
1457
- cellRuntime: {
1458
- ...state.cellRuntime,
1459
- [SETUP_CELL_ID]: createCellRuntimeState(),
1460
- },
1461
- cellHandles: {
1462
- ...state.cellHandles,
1463
- [SETUP_CELL_ID]: createRef(),
1464
- },
1465
- scrollKey: SETUP_CELL_ID,
1466
- };
1467
- },
1468
1104
  });
1469
1105
 
1470
1106
  // We apply the middleware here (rather than inline in createReducerAndAtoms)
@@ -1872,20 +1508,13 @@ export function createTracebackInfoAtom(
1872
1508
  });
1873
1509
  }
1874
1510
 
1875
- /**
1876
- * Use this hook to dispatch cell actions. This hook will not cause a re-render
1877
- * when cells change.
1878
- */
1879
- export function useCellActions(
1880
- options: { skipMiddleware?: boolean } = {},
1881
- ): CellActions {
1882
- return useActions(options);
1511
+ /** The reducer's actions. Components use `useCellActions` from `./actions`. */
1512
+ export function useReducerActions(): ReducerCellActions {
1513
+ return useActions();
1883
1514
  }
1884
1515
 
1885
- /**
1886
- * Map of cell actions
1887
- */
1888
- export type CellActions = ReturnType<typeof createActions>;
1516
+ /** The actions the reducer still handles. */
1517
+ export type ReducerCellActions = ReturnType<typeof createActions>;
1889
1518
 
1890
1519
  /**
1891
1520
  * This is exported for testing purposes only.