@marimo-team/islands 0.25.3-dev20 → 0.25.3-dev201

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 (688) hide show
  1. package/dist/{ConnectedDataExplorerComponent-CNMliVzw.js → ConnectedDataExplorerComponent-BsWiIPFk.js} +22 -20
  2. package/dist/{ErrorBoundary-DAPIRYqd.js → ErrorBoundary-JYAf40sB.js} +19 -19
  3. package/dist/{abnfDiagram-VCTEODGH-D-Ez91gV.js → abnfDiagram-VCTEODGH-I2x4Yu25.js} +2 -2
  4. package/dist/any-language-editor-BVFHDDQ3.js +75 -0
  5. package/dist/{architectureDiagram-5GKGNRK7-CTpcNtO8.js → architectureDiagram-5GKGNRK7-1bSQMKzK.js} +1 -1
  6. package/dist/{arrays-CN1djC4k.js → arrays-DoQ-AILx.js} +15 -27
  7. package/dist/assets/__vite-browser-external-BCJsx8QS.js +1 -0
  8. package/dist/assets/worker-DodjWT_Z.js +113 -0
  9. package/dist/{button-CvjkwdSk.js → button-BQRdqQi1.js} +199 -192
  10. package/dist/{capabilities-CQ5IjhzL.js → capabilities-C2QUNRXi.js} +2 -2
  11. package/dist/{chat-ui-DaHynk8g.js → chat-ui-DXDoSuS4.js} +370 -369
  12. package/dist/{check-EDwSwP89.js → check-C9Mp9eCK.js} +1 -1
  13. package/dist/{chunk-IMKFNOWR-DGxi-zeo.js → chunk-IMKFNOWR-fsCGAJ9G.js} +1 -1
  14. package/dist/{chunk-SHT3W25Y-CV9J3rAB.js → chunk-SHT3W25Y-D2STW4hj.js} +1 -1
  15. package/dist/{chunk-SVP7TREG-BL-9Fqgg.js → chunk-SVP7TREG-CiY7cQG-.js} +1 -1
  16. package/dist/{chunk-TICWLB2K-Cu_XMjDM.js → chunk-TICWLB2K-C_t5RBp9.js} +1 -1
  17. package/dist/{chunk-YOKDWASO-DPZk8vhS.js → chunk-YOKDWASO-DKplTrBY.js} +20 -20
  18. package/dist/{classDiagram-ZZMXUADV-BXXV13Hq.js → classDiagram-ZZMXUADV-B2JvZAP4.js} +2 -2
  19. package/dist/{classDiagram-v2-VYDZK3BY-B7FXJ1Cp.js → classDiagram-v2-VYDZK3BY-L8VsZG5A.js} +2 -2
  20. package/dist/code-visibility-BKyYMfr_.js +134 -0
  21. package/dist/{common-DsUPhkD6.js → common-BCY25vum.js} +6040 -5865
  22. package/dist/{copy-icon-Ft7t9bqz.js → copy-icon-De584pZ4.js} +3 -3
  23. package/dist/{cynefinDiagram-5FMLGOSQ-CH96rwba.js → cynefinDiagram-5FMLGOSQ-DdllC264.js} +1 -1
  24. package/dist/{dagre-GXQ25YYZ-BD3PmpFY.js → dagre-GXQ25YYZ-BaWHw4_f.js} +1 -1
  25. package/dist/{data-grid-overlay-editor-D7UuVsgB.js → data-grid-overlay-editor-gQmJxgU-.js} +5 -5
  26. package/dist/{data-grid-types-BH_r7g7l.js → data-grid-types-0PJ-9N5n.js} +1 -1
  27. package/dist/{diagram-S7CK7UJ4-BsSJJldB.js → diagram-S7CK7UJ4-pFt-tUDe.js} +1 -1
  28. package/dist/{diagram-UQ7AKVKN-muQtNZc4.js → diagram-UQ7AKVKN-DqGJiBp4.js} +1 -1
  29. package/dist/{diagram-VX7I27RA-CdhuZAqR.js → diagram-VX7I27RA-Dr-yzMjf.js} +1 -1
  30. package/dist/{diagram-Z3DM3KII-BD8mXjOj.js → diagram-Z3DM3KII-8xzJzv-w.js} +1 -1
  31. package/dist/{dialog-CEBFZNyc.js → dialog-DVRfQuhD.js} +25 -25
  32. package/dist/{dist-F8Ep36rk.js → dist-B1M0bGO3.js} +1 -1
  33. package/dist/{dist-D6d86eNJ.js → dist-BA13RJj5.js} +1 -1
  34. package/dist/{dist-Bvs24R_E.js → dist-BJtmSgcE.js} +48 -48
  35. package/dist/{dist-CN_BeG2I.js → dist-Ba2VIn1K.js} +53 -53
  36. package/dist/{dist-PXYI_yuP.js → dist-Bj4bmwUm.js} +12 -12
  37. package/dist/{dist-DoE4XJBC.js → dist-BmS-ZGjl.js} +8626 -8626
  38. package/dist/{dist-BorMD-uX.js → dist-Bzogh7CW.js} +87 -87
  39. package/dist/{dist-CN8zI-XC.js → dist-CThPVKaP.js} +1385 -1382
  40. package/dist/{dist-DiDibOYr.js → dist-CTi5FTiV.js} +87 -87
  41. package/dist/{dist-diIx5qJW.js → dist-Ck_C32ye.js} +2 -2
  42. package/dist/{dist-BLfKmR-l.js → dist-D1fnFp1J.js} +1 -1
  43. package/dist/{dist-OR1OnN-y.js → dist-D3x9__Fu.js} +46 -46
  44. package/dist/{dist-BMQf5zIS.js → dist-D7mrh5sO.js} +36 -36
  45. package/dist/{dist-CSJ0MDRw.js → dist-D9Q3xjiP.js} +1 -1
  46. package/dist/{dist-BuaRHxOc.js → dist-DP60Ubh8.js} +2 -2
  47. package/dist/{dist-CSbh1PNP.js → dist-DTv9KkpW.js} +12 -12
  48. package/dist/{dist-D-wwqlzJ.js → dist-DVVwOfrD.js} +109 -109
  49. package/dist/{dist-C8fAKSH1.js → dist-DYXM73-k.js} +36 -36
  50. package/dist/{dist-DBtSl3sf.js → dist-DjeybPOb.js} +1 -1
  51. package/dist/{dist-CrSP_H1M.js → dist-DtGn9N4h.js} +197 -197
  52. package/dist/{dist-CLdpGQVr.js → dist-KN80qWCP.js} +2 -2
  53. package/dist/{dist-DpBpA_n1.js → dist-Lm-D-Vr6.js} +3 -3
  54. package/dist/{dist-Cg7gzw-a.js → dist-O0qFpEJh.js} +56 -56
  55. package/dist/{dist-CkTTIdBe.js → dist-OUle2RT7.js} +207 -207
  56. package/dist/{dist-BK4Oj99o.js → dist-T5rh6JvL.js} +1 -1
  57. package/dist/{dropdown-menu-CP_qCYHC.js → dropdown-menu-NlnsmNA6.js} +1481 -1433
  58. package/dist/{ebnfDiagram-PWID7BFC-BIN1sweE.js → ebnfDiagram-PWID7BFC-Br04Wy-E.js} +2 -2
  59. package/dist/{erDiagram-RLTQ6QDP-Ctp5cfoN.js → erDiagram-RLTQ6QDP-D5-XVgvv.js} +1 -1
  60. package/dist/{error-banner-D-WCIiMt.js → error-banner-DxJ3s8YO.js} +86 -86
  61. package/dist/{error-entries-Bv6bZPF3.js → error-entries-C2Ux0Fjt.js} +8404 -9095
  62. package/dist/{esm-D2gTsXEV.js → esm-Bbw_Njjx.js} +575 -575
  63. package/dist/{esm-AMFssoNp.js → esm-mDQNEFe_.js} +354 -354
  64. package/dist/feedback-button-BKgw9MHY.js +374 -0
  65. package/dist/{flowDiagram-HODETNUW-3EfLrlFb.js → flowDiagram-HODETNUW-CVl2kREF.js} +2 -2
  66. package/dist/{formats-27CW_UP4.js → formats-BCYthwyc.js} +3 -3
  67. package/dist/{ganttDiagram-EL5Y4UJY-BQT16JSc.js → ganttDiagram-EL5Y4UJY-Dh_4cBJZ.js} +2 -2
  68. package/dist/{glide-data-editor-KvS90MGh.js → glide-data-editor-CzS51bb2.js} +370 -368
  69. package/dist/{glide-utils-DJyqh0EM.js → glide-utils-D98laidb.js} +16 -16
  70. package/dist/{globals-yruoELTW.js → globals-DQ4tv_a-.js} +3 -3
  71. package/dist/{highlighted-body-KPVGNVTW-zwjq21tf.js → highlighted-body-KPVGNVTW-3Ziwfz-V.js} +2 -2
  72. package/dist/{html-to-image-Bf8QMGi4.js → html-to-image-CkH7vdBZ.js} +477 -380
  73. package/dist/{infoDiagram-27XIBGKW-DipQoHxl.js → infoDiagram-27XIBGKW-BJ4vNcZE.js} +1 -1
  74. package/dist/{ishikawaDiagram-5VMMS53U-DgFMjSGz.js → ishikawaDiagram-5VMMS53U-B_Jy1Dsm.js} +1 -1
  75. package/dist/{kanban-definition-UXKFOSKX-w1ACVjgl.js → kanban-definition-UXKFOSKX-C6MkS9vw.js} +1 -1
  76. package/dist/{label-BDKF3Afc.js → label-Qvw1E97W.js} +3 -3
  77. package/dist/{linear-Cnaq3Tfb.js → linear-DWERh7eI.js} +121 -73
  78. package/dist/{loader-BQOfjTIJ.js → loader-DOVB0Syl.js} +12 -12
  79. package/dist/main.js +9706 -7709
  80. package/dist/{mermaid-CHo49Yv-.js → mermaid-Bh918qrq.js} +52 -52
  81. package/dist/mermaid-HWGCJPDP-Du3-HPQA.js +2 -0
  82. package/dist/{mindmap-definition-YA3MSWOX-CrT0KSfX.js → mindmap-definition-YA3MSWOX-BP3ZTuir.js} +1 -1
  83. package/dist/{number-overlay-editor-R2i0FdxV.js → number-overlay-editor-B3ZE3-6d.js} +3 -3
  84. package/dist/{objects-CzUgoMIF.js → objects-DCmMvZtn.js} +1 -3
  85. package/dist/{once-BtE-wKRL.js → once-NnpX_HnX.js} +1 -1
  86. package/dist/{pegDiagram-XKGWAZYB-BqvywNJA.js → pegDiagram-XKGWAZYB-nNGOQtnt.js} +2 -2
  87. package/dist/{pieDiagram-E7YTZNPT-BfxJ_XCc.js → pieDiagram-E7YTZNPT-CpRz5BZz.js} +1 -1
  88. package/dist/precisionRound-DhAzeRcm.js +49 -0
  89. package/dist/{process-output-BZg56CCb.js → process-output-OujAtFW4.js} +5 -5
  90. package/dist/{quadrantDiagram-AXDQQJYC-Ccw0xMjN.js → quadrantDiagram-AXDQQJYC-Blrm7V7_.js} +1 -1
  91. package/dist/{railroadDiagram-O6MQD6OU-8vFLVTPu.js → railroadDiagram-O6MQD6OU-BqOEN4I0.js} +2 -2
  92. package/dist/{react-vega-BED0DjiB.js → react-vega-C4T9Rb_w.js} +12284 -12270
  93. package/dist/{requirementDiagram-BXWQKSXE-a_MxGvxe.js → requirementDiagram-BXWQKSXE-CEfg6vu9.js} +1 -1
  94. package/dist/{reveal-component-C42-YBfQ.js → reveal-component-DXa9FXf6.js} +733 -710
  95. package/dist/{select-BButFi-d.js → select-2KOiReNY.js} +536 -457
  96. package/dist/{sequenceDiagram-WJ2MYXX4-CXFMnxGh.js → sequenceDiagram-WJ2MYXX4-IOmpQcOi.js} +1 -1
  97. package/dist/{spec-BwarBpUJ.js → spec-Dkp64vVQ.js} +41 -11
  98. package/dist/{stateDiagram-D77RDMKH-DQeA_CwI.js → stateDiagram-D77RDMKH-tgU-EY9_.js} +2 -2
  99. package/dist/{stateDiagram-v2-MP3YSRHH-BxxQucm1.js → stateDiagram-v2-MP3YSRHH-BofhG6q1.js} +2 -2
  100. package/dist/style.css +1 -1
  101. package/dist/{swimlanes-42K2YHIH-Do6L-cYg.js → swimlanes-42K2YHIH-DW4vWDBd.js} +1 -1
  102. package/dist/{swimlanesDiagram-VR7AAH4N-DUzPvSVU.js → swimlanesDiagram-VR7AAH4N-BJcrqUJr.js} +2 -2
  103. package/dist/{swiper-component-DKntyn8p.js → swiper-component-BVfnhbnt.js} +2 -2
  104. package/dist/{time-BieKwdYS.js → time-BaMbJs4z.js} +18 -18
  105. package/dist/{timeline-definition-24CTP7MA-D3c46WM5.js → timeline-definition-24CTP7MA-D_QU3NGs.js} +1 -1
  106. package/dist/{toDate-UtiwmnH2.js → toDate-BNDEXbfV.js} +44 -43
  107. package/dist/{toast-B9UMEi8u.js → toast-B5t9o6s_.js} +219 -201
  108. package/dist/{tooltip-BO1REbqs.js → tooltip-s4zZxwKe.js} +325 -301
  109. package/dist/{use-restore-focus-BEM7sFWl.js → use-restore-focus-BgDTd2tc.js} +1482 -1433
  110. package/dist/{useAsyncData-Deh5kszT.js → useAsyncData-ASDni9vH.js} +15 -14
  111. package/dist/{useDateFormatter-D8XruNGG.js → useDateFormatter-D3Tkfzej.js} +2 -2
  112. package/dist/{useDeepCompareMemoize-DXwzRINI.js → useDeepCompareMemoize-B1Ogyf53.js} +1 -1
  113. package/dist/{useIframeCapabilities-DBETvvPl.js → useIframeCapabilities-6a_yDNXp.js} +1 -1
  114. package/dist/{useLifecycle-j7D9uD1o.js → useLifecycle-Bfpa2m7k.js} +28 -25
  115. package/dist/{useTheme-CRnjkku0.js → useTheme-5TwyOh3B.js} +522 -314
  116. package/dist/{vega-component-BeF9dfyO.js → vega-component-BHS9UOtC.js} +158 -158
  117. package/dist/{vega-embed-container-BNHE18G9.js → vega-embed-container--LYZqguO.js} +2 -2
  118. package/dist/{vega-loader.browser-DNST6uVf.js → vega-loader.browser-B9Pz0EGP.js} +298 -507
  119. package/dist/{vennDiagram-4TSXK5OY-BSZD8JcZ.js → vennDiagram-4TSXK5OY-YFYsNIAh.js} +1 -1
  120. package/dist/{wardleyDiagram-VM6X3IG4-48cUWkfA.js → wardleyDiagram-VM6X3IG4-i_n8mAWC.js} +1 -1
  121. package/dist/{xychartDiagram-S5SC5T6Z-CswYgYeu.js → xychartDiagram-S5SC5T6Z-BYGN95wW.js} +2 -2
  122. package/package.json +16 -16
  123. package/src/__mocks__/document.tsx +63 -0
  124. package/src/__mocks__/notebook.ts +3 -4
  125. package/src/__mocks__/partial.ts +26 -0
  126. package/src/__mocks__/requests.ts +0 -3
  127. package/src/__tests__/main.test.tsx +24 -1
  128. package/src/__tests__/module-entry.test.ts +44 -0
  129. package/src/__tests__/mount.test.ts +1 -21
  130. package/src/__tests__/{host-imports.test.ts → replica-imports.test.ts} +12 -11
  131. package/src/components/ai/__tests__/ai-model-dropdown.test.tsx +63 -0
  132. package/src/components/ai/ai-model-dropdown.tsx +11 -6
  133. package/src/components/app-config/ai-config.tsx +7 -2
  134. package/src/components/app-config/app-config-form.tsx +5 -9
  135. package/src/components/app-config/user-config-form.tsx +35 -0
  136. package/src/components/chat/__tests__/message-queue.test.tsx +17 -0
  137. package/src/components/chat/__tests__/notebook-access-selector.test.tsx +64 -0
  138. package/src/components/chat/acp/__tests__/session-tabs.test.tsx +43 -0
  139. package/src/components/chat/acp/__tests__/state.test.ts +2 -2
  140. package/src/components/chat/acp/blocks.tsx +3 -2
  141. package/src/components/chat/acp/session-tabs.tsx +8 -3
  142. package/src/components/chat/chat-components.tsx +2 -0
  143. package/src/components/chat/chat-panel.tsx +20 -101
  144. package/src/components/chat/chat-utils.ts +4 -8
  145. package/src/components/chat/notebook-access-selector.stories.tsx +22 -0
  146. package/src/components/chat/notebook-access-selector.tsx +100 -0
  147. package/src/components/data-table/TableBottomBar.tsx +4 -1
  148. package/src/components/data-table/TableTopBar.tsx +2 -0
  149. package/src/components/data-table/__tests__/chart-spec-model.test.ts +195 -195
  150. package/src/components/data-table/__tests__/column-explorer.test.tsx +27 -1
  151. package/src/components/data-table/__tests__/columns.test.tsx +105 -0
  152. package/src/components/data-table/__tests__/context-menu-interaction.test.tsx +130 -0
  153. package/src/components/data-table/__tests__/data-table.test.tsx +1 -0
  154. package/src/components/data-table/__tests__/date-popover.test.tsx +72 -0
  155. package/src/components/data-table/__tests__/export-actions.test.tsx +105 -0
  156. package/src/components/data-table/__tests__/keyboard-selection.test.tsx +188 -0
  157. package/src/components/data-table/__tests__/pagination.test.tsx +28 -1
  158. package/src/components/data-table/__tests__/search-highlight.test.tsx +109 -0
  159. package/src/components/data-table/__tests__/url-detector.test.tsx +74 -0
  160. package/src/components/data-table/cell-selection/feature.ts +2 -2
  161. package/src/components/data-table/cell-selection/types.ts +2 -2
  162. package/src/components/data-table/charts/__tests__/chart-panel.test.tsx +74 -1
  163. package/src/components/data-table/charts/__tests__/spec-snapshot.test.ts +20 -20
  164. package/src/components/data-table/charts/__tests__/spec.test.ts +78 -52
  165. package/src/components/data-table/charts/__tests__/storage.test.ts +4 -1
  166. package/src/components/data-table/charts/chart-spec/spec.ts +68 -43
  167. package/src/components/data-table/charts/charts.tsx +40 -30
  168. package/src/components/data-table/charts/storage.ts +16 -5
  169. package/src/components/data-table/column-explorer-panel/column-explorer.tsx +1 -1
  170. package/src/components/data-table/column-summary/chart-spec-model.tsx +23 -16
  171. package/src/components/data-table/column-summary/column-summary.tsx +2 -4
  172. package/src/components/data-table/column-visibility-dropdown.tsx +8 -13
  173. package/src/components/data-table/columns.tsx +45 -46
  174. package/src/components/data-table/context-menu.tsx +36 -20
  175. package/src/components/data-table/data-table.tsx +17 -1
  176. package/src/components/data-table/date-popover.tsx +6 -4
  177. package/src/components/data-table/export-actions.tsx +76 -48
  178. package/src/components/data-table/pagination.tsx +44 -33
  179. package/src/components/data-table/range-focus/atoms.ts +4 -6
  180. package/src/components/data-table/range-focus/use-cell-range-selection.ts +65 -38
  181. package/src/components/data-table/range-focus/use-scroll-into-view.ts +21 -2
  182. package/src/components/data-table/renderers.tsx +16 -7
  183. package/src/components/data-table/row-viewer-panel/__tests__/row-viewer.test.tsx +24 -1
  184. package/src/components/data-table/row-viewer-panel/row-viewer.tsx +4 -4
  185. package/src/components/data-table/search-highlight.tsx +79 -0
  186. package/src/components/data-table/types.ts +1 -0
  187. package/src/components/data-table/url-detector.tsx +49 -9
  188. package/src/components/datasources/__tests__/column-actions.test.tsx +97 -0
  189. package/src/components/datasources/__tests__/column-preview.test.tsx +96 -5
  190. package/src/components/datasources/__tests__/datasources-interactions.test.tsx +181 -0
  191. package/src/components/datasources/__tests__/table-list.test.tsx +118 -0
  192. package/src/components/datasources/__tests__/utils.test.ts +57 -6
  193. package/src/components/datasources/column-preview.tsx +17 -12
  194. package/src/components/datasources/datasources.tsx +56 -43
  195. package/src/components/datasources/utils.ts +32 -23
  196. package/src/components/debugger/debugger-code.tsx +5 -0
  197. package/src/components/dependency-graph/custom-node.tsx +1 -1
  198. package/src/components/dependency-graph/dependency-graph-tree.tsx +8 -8
  199. package/src/components/dependency-graph/elements.ts +24 -21
  200. package/src/components/dependency-graph/minimap-content.tsx +13 -19
  201. package/src/components/dependency-graph/panels.tsx +4 -2
  202. package/src/components/dependency-graph/utils/__tests__/layout.test.ts +18 -11
  203. package/src/components/editor/SortableCell.tsx +1 -3
  204. package/src/components/editor/__tests__/Output.test.tsx +50 -32
  205. package/src/{core/attachments/__tests__/toast.test.tsx → components/editor/__tests__/agent-toast.test.tsx} +1 -1
  206. package/src/components/editor/__tests__/data-attributes.test.tsx +28 -14
  207. package/src/components/editor/__tests__/dynamic-favicon.test.tsx +111 -18
  208. package/src/components/editor/__tests__/output-persistence.test.tsx +6 -2
  209. package/src/components/editor/__tests__/package-alert.test.tsx +22 -1
  210. package/src/components/editor/actions/__tests__/name-cell-input.test.tsx +69 -0
  211. package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +34 -8
  212. package/src/components/editor/actions/__tests__/pair-with-agent.test.tsx +2 -6
  213. package/src/components/editor/actions/name-cell-input.tsx +4 -3
  214. package/src/components/editor/actions/pair-with-agent-commands.ts +2 -2
  215. package/src/components/editor/actions/pair-with-agent-modal.tsx +19 -16
  216. package/src/components/editor/actions/useCellActionButton.tsx +26 -31
  217. package/src/components/editor/actions/useConfigActions.tsx +3 -4
  218. package/src/components/editor/actions/useNotebookActions.tsx +19 -23
  219. package/src/components/editor/actions/useRestartKernel.tsx +1 -1
  220. package/src/components/editor/actions/useSetCodeVisibility.ts +11 -12
  221. package/src/components/editor/actions/useSetOutputsExpanded.ts +14 -19
  222. package/src/components/editor/ai/add-cell-with-ai.tsx +3 -3
  223. package/src/components/editor/ai/ai-completion-editor.tsx +1 -0
  224. package/src/components/editor/alerts/__tests__/connection-notice.test.tsx +1 -1
  225. package/src/components/editor/alerts/connection-notice.tsx +1 -1
  226. package/src/components/editor/alerts/startup-logs-alert.tsx +1 -0
  227. package/src/components/editor/alerts/stdin-blocking-alert.tsx +1 -1
  228. package/src/components/editor/app-container.tsx +1 -1
  229. package/src/components/editor/boundary/__tests__/ErrorBoundary.test.tsx +1 -1
  230. package/src/components/editor/cell/CreateCellButton.tsx +15 -21
  231. package/src/components/editor/cell/DeleteButton.tsx +3 -2
  232. package/src/components/editor/cell/RunButton.tsx +2 -2
  233. package/src/components/editor/cell/StagedAICell.tsx +10 -2
  234. package/src/components/editor/cell/StopButton.tsx +2 -2
  235. package/src/components/editor/cell/__tests__/StagedAICell.test.tsx +3 -1
  236. package/src/components/editor/cell/__tests__/useShouldShowInterrupt.test.ts +39 -0
  237. package/src/components/editor/cell/cell-actions.tsx +12 -1
  238. package/src/components/editor/cell/code/__tests__/language-toggle.test.tsx +54 -0
  239. package/src/components/editor/cell/code/cell-editor.tsx +36 -43
  240. package/src/components/editor/cell/code/language-toggle.tsx +8 -7
  241. package/src/components/editor/cell/collapse.tsx +11 -4
  242. package/src/components/editor/cell/useAddCell.ts +4 -8
  243. package/src/components/editor/cell/useDeleteCell.tsx +10 -36
  244. package/src/components/editor/cell/useRunCells.ts +2 -5
  245. package/src/components/editor/cell/useShouldShowInterrupt.ts +2 -12
  246. package/src/components/editor/cell/useSplitCell.tsx +10 -6
  247. package/src/components/editor/chrome/components/__tests__/feedback-button.test.tsx +1 -1
  248. package/src/components/editor/chrome/components/feedback-button.tsx +2 -8
  249. package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +31 -2
  250. package/src/components/editor/chrome/panels/__tests__/runtime-details.test.tsx +1 -1
  251. package/src/components/editor/chrome/panels/__tests__/sandbox-recovery.test.tsx +1 -1
  252. package/src/components/editor/chrome/panels/cache-panel.tsx +1 -0
  253. package/src/components/editor/chrome/panels/dependency-graph-panel.tsx +1 -1
  254. package/src/components/editor/chrome/panels/logs-panel.tsx +2 -1
  255. package/src/components/editor/chrome/panels/outline/__tests__/outline-keyboard.test.tsx +84 -0
  256. package/src/components/editor/chrome/panels/outline/floating-outline.tsx +39 -14
  257. package/src/components/editor/chrome/panels/outline/useActiveOutline.tsx +3 -2
  258. package/src/components/editor/chrome/panels/packages-panel.tsx +4 -7
  259. package/src/components/editor/chrome/panels/secrets-panel.tsx +1 -0
  260. package/src/components/editor/chrome/panels/session-panel.tsx +1 -1
  261. package/src/components/editor/chrome/panels/snippets-panel.tsx +10 -12
  262. package/src/components/editor/chrome/panels/variable-panel.tsx +1 -5
  263. package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +9 -18
  264. package/src/components/editor/chrome/wrapper/__tests__/document-sync-status.test.tsx +92 -0
  265. package/src/components/editor/chrome/wrapper/__tests__/pending-ai-cells.test.tsx +61 -2
  266. package/src/components/editor/chrome/wrapper/footer-items/attachment-status.tsx +4 -4
  267. package/src/components/editor/chrome/wrapper/footer-items/backend-status.tsx +1 -1
  268. package/src/components/editor/chrome/wrapper/footer-items/document-sync-status.tsx +54 -0
  269. package/src/components/editor/chrome/wrapper/footer-items/lsp-status.tsx +1 -1
  270. package/src/components/editor/chrome/wrapper/footer-items/machine-stats.tsx +1 -1
  271. package/src/components/editor/chrome/wrapper/footer-items/runtime-settings.tsx +1 -7
  272. package/src/components/editor/chrome/wrapper/footer.tsx +2 -0
  273. package/src/components/editor/chrome/wrapper/minimap-state.ts +1 -1
  274. package/src/components/editor/chrome/wrapper/pending-ai-cells.tsx +12 -2
  275. package/src/components/editor/chrome/wrapper/storage.ts +1 -1
  276. package/src/components/editor/columns/__tests__/cell-column.test.tsx +1 -1
  277. package/src/components/editor/columns/cell-column.tsx +1 -1
  278. package/src/components/editor/columns/sortable-column.tsx +2 -2
  279. package/src/components/editor/connections/components.tsx +5 -10
  280. package/src/components/editor/controls/Controls.tsx +4 -4
  281. package/src/components/editor/controls/__tests__/command-palette.test.tsx +53 -0
  282. package/src/components/editor/controls/__tests__/keyboard-shortcuts.test.tsx +61 -0
  283. package/src/components/editor/controls/__tests__/notebook-menu-dropdown.test.tsx +10 -4
  284. package/src/components/editor/controls/command-palette.tsx +22 -10
  285. package/src/components/editor/controls/keyboard-shortcuts.tsx +14 -5
  286. package/src/components/editor/documentation.css +34 -11
  287. package/src/components/editor/dynamic-favicon.tsx +21 -40
  288. package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +17 -29
  289. package/src/components/editor/errors/auto-fix.tsx +4 -8
  290. package/src/components/editor/errors/send-error-report-button.tsx +3 -6
  291. package/src/components/editor/file-tree/__tests__/file-explorer-interactions.test.tsx +6 -0
  292. package/src/components/editor/file-tree/__tests__/file-explorer-navigation.test.tsx +47 -0
  293. package/src/components/editor/file-tree/__tests__/use-file-search.test.tsx +91 -0
  294. package/src/components/editor/file-tree/file-explorer.tsx +48 -15
  295. package/src/components/editor/file-tree/file-name-input.tsx +1 -0
  296. package/src/components/editor/file-tree/file-viewer.tsx +4 -2
  297. package/src/components/editor/file-tree/use-file-search.ts +35 -21
  298. package/src/components/editor/header/__tests__/status.test.tsx +5 -3
  299. package/src/components/editor/header/app-header.tsx +1 -1
  300. package/src/components/editor/header/status.tsx +40 -20
  301. package/src/components/editor/links/__tests__/cell-link.test.tsx +148 -0
  302. package/src/components/editor/links/cell-link-list.tsx +1 -1
  303. package/src/components/editor/links/cell-link.tsx +17 -7
  304. package/src/components/editor/navigation/__tests__/clipboard.test.ts +60 -38
  305. package/src/components/editor/navigation/__tests__/navigation.test.ts +108 -85
  306. package/src/components/editor/navigation/__tests__/selection.test.ts +43 -21
  307. package/src/components/editor/navigation/clipboard.ts +17 -14
  308. package/src/components/editor/navigation/focus-utils.ts +23 -4
  309. package/src/components/editor/navigation/multi-cell-action-toolbar.tsx +17 -33
  310. package/src/components/editor/navigation/navigation.ts +71 -70
  311. package/src/components/editor/navigation/selection.ts +30 -29
  312. package/src/components/editor/notebook-banner.tsx +1 -0
  313. package/src/components/editor/notebook-cell.tsx +29 -31
  314. package/src/components/editor/output/MarimoErrorOutput.tsx +2 -1
  315. package/src/components/editor/output/MarimoTracebackOutput.tsx +19 -17
  316. package/src/components/editor/output/Outputs.css +8 -0
  317. package/src/components/editor/output/__tests__/traceback.test.tsx +27 -1
  318. package/src/components/editor/output/ansi-reduce.ts +33 -20
  319. package/src/components/editor/output/console/ConsoleOutput.tsx +19 -25
  320. package/src/components/editor/output/console/__tests__/ConsoleOutput.test.tsx +125 -69
  321. package/src/components/editor/package-alert.tsx +14 -7
  322. package/src/components/editor/renderers/cell-array.tsx +22 -33
  323. package/src/components/editor/renderers/grid-layout/grid-layout.tsx +12 -6
  324. package/src/components/editor/renderers/slides-layout/__tests__/plugin.test.ts +0 -1
  325. package/src/components/editor/renderers/slides-layout/slides-layout.tsx +1 -1
  326. package/src/components/editor/renderers/vertical-layout/__tests__/useFocusFirstEditor.test.ts +4 -4
  327. package/src/components/editor/renderers/vertical-layout/sidebar/toggle.tsx +7 -1
  328. package/src/components/editor/renderers/vertical-layout/useFocusFirstEditor.ts +1 -1
  329. package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +1 -1
  330. package/src/components/editor/stdin-blocking-alert.tsx +1 -1
  331. package/src/components/home/components.tsx +17 -31
  332. package/src/components/icons/__tests__/large-spinner.test.tsx +39 -0
  333. package/src/components/icons/large-spinner.tsx +3 -14
  334. package/src/components/icons/spinner.tsx +1 -1
  335. package/src/components/markdown/markdown-renderer.tsx +4 -8
  336. package/src/components/mcp/mcp-status-indicator.tsx +1 -1
  337. package/src/components/pages/__tests__/home-page.test.tsx +116 -0
  338. package/src/components/pages/edit-page.tsx +19 -17
  339. package/src/components/pages/home-page.tsx +164 -91
  340. package/src/components/scratchpad/scratchpad.test.tsx +97 -0
  341. package/src/components/scratchpad/scratchpad.tsx +20 -11
  342. package/src/components/slides/__tests__/minimap-actions.test.tsx +22 -29
  343. package/src/components/slides/__tests__/minimap.test.ts +38 -19
  344. package/src/components/slides/__tests__/slide-scroll-hint.test.tsx +37 -0
  345. package/src/components/slides/minimap.tsx +17 -12
  346. package/src/components/slides/reveal-component.tsx +58 -26
  347. package/src/components/slides/reveal-slides.css +5 -4
  348. package/src/components/slides/slide-cell-view.tsx +4 -5
  349. package/src/components/slides/slide-form.tsx +1 -0
  350. package/src/components/slides/slide-scroll-hint.tsx +3 -1
  351. package/src/components/sort/SortableCellsProvider.tsx +8 -25
  352. package/src/components/tracing/tracing-keyboard.test.tsx +85 -0
  353. package/src/components/tracing/tracing.tsx +46 -42
  354. package/src/components/ui/__tests__/draggable-popover.test.tsx +24 -0
  355. package/src/components/ui/__tests__/range-slider.test.tsx +165 -0
  356. package/src/components/ui/alert-dialog.tsx +1 -1
  357. package/src/components/ui/draggable-popover.tsx +24 -2
  358. package/src/components/ui/range-slider.tsx +22 -9
  359. package/src/core/MarimoApp.tsx +1 -1
  360. package/src/core/ai/__tests__/staged-cells.test.ts +27 -26
  361. package/src/core/ai/context/providers/__tests__/cell-output.test.ts +8 -12
  362. package/src/core/ai/context/providers/cell-output.ts +2 -2
  363. package/src/core/ai/context/providers/datasource.ts +6 -2
  364. package/src/core/ai/staged-cells.ts +15 -18
  365. package/src/core/ai/tools/__tests__/edit-notebook-tool.test.ts +40 -88
  366. package/src/core/ai/tools/__tests__/registry.test.ts +17 -0
  367. package/src/core/ai/tools/base.ts +18 -15
  368. package/src/core/ai/tools/edit-notebook-tool.ts +86 -80
  369. package/src/core/ai/tools/registry.ts +13 -10
  370. package/src/core/ai/tools/sample-tool.ts +6 -7
  371. package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +4 -37
  372. package/src/core/cells/__tests__/actions.test.ts +624 -35
  373. package/src/core/cells/__tests__/add-missing-import.test.ts +7 -8
  374. package/src/core/cells/__tests__/arrange.test.ts +432 -0
  375. package/src/core/cells/__tests__/cell.test.ts +39 -0
  376. package/src/core/cells/__tests__/cells.test.ts +218 -2491
  377. package/src/core/cells/__tests__/collapseConsoleOutputs.test.ts +54 -0
  378. package/src/core/cells/__tests__/document.test.ts +341 -0
  379. package/src/core/cells/__tests__/editor.test.ts +57 -0
  380. package/src/core/cells/__tests__/flush-document.test.ts +40 -0
  381. package/src/core/cells/__tests__/harness.ts +106 -0
  382. package/src/core/cells/__tests__/layout.test.ts +177 -0
  383. package/src/core/cells/__tests__/notebook-outline.test.ts +120 -0
  384. package/src/core/cells/__tests__/outputs.test.tsx +71 -53
  385. package/src/core/cells/__tests__/session.test.ts +445 -211
  386. package/src/core/cells/__tests__/utils.test.ts +9 -108
  387. package/src/core/cells/__tests__/with-document-save.test.ts +67 -0
  388. package/src/core/cells/actions.ts +636 -6
  389. package/src/core/cells/add-missing-import.ts +47 -43
  390. package/src/core/cells/arrange.ts +529 -0
  391. package/src/core/cells/cell.ts +14 -3
  392. package/src/core/cells/cells.ts +120 -1337
  393. package/src/core/cells/columns.ts +38 -0
  394. package/src/core/cells/document.ts +289 -0
  395. package/src/core/cells/edit.ts +30 -0
  396. package/src/core/cells/editor.ts +82 -0
  397. package/src/core/cells/layout.ts +221 -0
  398. package/src/core/cells/outputs.ts +12 -28
  399. package/src/core/cells/pending-delete-service.ts +3 -7
  400. package/src/core/cells/session.ts +76 -53
  401. package/src/core/cells/types.ts +22 -6
  402. package/src/core/cells/utils.ts +10 -8
  403. package/src/core/codemirror/__tests__/setup.test.ts +29 -29
  404. package/src/core/codemirror/ai/__tests__/resources.test.ts +117 -0
  405. package/src/core/codemirror/ai/resources.ts +1 -1
  406. package/src/core/codemirror/cells/__tests__/extensions.test.ts +42 -1
  407. package/src/core/codemirror/cells/extensions.ts +27 -9
  408. package/src/core/codemirror/cells/state.ts +1 -1
  409. package/src/core/codemirror/copilot/__tests__/getCodes.test.ts +6 -7
  410. package/src/core/codemirror/copilot/getCodes.ts +2 -2
  411. package/src/core/codemirror/format.ts +6 -5
  412. package/src/core/codemirror/keymaps/vim.ts +4 -8
  413. package/src/core/codemirror/language/__tests__/sql.test.ts +22 -20
  414. package/src/core/codemirror/language/__tests__/utils.test.ts +187 -15
  415. package/src/core/codemirror/language/cell-code.ts +114 -0
  416. package/src/core/codemirror/language/extension.ts +52 -35
  417. package/src/core/codemirror/language/languages/markdown.ts +8 -6
  418. package/src/core/codemirror/language/languages/python.ts +16 -10
  419. package/src/core/codemirror/language/languages/sql/sql.ts +16 -24
  420. package/src/core/codemirror/language/panel/panel.tsx +1 -0
  421. package/src/core/codemirror/language/types.ts +7 -7
  422. package/src/core/codemirror/language/utils.ts +9 -39
  423. package/src/core/codemirror/lsp/__tests__/notebook-lsp.test.ts +1 -2
  424. package/src/core/codemirror/lsp/notebook-lsp.ts +6 -5
  425. package/src/core/codemirror/lsp/utils.ts +1 -1
  426. package/src/core/codemirror/readonly/__tests__/extension.test.ts +1 -1
  427. package/src/core/codemirror/readonly/extension.ts +2 -2
  428. package/src/core/codemirror/rtc/extension.ts +24 -13
  429. package/src/core/codemirror/rtc/loro/awareness.ts +34 -19
  430. package/src/core/codemirror/rtc/loro/sync.ts +1 -0
  431. package/src/core/codemirror/utils.ts +1 -0
  432. package/src/core/config/__tests__/app-config.test.ts +64 -0
  433. package/src/core/config/__tests__/config-schema.test.ts +13 -6
  434. package/src/core/config/config-schema.ts +13 -3
  435. package/src/core/config/config.ts +26 -12
  436. package/src/core/datasets/__tests__/data-source-discovery.test.ts +5 -5
  437. package/src/core/datasets/data-source-discovery.ts +4 -6
  438. package/src/core/documentation/doc-lookup.ts +1 -1
  439. package/src/core/edit-app.tsx +21 -28
  440. package/src/core/errors/error-entries.ts +4 -1
  441. package/src/core/export/hooks.ts +1 -1
  442. package/src/core/export/layout.ts +2 -2
  443. package/src/core/hotkeys/actions.ts +2 -4
  444. package/src/core/islands/__tests__/bootstrap.test.ts +69 -43
  445. package/src/core/islands/__tests__/bridge.test.ts +72 -25
  446. package/src/core/islands/bootstrap.ts +46 -54
  447. package/src/core/islands/bridge.ts +74 -23
  448. package/src/core/islands/components/__tests__/IslandControls.test.tsx +7 -5
  449. package/src/core/islands/components/web-components.tsx +1 -1
  450. package/src/core/islands/worker/__tests__/controller.test.ts +62 -4
  451. package/src/core/islands/worker/controller.ts +4 -10
  452. package/src/core/islands/worker/worker.tsx +33 -33
  453. package/src/core/kernel/__tests__/RuntimeState.test.ts +1 -1
  454. package/src/core/kernel/__tests__/handlers.test.ts +7 -153
  455. package/src/core/kernel/handlers.ts +19 -72
  456. package/src/core/meta/__tests__/code-visibility.test.tsx +78 -70
  457. package/src/core/meta/code-visibility.ts +18 -9
  458. package/src/core/network/__tests__/connection.test.ts +1 -1
  459. package/src/core/network/__tests__/requests-lazy.test.ts +1 -3
  460. package/src/core/network/connection-notice.ts +1 -1
  461. package/src/core/network/connection.ts +1 -1
  462. package/src/core/network/requests-lazy.ts +1 -4
  463. package/src/core/network/requests-network.ts +0 -25
  464. package/src/core/network/requests-static.ts +0 -3
  465. package/src/core/network/requests-toasting.tsx +0 -3
  466. package/src/core/network/types.ts +0 -12
  467. package/src/core/network/useConnectionNotice.ts +8 -2
  468. package/src/core/notebook/__tests__/operations.test.ts +51 -0
  469. package/src/core/notebook/attachments.ts +38 -0
  470. package/src/core/notebook/document/__tests__/codemirror.test.ts +586 -0
  471. package/src/core/notebook/document/__tests__/draft.test.ts +363 -0
  472. package/src/core/notebook/document/__tests__/fixtures.ts +92 -0
  473. package/src/core/notebook/document/__tests__/hold.test.ts +88 -0
  474. package/src/core/notebook/document/__tests__/hooks.test.tsx +391 -0
  475. package/src/core/notebook/document/__tests__/lifecycle-scenarios.test.ts +61 -0
  476. package/src/core/notebook/document/__tests__/model.test.ts +247 -0
  477. package/src/core/notebook/document/__tests__/protocol-fixtures.ts +38 -0
  478. package/src/core/notebook/document/__tests__/recovery-scenarios.test.ts +230 -0
  479. package/src/core/notebook/document/__tests__/scenario-fixture.ts +43 -0
  480. package/src/core/notebook/document/__tests__/scenarios.test.ts +205 -0
  481. package/src/core/notebook/document/__tests__/state.test.ts +459 -0
  482. package/src/core/notebook/document/__tests__/submission.test.ts +203 -0
  483. package/src/core/notebook/document/__tests__/test-host.test.ts +92 -0
  484. package/src/core/notebook/document/__tests__/test-host.ts +247 -0
  485. package/src/core/notebook/document/__tests__/test-notebook.ts +149 -0
  486. package/src/core/notebook/document/__tests__/user.ts +61 -0
  487. package/src/core/notebook/document/codemirror.ts +216 -0
  488. package/src/core/notebook/document/draft.ts +502 -0
  489. package/src/core/notebook/document/hooks.tsx +118 -0
  490. package/src/core/notebook/document/index.ts +24 -0
  491. package/src/core/notebook/document/model.ts +105 -0
  492. package/src/core/notebook/document/select.ts +41 -0
  493. package/src/core/notebook/document/state.ts +378 -0
  494. package/src/core/notebook/document/sync.ts +418 -0
  495. package/src/core/notebook/index.ts +19 -0
  496. package/src/core/notebook/operations.ts +30 -0
  497. package/src/core/notebook/replica/AGENTS.md +11 -0
  498. package/src/core/notebook/replica/__tests__/attachments.test.ts +56 -0
  499. package/src/core/{host → notebook/replica}/__tests__/document.test.ts +18 -16
  500. package/src/core/notebook/replica/__tests__/fixtures.ts +79 -0
  501. package/src/core/notebook/replica/__tests__/replica.test.ts +550 -0
  502. package/src/core/notebook/replica/backend.ts +19 -0
  503. package/src/core/notebook/replica/connection.ts +42 -0
  504. package/src/core/notebook/replica/index.ts +13 -0
  505. package/src/core/notebook/replica/replica.ts +286 -0
  506. package/src/core/notebook/replica/resources/attachments.ts +22 -0
  507. package/src/core/{host → notebook/replica}/resources/document.ts +4 -4
  508. package/src/core/packages/__tests__/useSandboxController.test.tsx +1 -1
  509. package/src/core/packages/useSandboxController.ts +1 -1
  510. package/src/core/run-app.tsx +48 -51
  511. package/src/core/runtime/__tests__/adapter.test.ts +1 -1
  512. package/src/core/runtime/__tests__/createWsUrl.test.ts +77 -7
  513. package/src/core/runtime/__tests__/runtime.test.ts +32 -58
  514. package/src/core/runtime/adapter.ts +1 -1
  515. package/src/core/runtime/config.ts +2 -2
  516. package/src/core/runtime/runtime.ts +38 -42
  517. package/src/core/runtime/types.ts +0 -2
  518. package/src/core/saving/__tests__/state.test.ts +111 -0
  519. package/src/core/saving/__tests__/useAutoSave.test.tsx +67 -0
  520. package/src/core/saving/filename.ts +1 -1
  521. package/src/core/saving/save-component.tsx +28 -36
  522. package/src/core/saving/state.ts +38 -39
  523. package/src/core/saving/useAutoSave.ts +17 -53
  524. package/src/core/session/__tests__/backend.test.ts +236 -0
  525. package/src/core/session/__tests__/useNotebookReplica.hook.test.tsx +184 -0
  526. package/src/core/session/__tests__/useNotebookSession.hook.test.tsx +663 -0
  527. package/src/core/session/active-notebook.ts +22 -0
  528. package/src/core/session/backend.ts +123 -0
  529. package/src/core/session/embedded.ts +7 -0
  530. package/src/core/{websocket → session}/types.ts +2 -2
  531. package/src/core/session/useNotebookReplica.ts +74 -0
  532. package/src/core/{websocket → session}/useNotebookSession.tsx +77 -72
  533. package/src/core/state/__tests__/jotai.test.tsx +108 -0
  534. package/src/core/state/jotai.ts +1 -1
  535. package/src/core/static/__tests__/files.test.ts +38 -10
  536. package/src/core/static/files.ts +2 -0
  537. package/src/core/{websocket → transports}/__tests__/connection-state.test.ts +1 -13
  538. package/src/core/transports/__tests__/server.test.ts +408 -0
  539. package/src/core/transports/__tests__/static.test.ts +306 -0
  540. package/src/core/transports/__tests__/wasm.test.ts +166 -0
  541. package/src/core/{websocket → transports}/connection-state.ts +9 -16
  542. package/src/core/transports/server.ts +152 -0
  543. package/src/core/transports/static.ts +166 -0
  544. package/src/core/transports/wasm.ts +97 -0
  545. package/src/core/wasm/__tests__/bridge.test.ts +214 -50
  546. package/src/core/wasm/__tests__/worker.test.ts +187 -0
  547. package/src/core/wasm/bridge.ts +75 -69
  548. package/src/core/wasm/rpc.ts +15 -3
  549. package/src/core/wasm/store.ts +3 -3
  550. package/src/core/wasm/worker/bootstrap.ts +55 -51
  551. package/src/core/wasm/worker/getMarimoWheel.ts +1 -1
  552. package/src/core/wasm/worker/save-worker.ts +9 -10
  553. package/src/core/wasm/worker/types.ts +29 -18
  554. package/src/core/wasm/worker/worker.ts +88 -44
  555. package/src/css/globals.css +66 -22
  556. package/src/hooks/__tests__/useAsyncData.test.tsx +148 -0
  557. package/src/hooks/__tests__/useDebounce.test.tsx +51 -0
  558. package/src/hooks/__tests__/useDeepCompareMemoize.test.tsx +39 -0
  559. package/src/hooks/__tests__/useDelayElapsed.test.tsx +28 -0
  560. package/src/hooks/__tests__/useInternalStateWithSync.test.tsx +20 -0
  561. package/src/hooks/__tests__/useScript.test.tsx +77 -0
  562. package/src/hooks/__tests__/useStateForKey.test.tsx +257 -0
  563. package/src/hooks/useAsyncData.ts +3 -3
  564. package/src/hooks/useDataSourceDiscovery.ts +6 -4
  565. package/src/hooks/useDebounce.ts +6 -10
  566. package/src/hooks/useDeepCompareMemoize.ts +1 -1
  567. package/src/hooks/useDelayElapsed.ts +14 -10
  568. package/src/hooks/useElapsedTime.ts +3 -3
  569. package/src/hooks/useEventListener.ts +1 -2
  570. package/src/hooks/useInputHistory.ts +4 -7
  571. package/src/hooks/useInternalStateWithSync.ts +6 -6
  572. package/src/hooks/useResizeObserver.ts +4 -7
  573. package/src/hooks/useScript.ts +67 -70
  574. package/src/hooks/useStateForKey.ts +93 -0
  575. package/src/mount.tsx +20 -15
  576. package/src/plugins/__tests__/plugin-object-schemas.test.ts +104 -0
  577. package/src/plugins/core/registerReactComponent.tsx +1 -1
  578. package/src/plugins/impl/CodeEditorPlugin.tsx +1 -1
  579. package/src/plugins/impl/DataEditorPlugin.tsx +1 -1
  580. package/src/plugins/impl/DataTablePlugin.tsx +69 -37
  581. package/src/plugins/impl/DropdownPlugin.tsx +2 -6
  582. package/src/plugins/impl/FileBrowserPlugin.tsx +4 -16
  583. package/src/plugins/impl/FormPlugin.tsx +1 -1
  584. package/src/plugins/impl/RangeSliderPlugin.tsx +16 -2
  585. package/src/plugins/impl/RefreshPlugin.tsx +1 -0
  586. package/src/plugins/impl/SliderPlugin.tsx +6 -8
  587. package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +387 -1
  588. package/src/plugins/impl/__tests__/FileBrowserPlugin.test.tsx +45 -0
  589. package/src/plugins/impl/__tests__/RangeSliderPlugin.test.tsx +109 -0
  590. package/src/plugins/impl/__tests__/SliderPlugin.test.tsx +24 -3
  591. package/src/plugins/impl/anywidget/__tests__/registry.test.ts +153 -0
  592. package/src/plugins/impl/anywidget/__tests__/widget-binding.test.ts +241 -2
  593. package/src/plugins/impl/anywidget/model.ts +3 -1
  594. package/src/plugins/impl/anywidget/registry.ts +18 -14
  595. package/src/plugins/impl/anywidget/runtime.ts +21 -9
  596. package/src/plugins/impl/anywidget/widget-binding.ts +160 -82
  597. package/src/plugins/impl/chat/__tests__/chat-prompts.test.tsx +93 -0
  598. package/src/plugins/impl/chat/chat-ui.tsx +14 -11
  599. package/src/plugins/impl/code/any-language-editor.tsx +2 -2
  600. package/src/plugins/impl/data-editor/glide-data-editor.tsx +8 -5
  601. package/src/plugins/impl/data-editor/glide-utils.ts +3 -1
  602. package/src/plugins/impl/data-explorer/ConnectedDataExplorerComponent.tsx +1 -1
  603. package/src/plugins/impl/data-explorer/components/column-summary.tsx +4 -2
  604. package/src/plugins/impl/data-explorer/state/reducer.ts +1 -1
  605. package/src/plugins/impl/data-frames/DataFramePlugin.tsx +1 -2
  606. package/src/plugins/impl/data-frames/__tests__/sidebar.test.tsx +160 -0
  607. package/src/plugins/impl/data-frames/forms/__tests__/__snapshots__/form.test.tsx.snap +66 -66
  608. package/src/plugins/impl/data-frames/panel.tsx +43 -16
  609. package/src/plugins/impl/mpl-interactive/MplInteractivePlugin.tsx +5 -3
  610. package/src/plugins/impl/panel/PanelPlugin.tsx +16 -17
  611. package/src/plugins/impl/panel/__tests__/utils.test.ts +39 -1
  612. package/src/plugins/impl/panel/utils.ts +17 -0
  613. package/src/plugins/impl/plotly/PlotlyPlugin.tsx +2 -5
  614. package/src/plugins/impl/plotly/selection.ts +25 -19
  615. package/src/plugins/impl/vega/VegaPlugin.tsx +2 -3
  616. package/src/plugins/impl/vega/make-selectable.ts +4 -7
  617. package/src/plugins/layout/RoutesPlugin.tsx +1 -1
  618. package/src/stories/__tests__/editor-story.test.tsx +33 -0
  619. package/src/stories/button.stories.tsx +1 -0
  620. package/src/stories/cell.stories.tsx +2 -5
  621. package/src/stories/data-table.stories.tsx +31 -0
  622. package/src/stories/editor.stories.tsx +24 -17
  623. package/src/stories/form-wrapper.stories.tsx +2 -2
  624. package/src/stories/layout/vertical/one-column.stories.tsx +2 -5
  625. package/src/stories/runtime-story.tsx +23 -17
  626. package/src/stories/select.stories.tsx +2 -2
  627. package/src/stories/switchable-multi-select.stories.tsx +1 -1
  628. package/src/utils/__tests__/arrays.test.ts +35 -0
  629. package/src/utils/__tests__/batch-requests.test.ts +3 -3
  630. package/src/utils/__tests__/{createReducer.test.ts → createReducer.test.tsx} +29 -0
  631. package/src/utils/__tests__/numbers.test.ts +105 -1
  632. package/src/utils/__tests__/objects.test.ts +21 -0
  633. package/src/utils/arrays.ts +11 -2
  634. package/src/utils/blob.ts +1 -1
  635. package/src/utils/createReducer.ts +53 -26
  636. package/src/utils/maps.ts +0 -3
  637. package/src/utils/numbers.ts +79 -18
  638. package/src/utils/objects.ts +4 -9
  639. package/src/utils/once.ts +19 -19
  640. package/src/utils/tracer.ts +9 -5
  641. package/src/utils/zod-utils.ts +1 -3
  642. package/dist/any-language-editor-DZ_YTVus.js +0 -76
  643. package/dist/assets/__vite-browser-external-BXYkiFsk.js +0 -1
  644. package/dist/assets/worker-HPY9fbhz.js +0 -114
  645. package/dist/code-visibility-DkCqn-PO.js +0 -138
  646. package/dist/feedback-button-wnAMXOGA.js +0 -379
  647. package/dist/mermaid-HWGCJPDP-ByCsZnYF.js +0 -2
  648. package/dist/precisionRound-DMIVQrt2.js +0 -97
  649. package/src/components/editor/navigation/__tests__/state.test.ts +0 -81
  650. package/src/components/editor/navigation/state.ts +0 -65
  651. package/src/core/ai/tools/__tests__/run-cells-tool.test.ts +0 -656
  652. package/src/core/ai/tools/run-cells-tool.ts +0 -303
  653. package/src/core/attachments/state.ts +0 -21
  654. package/src/core/cells/__tests__/apply-transaction.test.ts +0 -991
  655. package/src/core/cells/__tests__/document-changes.test.ts +0 -1140
  656. package/src/core/cells/__tests__/document-roundtrip.test.ts +0 -392
  657. package/src/core/cells/document-changes.ts +0 -944
  658. package/src/core/host/AGENTS.md +0 -11
  659. package/src/core/host/__tests__/events.test.ts +0 -46
  660. package/src/core/host/__tests__/fixtures.ts +0 -39
  661. package/src/core/host/__tests__/notebook-client.test.ts +0 -303
  662. package/src/core/host/__tests__/protocol.test.ts +0 -214
  663. package/src/core/host/__tests__/stream.test.ts +0 -211
  664. package/src/core/host/connection.ts +0 -81
  665. package/src/core/host/events.ts +0 -60
  666. package/src/core/host/notebook-client.ts +0 -272
  667. package/src/core/host/protocol.ts +0 -1943
  668. package/src/core/host/resources/resource.ts +0 -59
  669. package/src/core/host/stream.ts +0 -213
  670. package/src/core/state/__tests__/jotai.test.ts +0 -25
  671. package/src/core/wasm/worker/message-buffer.ts +0 -33
  672. package/src/core/websocket/__tests__/createConnectionTransport.test.ts +0 -49
  673. package/src/core/websocket/__tests__/useNotebookClient.hook.test.tsx +0 -140
  674. package/src/core/websocket/__tests__/useNotebookSession.hook.test.tsx +0 -883
  675. package/src/core/websocket/transports/__tests__/basic.test.ts +0 -275
  676. package/src/core/websocket/transports/__tests__/sse.test.ts +0 -335
  677. package/src/core/websocket/transports/__tests__/transport.test.ts +0 -308
  678. package/src/core/websocket/transports/__tests__/ws.test.ts +0 -125
  679. package/src/core/websocket/transports/basic.ts +0 -102
  680. package/src/core/websocket/transports/create.ts +0 -31
  681. package/src/core/websocket/transports/sse.ts +0 -308
  682. package/src/core/websocket/transports/transport.ts +0 -50
  683. package/src/core/websocket/transports/ws.ts +0 -98
  684. package/src/core/websocket/useNotebookClient.ts +0 -80
  685. package/src/utils/__tests__/id-tree.test.ts +0 -2145
  686. package/src/utils/id-tree.tsx +0 -1134
  687. /package/src/{core/attachments/toast.ts → components/editor/agent-toast.ts} +0 -0
  688. /package/src/core/{websocket → session}/connection-utils.ts +0 -0
