@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
@@ -2,17 +2,29 @@
2
2
 
3
3
  import { act, fireEvent, render, screen } from "@testing-library/react";
4
4
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
5
+ import { createStore, Provider } from "jotai";
5
6
  import { SetupMocks } from "@/__mocks__/common";
7
+ import { mockDocumentModel } from "@/__mocks__/document";
6
8
  import { cellId } from "@/__tests__/branded";
7
9
  import { TooltipProvider } from "@/components/ui/tooltip";
8
10
  import type { WithResponse } from "@/core/cells/types";
11
+ import { DocumentProvider } from "@/core/notebook/document";
9
12
  import type { OutputMessage } from "@/core/kernel/messages";
10
13
  import { CONSOLE_CLEAR_DEBOUNCE_MS, ConsoleOutput } from "../ConsoleOutput";
11
14
 
12
15
  SetupMocks.resizeObserver();
13
16
 
17
+ const store = createStore();
18
+ const { model } = mockDocumentModel(store, null);
19
+
14
20
  const renderWithProvider = (ui: React.ReactElement) => {
15
- return render(<TooltipProvider>{ui}</TooltipProvider>);
21
+ return render(
22
+ <Provider store={store}>
23
+ <DocumentProvider model={model}>
24
+ <TooltipProvider>{ui}</TooltipProvider>
25
+ </DocumentProvider>
26
+ </Provider>,
27
+ );
16
28
  };
17
29
 
