@marimo-team/islands 0.25.3-dev27 → 0.25.3-dev271

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 (847) hide show
  1. package/dist/{ConnectedDataExplorerComponent-DfG3PRaJ.js → ConnectedDataExplorerComponent-eLHPh6uu.js} +120 -116
  2. package/dist/{ErrorBoundary-DKAio_E4.js → ErrorBoundary-VLVnXoqP.js} +66 -71
  3. package/dist/_baseSet-BfI5FjBM.js +328 -0
  4. package/dist/{abnfDiagram-VCTEODGH-B3w-XWX8.js → abnfDiagram-VCTEODGH-hqd0IBtX.js} +2 -2
  5. package/dist/any-language-editor-DnG26QXh.js +75 -0
  6. package/dist/{architectureDiagram-5GKGNRK7-C18U9A6k.js → architectureDiagram-5GKGNRK7-BzLAFcc0.js} +1 -1
  7. package/dist/{arrays-CN1djC4k.js → arrays-DoQ-AILx.js} +15 -27
  8. package/dist/assertNever-DIU3JJbt.js +507 -0
  9. package/dist/assets/__vite-browser-external-BKBpimif.js +1 -0
  10. package/dist/assets/worker-CgoMtD-_.js +113 -0
  11. package/dist/{blockDiagram-I7D4REHJ-Cg25UrxT.js → blockDiagram-I7D4REHJ-Dkhe6_PP.js} +1 -1
  12. package/dist/{button-CvjkwdSk.js → button-0-RqY1DW.js} +229 -218
  13. package/dist/{capabilities-CQ5IjhzL.js → capabilities-B6yy_CBN.js} +2 -2
  14. package/dist/{chat-ui-DDJuqS87.js → chat-ui-BWIkve2R.js} +2071 -1780
  15. package/dist/{check-BMKBBrdc.js → check-gUSZbRsl.js} +1 -1
  16. package/dist/{chunk-IMKFNOWR-CrS5bzqW.js → chunk-IMKFNOWR-DOeVvuyE.js} +1 -1
  17. package/dist/{chunk-SHT3W25Y-StpvPvUG.js → chunk-SHT3W25Y-DZoFI8wR.js} +1 -1
  18. package/dist/{chunk-SVP7TREG-CBXjbYZC.js → chunk-SVP7TREG-CWu86PSW.js} +1 -1
  19. package/dist/{chunk-TICWLB2K-Dp6bIb-q.js → chunk-TICWLB2K-BrM0GoP3.js} +1 -1
  20. package/dist/{chunk-YOKDWASO-DPZk8vhS.js → chunk-YOKDWASO-DKplTrBY.js} +20 -20
  21. package/dist/{classDiagram-ZZMXUADV-BFYuxV5g.js → classDiagram-ZZMXUADV-DYi4V2bB.js} +2 -2
  22. package/dist/{classDiagram-v2-VYDZK3BY-D_tjXVMl.js → classDiagram-v2-VYDZK3BY-tN7FIk-m.js} +2 -2
  23. package/dist/{code-visibility-CFvLbCZJ.js → code-visibility-Dv4bHkOd.js} +43 -50
  24. package/dist/{common-1HUDUr7W.js → common-KrCVlCIg.js} +10277 -9719
  25. package/dist/config-BsPruWMR.js +636 -0
  26. package/dist/{copy-icon-BgUO8jsi.js → copy-icon-C2I8sw4h.js} +3 -3
  27. package/dist/createReducer-DdtT_7ji.js +67 -0
  28. package/dist/{cynefinDiagram-5FMLGOSQ-DSgYrHB2.js → cynefinDiagram-5FMLGOSQ-D5nCnDc-.js} +1 -1
  29. package/dist/{dagre-GXQ25YYZ-GYlruCBf.js → dagre-GXQ25YYZ-BHkd7f_i.js} +3 -3
  30. package/dist/{dagre-BGSfXNEb.js → dagre-mqiMAsFY.js} +531 -680
  31. package/dist/{data-grid-overlay-editor-ZxoN8gVB.js → data-grid-overlay-editor-BnCDQaMh.js} +5 -5
  32. package/dist/{data-grid-types-BH_r7g7l.js → data-grid-types-0PJ-9N5n.js} +1 -1
  33. package/dist/{debounce-C3jyATpc.js → debounce-DrhhziJv.js} +2 -2
  34. package/dist/{diagram-S7CK7UJ4-DRu5LX5_.js → diagram-S7CK7UJ4-D1ALppT2.js} +1 -1
  35. package/dist/{diagram-UQ7AKVKN-DD7uiejP.js → diagram-UQ7AKVKN-DLv0b83L.js} +1 -1
  36. package/dist/{diagram-VX7I27RA-ByH9KAJJ.js → diagram-VX7I27RA-7XJ7J6qg.js} +1 -1
  37. package/dist/{diagram-Z3DM3KII-BsQHPRke.js → diagram-Z3DM3KII-7M7zbHp_.js} +1 -1
  38. package/dist/{dialog-Cc479ZEH.js → dialog-DzCj1qQv.js} +26 -26
  39. package/dist/{dist-Bvs24R_E.js → dist-11T5wq3_.js} +31 -30
  40. package/dist/{dist-F8Ep36rk.js → dist-17ugwGZc.js} +59 -58
  41. package/dist/{dist-BMQf5zIS.js → dist-7cRjf--y.js} +123 -122
  42. package/dist/{dist-diIx5qJW.js → dist-B8pkfkzc.js} +31 -30
  43. package/dist/{dist-CN_BeG2I.js → dist-B9UoM_wq.js} +58 -57
  44. package/dist/{dist-CLdpGQVr.js → dist-BGuMTs1i.js} +118 -117
  45. package/dist/{dist-BK4Oj99o.js → dist-BHW11fni.js} +18 -17
  46. package/dist/dist-BLY4m1Wi.js +850 -0
  47. package/dist/{dist-PXYI_yuP.js → dist-BNlcVoxs.js} +24 -23
  48. package/dist/{dist-D6d86eNJ.js → dist-BQYe5Bju.js} +47 -46
  49. package/dist/{dist-CkTTIdBe.js → dist-BX1mn8na.js} +337 -337
  50. package/dist/{dist-DiDibOYr.js → dist-Bh0PTKUW.js} +151 -150
  51. package/dist/{dist-CSJ0MDRw.js → dist-CKaXK92p.js} +56 -55
  52. package/dist/{dist-C8fAKSH1.js → dist-CLhwfhpJ.js} +53 -52
  53. package/dist/{dist-DoE4XJBC.js → dist-CYeqK4Xw.js} +9801 -10649
  54. package/dist/{dist-BLfKmR-l.js → dist-Cxlp3WH6.js} +16 -15
  55. package/dist/{dist-D-wwqlzJ.js → dist-DSJPGj42.js} +158 -157
  56. package/dist/{dist-BorMD-uX.js → dist-DU9ICsWZ.js} +75 -74
  57. package/dist/{dist-DBtSl3sf.js → dist-DWv0ALBi.js} +47 -46
  58. package/dist/{dist-OR1OnN-y.js → dist-DiBpWEsB.js} +31 -30
  59. package/dist/{dist-CrSP_H1M.js → dist-DiLFTlnl.js} +242 -242
  60. package/dist/{dist-DpBpA_n1.js → dist-Lm-D-Vr6.js} +3 -3
  61. package/dist/{dist-CN8zI-XC.js → dist-X9CsotJl.js} +239 -239
  62. package/dist/{dist-CSbh1PNP.js → dist-cVDV5IuI.js} +23 -22
  63. package/dist/{dist-BuaRHxOc.js → dist-fRfaOoq8.js} +44 -43
  64. package/dist/{dist-Cg7gzw-a.js → dist-gTTI-9Px.js} +78 -77
  65. package/dist/dropdown-menu-CdXE15an.js +6089 -0
  66. package/dist/{ebnfDiagram-PWID7BFC-CQRK5_eT.js → ebnfDiagram-PWID7BFC-DHH1qlmV.js} +2 -2
  67. package/dist/{erDiagram-RLTQ6QDP-BKtWrDVh.js → erDiagram-RLTQ6QDP-fI_0dGef.js} +1 -1
  68. package/dist/{error-banner-CFPHFw4h.js → error-banner-DPc9eIw5.js} +87 -86
  69. package/dist/error-entries-CxN-Nq3V.js +2573 -0
  70. package/dist/{esm-AMFssoNp.js → esm-BDqqT86j.js} +329 -329
  71. package/dist/{esm-D2gTsXEV.js → esm-BgKjMvWw.js} +530 -529
  72. package/dist/feedback-button-7UVo7ro6.js +374 -0
  73. package/dist/{flowDiagram-HODETNUW-1IN-ih2E.js → flowDiagram-HODETNUW-Brg9MA6i.js} +2 -2
  74. package/dist/{formats-Buu00Z2q.js → formats-DbSvAPY-.js} +6 -6
  75. package/dist/{ganttDiagram-EL5Y4UJY-BQT16JSc.js → ganttDiagram-EL5Y4UJY-Dh_4cBJZ.js} +2 -2
  76. package/dist/{glide-data-editor-BBgCAPbk.js → glide-data-editor-DTtBwgEa.js} +6676 -2890
  77. package/dist/{glide-utils-DM9zBO4W.js → glide-utils-BSw3SSHF.js} +84 -45
  78. package/dist/{globals-DhH_o2tF.js → globals-CedpcDTN.js} +3 -3
  79. package/dist/{graphlib-BmY_CCV9.js → graphlib-nlwT3Wvy.js} +177 -220
  80. package/dist/hasIn-C20JsQ2m.js +28 -0
  81. package/dist/{highlighted-body-KPVGNVTW-zwjq21tf.js → highlighted-body-KPVGNVTW-3Ziwfz-V.js} +2 -2
  82. package/dist/{error-entries-Cz3yNs7w.js → html-to-image-Bc2AvXHG.js} +26695 -26850
  83. package/dist/{infoDiagram-27XIBGKW-CkB-zr4p.js → infoDiagram-27XIBGKW-E9e403YT.js} +1 -1
  84. package/dist/isArrayLikeObject-B-HXCsex.js +626 -0
  85. package/dist/isEmpty-CdX7MTE1.js +14 -0
  86. package/dist/{ishikawaDiagram-5VMMS53U-wlyLjBmM.js → ishikawaDiagram-5VMMS53U-C-RlYDRo.js} +1 -1
  87. package/dist/{kanban-definition-UXKFOSKX-C2usY0i-.js → kanban-definition-UXKFOSKX-CLFl_uqK.js} +1 -1
  88. package/dist/{linear-Cnaq3Tfb.js → linear-DWERh7eI.js} +121 -73
  89. package/dist/{loader-BQOfjTIJ.js → loader-BVEtQ4F3.js} +12 -12
  90. package/dist/main.js +10420 -8311
  91. package/dist/mermaid-HWGCJPDP-Du3-HPQA.js +2 -0
  92. package/dist/{mermaid-BA-Z6wGL.js → mermaid-rd09e1JL.js} +54 -54
  93. package/dist/{mindmap-definition-YA3MSWOX-nKOWS8RR.js → mindmap-definition-YA3MSWOX-BQ8TWtyB.js} +1 -1
  94. package/dist/{now-jM8-M7IR.js → now-BKZCLE5P.js} +12 -13
  95. package/dist/{number-overlay-editor-CvLhxHLb.js → number-overlay-editor-D_avDRZT.js} +3 -3
  96. package/dist/{objects-CzUgoMIF.js → objects-DCmMvZtn.js} +1 -3
  97. package/dist/{once-BtE-wKRL.js → once-NnpX_HnX.js} +1 -1
  98. package/dist/{pegDiagram-XKGWAZYB-Dac9EIKD.js → pegDiagram-XKGWAZYB-CN8UHFPi.js} +2 -2
  99. package/dist/pencil-DcT8AE0h.js +10 -0
  100. package/dist/pick-tCszMUbm.js +35 -0
  101. package/dist/{pieDiagram-E7YTZNPT-B7S-DsW_.js → pieDiagram-E7YTZNPT-DGuJ0EBd.js} +1 -1
  102. package/dist/precisionRound-DhAzeRcm.js +49 -0
  103. package/dist/{process-output-B-KBO95p.js → process-output-qecD7Dk8.js} +4 -4
  104. package/dist/{quadrantDiagram-AXDQQJYC-Ccw0xMjN.js → quadrantDiagram-AXDQQJYC-Blrm7V7_.js} +1 -1
  105. package/dist/{railroadDiagram-O6MQD6OU-Cfa5Q-hr.js → railroadDiagram-O6MQD6OU-BPDA8wy8.js} +2 -2
  106. package/dist/{react-vega-BED0DjiB.js → react-vega-C4T9Rb_w.js} +12284 -12270
  107. package/dist/{requirementDiagram-BXWQKSXE-d8zXCJIk.js → requirementDiagram-BXWQKSXE-ChzLGbGW.js} +1 -1
  108. package/dist/{reveal-component-C9BavSsS.js → reveal-component-cJ717FaI.js} +1131 -1108
  109. package/dist/schemas-CdscWNy_.js +4747 -0
  110. package/dist/select-OxgozOC8.js +1200 -0
  111. package/dist/{sequenceDiagram-WJ2MYXX4-b6pei3lJ.js → sequenceDiagram-WJ2MYXX4-DFX-bE6O.js} +1 -1
  112. package/dist/{spec-BJzgmvVa.js → spec-oJn0RhpQ.js} +41 -11
  113. package/dist/{stateDiagram-D77RDMKH-Be-xSpaZ.js → stateDiagram-D77RDMKH-BR7cI3wK.js} +4 -4
  114. package/dist/{stateDiagram-v2-MP3YSRHH-5Y-FxesJ.js → stateDiagram-v2-MP3YSRHH-oGqB5UDI.js} +2 -2
  115. package/dist/static-state-CuV6Pc3a.js +72 -0
  116. package/dist/style.css +1 -1
  117. package/dist/{swimlanes-42K2YHIH-nRRl4NU0.js → swimlanes-42K2YHIH-DwSZl1yC.js} +1 -1
  118. package/dist/{swimlanesDiagram-VR7AAH4N-BS79510V.js → swimlanesDiagram-VR7AAH4N-BHlcd-Ri.js} +2 -2
  119. package/dist/{swiper-component-DKntyn8p.js → swiper-component-Dvj-goaD.js} +2 -2
  120. package/dist/{time-BieKwdYS.js → time-BaMbJs4z.js} +18 -18
  121. package/dist/{timeline-definition-24CTP7MA-Cfzf8WPf.js → timeline-definition-24CTP7MA-DMmP-G2O.js} +1 -1
  122. package/dist/toast-DbBHmxmB.js +491 -0
  123. package/dist/{tooltip-CtBZ5q8p.js → tooltip-BfcDQd2j.js} +329 -305
  124. package/dist/use-restore-focus-CmflVLWJ.js +1672 -0
  125. package/dist/{useAsyncData-BD534XmW.js → useAsyncData-BYIAoMqn.js} +15 -14
  126. package/dist/{useDateFormatter-DkajGwwd.js → useDateFormatter-ButWkdcK.js} +2 -2
  127. package/dist/{useDeepCompareMemoize-C72_RNsu.js → useDeepCompareMemoize-Bz0c5GN6.js} +1 -1
  128. package/dist/{useIframeCapabilities-DBETvvPl.js → useIframeCapabilities-BIolGsF1.js} +1 -1
  129. package/dist/useLifecycle-JjxCC7Vu.js +112 -0
  130. package/dist/useTheme-DPLobSFs.js +339 -0
  131. package/dist/utils-EBklsC5R.js +813 -0
  132. package/dist/{vega-component-nB1kV6sb.js → vega-component-rxIxAAh9.js} +171 -170
  133. package/dist/{vega-embed-container-BNHE18G9.js → vega-embed-container--LYZqguO.js} +2 -2
  134. package/dist/{vega-loader.browser-DNST6uVf.js → vega-loader.browser-B9Pz0EGP.js} +298 -507
  135. package/dist/{vennDiagram-4TSXK5OY-CeY6Z1xZ.js → vennDiagram-4TSXK5OY-Db8GtH1G.js} +1 -1
  136. package/dist/{wardleyDiagram-VM6X3IG4-Cxs9NtVi.js → wardleyDiagram-VM6X3IG4-DmIiC3FU.js} +1 -1
  137. package/dist/{xychartDiagram-S5SC5T6Z-DNWbvPK7.js → xychartDiagram-S5SC5T6Z-DS9OPrNq.js} +2 -2
  138. package/package.json +17 -20
  139. package/src/__mocks__/document.tsx +63 -0
  140. package/src/__mocks__/executions.ts +195 -0
  141. package/src/__mocks__/notebook.ts +3 -5
  142. package/src/__mocks__/partial.ts +26 -0
  143. package/src/__mocks__/requests.ts +1 -4
  144. package/src/__tests__/CellStatus.test.tsx +19 -20
  145. package/src/__tests__/fixtures/pair-config.ts +1 -1
  146. package/src/__tests__/main.test.tsx +24 -1
  147. package/src/__tests__/module-entry.test.ts +44 -0
  148. package/src/__tests__/mount.test.ts +1 -21
  149. package/src/__tests__/{host-imports.test.ts → replica-imports.test.ts} +12 -11
  150. package/src/components/ai/__tests__/ai-model-dropdown.test.tsx +63 -0
  151. package/src/components/ai/ai-model-dropdown.tsx +11 -6
  152. package/src/components/app-config/__tests__/state.test.tsx +50 -0
  153. package/src/components/app-config/ai-config.tsx +156 -150
  154. package/src/components/app-config/app-config-button.tsx +6 -5
  155. package/src/components/app-config/app-config-form.tsx +5 -9
  156. package/src/components/app-config/common.tsx +7 -9
  157. package/src/components/app-config/data-form.tsx +112 -113
  158. package/src/components/app-config/mcp-config.tsx +30 -53
  159. package/src/components/app-config/user-config-form.css +91 -0
  160. package/src/components/app-config/user-config-form.tsx +266 -349
  161. package/src/components/chat/__tests__/capabilities-popover.test.tsx +0 -1
  162. package/src/components/chat/__tests__/chat-display.test.tsx +196 -0
  163. package/src/components/chat/__tests__/chat-markdown.test.tsx +43 -0
  164. package/src/components/chat/__tests__/chat-utils.test.ts +93 -1
  165. package/src/components/chat/__tests__/message-queue.test.tsx +17 -0
  166. package/src/components/chat/__tests__/notebook-access-selector.test.tsx +64 -0
  167. package/src/components/chat/__tests__/turn-timing.test.ts +84 -0
  168. package/src/components/chat/__tests__/use-assistant-turn.test.ts +95 -0
  169. package/src/components/chat/__tests__/user-message.test.tsx +27 -0
  170. package/src/components/chat/acp/__tests__/agent-panel-initialization.test.tsx +1 -1
  171. package/src/components/chat/acp/__tests__/agent-panel.test.tsx +22 -17
  172. package/src/components/chat/acp/__tests__/session-tabs.test.tsx +43 -0
  173. package/src/components/chat/acp/__tests__/state.test.ts +2 -2
  174. package/src/components/chat/acp/agent-panel.tsx +6 -3
  175. package/src/components/chat/acp/blocks.tsx +13 -12
  176. package/src/components/chat/acp/common.tsx +4 -5
  177. package/src/components/chat/acp/model-selector.tsx +1 -1
  178. package/src/components/chat/acp/session-tabs.tsx +8 -3
  179. package/src/components/chat/agent-activity.tsx +132 -0
  180. package/src/components/chat/capabilities-popover.tsx +2 -11
  181. package/src/components/chat/chat-components.tsx +9 -4
  182. package/src/components/chat/chat-display.tsx +131 -57
  183. package/src/components/chat/chat-history-popover.tsx +6 -1
  184. package/src/components/chat/chat-markdown.tsx +15 -0
  185. package/src/components/chat/chat-panel.tsx +216 -294
  186. package/src/components/chat/chat-typography.css +108 -0
  187. package/src/components/chat/chat-utils.ts +24 -35
  188. package/src/components/chat/notebook-access-selector.stories.tsx +22 -0
  189. package/src/components/chat/notebook-access-selector.tsx +100 -0
  190. package/src/components/chat/tool-call/__tests__/tool-labels.test.ts +76 -0
  191. package/src/components/chat/tool-call/__tests__/tool-step.test.tsx +57 -0
  192. package/src/components/chat/tool-call/shared.ts +6 -0
  193. package/src/components/chat/tool-call/tool-call-view.tsx +22 -76
  194. package/src/components/chat/tool-call/tool-labels.ts +172 -0
  195. package/src/components/chat/tool-call/tool-result.tsx +1 -1
  196. package/src/components/chat/tool-call/tool-step.tsx +145 -0
  197. package/src/components/chat/turn-timing.ts +73 -0
  198. package/src/components/chat/use-assistant-turn.ts +65 -0
  199. package/src/components/chat/use-chat-generation.ts +20 -0
  200. package/src/components/chat/user-message.tsx +145 -0
  201. package/src/components/data-table/TableBottomBar.tsx +4 -1
  202. package/src/components/data-table/TableTopBar.tsx +2 -0
  203. package/src/components/data-table/__tests__/chart-spec-model.test.ts +195 -195
  204. package/src/components/data-table/__tests__/column-explorer.test.tsx +27 -1
  205. package/src/components/data-table/__tests__/columns.test.tsx +105 -0
  206. package/src/components/data-table/__tests__/context-menu-interaction.test.tsx +130 -0
  207. package/src/components/data-table/__tests__/data-table.test.tsx +1 -0
  208. package/src/components/data-table/__tests__/date-popover.test.tsx +72 -0
  209. package/src/components/data-table/__tests__/export-actions.test.tsx +105 -0
  210. package/src/components/data-table/__tests__/keyboard-selection.test.tsx +188 -0
  211. package/src/components/data-table/__tests__/pagination.test.tsx +28 -1
  212. package/src/components/data-table/__tests__/search-highlight.test.tsx +109 -0
  213. package/src/components/data-table/__tests__/url-detector.test.tsx +74 -0
  214. package/src/components/data-table/cell-selection/feature.ts +2 -2
  215. package/src/components/data-table/cell-selection/types.ts +2 -2
  216. package/src/components/data-table/charts/__tests__/chart-panel.test.tsx +74 -1
  217. package/src/components/data-table/charts/__tests__/spec-snapshot.test.ts +20 -20
  218. package/src/components/data-table/charts/__tests__/spec.test.ts +78 -52
  219. package/src/components/data-table/charts/__tests__/storage.test.ts +4 -1
  220. package/src/components/data-table/charts/chart-spec/spec.ts +68 -43
  221. package/src/components/data-table/charts/charts.tsx +40 -30
  222. package/src/components/data-table/charts/storage.ts +16 -5
  223. package/src/components/data-table/column-explorer-panel/column-explorer.tsx +1 -1
  224. package/src/components/data-table/column-formatting/types.ts +1 -2
  225. package/src/components/data-table/column-summary/chart-spec-model.tsx +23 -16
  226. package/src/components/data-table/column-summary/column-summary.tsx +2 -4
  227. package/src/components/data-table/column-visibility-dropdown.tsx +8 -13
  228. package/src/components/data-table/columns.tsx +45 -46
  229. package/src/components/data-table/context-menu.tsx +36 -20
  230. package/src/components/data-table/data-table.tsx +17 -1
  231. package/src/components/data-table/date-popover.tsx +6 -4
  232. package/src/components/data-table/export-actions.tsx +76 -48
  233. package/src/components/data-table/pagination.tsx +44 -33
  234. package/src/components/data-table/range-focus/atoms.ts +4 -6
  235. package/src/components/data-table/range-focus/use-cell-range-selection.ts +65 -38
  236. package/src/components/data-table/range-focus/use-scroll-into-view.ts +21 -2
  237. package/src/components/data-table/renderers.tsx +16 -7
  238. package/src/components/data-table/row-viewer-panel/__tests__/row-viewer.test.tsx +24 -1
  239. package/src/components/data-table/row-viewer-panel/row-viewer.tsx +4 -4
  240. package/src/components/data-table/search-highlight.tsx +79 -0
  241. package/src/components/data-table/types.ts +1 -0
  242. package/src/components/data-table/url-detector.tsx +49 -9
  243. package/src/components/datasources/__tests__/column-actions.test.tsx +97 -0
  244. package/src/components/datasources/__tests__/column-preview.test.tsx +96 -5
  245. package/src/components/datasources/__tests__/datasources-interactions.test.tsx +181 -0
  246. package/src/components/datasources/__tests__/table-list.test.tsx +118 -0
  247. package/src/components/datasources/__tests__/utils.test.ts +57 -6
  248. package/src/components/datasources/column-preview.tsx +17 -12
  249. package/src/components/datasources/datasources.tsx +56 -43
  250. package/src/components/datasources/utils.ts +32 -23
  251. package/src/components/debugger/debugger-code.tsx +5 -0
  252. package/src/components/dependency-graph/custom-node.tsx +1 -1
  253. package/src/components/dependency-graph/dependency-graph-tree.tsx +8 -8
  254. package/src/components/dependency-graph/elements.ts +24 -21
  255. package/src/components/dependency-graph/minimap-content.tsx +15 -20
  256. package/src/components/dependency-graph/panels.tsx +4 -2
  257. package/src/components/dependency-graph/utils/__tests__/layout.test.ts +18 -11
  258. package/src/components/editor/SortableCell.tsx +1 -3
  259. package/src/components/editor/__tests__/Output.test.tsx +50 -32
  260. package/src/{core/attachments/__tests__/toast.test.tsx → components/editor/__tests__/agent-toast.test.tsx} +1 -1
  261. package/src/components/editor/__tests__/data-attributes.test.tsx +28 -21
  262. package/src/components/editor/__tests__/dynamic-favicon.test.tsx +111 -18
  263. package/src/components/editor/__tests__/output-persistence.test.tsx +8 -4
  264. package/src/components/editor/__tests__/package-alert.test.tsx +22 -1
  265. package/src/components/editor/actions/__tests__/name-cell-input.test.tsx +69 -0
  266. package/src/components/editor/actions/__tests__/pair-with-agent-commands.test.ts +21 -40
  267. package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +41 -14
  268. package/src/components/editor/actions/__tests__/pair-with-agent.test.tsx +6 -24
  269. package/src/components/editor/actions/__tests__/useCellActionButton.test.tsx +131 -0
  270. package/src/components/editor/actions/name-cell-input.tsx +4 -3
  271. package/src/components/editor/actions/pair-with-agent-commands.ts +10 -7
  272. package/src/components/editor/actions/pair-with-agent-modal.tsx +29 -30
  273. package/src/components/editor/actions/pair-with-agent.tsx +16 -39
  274. package/src/components/editor/actions/useCellActionButton.tsx +47 -35
  275. package/src/components/editor/actions/useConfigActions.tsx +3 -4
  276. package/src/components/editor/actions/useNotebookActions.tsx +25 -29
  277. package/src/components/editor/actions/useRestartKernel.tsx +1 -1
  278. package/src/components/editor/actions/useSetCodeVisibility.ts +11 -12
  279. package/src/components/editor/actions/useSetOutputsExpanded.ts +14 -19
  280. package/src/components/editor/ai/ai-completion-editor.tsx +2 -1
  281. package/src/components/editor/ai/completion-handlers.tsx +0 -37
  282. package/src/components/editor/ai/prompt-input.tsx +189 -0
  283. package/src/components/editor/alerts/__tests__/connection-notice.test.tsx +1 -1
  284. package/src/components/editor/alerts/connection-notice.tsx +1 -1
  285. package/src/components/editor/alerts/startup-logs-alert.tsx +1 -0
  286. package/src/components/editor/alerts/stdin-blocking-alert.tsx +4 -2
  287. package/src/components/editor/app-container.tsx +1 -1
  288. package/src/components/editor/boundary/__tests__/ErrorBoundary.test.tsx +1 -1
  289. package/src/components/editor/cell/CellStatus.tsx +23 -19
  290. package/src/components/editor/cell/CreateCellButton.tsx +15 -21
  291. package/src/components/editor/cell/DeleteButton.tsx +3 -2
  292. package/src/components/editor/cell/RunButton.tsx +18 -8
  293. package/src/components/editor/cell/StagedAICell.tsx +52 -137
  294. package/src/components/editor/cell/StopButton.tsx +2 -2
  295. package/src/components/editor/cell/__tests__/useRunCells.test.ts +45 -0
  296. package/src/components/editor/cell/__tests__/useShouldShowInterrupt.test.ts +39 -0
  297. package/src/components/editor/cell/cell-actions.tsx +12 -1
  298. package/src/components/editor/cell/cell-context-menu.tsx +4 -1
  299. package/src/components/editor/cell/code/__tests__/language-toggle.test.tsx +54 -0
  300. package/src/components/editor/cell/code/cell-editor.tsx +65 -169
  301. package/src/components/editor/cell/code/language-toggle.tsx +8 -7
  302. package/src/components/editor/cell/collapse.tsx +15 -4
  303. package/src/components/editor/cell/useAddCell.ts +4 -8
  304. package/src/components/editor/cell/useDeleteCell.tsx +10 -36
  305. package/src/components/editor/cell/useRunCells.ts +20 -8
  306. package/src/components/editor/cell/useShouldShowInterrupt.ts +2 -12
  307. package/src/components/editor/cell/useSplitCell.tsx +10 -6
  308. package/src/components/editor/chrome/components/__tests__/feedback-button.test.tsx +1 -1
  309. package/src/components/editor/chrome/components/feedback-button.tsx +2 -8
  310. package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +31 -2
  311. package/src/components/editor/chrome/panels/__tests__/runtime-details.test.tsx +1 -1
  312. package/src/components/editor/chrome/panels/__tests__/sandbox-recovery.test.tsx +1 -1
  313. package/src/components/editor/chrome/panels/cache-panel.tsx +1 -0
  314. package/src/components/editor/chrome/panels/dependency-graph-panel.tsx +1 -1
  315. package/src/components/editor/chrome/panels/logs-panel.tsx +2 -1
  316. package/src/components/editor/chrome/panels/outline/__tests__/outline-keyboard.test.tsx +84 -0
  317. package/src/components/editor/chrome/panels/outline/floating-outline.tsx +39 -14
  318. package/src/components/editor/chrome/panels/outline/useActiveOutline.tsx +3 -2
  319. package/src/components/editor/chrome/panels/packages-panel.tsx +4 -7
  320. package/src/components/editor/chrome/panels/panel-header.tsx +13 -0
  321. package/src/components/editor/chrome/panels/secrets-panel.tsx +1 -0
  322. package/src/components/editor/chrome/panels/session-panel.tsx +1 -1
  323. package/src/components/editor/chrome/panels/snippets-panel.tsx +10 -12
  324. package/src/components/editor/chrome/panels/variable-panel.tsx +1 -5
  325. package/src/components/editor/chrome/types.ts +1 -1
  326. package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +7 -9
  327. package/src/components/editor/chrome/wrapper/__tests__/document-sync-status.test.tsx +92 -0
  328. package/src/components/editor/chrome/wrapper/__tests__/pending-ai-cells.test.tsx +265 -37
  329. package/src/components/editor/chrome/wrapper/__tests__/useAskAiAboutCell.test.tsx +58 -0
  330. package/src/components/editor/chrome/wrapper/app-chrome.tsx +91 -74
  331. package/src/components/editor/chrome/wrapper/footer-items/attachment-status.tsx +4 -4
  332. package/src/components/editor/chrome/wrapper/footer-items/backend-status.tsx +1 -1
  333. package/src/components/editor/chrome/wrapper/footer-items/document-sync-status.tsx +54 -0
  334. package/src/components/editor/chrome/wrapper/footer-items/lsp-status.tsx +1 -1
  335. package/src/components/editor/chrome/wrapper/footer-items/machine-stats.tsx +1 -1
  336. package/src/components/editor/chrome/wrapper/footer-items/runtime-settings.tsx +1 -7
  337. package/src/components/editor/chrome/wrapper/footer.tsx +2 -2
  338. package/src/components/editor/chrome/wrapper/minimap-state.ts +1 -1
  339. package/src/components/editor/chrome/wrapper/pending-ai-cells.tsx +143 -72
  340. package/src/components/editor/chrome/wrapper/pending-ai-changes-badge.tsx +25 -0
  341. package/src/components/editor/chrome/wrapper/sidebar.tsx +8 -4
  342. package/src/components/editor/chrome/wrapper/storage.ts +1 -1
  343. package/src/components/editor/chrome/wrapper/useAskAiAboutCell.ts +36 -0
  344. package/src/components/editor/columns/__tests__/cell-column.test.tsx +1 -1
  345. package/src/components/editor/columns/cell-column.tsx +1 -1
  346. package/src/components/editor/columns/sortable-column.tsx +2 -2
  347. package/src/components/editor/connections/components.tsx +5 -10
  348. package/src/components/editor/controls/Controls.tsx +4 -4
  349. package/src/components/editor/controls/__tests__/command-palette.test.tsx +53 -0
  350. package/src/components/editor/controls/__tests__/keyboard-shortcuts.test.tsx +61 -0
  351. package/src/components/editor/controls/__tests__/notebook-menu-dropdown.test.tsx +10 -4
  352. package/src/components/editor/controls/command-palette.tsx +22 -10
  353. package/src/components/editor/controls/keyboard-shortcuts.tsx +14 -5
  354. package/src/components/editor/documentation.css +34 -11
  355. package/src/components/editor/dynamic-favicon.tsx +21 -40
  356. package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +25 -20
  357. package/src/components/editor/errors/auto-fix.tsx +4 -8
  358. package/src/components/editor/errors/send-error-report-button.tsx +3 -6
  359. package/src/components/editor/file-tree/__tests__/file-explorer-interactions.test.tsx +6 -0
  360. package/src/components/editor/file-tree/__tests__/file-explorer-navigation.test.tsx +47 -0
  361. package/src/components/editor/file-tree/__tests__/use-file-search.test.tsx +91 -0
  362. package/src/components/editor/file-tree/file-explorer.tsx +48 -15
  363. package/src/components/editor/file-tree/file-name-input.tsx +1 -0
  364. package/src/components/editor/file-tree/file-viewer.tsx +4 -2
  365. package/src/components/editor/file-tree/use-file-search.ts +35 -21
  366. package/src/components/editor/header/__tests__/filename-form.test.tsx +1 -4
  367. package/src/components/editor/header/__tests__/status.test.tsx +5 -3
  368. package/src/components/editor/header/app-header.tsx +1 -1
  369. package/src/components/editor/header/filename-form.tsx +1 -1
  370. package/src/components/editor/header/status.tsx +44 -22
  371. package/src/components/editor/links/__tests__/cell-link.test.tsx +148 -0
  372. package/src/components/editor/links/cell-link-list.tsx +1 -1
  373. package/src/components/editor/links/cell-link.tsx +21 -11
  374. package/src/components/editor/navigation/__tests__/clipboard.test.ts +60 -38
  375. package/src/components/editor/navigation/__tests__/navigation.test.ts +108 -85
  376. package/src/components/editor/navigation/__tests__/selection.test.ts +43 -21
  377. package/src/components/editor/navigation/clipboard.ts +17 -14
  378. package/src/components/editor/navigation/focus-utils.ts +23 -4
  379. package/src/components/editor/navigation/multi-cell-action-toolbar.tsx +17 -33
  380. package/src/components/editor/navigation/navigation.ts +74 -72
  381. package/src/components/editor/navigation/selection.ts +30 -29
  382. package/src/components/editor/notebook-banner.tsx +1 -0
  383. package/src/components/editor/notebook-cell.tsx +114 -126
  384. package/src/components/editor/output/MarimoErrorOutput.tsx +2 -1
  385. package/src/components/editor/output/MarimoTracebackOutput.tsx +19 -17
  386. package/src/components/editor/output/Outputs.css +8 -0
  387. package/src/components/editor/output/VideoOutput.tsx +6 -0
  388. package/src/components/editor/output/__tests__/VideoOutput.test.tsx +131 -0
  389. package/src/components/editor/output/__tests__/traceback.test.tsx +27 -1
  390. package/src/components/editor/output/ansi-reduce.ts +33 -20
  391. package/src/components/editor/output/console/ConsoleOutput.tsx +19 -25
  392. package/src/components/editor/output/console/__tests__/ConsoleOutput.test.tsx +125 -69
  393. package/src/components/editor/package-alert.tsx +14 -7
  394. package/src/components/editor/renderers/__tests__/add-cell-buttons.test.tsx +18 -0
  395. package/src/components/editor/renderers/cell-array.tsx +32 -100
  396. package/src/components/editor/renderers/grid-layout/__tests__/grid-layout.test.tsx +75 -0
  397. package/src/components/editor/renderers/grid-layout/grid-layout.tsx +34 -22
  398. package/src/components/editor/renderers/slides-layout/__tests__/plugin.test.ts +0 -2
  399. package/src/components/editor/renderers/slides-layout/slides-layout.tsx +1 -1
  400. package/src/components/editor/renderers/vertical-layout/__tests__/useFocusFirstEditor.test.ts +4 -4
  401. package/src/components/editor/renderers/vertical-layout/sidebar/toggle.tsx +7 -1
  402. package/src/components/editor/renderers/vertical-layout/useFocusFirstEditor.ts +1 -1
  403. package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +13 -34
  404. package/src/components/editor/stdin-blocking-alert.tsx +4 -2
  405. package/src/components/home/components.tsx +17 -31
  406. package/src/components/icons/__tests__/large-spinner.test.tsx +39 -0
  407. package/src/components/icons/large-spinner.tsx +3 -14
  408. package/src/components/icons/spinner.tsx +1 -1
  409. package/src/components/markdown/markdown-renderer.tsx +39 -28
  410. package/src/components/mcp/mcp-status-indicator.tsx +18 -14
  411. package/src/components/pages/__tests__/home-page.test.tsx +116 -0
  412. package/src/components/pages/edit-page.tsx +19 -17
  413. package/src/components/pages/home-page.tsx +164 -91
  414. package/src/components/scratchpad/scratchpad.test.tsx +89 -0
  415. package/src/components/scratchpad/scratchpad.tsx +47 -33
  416. package/src/components/scratchpad/state.ts +7 -0
  417. package/src/components/slides/__tests__/minimap-actions.test.tsx +22 -29
  418. package/src/components/slides/__tests__/minimap.test.ts +38 -19
  419. package/src/components/slides/__tests__/slide-scroll-hint.test.tsx +37 -0
  420. package/src/components/slides/minimap.tsx +21 -14
  421. package/src/components/slides/reveal-component.tsx +72 -36
  422. package/src/components/slides/reveal-slides.css +5 -4
  423. package/src/components/slides/slide-cell-view.tsx +32 -29
  424. package/src/components/slides/slide-form.tsx +1 -0
  425. package/src/components/slides/slide-scroll-hint.tsx +3 -1
  426. package/src/components/slides/slide.tsx +3 -4
  427. package/src/components/sort/SortableCellsProvider.tsx +8 -25
  428. package/src/components/tracing/tracing-keyboard.test.tsx +85 -0
  429. package/src/components/tracing/tracing.tsx +46 -42
  430. package/src/components/ui/__tests__/draggable-popover.test.tsx +24 -0
  431. package/src/components/ui/__tests__/fullscreen.test.tsx +44 -2
  432. package/src/components/ui/__tests__/range-slider.test.tsx +165 -0
  433. package/src/components/ui/alert-dialog.tsx +1 -1
  434. package/src/components/ui/draggable-popover.tsx +24 -2
  435. package/src/components/ui/fullscreen.tsx +25 -1
  436. package/src/components/ui/range-slider.tsx +22 -9
  437. package/src/components/ui/toast.tsx +1 -1
  438. package/src/core/MarimoApp.tsx +3 -1
  439. package/src/core/ai/__tests__/staged-cells.test.ts +89 -608
  440. package/src/core/ai/context/context.ts +2 -0
  441. package/src/core/ai/context/providers/__tests__/cell-output.test.ts +8 -12
  442. package/src/core/ai/context/providers/__tests__/cell.test.ts +49 -0
  443. package/src/core/ai/context/providers/cell-output.ts +2 -2
  444. package/src/core/ai/context/providers/cell.ts +54 -0
  445. package/src/core/ai/context/providers/datasource.ts +6 -2
  446. package/src/core/ai/staged-cells.ts +56 -323
  447. package/src/core/ai/state.ts +3 -0
  448. package/src/core/ai/tools/__tests__/edit-notebook-tool.test.ts +40 -88
  449. package/src/core/ai/tools/__tests__/registry.test.ts +17 -0
  450. package/src/core/ai/tools/base.ts +18 -15
  451. package/src/core/ai/tools/edit-notebook-tool.ts +86 -80
  452. package/src/core/ai/tools/registry.ts +13 -10
  453. package/src/core/ai/tools/sample-tool.ts +6 -7
  454. package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +4 -426
  455. package/src/core/cells/__tests__/actions.test.ts +624 -35
  456. package/src/core/cells/__tests__/add-missing-import.test.ts +7 -8
  457. package/src/core/cells/__tests__/arrange.test.ts +432 -0
  458. package/src/core/cells/__tests__/cell.test.ts +85 -151
  459. package/src/core/cells/__tests__/cells.test.ts +269 -2667
  460. package/src/core/cells/__tests__/collapseConsoleOutputs.test.ts +54 -0
  461. package/src/core/cells/__tests__/document.test.ts +164 -0
  462. package/src/core/cells/__tests__/editor.test.ts +57 -0
  463. package/src/core/cells/__tests__/flush-document.test.ts +40 -0
  464. package/src/core/cells/__tests__/harness.ts +106 -0
  465. package/src/core/cells/__tests__/layout.test.ts +177 -0
  466. package/src/core/cells/__tests__/notebook-outline.test.ts +122 -0
  467. package/src/core/cells/__tests__/outputs.test.tsx +71 -53
  468. package/src/core/cells/__tests__/pending-delete-service.test.tsx +11 -20
  469. package/src/core/cells/__tests__/session.test.ts +445 -211
  470. package/src/core/cells/__tests__/utils.test.ts +76 -347
  471. package/src/core/cells/actions.ts +678 -6
  472. package/src/core/cells/add-missing-import.ts +47 -43
  473. package/src/core/cells/arrange.ts +529 -0
  474. package/src/core/cells/cell.ts +45 -136
  475. package/src/core/cells/cells.ts +133 -1385
  476. package/src/core/cells/columns.ts +38 -0
  477. package/src/core/cells/document.ts +178 -0
  478. package/src/core/cells/edit.ts +30 -0
  479. package/src/core/cells/editor.ts +82 -0
  480. package/src/core/cells/focus.ts +27 -6
  481. package/src/core/cells/layout.ts +221 -0
  482. package/src/core/cells/outputs.ts +12 -28
  483. package/src/core/cells/pending-delete-service.ts +8 -14
  484. package/src/core/cells/scrollCellIntoView.ts +6 -0
  485. package/src/core/cells/session.ts +76 -53
  486. package/src/core/cells/types.ts +34 -41
  487. package/src/core/cells/utils.ts +46 -75
  488. package/src/core/codemirror/__tests__/format.test.ts +0 -34
  489. package/src/core/codemirror/__tests__/setup.test.ts +61 -29
  490. package/src/core/codemirror/ai/__tests__/resources.test.ts +117 -0
  491. package/src/core/codemirror/ai/resources.ts +1 -1
  492. package/src/core/codemirror/cells/__tests__/extensions.test.ts +2 -6
  493. package/src/core/codemirror/cells/extensions.ts +30 -28
  494. package/src/core/codemirror/cells/state.ts +2 -6
  495. package/src/core/codemirror/cm.ts +5 -1
  496. package/src/core/codemirror/config/types.ts +1 -1
  497. package/src/core/codemirror/copilot/__tests__/getCodes.test.ts +6 -7
  498. package/src/core/codemirror/copilot/getCodes.ts +2 -2
  499. package/src/core/codemirror/format.ts +6 -23
  500. package/src/core/codemirror/keymaps/vim.ts +4 -8
  501. package/src/core/codemirror/language/__tests__/sql.test.ts +22 -20
  502. package/src/core/codemirror/language/__tests__/utils.test.ts +187 -15
  503. package/src/core/codemirror/language/cell-code.ts +114 -0
  504. package/src/core/codemirror/language/extension.ts +52 -35
  505. package/src/core/codemirror/language/languages/markdown.ts +8 -6
  506. package/src/core/codemirror/language/languages/python.ts +19 -13
  507. package/src/core/codemirror/language/languages/sql/sql.ts +16 -24
  508. package/src/core/codemirror/language/panel/panel.tsx +1 -0
  509. package/src/core/codemirror/language/types.ts +7 -7
  510. package/src/core/codemirror/language/utils.ts +9 -39
  511. package/src/core/codemirror/lsp/__tests__/notebook-lsp.test.ts +1 -2
  512. package/src/core/codemirror/lsp/notebook-lsp.ts +6 -5
  513. package/src/core/codemirror/lsp/utils.ts +1 -1
  514. package/src/core/codemirror/misc/__tests__/paste.test.ts +3 -1
  515. package/src/core/codemirror/misc/paste.ts +1 -1
  516. package/src/core/codemirror/placeholder/__tests__/extensions.test.ts +75 -0
  517. package/src/core/codemirror/placeholder/extensions.ts +80 -19
  518. package/src/core/codemirror/readonly/__tests__/extension.test.ts +1 -1
  519. package/src/core/codemirror/readonly/extension.ts +2 -2
  520. package/src/core/codemirror/utils.ts +1 -0
  521. package/src/core/config/__tests__/app-config.test.ts +64 -0
  522. package/src/core/config/__tests__/config-schema.test.ts +56 -6
  523. package/src/core/config/config-schema.ts +17 -4
  524. package/src/core/config/config.ts +65 -20
  525. package/src/core/config/feature-flag.tsx +0 -2
  526. package/src/core/datasets/__tests__/data-source-discovery.test.ts +5 -5
  527. package/src/core/datasets/data-source-discovery.ts +4 -6
  528. package/src/core/documentation/doc-lookup.ts +1 -1
  529. package/src/core/edit-app.tsx +22 -28
  530. package/src/core/errors/error-entries.ts +4 -1
  531. package/src/core/executions/__tests__/atoms.test.ts +99 -0
  532. package/src/core/executions/__tests__/select.test.ts +268 -0
  533. package/src/core/executions/atoms.ts +206 -0
  534. package/src/core/executions/hooks.ts +31 -0
  535. package/src/core/executions/index.ts +31 -0
  536. package/src/core/executions/select.ts +175 -0
  537. package/src/core/export/__tests__/hooks.test.ts +0 -5
  538. package/src/core/export/hooks.ts +1 -1
  539. package/src/core/export/layout.ts +2 -2
  540. package/src/core/hotkeys/actions.ts +2 -4
  541. package/src/core/hotkeys/hotkeys.ts +2 -1
  542. package/src/core/islands/__tests__/bootstrap.test.ts +69 -17
  543. package/src/core/islands/__tests__/bridge.test.ts +85 -27
  544. package/src/core/islands/bootstrap.ts +50 -54
  545. package/src/core/islands/bridge.ts +84 -25
  546. package/src/core/islands/components/__tests__/IslandControls.test.tsx +7 -5
  547. package/src/core/islands/components/output-wrapper.tsx +5 -5
  548. package/src/core/islands/components/web-components.tsx +1 -1
  549. package/src/core/islands/worker/__tests__/controller.test.ts +62 -4
  550. package/src/core/islands/worker/controller.ts +4 -10
  551. package/src/core/islands/worker/worker.tsx +33 -33
  552. package/src/core/kernel/__tests__/RuntimeState.test.ts +1 -1
  553. package/src/core/kernel/__tests__/handlers.test.ts +7 -153
  554. package/src/core/kernel/handlers.ts +17 -79
  555. package/src/core/kernel/state.ts +0 -15
  556. package/src/core/lifecycle/render-policy.ts +12 -26
  557. package/src/core/meta/__tests__/code-visibility.test.tsx +78 -70
  558. package/src/core/meta/code-visibility.ts +18 -9
  559. package/src/core/network/__tests__/connection.test.ts +1 -1
  560. package/src/core/network/__tests__/requests-lazy.test.ts +6 -8
  561. package/src/core/network/connection-notice.ts +1 -1
  562. package/src/core/network/connection.ts +1 -1
  563. package/src/core/network/requests-lazy.ts +5 -8
  564. package/src/core/network/requests-network.ts +5 -31
  565. package/src/core/network/requests-static.ts +1 -4
  566. package/src/core/network/requests-toasting.tsx +1 -4
  567. package/src/core/network/types.ts +6 -14
  568. package/src/core/network/useConnectionNotice.ts +8 -2
  569. package/src/core/notebook/__tests__/operations.test.ts +51 -0
  570. package/src/core/notebook/attachments.ts +38 -0
  571. package/src/core/notebook/document/__tests__/codemirror.test.ts +759 -0
  572. package/src/core/notebook/document/__tests__/draft.test.ts +363 -0
  573. package/src/core/notebook/document/__tests__/fixtures.ts +92 -0
  574. package/src/core/notebook/document/__tests__/hooks.test.tsx +391 -0
  575. package/src/core/notebook/document/__tests__/lifecycle-scenarios.test.ts +61 -0
  576. package/src/core/notebook/document/__tests__/model.test.ts +247 -0
  577. package/src/core/notebook/document/__tests__/protocol-fixtures.ts +38 -0
  578. package/src/core/notebook/document/__tests__/recovery-scenarios.test.ts +230 -0
  579. package/src/core/notebook/document/__tests__/scenario-fixture.ts +43 -0
  580. package/src/core/notebook/document/__tests__/scenarios.test.ts +205 -0
  581. package/src/core/notebook/document/__tests__/state.test.ts +459 -0
  582. package/src/core/notebook/document/__tests__/submission.test.ts +203 -0
  583. package/src/core/notebook/document/__tests__/test-host.test.ts +92 -0
  584. package/src/core/notebook/document/__tests__/test-host.ts +247 -0
  585. package/src/core/notebook/document/__tests__/test-notebook.ts +149 -0
  586. package/src/core/notebook/document/__tests__/user.ts +61 -0
  587. package/src/core/notebook/document/codemirror.ts +216 -0
  588. package/src/core/notebook/document/draft.ts +502 -0
  589. package/src/core/notebook/document/hooks.tsx +118 -0
  590. package/src/core/notebook/document/index.ts +24 -0
  591. package/src/core/notebook/document/model.ts +102 -0
  592. package/src/core/notebook/document/select.ts +41 -0
  593. package/src/core/notebook/document/state.ts +378 -0
  594. package/src/core/notebook/document/sync.ts +369 -0
  595. package/src/core/notebook/index.ts +19 -0
  596. package/src/core/notebook/operations.ts +30 -0
  597. package/src/core/notebook/replica/AGENTS.md +11 -0
  598. package/src/core/{host → notebook/replica}/__tests__/attachments.test.ts +9 -10
  599. package/src/core/{host → notebook/replica}/__tests__/document.test.ts +18 -16
  600. package/src/core/notebook/replica/__tests__/executions.test.ts +200 -0
  601. package/src/core/notebook/replica/__tests__/fixtures.ts +79 -0
  602. package/src/core/notebook/replica/__tests__/replica.test.ts +559 -0
  603. package/src/core/notebook/replica/__tests__/results.test.ts +193 -0
  604. package/src/core/notebook/replica/__tests__/runtime.test.ts +78 -0
  605. package/src/core/notebook/replica/backend.ts +19 -0
  606. package/src/core/notebook/replica/connection.ts +42 -0
  607. package/src/core/notebook/replica/index.ts +20 -0
  608. package/src/core/notebook/replica/replica.ts +336 -0
  609. package/src/core/{host → notebook/replica}/resources/attachments.ts +6 -3
  610. package/src/core/{host → notebook/replica}/resources/document.ts +3 -3
  611. package/src/core/notebook/replica/resources/executions.ts +157 -0
  612. package/src/core/notebook/replica/resources/results.ts +48 -0
  613. package/src/core/notebook/replica/resources/runtime.ts +14 -0
  614. package/src/core/packages/__tests__/useSandboxController.test.tsx +1 -1
  615. package/src/core/packages/useSandboxController.ts +1 -1
  616. package/src/core/run-app.tsx +49 -51
  617. package/src/core/runtime/__tests__/adapter.test.ts +4 -4
  618. package/src/core/runtime/__tests__/createWsUrl.test.ts +77 -7
  619. package/src/core/runtime/__tests__/runtime.test.ts +32 -74
  620. package/src/core/runtime/adapter.ts +5 -5
  621. package/src/core/runtime/config.ts +2 -2
  622. package/src/core/runtime/runtime.ts +33 -46
  623. package/src/core/runtime/types.ts +0 -2
  624. package/src/core/saving/__tests__/document.test.tsx +123 -0
  625. package/src/core/saving/__tests__/state.test.ts +111 -0
  626. package/src/core/saving/__tests__/useAutoSave.test.tsx +67 -0
  627. package/src/core/saving/document.ts +33 -0
  628. package/src/core/saving/filename.ts +1 -1
  629. package/src/core/saving/save-component.tsx +39 -60
  630. package/src/core/saving/state.ts +38 -39
  631. package/src/core/saving/useAutoSave.ts +17 -53
  632. package/src/core/session/__tests__/backend.test.ts +236 -0
  633. package/src/core/session/__tests__/useNotebookReplica.hook.test.tsx +186 -0
  634. package/src/core/{websocket → session}/__tests__/useNotebookSession.hook.test.tsx +247 -262
  635. package/src/core/session/active-notebook.ts +28 -0
  636. package/src/core/session/backend.ts +123 -0
  637. package/src/core/session/embedded.ts +7 -0
  638. package/src/core/{websocket → session}/types.ts +2 -2
  639. package/src/core/session/useNotebookReplica.ts +74 -0
  640. package/src/core/{websocket → session}/useNotebookSession.tsx +85 -67
  641. package/src/core/state/__tests__/jotai.test.tsx +108 -0
  642. package/src/core/state/jotai.ts +1 -1
  643. package/src/core/static/__tests__/files.test.ts +38 -10
  644. package/src/core/static/files.ts +2 -0
  645. package/src/core/{websocket → transports}/__tests__/connection-state.test.ts +1 -13
  646. package/src/core/transports/__tests__/server.test.ts +408 -0
  647. package/src/core/transports/__tests__/static.test.ts +306 -0
  648. package/src/core/transports/__tests__/wasm.test.ts +166 -0
  649. package/src/core/{websocket → transports}/connection-state.ts +9 -16
  650. package/src/core/transports/server.ts +152 -0
  651. package/src/core/transports/static.ts +166 -0
  652. package/src/core/transports/wasm.ts +97 -0
  653. package/src/core/vscode/__tests__/vscode-bindings.test.ts +125 -0
  654. package/src/core/vscode/vscode-bindings.ts +14 -2
  655. package/src/core/wasm/__tests__/bridge.test.ts +210 -64
  656. package/src/core/wasm/__tests__/worker.test.ts +188 -0
  657. package/src/core/wasm/bridge.ts +120 -73
  658. package/src/core/wasm/rpc.ts +15 -3
  659. package/src/core/wasm/store.ts +3 -3
  660. package/src/core/wasm/worker/bootstrap.ts +55 -51
  661. package/src/core/wasm/worker/getMarimoWheel.ts +1 -1
  662. package/src/core/wasm/worker/save-worker.ts +9 -10
  663. package/src/core/wasm/worker/types.ts +30 -18
  664. package/src/core/wasm/worker/worker.ts +118 -72
  665. package/src/css/app/Cell.css +7 -36
  666. package/src/css/app/codemirror.css +1 -1
  667. package/src/css/common.css +1 -1
  668. package/src/css/globals.css +66 -22
  669. package/src/hooks/__tests__/useAsyncData.test.tsx +148 -0
  670. package/src/hooks/__tests__/useDebounce.test.tsx +51 -0
  671. package/src/hooks/__tests__/useDeepCompareMemoize.test.tsx +39 -0
  672. package/src/hooks/__tests__/useDelayElapsed.test.tsx +28 -0
  673. package/src/hooks/__tests__/useInternalStateWithSync.test.tsx +20 -0
  674. package/src/hooks/__tests__/useScript.test.tsx +77 -0
  675. package/src/hooks/__tests__/useStateForKey.test.tsx +257 -0
  676. package/src/hooks/useAsyncData.ts +3 -3
  677. package/src/hooks/useDataSourceDiscovery.ts +6 -4
  678. package/src/hooks/useDebounce.ts +6 -10
  679. package/src/hooks/useDeepCompareMemoize.ts +1 -1
  680. package/src/hooks/useDelayElapsed.ts +14 -10
  681. package/src/hooks/useElapsedTime.ts +3 -3
  682. package/src/hooks/useEventListener.ts +1 -2
  683. package/src/hooks/useInputHistory.ts +4 -7
  684. package/src/hooks/useInternalStateWithSync.ts +6 -6
  685. package/src/hooks/useResizeObserver.ts +4 -7
  686. package/src/hooks/useScript.ts +67 -70
  687. package/src/hooks/useStateForKey.ts +93 -0
  688. package/src/mount.tsx +20 -15
  689. package/src/plugins/__tests__/plugin-object-schemas.test.ts +104 -0
  690. package/src/plugins/core/RenderHTML.tsx +3 -5
  691. package/src/plugins/core/__test__/RenderHTML-copy.test.tsx +60 -0
  692. package/src/plugins/core/registerReactComponent.tsx +5 -14
  693. package/src/plugins/impl/CodeEditorPlugin.tsx +1 -1
  694. package/src/plugins/impl/DataEditorPlugin.tsx +34 -9
  695. package/src/plugins/impl/DataTablePlugin.tsx +69 -37
  696. package/src/plugins/impl/DropdownPlugin.tsx +2 -6
  697. package/src/plugins/impl/FileBrowserPlugin.tsx +4 -16
  698. package/src/plugins/impl/FormPlugin.tsx +1 -1
  699. package/src/plugins/impl/RangeSliderPlugin.tsx +16 -2
  700. package/src/plugins/impl/RefreshPlugin.tsx +1 -0
  701. package/src/plugins/impl/SliderPlugin.tsx +6 -8
  702. package/src/plugins/impl/__tests__/DataEditorPlugin.test.ts +93 -4
  703. package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +387 -1
  704. package/src/plugins/impl/__tests__/FileBrowserPlugin.test.tsx +45 -0
  705. package/src/plugins/impl/__tests__/RangeSliderPlugin.test.tsx +109 -0
  706. package/src/plugins/impl/__tests__/SliderPlugin.test.tsx +24 -3
  707. package/src/plugins/impl/anywidget/__tests__/registry.test.ts +153 -0
  708. package/src/plugins/impl/anywidget/__tests__/widget-binding.test.ts +241 -2
  709. package/src/plugins/impl/anywidget/model.ts +3 -1
  710. package/src/plugins/impl/anywidget/registry.ts +18 -14
  711. package/src/plugins/impl/anywidget/runtime.ts +21 -9
  712. package/src/plugins/impl/anywidget/widget-binding.ts +160 -82
  713. package/src/plugins/impl/chat/__tests__/chat-prompts.test.tsx +93 -0
  714. package/src/plugins/impl/chat/chat-ui.tsx +17 -15
  715. package/src/plugins/impl/code/any-language-editor.tsx +2 -2
  716. package/src/plugins/impl/common/error-banner.tsx +5 -3
  717. package/src/plugins/impl/data-editor/__tests__/column-types.test.ts +37 -0
  718. package/src/plugins/impl/data-editor/__tests__/editor-state.test.ts +52 -1
  719. package/src/plugins/impl/data-editor/__tests__/glide-data-editor.test.tsx +113 -1
  720. package/src/plugins/impl/data-editor/__tests__/glide-utils.test.ts +116 -0
  721. package/src/plugins/impl/data-editor/column-types.ts +52 -0
  722. package/src/plugins/impl/data-editor/editor-state.ts +18 -2
  723. package/src/plugins/impl/data-editor/glide-data-editor.tsx +73 -12
  724. package/src/plugins/impl/data-editor/glide-utils.ts +33 -10
  725. package/src/plugins/impl/data-editor/types.ts +7 -0
  726. package/src/plugins/impl/data-explorer/ConnectedDataExplorerComponent.tsx +1 -1
  727. package/src/plugins/impl/data-explorer/components/column-summary.tsx +4 -2
  728. package/src/plugins/impl/data-explorer/state/reducer.ts +1 -1
  729. package/src/plugins/impl/data-frames/DataFramePlugin.tsx +1 -2
  730. package/src/plugins/impl/data-frames/__tests__/sidebar.test.tsx +160 -0
  731. package/src/plugins/impl/data-frames/forms/__tests__/__snapshots__/form.test.tsx.snap +66 -66
  732. package/src/plugins/impl/data-frames/panel.tsx +44 -16
  733. package/src/plugins/impl/mpl-interactive/MplInteractivePlugin.tsx +5 -3
  734. package/src/plugins/impl/panel/PanelPlugin.tsx +16 -17
  735. package/src/plugins/impl/panel/__tests__/utils.test.ts +39 -1
  736. package/src/plugins/impl/panel/utils.ts +17 -0
  737. package/src/plugins/impl/plotly/PlotlyPlugin.tsx +2 -5
  738. package/src/plugins/impl/plotly/selection.ts +25 -19
  739. package/src/plugins/impl/vega/VegaPlugin.tsx +2 -3
  740. package/src/plugins/impl/vega/make-selectable.ts +4 -7
  741. package/src/plugins/layout/DownloadPlugin.tsx +7 -11
  742. package/src/plugins/layout/RoutesPlugin.tsx +1 -1
  743. package/src/plugins/layout/__test__/DownloadPlugin.test.tsx +205 -0
  744. package/src/stories/__tests__/editor-story.test.tsx +33 -0
  745. package/src/stories/button.stories.tsx +1 -0
  746. package/src/stories/cell-status.stories.tsx +17 -10
  747. package/src/stories/cell.stories.tsx +2 -36
  748. package/src/stories/data-table.stories.tsx +31 -0
  749. package/src/stories/editor.stories.tsx +24 -17
  750. package/src/stories/form-wrapper.stories.tsx +2 -2
  751. package/src/stories/layout/vertical/one-column.stories.tsx +28 -32
  752. package/src/stories/runtime-story.tsx +23 -17
  753. package/src/stories/select.stories.tsx +2 -2
  754. package/src/stories/switchable-multi-select.stories.tsx +1 -1
  755. package/src/utils/__tests__/arrays.test.ts +35 -0
  756. package/src/utils/__tests__/batch-requests.test.ts +3 -3
  757. package/src/utils/__tests__/{createReducer.test.ts → createReducer.test.tsx} +127 -1
  758. package/src/utils/__tests__/download.test.tsx +183 -23
  759. package/src/utils/__tests__/numbers.test.ts +105 -1
  760. package/src/utils/__tests__/objects.test.ts +21 -0
  761. package/src/utils/arrays.ts +11 -2
  762. package/src/utils/blob.ts +1 -1
  763. package/src/utils/createReducer.ts +115 -79
  764. package/src/utils/download.ts +67 -6
  765. package/src/utils/maps.ts +0 -3
  766. package/src/utils/numbers.ts +79 -18
  767. package/src/utils/objects.ts +4 -9
  768. package/src/utils/time.ts +10 -0
  769. package/src/utils/tracer.ts +9 -5
  770. package/src/utils/zod-utils.ts +1 -3
  771. package/dist/_baseMerge-BiGRwuqv.js +0 -165
  772. package/dist/any-language-editor-D4q-IFcM.js +0 -76
  773. package/dist/assets/__vite-browser-external-BXYkiFsk.js +0 -1
  774. package/dist/assets/worker-HPY9fbhz.js +0 -114
  775. package/dist/dropdown-menu-DHP9P4VE.js +0 -4678
  776. package/dist/feedback-button-B2JRVQRd.js +0 -379
  777. package/dist/hasIn-o2Us0uVx.js +0 -132
  778. package/dist/html-to-image-DWTzN7q3.js +0 -3251
  779. package/dist/isArrayLikeObject-BZl-15ir.js +0 -450
  780. package/dist/isEmpty-BSZ1LaUo.js +0 -35
  781. package/dist/isSymbol-kQcfYbNX.js +0 -8
  782. package/dist/label-DoysmLMW.js +0 -281
  783. package/dist/mermaid-HWGCJPDP-ByCsZnYF.js +0 -2
  784. package/dist/pick-CJeoxYOS.js +0 -51
  785. package/dist/precisionRound-DMIVQrt2.js +0 -97
  786. package/dist/select-DemoKT4G.js +0 -1344
  787. package/dist/toDate-CJWU9dyl.js +0 -1185
  788. package/dist/toast-PkdZraM8.js +0 -1830
  789. package/dist/use-restore-focus-CKjfFjLM.js +0 -6369
  790. package/dist/useLifecycle-BGWzTwHK.js +0 -169
  791. package/dist/useTheme-BhQvec43.js +0 -904
  792. package/src/components/chat/reasoning-accordion.tsx +0 -61
  793. package/src/components/chat/tool-call/tool-error-card.tsx +0 -81
  794. package/src/components/chat/tool-call/tool-history-row.tsx +0 -174
  795. package/src/components/editor/ai/__tests__/staged-cell-submission.test.ts +0 -106
  796. package/src/components/editor/ai/add-cell-with-ai.tsx +0 -506
  797. package/src/components/editor/ai/staged-cell-submission.ts +0 -46
  798. package/src/components/editor/cell/__tests__/StagedAICell.test.tsx +0 -64
  799. package/src/components/editor/cell/code/__tests__/rtc-editor-config.test.ts +0 -50
  800. package/src/components/editor/chrome/wrapper/footer-items/rtc-status.tsx +0 -65
  801. package/src/components/editor/navigation/__tests__/state.test.ts +0 -81
  802. package/src/components/editor/navigation/state.ts +0 -65
  803. package/src/components/icons/loading-ellipsis.tsx +0 -46
  804. package/src/core/ai/tools/__tests__/run-cells-tool.test.ts +0 -656
  805. package/src/core/ai/tools/run-cells-tool.ts +0 -303
  806. package/src/core/attachments/state.ts +0 -18
  807. package/src/core/cells/__tests__/apply-transaction.test.ts +0 -991
  808. package/src/core/cells/__tests__/document-changes.test.ts +0 -1140
  809. package/src/core/cells/__tests__/document-roundtrip.test.ts +0 -392
  810. package/src/core/cells/document-changes.ts +0 -944
  811. package/src/core/codemirror/rtc/extension.ts +0 -453
  812. package/src/core/codemirror/rtc/loro/__tests__/sync.test.ts +0 -52
  813. package/src/core/codemirror/rtc/loro/awareness.ts +0 -531
  814. package/src/core/codemirror/rtc/loro/colors.ts +0 -38
  815. package/src/core/codemirror/rtc/loro/sync.ts +0 -168
  816. package/src/core/host/AGENTS.md +0 -11
  817. package/src/core/host/__tests__/events.test.ts +0 -46
  818. package/src/core/host/__tests__/fixtures.ts +0 -39
  819. package/src/core/host/__tests__/notebook-client.test.ts +0 -433
  820. package/src/core/host/__tests__/protocol.test.ts +0 -214
  821. package/src/core/host/__tests__/stream.test.ts +0 -209
  822. package/src/core/host/connection.ts +0 -81
  823. package/src/core/host/events.ts +0 -61
  824. package/src/core/host/notebook-client.ts +0 -290
  825. package/src/core/host/protocol.ts +0 -1945
  826. package/src/core/host/resources/resource.ts +0 -47
  827. package/src/core/host/stream.ts +0 -211
  828. package/src/core/rtc/state.ts +0 -34
  829. package/src/core/state/__tests__/jotai.test.ts +0 -25
  830. package/src/core/wasm/worker/message-buffer.ts +0 -33
  831. package/src/core/websocket/__tests__/createConnectionTransport.test.ts +0 -49
  832. package/src/core/websocket/__tests__/useNotebookClient.hook.test.tsx +0 -170
  833. package/src/core/websocket/state.ts +0 -7
  834. package/src/core/websocket/transports/__tests__/basic.test.ts +0 -275
  835. package/src/core/websocket/transports/__tests__/sse.test.ts +0 -335
  836. package/src/core/websocket/transports/__tests__/transport.test.ts +0 -308
  837. package/src/core/websocket/transports/__tests__/ws.test.ts +0 -125
  838. package/src/core/websocket/transports/basic.ts +0 -102
  839. package/src/core/websocket/transports/create.ts +0 -31
  840. package/src/core/websocket/transports/sse.ts +0 -308
  841. package/src/core/websocket/transports/transport.ts +0 -50
  842. package/src/core/websocket/transports/ws.ts +0 -98
  843. package/src/core/websocket/useNotebookClient.ts +0 -88
  844. package/src/utils/__tests__/id-tree.test.ts +0 -2145
  845. package/src/utils/id-tree.tsx +0 -1134
  846. /package/src/{core/attachments/toast.ts → components/editor/agent-toast.ts} +0 -0
  847. /package/src/core/{websocket → session}/connection-utils.ts +0 -0