@@ -1,52 +1,39 @@
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";
10
- import {
11
- type CellColumnId,
12
- type CellIndex,
13
- MultiColumn,
14
- } from "@/utils/id-tree";
8
+ import type { EditableDocument, Placement } from "@/core/notebook/document";
9
+ import type { CellColumnId } from "./layout";
15
10
  import { invariant } from "@/utils/invariant";
16
11
  import { Logger } from "@/utils/Logger";
17
- import { clamp } from "@/utils/math";
18
12
  import { Objects } from "@/utils/objects";
19
13
  import { extractAllTracebackInfo, type TracebackInfo } from "@/utils/traceback";
20
14
  import { createReducerAndAtoms } from "../../utils/createReducer";
21
15
  import { foldAllBulk, unfoldAllBulk } from "../codemirror/editing/commands";
22
16
  import { editorMountScheduler } from "../codemirror/editor-mount-scheduler";
23
- import {
24
- splitEditor,
25
- updateEditorCodeFromPython,
26
- } from "../codemirror/language/utils";
27
17
  import type { SerializedEditorState } from "../codemirror/types";
28
- import { findCollapseRange, mergeOutlines } from "../dom/outline";
18
+ import { mergeOutlines } from "../dom/outline";
29
19
  import type { CellMessage } from "../kernel/messages";
