@marimo-team/islands 0.25.3-dev173 → 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 (134) hide show
  1. package/dist/{ErrorBoundary-wT3f3iJ1.js → ErrorBoundary-DNvcAIsP.js} +1 -1
  2. package/dist/{any-language-editor-B92WMzoC.js → any-language-editor-BVFHDDQ3.js} +2 -2
  3. package/dist/{chat-ui-CuORr6fL.js → chat-ui-X-cWrL9u.js} +1222 -1222
  4. package/dist/{code-visibility-ZReaeMaF.js → code-visibility-CH1lmM1h.js} +2 -2
  5. package/dist/{common-DCevHg1m.js → common-DIrS1UR9.js} +1074 -1078
  6. package/dist/{data-grid-overlay-editor-CTC-bJus.js → data-grid-overlay-editor-DFLZvFK0.js} +1 -1
  7. package/dist/{dist-F8Ep36rk.js → dist-B1M0bGO3.js} +1 -1
  8. package/dist/{dist-D6d86eNJ.js → dist-BA13RJj5.js} +1 -1
  9. package/dist/{dist-Bvs24R_E.js → dist-BJtmSgcE.js} +48 -48
  10. package/dist/{dist-CN_BeG2I.js → dist-Ba2VIn1K.js} +53 -53
  11. package/dist/{dist-PXYI_yuP.js → dist-Bj4bmwUm.js} +12 -12
  12. package/dist/{dist-DoE4XJBC.js → dist-BmS-ZGjl.js} +8626 -8626
  13. package/dist/{dist-BorMD-uX.js → dist-Bzogh7CW.js} +87 -87
  14. package/dist/{dist-CN8zI-XC.js → dist-CThPVKaP.js} +1385 -1382
  15. package/dist/{dist-DiDibOYr.js → dist-CTi5FTiV.js} +87 -87
  16. package/dist/{dist-diIx5qJW.js → dist-Ck_C32ye.js} +2 -2
  17. package/dist/{dist-BLfKmR-l.js → dist-D1fnFp1J.js} +1 -1
  18. package/dist/{dist-OR1OnN-y.js → dist-D3x9__Fu.js} +46 -46
  19. package/dist/{dist-BMQf5zIS.js → dist-D7mrh5sO.js} +36 -36
  20. package/dist/{dist-CSJ0MDRw.js → dist-D9Q3xjiP.js} +1 -1
  21. package/dist/{dist-BuaRHxOc.js → dist-DP60Ubh8.js} +2 -2
  22. package/dist/{dist-CSbh1PNP.js → dist-DTv9KkpW.js} +12 -12
  23. package/dist/{dist-D-wwqlzJ.js → dist-DVVwOfrD.js} +109 -109
  24. package/dist/{dist-C8fAKSH1.js → dist-DYXM73-k.js} +36 -36
  25. package/dist/{dist-DBtSl3sf.js → dist-DjeybPOb.js} +1 -1
  26. package/dist/{dist-CrSP_H1M.js → dist-DtGn9N4h.js} +197 -197
  27. package/dist/{dist-CLdpGQVr.js → dist-KN80qWCP.js} +2 -2
  28. package/dist/{dist-Cg7gzw-a.js → dist-O0qFpEJh.js} +56 -56
  29. package/dist/{dist-CkTTIdBe.js → dist-OUle2RT7.js} +207 -207
  30. package/dist/{dist-BK4Oj99o.js → dist-T5rh6JvL.js} +1 -1
  31. package/dist/{error-entries-CmWMEsY_.js → error-entries-mpoD5t79.js} +4939 -4983
  32. package/dist/{esm-D2gTsXEV.js → esm-Bbw_Njjx.js} +575 -575
  33. package/dist/{esm-AMFssoNp.js → esm-mDQNEFe_.js} +354 -354
  34. package/dist/{feedback-button-DwXkmewo.js → feedback-button-DL0DJ-LB.js} +21 -21
  35. package/dist/{glide-data-editor-BlmcP-lw.js → glide-data-editor-n4wnx19S.js} +3 -3
  36. package/dist/{globals-BdRitZ4p.js → globals-NCsYyjpB.js} +1 -1
  37. package/dist/{html-to-image-bpiQh7ND.js → html-to-image-BoZthB4P.js} +454 -445
  38. package/dist/main.js +3587 -3587
  39. package/dist/{number-overlay-editor-DQG-hFHT.js → number-overlay-editor-rlvo4mo3.js} +1 -1
  40. package/dist/{process-output-BzloOI7F.js → process-output-DLS4Mqg_.js} +2 -2
  41. package/dist/{reveal-component-D3r3vSTK.js → reveal-component-BJfmRUzb.js} +3 -3
  42. package/package.json +1 -1
  43. package/src/__mocks__/document.tsx +54 -0
  44. package/src/__mocks__/notebook.ts +2 -1
  45. package/src/__mocks__/requests.ts +0 -2
  46. package/src/components/chat/chat-panel.tsx +3 -3
  47. package/src/components/datasources/__tests__/column-preview.test.tsx +6 -8
  48. package/src/components/datasources/column-preview.tsx +4 -8
  49. package/src/components/datasources/datasources.tsx +4 -3
  50. package/src/components/dependency-graph/minimap-content.tsx +2 -6
  51. package/src/components/dependency-graph/panels.tsx +2 -1
  52. package/src/components/editor/__tests__/Output.test.tsx +50 -32
  53. package/src/components/editor/__tests__/data-attributes.test.tsx +22 -11
  54. package/src/components/editor/__tests__/output-persistence.test.tsx +6 -1
  55. package/src/components/editor/actions/__tests__/name-cell-input.test.tsx +69 -0
  56. package/src/components/editor/actions/name-cell-input.tsx +4 -3
  57. package/src/components/editor/actions/useCellActionButton.tsx +20 -26
  58. package/src/components/editor/actions/useNotebookActions.tsx +13 -18
  59. package/src/components/editor/actions/useSetCodeVisibility.ts +10 -11
  60. package/src/components/editor/actions/useSetOutputsExpanded.ts +13 -18
  61. package/src/components/editor/cell/CreateCellButton.tsx +5 -9
  62. package/src/components/editor/cell/__tests__/StagedAICell.test.tsx +3 -1
  63. package/src/components/editor/cell/code/cell-editor.tsx +13 -20
  64. package/src/components/editor/cell/useAddCell.ts +4 -8
  65. package/src/components/editor/cell/useDeleteCell.tsx +10 -36
  66. package/src/components/editor/cell/useRunCells.ts +2 -5
  67. package/src/components/editor/cell/useSplitCell.tsx +2 -1
  68. package/src/components/editor/chrome/panels/logs-panel.tsx +2 -1
  69. package/src/components/editor/chrome/panels/snippets-panel.tsx +5 -11
  70. package/src/components/editor/chrome/wrapper/__tests__/pending-ai-cells.test.tsx +3 -1
  71. package/src/components/editor/columns/sortable-column.tsx +1 -1
  72. package/src/components/editor/connections/components.tsx +4 -9
  73. package/src/components/editor/controls/Controls.tsx +1 -1
  74. package/src/components/editor/controls/__tests__/notebook-menu-dropdown.test.tsx +9 -3
  75. package/src/components/editor/documentation.css +34 -11
  76. package/src/components/editor/errors/auto-fix.tsx +4 -8
  77. package/src/components/editor/file-tree/file-explorer.tsx +3 -7
  78. package/src/components/editor/links/__tests__/cell-link.test.tsx +3 -1
  79. package/src/components/editor/links/cell-link.tsx +2 -1
  80. package/src/components/editor/navigation/__tests__/clipboard.test.ts +60 -38
  81. package/src/components/editor/navigation/__tests__/navigation.test.ts +73 -64
  82. package/src/components/editor/navigation/clipboard.ts +12 -11
  83. package/src/components/editor/navigation/multi-cell-action-toolbar.tsx +11 -20
  84. package/src/components/editor/navigation/navigation.ts +20 -28
  85. package/src/components/editor/notebook-cell.tsx +7 -10
  86. package/src/components/editor/output/console/__tests__/ConsoleOutput.test.tsx +125 -69
  87. package/src/components/editor/renderers/cell-array.tsx +10 -17
  88. package/src/components/markdown/markdown-renderer.tsx +4 -8
  89. package/src/components/pages/edit-page.tsx +32 -17
  90. package/src/components/scratchpad/scratchpad.test.tsx +3 -1
  91. package/src/components/scratchpad/scratchpad.tsx +4 -7
  92. package/src/components/slides/__tests__/minimap-actions.test.tsx +9 -25
  93. package/src/components/slides/minimap.tsx +7 -3
  94. package/src/components/slides/slide-cell-view.tsx +3 -3
  95. package/src/components/sort/SortableCellsProvider.tsx +2 -1
  96. package/src/core/ai/__tests__/staged-cells.test.ts +18 -20
  97. package/src/core/ai/staged-cells.ts +8 -16
  98. package/src/core/ai/tools/__tests__/edit-notebook-tool.test.ts +2 -2
  99. package/src/core/ai/tools/__tests__/run-cells-tool.test.ts +2 -2
  100. package/src/core/ai/tools/base.ts +2 -2
  101. package/src/core/ai/tools/edit-notebook-tool.ts +13 -21
  102. package/src/core/cells/__tests__/actions.test.ts +474 -34
  103. package/src/core/cells/__tests__/add-missing-import.test.ts +7 -8
  104. package/src/core/cells/__tests__/cells.test.ts +171 -813
  105. package/src/core/cells/__tests__/document.test.ts +5 -35
  106. package/src/core/cells/__tests__/notebook-outline.test.ts +7 -1
  107. package/src/core/cells/__tests__/outputs.test.tsx +71 -53
  108. package/src/core/cells/actions.ts +391 -3
  109. package/src/core/cells/add-missing-import.ts +46 -42
  110. package/src/core/cells/cells.ts +38 -413
  111. package/src/core/cells/document.ts +7 -4
  112. package/src/core/cells/outputs.ts +12 -28
  113. package/src/core/codemirror/cells/extensions.ts +6 -6
  114. package/src/core/codemirror/cells/state.ts +1 -1
  115. package/src/core/codemirror/keymaps/vim.ts +2 -2
  116. package/src/core/edit-app.tsx +19 -27
  117. package/src/core/islands/bridge.ts +0 -2
  118. package/src/core/kernel/handlers.ts +3 -3
  119. package/src/core/network/__tests__/requests-lazy.test.ts +1 -3
  120. package/src/core/network/requests-lazy.ts +0 -2
  121. package/src/core/network/requests-network.ts +0 -16
  122. package/src/core/network/requests-static.ts +0 -2
  123. package/src/core/network/requests-toasting.tsx +0 -2
  124. package/src/core/network/types.ts +0 -7
  125. package/src/core/notebook/document/__tests__/codemirror.test.ts +5 -0
  126. package/src/core/notebook/document/__tests__/draft.test.ts +41 -0
  127. package/src/core/notebook/document/__tests__/state.test.ts +21 -0
  128. package/src/core/notebook/document/draft.ts +35 -5
  129. package/src/core/notebook/document/index.ts +1 -0
  130. package/src/core/notebook/document/state.ts +6 -2
  131. package/src/core/session/useNotebookSession.tsx +6 -2
  132. package/src/core/wasm/bridge.ts +0 -16
  133. package/src/css/globals.css +56 -22
  134. package/src/stories/runtime-story.tsx +22 -15