@@ -1,74 +1,68 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
- import { historyField } from "@codemirror/commands";
4
- import { dequal as isEqual } from "dequal";
3
+ import type * as Protocol from "@marimo-team/client/protocol";
5
4
  import { type Atom, atom, useAtom, useAtomValue } from "jotai";
6
5
  import { atomFamily, selectAtom, splitAtom } from "jotai/utils";
7
6
  import { createRef, type ReducerWithoutAction } from "react";
8
7
  import type { CellHandle } from "@/components/editor/notebook-cell";
9
- import type { CollapsibleTree } from "@/utils/id-tree";
8
+ import type { EditableDocument, Placement } from "@/core/notebook/document";
10
9
  import {
11
- type CellColumnId,
12
- type CellIndex,
13
- MultiColumn,
14
- } from "@/utils/id-tree";
10
+ cellExecutionInputsAtom,
11
+ cellExecutionView,
12
+ withdrawCells,
13
+ } from "@/core/executions";
14
+ import type { CellColumnId } from "./layout";
15
15
  import { invariant } from "@/utils/invariant";
16
16
  import { Logger } from "@/utils/Logger";
17
- import { clamp } from "@/utils/math";
18
17
  import { Objects } from "@/utils/objects";
19
18
  import { extractAllTracebackInfo, type TracebackInfo } from "@/utils/traceback";