30
20
  import { isErrorMime } from "../mime";
31
21
  import type { CellConfig } from "../network/types";
32
- import { canUseRtc, isRtcEnabled } from "../rtc/state";
33
22
  import { createDeepEqualAtom, store } from "../state/jotai";
34
23
  import { isWasm } from "../wasm/utils";
35
24
  import { prepareCellForExecution, transitionCell } from "./cell";
36
- import { documentTransactionMiddleware } from "./document-changes";
37
- import { CellId, SCRATCH_CELL_ID, SETUP_CELL_ID } from "./ids";
25
+ import { applyDocument, documentEditMiddleware } from "./document";
26
+ import { isCodeHidden, shownHiddenCodeAtom } from "./editor";
27
+ import { type CellId, SCRATCH_CELL_ID } from "./ids";
38
28
  import { type CellLog, getCellLogsForMessage } from "./logs";
39
- import {
40
- focusAndScrollCellIntoView,
41
- scrollToBottom,
42
- scrollToTop,
43
- } from "./scrollCellIntoView";
29
+ import type { Outline } from "./outline";
30
+ import { focusAndScrollCellIntoView } from "./scrollCellIntoView";
44
31
  import {
45
32
  type CellData,
46
33
  type CellRuntimeState,
47
34
  createCell,
48
- createCellConfig,
49
35
  createCellRuntimeState,
36
+ isEdited,
50
37
  } from "./types";
