@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
@@ -3,15 +3,37 @@
3
3
 
4
4
  import type { EditorView } from "@codemirror/view";
5
5
  import { act, renderHook } from "@testing-library/react";
6
- import { Provider } from "jotai";
7
6
  import React, { createRef } from "react";
8
- import { afterAll, beforeEach, describe, expect, it, vi } from "vitest";
7
+ import {
8
+ afterAll,
9
+ afterEach,
10
+ beforeEach,
11
+ describe,
12
+ expect,
13
+ it,
14
+ vi,
15
+ } from "vitest";
9
16
  import { Mocks } from "@/__mocks__/common";
17
+
18
+ const { mockCreateCell, mockMoveToNextCell } = vi.hoisted(() => ({
19
+ mockCreateCell: vi.fn(),
20
+ mockMoveToNextCell: vi.fn(),
21
+ }));
22
+
23
+ import { DocumentWrapper, mockDocumentModel } from "@/__mocks__/document";
10
24
  import { MockNotebook } from "@/__mocks__/notebook";
11
25
  import { MockRequestClient } from "@/__mocks__/requests";
12
26
  import { aiCompletionCellAtom } from "@/core/ai/state";
13
- import type { CellActions } from "@/core/cells/cells";
27
+ import type { CellActions } from "@/core/cells/actions";
14
28
  import { notebookAtom } from "@/core/cells/cells";
29
+ import type { CellId } from "@/core/cells/ids";
30
+ import type { DocumentModel } from "@/core/notebook/document";
31
+ import {
32
+ cell,
33
+ CONFIG,
34
+ document as documentOf,
35
+ withCells,
36
+ } from "@/core/notebook/document/__tests__/fixtures";
15
37
  import { LanguageAdapters } from "@/core/codemirror/language/LanguageAdapters";
16
38
  import { signatureHintField } from "@/core/codemirror/completion/signature-hint";