20
19
  import { createReducerAndAtoms } from "../../utils/createReducer";
21
20
  import { foldAllBulk, unfoldAllBulk } from "../codemirror/editing/commands";
22
21
  import { editorMountScheduler } from "../codemirror/editor-mount-scheduler";
23
- import {
24
- splitEditor,
25
- updateEditorCodeFromPython,
26
- } from "../codemirror/language/utils";
27
22
  import type { SerializedEditorState } from "../codemirror/types";
28
- import { findCollapseRange, mergeOutlines } from "../dom/outline";
23
+ import { mergeOutlines } from "../dom/outline";
29
24
  import type { CellMessage } from "../kernel/messages";
30
25
  import { isErrorMime } from "../mime";
31
26
  import type { CellConfig } from "../network/types";
32
- import { canUseRtc, isRtcEnabled } from "../rtc/state";
33
27
  import { createDeepEqualAtom, store } from "../state/jotai";
34
28
  import { isWasm } from "../wasm/utils";
35
- import { prepareCellForExecution, transitionCell } from "./cell";
36
- import { documentTransactionMiddleware } from "./document-changes";
37
- import { CellId, SCRATCH_CELL_ID, SETUP_CELL_ID } from "./ids";
29
+ import { transitionCell } from "./cell";
30
+ import { applyDocument } from "./document";
31
+ import { isCodeHidden, shownHiddenCodeAtom } from "./editor";
32
+ import { type CellId, SCRATCH_CELL_ID } from "./ids";
38
33
  import { type CellLog, getCellLogsForMessage } from "./logs";
