@marimo-team/islands 0.25.3-dev28 → 0.25.3-dev284

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 (841) hide show
  1. package/dist/{ConnectedDataExplorerComponent-7EaP9VWb.js → ConnectedDataExplorerComponent-CErm132k.js} +116 -113
  2. package/dist/{ErrorBoundary-CUdSBv06.js → ErrorBoundary-DlAypzMw.js} +76 -82
  3. package/dist/_baseSet-BfI5FjBM.js +328 -0
  4. package/dist/{abnfDiagram-VCTEODGH-z_OSynK8.js → abnfDiagram-VCTEODGH-d-Qn2fwb.js} +2 -2
  5. package/dist/any-language-editor-6k6F12Z3.js +75 -0
  6. package/dist/{architectureDiagram-5GKGNRK7-Cs8J8BZr.js → architectureDiagram-5GKGNRK7-DxragHYv.js} +1 -1
  7. package/dist/{arrays-CN1djC4k.js → arrays-DoQ-AILx.js} +15 -27
  8. package/dist/assertNever-B-YzWzbL.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-DZuNnIZO.js} +56 -48
  13. package/dist/{capabilities-CQ5IjhzL.js → capabilities-D9FskwsK.js} +2 -2
  14. package/dist/{chat-ui-BzjAsTFN.js → chat-ui-ByGe2rP1.js} +2100 -1810
  15. package/dist/{check-BMKBBrdc.js → check-XFzZeH-y.js} +1 -1
  16. package/dist/{chunk-IMKFNOWR-aoSFasqF.js → chunk-IMKFNOWR-C0x0feFg.js} +1 -1
  17. package/dist/{chunk-SHT3W25Y-CMaybO3O.js → chunk-SHT3W25Y-D0ZdASqB.js} +1 -1
  18. package/dist/{chunk-SVP7TREG-BNBrTAw1.js → chunk-SVP7TREG-BHjL96zx.js} +1 -1
  19. package/dist/{chunk-TICWLB2K-DXNxvUJn.js → chunk-TICWLB2K-CzcrwGfp.js} +1 -1
  20. package/dist/{classDiagram-ZZMXUADV-DXdCCDOg.js → classDiagram-ZZMXUADV-BTqFAZTP.js} +2 -2
  21. package/dist/{classDiagram-v2-VYDZK3BY-DyZqrtLP.js → classDiagram-v2-VYDZK3BY-EPXb0Elt.js} +2 -2
  22. package/dist/{code-visibility-BuxqA3v5.js → code-visibility-kxOpbw86.js} +43 -50
  23. package/dist/{common-xtZHxsM2.js → common-58GnHt6Y.js} +10605 -10045
  24. package/dist/config-C8MFHQ2E.js +637 -0
  25. package/dist/{copy-icon-BgUO8jsi.js → copy-icon-CtllOdmg.js} +3 -3
  26. package/dist/createReducer-DRqwmaTc.js +67 -0
  27. package/dist/{cynefinDiagram-5FMLGOSQ-DWHuKJs8.js → cynefinDiagram-5FMLGOSQ-CBSD344q.js} +1 -1
  28. package/dist/{dagre-GXQ25YYZ-DYLu-OCU.js → dagre-GXQ25YYZ-JItjh3Ge.js} +3 -3
  29. package/dist/{dagre-BGSfXNEb.js → dagre-mqiMAsFY.js} +531 -680
  30. package/dist/{data-grid-overlay-editor-qnbf5J94.js → data-grid-overlay-editor-CeO8C9Le.js} +1 -1
  31. package/dist/{debounce-C3jyATpc.js → debounce-DrhhziJv.js} +2 -2
  32. package/dist/{diagram-S7CK7UJ4-PwwcUOHP.js → diagram-S7CK7UJ4-CTD9xTVb.js} +1 -1
  33. package/dist/{diagram-UQ7AKVKN-CvJbL_IM.js → diagram-UQ7AKVKN-DTfnFai8.js} +1 -1
  34. package/dist/{diagram-VX7I27RA-B5RkqnzN.js → diagram-VX7I27RA-BcSnucV-.js} +1 -1
  35. package/dist/{diagram-Z3DM3KII-CyijwTR2.js → diagram-Z3DM3KII-C2ATIE8A.js} +1 -1
  36. package/dist/{dialog-Cc479ZEH.js → dialog-CaYm5imE.js} +32 -32
  37. package/dist/{dist-Bvs24R_E.js → dist-11T5wq3_.js} +31 -30
  38. package/dist/{dist-F8Ep36rk.js → dist-17ugwGZc.js} +59 -58
  39. package/dist/{dist-BMQf5zIS.js → dist-7cRjf--y.js} +123 -122
  40. package/dist/{dist-diIx5qJW.js → dist-B8pkfkzc.js} +31 -30
  41. package/dist/{dist-CN_BeG2I.js → dist-B9UoM_wq.js} +58 -57
  42. package/dist/{dist-CLdpGQVr.js → dist-BGuMTs1i.js} +118 -117
  43. package/dist/{dist-BK4Oj99o.js → dist-BHW11fni.js} +18 -17
  44. package/dist/dist-BLY4m1Wi.js +850 -0
  45. package/dist/{dist-PXYI_yuP.js → dist-BNlcVoxs.js} +24 -23
  46. package/dist/{dist-D6d86eNJ.js → dist-BQYe5Bju.js} +47 -46
  47. package/dist/{dist-CkTTIdBe.js → dist-BX1mn8na.js} +337 -337
  48. package/dist/{dist-DiDibOYr.js → dist-Bh0PTKUW.js} +151 -150
  49. package/dist/{dist-CSJ0MDRw.js → dist-CKaXK92p.js} +56 -55
  50. package/dist/{dist-C8fAKSH1.js → dist-CLhwfhpJ.js} +53 -52
  51. package/dist/{dist-DoE4XJBC.js → dist-CYeqK4Xw.js} +9801 -10649
  52. package/dist/{dist-BLfKmR-l.js → dist-Cxlp3WH6.js} +16 -15
  53. package/dist/{dist-D-wwqlzJ.js → dist-DSJPGj42.js} +158 -157
  54. package/dist/{dist-BorMD-uX.js → dist-DU9ICsWZ.js} +75 -74
  55. package/dist/{dist-DBtSl3sf.js → dist-DWv0ALBi.js} +47 -46
  56. package/dist/{dist-OR1OnN-y.js → dist-DiBpWEsB.js} +31 -30
  57. package/dist/{dist-CrSP_H1M.js → dist-DiLFTlnl.js} +242 -242
  58. package/dist/{dist-CN8zI-XC.js → dist-X9CsotJl.js} +239 -239
  59. package/dist/{dist-CSbh1PNP.js → dist-cVDV5IuI.js} +23 -22
  60. package/dist/{dist-BuaRHxOc.js → dist-fRfaOoq8.js} +44 -43
  61. package/dist/{dist-Cg7gzw-a.js → dist-gTTI-9Px.js} +78 -77
  62. package/dist/{dropdown-menu-BuE2b8Yb.js → dropdown-menu-Bb0bFOIk.js} +2904 -1493
  63. package/dist/{ebnfDiagram-PWID7BFC-DG90ELFp.js → ebnfDiagram-PWID7BFC-B7B9JPxc.js} +2 -2
  64. package/dist/{erDiagram-RLTQ6QDP-FMo_SpyX.js → erDiagram-RLTQ6QDP-v6hDno1O.js} +1 -1
  65. package/dist/{error-banner-CFPHFw4h.js → error-banner-CHCdHVFB.js} +87 -87
  66. package/dist/error-entries-9U4BFHjf.js +2586 -0
  67. package/dist/{esm-AMFssoNp.js → esm-BDqqT86j.js} +329 -329
  68. package/dist/{esm-D2gTsXEV.js → esm-BgKjMvWw.js} +530 -529
  69. package/dist/feedback-button-CU5tLok7.js +374 -0
  70. package/dist/{flowDiagram-HODETNUW-DQ4JuadE.js → flowDiagram-HODETNUW-aIFlAtOy.js} +2 -2
  71. package/dist/{formats-Bv_5N2Ff.js → formats-PzBQMUnr.js} +6 -6
  72. package/dist/{glide-data-editor-DQ_cEi05.js → glide-data-editor-D-EO3BiP.js} +6630 -2846
  73. package/dist/{glide-utils-pisbYVjH.js → glide-utils-BSQr4d90.js} +107 -76
  74. package/dist/{globals-D7vITxEX.js → globals-BscgCh7D.js} +3 -3
  75. package/dist/{graphlib-BmY_CCV9.js → graphlib-nlwT3Wvy.js} +177 -220
  76. package/dist/hasIn-C20JsQ2m.js +28 -0
  77. package/dist/{error-entries-j85LP88u.js → html-to-image-BYh_MorV.js} +26294 -26451
  78. package/dist/{infoDiagram-27XIBGKW-CRSDtRrd.js → infoDiagram-27XIBGKW-D6YHLeCP.js} +1 -1
  79. package/dist/isArrayLikeObject-B-HXCsex.js +626 -0
  80. package/dist/isEmpty-CdX7MTE1.js +14 -0
  81. package/dist/{ishikawaDiagram-5VMMS53U-DxLEUldB.js → ishikawaDiagram-5VMMS53U-D1ZvprOX.js} +1 -1
  82. package/dist/{kanban-definition-UXKFOSKX-BLhvu8o4.js → kanban-definition-UXKFOSKX-Bna_3BMr.js} +1 -1
  83. package/dist/{loader-BPdQhmlq.js → loader-DZvtzQoD.js} +3 -3
  84. package/dist/main.js +10276 -8171
  85. package/dist/{mermaid-Bjm6C1qN.js → mermaid-A36unKXJ.js} +62 -62
  86. package/dist/{mindmap-definition-YA3MSWOX-g2LwxnYQ.js → mindmap-definition-YA3MSWOX-CyVCs__G.js} +1 -1
  87. package/dist/{now-jM8-M7IR.js → now-BKZCLE5P.js} +12 -13
  88. package/dist/{number-overlay-editor-DJHB-CGK.js → number-overlay-editor-Dtmaj1ET.js} +1 -1
  89. package/dist/{objects-CzUgoMIF.js → objects-DCmMvZtn.js} +1 -3
  90. package/dist/{once-BtE-wKRL.js → once-NnpX_HnX.js} +1 -1
  91. package/dist/{pegDiagram-XKGWAZYB-CpMAt3Qs.js → pegDiagram-XKGWAZYB-9hNBD2wZ.js} +2 -2
  92. package/dist/pencil-DYUlSKxZ.js +10 -0
  93. package/dist/pick-tCszMUbm.js +35 -0
  94. package/dist/{pieDiagram-E7YTZNPT-CVTthoZo.js → pieDiagram-E7YTZNPT-8tr-NYo7.js} +1 -1
  95. package/dist/{process-output-DGzGHArA.js → process-output-bS3MGJ3f.js} +4 -4
  96. package/dist/{railroadDiagram-O6MQD6OU-ao8cqqFB.js → railroadDiagram-O6MQD6OU-Dzg1dyyp.js} +2 -2
  97. package/dist/{react-vega-BDpbvBCb.js → react-vega-C4T9Rb_w.js} +1 -1
  98. package/dist/{requirementDiagram-BXWQKSXE--J8vyfm-.js → requirementDiagram-BXWQKSXE-B7sNqwGG.js} +1 -1
  99. package/dist/{reveal-component-BxYUpAPg.js → reveal-component-DDr66wjo.js} +1151 -1128
  100. package/dist/select-DsoP1qe8.js +1200 -0
  101. package/dist/{sequenceDiagram-WJ2MYXX4-CiQbn62x.js → sequenceDiagram-WJ2MYXX4-oP9shbaG.js} +1 -1
  102. package/dist/{spec-D_u2ipzF.js → spec-BZMDxAu7.js} +41 -11
  103. package/dist/{stateDiagram-D77RDMKH-DVEeGs5b.js → stateDiagram-D77RDMKH-CjCv4ogv.js} +4 -4
  104. package/dist/{stateDiagram-v2-MP3YSRHH-1OQRe6iA.js → stateDiagram-v2-MP3YSRHH-BW9X162a.js} +2 -2
  105. package/dist/static-state-D-Q-Ajaa.js +72 -0
  106. package/dist/style.css +1 -1
  107. package/dist/{swimlanes-42K2YHIH-CqkGoGqn.js → swimlanes-42K2YHIH-Ckfs9Wrn.js} +1 -1
  108. package/dist/{swimlanesDiagram-VR7AAH4N-CszjkrKo.js → swimlanesDiagram-VR7AAH4N-DpTNG9YP.js} +2 -2
  109. package/dist/{swiper-component-DKntyn8p.js → swiper-component-DQbK_ZWm.js} +7 -7
  110. package/dist/{timeline-definition-24CTP7MA-DLRUNVAx.js → timeline-definition-24CTP7MA-BXaBlV6U.js} +1 -1
  111. package/dist/toast-BhjIy3Tq.js +491 -0
  112. package/dist/{tooltip-CtBZ5q8p.js → tooltip-DMMiOn2f.js} +321 -297
  113. package/dist/use-restore-focus-CosX7-gx.js +7568 -0
  114. package/dist/{useAsyncData-BneN349z.js → useAsyncData-Ci4AvJff.js} +18 -17
  115. package/dist/{useDateFormatter-DkajGwwd.js → useDateFormatter-CPlava52.js} +2 -2
  116. package/dist/{useDeepCompareMemoize-Z_7R8wTt.js → useDeepCompareMemoize-NaaWoFlX.js} +1 -1
  117. package/dist/useEvent-BRH-aCqi.js +19 -0
  118. package/dist/{useIframeCapabilities-DBETvvPl.js → useIframeCapabilities-BXhDDoCG.js} +1 -1
  119. package/dist/useLifecycle-SniivYmC.js +112 -0
  120. package/dist/{vega-component-DPd52jvr.js → vega-component-DIxaDQNs.js} +161 -160
  121. package/dist/{vega-embed-container-BgKnsDbB.js → vega-embed-container--LYZqguO.js} +2 -2
  122. package/dist/{vega-loader.browser-CpbZAXub.js → vega-loader.browser-B9Pz0EGP.js} +1 -1
  123. package/dist/{vennDiagram-4TSXK5OY-CypgoBVZ.js → vennDiagram-4TSXK5OY-C-hQ4iNJ.js} +1 -1
  124. package/dist/{wardleyDiagram-VM6X3IG4-B6xjiQ_g.js → wardleyDiagram-VM6X3IG4-C2MQ_Umv.js} +1 -1
  125. package/dist/{xychartDiagram-S5SC5T6Z-e0PefDVs.js → xychartDiagram-S5SC5T6Z-BtTTBn7Q.js} +1 -1
  126. package/package.json +16 -20
  127. package/src/__mocks__/document.tsx +63 -0
  128. package/src/__mocks__/executions.ts +197 -0
  129. package/src/__mocks__/notebook.ts +3 -7
  130. package/src/__mocks__/partial.ts +26 -0
  131. package/src/__mocks__/requests.ts +1 -4
  132. package/src/__tests__/CellStatus.test.tsx +25 -26
  133. package/src/__tests__/fixtures/pair-config.ts +1 -1
  134. package/src/__tests__/main.test.tsx +24 -1
  135. package/src/__tests__/module-entry.test.ts +44 -0
  136. package/src/__tests__/mount.test.ts +1 -21
  137. package/src/__tests__/{host-imports.test.ts → replica-imports.test.ts} +12 -11
  138. package/src/components/ai/__tests__/ai-model-dropdown.test.tsx +63 -0
  139. package/src/components/ai/ai-model-dropdown.tsx +11 -6
  140. package/src/components/app-config/__tests__/state.test.tsx +50 -0
  141. package/src/components/app-config/ai-config.tsx +156 -150
  142. package/src/components/app-config/app-config-button.tsx +6 -5
  143. package/src/components/app-config/app-config-form.tsx +5 -9
  144. package/src/components/app-config/common.tsx +7 -9
  145. package/src/components/app-config/data-form.tsx +112 -113
  146. package/src/components/app-config/mcp-config.tsx +30 -53
  147. package/src/components/app-config/user-config-form.css +91 -0
  148. package/src/components/app-config/user-config-form.tsx +266 -349
  149. package/src/components/chat/__tests__/capabilities-popover.test.tsx +0 -1
  150. package/src/components/chat/__tests__/chat-display.test.tsx +196 -0
  151. package/src/components/chat/__tests__/chat-markdown.test.tsx +43 -0
  152. package/src/components/chat/__tests__/chat-utils.test.ts +93 -1
  153. package/src/components/chat/__tests__/message-queue.test.tsx +17 -0
  154. package/src/components/chat/__tests__/notebook-access-selector.test.tsx +64 -0
  155. package/src/components/chat/__tests__/turn-timing.test.ts +84 -0
  156. package/src/components/chat/__tests__/use-assistant-turn.test.ts +95 -0
  157. package/src/components/chat/__tests__/user-message.test.tsx +27 -0
  158. package/src/components/chat/acp/__tests__/agent-panel-initialization.test.tsx +1 -1
  159. package/src/components/chat/acp/__tests__/agent-panel.test.tsx +22 -17
  160. package/src/components/chat/acp/__tests__/session-tabs.test.tsx +43 -0
  161. package/src/components/chat/acp/__tests__/state.test.ts +2 -2
  162. package/src/components/chat/acp/agent-panel.tsx +6 -3
  163. package/src/components/chat/acp/blocks.tsx +13 -12
  164. package/src/components/chat/acp/common.tsx +4 -5
  165. package/src/components/chat/acp/model-selector.tsx +1 -1
  166. package/src/components/chat/acp/session-tabs.tsx +8 -3
  167. package/src/components/chat/agent-activity.tsx +132 -0
  168. package/src/components/chat/capabilities-popover.tsx +2 -11
  169. package/src/components/chat/chat-components.tsx +9 -4
  170. package/src/components/chat/chat-display.tsx +131 -57
  171. package/src/components/chat/chat-history-popover.tsx +6 -1
  172. package/src/components/chat/chat-markdown.tsx +15 -0
  173. package/src/components/chat/chat-panel.tsx +216 -294
  174. package/src/components/chat/chat-typography.css +108 -0
  175. package/src/components/chat/chat-utils.ts +24 -35
  176. package/src/components/chat/notebook-access-selector.stories.tsx +22 -0
  177. package/src/components/chat/notebook-access-selector.tsx +100 -0
  178. package/src/components/chat/tool-call/__tests__/tool-labels.test.ts +76 -0
  179. package/src/components/chat/tool-call/__tests__/tool-step.test.tsx +57 -0
  180. package/src/components/chat/tool-call/shared.ts +6 -0
  181. package/src/components/chat/tool-call/tool-call-view.tsx +22 -76
  182. package/src/components/chat/tool-call/tool-labels.ts +172 -0
  183. package/src/components/chat/tool-call/tool-result.tsx +1 -1
  184. package/src/components/chat/tool-call/tool-step.tsx +145 -0
  185. package/src/components/chat/turn-timing.ts +73 -0
  186. package/src/components/chat/use-assistant-turn.ts +65 -0
  187. package/src/components/chat/use-chat-generation.ts +20 -0
  188. package/src/components/chat/user-message.tsx +145 -0
  189. package/src/components/data-table/TableBottomBar.tsx +4 -1
  190. package/src/components/data-table/TableTopBar.tsx +2 -0
  191. package/src/components/data-table/__tests__/chart-spec-model.test.ts +195 -195
  192. package/src/components/data-table/__tests__/column-explorer.test.tsx +27 -1
  193. package/src/components/data-table/__tests__/columns.test.tsx +105 -0
  194. package/src/components/data-table/__tests__/context-menu-interaction.test.tsx +130 -0
  195. package/src/components/data-table/__tests__/data-table.test.tsx +1 -0
  196. package/src/components/data-table/__tests__/date-popover.test.tsx +72 -0
  197. package/src/components/data-table/__tests__/export-actions.test.tsx +105 -0
  198. package/src/components/data-table/__tests__/keyboard-selection.test.tsx +188 -0
  199. package/src/components/data-table/__tests__/pagination.test.tsx +28 -1
  200. package/src/components/data-table/__tests__/search-highlight.test.tsx +109 -0
  201. package/src/components/data-table/__tests__/url-detector.test.tsx +74 -0
  202. package/src/components/data-table/cell-selection/feature.ts +2 -2
  203. package/src/components/data-table/cell-selection/types.ts +2 -2
  204. package/src/components/data-table/charts/__tests__/chart-panel.test.tsx +74 -1
  205. package/src/components/data-table/charts/__tests__/spec-snapshot.test.ts +20 -20
  206. package/src/components/data-table/charts/__tests__/spec.test.ts +78 -52
  207. package/src/components/data-table/charts/__tests__/storage.test.ts +4 -1
  208. package/src/components/data-table/charts/chart-spec/spec.ts +68 -43
  209. package/src/components/data-table/charts/charts.tsx +40 -30
  210. package/src/components/data-table/charts/storage.ts +16 -5
  211. package/src/components/data-table/column-explorer-panel/column-explorer.tsx +1 -1
  212. package/src/components/data-table/column-formatting/types.ts +1 -2
  213. package/src/components/data-table/column-summary/chart-spec-model.tsx +23 -16
  214. package/src/components/data-table/column-summary/column-summary.tsx +2 -4
  215. package/src/components/data-table/column-visibility-dropdown.tsx +8 -13
  216. package/src/components/data-table/columns.tsx +45 -46
  217. package/src/components/data-table/context-menu.tsx +36 -20
  218. package/src/components/data-table/data-table.tsx +17 -1
  219. package/src/components/data-table/date-popover.tsx +6 -4
  220. package/src/components/data-table/export-actions.tsx +76 -48
  221. package/src/components/data-table/pagination.tsx +44 -33
  222. package/src/components/data-table/range-focus/atoms.ts +4 -6
  223. package/src/components/data-table/range-focus/use-cell-range-selection.ts +65 -38
  224. package/src/components/data-table/range-focus/use-scroll-into-view.ts +21 -2
  225. package/src/components/data-table/renderers.tsx +16 -7
  226. package/src/components/data-table/row-viewer-panel/__tests__/row-viewer.test.tsx +24 -1
  227. package/src/components/data-table/row-viewer-panel/row-viewer.tsx +4 -4
  228. package/src/components/data-table/search-highlight.tsx +79 -0
  229. package/src/components/data-table/types.ts +1 -0
  230. package/src/components/data-table/url-detector.tsx +49 -9
  231. package/src/components/datasources/__tests__/column-actions.test.tsx +97 -0
  232. package/src/components/datasources/__tests__/column-preview.test.tsx +96 -5
  233. package/src/components/datasources/__tests__/datasources-interactions.test.tsx +181 -0
  234. package/src/components/datasources/__tests__/table-list.test.tsx +118 -0
  235. package/src/components/datasources/__tests__/utils.test.ts +57 -6
  236. package/src/components/datasources/column-preview.tsx +17 -12
  237. package/src/components/datasources/datasources.tsx +56 -43
  238. package/src/components/datasources/utils.ts +32 -23
  239. package/src/components/debugger/debugger-code.tsx +5 -0
  240. package/src/components/dependency-graph/custom-node.tsx +1 -1
  241. package/src/components/dependency-graph/dependency-graph-tree.tsx +8 -8
  242. package/src/components/dependency-graph/elements.ts +24 -21
  243. package/src/components/dependency-graph/minimap-content.tsx +15 -20
  244. package/src/components/dependency-graph/panels.tsx +4 -2
  245. package/src/components/dependency-graph/utils/__tests__/layout.test.ts +18 -11
  246. package/src/components/editor/SortableCell.tsx +1 -3
  247. package/src/components/editor/__tests__/Output.test.tsx +50 -32
  248. package/src/{core/attachments/__tests__/toast.test.tsx → components/editor/__tests__/agent-toast.test.tsx} +1 -1
  249. package/src/components/editor/__tests__/data-attributes.test.tsx +28 -23
  250. package/src/components/editor/__tests__/dynamic-favicon.test.tsx +111 -18
  251. package/src/components/editor/__tests__/output-persistence.test.tsx +28 -9
  252. package/src/components/editor/__tests__/package-alert.test.tsx +22 -1
  253. package/src/components/editor/actions/__tests__/name-cell-input.test.tsx +69 -0
  254. package/src/components/editor/actions/__tests__/pair-with-agent-commands.test.ts +21 -40
  255. package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +41 -14
  256. package/src/components/editor/actions/__tests__/pair-with-agent.test.tsx +6 -24
  257. package/src/components/editor/actions/__tests__/useCellActionButton.test.tsx +131 -0
  258. package/src/components/editor/actions/name-cell-input.tsx +4 -3
  259. package/src/components/editor/actions/pair-with-agent-commands.ts +10 -7
  260. package/src/components/editor/actions/pair-with-agent-modal.tsx +29 -30
  261. package/src/components/editor/actions/pair-with-agent.tsx +16 -39
  262. package/src/components/editor/actions/useCellActionButton.tsx +47 -35
  263. package/src/components/editor/actions/useConfigActions.tsx +3 -4
  264. package/src/components/editor/actions/useNotebookActions.tsx +25 -29
  265. package/src/components/editor/actions/useRestartKernel.tsx +1 -1
  266. package/src/components/editor/actions/useSetCodeVisibility.ts +11 -12
  267. package/src/components/editor/actions/useSetOutputsExpanded.ts +14 -19
  268. package/src/components/editor/ai/ai-completion-editor.tsx +2 -1
  269. package/src/components/editor/ai/completion-handlers.tsx +0 -37
  270. package/src/components/editor/ai/prompt-input.tsx +189 -0
  271. package/src/components/editor/alerts/__tests__/connection-notice.test.tsx +1 -1
  272. package/src/components/editor/alerts/connection-notice.tsx +1 -1
  273. package/src/components/editor/alerts/startup-logs-alert.tsx +1 -0
  274. package/src/components/editor/alerts/stdin-blocking-alert.tsx +4 -2
  275. package/src/components/editor/app-container.tsx +1 -1
  276. package/src/components/editor/boundary/__tests__/ErrorBoundary.test.tsx +1 -1
  277. package/src/components/editor/cell/CellStatus.tsx +28 -23
  278. package/src/components/editor/cell/CreateCellButton.tsx +15 -21
  279. package/src/components/editor/cell/DeleteButton.tsx +3 -2
  280. package/src/components/editor/cell/RunButton.tsx +18 -8
  281. package/src/components/editor/cell/StagedAICell.tsx +52 -137
  282. package/src/components/editor/cell/StopButton.tsx +2 -2
  283. package/src/components/editor/cell/__tests__/useRunCells.test.tsx +169 -0
  284. package/src/components/editor/cell/__tests__/useShouldShowInterrupt.test.ts +39 -0
  285. package/src/components/editor/cell/cell-actions.tsx +12 -1
  286. package/src/components/editor/cell/cell-context-menu.tsx +4 -1
  287. package/src/components/editor/cell/code/__tests__/language-toggle.test.tsx +54 -0
  288. package/src/components/editor/cell/code/cell-editor.tsx +65 -169
  289. package/src/components/editor/cell/code/language-toggle.tsx +8 -7
  290. package/src/components/editor/cell/collapse.tsx +16 -6
  291. package/src/components/editor/cell/useAddCell.ts +4 -8
  292. package/src/components/editor/cell/useDeleteCell.tsx +10 -36
  293. package/src/components/editor/cell/useRunCells.ts +49 -8
  294. package/src/components/editor/cell/useShouldShowInterrupt.ts +2 -12
  295. package/src/components/editor/cell/useSplitCell.tsx +10 -6
  296. package/src/components/editor/chrome/components/__tests__/feedback-button.test.tsx +1 -1
  297. package/src/components/editor/chrome/components/feedback-button.tsx +2 -8
  298. package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +31 -2
  299. package/src/components/editor/chrome/panels/__tests__/runtime-details.test.tsx +1 -1
  300. package/src/components/editor/chrome/panels/__tests__/sandbox-recovery.test.tsx +1 -1
  301. package/src/components/editor/chrome/panels/cache-panel.tsx +1 -0
  302. package/src/components/editor/chrome/panels/dependency-graph-panel.tsx +1 -1
  303. package/src/components/editor/chrome/panels/logs-panel.tsx +2 -1
  304. package/src/components/editor/chrome/panels/outline/__tests__/outline-keyboard.test.tsx +84 -0
  305. package/src/components/editor/chrome/panels/outline/floating-outline.tsx +39 -14
  306. package/src/components/editor/chrome/panels/outline/useActiveOutline.tsx +3 -2
  307. package/src/components/editor/chrome/panels/packages-panel.tsx +4 -7
  308. package/src/components/editor/chrome/panels/panel-header.tsx +13 -0
  309. package/src/components/editor/chrome/panels/secrets-panel.tsx +1 -0
  310. package/src/components/editor/chrome/panels/session-panel.tsx +1 -1
  311. package/src/components/editor/chrome/panels/snippets-panel.tsx +10 -12
  312. package/src/components/editor/chrome/panels/variable-panel.tsx +1 -5
  313. package/src/components/editor/chrome/types.ts +1 -1
  314. package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +7 -9
  315. package/src/components/editor/chrome/wrapper/__tests__/document-sync-status.test.tsx +92 -0
  316. package/src/components/editor/chrome/wrapper/__tests__/pending-ai-cells.test.tsx +265 -37
  317. package/src/components/editor/chrome/wrapper/__tests__/useAskAiAboutCell.test.tsx +58 -0
  318. package/src/components/editor/chrome/wrapper/app-chrome.tsx +91 -74
  319. package/src/components/editor/chrome/wrapper/footer-items/attachment-status.tsx +4 -4
  320. package/src/components/editor/chrome/wrapper/footer-items/backend-status.tsx +1 -1
  321. package/src/components/editor/chrome/wrapper/footer-items/document-sync-status.tsx +54 -0
  322. package/src/components/editor/chrome/wrapper/footer-items/lsp-status.tsx +1 -1
  323. package/src/components/editor/chrome/wrapper/footer-items/machine-stats.tsx +1 -1
  324. package/src/components/editor/chrome/wrapper/footer-items/runtime-settings.tsx +1 -7
  325. package/src/components/editor/chrome/wrapper/footer.tsx +2 -2
  326. package/src/components/editor/chrome/wrapper/minimap-state.ts +1 -1
  327. package/src/components/editor/chrome/wrapper/pending-ai-cells.tsx +143 -72
  328. package/src/components/editor/chrome/wrapper/pending-ai-changes-badge.tsx +25 -0
  329. package/src/components/editor/chrome/wrapper/sidebar.tsx +8 -4
  330. package/src/components/editor/chrome/wrapper/storage.ts +1 -1
  331. package/src/components/editor/chrome/wrapper/useAskAiAboutCell.ts +36 -0
  332. package/src/components/editor/columns/__tests__/cell-column.test.tsx +1 -1
  333. package/src/components/editor/columns/cell-column.tsx +1 -1
  334. package/src/components/editor/columns/sortable-column.tsx +2 -2
  335. package/src/components/editor/connections/components.tsx +5 -10
  336. package/src/components/editor/controls/Controls.tsx +4 -4
  337. package/src/components/editor/controls/__tests__/command-palette.test.tsx +53 -0
  338. package/src/components/editor/controls/__tests__/keyboard-shortcuts.test.tsx +61 -0
  339. package/src/components/editor/controls/__tests__/notebook-menu-dropdown.test.tsx +10 -4
  340. package/src/components/editor/controls/command-palette.tsx +22 -10
  341. package/src/components/editor/controls/keyboard-shortcuts.tsx +14 -5
  342. package/src/components/editor/documentation.css +34 -11
  343. package/src/components/editor/dynamic-favicon.tsx +21 -40
  344. package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +26 -22
  345. package/src/components/editor/errors/auto-fix.tsx +4 -8
  346. package/src/components/editor/errors/send-error-report-button.tsx +3 -6
  347. package/src/components/editor/file-tree/__tests__/file-explorer-interactions.test.tsx +6 -0
  348. package/src/components/editor/file-tree/__tests__/file-explorer-navigation.test.tsx +47 -0
  349. package/src/components/editor/file-tree/__tests__/use-file-search.test.tsx +91 -0
  350. package/src/components/editor/file-tree/file-explorer.tsx +48 -15
  351. package/src/components/editor/file-tree/file-name-input.tsx +1 -0
  352. package/src/components/editor/file-tree/file-viewer.tsx +4 -2
  353. package/src/components/editor/file-tree/use-file-search.ts +35 -21
  354. package/src/components/editor/header/__tests__/filename-form.test.tsx +1 -4
  355. package/src/components/editor/header/__tests__/status.test.tsx +5 -3
  356. package/src/components/editor/header/app-header.tsx +1 -1
  357. package/src/components/editor/header/filename-form.tsx +1 -1
  358. package/src/components/editor/header/status.tsx +44 -22
  359. package/src/components/editor/links/__tests__/cell-link.test.tsx +148 -0
  360. package/src/components/editor/links/cell-link-list.tsx +1 -1
  361. package/src/components/editor/links/cell-link.tsx +21 -11
  362. package/src/components/editor/navigation/__tests__/clipboard.test.ts +60 -38
  363. package/src/components/editor/navigation/__tests__/navigation.test.ts +108 -85
  364. package/src/components/editor/navigation/__tests__/selection.test.ts +43 -21
  365. package/src/components/editor/navigation/clipboard.ts +17 -14
  366. package/src/components/editor/navigation/focus-utils.ts +23 -4
  367. package/src/components/editor/navigation/multi-cell-action-toolbar.tsx +17 -33
  368. package/src/components/editor/navigation/navigation.ts +74 -72
  369. package/src/components/editor/navigation/selection.ts +30 -29
  370. package/src/components/editor/notebook-banner.tsx +1 -0
  371. package/src/components/editor/notebook-cell.tsx +126 -152
  372. package/src/components/editor/output/MarimoErrorOutput.tsx +2 -1
  373. package/src/components/editor/output/MarimoTracebackOutput.tsx +19 -17
  374. package/src/components/editor/output/Outputs.css +8 -0
  375. package/src/components/editor/output/VideoOutput.tsx +6 -0
  376. package/src/components/editor/output/__tests__/VideoOutput.test.tsx +131 -0
  377. package/src/components/editor/output/__tests__/traceback.test.tsx +27 -1
  378. package/src/components/editor/output/ansi-reduce.ts +33 -20
  379. package/src/components/editor/output/console/ConsoleOutput.tsx +19 -25
  380. package/src/components/editor/output/console/__tests__/ConsoleOutput.test.tsx +125 -69
  381. package/src/components/editor/package-alert.tsx +14 -7
  382. package/src/components/editor/renderers/__tests__/add-cell-buttons.test.tsx +18 -0
  383. package/src/components/editor/renderers/cell-array.tsx +32 -100
  384. package/src/components/editor/renderers/grid-layout/__tests__/grid-layout.test.tsx +75 -0
  385. package/src/components/editor/renderers/grid-layout/grid-layout.tsx +28 -22
  386. package/src/components/editor/renderers/slides-layout/__tests__/plugin.test.ts +0 -4
  387. package/src/components/editor/renderers/slides-layout/slides-layout.tsx +1 -1
  388. package/src/components/editor/renderers/vertical-layout/__tests__/useFocusFirstEditor.test.ts +4 -4
  389. package/src/components/editor/renderers/vertical-layout/sidebar/toggle.tsx +7 -1
  390. package/src/components/editor/renderers/vertical-layout/useFocusFirstEditor.ts +1 -1
  391. package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +19 -37
  392. package/src/components/editor/stdin-blocking-alert.tsx +4 -2
  393. package/src/components/home/components.tsx +17 -31
  394. package/src/components/icons/__tests__/large-spinner.test.tsx +39 -0
  395. package/src/components/icons/large-spinner.tsx +3 -14
  396. package/src/components/icons/spinner.tsx +1 -1
  397. package/src/components/markdown/markdown-renderer.tsx +39 -28
  398. package/src/components/mcp/mcp-status-indicator.tsx +18 -14
  399. package/src/components/pages/__tests__/home-page.test.tsx +116 -0
  400. package/src/components/pages/edit-page.tsx +19 -17
  401. package/src/components/pages/home-page.tsx +164 -91
  402. package/src/components/scratchpad/scratchpad.test.tsx +89 -0
  403. package/src/components/scratchpad/scratchpad.tsx +47 -33
  404. package/src/components/scratchpad/state.ts +7 -0
  405. package/src/components/slides/__tests__/minimap-actions.test.tsx +22 -29
  406. package/src/components/slides/__tests__/minimap.test.ts +38 -19
  407. package/src/components/slides/__tests__/slide-scroll-hint.test.tsx +37 -0
  408. package/src/components/slides/minimap.tsx +22 -14
  409. package/src/components/slides/reveal-component.tsx +73 -36
  410. package/src/components/slides/reveal-slides.css +5 -4
  411. package/src/components/slides/slide-cell-view.tsx +37 -34
  412. package/src/components/slides/slide-form.tsx +1 -0
  413. package/src/components/slides/slide-scroll-hint.tsx +3 -1
  414. package/src/components/slides/slide.tsx +3 -4
  415. package/src/components/sort/SortableCellsProvider.tsx +8 -25
  416. package/src/components/tracing/tracing-keyboard.test.tsx +85 -0
  417. package/src/components/tracing/tracing.tsx +46 -42
  418. package/src/components/ui/__tests__/draggable-popover.test.tsx +24 -0
  419. package/src/components/ui/__tests__/fullscreen.test.tsx +44 -2
  420. package/src/components/ui/__tests__/range-slider.test.tsx +165 -0
  421. package/src/components/ui/alert-dialog.tsx +1 -1
  422. package/src/components/ui/draggable-popover.tsx +24 -2
  423. package/src/components/ui/fullscreen.tsx +25 -1
  424. package/src/components/ui/range-slider.tsx +22 -9
  425. package/src/components/ui/toast.tsx +1 -1
  426. package/src/core/MarimoApp.tsx +3 -1
  427. package/src/core/ai/__tests__/staged-cells.test.ts +89 -608
  428. package/src/core/ai/context/context.ts +2 -0
  429. package/src/core/ai/context/providers/__tests__/cell-output.test.ts +8 -12
  430. package/src/core/ai/context/providers/__tests__/cell.test.ts +49 -0
  431. package/src/core/ai/context/providers/cell-output.ts +2 -2
  432. package/src/core/ai/context/providers/cell.ts +54 -0
  433. package/src/core/ai/context/providers/datasource.ts +6 -2
  434. package/src/core/ai/staged-cells.ts +56 -323
  435. package/src/core/ai/state.ts +3 -0
  436. package/src/core/ai/tools/__tests__/edit-notebook-tool.test.ts +40 -88
  437. package/src/core/ai/tools/__tests__/registry.test.ts +17 -0
  438. package/src/core/ai/tools/base.ts +18 -15
  439. package/src/core/ai/tools/edit-notebook-tool.ts +86 -80
  440. package/src/core/ai/tools/registry.ts +13 -10
  441. package/src/core/ai/tools/sample-tool.ts +6 -7
  442. package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +4 -527
  443. package/src/core/cells/__tests__/actions.test.ts +623 -35
  444. package/src/core/cells/__tests__/add-missing-import.test.ts +7 -8
  445. package/src/core/cells/__tests__/arrange.test.ts +432 -0
  446. package/src/core/cells/__tests__/cell.test.ts +148 -150
  447. package/src/core/cells/__tests__/cells.test.ts +390 -2925
  448. package/src/core/cells/__tests__/collapseConsoleOutputs.test.ts +54 -0
  449. package/src/core/cells/__tests__/document.test.ts +158 -0
  450. package/src/core/cells/__tests__/editor.test.ts +57 -0
  451. package/src/core/cells/__tests__/flush-document.test.ts +40 -0
  452. package/src/core/cells/__tests__/harness.ts +106 -0
  453. package/src/core/cells/__tests__/layout.test.ts +176 -0
  454. package/src/core/cells/__tests__/logs.test.ts +0 -13
  455. package/src/core/cells/__tests__/notebook-outline.test.ts +122 -0
  456. package/src/core/cells/__tests__/outputs.test.tsx +71 -53
  457. package/src/core/cells/__tests__/pending-delete-service.test.tsx +11 -20
  458. package/src/core/cells/__tests__/session.test.ts +445 -211
  459. package/src/core/cells/__tests__/utils.test.ts +135 -337
  460. package/src/core/cells/actions.ts +677 -6
  461. package/src/core/cells/add-missing-import.ts +47 -43
  462. package/src/core/cells/arrange.ts +529 -0
  463. package/src/core/cells/cell.ts +78 -146
  464. package/src/core/cells/cells.ts +116 -1457
  465. package/src/core/cells/columns.ts +38 -0
  466. package/src/core/cells/document.ts +167 -0
  467. package/src/core/cells/edit.ts +30 -0
  468. package/src/core/cells/editor.ts +82 -0
  469. package/src/core/cells/focus.ts +27 -6
  470. package/src/core/cells/layout.ts +221 -0
  471. package/src/core/cells/outputs.ts +12 -28
  472. package/src/core/cells/pending-delete-service.ts +8 -14
  473. package/src/core/cells/scrollCellIntoView.ts +6 -0
  474. package/src/core/cells/session.ts +76 -53
  475. package/src/core/cells/types.ts +21 -51
  476. package/src/core/cells/utils.ts +85 -87
  477. package/src/core/codemirror/__tests__/format.test.ts +0 -34
  478. package/src/core/codemirror/__tests__/setup.test.ts +61 -29
  479. package/src/core/codemirror/ai/__tests__/resources.test.ts +117 -0
  480. package/src/core/codemirror/ai/resources.ts +1 -1
  481. package/src/core/codemirror/cells/__tests__/extensions.test.ts +2 -6
  482. package/src/core/codemirror/cells/extensions.ts +18 -30
  483. package/src/core/codemirror/cells/state.ts +2 -6
  484. package/src/core/codemirror/cm.ts +5 -1
  485. package/src/core/codemirror/config/types.ts +1 -1
  486. package/src/core/codemirror/copilot/__tests__/getCodes.test.ts +6 -7
  487. package/src/core/codemirror/copilot/getCodes.ts +2 -2
  488. package/src/core/codemirror/format.ts +6 -23
  489. package/src/core/codemirror/keymaps/vim.ts +4 -8
  490. package/src/core/codemirror/language/__tests__/sql.test.ts +22 -20
  491. package/src/core/codemirror/language/__tests__/utils.test.ts +187 -15
  492. package/src/core/codemirror/language/cell-code.ts +114 -0
  493. package/src/core/codemirror/language/extension.ts +52 -35
  494. package/src/core/codemirror/language/languages/markdown.ts +8 -6
  495. package/src/core/codemirror/language/languages/python.ts +19 -13
  496. package/src/core/codemirror/language/languages/sql/sql.ts +16 -24
  497. package/src/core/codemirror/language/panel/panel.tsx +1 -0
  498. package/src/core/codemirror/language/types.ts +7 -7
  499. package/src/core/codemirror/language/utils.ts +9 -39
  500. package/src/core/codemirror/lsp/__tests__/notebook-lsp.test.ts +1 -2
  501. package/src/core/codemirror/lsp/notebook-lsp.ts +6 -5
  502. package/src/core/codemirror/lsp/utils.ts +1 -1
  503. package/src/core/codemirror/misc/__tests__/paste.test.ts +3 -1
  504. package/src/core/codemirror/misc/paste.ts +1 -1
  505. package/src/core/codemirror/placeholder/__tests__/extensions.test.ts +75 -0
  506. package/src/core/codemirror/placeholder/extensions.ts +80 -19
  507. package/src/core/codemirror/readonly/__tests__/extension.test.ts +1 -1
  508. package/src/core/codemirror/readonly/extension.ts +2 -2
  509. package/src/core/codemirror/utils.ts +1 -0
  510. package/src/core/config/__tests__/app-config.test.ts +64 -0
  511. package/src/core/config/__tests__/config-schema.test.ts +56 -6
  512. package/src/core/config/config-schema.ts +17 -4
  513. package/src/core/config/config.ts +65 -20
  514. package/src/core/config/feature-flag.tsx +0 -2
  515. package/src/core/datasets/__tests__/data-source-discovery.test.ts +5 -5
  516. package/src/core/datasets/data-source-discovery.ts +4 -6
  517. package/src/core/documentation/doc-lookup.ts +1 -1
  518. package/src/core/edit-app.tsx +22 -28
  519. package/src/core/errors/__tests__/error-entries.test.ts +40 -1
  520. package/src/core/errors/error-entries.ts +6 -4
  521. package/src/core/executions/__tests__/atoms.test.ts +98 -0
  522. package/src/core/executions/__tests__/freshness.test.ts +482 -0
  523. package/src/core/executions/__tests__/hooks.test.tsx +106 -0
  524. package/src/core/executions/__tests__/select.test.ts +268 -0
  525. package/src/core/executions/atoms.ts +228 -0
  526. package/src/core/executions/freshness.ts +171 -0
  527. package/src/core/executions/hooks.ts +47 -0
  528. package/src/core/executions/index.ts +33 -0
  529. package/src/core/executions/select.ts +175 -0
  530. package/src/core/export/__tests__/hooks.test.ts +0 -5
  531. package/src/core/export/hooks.ts +1 -1
  532. package/src/core/export/layout.ts +2 -2
  533. package/src/core/hotkeys/actions.ts +2 -4
  534. package/src/core/hotkeys/hotkeys.ts +2 -1
  535. package/src/core/islands/__tests__/bootstrap.test.ts +69 -17
  536. package/src/core/islands/__tests__/bridge.test.ts +85 -27
  537. package/src/core/islands/bootstrap.ts +49 -54
  538. package/src/core/islands/bridge.ts +84 -25
  539. package/src/core/islands/components/__tests__/IslandControls.test.tsx +7 -5
  540. package/src/core/islands/components/__tests__/web-components.test.tsx +63 -0
  541. package/src/core/islands/components/output-wrapper.tsx +5 -5
  542. package/src/core/islands/components/web-components.tsx +12 -1
  543. package/src/core/islands/worker/__tests__/controller.test.ts +62 -4
  544. package/src/core/islands/worker/controller.ts +4 -10
  545. package/src/core/islands/worker/worker.tsx +33 -33
  546. package/src/core/kernel/__tests__/RuntimeState.test.ts +1 -1
  547. package/src/core/kernel/__tests__/handlers.test.ts +7 -157
  548. package/src/core/kernel/handlers.ts +13 -79
  549. package/src/core/kernel/state.ts +0 -15
  550. package/src/core/lifecycle/render-policy.ts +12 -26
  551. package/src/core/meta/__tests__/code-visibility.test.tsx +78 -70
  552. package/src/core/meta/code-visibility.ts +18 -9
  553. package/src/core/network/__tests__/connection.test.ts +1 -1
  554. package/src/core/network/__tests__/requests-lazy.test.ts +6 -8
  555. package/src/core/network/connection-notice.ts +1 -1
  556. package/src/core/network/connection.ts +1 -1
  557. package/src/core/network/requests-lazy.ts +5 -8
  558. package/src/core/network/requests-network.ts +5 -31
  559. package/src/core/network/requests-static.ts +1 -4
  560. package/src/core/network/requests-toasting.tsx +1 -4
  561. package/src/core/network/types.ts +6 -14
  562. package/src/core/network/useConnectionNotice.ts +8 -2
  563. package/src/core/notebook/__tests__/operations.test.ts +51 -0
  564. package/src/core/notebook/attachments.ts +38 -0
  565. package/src/core/notebook/document/__tests__/codemirror.test.ts +759 -0
  566. package/src/core/notebook/document/__tests__/draft.test.ts +363 -0
  567. package/src/core/notebook/document/__tests__/fixtures.ts +92 -0
  568. package/src/core/notebook/document/__tests__/hooks.test.tsx +391 -0
  569. package/src/core/notebook/document/__tests__/lifecycle-scenarios.test.ts +61 -0
  570. package/src/core/notebook/document/__tests__/model.test.ts +247 -0
  571. package/src/core/notebook/document/__tests__/protocol-fixtures.ts +38 -0
  572. package/src/core/notebook/document/__tests__/recovery-scenarios.test.ts +230 -0
  573. package/src/core/notebook/document/__tests__/scenario-fixture.ts +43 -0
  574. package/src/core/notebook/document/__tests__/scenarios.test.ts +205 -0
  575. package/src/core/notebook/document/__tests__/state.test.ts +459 -0
  576. package/src/core/notebook/document/__tests__/submission.test.ts +203 -0
  577. package/src/core/notebook/document/__tests__/test-host.test.ts +92 -0
  578. package/src/core/notebook/document/__tests__/test-host.ts +247 -0
  579. package/src/core/notebook/document/__tests__/test-notebook.ts +149 -0
  580. package/src/core/notebook/document/__tests__/user.ts +61 -0
  581. package/src/core/notebook/document/codemirror.ts +216 -0
  582. package/src/core/notebook/document/draft.ts +502 -0
  583. package/src/core/notebook/document/hooks.tsx +118 -0
  584. package/src/core/notebook/document/index.ts +24 -0
  585. package/src/core/notebook/document/model.ts +102 -0
  586. package/src/core/notebook/document/select.ts +41 -0
  587. package/src/core/notebook/document/state.ts +378 -0
  588. package/src/core/notebook/document/sync.ts +369 -0
  589. package/src/core/notebook/index.ts +19 -0
  590. package/src/core/notebook/operations.ts +30 -0
  591. package/src/core/notebook/replica/AGENTS.md +11 -0
  592. package/src/core/{host → notebook/replica}/__tests__/attachments.test.ts +9 -10
  593. package/src/core/{host → notebook/replica}/__tests__/document.test.ts +18 -16
  594. package/src/core/notebook/replica/__tests__/executions.test.ts +200 -0
  595. package/src/core/notebook/replica/__tests__/fixtures.ts +79 -0
  596. package/src/core/notebook/replica/__tests__/replica.test.ts +559 -0
  597. package/src/core/notebook/replica/__tests__/results.test.ts +193 -0
  598. package/src/core/notebook/replica/__tests__/runtime.test.ts +78 -0
  599. package/src/core/notebook/replica/backend.ts +19 -0
  600. package/src/core/notebook/replica/connection.ts +42 -0
  601. package/src/core/notebook/replica/index.ts +20 -0
  602. package/src/core/notebook/replica/replica.ts +336 -0
  603. package/src/core/{host → notebook/replica}/resources/attachments.ts +6 -3
  604. package/src/core/{host → notebook/replica}/resources/document.ts +3 -3
  605. package/src/core/notebook/replica/resources/executions.ts +157 -0
  606. package/src/core/notebook/replica/resources/results.ts +48 -0
  607. package/src/core/notebook/replica/resources/runtime.ts +14 -0
  608. package/src/core/packages/__tests__/useSandboxController.test.tsx +1 -1
  609. package/src/core/packages/useSandboxController.ts +1 -1
  610. package/src/core/run-app.tsx +49 -51
  611. package/src/core/runtime/__tests__/adapter.test.ts +4 -4
  612. package/src/core/runtime/__tests__/createWsUrl.test.ts +77 -7
  613. package/src/core/runtime/__tests__/runtime.test.ts +32 -74
  614. package/src/core/runtime/adapter.ts +5 -5
  615. package/src/core/runtime/config.ts +2 -2
  616. package/src/core/runtime/runtime.ts +33 -46
  617. package/src/core/runtime/types.ts +0 -2
  618. package/src/core/saving/__tests__/document.test.tsx +123 -0
  619. package/src/core/saving/__tests__/state.test.ts +111 -0
  620. package/src/core/saving/__tests__/useAutoSave.test.tsx +67 -0
  621. package/src/core/saving/document.ts +33 -0
  622. package/src/core/saving/filename.ts +1 -1
  623. package/src/core/saving/save-component.tsx +39 -60
  624. package/src/core/saving/state.ts +38 -39
  625. package/src/core/saving/useAutoSave.ts +17 -53
  626. package/src/core/session/__tests__/backend.test.ts +236 -0
  627. package/src/core/session/__tests__/useNotebookReplica.hook.test.tsx +186 -0
  628. package/src/core/{websocket → session}/__tests__/useNotebookSession.hook.test.tsx +245 -262
  629. package/src/core/session/active-notebook.ts +28 -0
  630. package/src/core/session/backend.ts +123 -0
  631. package/src/core/session/embedded.ts +7 -0
  632. package/src/core/{websocket → session}/types.ts +2 -2
  633. package/src/core/session/useNotebookReplica.ts +74 -0
  634. package/src/core/{websocket → session}/useNotebookSession.tsx +80 -67
  635. package/src/core/state/__tests__/jotai.test.tsx +108 -0
  636. package/src/core/state/jotai.ts +1 -1
  637. package/src/core/static/__tests__/files.test.ts +38 -10
  638. package/src/core/static/files.ts +2 -0
  639. package/src/core/{websocket → transports}/__tests__/connection-state.test.ts +1 -13
  640. package/src/core/transports/__tests__/server.test.ts +408 -0
  641. package/src/core/transports/__tests__/static.test.ts +306 -0
  642. package/src/core/transports/__tests__/wasm.test.ts +166 -0
  643. package/src/core/{websocket → transports}/connection-state.ts +9 -16
  644. package/src/core/transports/server.ts +152 -0
  645. package/src/core/transports/static.ts +166 -0
  646. package/src/core/transports/wasm.ts +97 -0
  647. package/src/core/vscode/__tests__/vscode-bindings.test.ts +125 -0
  648. package/src/core/vscode/vscode-bindings.ts +14 -2
  649. package/src/core/wasm/__tests__/bridge.test.ts +210 -64
  650. package/src/core/wasm/__tests__/worker.test.ts +188 -0
  651. package/src/core/wasm/bridge.ts +120 -73
  652. package/src/core/wasm/rpc.ts +15 -3
  653. package/src/core/wasm/store.ts +3 -3
  654. package/src/core/wasm/worker/bootstrap.ts +55 -51
  655. package/src/core/wasm/worker/getMarimoWheel.ts +1 -1
  656. package/src/core/wasm/worker/save-worker.ts +9 -10
  657. package/src/core/wasm/worker/types.ts +30 -18
  658. package/src/core/wasm/worker/worker.ts +118 -72
  659. package/src/css/app/Cell.css +7 -36
  660. package/src/css/app/codemirror.css +1 -1
  661. package/src/css/common.css +1 -1
  662. package/src/css/globals.css +72 -24
  663. package/src/hooks/__tests__/useAsyncData.test.tsx +148 -0
  664. package/src/hooks/__tests__/useDebounce.test.tsx +51 -0
  665. package/src/hooks/__tests__/useDeepCompareMemoize.test.tsx +39 -0
  666. package/src/hooks/__tests__/useDelayElapsed.test.tsx +28 -0
  667. package/src/hooks/__tests__/useInternalStateWithSync.test.tsx +20 -0
  668. package/src/hooks/__tests__/useScript.test.tsx +77 -0
  669. package/src/hooks/__tests__/useStateForKey.test.tsx +257 -0
  670. package/src/hooks/useAsyncData.ts +3 -3
  671. package/src/hooks/useDataSourceDiscovery.ts +6 -4
  672. package/src/hooks/useDebounce.ts +6 -10
  673. package/src/hooks/useDeepCompareMemoize.ts +1 -1
  674. package/src/hooks/useDelayElapsed.ts +14 -10
  675. package/src/hooks/useElapsedTime.ts +3 -3
  676. package/src/hooks/useEventListener.ts +1 -2
  677. package/src/hooks/useInputHistory.ts +4 -7
  678. package/src/hooks/useInternalStateWithSync.ts +6 -6
  679. package/src/hooks/useResizeObserver.ts +4 -7
  680. package/src/hooks/useScript.ts +67 -70
  681. package/src/hooks/useStateForKey.ts +93 -0
  682. package/src/mount.tsx +20 -15
  683. package/src/plugins/__tests__/plugin-object-schemas.test.ts +104 -0
  684. package/src/plugins/core/RenderHTML.tsx +3 -5
  685. package/src/plugins/core/__test__/RenderHTML-copy.test.tsx +60 -0
  686. package/src/plugins/core/registerReactComponent.tsx +5 -14
  687. package/src/plugins/impl/CodeEditorPlugin.tsx +1 -1
  688. package/src/plugins/impl/DataEditorPlugin.tsx +34 -13
  689. package/src/plugins/impl/DataTablePlugin.tsx +69 -37
  690. package/src/plugins/impl/DropdownPlugin.tsx +2 -6
  691. package/src/plugins/impl/FileBrowserPlugin.tsx +4 -16
  692. package/src/plugins/impl/FormPlugin.tsx +1 -1
  693. package/src/plugins/impl/RangeSliderPlugin.tsx +16 -2
  694. package/src/plugins/impl/RefreshPlugin.tsx +1 -0
  695. package/src/plugins/impl/SliderPlugin.tsx +6 -8
  696. package/src/plugins/impl/__tests__/DataEditorPlugin.test.ts +93 -4
  697. package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +387 -1
  698. package/src/plugins/impl/__tests__/FileBrowserPlugin.test.tsx +45 -0
  699. package/src/plugins/impl/__tests__/RangeSliderPlugin.test.tsx +109 -0
  700. package/src/plugins/impl/__tests__/SliderPlugin.test.tsx +24 -3
  701. package/src/plugins/impl/anywidget/__tests__/registry.test.ts +153 -0
  702. package/src/plugins/impl/anywidget/__tests__/widget-binding.test.ts +241 -2
  703. package/src/plugins/impl/anywidget/model.ts +3 -1
  704. package/src/plugins/impl/anywidget/registry.ts +18 -14
  705. package/src/plugins/impl/anywidget/runtime.ts +21 -9
  706. package/src/plugins/impl/anywidget/widget-binding.ts +160 -82
  707. package/src/plugins/impl/chat/__tests__/chat-prompts.test.tsx +93 -0
  708. package/src/plugins/impl/chat/chat-ui.tsx +17 -15
  709. package/src/plugins/impl/code/any-language-editor.tsx +2 -2
  710. package/src/plugins/impl/common/error-banner.tsx +5 -3
  711. package/src/plugins/impl/data-editor/__tests__/column-types.test.ts +42 -0
  712. package/src/plugins/impl/data-editor/__tests__/editor-state.test.ts +52 -1
  713. package/src/plugins/impl/data-editor/__tests__/glide-data-editor.test.tsx +120 -4
  714. package/src/plugins/impl/data-editor/__tests__/glide-utils.test.ts +116 -0
  715. package/src/plugins/impl/data-editor/column-types.ts +55 -0
  716. package/src/plugins/impl/data-editor/editor-state.ts +18 -2
  717. package/src/plugins/impl/data-editor/glide-data-editor.tsx +74 -12
  718. package/src/plugins/impl/data-editor/glide-utils.ts +39 -38
  719. package/src/plugins/impl/data-editor/types.ts +5 -0
  720. package/src/plugins/impl/data-explorer/ConnectedDataExplorerComponent.tsx +1 -1
  721. package/src/plugins/impl/data-explorer/components/column-summary.tsx +4 -2
  722. package/src/plugins/impl/data-explorer/state/reducer.ts +1 -1
  723. package/src/plugins/impl/data-frames/DataFramePlugin.tsx +1 -2
  724. package/src/plugins/impl/data-frames/__tests__/sidebar.test.tsx +160 -0
  725. package/src/plugins/impl/data-frames/forms/__tests__/__snapshots__/form.test.tsx.snap +66 -66
  726. package/src/plugins/impl/data-frames/panel.tsx +44 -16
  727. package/src/plugins/impl/mpl-interactive/MplInteractivePlugin.tsx +5 -3
  728. package/src/plugins/impl/panel/PanelPlugin.tsx +16 -17
  729. package/src/plugins/impl/panel/__tests__/utils.test.ts +39 -1
  730. package/src/plugins/impl/panel/utils.ts +17 -0
  731. package/src/plugins/impl/plotly/PlotlyPlugin.tsx +2 -5
  732. package/src/plugins/impl/plotly/selection.ts +25 -19
  733. package/src/plugins/impl/vega/VegaPlugin.tsx +2 -3
  734. package/src/plugins/impl/vega/make-selectable.ts +4 -7
  735. package/src/plugins/layout/DownloadPlugin.tsx +7 -11
  736. package/src/plugins/layout/RoutesPlugin.tsx +1 -1
  737. package/src/plugins/layout/__test__/DownloadPlugin.test.tsx +205 -0
  738. package/src/stories/__tests__/editor-story.test.tsx +33 -0
  739. package/src/stories/button.stories.tsx +1 -0
  740. package/src/stories/cell-status.stories.tsx +29 -24
  741. package/src/stories/cell.stories.tsx +46 -46
  742. package/src/stories/data-table.stories.tsx +31 -0
  743. package/src/stories/editor.stories.tsx +24 -17
  744. package/src/stories/form-wrapper.stories.tsx +2 -2
  745. package/src/stories/layout/vertical/one-column.stories.tsx +28 -42
  746. package/src/stories/runtime-story.tsx +23 -17
  747. package/src/stories/select.stories.tsx +2 -2
  748. package/src/stories/switchable-multi-select.stories.tsx +1 -1
  749. package/src/theme/__tests__/islands-theme.test.tsx +276 -0
  750. package/src/theme/namespace.tsx +4 -3
  751. package/src/theme/useTheme.ts +71 -41
  752. package/src/utils/__tests__/arrays.test.ts +35 -0
  753. package/src/utils/__tests__/batch-requests.test.ts +3 -3
  754. package/src/utils/__tests__/{createReducer.test.ts → createReducer.test.tsx} +127 -1
  755. package/src/utils/__tests__/download.test.tsx +183 -23
  756. package/src/utils/__tests__/numbers.test.ts +105 -1
  757. package/src/utils/__tests__/objects.test.ts +21 -0
  758. package/src/utils/arrays.ts +11 -2
  759. package/src/utils/blob.ts +1 -1
  760. package/src/utils/createReducer.ts +115 -79
  761. package/src/utils/download.ts +67 -6
  762. package/src/utils/maps.ts +0 -3
  763. package/src/utils/numbers.ts +79 -18
  764. package/src/utils/objects.ts +4 -9
  765. package/src/utils/time.ts +10 -0
  766. package/src/utils/tracer.ts +9 -5
  767. package/src/utils/zod-utils.ts +1 -3
  768. package/dist/_baseMerge-BiGRwuqv.js +0 -165
  769. package/dist/any-language-editor-D4q-IFcM.js +0 -76
  770. package/dist/assets/__vite-browser-external-BXYkiFsk.js +0 -1
  771. package/dist/assets/worker-HPY9fbhz.js +0 -114
  772. package/dist/feedback-button-g9eH5l7Q.js +0 -379
  773. package/dist/hasIn-o2Us0uVx.js +0 -132
  774. package/dist/html-to-image-B5H-xKRH.js +0 -3251
  775. package/dist/isArrayLikeObject-BZl-15ir.js +0 -450
  776. package/dist/isEmpty-BSZ1LaUo.js +0 -35
  777. package/dist/isSymbol-kQcfYbNX.js +0 -8
  778. package/dist/label-DoysmLMW.js +0 -281
  779. package/dist/pick-CJeoxYOS.js +0 -51
  780. package/dist/select-DemoKT4G.js +0 -1344
  781. package/dist/toDate-BvdkR0aH.js +0 -1185
  782. package/dist/toast-PkdZraM8.js +0 -1830
  783. package/dist/use-restore-focus-CKjfFjLM.js +0 -6369
  784. package/dist/useLifecycle-Dfq8UD-b.js +0 -169
  785. package/dist/useTheme-Bu5bBgrh.js +0 -904
  786. package/src/components/chat/reasoning-accordion.tsx +0 -61
  787. package/src/components/chat/tool-call/tool-error-card.tsx +0 -81
  788. package/src/components/chat/tool-call/tool-history-row.tsx +0 -174
  789. package/src/components/editor/ai/__tests__/staged-cell-submission.test.ts +0 -106
  790. package/src/components/editor/ai/add-cell-with-ai.tsx +0 -506
  791. package/src/components/editor/ai/staged-cell-submission.ts +0 -46
  792. package/src/components/editor/cell/__tests__/StagedAICell.test.tsx +0 -64
  793. package/src/components/editor/cell/code/__tests__/rtc-editor-config.test.ts +0 -50
  794. package/src/components/editor/chrome/wrapper/footer-items/rtc-status.tsx +0 -65
  795. package/src/components/editor/navigation/__tests__/state.test.ts +0 -81
  796. package/src/components/editor/navigation/state.ts +0 -65
  797. package/src/components/icons/loading-ellipsis.tsx +0 -46
  798. package/src/core/ai/tools/__tests__/run-cells-tool.test.ts +0 -656
  799. package/src/core/ai/tools/run-cells-tool.ts +0 -303
  800. package/src/core/attachments/state.ts +0 -18
  801. package/src/core/cells/__tests__/apply-transaction.test.ts +0 -991
  802. package/src/core/cells/__tests__/document-changes.test.ts +0 -1140
  803. package/src/core/cells/__tests__/document-roundtrip.test.ts +0 -392
  804. package/src/core/cells/document-changes.ts +0 -944
  805. package/src/core/codemirror/rtc/extension.ts +0 -453
  806. package/src/core/codemirror/rtc/loro/__tests__/sync.test.ts +0 -52
  807. package/src/core/codemirror/rtc/loro/awareness.ts +0 -531
  808. package/src/core/codemirror/rtc/loro/colors.ts +0 -38
  809. package/src/core/codemirror/rtc/loro/sync.ts +0 -168
  810. package/src/core/host/AGENTS.md +0 -11
  811. package/src/core/host/__tests__/events.test.ts +0 -46
  812. package/src/core/host/__tests__/fixtures.ts +0 -39
  813. package/src/core/host/__tests__/notebook-client.test.ts +0 -433
  814. package/src/core/host/__tests__/protocol.test.ts +0 -214
  815. package/src/core/host/__tests__/stream.test.ts +0 -209
  816. package/src/core/host/connection.ts +0 -81
  817. package/src/core/host/events.ts +0 -61
  818. package/src/core/host/notebook-client.ts +0 -290
  819. package/src/core/host/protocol.ts +0 -1945
  820. package/src/core/host/resources/resource.ts +0 -47
  821. package/src/core/host/stream.ts +0 -211
  822. package/src/core/rtc/state.ts +0 -34
  823. package/src/core/state/__tests__/jotai.test.ts +0 -25
  824. package/src/core/wasm/worker/message-buffer.ts +0 -33
  825. package/src/core/websocket/__tests__/createConnectionTransport.test.ts +0 -49
  826. package/src/core/websocket/__tests__/useNotebookClient.hook.test.tsx +0 -170
  827. package/src/core/websocket/state.ts +0 -7
  828. package/src/core/websocket/transports/__tests__/basic.test.ts +0 -275
  829. package/src/core/websocket/transports/__tests__/sse.test.ts +0 -335
  830. package/src/core/websocket/transports/__tests__/transport.test.ts +0 -308
  831. package/src/core/websocket/transports/__tests__/ws.test.ts +0 -125
  832. package/src/core/websocket/transports/basic.ts +0 -102
  833. package/src/core/websocket/transports/create.ts +0 -31
  834. package/src/core/websocket/transports/sse.ts +0 -308
  835. package/src/core/websocket/transports/transport.ts +0 -50
  836. package/src/core/websocket/transports/ws.ts +0 -98
  837. package/src/core/websocket/useNotebookClient.ts +0 -88
  838. package/src/utils/__tests__/id-tree.test.ts +0 -2145
  839. package/src/utils/id-tree.tsx +0 -1134
  840. /package/src/{core/attachments/toast.ts → components/editor/agent-toast.ts} +0 -0
  841. /package/src/core/{websocket → session}/connection-utils.ts +0 -0