51
38
  import {
52
39
  canUndoDeletes,
@@ -58,17 +45,21 @@ import {
58
45
  notebookQueueOrRunningCount,
59
46
  } from "./utils";
60
47
 
48
+ /** A deleted cell as it was, with where it went in the document. */
49
+ export interface DeletedCell {
50
+ cell: Protocol.CellInput;
51
+ placement: Placement;
52
+ /** The editor's state at deletion, so undo brings its history back. */
53
+ serializedEditorState: SerializedEditorState | null;
54
+ }
55
+
61
56
  /**
62
- * History entry for undoing a cell deletion.
57
+ * History entry for undoing one deletion, of one or more cells.
63
58
  */
64
59
  export interface UndoDeleteEntry {
65
60
  type: "delete";
66
- name: string;
67
- serializedEditorState: SerializedEditorState;
68
- column: CellColumnId;
69
- index: CellIndex;
70
- isSetupCell: boolean;
71
- config: CellConfig;
61
+ /** In document order. Each cell is placed after the one before it. */
62
+ cells: DeletedCell[];
72
63
  }
73
64
 
74
65
  /**
@@ -77,7 +68,7 @@ export interface UndoDeleteEntry {
77
68
  export interface UndoMoveEntry {
78
69
  type: "move";
79
70
  cellIds: CellId[];
80
- placements: Array<{ columnId: CellColumnId; index: CellIndex }>;
71
+ placements: Array<{ columnId: CellColumnId; index: number }>;
81
72
  }
82
73
 
83
74
  export type HistoryEntry = UndoDeleteEntry | UndoMoveEntry;
@@ -89,7 +80,8 @@ export interface NotebookState {
89
80
  /**
90
81
  * Order of cells on the page.
91
82
  */
92
- cellIds: MultiColumn<CellId>;
83
+ /** The cells in document order. */
84
+ cellIds: CellId[];
93
85
  /**
94
86
  * Map of cells to their view state
95
87
  */
@@ -116,12 +108,6 @@ export interface NotebookState {
116
108
  * Logs of all cell messages
117
109
  */
118
110
  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
111
  }
126
112
 
127
113
  function withScratchCell(notebookState: NotebookState): NotebookState {
@@ -153,51 +139,16 @@ function withScratchCell(notebookState: NotebookState): NotebookState {
153
139
  */
154
140
  export function initialNotebookState(): NotebookState {
155
141
  return withScratchCell({
156
- cellIds: MultiColumn.from([]),
142
+ cellIds: [],
157
143
  cellData: {},
158
144
  cellRuntime: {},
159
145
  cellHandles: {},
160
146
  history: [],
161
147
  scrollKey: null,
162
148
  cellLogs: [],
163
- untouchedNewCells: new Set<CellId>(),
164
149
  });
165
150
  }
166
151
 
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
152
  /**
202
153
  * Actions and reducer for the notebook state.
203
154
  */
@@ -208,616 +159,11 @@ const {
208
159
  useActions,
209
160
  valueAtom: notebookAtom,
210
161
  } = 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
- },
162
+ /**
163
+ * Put moved cells back where they were.
164
+ *
165
+ * TODO(MO-8028): Move to `undoDeleteCellAtom` with the order family.
166
+ */
821
167
  updateCellCode: (
822
168
  state,
823
169
  action: {
@@ -855,35 +201,6 @@ const {
855
201
  },
856
202
  });
857
203
  },
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
204
  prepareForRun: (state, action: { cellId: CellId }) => {
888
205
  const newState = updateCellRuntimeState({
889
206
  state,
@@ -939,158 +256,14 @@ const {
939
256
  cellLogs: [...state.cellLogs, ...action.logs],
940
257
  };
941
258
  },
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();
259
+ /** Follow the host document's cells and order. */
260
+ reconcileDocument: (state, document: EditableDocument) => {
261
+ const nextState = applyDocument(state, document);
262
+ for (const cellId of state.cellIds) {
263
+ if (!(cellId in nextState.cellData)) {
264
+ releaseCellAtoms(cellId);
962
265
  }
963
266
  }
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;
1076
- }
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
- }
1093
-
1094
267
  return nextState;
1095
268
  },
1096
269
  setStdinResponse: (
@@ -1114,6 +287,7 @@ const {
1114
287
  mimetype: stdinOutput.mimetype,
1115
288
  data: stdinOutput.data,
1116
289
  timestamp: stdinOutput.timestamp,
290
+ id: stdinOutput.id,
1117
291
  // An empty response resolves the prompt without retaining the secret.
1118
292
  response: stdinOutput.mimetype === "text/password" ? "" : response,
1119
293
  };
@@ -1126,155 +300,44 @@ const {
1126
300
  },
1127
301
  });