39
- import {
40
- focusAndScrollCellIntoView,
41
- scrollToBottom,
42
- scrollToTop,
43
- } from "./scrollCellIntoView";
34
+ import type { Outline } from "./outline";
35
+ import { focusAndScrollCellIntoView } from "./scrollCellIntoView";
44
36
  import {
45
37
  type CellData,
46
38
  type CellRuntimeState,
47
39
  createCell,
48
- createCellConfig,
49
40
  createCellRuntimeState,
41
+ isEdited,
50
42
  } from "./types";
51
43
  import {
52
44
  canUndoDeletes,
53
45
  disabledCellIds,
54
46
  enabledCellIds,
55
47
  getUndoLabel,
56
- notebookIsRunning,
57
48
  notebookNeedsRun,
58
- notebookQueueOrRunningCount,
59
49
  } from "./utils";
60
50
 
51
+ /** A deleted cell as it was, with where it went in the document. */
52
+ export interface DeletedCell {
53
+ cell: Protocol.CellInput;
54
+ placement: Placement;
55
+ /** The editor's state at deletion, so undo brings its history back. */
56
+ serializedEditorState: SerializedEditorState | null;
57
+ }
58
+
61
59
  /**
62
- * History entry for undoing a cell deletion.
60
+ * History entry for undoing one deletion, of one or more cells.
63
61
  */