@@ -1,51 +1,43 @@
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
+ cellFreshnessAtom,
13
+ withdrawCells,
14
+ } from "@/core/executions";
15
+ import type { CellColumnId } from "./layout";
15
16
  import { invariant } from "@/utils/invariant";
16
17
  import { Logger } from "@/utils/Logger";
17
- import { clamp } from "@/utils/math";
18
18
  import { Objects } from "@/utils/objects";
19
19
  import { extractAllTracebackInfo, type TracebackInfo } from "@/utils/traceback";
20
20
  import { createReducerAndAtoms } from "../../utils/createReducer";
21
21
  import { foldAllBulk, unfoldAllBulk } from "../codemirror/editing/commands";
22
22
  import { editorMountScheduler } from "../codemirror/editor-mount-scheduler";
23
- import {
24
- splitEditor,
25
- updateEditorCodeFromPython,
26
- } from "../codemirror/language/utils";
27
23
  import type { SerializedEditorState } from "../codemirror/types";
28
- import { findCollapseRange, mergeOutlines } from "../dom/outline";
24
+ import { mergeOutlines } from "../dom/outline";
29
25
  import type { CellMessage } from "../kernel/messages";
30
26
  import { isErrorMime } from "../mime";
31
27
  import type { CellConfig } from "../network/types";