1128
302
  },
1129
- setCells: (state, cells: CellData[]) => {
1130
- const cellData = Object.fromEntries(cells.map((cell) => [cell.id, cell]));
1131
-
1132
- // WASM has no server-side SessionView to replay outputs, so the
1133
- // snapshot hydrated by notebookStateFromSession is the only source.
1134
- const preserveSnapshot = isWasm();
1135
- const runtimeFor = (cellId: CellId): CellRuntimeState => {
1136
- if (!preserveSnapshot) {
1137
- return createCellRuntimeState();
1138
- }
1139
- const prev = state.cellRuntime[cellId];
1140
- const hasSnapshot =
1141
- prev && (prev.output != null || prev.consoleOutputs.length > 0);
1142
- return hasSnapshot ? prev : createCellRuntimeState();
1143
- };
1144
- const cellRuntime = Object.fromEntries(
1145
- cells.map((cell) => [cell.id, runtimeFor(cell.id)]),
1146
- );
1147
-
1148
- return withScratchCell({
1149
- ...state,
1150
- cellIds: MultiColumn.fromIdsAndColumns(
1151
- cells.map((cell) => [cell.id, cell.config.column]),
1152
- ),
1153
- cellData: cellData,
1154
- cellRuntime: cellRuntime,
1155
- cellHandles: Object.fromEntries(
1156
- cells.map((cell) => [cell.id, createRef()]),
1157
- ),
1158
- });
1159
- },
1160
- /**
1161
- * Move focus to next cell
1162
- *
1163
- * Creates a new cell if the current cell is the last one in the array.
1164
- *
1165
- * If needed, scrolls newly created or focused cell into view.
1166
- *
1167
- * Replicates Shift+Enter functionality of Jupyter
1168
- */
1169
- moveToNextCell: (
303
+ /** Record what the kernel last ran for each cell. */
304
+ setLastRun: (
1170
305
  state,
1171
- action: { cellId: CellId; before: boolean; noCreate?: boolean },
306
+ action: {
307
+ lastCodeRun: Record<CellId, string>;
308
+ lastExecutionTime: Record<CellId, number>;
309
+ },
1172
310
  ) => {
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,
311
+ const cellData = { ...state.cellData };
312
+ for (const cellId of state.cellIds) {
313
+ const cell = state.cellData[cellId];
314
+ const lastCodeRun = action.lastCodeRun[cellId] ?? null;
315
+ cellData[cellId] = {
316
+ ...cell,
317
+ lastCodeRun,
318
+ lastExecutionTime: action.lastExecutionTime[cellId] ?? null,
319
+ // A cell the kernel never ran is not stale on load.
320
+ edited: lastCodeRun !== null && isEdited(lastCodeRun, cell.code),
1260
321
  };
1261
322
  }
1262
-
1263
- return state;
323
+ return { ...state, cellData };
1264
324
  },
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
- };
325
+ /**
326
+ * Start each cell's runtime over for a new kernel. WASM keeps the outputs
327
+ * its mount-time snapshot gave it.
328
+ */
329
+ resetCellRuntimes: (state) => {
330
+ const preserveSnapshot = isWasm();
331
+ const cellRuntime = { ...state.cellRuntime };
332
+ for (const cellId of [...state.cellIds, SCRATCH_CELL_ID]) {
333
+ const previous = state.cellRuntime[cellId];
334
+ const hasSnapshot =
335
+ preserveSnapshot &&
336
+ previous !== undefined &&
337
+ (previous.output != null || previous.consoleOutputs.length > 0);
338
+ cellRuntime[cellId] = hasSnapshot ? previous : createCellRuntimeState();
1275
339
  }
1276
-
1277
- return state;
340
+ return { ...state, cellRuntime };
1278
341
  },