64
62
  export interface UndoDeleteEntry {
65
63
  type: "delete";
66
- name: string;
67
- serializedEditorState: SerializedEditorState;
68
- column: CellColumnId;
69
- index: CellIndex;
70
- isSetupCell: boolean;
71
- config: CellConfig;
64
+ /** In document order. Each cell is placed after the one before it. */
65
+ cells: DeletedCell[];
72
66
  }
73
67
 
74
68
  /**
@@ -77,7 +71,7 @@ export interface UndoDeleteEntry {
77
71
  export interface UndoMoveEntry {
78
72
  type: "move";
79
73
  cellIds: CellId[];
80
- placements: Array<{ columnId: CellColumnId; index: CellIndex }>;
74
+ placements: Array<{ columnId: CellColumnId; index: number }>;
81
75
  }
82
76
 
83
77
  export type HistoryEntry = UndoDeleteEntry | UndoMoveEntry;
@@ -89,7 +83,8 @@ export interface NotebookState {
89
83
  /**
90
84
  * Order of cells on the page.
91
85
  */
92
- cellIds: MultiColumn<CellId>;
86
+ /** The cells in document order. */
87
+ cellIds: CellId[];
93
88
  /**
94
89
  * Map of cells to their view state
95
90
  */
@@ -116,12 +111,6 @@ export interface NotebookState {
116
111
  * Logs of all cell messages
117
112
  */
118
113
  cellLogs: CellLog[];
119
- /**
120
- * Set of cells that have been created that are initialized with `hide_code`.
121
- *
122
- * These start temporarily open until their first blur event.
123
- */
124
- untouchedNewCells: Set<CellId>;
125
114
  }
126
115
 