32
- import { canUseRtc, isRtcEnabled } from "../rtc/state";
33
28
  import { createDeepEqualAtom, store } from "../state/jotai";
34
29
  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";
30
+ import { transitionCell } from "./cell";
31
+ import { applyDocument } from "./document";
32
+ import { isCodeHidden, shownHiddenCodeAtom } from "./editor";
33
+ import { type CellId, SCRATCH_CELL_ID } from "./ids";
38
34
  import { type CellLog, getCellLogsForMessage } from "./logs";
39
- import {
40
- focusAndScrollCellIntoView,
41
- scrollToBottom,
42
- scrollToTop,
43
- } from "./scrollCellIntoView";
35
+ import type { Outline } from "./outline";
36
+ import { focusAndScrollCellIntoView } from "./scrollCellIntoView";
44
37
  import {
45
38
  type CellData,
46
39
  type CellRuntimeState,
47
40
  createCell,
48
- createCellConfig,
49
41
  createCellRuntimeState,
50
42
  } from "./types";
51
43
  import {
@@ -53,22 +45,24 @@ import {
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,781 +142,40 @@ 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
- valueAtom: notebookAtom,
210
- } = 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: (
822
- 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
- },
832
- ) => {
833
- const { cellId, code, formattingChange } = action;
834
- if (!state.cellData[cellId]) {
835
- return state;
836
- }
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
- return updateCellData({
861
- state,
862
- cellId,
863
- cellReducer: (cell) => {
864
- return {
865
- ...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 }),
883
- };
884
- },
885
- });
886
- },
887
- prepareForRun: (state, action: { cellId: CellId }) => {
888
- const newState = updateCellRuntimeState({
162
+ valueAtom: notebookAtom,
163
+ } = createReducerAndAtoms(initialNotebookState, {
164
+ prepareForRun: (state, action: { cellId: CellId }) =>
165
+ updateCellRuntimeState({
889
166
  state,
890
167
  cellId: action.cellId,
891
- cellReducer: (cell) => {
892
- return prepareCellForExecution(cell);
893
- },
894
- });
895
- return updateCellData({
896
- state: newState,
897
- cellId: action.cellId,
898
- cellReducer: (cell) => {
899
- return {
900
- ...cell,
901
- edited: false,
902
- lastCodeRun: cell.code.trim(),
903
- };
904
- },
905
- });
906
- },
168
+ cellReducer: (cell) => ({ ...cell, debuggerActive: false }),
169
+ }),
907
170
  handleCellMessage: (state, message: CellMessage) => {
908
171
  const cellId = message.cell_id;
909
- let nextState = updateCellRuntimeState({
172
+ const nextState = updateCellRuntimeState({
910
173
  state,
911
174
  cellId,
912
175
  cellReducer: (cell) => {
913
176
  return transitionCell(cell, message);
914
177
  },
915
178
  });
916
- // When a cell is queued for execution, snapshot the current code
917
- // as lastCodeRun. This clears staleness for cells executed by the
918
- // kernel (e.g. via code_mode). If the user edits during execution,
919
- // code !== lastCodeRun keeps the cell stale.
920
- if (message.status === "queued") {
921
- nextState = updateCellData({
922
- state: nextState,
923
- cellId,
924
- cellReducer: (cell) => ({
925
- ...cell,
926
- lastCodeRun: cell.code.trim(),
927
- edited: false,
928
- }),
929
- });
930
- }
931
179
  return {
932
180
  ...nextState,
933
181
  cellLogs: [...nextState.cellLogs, ...getCellLogsForMessage(message)],
@@ -939,158 +187,14 @@ const {
939
187
  cellLogs: [...state.cellLogs, ...action.logs],
940
188
  };
941
189
  },
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;
190
+ /** Follow the host document's cells and order. */
191
+ reconcileDocument: (state, document: EditableDocument) => {
192
+ const nextState = applyDocument(state, document);
193
+ for (const cellId of state.cellIds) {
194
+ if (!(cellId in nextState.cellData)) {
195
+ releaseCellAtoms(cellId);
1076
196
  }
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
197
  }
1093
-
1094
198
  return nextState;
1095
199
  },
1096
200
  setStdinResponse: (
@@ -1114,6 +218,7 @@ const {
1114
218
  mimetype: stdinOutput.mimetype,
1115
219
  data: stdinOutput.data,
1116
220
  timestamp: stdinOutput.timestamp,
221
+ id: stdinOutput.id,
1117
222
  // An empty response resolves the prompt without retaining the secret.
1118
223
  response: stdinOutput.mimetype === "text/password" ? "" : response,
1119
224
  };
@@ -1126,155 +231,22 @@ const {
1126
231
  },
1127
232
  });
1128
233
  },
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
234
  /**
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
235
+ * Start each cell's runtime over for a new kernel. WASM keeps the outputs
236
+ * its mount-time snapshot gave it.
1168
237
  */
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,
1260
- };
1261
- }
1262
-
1263
- return state;
1264
- },
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
- };
238
+ resetCellRuntimes: (state) => {
239
+ const preserveSnapshot = isWasm();
240
+ const cellRuntime = { ...state.cellRuntime };
241
+ for (const cellId of [...state.cellIds, SCRATCH_CELL_ID]) {
242
+ const previous = state.cellRuntime[cellId];
243
+ const hasSnapshot =
244
+ preserveSnapshot &&
245
+ previous !== undefined &&
246
+ (previous.output != null || previous.consoleOutputs.length > 0);
247
+ cellRuntime[cellId] = hasSnapshot ? previous : createCellRuntimeState();
1275
248
  }
1276
-
1277
- return state;
249
+ return { ...state, cellRuntime };
1278
250
  },