1279
342
  scrollToTarget: (state) => {
1280
343
  // Scroll to the specified cell and clear the scroll key.
@@ -1283,16 +346,15 @@ const {
1283
346
  return state;
1284
347
  }
1285
348
 
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
-
349
+ const cellId = scrollKey;
1292
350
  focusAndScrollCellIntoView({
1293
351
  cellId: cellId,
1294
352
  cell: state.cellHandles[cellId],
1295
- isCodeHidden: isCellCodeHidden(state, cellId),
353
+ isCodeHidden: isCodeHidden(
354
+ state.cellData[cellId]?.config.hide_code,
355
+ store.get(shownHiddenCodeAtom),
356
+ cellId,
357
+ ),
1296
358
  codeFocus: undefined,
1297
359
  variableName: undefined,
1298
360
  });
@@ -1322,201 +384,6 @@ const {
1322
384
  cellLogs: [],
1323
385
  };
1324
386
  },
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
387
  clearCellOutput: (state, action: { cellId: CellId }) => {
1521
388
  const { cellId } = action;
1522
389
  return updateCellRuntimeState({
@@ -1545,7 +412,7 @@ const {
1545
412
  },
1546
413
  clearAllCellOutputs: (state) => {
1547
414
  const newCellRuntime = { ...state.cellRuntime };
1548
- for (const cellId of state.cellIds.inOrderIds) {
415
+ for (const cellId of state.cellIds) {
1549
416
  newCellRuntime[cellId] = {
1550
417
  ...newCellRuntime[cellId],
1551
418
  output: null,
@@ -1557,57 +424,12 @@ const {
1557
424
  cellRuntime: newCellRuntime,
1558
425
  };
1559
426
  },
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
427
  });
1599
428
 
1600
429
  // We apply the middleware here (rather than inline in createReducerAndAtoms)
1601
- // so that the document transaction middleware can import CellActions and
430
+ // so that the document edit middleware can import CellActions and
1602
431
  // 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
- }
432
+ addMiddleware(documentEditMiddleware);
1611
433
 
1612
434
  // Helper function to update a cell in the array
1613
435
  function updateCellRuntimeState({
@@ -1656,43 +478,12 @@ function updateCellData({
1656
478
  };
1657
479
  }
1658
480
 
481
+ /** Each cell's config in order. The document's `column` configs are canonical. */
1659
482
  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
- });
483
+ return state.cellIds.map((cellId) => ({
484
+ ...state.cellData[cellId].config,
485
+ column: state.cellData[cellId].config.column ?? null,
486
+ }));
1696
487
  }
1697
488
 
1698
489
  export {
@@ -1712,9 +503,7 @@ export const addLogs = imperativeActions.addLogs;
1712
503
 
1713
504
  export const cellIdsAtom = atom((get) => get(notebookAtom).cellIds);
1714
505
 
1715
- export const hasOnlyOneCellAtom = atom((get) =>
1716
- get(cellIdsAtom).hasOnlyOneId(),
1717
- );
506
+ export const hasOnlyOneCellAtom = atom((get) => get(cellIdsAtom).length === 1);
1718
507
 
1719
508
  export const hasDisabledCellsAtom = atom(
1720
509
  (get) => disabledCellIds(get(notebookAtom)).length > 0,
@@ -1732,7 +521,7 @@ export const needsRunAtom = atom((get) => notebookNeedsRun(get(notebookAtom)));
1732
521
 
1733
522
  export const cellErrorsAtom = atom((get) => {
1734
523
  const { cellIds, cellRuntime, cellData } = get(notebookAtom);
1735
- const errors = cellIds.inOrderIds
524
+ const errors = cellIds
1736
525
  .map((cellId) => {
1737
526
  const cell = cellRuntime[cellId];
1738
527
  const { name } = cellData[cellId];
@@ -1760,11 +549,28 @@ export const cellErrorsAtom = atom((get) => {
1760
549
  return errors;
1761
550
  });
1762
551
 
552
+ /**
553
+ * The outline of each cell whose output has a heading. Compared by value, so
554
+ * readers are left alone by the runtime messages that change no heading.
555
+ */
556
+ export const cellOutlinesAtom = createDeepEqualAtom(
557
+ atom((get) => {
558
+ const outlines: Partial<Record<CellId, Outline>> = {};
559
+ for (const [id, runtime] of Objects.entries(
560
+ get(notebookAtom).cellRuntime,
561
+ )) {
562
+ if (runtime.outline !== null && runtime.outline.items.length > 0) {
563
+ outlines[id] = runtime.outline;
564
+ }
565
+ }
566
+ return outlines;
567
+ }),
568
+ );
569
+
1763
570
  export const notebookOutline = atom((get) => {
1764
- const { cellIds, cellRuntime } = get(notebookAtom);
1765
- const outlines = cellIds.inOrderIds.map(
1766
- (cellId) => cellRuntime[cellId].outline,
1767
- );
571
+ const cellIds = get(cellIdsAtom);
572
+ const cellRuntime = get(cellsRuntimeAtom);
573
+ const outlines = cellIds.map((cellId) => cellRuntime[cellId].outline);
1768
574
  return mergeOutlines(outlines);
1769
575
  });
1770
576
 
@@ -1774,7 +580,7 @@ export const cellIdToNamesMap = createDeepEqualAtom(
1774
580
  atom((get) => {
1775
581
  const { cellIds, cellData } = get(notebookAtom);
1776
582
  const names: Record<CellId, string | undefined> = Objects.fromEntries(
1777
- cellIds.inOrderIds.map((cellId) => [cellId, cellData[cellId]?.name]),
583
+ cellIds.map((cellId) => [cellId, cellData[cellId]?.name]),
1778
584
  );
1779
585
  return names;
1780
586
  }),
@@ -1831,12 +637,12 @@ export const getCells = () => store.get(notebookAtom).cellIds;
1831
637
  */
1832
638
  export const getCellNames = () => {
1833
639
  const { cellIds, cellData } = store.get(notebookAtom);
1834
- return cellIds.inOrderIds.map((id) => cellData[id]?.name).filter(Boolean);
640
+ return cellIds.map((id) => cellData[id]?.name).filter(Boolean);
1835
641
  };
1836
642
 
1837
643
  const cellDataAtoms = splitAtom(
1838
644
  selectAtom(notebookAtom, (cells) =>
1839
- cells.cellIds.inOrderIds.map((id) => cells.cellData[id]),
645
+ cells.cellIds.map((id) => cells.cellData[id]),
1840
646
  ),
1841
647
  );
1842
648
  export const useCellDataAtoms = () => useAtom(cellDataAtoms);
@@ -1856,16 +662,7 @@ export const notebookQueuedOrRunningCountAtom = atom((get) =>
1856
662
  notebookQueueOrRunningCount(get(notebookAtom)),
1857
663
  );
1858
664
 
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
-
665
+ export const hasCellsAtom = atom((get) => get(notebookAtom).cellIds.length > 0);
1869
666
  export const cellDataAtom = atomFamily((cellId: CellId) =>
1870
667
  atom((get) => get(notebookAtom).cellData[cellId]),
1871
668
  );
@@ -1906,7 +703,7 @@ export const useCellHandle = (cellId: CellId) =>
1906
703
  */
1907
704
  export const getAllEditorViews = () => {
1908
705
  const { cellIds, cellHandles } = store.get(notebookAtom);
1909
- return cellIds.inOrderIds
706
+ return cellIds
1910
707
  .map((cellId) => cellHandles[cellId]?.current?.editorViewOrNull)
1911
708
  .filter(Boolean);
1912
709
  };
@@ -1930,16 +727,10 @@ export function flattenTopLevelNotebookCells(
1930
727
  state: NotebookState,
1931
728
  ): (CellData & CellRuntimeState)[] {
1932
729
  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));
730
+ return cellIds.map((cellId) => ({
731
+ ...cellData[cellId],
732
+ ...cellRuntime[cellId],
733
+ }));
1943
734
  }
1944
735
 
1945
736
  export function createTracebackInfoAtom(
@@ -2001,20 +792,13 @@ export function createTracebackInfoAtom(
2001
792
  });
2002
793
  }
2003
794
 
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);
795
+ /** The reducer's actions. Components use `useCellActions` from `./actions`. */
796
+ export function useReducerActions(): ReducerCellActions {
797
+ return useActions();
2012
798
  }
2013
799
 
2014
- /**
2015
- * Map of cell actions
2016
- */
2017
- export type CellActions = ReturnType<typeof createActions>;
800
+ /** The actions the reducer still handles. */
801
+ export type ReducerCellActions = ReturnType<typeof createActions>;
2018
802
 
2019
803
  /**
2020
804
  * This is exported for testing purposes only.
@@ -2023,7 +807,6 @@ export const exportedForTesting = {
2023
807
  reducer,
2024
808
  createActions,
2025
809
  initialNotebookState,
2026
- isCellCodeHidden,
2027
810
  // Export atom families for testing cleanup
2028
811
  cellDataAtom,
2029
812
  cellRuntimeAtom,