127
116
  function withScratchCell(notebookState: NotebookState): NotebookState {
@@ -153,733 +142,49 @@ function withScratchCell(notebookState: NotebookState): NotebookState {
153
142
  */
154
143
  export function initialNotebookState(): NotebookState {
155
144
  return withScratchCell({
156
- cellIds: MultiColumn.from([]),
145
+ cellIds: [],
157
146
  cellData: {},
158
147
  cellRuntime: {},
159
148
  cellHandles: {},
160
149
  history: [],
161
150
  scrollKey: null,
162
151
  cellLogs: [],
163
- untouchedNewCells: new Set<CellId>(),
164
152
  });
165
153
  }
166
154
 
167
- /** The target cell ID to create a new cell relative to. Can be:
168
- * - A CellId string for an existing cell
169
- * - "__end__" to append at the end of the first column
170
- * - {type: "__end__", columnId} to append at the end of a specific column
171
- */
172
- export type CellPosition =
173
- | CellId
174
- | "__end__"
175
- | { type: "__end__"; columnId: CellColumnId };
176
-
177
- export interface CreateNewCellAction {
178
- cellId: CellPosition;
179
- /** Whether to insert before (true) or after (false) the target cell */
180
- before: boolean;
181
- /** Initial code content for the new cell */
182
- code?: string;
183
- /** Optional name for the new cell */
184
- name?: string;
185
- /** Optional cell configuration */
186
- config?: CellConfig;
187
- /** The last executed code for the new cell */
188
- lastCodeRun?: string;
189
- /** Timestamp of the last execution */
190
- lastExecutionTime?: number;
191
- /** Optional custom ID for the new cell. Auto-generated if not provided */
192
- newCellId?: CellId;
193
- /** Whether to focus the new cell after creation */
194
- autoFocus?: boolean;
195
- /** If true, skip creation if code already exists */
196
- skipIfCodeExists?: boolean;
197
- /** Hide the code in the new cell. This will be initially shown until the cell is blurred for the first time. */
198
- hideCode?: boolean;
199
- }
200
-
201
155
  /**
202
156
  * Actions and reducer for the notebook state.
203
157
  */
204
158
  const {
205
159
  reducer,
206
- addMiddleware,
207
160
  createActions,
208
161
  useActions,
209
162
  valueAtom: notebookAtom,
210
163
  } = createReducerAndAtoms(initialNotebookState, {
211
- createNewCell: (state, action: CreateNewCellAction) => {
212
- const {
213
- cellId,
214
- before,
215
- code,
216
- name,
217
- config,
218
- lastCodeRun = null,
219
- lastExecutionTime = null,
220
- autoFocus = true,
221
- skipIfCodeExists = false,
222
- hideCode = undefined,
223
- } = action;
224
-
225
- let columnId: CellColumnId;
226
- let cellIndex: number;
227
-
228
- // If skipIfCodeExists is true, check if the code already exists in the notebook
229
- if (skipIfCodeExists) {
230
- for (const cellId of state.cellIds.inOrderIds) {
231
- if (state.cellData[cellId]?.code === code) {
232
- return state;
233
- }
234
- }
235
- }
236
-
237
- if (cellId === "__end__") {
238
- const column = state.cellIds.atOrThrow(0);
239
- columnId = column.id;
240
- cellIndex = column.length;
241
- } else if (typeof cellId === "string") {
242
- const column = state.cellIds.findWithId(cellId);
243
- columnId = column.id;
244
- cellIndex = column.topLevelIds.indexOf(cellId);
245
- } else if (cellId.type === "__end__") {
246
- const column =
247
- state.cellIds.get(cellId.columnId) || state.cellIds.atOrThrow(0);
248
- columnId = column.id;
249
- cellIndex = column.length;
250
- } else {
251
- throw new Error("Invalid cellId");
252
- }
253
-
254
- const newCellId = action.newCellId || CellId.create();
255
- const insertionIndex = before ? cellIndex : cellIndex + 1;
256
-
257
- return {
258
- ...state,
259
- cellIds: state.cellIds.insertId(newCellId, columnId, insertionIndex),
260
- cellData: {
261
- ...state.cellData,
262
- [newCellId]: createCell({
263
- id: newCellId,
264
- code,
265
- name,
266
- lastCodeRun,
267
- config: createCellConfig({
268
- ...config,
269
- ...(hideCode != null && { hide_code: hideCode }),
270
- }),
271
- lastExecutionTime,
272
- edited: Boolean(code) && code !== lastCodeRun,
273
- }),
274
- },
275
- cellRuntime: {
276
- ...state.cellRuntime,
277
- [newCellId]: createCellRuntimeState(),
278
- },
279
- cellHandles: {
280
- ...state.cellHandles,
281
- [newCellId]: createRef(),
282
- },
283
- scrollKey: autoFocus ? newCellId : null,
284
- untouchedNewCells:
285
- hideCode && autoFocus
286
- ? new Set([...state.untouchedNewCells, newCellId])
287
- : state.untouchedNewCells,
288
- };
289
- },
290
- moveCell: (
291
- state,
292
- action: {
293
- cellId: CellId;
294
- before?: boolean;
295
- direction?: "left" | "right";
296
- },
297
- ) => {
298
- const { cellId, before, direction } = action;
299
-
300
- if (before !== undefined && direction !== undefined) {
301
- Logger.warn(
302
- "Both before and direction specified for moveCell. Ignoring one.",
303
- );
304
- }
305
-
306
- // Handle left/right movement
307
- if (direction) {
308
- const fromColumn = state.cellIds.findWithId(cellId);
309
- const fromColumnIndex = state.cellIds.indexOf(fromColumn);
310
- const toColumnIndex =
311
- direction === "left" ? fromColumnIndex - 1 : fromColumnIndex + 1;
312
- const toColumn = state.cellIds.at(toColumnIndex);
313
-
314
- // If no column to move to, return unchanged state
315
- if (!toColumn) {
316
- return state;
317
- }
318
-
319
- return {
320
- ...state,
321
- cellIds: state.cellIds.moveAcrossColumns(
322
- fromColumn.id,
323
- cellId,
324
- toColumn.id,
325
- undefined,
326
- ),
327
- scrollKey: cellId,
328
- };
329
- }
330
-
331
- // Handle up/down movement
332
- const column = state.cellIds.findWithId(cellId);
333
- const cellIndex = column.indexOfOrThrow(cellId);
334
-
335
- if (before && cellIndex === 0) {
336
- return {
337
- ...state,
338
- cellIds: state.cellIds.moveWithinColumn(column.id, cellIndex, 0),
339
- scrollKey: cellId,
340
- };
341
- }
342
- if (!before && cellIndex === column.length - 1) {
343
- return {
344
- ...state,
345
- cellIds: state.cellIds.moveWithinColumn(
346
- column.id,
347
- cellIndex,
348
- column.length - 1,
349
- ),
350
- scrollKey: cellId,
351
- };
352
- }
353
-
354
- return before
355
- ? {
356
- ...state,
357
- cellIds: state.cellIds.moveWithinColumn(
358
- column.id,
359
- cellIndex,
360
- cellIndex - 1,
361
- ),
362
- scrollKey: cellId,
363
- }
364
- : {
365
- ...state,
366
- cellIds: state.cellIds.moveWithinColumn(
367
- column.id,
368
- cellIndex,
369
- cellIndex + 1,
370
- ),
371
- scrollKey: cellId,
372
- };
373
- },
374
- moveCellToIndex: (
375
- state,
376
- action: {
377
- cellId: CellId;
378
- columnId: CellColumnId;
379
- index: number;
380
- },
381
- ) => {
382
- const { cellId, columnId, index } = action;
383
- const fromColumn = state.cellIds.findWithId(cellId);
384
- const fromIndex = fromColumn.indexOfOrThrow(cellId);
385
-
386
- const destinationColumn = state.cellIds.get(columnId);
387
- if (!destinationColumn) {
388
- return state;
389
- }
390
-
391
- const clampedIndex = Math.max(0, Math.min(index, destinationColumn.length));
392
- const adjustedIndex =
393
- fromColumn.id === columnId && fromIndex < clampedIndex
394
- ? clampedIndex - 1
395
- : clampedIndex;
396
-
397
- if (fromColumn.id === columnId && fromIndex === adjustedIndex) {
398
- return state;
399
- }
400
-
401
- const withoutCell = state.cellIds.deleteById(cellId);
402
- const updatedColumn = withoutCell.get(columnId);
403
- if (!updatedColumn) {
404
- return state;
405
- }
406
-
407
- return {
408
- ...state,
409
- cellIds: withoutCell.insertId(
410
- cellId,
411
- columnId,
412
- Math.max(0, Math.min(adjustedIndex, updatedColumn.length)),
413
- ),
414
- scrollKey: null,
415
- };
416
- },
417
- dropCellOverCell: (state, action: { cellId: CellId; overCellId: CellId }) => {
418
- const { cellId, overCellId } = action;
419
-
420
- const fromColumn = state.cellIds.findWithId(cellId);
421
- const toColumn = state.cellIds.findWithId(overCellId);
422
-
423
- const fromIndex = fromColumn.indexOfOrThrow(cellId);
424
- const toIndex = toColumn.indexOfOrThrow(overCellId);
425
-
426
- if (fromColumn.id === toColumn.id) {
427
- if (fromIndex === toIndex) {
428
- return state;
429
- }
430
- return {
431
- ...state,
432
- cellIds: state.cellIds.moveWithinColumn(
433
- fromColumn.id,
434
- fromIndex,
435
- toIndex,
436
- ),
437
- scrollKey: null,
438
- };
439
- }
440
-
441
- return {
442
- ...state,
443
- cellIds: state.cellIds.moveAcrossColumns(
444
- fromColumn.id,
445
- cellId,
446
- toColumn.id,
447
- overCellId,
448
- ),
449
- scrollKey: null,
450
- };
451
- },
452
- moveCellsRelativeTo: (
453
- state,
454
- action: {
455
- cellIds: CellId[];
456
- targetCellId: CellId;
457
- position: "before" | "after";
458
- previousPlacements?: Array<{ columnId: CellColumnId; index: CellIndex }>;
459
- },
460
- ) => {
461
- const { cellIds, targetCellId, position, previousPlacements } = action;
462
- if (cellIds.length === 0) {
463
- return state;
464
- }
465
- const newCellIds = state.cellIds.moveCellsRelativeTo(
466
- cellIds,
467
- targetCellId,
468
- position,
469
- );
470
- // Only record undo when caller provided full before-state
471
- const canUndoMove =
472
- previousPlacements && previousPlacements.length === cellIds.length;
473
- const history = canUndoMove
474
- ? [
475
- ...state.history,
476
- { type: "move" as const, cellIds, placements: previousPlacements },
477
- ]
478
- : state.history;
479
- return {
480
- ...state,
481
- cellIds: newCellIds,
482
- history,
483
- scrollKey: null,
484
- };
485
- },
486
- dropCellOverColumn: (
487
- state,
488
- action: { cellId: CellId; columnId: CellColumnId },
489
- ) => {
490
- const { cellId, columnId } = action;
491
- const fromColumn = state.cellIds.findWithId(cellId);
492
-
493
- return {
494
- ...state,
495
- cellIds: state.cellIds.moveAcrossColumns(
496
- fromColumn.id,
497
- cellId,
498
- columnId,
499
- undefined,
500
- ),
501
- };
502
- },
503
- dropOverNewColumn: (state, action: { cellId: CellId }) => {
504
- const { cellId } = action;
505
- return {
506
- ...state,
507
- cellIds: state.cellIds.moveToNewColumn(cellId),
508
- };
509
- },
510
- moveColumn: (
511
- state,
512
- action: {
513
- column: CellColumnId;
514
- overColumn: CellColumnId | "_left_" | "_right_";
515
- },
516
- ) => {
517
- if (action.column === action.overColumn) {
518
- return state;
519
- }
520
- return {
521
- ...state,
522
- cellIds: state.cellIds.moveColumn(action.column, action.overColumn),
523
- };
524
- },
525
- focusCell: (
526
- state,
527
- action: { cellId: CellId; where: "before" | "after" | "exact" },
528
- ) => {
529
- const column = state.cellIds.findWithId(action.cellId);
530
- if (column.length === 0) {
531
- return state;
532
- }
533
-
534
- const { cellId, where } = action;
535
- const index = column.indexOfOrThrow(cellId);
536
-
537
- let focusIndex: number;
538
- if (where === "before") {
539
- focusIndex = clamp(index - 1, 0, column.length - 1);
540
- } else if (where === "after") {
541
- focusIndex = clamp(index + 1, 0, column.length - 1);
542
- } else {
543
- focusIndex = index;
544
- }
545
- const focusCellId = column.atOrThrow(focusIndex);
546
- // can scroll immediately, without setting scrollKey in state, because
547
- // CellArray won't need to re-render
548
- focusAndScrollCellIntoView({
549
- cellId: focusCellId,
550
- cell: state.cellHandles[focusCellId],
551
- isCodeHidden: isCellCodeHidden(state, focusCellId),
552
- codeFocus: where === "after" ? "top" : "bottom",
553
- variableName: undefined,
554
- });
555
- return state;
556
- },
557
- focusTopCell: (state) => {
558
- // TODO: focus the existing column, not the first column
559
- const column = state.cellIds.getColumns().at(0);
560
- if (column === undefined || column.length === 0) {
561
- return state;
562
- }
563
-
564
- const cellId = column.first();
565
- focusAndScrollCellIntoView({
566
- cellId: cellId,
567
- cell: state.cellHandles[cellId],
568
- isCodeHidden: isCellCodeHidden(state, cellId),
569
- codeFocus: undefined,
570
- variableName: undefined,
571
- });
572
- scrollToTop();
573
- return state;
574
- },
575
- focusBottomCell: (state) => {
576
- // TODO: focus the existing column, not the last column
577
- const column = state.cellIds.getColumns().at(-1);
578
- if (column === undefined || column.length === 0) {
579
- return state;
580
- }
581
-
582
- const cellId = column.last();
583
- focusAndScrollCellIntoView({
584
- cellId: cellId,
585
- cell: state.cellHandles[cellId],
586
- isCodeHidden: isCellCodeHidden(state, cellId),
587
- codeFocus: undefined,
588
- variableName: undefined,
589
- });
590
- scrollToBottom();
591
- return state;
592
- },
593
- sendToTop: (state, action: { cellId: CellId; scroll?: boolean }) => {
594
- const column = state.cellIds.findWithId(action.cellId);
595
- if (column.length === 0) {
596
- return state;
597
- }
598
-
599
- const { cellId, scroll = true } = action;
600
- const cellIndex = column.indexOfOrThrow(cellId);
601
- if (cellIndex === 0) {
602
- return state;
603
- }
604
-
605
- return {
606
- ...state,
607
- cellIds: state.cellIds.moveWithinColumn(column.id, cellIndex, 0),
608
- scrollKey: scroll ? cellId : null,
609
- };
610
- },
611
- sendToBottom: (state, action: { cellId: CellId; scroll?: boolean }) => {
612
- const column = state.cellIds.findWithId(action.cellId);
613
- if (column.length === 0) {
614
- return state;
615
- }
616
-
617
- const { cellId, scroll = true } = action;
618
- const cellIndex = column.indexOfOrThrow(cellId);
619
- const newIndex = column.length - 1;
620
-
621
- if (cellIndex === newIndex) {
622
- return state;
623
- }
624
-
625
- return {
626
- ...state,
627
- cellIds: state.cellIds.moveWithinColumn(column.id, cellIndex, newIndex),
628
- scrollKey: scroll ? cellId : null,
629
- };
630
- },
631
- addColumn: (state, action: { columnId: CellColumnId }) => {
632
- // Add column and new cell
633
- const newCellId = CellId.create();
634
- return {
635
- ...state,
636
- cellIds: state.cellIds.addColumn(action.columnId, [newCellId]),
637
- cellData: {
638
- ...state.cellData,
639
- [newCellId]: createCell({
640
- id: newCellId,
641
- }),
642
- },
643
- cellRuntime: {
644
- ...state.cellRuntime,
645
- [newCellId]: createCellRuntimeState(),
646
- },
647
- cellHandles: {
648
- ...state.cellHandles,
649
- [newCellId]: createRef(),
650
- },
651
- scrollKey: newCellId,
652
- };
653
- },
654
- addColumnBreakpoint: (state, action: { cellId: CellId }) => {
655
- const { cellId } = action;
656
- if (state.cellIds.getColumns()[0].inOrderIds[0] === cellId) {
657
- return state;
658
- }
659
- return {
660
- ...state,
661
- cellIds: state.cellIds.insertBreakpoint(cellId),
662
- };
663
- },
664
- deleteColumn: (state, action: { columnId: CellColumnId }) => {
665
- // Move all cells in the column to the previous column
666
- const { columnId } = action;
667
- return {
668
- ...state,
669
- cellIds: state.cellIds.delete(columnId),
670
- };
671
- },
672
- mergeAllColumns: (state) => {
673
- return {
674
- ...state,
675
- cellIds: state.cellIds.mergeAllColumns(),
676
- };
677
- },
678
- compactColumns: (state) => {
679
- return {
680
- ...state,
681
- cellIds: state.cellIds.compact(),
682
- };
683
- },
684
- deleteCell: (state, action: { cellId: CellId }) => {
685
- const cellId = action.cellId;
686
-
687
- // Can't delete the last cell, across all columns
688
- if (state.cellIds.hasOnlyOneId()) {
689
- return state;
690
- }
691
-
692
- let column: CollapsibleTree<CellId>;
693
- try {
694
- column = state.cellIds.findWithId(cellId);
695
- } catch (error) {
696
- // Expected for kernel-only cells or out-of-order transactions.
697
- Logger.warn("Skipping delete for missing cellId", {
698
- cellId,
699
- error,
700
- });
701
- return state;
702
- }
703
-
704
- const cellIndex = column.indexOfOrThrow(cellId);
705
- const focusIndex = cellIndex === 0 ? 1 : cellIndex - 1;
706
- let scrollKey: CellId | null = null;
707
- if (column.length > 1) {
708
- scrollKey = column.atOrThrow(focusIndex);
709
- }
710
-
711
- const editorView = state.cellHandles[cellId].current?.editorView;
712
- const serializedEditorState = editorView?.state.toJSON({
713
- history: historyField,
714
- });
715
- if (serializedEditorState) {
716
- serializedEditorState.doc = state.cellData[cellId].code;
717
- }
718
-
719
- // release the granular atom(s) created for this cell
720
- releaseCellAtoms(cellId);
721
- const prevData = state.cellData[cellId];
722
- return {
723
- ...state,
724
- cellIds: state.cellIds.deleteById(cellId),
725
- history: [
726
- ...state.history,
727
- {
728
- type: "delete",
729
- name: prevData.name,
730
- serializedEditorState: serializedEditorState,
731
- column: column.id,
732
- index: cellIndex,
733
- isSetupCell: cellId === SETUP_CELL_ID,
734
- config: prevData.config,
735
- },
736
- ],
737
- scrollKey: scrollKey,
738
- };
739
- },
740
- undoDeleteCell: (state) => {
741
- if (state.history.length === 0) {
742
- return state;
743
- }
744
-
745
- const last = state.history[state.history.length - 1];
746
-
747
- if (last.type === "move") {
748
- const { cellIds, placements } = last;
749
- if (
750
- cellIds.length === 0 ||
751
- placements.length !== cellIds.length ||
752
- cellIds.some((id) => !state.cellData[id])
753
- ) {
754
- return { ...state, history: state.history.slice(0, -1) };
755
- }
756
- const toRestore = cellIds.map((id, i) => ({
757
- id,
758
- columnId: placements[i].columnId,
759
- index: placements[i].index,
760
- }));
761
- return {
762
- ...state,
763
- cellIds: state.cellIds.placeCells(toRestore),
764
- history: state.history.slice(0, -1),
765
- scrollKey: cellIds[0] ?? null,
766
- };
767
- }
768
-
769
- const {
770
- name,
771
- serializedEditorState = { doc: "" },
772
- column,
773
- index,
774
- isSetupCell,
775
- config,
776
- } = last;
777
-
778
- const cellId = isSetupCell ? SETUP_CELL_ID : CellId.create();
779
- const undoCell = createCell({
780
- id: cellId,
781
- name,
782
- code: serializedEditorState.doc,
783
- edited: serializedEditorState.doc.trim().length > 0,
784
- serializedEditorState,
785
- config,
786
- });
787
-
788
- return {
789
- ...state,
790
- cellIds: state.cellIds.insertId(cellId, column, index),
791
- cellData: {
792
- ...state.cellData,
793
- [cellId]: undoCell,
794
- },
795
- cellRuntime: {
796
- ...state.cellRuntime,
797
- [cellId]: createCellRuntimeState(),
798
- },
799
- cellHandles: {
800
- ...state.cellHandles,
801
- [cellId]: createRef(),
802
- },
803
- history: state.history.slice(0, -1),
804
- // Scroll to the newly created cell
805
- scrollKey: cellId,
806
- };
807
- },
808
- clearSerializedEditorState: (state, action: { cellId: CellId }) => {
809
- const { cellId } = action;
810
- return updateCellData({
811
- state,
812
- cellId,
813
- cellReducer: (cell) => {
814
- return {
815
- ...cell,
816
- serializedEditorState: null,
817
- };
818
- },
819
- });
820
- },
821
- updateCellCode: (
164
+ /**
165
+ * Keep a cell as run, or as edited, as it was before formatting rewrote
166
+ * its code from `before` to `after`. The code itself arrives through the
167
+ * document.
168
+ */
169
+ codeFormatted: (
822
170
  state,
823
- action: {
824
- cellId: CellId;
825
- code: string;
826
- /**
827
- * Whether or not the update is a formatting change,
828
- * if so, the 'edited' state will be handled differently.
829
- */
830
- formattingChange: boolean;
831
- },
171
+ action: { cellId: CellId; before: string; after: string },
832
172
  ) => {
833
- const { cellId, code, formattingChange } = action;
173
+ const { cellId, before, after } = action;
834
174
  if (!state.cellData[cellId]) {
835
175
  return state;
836
176
  }
837
-
838
- return updateCellData({
839
- state,
840
- cellId,
841
- cellReducer: (cell) => {
842
- // Formatting-only change means we can re-use the last code run
843
- // if it was not previously edited. And we don't change the edited state.
844
- return formattingChange
845
- ? {
846
- ...cell,
847
- code: code,
848
- lastCodeRun: cell.edited ? cell.lastCodeRun : code,
849
- }
850
- : {
851
- ...cell,
852
- code: code,
853
- edited: code.trim() !== cell.lastCodeRun,
854
- };
855
- },
856
- });
857
- },
858
- updateCellName: (state, action: { cellId: CellId; name: string }) => {
859
- const { cellId, name } = action;
860
177
  return updateCellData({
861
178
  state,
862
179
  cellId,
863
180
  cellReducer: (cell) => {
181
+ const lastCodeRun = isEdited(cell.lastCodeRun, before)
182
+ ? cell.lastCodeRun
183
+ : after;
864
184
  return {
865
185
  ...cell,
866
- name: name,
867
- };
868
- },
869
- });
870
- },
871
- updateCellConfig: (
872
- state,
873
- action: { cellId: CellId; config: Partial<CellConfig> },
874
- ) => {
875
- const { cellId, config } = action;
876
- return updateCellData({
877
- state,
878
- cellId,
879
- cellReducer: (cell) => {
880
- return {
881
- ...cell,
882
- config: createCellConfig({ ...cell.config, ...config }),
186
+ lastCodeRun,
187
+ edited: isEdited(lastCodeRun, cell.code),
883
188
  };
884
189
  },
885
190
  });
@@ -888,9 +193,7 @@ const {
888
193
  const newState = updateCellRuntimeState({
889
194
  state,
890
195
  cellId: action.cellId,
891
- cellReducer: (cell) => {
892
- return prepareCellForExecution(cell);
893
- },
196
+ cellReducer: (cell) => ({ ...cell, debuggerActive: false }),
894
197
  });
895
198
  return updateCellData({
896
199
  state: newState,
@@ -939,158 +242,14 @@ const {
939
242
  cellLogs: [...state.cellLogs, ...action.logs],
940
243
  };
941
244
  },
942
- setCellIds: (state, action: { cellIds: CellId[] }) => {
943
- const isTheSame = isEqual(state.cellIds.inOrderIds, action.cellIds);
944
- if (isTheSame) {
945
- return state;
946
- }
947
-
948
- // Create new cell data and runtime states for the new cell IDs
949
- const nextCellData = { ...state.cellData };
950
- const nextCellRuntime = { ...state.cellRuntime };
951
- const nextCellHandles = { ...state.cellHandles };
952
-
953
- for (const cellId of action.cellIds) {
954
- if (!(cellId in state.cellData)) {
955
- nextCellData[cellId] = createCell({ id: cellId });
956
- }
957
- if (!(cellId in state.cellRuntime)) {
958
- nextCellRuntime[cellId] = createCellRuntimeState();
959
- }
960
- if (!(cellId in state.cellHandles)) {
961
- nextCellHandles[cellId] = createRef();
962
- }
963
- }
964
-
965
- return {
966
- ...state,
967
- cellIds: MultiColumn.fromWithPreviousShape(action.cellIds, state.cellIds),
968
- cellData: nextCellData,
969
- cellRuntime: nextCellRuntime,
970
- cellHandles: nextCellHandles,
971
- };
972
- },
973
- /**
974
- * Rebuild the MultiColumn tree using each cell's `config.column` value.
975
- *
976
- * Used after a transaction whose `set-config` changes updated cells'
977
- * column metadata without physically moving them in the tree. Cells with
978
- * `config.column == null` inherit the column of the previous cell in the
979
- * given order (see `MultiColumn.fromIdsAndColumns`), which lets the server
980
- * send column changes only at column boundaries.
981
- */
982
- rebuildCellColumns: (state, action: { cellIds: CellId[] }) => {
983
- const newCellIds = MultiColumn.fromIdsAndColumns(
984
- action.cellIds.map((id) => [
985
- id,
986
- state.cellData[id]?.config.column ?? null,
987
- ]),
988
- );
989
- return { ...state, cellIds: newCellIds };
990
- },
991
- setCellCodes: (
992
- state,
993
- action: {
994
- codes: string[];
995
- ids: CellId[];
996
- codeIsStale: boolean;
997
- names?: string[];
998
- configs?: CellConfig[];
999
- },
1000
- ) => {
1001
- invariant(
1002
- action.codes.length === action.ids.length,
1003
- "Expected codes and ids to have the same length",
1004
- );
1005
-
1006
- let nextState = { ...state };
1007
-
1008
- const cellReducer = ({
1009
- cell,
1010
- code,
1011
- cellId,
1012
- name,
1013
- config,
1014
- }: {
1015
- cell: CellData | undefined;
1016
- code: string;
1017
- cellId: CellId;
1018
- name?: string;
1019
- config?: CellConfig;
1020
- }) => {
1021
- if (!cell) {
1022
- return createCell({
1023
- id: cellId,
1024
- code,
1025
- name: name,
1026
- config: config,
1027
- lastCodeRun: action.codeIsStale ? null : code,
1028
- edited: action.codeIsStale && code.trim().length > 0,
1029
- });
1030
- }
1031
-
1032
- // If code is stale, we don't promote it to lastCodeRun
1033
- const lastCodeRun = action.codeIsStale ? cell.lastCodeRun : code;
1034
-
1035
- // Mark as edited if the code has changed
1036
- const edited = lastCodeRun
1037
- ? lastCodeRun.trim() !== code.trim()
1038
- : Boolean(code);
1039
-
1040
- // No change
1041
- if (cell.code.trim() === code.trim()) {
1042
- return {
1043
- ...cell,
1044
- code: code,
1045
- edited,
1046
- lastCodeRun,
1047
- ...(name !== undefined && { name }),
1048
- ...(config !== undefined && { config }),
1049
- };
1050
- }
1051
-
1052
- // Update codemirror if mounted. RTC-backed cells sync via Loro; others
1053
- // (e.g. scratchpad) still need an explicit editor update.
1054
- if (!isRtcEnabled() || !canUseRtc(cellId)) {
1055
- const cellHandle = nextState.cellHandles[cellId].current;
1056
- if (cellHandle?.editorViewOrNull) {
1057
- updateEditorCodeFromPython(cellHandle.editorViewOrNull, code);
1058
- }
1059
- }
1060
-
1061
- return {
1062
- ...cell,
1063
- code: code,
1064
- edited,
1065
- lastCodeRun,
1066
- ...(name !== undefined && { name }),
1067
- ...(config !== undefined && { config }),
1068
- };
1069
- };
1070
-
1071
- for (let i = 0; i < action.ids.length; i++) {
1072
- const cellId = action.ids[i];
1073
- const code = action.codes[i];
1074
- if (cellId === undefined || code === undefined) {
1075
- continue;
245
+ /** Follow the host document's cells and order. */
246
+ reconcileDocument: (state, document: EditableDocument) => {
247
+ const nextState = applyDocument(state, document);
248
+ for (const cellId of state.cellIds) {
249
+ if (!(cellId in nextState.cellData)) {
250
+ releaseCellAtoms(cellId);
1076
251
  }
1077
- const name = action.names?.[i];
1078
- const config = action.configs?.[i];
1079
- nextState = {
1080
- ...nextState,
1081
- cellData: {
1082
- ...nextState.cellData,
1083
- [cellId]: cellReducer({
1084
- cell: nextState.cellData[cellId],
1085
- code,
1086
- cellId,
1087
- name,
1088
- config,
1089
- }),
1090
- },
1091
- };
1092
252
  }
1093
-
1094
253
  return nextState;
1095
254
  },
1096
255
  setStdinResponse: (
@@ -1114,6 +273,7 @@ const {
1114
273
  mimetype: stdinOutput.mimetype,
1115
274
  data: stdinOutput.data,
1116
275
  timestamp: stdinOutput.timestamp,
276
+ id: stdinOutput.id,
1117
277
  // An empty response resolves the prompt without retaining the secret.
1118
278
  response: stdinOutput.mimetype === "text/password" ? "" : response,
1119
279
  };
@@ -1126,155 +286,37 @@ const {
1126
286
  },
1127
287
  });
1128
288
  },
1129
- setCells: (state, cells: CellData[]) => {
1130
- const cellData = Object.fromEntries(cells.map((cell) => [cell.id, cell]));
1131
-
1132
- // WASM has no server-side SessionView to replay outputs, so the
1133
- // snapshot hydrated by notebookStateFromSession is the only source.
1134
- const preserveSnapshot = isWasm();
1135
- const runtimeFor = (cellId: CellId): CellRuntimeState => {
1136
- if (!preserveSnapshot) {
1137
- return createCellRuntimeState();
1138
- }
1139
- const prev = state.cellRuntime[cellId];
1140
- const hasSnapshot =
1141
- prev && (prev.output != null || prev.consoleOutputs.length > 0);
1142
- return hasSnapshot ? prev : createCellRuntimeState();
1143
- };
1144
- const cellRuntime = Object.fromEntries(
1145
- cells.map((cell) => [cell.id, runtimeFor(cell.id)]),
1146
- );
1147
-
1148
- return withScratchCell({
1149
- ...state,
1150
- cellIds: MultiColumn.fromIdsAndColumns(
1151
- cells.map((cell) => [cell.id, cell.config.column]),
1152
- ),
1153
- cellData: cellData,
1154
- cellRuntime: cellRuntime,
1155
- cellHandles: Object.fromEntries(
1156
- cells.map((cell) => [cell.id, createRef()]),
1157
- ),
1158
- });
1159
- },
1160
- /**
1161
- * Move focus to next cell
1162
- *
1163
- * Creates a new cell if the current cell is the last one in the array.
1164
- *
1165
- * If needed, scrolls newly created or focused cell into view.
1166
- *
1167
- * Replicates Shift+Enter functionality of Jupyter
1168
- */
1169
- moveToNextCell: (
1170
- state,
1171
- action: { cellId: CellId; before: boolean; noCreate?: boolean },
1172
- ) => {
1173
- const { cellId, before, noCreate = false } = action;
1174
-
1175
- // Can't move focus of scratch cell
1176
- if (cellId === SCRATCH_CELL_ID) {
1177
- return state;
1178
- }
1179
-
1180
- const column = state.cellIds.findWithId(cellId);
1181
- const index = column.indexOfOrThrow(cellId);
1182
- const nextCellIndex = before ? index - 1 : index + 1;
1183
-
1184
- const isPastLastCell = nextCellIndex === column.length;
1185
- const isBeforeFirstCell = nextCellIndex === -1;
1186
-
1187
- // Create a new cell at the end and set scrollKey to focus it
1188
- if (isPastLastCell && !noCreate) {
1189
- const newCellId = CellId.create();
1190
- return {
1191
- ...state,
1192
- cellIds: state.cellIds.insertId(newCellId, column.id, nextCellIndex),
1193
- cellData: {
1194
- ...state.cellData,
1195
- [newCellId]: createCell({ id: newCellId }),
1196
- },
1197
- cellRuntime: {
1198
- ...state.cellRuntime,
1199
- [newCellId]: createCellRuntimeState(),
1200
- },
1201
- cellHandles: {
1202
- ...state.cellHandles,
1203
- [newCellId]: createRef(),
1204
- },
1205
- scrollKey: newCellId,
1206
- };
1207
- }
1208
-
1209
- if (isBeforeFirstCell && !noCreate) {
1210
- const newCellId = CellId.create();
1211
- return {
1212
- ...state,
1213
- cellIds: state.cellIds.insertId(newCellId, column.id, 0),
1214
- cellData: {
1215
- ...state.cellData,
1216
- [newCellId]: createCell({ id: newCellId }),
1217
- },
1218
- cellRuntime: {
1219
- ...state.cellRuntime,
1220
- [newCellId]: createCellRuntimeState(),
1221
- },
1222
- cellHandles: {
1223
- ...state.cellHandles,
1224
- [newCellId]: createRef(),
1225
- },
1226
- scrollKey: newCellId,
1227
- };
1228
- }
1229
-
1230
- if ((isPastLastCell || isBeforeFirstCell) && noCreate) {
1231
- // Trying to move past the last cell or before the first cell
1232
- // with noCreate=true - do nothing
1233
- return state;
1234
- }
1235
-
1236
- if (nextCellIndex >= 0 && nextCellIndex < column.length) {
1237
- // Move to valid cell within bounds
1238
- const nextCellId = column.atOrThrow(nextCellIndex);
1239
- // Just focus, no state change
1240
- focusAndScrollCellIntoView({
1241
- cellId: nextCellId,
1242
- cell: state.cellHandles[nextCellId],
1243
- isCodeHidden: isCellCodeHidden(state, nextCellId),
1244
- codeFocus: before ? "bottom" : "top",
1245
- variableName: undefined,
1246
- });
1247
- }
1248
-
1249
- return state;
1250
- },
1251
- markTouched: (state, action: { cellId: CellId }) => {
1252
- const { cellId } = action;
1253
-
1254
- if (state.untouchedNewCells.has(cellId)) {
1255
- const nextUntouchedNewCells = new Set(state.untouchedNewCells);
1256
- nextUntouchedNewCells.delete(cellId);
1257
- return {
1258
- ...state,
1259
- untouchedNewCells: nextUntouchedNewCells,
289
+ /** Record what the kernel last ran for each cell. */
290
+ setLastRun: (state, action: { lastCodeRun: Record<CellId, string> }) => {
291
+ const cellData = { ...state.cellData };
292
+ for (const cellId of state.cellIds) {
293
+ const cell = state.cellData[cellId];
294
+ const lastCodeRun = action.lastCodeRun[cellId] ?? null;
295
+ cellData[cellId] = {
296
+ ...cell,
297
+ lastCodeRun,
298
+ // A cell the kernel never ran is not stale on load.
299
+ edited: lastCodeRun !== null && isEdited(lastCodeRun, cell.code),
1260
300
  };
1261
301
  }
1262
-
1263
- return state;
302
+ return { ...state, cellData };
1264
303
  },
1265
- markUntouched: (state, action: { cellId: CellId }) => {
1266
- const { cellId } = action;
1267
-
1268
- if (!state.untouchedNewCells.has(cellId)) {
1269
- const nextUntouchedNewCells = new Set(state.untouchedNewCells);
1270
- nextUntouchedNewCells.add(cellId);
1271
- return {
1272
- ...state,
1273
- untouchedNewCells: nextUntouchedNewCells,
1274
- };
304
+ /**
305
+ * Start each cell's runtime over for a new kernel. WASM keeps the outputs
306
+ * its mount-time snapshot gave it.
307
+ */
308
+ resetCellRuntimes: (state) => {
309
+ const preserveSnapshot = isWasm();
310
+ const cellRuntime = { ...state.cellRuntime };
311
+ for (const cellId of [...state.cellIds, SCRATCH_CELL_ID]) {
312
+ const previous = state.cellRuntime[cellId];
313
+ const hasSnapshot =
314
+ preserveSnapshot &&
315
+ previous !== undefined &&
316
+ (previous.output != null || previous.consoleOutputs.length > 0);
317
+ cellRuntime[cellId] = hasSnapshot ? previous : createCellRuntimeState();
1275
318
  }
1276
-
1277
- return state;
319
+ return { ...state, cellRuntime };
1278
320
  },
1279
321
  scrollToTarget: (state) => {
1280
322
  // Scroll to the specified cell and clear the scroll key.
@@ -1283,16 +325,15 @@ const {
1283
325
  return state;
1284
326
  }
1285
327
 
1286
- const column = state.cellIds.findWithId(scrollKey);
1287
- const index = column.indexOfOrThrow(scrollKey);
1288
-
1289
- const cellId =
1290
- index === column.length - 1 ? column.last() : column.atOrThrow(index);
1291
-
328
+ const cellId = scrollKey;
1292
329
  focusAndScrollCellIntoView({
1293
330
  cellId: cellId,
1294
331
  cell: state.cellHandles[cellId],
1295
- isCodeHidden: isCellCodeHidden(state, cellId),
332
+ isCodeHidden: isCodeHidden(
333
+ state.cellData[cellId]?.config.hide_code,
334
+ store.get(shownHiddenCodeAtom),
335
+ cellId,
336
+ ),
1296
337
  codeFocus: undefined,
1297
338
  variableName: undefined,
1298
339
  });
@@ -1322,201 +363,6 @@ const {
1322
363
  cellLogs: [],
1323
364
  };
1324
365
  },
1325
- collapseCell: (state, action: { cellId: CellId }) => {
1326
- const { cellId } = action;
1327
- const column = state.cellIds.findWithId(cellId);
1328
-
1329
- // Get all the top-level outlines
1330
- const outlines = column.topLevelIds.map((id) => {
1331
- const cell = state.cellRuntime[id];
1332
- return cell.outline;
1333
- });
1334
-
1335
- // Find the start/end of the collapsed range
1336
- const startIndex = column.indexOfOrThrow(cellId);
1337
- const range = findCollapseRange(startIndex, outlines);
1338
- if (!range) {
1339
- return state;
1340
- }
1341
- const endCellId = column.atOrThrow(range[1]);
1342
-
1343
- return {
1344
- ...state,
1345
- // Collapse the range
1346
- cellIds: state.cellIds.transformWithCellId(cellId, (column) =>
1347
- column.collapse(cellId, endCellId),
1348
- ),
1349
- scrollKey: cellId,
1350
- };
1351
- },
1352
- expandCell: (state, action: { cellId: CellId }) => {
1353
- const { cellId } = action;
1354
- return {
1355
- ...state,
1356
- cellIds: state.cellIds.transformWithCellId(cellId, (column) =>
1357
- column.expand(cellId),
1358
- ),
1359
- scrollKey: cellId,
1360
- };
1361
- },
1362
- collapseAllCells: (state) => {
1363
- return {
1364
- ...state,
1365
- cellIds: state.cellIds.transformAll((column) => {
1366
- // Get all the top-level outlines
1367
- const outlines = column.topLevelIds.map((id) => {
1368
- const cell = state.cellRuntime[id];
1369
- return cell.outline;
1370
- });
1371
-
1372
- // Find the start/end of the collapsed ranges
1373
- const nodes = [...column.nodes];
1374
- const visibleEndByOriginalEnd = new Map<CellIndex, CellIndex>();
1375
- const reversedCollapseRanges = [];
1376
-
1377
- // Iterate in reverse order (bottom-up) to process children first
1378
- let i = nodes.length - 1;
1379
- while (i >= 0) {
1380
- const range = findCollapseRange(i, outlines);
1381
- if (range) {
1382
- const startIndex = i;
1383
- const originalEnd = range[1];
1384
- const endIndex =
1385
- visibleEndByOriginalEnd.get(originalEnd) ?? originalEnd;
1386
-
1387
- // After this section collapses, its heading represents this
1388
- // endpoint for any enclosing section that shares it.
1389
- visibleEndByOriginalEnd.set(originalEnd, startIndex);
1390
-
1391
- // Add the range to the list of ranges
1392
- const cellId = column.atOrThrow(startIndex);
1393
- const until = column.atOrThrow(endIndex);
1394
- reversedCollapseRanges.push({ id: cellId, until });
1395
- } else {
1396
- reversedCollapseRanges.push(null);
1397
- }
1398
- i--;
1399
- }
1400
-
1401
- const collapseRanges = reversedCollapseRanges.toReversed();
1402
- return column.collapseAll(collapseRanges);
1403
- }),
1404
- };
1405
- },
1406
- expandAllCells: (state) => {
1407
- return {
1408
- ...state,
1409
- cellIds: state.cellIds.transformAll((column) => column.expandAll()),
1410
- };
1411
- },
1412
- showCellIfHidden: (state, action: { cellId: CellId }) => {
1413
- const { cellId } = action;
1414
- const column = state.cellIds.findWithId(cellId);
1415
- const prev = column;
1416
- const result = column.findAndExpandDeep(cellId);
1417
-
1418
- if (result.equals(prev)) {
1419
- return state;
1420
- }
1421
-
1422
- return {
1423
- ...state,
1424
- cellIds: state.cellIds.transformWithCellId(cellId, () => result),
1425
- };
1426
- },
1427
- splitCell: (state, action: { cellId: CellId }) => {
1428
- const { cellId } = action;
1429
- const column = state.cellIds.findWithId(cellId);
1430
- const index = column.indexOfOrThrow(cellId);
1431
- const cell = state.cellData[cellId];
1432
- const cellHandle = state.cellHandles[cellId].current;
1433
-
1434
- if (cellHandle?.editorView == null) {
1435
- return state;
1436
- }
1437
-
1438
- const { beforeCursorCode, afterCursorCode } = splitEditor(
1439
- cellHandle.editorView,
1440
- );
1441
-
1442
- updateEditorCodeFromPython(cellHandle.editorView, beforeCursorCode);
1443
-
1444
- const newCellId = CellId.create();
1445
-
1446
- return {
1447
- ...state,
1448
- cellIds: state.cellIds.insertId(newCellId, column.id, index + 1),
1449
- cellData: {
1450
- ...state.cellData,
1451
- [cellId]: {
1452
- ...cell,
1453
- code: beforeCursorCode,
1454
- edited:
1455
- Boolean(beforeCursorCode) &&
1456
- beforeCursorCode.trim() !== cell.lastCodeRun?.trim(),
1457
- },
1458
- [newCellId]: createCell({
1459
- id: newCellId,
1460
- code: afterCursorCode,
1461
- edited: Boolean(afterCursorCode),
1462
- }),
1463
- },
1464
- cellRuntime: {
1465
- ...state.cellRuntime,
1466
- [cellId]: {
1467
- ...state.cellRuntime[cellId],
1468
- output: null,
1469
- consoleOutputs: [],
1470
- },
1471
- [newCellId]: createCellRuntimeState(),
1472
- },
1473
- cellHandles: {
1474
- ...state.cellHandles,
1475
- [newCellId]: createRef(),
1476
- },
1477
- scrollKey: newCellId,
1478
- };
1479
- },
1480
- undoSplitCell: (state, action: { cellId: CellId; snapshot: string }) => {
1481
- const { cellId, snapshot } = action;
1482
-
1483
- const cell = state.cellData[cellId];
1484
- const cellHandle = state.cellHandles[cellId].current;
1485
-
1486
- if (cellHandle?.editorView == null) {
1487
- return state;
1488
- }
1489
-
1490
- updateEditorCodeFromPython(cellHandle.editorView, snapshot);
1491
-
1492
- return {
1493
- ...state,
1494
- cellIds: state.cellIds.transformWithCellId(cellId, (column) => {
1495
- const newCellIndex = column.indexOfOrThrow(cellId) + 1;
1496
- return column.deleteAtIndex(newCellIndex);
1497
- }),
1498
- cellData: {
1499
- ...state.cellData,
1500
- [cellId]: {
1501
- ...cell,
1502
- code: snapshot,
1503
- edited:
1504
- Boolean(snapshot) && snapshot?.trim() !== cell.lastCodeRun?.trim(),
1505
- },
1506
- },
1507
- cellRuntime: {
1508
- ...state.cellRuntime,
1509
- [cellId]: {
1510
- ...state.cellRuntime[cellId],
1511
- output: null,
1512
- consoleOutputs: [],
1513
- },
1514
- },
1515
- cellHandles: {
1516
- ...state.cellHandles,
1517
- },
1518
- };
1519
- },
1520
366
  clearCellOutput: (state, action: { cellId: CellId }) => {
1521
367
  const { cellId } = action;
1522
368
  return updateCellRuntimeState({
@@ -1545,7 +391,7 @@ const {
1545
391
  },
1546
392
  clearAllCellOutputs: (state) => {
1547
393
  const newCellRuntime = { ...state.cellRuntime };
1548
- for (const cellId of state.cellIds.inOrderIds) {
394
+ for (const cellId of state.cellIds) {
1549
395
  newCellRuntime[cellId] = {
1550
396
  ...newCellRuntime[cellId],
1551
397
  output: null,
@@ -1557,58 +403,8 @@ const {
1557
403
  cellRuntime: newCellRuntime,
1558
404
  };
1559
405
  },
1560
- addSetupCellIfDoesntExist: (state, action: { code?: string }) => {
1561
- const { code } = action;
1562
-
1563
- if (state.cellIds.setupCellExists()) {
1564
- // Just focus on the existing setup cell
1565
- return {
1566
- ...state,
1567
- scrollKey: SETUP_CELL_ID,
1568
- };
1569
- }
1570
-
1571
- return {
1572
- ...state,
1573
- cellIds: state.cellIds.insertId(
1574
- SETUP_CELL_ID,
1575
- state.cellIds.atOrThrow(0).id,
1576
- 0,
1577
- ),
1578
- cellData: {
1579
- ...state.cellData,
1580
- [SETUP_CELL_ID]: createCell({
1581
- id: SETUP_CELL_ID,
1582
- name: SETUP_CELL_ID,
1583
- code,
1584
- edited: Boolean(code),
1585
- }),
1586
- },
1587
- cellRuntime: {
1588
- ...state.cellRuntime,
1589
- [SETUP_CELL_ID]: createCellRuntimeState(),
1590
- },
1591
- cellHandles: {
1592
- ...state.cellHandles,
1593
- [SETUP_CELL_ID]: createRef(),
1594
- },
1595
- scrollKey: SETUP_CELL_ID,
1596
- };
1597
- },
1598
406
  });
1599
407
 
1600
- // We apply the middleware here (rather than inline in createReducerAndAtoms)
1601
- // so that the document transaction middleware can import CellActions and
1602
- // strictly type the dispatched actions without creating a circular dependency.
1603
- addMiddleware(documentTransactionMiddleware);
1604
-
1605
- function isCellCodeHidden(state: NotebookState, cellId: CellId): boolean {
1606
- return (
1607
- Boolean(state.cellData[cellId].config.hide_code) &&
1608
- !state.untouchedNewCells.has(cellId)
1609
- );
1610
- }
1611
-
1612
408
  // Helper function to update a cell in the array
1613
409
  function updateCellRuntimeState({
1614
410
  state,
@@ -1656,43 +452,12 @@ function updateCellData({
1656
452
  };
1657
453
  }
1658
454
 
455
+ /** Each cell's config in order. The document's `column` configs are canonical. */
1659
456
  export function getCellConfigs(state: NotebookState): CellConfig[] {
1660
- const cells = state.cellData;
1661
-
1662
- // We set to null by default to prevent inconsistencies between undefined & null
1663
- const defaultCellConfig: Partial<CellConfig> = { column: null };
1664
-
1665
- // Handle the case where there's only one column
1666
- // We don't want to set the column config
1667
- const hasMultipleColumns = state.cellIds.getColumns().length > 1;
1668
- if (!hasMultipleColumns) {
1669
- return state.cellIds.getColumns().flatMap((column) => {
1670
- return column.inOrderIds.map((cellId) => {
1671
- return {
1672
- ...cells[cellId].config,
1673
- ...defaultCellConfig,
1674
- };
1675
- });
1676
- });
1677
- }
1678
-
1679
- return state.cellIds.getColumns().flatMap((column, columnIndex) => {
1680
- return column.inOrderIds.map((cellId, cellIndex) => {
1681
- const config: Partial<CellConfig> = { ...defaultCellConfig };
1682
-
1683
- // Only set the column index for the first cell in the column
1684
- if (cellIndex === 0) {
1685
- config.column = columnIndex;
1686
- }
1687
-
1688
- const newConfig = {
1689
- ...cells[cellId].config,
1690
- ...config,
1691
- };
1692
-
1693
- return newConfig;
1694
- });
1695
- });
457
+ return state.cellIds.map((cellId) => ({
458
+ ...state.cellData[cellId].config,
459
+ column: state.cellData[cellId].config.column ?? null,
460
+ }));
1696
461
  }
1697
462
 
1698
463
  export {
@@ -1712,9 +477,7 @@ export const addLogs = imperativeActions.addLogs;
1712
477
 
1713
478
  export const cellIdsAtom = atom((get) => get(notebookAtom).cellIds);
1714
479
 
1715
- export const hasOnlyOneCellAtom = atom((get) =>
1716
- get(cellIdsAtom).hasOnlyOneId(),
1717
- );
480
+ export const hasOnlyOneCellAtom = atom((get) => get(cellIdsAtom).length === 1);
1718
481
 
1719
482
  export const hasDisabledCellsAtom = atom(
1720
483
  (get) => disabledCellIds(get(notebookAtom)).length > 0,
@@ -1728,11 +491,13 @@ export const canUndoDeletesAtom = atom((get) =>
1728
491
 
1729
492
  export const undoLabelAtom = atom((get) => getUndoLabel(get(notebookAtom)));
1730
493
 
1731
- export const needsRunAtom = atom((get) => notebookNeedsRun(get(notebookAtom)));
494
+ export const needsRunAtom = atom((get) =>
495
+ notebookNeedsRun(get(notebookAtom), get(cellExecutionInputsAtom)),
496
+ );
1732
497
 
1733
498
  export const cellErrorsAtom = atom((get) => {
1734
499
  const { cellIds, cellRuntime, cellData } = get(notebookAtom);
1735
- const errors = cellIds.inOrderIds
500
+ const errors = cellIds
1736
501
  .map((cellId) => {
1737
502
  const cell = cellRuntime[cellId];
1738
503
  const { name } = cellData[cellId];
@@ -1760,11 +525,28 @@ export const cellErrorsAtom = atom((get) => {
1760
525
  return errors;
1761
526
  });
1762
527
 
528
+ /**
529
+ * The outline of each cell whose output has a heading. Compared by value, so
530
+ * readers are left alone by the runtime messages that change no heading.
531
+ */
532
+ export const cellOutlinesAtom = createDeepEqualAtom(
533
+ atom((get) => {
534
+ const outlines: Partial<Record<CellId, Outline>> = {};
535
+ for (const [id, runtime] of Objects.entries(
536
+ get(notebookAtom).cellRuntime,
537
+ )) {
538
+ if (runtime.outline !== null && runtime.outline.items.length > 0) {
539
+ outlines[id] = runtime.outline;
540
+ }
541
+ }
542
+ return outlines;
543
+ }),
544
+ );
545
+
1763
546
  export const notebookOutline = atom((get) => {
1764
- const { cellIds, cellRuntime } = get(notebookAtom);
1765
- const outlines = cellIds.inOrderIds.map(
1766
- (cellId) => cellRuntime[cellId].outline,
1767
- );
547
+ const cellIds = get(cellIdsAtom);
548
+ const cellRuntime = get(cellsRuntimeAtom);
549
+ const outlines = cellIds.map((cellId) => cellRuntime[cellId].outline);
1768
550
  return mergeOutlines(outlines);
1769
551
  });
1770
552
 
@@ -1774,7 +556,7 @@ export const cellIdToNamesMap = createDeepEqualAtom(
1774
556
  atom((get) => {
1775
557
  const { cellIds, cellData } = get(notebookAtom);
1776
558
  const names: Record<CellId, string | undefined> = Objects.fromEntries(
1777
- cellIds.inOrderIds.map((cellId) => [cellId, cellData[cellId]?.name]),
559
+ cellIds.map((cellId) => [cellId, cellData[cellId]?.name]),
1778
560
  );
1779
561
  return names;
1780
562
  }),
@@ -1831,41 +613,19 @@ export const getCells = () => store.get(notebookAtom).cellIds;
1831
613
  */
1832
614
  export const getCellNames = () => {
1833
615
  const { cellIds, cellData } = store.get(notebookAtom);
1834
- return cellIds.inOrderIds.map((id) => cellData[id]?.name).filter(Boolean);
616
+ return cellIds.map((id) => cellData[id]?.name).filter(Boolean);
1835
617
  };
1836
618
 
1837
619
  const cellDataAtoms = splitAtom(
1838
620
  selectAtom(notebookAtom, (cells) =>
1839
- cells.cellIds.inOrderIds.map((id) => cells.cellData[id]),
621
+ cells.cellIds.map((id) => cells.cellData[id]),
1840
622
  ),
1841
623
  );
1842
624
  export const useCellDataAtoms = () => useAtom(cellDataAtoms);
1843
625
 
1844
626
  export const cellsRuntimeAtom = atom((get) => get(notebookAtom).cellRuntime);
1845
627
 
1846
- export const notebookIsRunningAtom = atom((get) =>
1847
- notebookIsRunning(get(notebookAtom)),
1848
- );
1849
- export const onlyScratchpadIsRunningAtom = atom((get) => {
1850
- const { cellRuntime } = get(notebookAtom);
1851
- return Object.entries(cellRuntime).every(
1852
- ([id, rt]) => rt.status !== "running" || id === SCRATCH_CELL_ID,
1853
- );
1854
- });
1855
- export const notebookQueuedOrRunningCountAtom = atom((get) =>
1856
- notebookQueueOrRunningCount(get(notebookAtom)),
1857
- );
1858
-
1859
- export const numColumnsAtom = atom(
1860
- (get) => get(notebookAtom).cellIds.colLength,
1861
- );
1862
- export const hasCellsAtom = atom(
1863
- (get) => get(notebookAtom).cellIds.idLength > 0,
1864
- );
1865
- export const columnIdsAtom = atom((get) =>
1866
- get(notebookAtom).cellIds.getColumnIds(),
1867
- );
1868
-
628
+ export const hasCellsAtom = atom((get) => get(notebookAtom).cellIds.length > 0);
1869
629
  export const cellDataAtom = atomFamily((cellId: CellId) =>
1870
630
  atom((get) => get(notebookAtom).cellData[cellId]),
1871
631
  );
@@ -1889,6 +649,7 @@ export function releaseCellAtoms(cellId: CellId) {
1889
649
  cellDataAtom.remove(cellId);
1890
650
  cellRuntimeAtom.remove(cellId);
1891
651
  cellHandleAtom.remove(cellId);
652
+ withdrawCells(store, [cellId]);
1892
653
  }
1893
654
 
1894
655
  /** Subscribes to reactive updates of the cell's data. */
@@ -1906,7 +667,7 @@ export const useCellHandle = (cellId: CellId) =>
1906
667
  */
1907
668
  export const getAllEditorViews = () => {
1908
669
  const { cellIds, cellHandles } = store.get(notebookAtom);
1909
- return cellIds.inOrderIds
670
+ return cellIds
1910
671
  .map((cellId) => cellHandles[cellId]?.current?.editorViewOrNull)
1911
672
  .filter(Boolean);
1912
673
  };
@@ -1930,16 +691,10 @@ export function flattenTopLevelNotebookCells(
1930
691
  state: NotebookState,
1931
692
  ): (CellData & CellRuntimeState)[] {
1932
693
  const { cellIds, cellData, cellRuntime } = state;
1933
- return cellIds.getColumns().flatMap((column) =>
1934
- column.topLevelIds.map((cellId) => ({
1935
- ...cellData[cellId],
1936
- ...cellRuntime[cellId],
1937
- })),
1938
- );
1939
- }
1940
-
1941
- export function createUntouchedCellAtom(cellId: CellId): Atom<boolean> {
1942
- return atom((get) => get(notebookAtom).untouchedNewCells.has(cellId));
694
+ return cellIds.map((cellId) => ({
695
+ ...cellData[cellId],
696
+ ...cellRuntime[cellId],
697
+ }));
1943
698
  }
1944
699
 
1945
700
  export function createTracebackInfoAtom(
@@ -1955,7 +710,8 @@ export function createTracebackInfoAtom(
1955
710
  return undefined;
1956
711
  }
1957
712
 
1958
- if (data.status === "queued" || data.status === "running") {
713
+ const { status } = cellExecutionView(get(cellExecutionInputsAtom), cellId);
714
+ if (status === "queued" || status === "running") {
1959
715
  return undefined;
1960
716
  }
1961
717
 
@@ -2001,20 +757,13 @@ export function createTracebackInfoAtom(
2001
757
  });
2002
758
  }
2003
759
 
2004
- /**
2005
- * Use this hook to dispatch cell actions. This hook will not cause a re-render
2006
- * when cells change.
2007
- */
2008
- export function useCellActions(
2009
- options: { skipMiddleware?: boolean } = {},
2010
- ): CellActions {
2011
- return useActions(options);
760
+ /** The reducer's actions. Components use `useCellActions` from `./actions`. */
761
+ export function useReducerActions(): ReducerCellActions {
762
+ return useActions();
2012
763
  }
2013
764
 
2014
- /**
2015
- * Map of cell actions
2016
- */
2017
- export type CellActions = ReturnType<typeof createActions>;
765
+ /** The actions the reducer still handles. */
766
+ export type ReducerCellActions = ReturnType<typeof createActions>;
2018
767
 
2019
768
  /**
2020
769
  * This is exported for testing purposes only.
@@ -2023,7 +772,6 @@ export const exportedForTesting = {
2023
772
  reducer,
2024
773
  createActions,
2025
774
  initialNotebookState,
2026
- isCellCodeHidden,
2027
775
  // Export atom families for testing cleanup
2028
776
  cellDataAtom,
2029
777
  cellRuntimeAtom,