17
39
  import {
@@ -30,9 +52,11 @@ import {
30
52
  } from "../navigation";
31
53
 
32
54
  // Mock only the essential dependencies that we need to control
55
+ vi.mock("@/core/cells/actions", () => ({
56
+ useCellActions: vi.fn(),
57
+ }));
33
58
  vi.mock("@/core/cells/cells", async (importOriginal) => ({
34
59
  ...(await importOriginal()),
35
- useCellActions: vi.fn(),
36
60
  }));
37
61
 
38
62
  vi.mock("@/core/cells/focus", async (importOriginal) => ({
@@ -80,7 +104,7 @@ vi.mock("@codemirror/autocomplete", () => ({
80
104
 
81
105
  // Get mocked functions
82
106
  const mockUseCellActions = vi.mocked(
83
- await import("@/core/cells/cells"),
107
+ await import("@/core/cells/actions"),
84
108
  ).useCellActions;
85
109
 
86
110
  const mockUseSaveNotebook = vi.mocked(
@@ -93,6 +117,8 @@ const mockUseCellClipboard = vi.mocked(
93
117
  await import("../clipboard"),
94
118
  ).useCellClipboard;
95
119
 
120
+ afterEach(() => stopModel());
121
+
96
122
  afterAll(() => {
97
123
  vi.resetAllMocks();
98
124
  });
@@ -107,11 +133,17 @@ import {
107
133
  useIsCellSelected,
108
134
  } from "../selection";
109
135
 
136
+ // The document the hooks edit, with cellId3's code hidden
137
+ let model: DocumentModel;
138
+ let stopModel = () => {};
139
+ const hideCodeOf = (id: CellId) =>
140
+ store.get(model.documentAtom)?.cells.get(id)?.config.hide_code;
141
+
110
142
  // Shared render helper
111
143
  const renderWithProvider = <T>(hook: () => T) => {
112
144
  return renderHook(hook, {
113
145
  wrapper: ({ children }) =>
114
- React.createElement(Provider, { store }, children),
146
+ React.createElement(DocumentWrapper, { store, model }, children),
115
147
  });
116
148
  };
117
149
 
@@ -125,17 +157,16 @@ const mockCutCell = vi.fn().mockResolvedValue(undefined);
125
157
  const mockPasteCell = vi.fn();
126
158
 
127
159
  const mockCellActions = MockNotebook.cellActions({
160
+ createCell: mockCreateCell,
161
+ moveToNextCell: mockMoveToNextCell,
128
162
  focusCell: vi.fn(),
129
- moveToNextCell: vi.fn(),
130
163
  focusTopCell: vi.fn(),
131
164
  focusBottomCell: vi.fn(),
132
- createNewCell: vi.fn(),
133
165
  moveCell: vi.fn(),
134
166
  sendToTop: vi.fn(),
135
167
  sendToBottom: vi.fn(),
136
- updateCellConfig: vi.fn(),
137
168
  markTouched: vi.fn(),
138
- deleteCell: vi.fn(),
169
+ deleteCells: vi.fn(),
139
170
  undoDeleteCell: vi.fn(),
140
171
  });
141
172
 
@@ -207,6 +238,23 @@ describe("useCellNavigationProps", () => {
207
238
  },
208
239
  });
209
240
  store.set(notebookAtom, notebookState);
241
+ stopModel();
242
+ ({ model, stop: stopModel } = mockDocumentModel(
243
+ store,
244
+ withCells(
245
+ documentOf(),
246
+ {
247
+ c1: null,
248
+ c2: null,
249
+ [cellId1]: cell(cellId1, ""),
250
+ [cellId2]: cell(cellId2, ""),
251
+ [cellId3]: cell(cellId3, "", {
252
+ config: { ...CONFIG, hide_code: true },
253
+ }),
254
+ },
255
+ [cellId1, cellId2, cellId3],
256
+ ),
257
+ ));
210
258
 
211
259
  // Clear AI completion state
212
260
  store.set(aiCompletionCellAtom, null);
@@ -367,7 +415,7 @@ describe("useCellNavigationProps", () => {
367
415
  });
368
416
 
369
417
  expect(mockRunCell).toHaveBeenCalled();
370
- expect(mockCellActions.moveToNextCell).toHaveBeenCalledWith({
418
+ expect(mockMoveToNextCell).toHaveBeenCalledWith({
371
419
  cellId: mockCellId,
372
420
  before: false,
373
421
  });
@@ -399,11 +447,7 @@ describe("useCellNavigationProps", () => {
399
447
  result.current.onKeyDown?.(mockEvent);
400
448
  });
401
449
 
402
- expect(mockCellActions.createNewCell).toHaveBeenCalledWith({
403
- cellId: mockCellId,
404
- before: true,
405
- autoFocus: true,
406
- });
450
+ expect(mockCreateCell).toHaveBeenCalledWith({ before: mockCellId });
407
451
  });
408
452
 
409
453
  it("should create cell after when 'b' key is pressed", () => {
@@ -417,11 +461,7 @@ describe("useCellNavigationProps", () => {
417
461
  result.current.onKeyDown?.(mockEvent);
418
462
  });
419
463
 
420
- expect(mockCellActions.createNewCell).toHaveBeenCalledWith({
421
- cellId: mockCellId,
422
- before: false,
423
- autoFocus: true,
424
- });
464
+ expect(mockCreateCell).toHaveBeenCalledWith({ after: mockCellId });
425
465
  });
426
466
 
427
467
  it("should add an import before and a SQL cell after when 'q' is pressed", () => {
@@ -435,18 +475,15 @@ describe("useCellNavigationProps", () => {
435
475
  result.current.onKeyDown?.(Mocks.keyboardEvent({ key: "q" }));
436
476
  });
437
477
 
438
- expect(mockCellActions.createNewCell).toHaveBeenNthCalledWith(
478
+ expect(mockCreateCell).toHaveBeenNthCalledWith(
439
479
  1,
440
480
  expect.objectContaining({
441
- cellId: mockCellId,
442
- before: true,
481
+ before: mockCellId,
443
482
  code: "import marimo as mo",
444
483
  }),
445
484
  );
446
- expect(mockCellActions.createNewCell).toHaveBeenNthCalledWith(2, {
447
- cellId: mockCellId,
448
- before: false,
449
- autoFocus: true,
485
+ expect(mockCreateCell).toHaveBeenNthCalledWith(2, {
486
+ after: mockCellId,
450
487
  code: LanguageAdapters.sql.defaultCode,
451
488
  });
452
489
  });
@@ -1034,7 +1071,7 @@ describe("useCellNavigationProps", () => {
1034
1071
 
1035
1072
  expect(mockRunCell).toHaveBeenCalledWith([cellId1, cellId2]);
1036
1073
  // Should move to next cell after the last selected cell
1037
- expect(mockCellActions.moveToNextCell).toHaveBeenCalledWith({
1074
+ expect(mockMoveToNextCell).toHaveBeenCalledWith({
1038
1075
  cellId: cellId2,
1039
1076
  before: false,
1040
1077
  });
@@ -1066,7 +1103,7 @@ describe("useCellNavigationProps", () => {
1066
1103
 
1067
1104
  expect(mockRunCell).toHaveBeenCalledWith([cellId2, cellId3]);
1068
1105
  // Should move to previous cell before the first selected cell
1069
- expect(mockCellActions.moveToNextCell).toHaveBeenCalledWith({
1106
+ expect(mockMoveToNextCell).toHaveBeenCalledWith({
1070
1107
  cellId: cellId2,
1071
1108
  before: true,
1072
1109
  });
@@ -1167,15 +1204,7 @@ describe("useCellNavigationProps", () => {
1167
1204
  result.current.onKeyDown?.(mockEvent);
1168
1205
  });
1169
1206
 
1170
- expect(mockRequestClient.saveCellConfig).toHaveBeenCalledWith({
1171
- configs: {
1172
- [cellId1]: { hide_code: true },
1173
- },
1174
- });
1175
- expect(mockCellActions.updateCellConfig).toHaveBeenCalledWith({
1176
- cellId: cellId1,
1177
- config: { hide_code: true },
1178
- });
1207
+ expect(hideCodeOf(cellId1)).toBe(true);
1179
1208
  expect(mockEvent.preventDefault).toHaveBeenCalled();
1180
1209
  });
1181
1210
 
@@ -1190,15 +1219,7 @@ describe("useCellNavigationProps", () => {
1190
1219
  result.current.onKeyDown?.(mockEvent);
1191
1220
  });
1192
1221
 
1193
- expect(mockRequestClient.saveCellConfig).toHaveBeenCalledWith({
1194
- configs: {
1195
- [cellId3]: { hide_code: false },
1196
- },
1197
- });
1198
- expect(mockCellActions.updateCellConfig).toHaveBeenCalledWith({
1199
- cellId: cellId3,
1200
- config: { hide_code: false },
1201
- });
1222
+ expect(hideCodeOf(cellId3)).toBe(false);
1202
1223
  });
1203
1224
 
1204
1225
  it("should toggle hide code for multiple selected cells", () => {
@@ -1220,20 +1241,8 @@ describe("useCellNavigationProps", () => {
1220
1241
  result.current.onKeyDown?.(mockEvent);
1221
1242
  });
1222
1243
 
1223
- expect(mockRequestClient.saveCellConfig).toHaveBeenCalledWith({
1224
- configs: {
1225
- [cellId1]: { hide_code: true },
1226
- [cellId2]: { hide_code: true },
1227
- },
1228
- });
1229
- expect(mockCellActions.updateCellConfig).toHaveBeenCalledWith({
1230
- cellId: cellId1,
1231
- config: { hide_code: true },
1232
- });
1233
- expect(mockCellActions.updateCellConfig).toHaveBeenCalledWith({
1234
- cellId: cellId2,
1235
- config: { hide_code: true },
1236
- });
1244
+ expect(hideCodeOf(cellId1)).toBe(true);
1245
+ expect(hideCodeOf(cellId2)).toBe(true);
1237
1246
  });
1238
1247
  });
1239
1248
 
@@ -1701,7 +1710,7 @@ describe("useCellNavigationProps", () => {
1701
1710
  result.current.onKeyDown?.(mockEvent);
1702
1711
  });
1703
1712
 
1704
- expect(mockCellActions.createNewCell).not.toHaveBeenCalled();
1713
+ expect(mockCreateCell).not.toHaveBeenCalled();
1705
1714
  expect(mockEvent.preventDefault).not.toHaveBeenCalled();
1706
1715
  });
1707
1716
 
@@ -1716,7 +1725,7 @@ describe("useCellNavigationProps", () => {
1716
1725
  result.current.onKeyDown?.(mockEvent);
1717
1726
  });
1718
1727
 
1719
- expect(mockCellActions.createNewCell).not.toHaveBeenCalled();
1728
+ expect(mockCreateCell).not.toHaveBeenCalled();
1720
1729
  expect(mockEvent.preventDefault).not.toHaveBeenCalled();
1721
1730
  });
1722
1731
  });
@@ -3,7 +3,8 @@
3
3
  import useEvent from "react-use-event-hook";
4
4
  import { z } from "zod";
5
5
  import { toast } from "@/components/ui/use-toast";
6
- import { getNotebook, useCellActions } from "@/core/cells/cells";
6
+ import { getNotebook } from "@/core/cells/cells";
7
+ import { useCellActions } from "@/core/cells/actions";
7
8
  import type { CellId } from "@/core/cells/ids";
8
9
  import {
9
10
  usePendingCutActions,
@@ -73,6 +74,7 @@ async function writeCellsToClipboard(
73
74
 
74
75
  export function useCellClipboard() {
75
76
  const actions = useCellActions();
77
+ const { createCell } = actions;
76
78
  const pendingCutActions = usePendingCutActions();
77
79
  const pendingCutState = usePendingCutState();
78
80
 
@@ -178,14 +180,15 @@ export function useCellClipboard() {
178
180
  const currentCellId = cellId;
179
181
  const reversedCells = clipboardData.cells.toReversed();
180
182
  for (const cell of reversedCells) {
181
- actions.createNewCell({
182
- cellId: currentCellId,
183
- before,
183
+ // The pasted cell joins the column it is pasted into.
184
+ const { column: _column, ...config } = cell.config ?? {};
185
+ createCell({
186
+ ...(before
187
+ ? { before: currentCellId }
188
+ : { after: currentCellId }),
184
189
  code: cell.code,
185
190
  name: cell.name,
186
- config: cell.config,
187
- hideCode: cell.config?.hide_code,
188
- autoFocus: true,
191
+ config,
189
192
  });
190
193
  }
191
194
 
@@ -199,11 +202,9 @@ export function useCellClipboard() {
199
202
  // Fallback to plain text
200
203
  const text = await navigator.clipboard.readText();
201
204
  if (text.trim()) {
202
- actions.createNewCell({
203
- cellId,
204
- before,
205
+ createCell({
206
+ ...(before ? { before: cellId } : { after: cellId }),
205
207
  code: text,
206
- autoFocus: true,
207
208
  });
208
209
  } else {
209
210
  toastNothingToPaste();
@@ -34,14 +34,14 @@ import {
34
34
  getCellEditorView,
35
35
  hasOnlyOneCellAtom,
36
36
  notebookAtom,
37
- useCellActions,
38
37
  } from "@/core/cells/cells";
38
+ import { useCellActions } from "@/core/cells/actions";
39
+ import { useNotebookDocumentEdit } from "@/core/notebook/document";
39
40
  import type { CellId } from "@/core/cells/ids";
40
41
  import { usePendingDeleteService } from "@/core/cells/pending-delete-service";
41
42
  import { formatEditorViews } from "@/core/codemirror/format";
42
43
  import { userConfigAtom } from "@/core/config/config";
43
44
  import type { HotkeyAction } from "@/core/hotkeys/hotkeys";
44
- import { useRequestClient } from "@/core/network/requests";
45
45
  import type { CellConfig } from "@/core/network/types";
46
46
  import { store } from "@/core/state/jotai";
47
47
  import { useEventListener } from "@/hooks/useEventListener";
@@ -112,20 +112,15 @@ const CellStateDropdown: React.FC<{
112
112
  };
113
113
 
114
114
  export function useMultiCellActionButtons(cellIds: CellId[]) {
115
- const {
116
- updateCellConfig,
117
- moveCell,
118
- clearCellOutput,
119
- sendToTop,
120
- sendToBottom,
121
- } = useCellActions();
115
+ const { moveCell, clearCellOutput, sendToTop, sendToBottom } =
116
+ useCellActions();
117
+ const edit = useNotebookDocumentEdit();
122
118
  const deleteCell = useDeleteManyCellsCallback();
123
119
  const hasOnlyOneCell = useAtomValue(hasOnlyOneCellAtom);
124
120
  const selectionActions = useCellSelectionActions();
125
121
  const runCells = useRunCells();
126
122
  const pendingDeleteService = usePendingDeleteService();
127
123
  const userConfig = useAtomValue(userConfigAtom);
128
- const { saveCellConfig } = useRequestClient();
129
124
 
130
125
  const selectedCount = cellIds.length;
131
126
 
@@ -205,16 +200,12 @@ export function useMultiCellActionButtons(cellIds: CellId[]) {
205
200
  });
206
201
 
207
202
  const toggleSelectedCellsProperty = useEvent(
208
- async (cellIds: CellId[], property: keyof CellConfig, value: boolean) => {
209
- const configs: Record<CellId, Partial<CellConfig>> = {};
210
- cellIds.forEach((cellId) => {
211
- configs[cellId] = { [property]: value };
212
- });
213
-
214
- await saveCellConfig({ configs });
215
-
216
- cellIds.forEach((cellId) => {
217
- updateCellConfig({ cellId, config: configs[cellId] });
203
+ (cellIds: CellId[], property: keyof CellConfig, value: boolean) => {
204
+ edit((tx) => {
205
+ for (const cellId of cellIds) {
206
+ const config: Partial<CellConfig> = { [property]: value };
207
+ tx.updateConfig(cellId, config);
208
+ }
218
209
  });
219
210
  },
220
211
  );
@@ -13,7 +13,9 @@ import { mergeProps, useFocusWithin, useKeyboard } from "react-aria";
13
13
  import { DATA_FOR_CELL_ID } from "@/components/data-table/cell-utils";
14
14
  import { aiCompletionCellAtom } from "@/core/ai/state";
15
15
  import { maybeAddMarimoImport } from "@/core/cells/add-missing-import";
16
- import { cellIdsAtom, notebookAtom, useCellActions } from "@/core/cells/cells";
16
+ import { cellIdsAtom, notebookAtom } from "@/core/cells/cells";
17
+ import { useCellActions } from "@/core/cells/actions";
18
+ import { useNotebookDocumentEdit } from "@/core/notebook/document";
17
19
  import { useCellFocusActions } from "@/core/cells/focus";
18
20
  import type { CellId } from "@/core/cells/ids";
19
21
  import { HTMLCellId } from "@/core/cells/ids";
@@ -36,7 +38,6 @@ import {
36
38
  } from "@/core/config/config";
37
39
  import type { HotkeyAction } from "@/core/hotkeys/hotkeys";
38
40
  import { parseShortcut } from "@/core/hotkeys/shortcuts";
39
- import { useRequestClient } from "@/core/network/requests";
40
41
  import { useSaveNotebook } from "@/core/saving/save-component";
41
42
  import { Events } from "@/utils/events";
42
43
  import type { CollapsibleTree } from "@/utils/id-tree";
@@ -189,9 +190,10 @@ export function useCellNavigationProps(
189
190
  },
190
191
  ) {
191
192
  const { saveOrNameNotebook } = useSaveNotebook();
192
- const { saveCellConfig } = useRequestClient();
193
+ const edit = useNotebookDocumentEdit();
193
194
  const setAiCompletionCell = useSetAtom(aiCompletionCellAtom);
194
195
  const actions = useCellActions();
196
+ const { createCell, moveToNextCell } = useCellActions();
195
197
  const store = useStore();
196
198
  const temporarilyShownCodeActions = useTemporarilyShownCodeActions();
197
199
  const runCells = useRunCells();
@@ -376,21 +378,21 @@ export function useCellNavigationProps(
376
378
  "cell.runAndNewBelow": addSingleHandler((cellIds) => {
377
379
  runCells(cellIds);
378
380
  const lastCellId = cellIds[cellIds.length - 1];
379
- actions.moveToNextCell({ cellId: lastCellId, before: false });
381
+ moveToNextCell({ cellId: lastCellId, before: false });
380
382
  return true;
381
383
  }),
382
384
  "cell.runAndNewAbove": addSingleHandler((cellIds) => {
383
385
  runCells(cellIds);
384
386
  const firstCellId = cellIds[0];
385
- actions.moveToNextCell({ cellId: firstCellId, before: true });
387
+ moveToNextCell({ cellId: firstCellId, before: true });
386
388
  return true;
387
389
  }),
388
390
  "cell.createAbove": (cellId) => {
389
- actions.createNewCell({ cellId, before: true });
391
+ createCell({ before: cellId });
390
392
  return true;
391
393
  },
392
394
  "cell.createBelow": (cellId) => {
393
- actions.createNewCell({ cellId, before: false });
395
+ createCell({ after: cellId });
394
396
  return true;
395
397
  },
396
398
  "cell.moveUp": addSingleHandler((cellIds) => {
@@ -453,20 +455,12 @@ export function useCellNavigationProps(
453
455
  (config) => config?.hide_code,
454
456
  );
455
457
 
456
- // Fire-and-forget
457
- void saveCellConfig({
458
- configs: Object.fromEntries(
459
- cellIds.map((cellId) => [cellId, { hide_code: nextHideCode }]),
460
- ),
458
+ edit((tx) => {
459
+ for (const cellId of cellIds) {
460
+ tx.updateConfig(cellId, { hide_code: nextHideCode });
461
+ }
461
462
  });
462
463
 
463
- for (const cellId of cellIds) {
464
- actions.updateCellConfig({
465
- cellId,
466
- config: { hide_code: nextHideCode },
467
- });
468
- }
469
-
470
464
  // Only focus if it is a single cell
471
465
  if (cellIds.length === 1) {
472
466
  const cellId = cellIds[0];
@@ -543,14 +537,14 @@ export function useCellNavigationProps(
543
537
  if (Events.hasModifier(evt)) {
544
538
  return false;
545
539
  }
546
- actions.createNewCell({ cellId, before: true, autoFocus: true });
540
+ createCell({ before: cellId });
547
541
  return true;
548
542
  },
549
543
  "command.createCellAfter": (cellId) => {
550
544
  if (Events.hasModifier(evt)) {
551
545
  return false;
552
546
  }
553
- actions.createNewCell({ cellId, before: false, autoFocus: true });
547
+ createCell({ after: cellId });
554
548
  return true;
555
549
  },
556
550
  "command.createSqlCellAfter": (cellId) => {
@@ -559,14 +553,12 @@ export function useCellNavigationProps(
559
553
  }
560
554
  maybeAddMarimoImport({
561
555
  autoInstantiate: true,
562
- createNewCell: actions.createNewCell,
556
+ createCell: createCell,
563
557
  fromCellId: cellId,
564
558
  before: true,
565
559
  });
566
- actions.createNewCell({
567
- cellId,
568
- before: false,
569
- autoFocus: true,
560
+ createCell({
561
+ after: cellId,
570
562
  code: LanguageAdapters.sql.defaultCode,
571
563
  });
572
564
  return true;
@@ -635,11 +627,11 @@ export function useCellNavigationProps(
635
627
  return true;
636
628
  },
637
629
  o: () => {
638
- actions.createNewCell({ cellId, before: false, autoFocus: true });
630
+ createCell({ after: cellId });
639
631
  return true;
640
632
  },
641
633
  "shift+o": () => {
642
- actions.createNewCell({ cellId, before: true, autoFocus: true });
634
+ createCell({ before: cellId });
643
635
  return true;
644
636
  },
645
637
  u: () => {
@@ -43,13 +43,12 @@ import { useResizeObserver } from "@/hooks/useResizeObserver";
43
43
  import { cn } from "@/utils/cn";
44
44
  import type { Milliseconds, Seconds } from "@/utils/time";
45
45
  import {
46
- type CellActions,
47
46
  createUntouchedCellAtom,
48
- useCellActions,
49
47
  useCellData,
50
48
  useCellHandle,
51
49
  useCellRuntime,
52
50
  } from "../../core/cells/cells";
51
+ import { type CellActions, useCellActions } from "../../core/cells/actions";
53
52
  import { type CellId, SETUP_CELL_ID } from "../../core/cells/ids";
54
53
  import {
55
54
  cellNeedsRun,
@@ -216,13 +215,10 @@ function useCellHiddenLogic({
216
215
  export type CellComponentActions = Pick<
217
216
  CellActions,
218
217
  | "updateCellCode"
219
- | "createNewCell"
220
218
  | "focusCell"
221
219
  | "moveCell"
222
220
  | "collapseCell"
223
221
  | "expandCell"
224
- | "moveToNextCell"
225
- | "updateCellConfig"
226
222
  | "clearSerializedEditorState"
227
223
  | "setStdinResponse"
228
224
  | "clearCellConsoleOutput"
@@ -928,15 +924,16 @@ const CellLeftSideActions = memo(
928
924
  actions: CellComponentActions;
929
925
  }) => {
930
926
  const connection = useAtomValue(connectionAtom);
931
- const { className, actions, cellId } = props;
927
+ const { className, cellId } = props;
928
+ const { createCell } = useCellActions();
932
929
 
933
930
  const createBelow = useEvent(
934
- (opts: { code?: string; hideCode?: boolean } = {}) =>
935
- actions.createNewCell({ cellId, before: false, ...opts }),
931
+ ({ code, hideCode }: { code?: string; hideCode?: boolean } = {}) =>
932
+ createCell({ after: cellId, code, config: { hide_code: hideCode } }),
936
933
  );
937
934
  const createAbove = useEvent(
938
- (opts: { code?: string; hideCode?: boolean } = {}) =>
939
- actions.createNewCell({ cellId, before: true, ...opts }),
935
+ ({ code, hideCode }: { code?: string; hideCode?: boolean } = {}) =>
936
+ createCell({ before: cellId, code, config: { hide_code: hideCode } }),
940
937
  );
941
938
 
942
939
  const oneClickShortcut = "mod";