1279
251
  scrollToTarget: (state) => {
1280
252
  // Scroll to the specified cell and clear the scroll key.
@@ -1283,16 +255,15 @@ const {
1283
255
  return state;
1284
256
  }
1285
257
 
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
-
258
+ const cellId = scrollKey;
1292
259
  focusAndScrollCellIntoView({
1293
260
  cellId: cellId,
1294
261
  cell: state.cellHandles[cellId],
1295
- isCodeHidden: isCellCodeHidden(state, cellId),
262
+ isCodeHidden: isCodeHidden(
263
+ state.cellData[cellId]?.config.hide_code,
264
+ store.get(shownHiddenCodeAtom),
265
+ cellId,
266
+ ),
1296
267
  codeFocus: undefined,
1297
268
  variableName: undefined,
1298
269
  });
@@ -1322,201 +293,6 @@ const {
1322
293
  cellLogs: [],
1323
294
  };
1324
295
  },
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
296
  clearCellOutput: (state, action: { cellId: CellId }) => {
1521
297
  const { cellId } = action;
1522
298
  return updateCellRuntimeState({
@@ -1545,7 +321,7 @@ const {
1545
321
  },
1546
322
  clearAllCellOutputs: (state) => {
1547
323
  const newCellRuntime = { ...state.cellRuntime };
1548
- for (const cellId of state.cellIds.inOrderIds) {
324
+ for (const cellId of state.cellIds) {
1549
325
  newCellRuntime[cellId] = {
1550
326
  ...newCellRuntime[cellId],
1551
327
  output: null,
@@ -1557,58 +333,8 @@ const {
1557
333
  cellRuntime: newCellRuntime,
1558
334
  };
1559
335
  },
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
336
  });
1599
337
 
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
338
  // Helper function to update a cell in the array
1613
339
  function updateCellRuntimeState({
1614
340
  state,
@@ -1633,66 +359,12 @@ function updateCellRuntimeState({
1633
359
  };
1634
360
  }
1635
361
 
1636
- function updateCellData({
1637
- state,
1638
- cellId,
1639
- cellReducer,
1640
- }: {
1641
- state: NotebookState;
1642
- cellId: CellId;
1643
- cellReducer: ReducerWithoutAction<CellData>;
1644
- }) {
1645
- if (!(cellId in state.cellData)) {
1646
- Logger.warn(`Cell ${cellId} not found in state`);
1647
- return state;
1648
- }
1649
-
1650
- return {
1651
- ...state,
1652
- cellData: {
1653
- ...state.cellData,
1654
- [cellId]: cellReducer(state.cellData[cellId]),
1655
- },
1656
- };
1657
- }
1658
-
362
+ /** Each cell's config in order. The document's `column` configs are canonical. */
1659
363
  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
- });
364
+ return state.cellIds.map((cellId) => ({
365
+ ...state.cellData[cellId].config,
366
+ column: state.cellData[cellId].config.column ?? null,
367
+ }));
1696
368
  }
1697
369
 
1698
370
  export {
@@ -1712,9 +384,7 @@ export const addLogs = imperativeActions.addLogs;
1712
384
 
1713
385
  export const cellIdsAtom = atom((get) => get(notebookAtom).cellIds);
1714
386
 
1715
- export const hasOnlyOneCellAtom = atom((get) =>
1716
- get(cellIdsAtom).hasOnlyOneId(),
1717
- );
387
+ export const hasOnlyOneCellAtom = atom((get) => get(cellIdsAtom).length === 1);
1718
388
 
1719
389
  export const hasDisabledCellsAtom = atom(
1720
390
  (get) => disabledCellIds(get(notebookAtom)).length > 0,
@@ -1728,11 +398,17 @@ export const canUndoDeletesAtom = atom((get) =>
1728
398
 
1729
399
  export const undoLabelAtom = atom((get) => getUndoLabel(get(notebookAtom)));
1730
400
 
1731
- export const needsRunAtom = atom((get) => notebookNeedsRun(get(notebookAtom)));
401
+ export const needsRunAtom = atom((get) =>
402
+ notebookNeedsRun(
403
+ get(notebookAtom),
404
+ get(cellExecutionInputsAtom),
405
+ get(cellFreshnessAtom),
406
+ ),
407
+ );
1732
408
 
1733
409
  export const cellErrorsAtom = atom((get) => {
1734
410
  const { cellIds, cellRuntime, cellData } = get(notebookAtom);
1735
- const errors = cellIds.inOrderIds
411
+ const errors = cellIds
1736
412
  .map((cellId) => {
1737
413
  const cell = cellRuntime[cellId];
1738
414
  const { name } = cellData[cellId];
@@ -1760,11 +436,28 @@ export const cellErrorsAtom = atom((get) => {
1760
436
  return errors;
1761
437
  });
1762
438
 
439
+ /**
440
+ * The outline of each cell whose output has a heading. Compared by value, so
441
+ * readers are left alone by the runtime messages that change no heading.
442
+ */
443
+ export const cellOutlinesAtom = createDeepEqualAtom(
444
+ atom((get) => {
445
+ const outlines: Partial<Record<CellId, Outline>> = {};
446
+ for (const [id, runtime] of Objects.entries(
447
+ get(notebookAtom).cellRuntime,
448
+ )) {
449
+ if (runtime.outline !== null && runtime.outline.items.length > 0) {
450
+ outlines[id] = runtime.outline;
451
+ }
452
+ }
453
+ return outlines;
454
+ }),
455
+ );
456
+
1763
457
  export const notebookOutline = atom((get) => {
1764
- const { cellIds, cellRuntime } = get(notebookAtom);
1765
- const outlines = cellIds.inOrderIds.map(
1766
- (cellId) => cellRuntime[cellId].outline,
1767
- );
458
+ const cellIds = get(cellIdsAtom);
459
+ const cellRuntime = get(cellsRuntimeAtom);
460
+ const outlines = cellIds.map((cellId) => cellRuntime[cellId].outline);
1768
461
  return mergeOutlines(outlines);
1769
462
  });
1770
463
 
@@ -1774,7 +467,7 @@ export const cellIdToNamesMap = createDeepEqualAtom(
1774
467
  atom((get) => {
1775
468
  const { cellIds, cellData } = get(notebookAtom);
1776
469
  const names: Record<CellId, string | undefined> = Objects.fromEntries(
1777
- cellIds.inOrderIds.map((cellId) => [cellId, cellData[cellId]?.name]),
470
+ cellIds.map((cellId) => [cellId, cellData[cellId]?.name]),
1778
471
  );
1779
472
  return names;
1780
473
  }),
@@ -1831,41 +524,19 @@ export const getCells = () => store.get(notebookAtom).cellIds;
1831
524
  */
1832
525
  export const getCellNames = () => {
1833
526
  const { cellIds, cellData } = store.get(notebookAtom);
1834
- return cellIds.inOrderIds.map((id) => cellData[id]?.name).filter(Boolean);
527
+ return cellIds.map((id) => cellData[id]?.name).filter(Boolean);
1835
528
  };
1836
529
 
1837
530
  const cellDataAtoms = splitAtom(
1838
531
  selectAtom(notebookAtom, (cells) =>
1839
- cells.cellIds.inOrderIds.map((id) => cells.cellData[id]),
532
+ cells.cellIds.map((id) => cells.cellData[id]),
1840
533
  ),
1841
534
  );
1842
535
  export const useCellDataAtoms = () => useAtom(cellDataAtoms);
1843
536
 
1844
537
  export const cellsRuntimeAtom = atom((get) => get(notebookAtom).cellRuntime);
1845
538
 
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
-
539
+ export const hasCellsAtom = atom((get) => get(notebookAtom).cellIds.length > 0);
1869
540
  export const cellDataAtom = atomFamily((cellId: CellId) =>
1870
541
  atom((get) => get(notebookAtom).cellData[cellId]),
1871
542
  );
@@ -1889,6 +560,7 @@ export function releaseCellAtoms(cellId: CellId) {
1889
560
  cellDataAtom.remove(cellId);
1890
561
  cellRuntimeAtom.remove(cellId);
1891
562
  cellHandleAtom.remove(cellId);
563
+ withdrawCells(store, [cellId]);
1892
564
  }
1893
565
 
1894
566
  /** Subscribes to reactive updates of the cell's data. */
@@ -1906,7 +578,7 @@ export const useCellHandle = (cellId: CellId) =>
1906
578
  */
1907
579
  export const getAllEditorViews = () => {
1908
580
  const { cellIds, cellHandles } = store.get(notebookAtom);
1909
- return cellIds.inOrderIds
581
+ return cellIds
1910
582
  .map((cellId) => cellHandles[cellId]?.current?.editorViewOrNull)
1911
583
  .filter(Boolean);
1912
584
  };
@@ -1930,16 +602,10 @@ export function flattenTopLevelNotebookCells(
1930
602
  state: NotebookState,
1931
603
  ): (CellData & CellRuntimeState)[] {
1932
604
  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));
605
+ return cellIds.map((cellId) => ({
606
+ ...cellData[cellId],
607
+ ...cellRuntime[cellId],
608
+ }));
1943
609
  }
1944
610
 
1945
611
  export function createTracebackInfoAtom(
@@ -1955,7 +621,8 @@ export function createTracebackInfoAtom(
1955
621
  return undefined;
1956
622
  }
1957
623
 
1958
- if (data.status === "queued" || data.status === "running") {
624
+ const { status } = cellExecutionView(get(cellExecutionInputsAtom), cellId);
625
+ if (status === "queued" || status === "running") {
1959
626
  return undefined;
1960
627
  }
1961
628
 
@@ -2001,20 +668,13 @@ export function createTracebackInfoAtom(
2001
668
  });
2002
669
  }
2003
670
 
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);
671
+ /** The reducer's actions. Components use `useCellActions` from `./actions`. */
672
+ export function useReducerActions(): ReducerCellActions {
673
+ return useActions();
2012
674
  }
2013
675
 
2014
- /**
2015
- * Map of cell actions
2016
- */
2017
- export type CellActions = ReturnType<typeof createActions>;
676
+ /** The actions the reducer still handles. */
677
+ export type ReducerCellActions = ReturnType<typeof createActions>;
2018
678
 
2019
679
  /**
2020
680
  * This is exported for testing purposes only.
@@ -2023,7 +683,6 @@ export const exportedForTesting = {
2023
683
  reducer,
2024
684
  createActions,
2025
685
  initialNotebookState,
2026
- isCellCodeHidden,
2027
686
  // Export atom families for testing cleanup
2028
687
  cellDataAtom,
2029
688
  cellRuntimeAtom,