@@ -3,7 +3,8 @@
3
3
  import { getRequestClient } from "../network/requests";
4
4
  import { store } from "../state/jotai";
5
5
  import { variablesAtom } from "../variables/state";
6
- import { type CellActions, notebookAtom } from "./cells";
6
+ import { notebookAtom } from "./cells";
7
+ import type { CreateCell, CreateCellOptions } from "./actions";
7
8
  import { CellId } from "./ids";
8
9
 
9
10
  /**
@@ -50,82 +51,85 @@ export function maybeAddMissingImport({
50
51
  /**
51
52
  * Adds a marimo import to the notebook if not already present.
52
53
  * @param autoInstantiate Whether to automatically run the cell.
53
- * @param createNewCell The function to create a new cell.
54
- * @param fromCellId The cell to add the import to.
54
+ * @param createCell The command that creates a cell.
55
+ * @param fromCellId The cell to add the import next to.
55
56
  * @param before Whether to add the import before or after the cell.
56
57
  *
57
58
  * Returns the ID of the new cell if added, otherwise null.
58
59
  */
59
60
  export function maybeAddMarimoImport({
60
61
  autoInstantiate,
61
- createNewCell,
62
+ createCell,
62
63
  fromCellId,
63
64
  before,
64
65
  }: {
65
66
  autoInstantiate: boolean;
66
- createNewCell: CellActions["createNewCell"];
67
+ createCell: CreateCell;
67
68
  fromCellId?: CellId | null;
68
69
  before?: boolean;
69
70
  }): CellId | null {
70
- const client = getRequestClient();
71
- let newCellId: CellId | null = null;
72
- const added = maybeAddMissingImport({
71
+ return addImport({
73
72
  moduleName: "marimo",
74
73
  variableName: "mo",
75
- onAddImport: (importStatement) => {
76
- newCellId = CellId.create();
77
- createNewCell({
78
- cellId: fromCellId ?? "__end__",
79
- before: before ?? false,
80
- code: importStatement,
81
- lastCodeRun: autoInstantiate ? importStatement : undefined,
82
- newCellId: newCellId,
83
- skipIfCodeExists: true,
84
- autoFocus: false,
85
- });
86
- if (autoInstantiate) {
87
- void client.sendRun({
88
- cellIds: [newCellId],
89
- codes: [importStatement],
90
- });
91
- }
92
- },
74
+ autoInstantiate,
75
+ createCell,
76
+ placement: before ? { before: fromCellId } : { after: fromCellId },
93
77
  });
94
- return added ? newCellId : null;
95
78
  }
96
79
 
97
80
  export function maybeAddAltairImport({
98
81
  autoInstantiate,
99
- createNewCell,
82
+ createCell,
100
83
  fromCellId,
101
84
  }: {
102
85
  autoInstantiate: boolean;
103
- createNewCell: CellActions["createNewCell"];
86
+ createCell: CreateCell;
104
87
  fromCellId?: CellId | null;
105
88
  }): CellId | null {
106
- const client = getRequestClient();
107
- let newCellId: CellId | null = null;
108
- const added = maybeAddMissingImport({
89
+ return addImport({
109
90
  moduleName: "altair",
110
91
  variableName: "alt",
92
+ autoInstantiate,
93
+ createCell,
94
+ placement: { after: fromCellId },
95
+ });
96
+ }
97
+
98
+ /** Create the import cell, and run it when the notebook runs on its own. */
99
+ function addImport({
100
+ moduleName,
101
+ variableName,
102
+ autoInstantiate,
103
+ createCell,
104
+ placement,
105
+ }: {
106
+ moduleName: string;
107
+ variableName: string;
108
+ autoInstantiate: boolean;
109
+ createCell: CreateCell;
110
+ placement: Pick<CreateCellOptions, "after" | "before">;
111
+ }): CellId | null {
112
+ let newCellId: CellId | null = null;
113
+ maybeAddMissingImport({
114
+ moduleName,
115
+ variableName,
111
116
  onAddImport: (importStatement) => {
112
- newCellId = CellId.create();
113
- createNewCell({
114
- cellId: fromCellId ?? "__end__",
115
- before: false,
117
+ const id = CellId.create();
118
+ const created = createCell({
119
+ ...placement,
120
+ id,
116
121
  code: importStatement,
117
- lastCodeRun: autoInstantiate ? importStatement : undefined,
118
- newCellId: newCellId,
119
122
  skipIfCodeExists: true,
120
- autoFocus: false,
123
+ focus: false,
121
124
  });
122
- if (autoInstantiate) {
123
- void client.sendRun({
125
+ newCellId = created === null ? null : id;
126
+ if (autoInstantiate && newCellId !== null) {
127
+ void getRequestClient().sendRun({
124
128
  cellIds: [newCellId],
125
129
  codes: [importStatement],
126
130
  });
127
131
  }
128
132
  },
129
133
  });
130
- return added ? newCellId : null;
134
+ return newCellId;
131
135
  }
@@ -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,7 +41,6 @@ import {
42
41
  type CellData,
43
42
  type CellRuntimeState,
44
43
  createCell,
45
- createCellConfig,
46
44
  createCellRuntimeState,
47
45
  isEdited,
48
46
  } from "./types";
@@ -56,17 +54,21 @@ import {
56
54
  notebookQueueOrRunningCount,
57
55
  } from "./utils";
58
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
+
59
65
  /**
60
- * History entry for undoing a cell deletion.
66
+ * History entry for undoing one deletion, of one or more cells.
61
67
  */
62
68
  export interface UndoDeleteEntry {
63
69
  type: "delete";
64
- name: string;
65
- serializedEditorState: SerializedEditorState;
66
- column: CellColumnId;
67
- index: CellIndex;
68
- isSetupCell: boolean;
69
- config: CellConfig;
70
+ /** In document order. Each cell is placed after the one before it. */
71
+ cells: DeletedCell[];
70
72
  }
71
73
 
72
74
  /**
@@ -162,40 +164,6 @@ export function initialNotebookState(): NotebookState {
162
164
  });
163
165
  }
164
166
 
165
- /** The target cell ID to create a new cell relative to. Can be:
166
- * - A CellId string for an existing cell
167
- * - "__end__" to append at the end of the first column
168
- * - {type: "__end__", columnId} to append at the end of a specific column
169
- */
170
- export type CellPosition =
171
- | CellId
172
- | "__end__"
173
- | { type: "__end__"; columnId: CellColumnId };
174
-
175
- export interface CreateNewCellAction {
176
- cellId: CellPosition;
177
- /** Whether to insert before (true) or after (false) the target cell */
178
- before: boolean;
179
- /** Initial code content for the new cell */
180
- code?: string;
181
- /** Optional name for the new cell */
182
- name?: string;
183
- /** Optional cell configuration */
184
- config?: CellConfig;
185
- /** The last executed code for the new cell */
186
- lastCodeRun?: string;
187
- /** Timestamp of the last execution */
188
- lastExecutionTime?: number;
189
- /** Optional custom ID for the new cell. Auto-generated if not provided */
190
- newCellId?: CellId;
191
- /** Whether to focus the new cell after creation */
192
- autoFocus?: boolean;
193
- /** If true, skip creation if code already exists */
194
- skipIfCodeExists?: boolean;
195
- /** Hide the code in the new cell. This will be initially shown until the cell is blurred for the first time. */
196
- hideCode?: boolean;
197
- }
198
-
199
167
  /**
200
168
  * Actions and reducer for the notebook state.
201
169
  */
@@ -206,85 +174,6 @@ const {
206
174
  useActions,
207
175
  valueAtom: notebookAtom,
208
176
  } = createReducerAndAtoms(initialNotebookState, {
209
- createNewCell: (state, action: CreateNewCellAction) => {
210
- const {
211
- cellId,
212
- before,
213
- code,
214
- name,
215
- config,
216
- lastCodeRun = null,
217
- lastExecutionTime = null,
218
- autoFocus = true,
219
- skipIfCodeExists = false,
220
- hideCode = undefined,
221
- } = action;
222
-
223
- let columnId: CellColumnId;
224
- let cellIndex: number;
225
-
226
- // If skipIfCodeExists is true, check if the code already exists in the notebook
227
- if (skipIfCodeExists) {
228
- for (const cellId of state.cellIds.inOrderIds) {
229
- if (state.cellData[cellId]?.code === code) {
230
- return state;
231
- }
232
- }
233
- }
234
-
235
- if (cellId === "__end__") {
236
- const column = state.cellIds.atOrThrow(0);
237
- columnId = column.id;
238
- cellIndex = column.length;
239
- } else if (typeof cellId === "string") {
240
- const column = state.cellIds.findWithId(cellId);
241
- columnId = column.id;
242
- cellIndex = column.topLevelIds.indexOf(cellId);
243
- } else if (cellId.type === "__end__") {
244
- const column =
245
- state.cellIds.get(cellId.columnId) || state.cellIds.atOrThrow(0);
246
- columnId = column.id;
247
- cellIndex = column.length;
248
- } else {
249
- throw new Error("Invalid cellId");
250
- }
251
-
252
- const newCellId = action.newCellId || CellId.create();
253
- const insertionIndex = before ? cellIndex : cellIndex + 1;
254
-
255
- return {
256
- ...state,
257
- cellIds: state.cellIds.insertId(newCellId, columnId, insertionIndex),
258
- cellData: {
259
- ...state.cellData,
260
- [newCellId]: createCell({
261
- id: newCellId,
262
- code,
263
- name,
264
- lastCodeRun,
265
- config: createCellConfig({
266
- ...config,
267
- ...(hideCode != null && { hide_code: hideCode }),
268
- }),
269
- lastExecutionTime,
270
- edited: Boolean(code) && code !== lastCodeRun,
271
- }),
272
- },
273
- cellRuntime: {
274
- ...state.cellRuntime,
275
- [newCellId]: createCellRuntimeState(),
276
- },
277
- cellHandles: {
278
- ...state.cellHandles,
279
- [newCellId]: createRef(),
280
- },
281
- scrollKey: autoFocus ? newCellId : null,
282
- untouchedNewCells:
283
- hideCode && autoFocus
284
- ? new Set([...state.untouchedNewCells, newCellId])
285
- : state.untouchedNewCells,
286
- };
287
- },
288
177
  moveCell: (
289
178
  state,
290
179
  action: {
@@ -679,128 +568,29 @@ const {
679
568
  cellIds: state.cellIds.compact(),
680
569
  };
681
570
  },
682
- deleteCell: (state, action: { cellId: CellId }) => {
683
- const cellId = action.cellId;
684
-
685
- // Can't delete the last cell, across all columns
686
- if (state.cellIds.hasOnlyOneId()) {
687
- return state;
688
- }
689
-
690
- let column: CollapsibleTree<CellId>;
691
- try {
692
- column = state.cellIds.findWithId(cellId);
693
- } catch (error) {
694
- // Expected for kernel-only cells or out-of-order transactions.
695
- Logger.warn("Skipping delete for missing cellId", {
696
- cellId,
697
- error,
698
- });
699
- return state;
700
- }
701
-
702
- const cellIndex = column.indexOfOrThrow(cellId);
703
- const focusIndex = cellIndex === 0 ? 1 : cellIndex - 1;
704
- let scrollKey: CellId | null = null;
705
- if (column.length > 1) {
706
- scrollKey = column.atOrThrow(focusIndex);
707
- }
708
-
709
- const editorView = state.cellHandles[cellId].current?.editorView;
710
- const serializedEditorState = editorView?.state.toJSON({
711
- history: historyField,
712
- });
713
- if (serializedEditorState) {
714
- serializedEditorState.doc = state.cellData[cellId].code;
715
- }
716
-
717
- // release the granular atom(s) created for this cell
718
- releaseCellAtoms(cellId);
719
- const prevData = state.cellData[cellId];
720
- return {
721
- ...state,
722
- cellIds: state.cellIds.deleteById(cellId),
723
- history: [
724
- ...state.history,
725
- {
726
- type: "delete",
727
- name: prevData.name,
728
- serializedEditorState: serializedEditorState,
729
- column: column.id,
730
- index: cellIndex,
731
- isSetupCell: cellId === SETUP_CELL_ID,
732
- config: prevData.config,
733
- },
734
- ],
735
- scrollKey: scrollKey,
736
- };
737
- },
738
- undoDeleteCell: (state) => {
739
- 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
+ ) {
740
583
  return state;
741
584
  }
742
-
743
- const last = state.history[state.history.length - 1];
744
-
745
- if (last.type === "move") {
746
- const { cellIds, placements } = last;
747
- if (
748
- cellIds.length === 0 ||
749
- placements.length !== cellIds.length ||
750
- cellIds.some((id) => !state.cellData[id])
751
- ) {
752
- return { ...state, history: state.history.slice(0, -1) };
753
- }
754
- const toRestore = cellIds.map((id, i) => ({
755
- id,
756
- columnId: placements[i].columnId,
757
- index: placements[i].index,
758
- }));
759
- return {
760
- ...state,
761
- cellIds: state.cellIds.placeCells(toRestore),
762
- history: state.history.slice(0, -1),
763
- scrollKey: cellIds[0] ?? null,
764
- };
765
- }
766
-
767
- const {
768
- name,
769
- serializedEditorState = { doc: "" },
770
- column,
771
- index,
772
- isSetupCell,
773
- config,
774
- } = last;
775
-
776
- const cellId = isSetupCell ? SETUP_CELL_ID : CellId.create();
777
- const undoCell = createCell({
778
- id: cellId,
779
- name,
780
- code: serializedEditorState.doc,
781
- edited: serializedEditorState.doc.trim().length > 0,
782
- serializedEditorState,
783
- config,
784
- });
785
-
585
+ const toRestore = cellIds.map((id, i) => ({
586
+ id,
587
+ columnId: placements[i].columnId,
588
+ index: placements[i].index,
589
+ }));
786
590
  return {
787
591
  ...state,
788
- cellIds: state.cellIds.insertId(cellId, column, index),
789
- cellData: {
790
- ...state.cellData,
791
- [cellId]: undoCell,
792
- },
793
- cellRuntime: {
794
- ...state.cellRuntime,
795
- [cellId]: createCellRuntimeState(),
796
- },
797
- cellHandles: {
798
- ...state.cellHandles,
799
- [cellId]: createRef(),
800
- },
801
- history: state.history.slice(0, -1),
802
- // Scroll to the newly created cell
803
- scrollKey: cellId,
592
+ cellIds: state.cellIds.placeCells(toRestore),
593
+ scrollKey: cellIds[0] ?? null,
804
594
  };
805
595
  },
806
596
  clearSerializedEditorState: (state, action: { cellId: CellId }) => {
@@ -853,35 +643,6 @@ const {
853
643
  },
854
644
  });
855
645
  },
856
- updateCellName: (state, action: { cellId: CellId; name: string }) => {
857
- const { cellId, name } = action;
858
- return updateCellData({
859
- state,
860
- cellId,
861
- cellReducer: (cell) => {
862
- return {
863
- ...cell,
864
- name: name,
865
- };
866
- },
867
- });
868
- },
869
- updateCellConfig: (
870
- state,
871
- action: { cellId: CellId; config: Partial<CellConfig> },
872
- ) => {
873
- const { cellId, config } = action;
874
- return updateCellData({
875
- state,
876
- cellId,
877
- cellReducer: (cell) => {
878
- return {
879
- ...cell,
880
- config: createCellConfig({ ...cell.config, ...config }),
881
- };
882
- },
883
- });
884
- },
885
646
  prepareForRun: (state, action: { cellId: CellId }) => {
886
647
  const newState = updateCellRuntimeState({
887
648
  state,
@@ -1020,97 +781,6 @@ const {
1020
781
  }
1021
782
  return { ...state, cellRuntime };
1022
783
  },
1023
- /**
1024
- * Move focus to next cell
1025
- *
1026
- * Creates a new cell if the current cell is the last one in the array.
1027
- *
1028
- * If needed, scrolls newly created or focused cell into view.
1029
- *
1030
- * Replicates Shift+Enter functionality of Jupyter
1031
- */
1032
- moveToNextCell: (
1033
- state,
1034
- action: { cellId: CellId; before: boolean; noCreate?: boolean },
1035
- ) => {
1036
- const { cellId, before, noCreate = false } = action;
1037
-
1038
- // Can't move focus of scratch cell
1039
- if (cellId === SCRATCH_CELL_ID) {
1040
- return state;
1041
- }
1042
-
1043
- const column = state.cellIds.findWithId(cellId);
1044
- const index = column.indexOfOrThrow(cellId);
1045
- const nextCellIndex = before ? index - 1 : index + 1;
1046
-
1047
- const isPastLastCell = nextCellIndex === column.length;
1048
- const isBeforeFirstCell = nextCellIndex === -1;
1049
-
1050
- // Create a new cell at the end and set scrollKey to focus it
1051
- if (isPastLastCell && !noCreate) {
1052
- const newCellId = CellId.create();
1053
- return {
1054
- ...state,
1055
- cellIds: state.cellIds.insertId(newCellId, column.id, nextCellIndex),
1056
- cellData: {
1057
- ...state.cellData,
1058
- [newCellId]: createCell({ id: newCellId }),
1059
- },
1060
- cellRuntime: {
1061
- ...state.cellRuntime,
1062
- [newCellId]: createCellRuntimeState(),
1063
- },
1064
- cellHandles: {
1065
- ...state.cellHandles,
1066
- [newCellId]: createRef(),
1067
- },
1068
- scrollKey: newCellId,
1069
- };
1070
- }
1071
-
1072
- if (isBeforeFirstCell && !noCreate) {
1073
- const newCellId = CellId.create();
1074
- return {
1075
- ...state,
1076
- cellIds: state.cellIds.insertId(newCellId, column.id, 0),
1077
- cellData: {
1078
- ...state.cellData,
1079
- [newCellId]: createCell({ id: newCellId }),
1080
- },
1081
- cellRuntime: {
1082
- ...state.cellRuntime,
1083
- [newCellId]: createCellRuntimeState(),
1084
- },
1085
- cellHandles: {
1086
- ...state.cellHandles,
1087
- [newCellId]: createRef(),
1088
- },
1089
- scrollKey: newCellId,
1090
- };
1091
- }
1092
-
1093
- if ((isPastLastCell || isBeforeFirstCell) && noCreate) {
1094
- // Trying to move past the last cell or before the first cell
1095
- // with noCreate=true - do nothing
1096
- return state;
1097
- }
1098
-
1099
- if (nextCellIndex >= 0 && nextCellIndex < column.length) {
1100
- // Move to valid cell within bounds
1101
- const nextCellId = column.atOrThrow(nextCellIndex);
1102
- // Just focus, no state change
1103
- focusAndScrollCellIntoView({
1104
- cellId: nextCellId,
1105
- cell: state.cellHandles[nextCellId],
1106
- isCodeHidden: isCellCodeHidden(state, nextCellId),
1107
- codeFocus: before ? "bottom" : "top",
1108
- variableName: undefined,
1109
- });
1110
- }
1111
-
1112
- return state;
1113
- },
1114
784
  markTouched: (state, action: { cellId: CellId }) => {
1115
785
  const { cellId } = action;
1116
786
 
@@ -1431,44 +1101,6 @@ const {
1431
1101
  cellRuntime: newCellRuntime,
1432
1102
  };
1433
1103
  },
1434
- addSetupCellIfDoesntExist: (state, action: { code?: string }) => {
1435
- const { code } = action;
1436
-
1437
- if (state.cellIds.setupCellExists()) {
1438
- // Just focus on the existing setup cell
1439
- return {
1440
- ...state,
1441
- scrollKey: SETUP_CELL_ID,
1442
- };
1443
- }
1444
-
1445
- return {
1446
- ...state,
1447
- cellIds: state.cellIds.insertId(
1448
- SETUP_CELL_ID,
1449
- state.cellIds.atOrThrow(0).id,
1450
- 0,
1451
- ),
1452
- cellData: {
1453
- ...state.cellData,
1454
- [SETUP_CELL_ID]: createCell({
1455
- id: SETUP_CELL_ID,
1456
- name: SETUP_CELL_ID,
1457
- code,
1458
- edited: Boolean(code),
1459
- }),
1460
- },
1461
- cellRuntime: {
1462
- ...state.cellRuntime,
1463
- [SETUP_CELL_ID]: createCellRuntimeState(),
1464
- },
1465
- cellHandles: {
1466
- ...state.cellHandles,
1467
- [SETUP_CELL_ID]: createRef(),
1468
- },
1469
- scrollKey: SETUP_CELL_ID,
1470
- };
1471
- },
1472
1104
  });
1473
1105
 
1474
1106
  // We apply the middleware here (rather than inline in createReducerAndAtoms)
@@ -1876,20 +1508,13 @@ export function createTracebackInfoAtom(
1876
1508
  });
1877
1509
  }
1878
1510
 
1879
- /**
1880
- * Use this hook to dispatch cell actions. This hook will not cause a re-render
1881
- * when cells change.
1882
- */
1883
- export function useCellActions(
1884
- options: { skipMiddleware?: boolean } = {},
1885
- ): CellActions {
1886
- return useActions(options);
1511
+ /** The reducer's actions. Components use `useCellActions` from `./actions`. */
1512
+ export function useReducerActions(): ReducerCellActions {
1513
+ return useActions();
1887
1514
  }
1888
1515
 
1889
- /**
1890
- * Map of cell actions
1891
- */
1892
- export type CellActions = ReturnType<typeof createActions>;
1516
+ /** The actions the reducer still handles. */
1517
+ export type ReducerCellActions = ReturnType<typeof createActions>;
1893
1518
 
1894
1519
  /**
1895
1520
  * This is exported for testing purposes only.