18
30
  describe("ConsoleOutput integration", () => {
@@ -104,13 +116,17 @@ describe("ConsoleOutput pdb history", () => {
104
116
  ];
105
117
 
106
118
  rerender(
107
- <TooltipProvider>
108
- <ConsoleOutput
109
- {...defaultProps}
110
- consoleOutputs={outputs2}
111
- onSubmitDebugger={onSubmitDebugger}
112
- />
113
- </TooltipProvider>,
119
+ <Provider store={store}>
120
+ <DocumentProvider model={model}>
121
+ <TooltipProvider>
122
+ <ConsoleOutput
123
+ {...defaultProps}
124
+ consoleOutputs={outputs2}
125
+ onSubmitDebugger={onSubmitDebugger}
126
+ />
127
+ </TooltipProvider>
128
+ </DocumentProvider>
129
+ </Provider>,
114
130
  );
115
131
 
116
132
  // New StdInput mounted — press ArrowUp to recall previous command
@@ -138,13 +154,17 @@ describe("ConsoleOutput pdb history", () => {
138
154
  fireEvent.keyDown(nameInput, { key: "Enter" });
139
155
 
140
156
  rerender(
141
- <TooltipProvider>
142
- <ConsoleOutput
143
- {...defaultProps}
144
- consoleOutputs={[stdinPrompt("Name: ", "marimo"), passwordPrompt]}
145
- onSubmitDebugger={onSubmitDebugger}
146
- />
147
- </TooltipProvider>,
157
+ <Provider store={store}>
158
+ <DocumentProvider model={model}>
159
+ <TooltipProvider>
160
+ <ConsoleOutput
161
+ {...defaultProps}
162
+ consoleOutputs={[stdinPrompt("Name: ", "marimo"), passwordPrompt]}
163
+ onSubmitDebugger={onSubmitDebugger}
164
+ />
165
+ </TooltipProvider>
166
+ </DocumentProvider>
167
+ </Provider>,
148
168
  );
149
169
  const input = screen.getByTestId("console-input");
150
170
  expect(input).toHaveAttribute("type", "password");
@@ -161,16 +181,20 @@ describe("ConsoleOutput pdb history", () => {
161
181
  expect(input).toHaveValue("");
162
182
 
163
183
  rerender(
164
- <TooltipProvider>
165
- <ConsoleOutput
166
- {...defaultProps}
167
- consoleOutputs={[
168
- stdinPrompt("Name: ", "marimo"),
169
- { ...passwordPrompt, response: "" },
170
- stdinPrompt("Name: "),
171
- ]}
172
- />
173
- </TooltipProvider>,
184
+ <Provider store={store}>
185
+ <DocumentProvider model={model}>
186
+ <TooltipProvider>
187
+ <ConsoleOutput
188
+ {...defaultProps}
189
+ consoleOutputs={[
190
+ stdinPrompt("Name: ", "marimo"),
191
+ { ...passwordPrompt, response: "" },
192
+ stdinPrompt("Name: "),
193
+ ]}
194
+ />
195
+ </TooltipProvider>
196
+ </DocumentProvider>
197
+ </Provider>,
174
198
  );
175
199
  const nextInput = screen.getByTestId("console-input");
176
200
  fireEvent.keyDown(nextInput, { key: "ArrowUp" });
@@ -239,13 +263,17 @@ describe("ConsoleOutput pdb history", () => {
239
263
  stdinPrompt("(Pdb) "),
240
264
  ];
241
265
  rerender(
242
- <TooltipProvider>
243
- <ConsoleOutput
244
- {...defaultProps}
245
- consoleOutputs={outputs2}
246
- onSubmitDebugger={onSubmitDebugger}
247
- />
248
- </TooltipProvider>,
266
+ <Provider store={store}>
267
+ <DocumentProvider model={model}>
268
+ <TooltipProvider>
269
+ <ConsoleOutput
270
+ {...defaultProps}
271
+ consoleOutputs={outputs2}
272
+ onSubmitDebugger={onSubmitDebugger}
273
+ />
274
+ </TooltipProvider>
275
+ </DocumentProvider>
276
+ </Provider>,
249
277
  );
250
278
 
251
279
  // Submit an empty value; this should NOT enter the history stack.
@@ -258,13 +286,17 @@ describe("ConsoleOutput pdb history", () => {
258
286
  stdinPrompt("(Pdb) "),
259
287
  ];
260
288
  rerender(
261
- <TooltipProvider>
262
- <ConsoleOutput
263
- {...defaultProps}
264
- consoleOutputs={outputs3}
265
- onSubmitDebugger={onSubmitDebugger}
266
- />
267
- </TooltipProvider>,
289
+ <Provider store={store}>
290
+ <DocumentProvider model={model}>
291
+ <TooltipProvider>
292
+ <ConsoleOutput
293
+ {...defaultProps}
294
+ consoleOutputs={outputs3}
295
+ onSubmitDebugger={onSubmitDebugger}
296
+ />
297
+ </TooltipProvider>
298
+ </DocumentProvider>
299
+ </Provider>,
268
300
  );
269
301
 
270
302
  // ArrowUp should jump back to "step", skipping the empty submission.
@@ -299,13 +331,17 @@ describe("ConsoleOutput pdb history", () => {
299
331
  ];
300
332
 
301
333
  rerender(
302
- <TooltipProvider>
303
- <ConsoleOutput
304
- {...defaultProps}
305
- consoleOutputs={outputs2}
306
- onSubmitDebugger={onSubmitDebugger}
307
- />
308
- </TooltipProvider>,
334
+ <Provider store={store}>
335
+ <DocumentProvider model={model}>
336
+ <TooltipProvider>
337
+ <ConsoleOutput
338
+ {...defaultProps}
339
+ consoleOutputs={outputs2}
340
+ onSubmitDebugger={onSubmitDebugger}
341
+ />
342
+ </TooltipProvider>
343
+ </DocumentProvider>
344
+ </Provider>,
309
345
  );
310
346
 
311
347
  // Submit "print(x)"
@@ -321,13 +357,17 @@ describe("ConsoleOutput pdb history", () => {
321
357
  ];
322
358
 
323
359
  rerender(
324
- <TooltipProvider>
325
- <ConsoleOutput
326
- {...defaultProps}
327
- consoleOutputs={outputs3}
328
- onSubmitDebugger={onSubmitDebugger}
329
- />
330
- </TooltipProvider>,
360
+ <Provider store={store}>
361
+ <DocumentProvider model={model}>
362
+ <TooltipProvider>
363
+ <ConsoleOutput
364
+ {...defaultProps}
365
+ consoleOutputs={outputs3}
366
+ onSubmitDebugger={onSubmitDebugger}
367
+ />
368
+ </TooltipProvider>
369
+ </DocumentProvider>
370
+ </Provider>,
331
371
  );
332
372
 
333
373
  // ArrowUp should show most recent command first
@@ -370,13 +410,17 @@ describe("ConsoleOutput pdb history", () => {
370
410
  // Same outputs, but the cell isn't interrupted -- this is a real
371
411
  // bare-Enter submission, so we should render the (empty) placeholder.
372
412
  rerender(
373
- <TooltipProvider>
374
- <ConsoleOutput
375
- {...defaultProps}
376
- consoleOutputs={interruptedOutputs}
377
- interrupted={false}
378
- />
379
- </TooltipProvider>,
413
+ <Provider store={store}>
414
+ <DocumentProvider model={model}>
415
+ <TooltipProvider>
416
+ <ConsoleOutput
417
+ {...defaultProps}
418
+ consoleOutputs={interruptedOutputs}
419
+ interrupted={false}
420
+ />
421
+ </TooltipProvider>
422
+ </DocumentProvider>
423
+ </Provider>,
380
424
  );
381
425
 
382
426
  expect(screen.getByLabelText("stdin response")).toBeInTheDocument();
@@ -426,9 +470,13 @@ describe("ConsoleOutput debounced clearing", () => {
426
470
 
427
471
  // Clear outputs (simulates cell re-run)
428
472
  rerender(
429
- <TooltipProvider>
430
- <ConsoleOutput {...defaultProps} consoleOutputs={[]} />
431
- </TooltipProvider>,
473
+ <Provider store={store}>
474
+ <DocumentProvider model={model}>
475
+ <TooltipProvider>
476
+ <ConsoleOutput {...defaultProps} consoleOutputs={[]} />
477
+ </TooltipProvider>
478
+ </DocumentProvider>
479
+ </Provider>,
432
480
  );
433
481
 
434
482
  // Old output should still be visible during debounce period
@@ -437,9 +485,13 @@ describe("ConsoleOutput debounced clearing", () => {
437
485
  // New outputs arrive before debounce fires
438
486
  const outputs2 = [createOutput("new output")];
439
487
  rerender(
440
- <TooltipProvider>
441
- <ConsoleOutput {...defaultProps} consoleOutputs={outputs2} />
442
- </TooltipProvider>,
488
+ <Provider store={store}>
489
+ <DocumentProvider model={model}>
490
+ <TooltipProvider>
491
+ <ConsoleOutput {...defaultProps} consoleOutputs={outputs2} />
492
+ </TooltipProvider>
493
+ </DocumentProvider>
494
+ </Provider>,
443
495
  );
444
496
 
445
497
  // New output should be shown immediately
@@ -458,9 +510,13 @@ describe("ConsoleOutput debounced clearing", () => {
458
510
 
459
511
  // Clear outputs
460
512
  rerender(
461
- <TooltipProvider>
462
- <ConsoleOutput {...defaultProps} consoleOutputs={[]} />
463
- </TooltipProvider>,
513
+ <Provider store={store}>
514
+ <DocumentProvider model={model}>
515
+ <TooltipProvider>
516
+ <ConsoleOutput {...defaultProps} consoleOutputs={[]} />
517
+ </TooltipProvider>
518
+ </DocumentProvider>
519
+ </Provider>,
464
520
  );
465
521
 
466
522
  // Still visible during debounce
@@ -33,10 +33,10 @@ import type { CellColumnId } from "@/utils/id-tree";
33
33
  import { invariant } from "@/utils/invariant";
34
34
  import {
35
35
  columnIdsAtom,
36
- useCellActions,
37
36
  useCellIds,
38
37
  useScrollKey,
39
38
  } from "../../../core/cells/cells";
39
+ import { useCellActions } from "../../../core/cells/actions";
40
40
  import { formatAll } from "../../../core/codemirror/format";
41
41
  import type { AppConfig, UserConfig } from "../../../core/config/config-schema";
42
42
  import type { AppMode } from "../../../core/mode";
@@ -267,7 +267,7 @@ const AddCellButtons: React.FC<{
267
267
  columnId: CellColumnId;
268
268
  className?: string;
269
269
  }> = ({ columnId, className }) => {
270
- const { createNewCell } = useCellActions();
270
+ const { createCell } = useCellActions();
271
271
  const [isAiButtonOpen, isAiButtonOpenActions] = useBoolean(false);
272
272
  const aiEnabled = useAtomValue(aiEnabledAtom);
273
273
  const aiFeaturesEnabled = useAtomValue(aiFeaturesEnabledAtom);
@@ -291,12 +291,7 @@ const AddCellButtons: React.FC<{
291
291
  variant="text"
292
292
  size="sm"
293
293
  disabled={!canInteractWithApp}
294
- onClick={() =>
295
- createNewCell({
296
- cellId: { type: "__end__", columnId },
297
- before: false,
298
- })
299
- }
294
+ onClick={() => createCell({ endOf: columnId })}
300
295
  >
301
296
  <SquareCodeIcon className="mr-2 size-4 shrink-0" />
302
297
  Python
@@ -307,13 +302,12 @@ const AddCellButtons: React.FC<{
307
302
  size="sm"
308
303
  disabled={!canInteractWithApp}
309
304
  onClick={() => {
310
- maybeAddMarimoImport({ autoInstantiate: true, createNewCell });
305
+ maybeAddMarimoImport({ autoInstantiate: true, createCell });
311
306
 
312
- createNewCell({
313
- cellId: { type: "__end__", columnId },
314
- before: false,
307
+ createCell({
308
+ endOf: columnId,
315
309
  code: LanguageAdapters.markdown.defaultCode,
316
- hideCode: MARKDOWN_INITIAL_HIDE_CODE,
310
+ config: { hide_code: MARKDOWN_INITIAL_HIDE_CODE },
317
311
  });
318
312
  }}
319
313
  >
@@ -326,11 +320,10 @@ const AddCellButtons: React.FC<{
326
320
  size="sm"
327
321
  disabled={!canInteractWithApp}
328
322
  onClick={() => {
329
- maybeAddMarimoImport({ autoInstantiate: true, createNewCell });
323
+ maybeAddMarimoImport({ autoInstantiate: true, createCell });
330
324
 
331
- createNewCell({
332
- cellId: { type: "__end__", columnId },
333
- before: false,
325
+ createCell({
326
+ endOf: columnId,
334
327
  code: LanguageAdapters.sql.defaultCode,
335
328
  });
336
329
  }}
@@ -12,7 +12,7 @@ import {
12
12
  } from "streamdown";
13
13
  import { Button, type ButtonProps } from "@/components/ui/button";
14
14
  import { maybeAddMarimoImport } from "@/core/cells/add-missing-import";
15
- import { useCellActions } from "@/core/cells/cells";
15
+ import { useCellActions } from "@/core/cells/actions";
16
16
  import { useLastFocusedCellId } from "@/core/cells/focus";
17
17
  import { MarkdownLanguageAdapter } from "@/core/codemirror/language/languages/markdown";
18
18
  import { SQLLanguageAdapter } from "@/core/codemirror/language/languages/sql/sql";
@@ -71,7 +71,7 @@ function maybeTransform(
71
71
  }
72
72
 
73
73
  const InsertCodeBlockButton = ({ code, language }: CodeBlockProps) => {
74
- const { createNewCell } = useCellActions();
74
+ const { createCell } = useCellActions();
75
75
  const lastFocusedCellId = useLastFocusedCellId();
76
76
  const autoInstantiate = useAtomValue(autoInstantiateAtom);
77
77
 
@@ -81,15 +81,11 @@ const InsertCodeBlockButton = ({ code, language }: CodeBlockProps) => {
81
81
  if (language === "sql") {
82
82
  maybeAddMarimoImport({
83
83
  autoInstantiate,
84
- createNewCell,
84
+ createCell,
85
85
  fromCellId: lastFocusedCellId,
86
86
  });
87
87
  }
88
- createNewCell({
89
- code: result.code,
90
- before: false,
91
- cellId: lastFocusedCellId ?? "__end__",
92
- });
88
+ createCell({ after: lastFocusedCellId, code: result.code });
93
89
  };
94
90
 
95
91
  return (
@@ -1,15 +1,17 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
- import { lazy } from "react";
3
+ import { useSetAtom, useStore } from "jotai";
4
+ import { notebookAtom } from "@/core/cells/cells";
5
+ import { notebookCells } from "@/core/cells/utils";
4
6
  import type { AppConfig, UserConfig } from "@/core/config/config-schema";
5
7
  import { KnownQueryParams } from "@/core/constants";
6
8
  import { EditApp } from "@/core/edit-app";
9
+ import { getSessionId } from "@/core/kernel/session";
10
+ import { DocumentProvider } from "@/core/notebook/document";
11
+ import { lastSavedNotebookAtom } from "@/core/saving/state";
12
+ import { useNotebookSession } from "@/core/session/useNotebookSession";
7
13
  import { AppChrome } from "../editor/chrome/wrapper/app-chrome";
8
14
 
9
- const LazyCommandPalette = lazy(
10
- () => import("../editor/controls/command-palette"),
11
- );
12
-
13
15
  interface Props {
14
16
  userConfig: UserConfig;
15
17
  appConfig: AppConfig;
@@ -20,21 +22,34 @@ const hideChrome = (() => {
20
22
  return url.searchParams.get(KnownQueryParams.showChrome) === "false";
21
23
  })();
22
24
 
25
+ /** Own the notebook session and provide its document model to the editor and its chrome. */
23
26
  const EditPage = (props: Props) => {
24
- if (hideChrome) {
25
- return (
26
- <>
27
- <EditApp hideControls={true} {...props} />
28
- <LazyCommandPalette />
29
- </>
30
- );
31
- }
27
+ const store = useStore();
28
+ const setLastSavedNotebook = useSetAtom(lastSavedNotebookAtom);
29
+ const { connection, reconnect, documentModel } = useNotebookSession({
30
+ autoInstantiate: props.userConfig.runtime.auto_instantiate,
31
+ onKernelReady: (layout) => {
32
+ const cells = notebookCells(store.get(notebookAtom));
33
+ const names = cells.map((cell) => cell.name);
34
+ const codes = cells.map((cell) => cell.code);
35
+ const configs = cells.map((cell) => cell.config);
36
+ setLastSavedNotebook({ names, codes, configs, layout });
37
+ },
38
+ sessionId: getSessionId(),
39
+ });
40
+ const app = (
41
+ <EditApp
42
+ {...props}
43
+ hideControls={hideChrome}
44
+ connection={connection}
45
+ reconnect={reconnect}
46
+ />
47
+ );
32
48
 
33
49
  return (
34
- <AppChrome>
35
- <EditApp {...props} />
36
- <LazyCommandPalette />
37
- </AppChrome>
50
+ <DocumentProvider model={documentModel}>
51
+ {hideChrome ? app : <AppChrome>{app}</AppChrome>}
52
+ </DocumentProvider>
38
53
  );
39
54
  };
40
55
 
@@ -21,8 +21,10 @@ const state = vi.hoisted(() => ({
21
21
 
22
22
  vi.mock("@/core/cells/cells", () => ({
23
23
  useNotebook: () => ({ cellRuntime: {}, cellData: {} }),
24
+ }));
25
+ vi.mock("@/core/cells/actions", () => ({
24
26
  useCellActions: () => ({
25
- createNewCell: vi.fn(),
27
+ createCell: vi.fn(),
26
28
  updateCellCode: state.updateCellCode,
27
29
  }),
28
30
  }));
@@ -13,7 +13,8 @@ import type React from "react";
13
13
  import { Suspense, useRef, useState } from "react";
14
14
  import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";
15
15
  import useEvent from "react-use-event-hook";
16
- import { useCellActions, useNotebook } from "@/core/cells/cells";
16
+ import { useNotebook } from "@/core/cells/cells";
17
+ import { useCellActions } from "@/core/cells/actions";
17
18
  import { useLastFocusedCellId } from "@/core/cells/focus";
18
19
  import { HTMLCellId, SCRATCH_CELL_ID } from "@/core/cells/ids";
19
20
  import { DEFAULT_CELL_NAME } from "@/core/cells/names";
@@ -55,7 +56,7 @@ export const ScratchPad: React.FC = () => {
55
56
  const { theme } = useTheme();
56
57
  const ref = useRef<EditorView | null>(null);
57
58
  const lastFocusedCellId = useLastFocusedCellId();
58
- const { createNewCell, updateCellCode } = useCellActions();
59
+ const { updateCellCode, createCell } = useCellActions();
59
60
  const { sendRunScratchpad } = useRequestClient();
60
61
  const orientation = usePanelOrientation();
61
62
  const section = usePanelSection();
@@ -81,11 +82,7 @@ export const ScratchPad: React.FC = () => {
81
82
  if (!code.trim()) {
82
83
  return;
83
84
  }
84
- createNewCell({
85
- code,
86
- before: false,
87
- cellId: lastFocusedCellId ?? "__end__",
88
- });
85
+ createCell({ after: lastFocusedCellId, code });
89
86
  });
90
87
 
91
88
  const handleClearCode = useEvent(() => {
@@ -13,17 +13,19 @@ const A = cellId("a");
13
13
  const B = cellId("b");
14
14
 
15
15
  // Spies shared with the hoisted module mocks below.
16
- const { createNewCell, deleteCell, moveCellToIndex } = vi.hoisted(() => ({
17
- createNewCell: vi.fn(),
16
+ const { createCell, deleteCell, moveCellToIndex } = vi.hoisted(() => ({
17
+ createCell: vi.fn(),
18
18
  deleteCell: vi.fn(),
19
19
  moveCellToIndex: vi.fn(),
20
20
  }));
21
21
 
22
+ vi.mock("@/core/cells/actions", () => ({
23
+ useCellActions: () => ({ moveCellToIndex, createCell }),
24
+ }));
22
25
  vi.mock("@/core/cells/cells", async (importOriginal) => {
23
26
  const actual = await importOriginal<typeof import("@/core/cells/cells")>();
24
27
  return {
25
28
  ...actual,
26
- useCellActions: () => ({ moveCellToIndex, createNewCell }),
27
29
  useCellIds: () => MultiColumn.from([[A, B]]),
28
30
  };
29
31
  });
@@ -91,8 +93,6 @@ function renderMinimap() {
91
93
  return { ...utils, onSlideClick };
92
94
  }
93
95
 
94
- const EMPTY_CELL = { code: "", autoFocus: false } as const;
95
-
96
96
  describe("SlidesMinimap insert lines", () => {
97
97
  it("inserts a blank cell above the first row and below any row", () => {
98
98
  renderMinimap();
@@ -102,25 +102,13 @@ describe("SlidesMinimap insert lines", () => {
102
102
  expect(inserts).toHaveLength(3);
103
103
 
104
104
  fireEvent.click(inserts[0]);
105
- expect(createNewCell).toHaveBeenLastCalledWith({
106
- cellId: A,
107
- before: true,
108
- ...EMPTY_CELL,
109
- });
105
+ expect(createCell).toHaveBeenLastCalledWith({ before: A, focus: false });
110
106
 
111
107
  fireEvent.click(inserts[1]);
112
- expect(createNewCell).toHaveBeenLastCalledWith({
113
- cellId: A,
114
- before: false,
115
- ...EMPTY_CELL,
116
- });
108
+ expect(createCell).toHaveBeenLastCalledWith({ after: A, focus: false });
117
109
 
118
110
  fireEvent.click(inserts[2]);
119
- expect(createNewCell).toHaveBeenLastCalledWith({
120
- cellId: B,
121
- before: false,
122
- ...EMPTY_CELL,
123
- });
111
+ expect(createCell).toHaveBeenLastCalledWith({ after: B, focus: false });
124
112
  });
125
113
  });
126
114
 
@@ -135,11 +123,7 @@ describe("SlidesMinimap context menu", () => {
135
123
  const { container } = renderMinimap();
136
124
  openRowMenu(container, A);
137
125
  fireEvent.click(screen.getByText("Add cell"));
138
- expect(createNewCell).toHaveBeenCalledWith({
139
- cellId: A,
140
- before: false,
141
- ...EMPTY_CELL,
142
- });
126
+ expect(createCell).toHaveBeenCalledWith({ after: A, focus: false });
143
127
  });
144
128
 
145
129
  it('"Delete cell" deletes the row\'s cell', () => {
@@ -2,7 +2,8 @@
2
2
 
3
3
  import { useDeleteCellCallback } from "@/components/editor/cell/useDeleteCell";
4
4
  import { outputIsStale } from "@/core/cells/cell";
5
- import { useCellActions, useCellIds } from "@/core/cells/cells";
5
+ import { useCellIds } from "@/core/cells/cells";
6
+ import { useCellActions } from "@/core/cells/actions";
6
7
  import type { CellId } from "@/core/cells/ids";
7
8
  import type { CellColumnId } from "@/utils/id-tree";
8
9
  import { useEffect, useRef, useState } from "react";
@@ -229,7 +230,7 @@ export const SlidesMinimap = ({
229
230
  onSlideClick,
230
231
  }: SlidesMinimapProps) => {
231
232
  const cellIds = useCellIds();
232
- const { moveCellToIndex, createNewCell } = useCellActions();
233
+ const { moveCellToIndex, createCell } = useCellActions();
233
234
  const deleteCell = useDeleteCellCallback();
234
235
  const containerRef = useRef<HTMLDivElement>(null);
235
236
  const visibleIds = useVisibleCellIds(containerRef);
@@ -240,7 +241,10 @@ export const SlidesMinimap = ({
240
241
  const dimensions = computeThumbnailDimensions(thumbnailWidth);
241
242
 
242
243
  const insertCell = (cellId: CellId, before: boolean) => {
243
- createNewCell({ cellId, before, code: "", autoFocus: false });
244
+ createCell({
245
+ ...(before ? { before: cellId } : { after: cellId }),
246
+ focus: false,
247
+ });
244
248
  };
245
249
 
246
250
  useEffect(() => {
@@ -14,7 +14,7 @@ import { useRunCell } from "@/components/editor/cell/useRunCells";
14
14
  import { Slide as CellOutputSlide } from "@/components/slides/slide";
15
15
  import { maybeAddMarimoImport } from "@/core/cells/add-missing-import";
16
16
  import { outputIsStale } from "@/core/cells/cell";
17
- import { useCellActions } from "@/core/cells/cells";
17
+ import { useCellActions } from "@/core/cells/actions";
18
18
  import { autoInstantiateAtom, useUserConfig } from "@/core/config/config";
19
19
  import {
20
20
  cellNeedsRun,
@@ -42,7 +42,7 @@ export const SlideCellView = ({ cell }: { cell: RuntimeCell }) => {
42
42
  const { theme } = useTheme();
43
43
  const runCell = useRunCell(cell.id);
44
44
  const connection = useAtomValue(connectionAtom);
45
- const cellActions = useCellActions();
45
+ const { createCell } = useCellActions();
46
46
  const autoInstantiate = useAtomValue(autoInstantiateAtom);
47
47
  const editorViewRef = useRef<EditorView | null>(null);
48
48
  const editorViewParentRef = useRef<HTMLDivElement | null>(null);
@@ -53,7 +53,7 @@ export const SlideCellView = ({ cell }: { cell: RuntimeCell }) => {
53
53
  const afterToggleLanguage = useEvent(() => {
54
54
  maybeAddMarimoImport({
55
55
  autoInstantiate,
56
- createNewCell: cellActions.createNewCell,
56
+ createCell,
57
57
  });
58
58
  });
59
59
 
@@ -24,7 +24,8 @@ import { useAppConfig } from "@/core/config/config";
24
24
  import { Arrays } from "@/utils/arrays";
25
25
  import type { CellColumnId, MultiColumn } from "@/utils/id-tree";
26
26
  import { invariant } from "@/utils/invariant";
27
- import { getNotebook, useCellActions } from "../../core/cells/cells";
27
+ import { getNotebook } from "../../core/cells/cells";
28
+ import { useCellActions } from "../../core/cells/actions";
28
29
  import { useEvent } from "../../hooks/useEvent";
29
30
 
30
31
  interface SortableCellsProviderProps {