@marimo-team/islands 0.25.3-dev2 → 0.25.3-dev200

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 (690) hide show
  1. package/dist/{ConnectedDataExplorerComponent-CNSxdKyJ.js → ConnectedDataExplorerComponent-BsWiIPFk.js} +22 -20
  2. package/dist/{ErrorBoundary-DWEpirf3.js → ErrorBoundary-UWARECs-.js} +19 -19
  3. package/dist/{abnfDiagram-VCTEODGH-DQDjNXt3.js → abnfDiagram-VCTEODGH-I2x4Yu25.js} +2 -2
  4. package/dist/any-language-editor-BVFHDDQ3.js +75 -0
  5. package/dist/{architectureDiagram-5GKGNRK7-Nmls7Wxn.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-BDpymeoe.js → chat-ui-DXDoSuS4.js} +667 -667
  12. package/dist/{check-EDwSwP89.js → check-C9Mp9eCK.js} +1 -1
  13. package/dist/{chunk-IMKFNOWR-CoIFEo-1.js → chunk-IMKFNOWR-fsCGAJ9G.js} +1 -1
  14. package/dist/{chunk-SHT3W25Y-lHiGx3ZC.js → chunk-SHT3W25Y-D2STW4hj.js} +1 -1
  15. package/dist/{chunk-SVP7TREG-BvsHIrY_.js → chunk-SVP7TREG-CiY7cQG-.js} +1 -1
  16. package/dist/{chunk-TICWLB2K-BC8YIm_n.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-Bt98lkkv.js → classDiagram-ZZMXUADV-B2JvZAP4.js} +2 -2
  19. package/dist/{classDiagram-v2-VYDZK3BY-D9bfbcC3.js → classDiagram-v2-VYDZK3BY-L8VsZG5A.js} +2 -2
  20. package/dist/{code-visibility-CLNfgCjd.js → code-visibility-33fxfX1p.js} +45 -49
  21. package/dist/{common-fyD_6mT2.js → common-D09oJJ8Z.js} +7354 -6884
  22. package/dist/{copy-icon-Ft7t9bqz.js → copy-icon-De584pZ4.js} +3 -3
  23. package/dist/{cynefinDiagram-5FMLGOSQ-CgWra7wt.js → cynefinDiagram-5FMLGOSQ-DdllC264.js} +1 -1
  24. package/dist/{dagre-GXQ25YYZ-CFZ8-35c.js → dagre-GXQ25YYZ-BaWHw4_f.js} +1 -1
  25. package/dist/{data-grid-overlay-editor-BmqASKep.js → data-grid-overlay-editor-lGsInXfR.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-lCNQrW7d.js → diagram-S7CK7UJ4-pFt-tUDe.js} +1 -1
  28. package/dist/{diagram-UQ7AKVKN-CQPQ6pP_.js → diagram-UQ7AKVKN-DqGJiBp4.js} +1 -1
  29. package/dist/{diagram-VX7I27RA-Cf3b0llz.js → diagram-VX7I27RA-Dr-yzMjf.js} +1 -1
  30. package/dist/{diagram-Z3DM3KII-Bt4QLrpn.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-D3NAyPFY.js → ebnfDiagram-PWID7BFC-Br04Wy-E.js} +2 -2
  59. package/dist/{erDiagram-RLTQ6QDP-Tlu09_Nm.js → erDiagram-RLTQ6QDP-D5-XVgvv.js} +1 -1
  60. package/dist/{error-banner-Da600cwz.js → error-banner-DxJ3s8YO.js} +89 -84
  61. package/dist/{cells-C8iCYvLU.js → error-entries-C2Ux0Fjt.js} +7970 -8605
  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-B0CMhVv7.js +374 -0
  65. package/dist/{flowDiagram-HODETNUW-CAYbpJmV.js → flowDiagram-HODETNUW-CVl2kREF.js} +2 -2
  66. package/dist/{formats-BGh2thRc.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-BkjP14aq.js → glide-data-editor-CL535A5q.js} +370 -368
  69. package/dist/{glide-utils-DJyqh0EM.js → glide-utils-D98laidb.js} +16 -16
  70. package/dist/{globals-BN2O0F8N.js → globals-Bsr6T2yD.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-CZY1pIZH.js → html-to-image-CkH7vdBZ.js} +478 -381
  73. package/dist/{infoDiagram-27XIBGKW-XrDxhr0x.js → infoDiagram-27XIBGKW-BJ4vNcZE.js} +1 -1
  74. package/dist/{ishikawaDiagram-5VMMS53U-CD1muzov.js → ishikawaDiagram-5VMMS53U-B_Jy1Dsm.js} +1 -1
  75. package/dist/{kanban-definition-UXKFOSKX-Dg9W142_.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 -7708
  80. package/dist/{mermaid-CiAXjLpA.js → mermaid-Bh918qrq.js} +52 -52
  81. package/dist/mermaid-HWGCJPDP-Du3-HPQA.js +2 -0
  82. package/dist/{mindmap-definition-YA3MSWOX-DgCidgYJ.js → mindmap-definition-YA3MSWOX-BP3ZTuir.js} +1 -1
  83. package/dist/{number-overlay-editor-BEBWJnbL.js → number-overlay-editor-CzbkG4E2.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-coOYYKB6.js → pegDiagram-XKGWAZYB-nNGOQtnt.js} +2 -2
  87. package/dist/{pieDiagram-E7YTZNPT-BnXTc3bj.js → pieDiagram-E7YTZNPT-CpRz5BZz.js} +1 -1
  88. package/dist/precisionRound-DhAzeRcm.js +49 -0
  89. package/dist/{process-output-e4jj9Ijt.js → process-output-OujAtFW4.js} +4 -4
  90. package/dist/{quadrantDiagram-AXDQQJYC-Ccw0xMjN.js → quadrantDiagram-AXDQQJYC-Blrm7V7_.js} +1 -1
  91. package/dist/{railroadDiagram-O6MQD6OU-B28-u83n.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-CdYCTMu0.js → requirementDiagram-BXWQKSXE-CEfg6vu9.js} +1 -1
  94. package/dist/{reveal-component-DK69bOki.js → reveal-component-CbEK0sNX.js} +733 -710
  95. package/dist/{select-BButFi-d.js → select-2KOiReNY.js} +536 -457
  96. package/dist/{sequenceDiagram-WJ2MYXX4-CM3aRAe_.js → sequenceDiagram-WJ2MYXX4-IOmpQcOi.js} +1 -1
  97. package/dist/{spec-BwarBpUJ.js → spec-Dkp64vVQ.js} +41 -11
  98. package/dist/{stateDiagram-D77RDMKH-DOZUMVj8.js → stateDiagram-D77RDMKH-tgU-EY9_.js} +2 -2
  99. package/dist/{stateDiagram-v2-MP3YSRHH-CISYxma6.js → stateDiagram-v2-MP3YSRHH-BofhG6q1.js} +2 -2
  100. package/dist/style.css +1 -1
  101. package/dist/{swimlanes-42K2YHIH-BkSppxp8.js → swimlanes-42K2YHIH-DW4vWDBd.js} +1 -1
  102. package/dist/{swimlanesDiagram-VR7AAH4N-c8G5pGxC.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-CaMQayqK.js → timeline-definition-24CTP7MA-D_QU3NGs.js} +1 -1
  106. package/dist/{toDate-BNJf01u1.js → toDate-BNDEXbfV.js} +77 -74
  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-DG29TAB7.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-CF8pfaK3.js → vennDiagram-4TSXK5OY-YFYsNIAh.js} +1 -1
  120. package/dist/{wardleyDiagram-VM6X3IG4-dGPu39d-.js → wardleyDiagram-VM6X3IG4-i_n8mAWC.js} +1 -1
  121. package/dist/{xychartDiagram-S5SC5T6Z-CP6-9q56.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__/fixtures/{pair-preview.ts → pair-config.ts} +3 -2
  128. package/src/__tests__/main.test.tsx +24 -1
  129. package/src/__tests__/module-entry.test.ts +44 -0
  130. package/src/__tests__/mount.test.ts +9 -9
  131. package/src/__tests__/{host-imports.test.ts → replica-imports.test.ts} +12 -11
  132. package/src/components/ai/__tests__/ai-model-dropdown.test.tsx +63 -0
  133. package/src/components/ai/ai-model-dropdown.tsx +11 -6
  134. package/src/components/app-config/ai-config.tsx +7 -2
  135. package/src/components/app-config/app-config-form.tsx +5 -9
  136. package/src/components/app-config/user-config-form.tsx +35 -0
  137. package/src/components/chat/__tests__/message-queue.test.tsx +17 -0
  138. package/src/components/chat/__tests__/notebook-access-selector.test.tsx +64 -0
  139. package/src/components/chat/acp/__tests__/session-tabs.test.tsx +43 -0
  140. package/src/components/chat/acp/__tests__/state.test.ts +2 -2
  141. package/src/components/chat/acp/blocks.tsx +3 -2
  142. package/src/components/chat/acp/session-tabs.tsx +8 -3
  143. package/src/components/chat/chat-components.tsx +2 -0
  144. package/src/components/chat/chat-panel.tsx +20 -101
  145. package/src/components/chat/chat-utils.ts +4 -8
  146. package/src/components/chat/notebook-access-selector.stories.tsx +22 -0
  147. package/src/components/chat/notebook-access-selector.tsx +100 -0
  148. package/src/components/data-table/TableBottomBar.tsx +4 -1
  149. package/src/components/data-table/TableTopBar.tsx +2 -0
  150. package/src/components/data-table/__tests__/chart-spec-model.test.ts +195 -195
  151. package/src/components/data-table/__tests__/column-explorer.test.tsx +27 -1
  152. package/src/components/data-table/__tests__/columns.test.tsx +105 -0
  153. package/src/components/data-table/__tests__/context-menu-interaction.test.tsx +130 -0
  154. package/src/components/data-table/__tests__/context-menu.test.ts +176 -0
  155. package/src/components/data-table/__tests__/data-table.test.tsx +1 -0
  156. package/src/components/data-table/__tests__/date-filter-inputs.test.tsx +24 -1
  157. package/src/components/data-table/__tests__/date-popover.test.tsx +72 -0
  158. package/src/components/data-table/__tests__/export-actions.test.tsx +434 -6
  159. package/src/components/data-table/__tests__/keyboard-selection.test.tsx +188 -0
  160. package/src/components/data-table/__tests__/pagination.test.tsx +28 -1
  161. package/src/components/data-table/__tests__/search-highlight.test.tsx +109 -0
  162. package/src/components/data-table/__tests__/url-detector.test.tsx +74 -0
  163. package/src/components/data-table/cell-selection/feature.ts +2 -2
  164. package/src/components/data-table/cell-selection/types.ts +2 -2
  165. package/src/components/data-table/charts/__tests__/chart-panel.test.tsx +74 -1
  166. package/src/components/data-table/charts/__tests__/spec-snapshot.test.ts +20 -20
  167. package/src/components/data-table/charts/__tests__/spec.test.ts +78 -52
  168. package/src/components/data-table/charts/__tests__/storage.test.ts +4 -1
  169. package/src/components/data-table/charts/chart-spec/spec.ts +68 -43
  170. package/src/components/data-table/charts/charts.tsx +40 -30
  171. package/src/components/data-table/charts/storage.ts +16 -5
  172. package/src/components/data-table/column-explorer-panel/column-explorer.tsx +1 -1
  173. package/src/components/data-table/column-summary/chart-spec-model.tsx +23 -16
  174. package/src/components/data-table/column-summary/column-summary.tsx +2 -4
  175. package/src/components/data-table/column-visibility-dropdown.tsx +8 -13
  176. package/src/components/data-table/columns.tsx +45 -46
  177. package/src/components/data-table/context-menu.tsx +161 -46
  178. package/src/components/data-table/data-table.tsx +17 -1
  179. package/src/components/data-table/date-filter-inputs.tsx +15 -42
  180. package/src/components/data-table/date-popover.tsx +6 -4
  181. package/src/components/data-table/export-actions.tsx +267 -163
  182. package/src/components/data-table/pagination.tsx +44 -33
  183. package/src/components/data-table/range-focus/atoms.ts +4 -6
  184. package/src/components/data-table/range-focus/use-cell-range-selection.ts +65 -38
  185. package/src/components/data-table/range-focus/use-scroll-into-view.ts +21 -2
  186. package/src/components/data-table/renderers.tsx +16 -7
  187. package/src/components/data-table/row-viewer-panel/__tests__/row-viewer.test.tsx +24 -1
  188. package/src/components/data-table/row-viewer-panel/row-viewer.tsx +4 -4
  189. package/src/components/data-table/schemas.ts +14 -36
  190. package/src/components/data-table/search-highlight.tsx +79 -0
  191. package/src/components/data-table/types.ts +1 -0
  192. package/src/components/data-table/url-detector.tsx +49 -9
  193. package/src/components/data-table/utils.ts +18 -0
  194. package/src/components/datasources/__tests__/column-actions.test.tsx +97 -0
  195. package/src/components/datasources/__tests__/column-preview.test.tsx +96 -5
  196. package/src/components/datasources/__tests__/datasources-interactions.test.tsx +181 -0
  197. package/src/components/datasources/__tests__/table-list.test.tsx +118 -0
  198. package/src/components/datasources/__tests__/utils.test.ts +57 -6
  199. package/src/components/datasources/column-preview.tsx +17 -12
  200. package/src/components/datasources/datasources.tsx +56 -43
  201. package/src/components/datasources/utils.ts +32 -23
  202. package/src/components/debugger/debugger-code.tsx +5 -0
  203. package/src/components/dependency-graph/custom-node.tsx +1 -1
  204. package/src/components/dependency-graph/dependency-graph-tree.tsx +8 -8
  205. package/src/components/dependency-graph/elements.ts +24 -21
  206. package/src/components/dependency-graph/minimap-content.tsx +13 -19
  207. package/src/components/dependency-graph/panels.tsx +4 -2
  208. package/src/components/dependency-graph/utils/__tests__/layout.test.ts +18 -11
  209. package/src/components/editor/Output.tsx +9 -2
  210. package/src/components/editor/SortableCell.tsx +1 -3
  211. package/src/components/editor/__tests__/Output.test.tsx +50 -32
  212. package/src/components/editor/__tests__/agent-toast.test.tsx +58 -0
  213. package/src/components/editor/__tests__/data-attributes.test.tsx +28 -14
  214. package/src/components/editor/__tests__/dynamic-favicon.test.tsx +111 -18
  215. package/src/components/editor/__tests__/output-persistence.test.tsx +6 -2
  216. package/src/components/editor/__tests__/package-alert.test.tsx +22 -1
  217. package/src/components/editor/actions/__tests__/name-cell-input.test.tsx +69 -0
  218. package/src/components/editor/actions/__tests__/pair-with-agent-commands.test.ts +80 -252
  219. package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +218 -40
  220. package/src/components/editor/actions/__tests__/pair-with-agent.test.tsx +2 -6
  221. package/src/components/editor/actions/name-cell-input.tsx +4 -3
  222. package/src/components/editor/actions/pair-with-agent-commands.ts +44 -53
  223. package/src/components/editor/actions/pair-with-agent-modal.tsx +101 -38
  224. package/src/components/editor/actions/useCellActionButton.tsx +26 -31
  225. package/src/components/editor/actions/useConfigActions.tsx +3 -4
  226. package/src/components/editor/actions/useNotebookActions.tsx +19 -23
  227. package/src/components/editor/actions/useRestartKernel.tsx +1 -1
  228. package/src/components/editor/actions/useSetCodeVisibility.ts +11 -12
  229. package/src/components/editor/actions/useSetOutputsExpanded.ts +14 -19
  230. package/src/components/editor/agent-toast.ts +15 -0
  231. package/src/components/editor/ai/add-cell-with-ai.tsx +3 -3
  232. package/src/components/editor/ai/ai-completion-editor.tsx +1 -0
  233. package/src/components/editor/alerts/__tests__/connection-notice.test.tsx +1 -1
  234. package/src/components/editor/alerts/connection-notice.tsx +1 -1
  235. package/src/components/editor/alerts/startup-logs-alert.tsx +1 -0
  236. package/src/components/editor/alerts/stdin-blocking-alert.tsx +1 -1
  237. package/src/components/editor/app-container.tsx +1 -1
  238. package/src/components/editor/boundary/__tests__/ErrorBoundary.test.tsx +1 -1
  239. package/src/components/editor/cell/CreateCellButton.tsx +15 -21
  240. package/src/components/editor/cell/DeleteButton.tsx +3 -2
  241. package/src/components/editor/cell/RunButton.tsx +2 -2
  242. package/src/components/editor/cell/StagedAICell.tsx +10 -2
  243. package/src/components/editor/cell/StopButton.tsx +2 -2
  244. package/src/components/editor/cell/__tests__/StagedAICell.test.tsx +3 -1
  245. package/src/components/editor/cell/__tests__/useShouldShowInterrupt.test.ts +39 -0
  246. package/src/components/editor/cell/cell-actions.tsx +12 -1
  247. package/src/components/editor/cell/code/__tests__/language-toggle.test.tsx +54 -0
  248. package/src/components/editor/cell/code/cell-editor.tsx +36 -43
  249. package/src/components/editor/cell/code/language-toggle.tsx +8 -7
  250. package/src/components/editor/cell/collapse.tsx +11 -4
  251. package/src/components/editor/cell/useAddCell.ts +4 -8
  252. package/src/components/editor/cell/useDeleteCell.tsx +10 -36
  253. package/src/components/editor/cell/useRunCells.ts +2 -5
  254. package/src/components/editor/cell/useShouldShowInterrupt.ts +2 -12
  255. package/src/components/editor/cell/useSplitCell.tsx +10 -6
  256. package/src/components/editor/chrome/components/__tests__/feedback-button.test.tsx +1 -1
  257. package/src/components/editor/chrome/components/feedback-button.tsx +2 -8
  258. package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +31 -2
  259. package/src/components/editor/chrome/panels/__tests__/runtime-details.test.tsx +1 -1
  260. package/src/components/editor/chrome/panels/__tests__/sandbox-recovery.test.tsx +1 -1
  261. package/src/components/editor/chrome/panels/cache-panel.tsx +1 -0
  262. package/src/components/editor/chrome/panels/dependency-graph-panel.tsx +1 -1
  263. package/src/components/editor/chrome/panels/error-panel.tsx +1 -0
  264. package/src/components/editor/chrome/panels/logs-panel.tsx +2 -1
  265. package/src/components/editor/chrome/panels/outline/__tests__/outline-keyboard.test.tsx +84 -0
  266. package/src/components/editor/chrome/panels/outline/floating-outline.tsx +39 -14
  267. package/src/components/editor/chrome/panels/outline/useActiveOutline.tsx +3 -2
  268. package/src/components/editor/chrome/panels/packages-panel.tsx +4 -7
  269. package/src/components/editor/chrome/panels/secrets-panel.tsx +1 -0
  270. package/src/components/editor/chrome/panels/session-panel.tsx +1 -1
  271. package/src/components/editor/chrome/panels/snippets-panel.tsx +10 -12
  272. package/src/components/editor/chrome/panels/variable-panel.tsx +1 -5
  273. package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +363 -0
  274. package/src/components/editor/chrome/wrapper/__tests__/document-sync-status.test.tsx +92 -0
  275. package/src/components/editor/chrome/wrapper/__tests__/pending-ai-cells.test.tsx +61 -2
  276. package/src/components/editor/chrome/wrapper/footer-item.tsx +32 -24
  277. package/src/components/editor/chrome/wrapper/footer-items/attachment-status.tsx +237 -0
  278. package/src/components/editor/chrome/wrapper/footer-items/backend-status.tsx +1 -1
  279. package/src/components/editor/chrome/wrapper/footer-items/document-sync-status.tsx +54 -0
  280. package/src/components/editor/chrome/wrapper/footer-items/lsp-status.tsx +1 -1
  281. package/src/components/editor/chrome/wrapper/footer-items/machine-stats.tsx +1 -1
  282. package/src/components/editor/chrome/wrapper/footer-items/runtime-settings.tsx +1 -7
  283. package/src/components/editor/chrome/wrapper/footer.tsx +4 -0
  284. package/src/components/editor/chrome/wrapper/minimap-state.ts +1 -1
  285. package/src/components/editor/chrome/wrapper/pending-ai-cells.tsx +12 -2
  286. package/src/components/editor/chrome/wrapper/storage.ts +1 -1
  287. package/src/components/editor/columns/__tests__/cell-column.test.tsx +1 -1
  288. package/src/components/editor/columns/cell-column.tsx +1 -1
  289. package/src/components/editor/columns/sortable-column.tsx +2 -2
  290. package/src/components/editor/connections/components.tsx +5 -10
  291. package/src/components/editor/controls/Controls.tsx +4 -4
  292. package/src/components/editor/controls/__tests__/command-palette.test.tsx +53 -0
  293. package/src/components/editor/controls/__tests__/keyboard-shortcuts.test.tsx +61 -0
  294. package/src/components/editor/controls/__tests__/notebook-menu-dropdown.test.tsx +10 -4
  295. package/src/components/editor/controls/command-palette.tsx +22 -10
  296. package/src/components/editor/controls/keyboard-shortcuts.tsx +14 -5
  297. package/src/components/editor/documentation.css +34 -11
  298. package/src/components/editor/dynamic-favicon.tsx +21 -40
  299. package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +536 -0
  300. package/src/components/editor/errors/auto-fix.tsx +4 -8
  301. package/src/components/editor/errors/send-error-report-button.tsx +66 -0
  302. package/src/components/editor/file-tree/__tests__/file-explorer-interactions.test.tsx +6 -0
  303. package/src/components/editor/file-tree/__tests__/file-explorer-navigation.test.tsx +47 -0
  304. package/src/components/editor/file-tree/__tests__/use-file-search.test.tsx +91 -0
  305. package/src/components/editor/file-tree/file-explorer.tsx +48 -15
  306. package/src/components/editor/file-tree/file-name-input.tsx +1 -0
  307. package/src/components/editor/file-tree/file-viewer.tsx +4 -2
  308. package/src/components/editor/file-tree/use-file-search.ts +35 -21
  309. package/src/components/editor/header/__tests__/status.test.tsx +5 -3
  310. package/src/components/editor/header/app-header.tsx +1 -1
  311. package/src/components/editor/header/status.tsx +40 -20
  312. package/src/components/editor/links/__tests__/cell-link.test.tsx +148 -0
  313. package/src/components/editor/links/cell-link-list.tsx +1 -1
  314. package/src/components/editor/links/cell-link.tsx +17 -7
  315. package/src/components/editor/navigation/__tests__/clipboard.test.ts +60 -38
  316. package/src/components/editor/navigation/__tests__/navigation.test.ts +108 -85
  317. package/src/components/editor/navigation/__tests__/selection.test.ts +43 -21
  318. package/src/components/editor/navigation/clipboard.ts +17 -14
  319. package/src/components/editor/navigation/focus-utils.ts +23 -4
  320. package/src/components/editor/navigation/multi-cell-action-toolbar.tsx +17 -33
  321. package/src/components/editor/navigation/navigation.ts +71 -70
  322. package/src/components/editor/navigation/selection.ts +30 -29
  323. package/src/components/editor/notebook-banner.tsx +1 -0
  324. package/src/components/editor/notebook-cell.tsx +29 -31
  325. package/src/components/editor/output/MarimoErrorOutput.tsx +23 -6
  326. package/src/components/editor/output/MarimoTracebackOutput.tsx +54 -37
  327. package/src/components/editor/output/Outputs.css +8 -0
  328. package/src/components/editor/output/__tests__/ansi-reduce.test.ts +18 -0
  329. package/src/components/editor/output/__tests__/traceback.test.tsx +27 -1
  330. package/src/components/editor/output/ansi-reduce.ts +36 -17
  331. package/src/components/editor/output/console/ConsoleOutput.tsx +19 -25
  332. package/src/components/editor/output/console/__tests__/ConsoleOutput.test.tsx +125 -69
  333. package/src/components/editor/package-alert.tsx +14 -7
  334. package/src/components/editor/renderers/cell-array.tsx +22 -33
  335. package/src/components/editor/renderers/grid-layout/grid-layout.tsx +12 -6
  336. package/src/components/editor/renderers/slides-layout/__tests__/plugin.test.ts +0 -1
  337. package/src/components/editor/renderers/slides-layout/slides-layout.tsx +1 -1
  338. package/src/components/editor/renderers/vertical-layout/__tests__/useFocusFirstEditor.test.ts +4 -4
  339. package/src/components/editor/renderers/vertical-layout/sidebar/toggle.tsx +7 -1
  340. package/src/components/editor/renderers/vertical-layout/useFocusFirstEditor.ts +1 -1
  341. package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +1 -1
  342. package/src/components/editor/stdin-blocking-alert.tsx +1 -1
  343. package/src/components/home/components.tsx +17 -31
  344. package/src/components/icons/__tests__/large-spinner.test.tsx +39 -0
  345. package/src/components/icons/large-spinner.tsx +3 -14
  346. package/src/components/icons/spinner.tsx +1 -1
  347. package/src/components/markdown/markdown-renderer.tsx +4 -8
  348. package/src/components/mcp/mcp-status-indicator.tsx +1 -1
  349. package/src/components/pages/__tests__/home-page.test.tsx +116 -0
  350. package/src/components/pages/edit-page.tsx +19 -17
  351. package/src/components/pages/home-page.tsx +164 -91
  352. package/src/components/scratchpad/scratchpad.test.tsx +97 -0
  353. package/src/components/scratchpad/scratchpad.tsx +20 -11
  354. package/src/components/slides/__tests__/minimap-actions.test.tsx +22 -29
  355. package/src/components/slides/__tests__/minimap.test.ts +38 -19
  356. package/src/components/slides/__tests__/slide-scroll-hint.test.tsx +37 -0
  357. package/src/components/slides/minimap.tsx +17 -12
  358. package/src/components/slides/reveal-component.tsx +58 -26
  359. package/src/components/slides/reveal-slides.css +5 -4
  360. package/src/components/slides/slide-cell-view.tsx +4 -5
  361. package/src/components/slides/slide-form.tsx +1 -0
  362. package/src/components/slides/slide-scroll-hint.tsx +3 -1
  363. package/src/components/sort/SortableCellsProvider.tsx +8 -25
  364. package/src/components/tracing/tracing-keyboard.test.tsx +85 -0
  365. package/src/components/tracing/tracing.tsx +46 -42
  366. package/src/components/ui/__tests__/draggable-popover.test.tsx +24 -0
  367. package/src/components/ui/__tests__/range-slider.test.tsx +165 -0
  368. package/src/components/ui/alert-dialog.tsx +1 -1
  369. package/src/components/ui/draggable-popover.tsx +24 -2
  370. package/src/components/ui/range-slider.tsx +22 -9
  371. package/src/core/MarimoApp.tsx +1 -1
  372. package/src/core/ai/__tests__/staged-cells.test.ts +27 -26
  373. package/src/core/ai/context/providers/__tests__/cell-output.test.ts +8 -12
  374. package/src/core/ai/context/providers/cell-output.ts +2 -2
  375. package/src/core/ai/context/providers/datasource.ts +6 -2
  376. package/src/core/ai/staged-cells.ts +15 -18
  377. package/src/core/ai/tools/__tests__/edit-notebook-tool.test.ts +40 -88
  378. package/src/core/ai/tools/__tests__/registry.test.ts +17 -0
  379. package/src/core/ai/tools/base.ts +18 -15
  380. package/src/core/ai/tools/edit-notebook-tool.ts +86 -80
  381. package/src/core/ai/tools/registry.ts +13 -10
  382. package/src/core/ai/tools/sample-tool.ts +6 -7
  383. package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +4 -37
  384. package/src/core/cells/__tests__/actions.test.ts +624 -35
  385. package/src/core/cells/__tests__/add-missing-import.test.ts +7 -8
  386. package/src/core/cells/__tests__/arrange.test.ts +432 -0
  387. package/src/core/cells/__tests__/cell.test.ts +39 -0
  388. package/src/core/cells/__tests__/cells.test.ts +256 -2477
  389. package/src/core/cells/__tests__/collapseConsoleOutputs.test.ts +54 -0
  390. package/src/core/cells/__tests__/document.test.ts +341 -0
  391. package/src/core/cells/__tests__/editor.test.ts +57 -0
  392. package/src/core/cells/__tests__/error-report.test.ts +139 -0
  393. package/src/core/cells/__tests__/flush-document.test.ts +40 -0
  394. package/src/core/cells/__tests__/harness.ts +106 -0
  395. package/src/core/cells/__tests__/layout.test.ts +177 -0
  396. package/src/core/cells/__tests__/notebook-outline.test.ts +120 -0
  397. package/src/core/cells/__tests__/outputs.test.tsx +71 -53
  398. package/src/core/cells/__tests__/session.test.ts +493 -210
  399. package/src/core/cells/__tests__/utils.test.ts +9 -108
  400. package/src/core/cells/__tests__/with-document-save.test.ts +67 -0
  401. package/src/core/cells/actions.ts +636 -6
  402. package/src/core/cells/add-missing-import.ts +47 -43
  403. package/src/core/cells/arrange.ts +529 -0
  404. package/src/core/cells/cell.ts +14 -3
  405. package/src/core/cells/cells.ts +120 -1337
  406. package/src/core/cells/columns.ts +38 -0
  407. package/src/core/cells/document.ts +289 -0
  408. package/src/core/cells/edit.ts +30 -0
  409. package/src/core/cells/editor.ts +82 -0
  410. package/src/core/cells/error-report.ts +63 -0
  411. package/src/core/cells/layout.ts +221 -0
  412. package/src/core/cells/outputs.ts +12 -28
  413. package/src/core/cells/pending-delete-service.ts +3 -7
  414. package/src/core/cells/session.ts +104 -54
  415. package/src/core/cells/types.ts +22 -6
  416. package/src/core/cells/utils.ts +10 -8
  417. package/src/core/codemirror/__tests__/setup.test.ts +29 -29
  418. package/src/core/codemirror/ai/__tests__/resources.test.ts +117 -0
  419. package/src/core/codemirror/ai/resources.ts +1 -1
  420. package/src/core/codemirror/cells/__tests__/extensions.test.ts +42 -1
  421. package/src/core/codemirror/cells/extensions.ts +27 -9
  422. package/src/core/codemirror/cells/state.ts +1 -1
  423. package/src/core/codemirror/copilot/__tests__/getCodes.test.ts +6 -7
  424. package/src/core/codemirror/copilot/getCodes.ts +2 -2
  425. package/src/core/codemirror/format.ts +6 -5
  426. package/src/core/codemirror/keymaps/vim.ts +4 -8
  427. package/src/core/codemirror/language/__tests__/sql.test.ts +22 -20
  428. package/src/core/codemirror/language/__tests__/utils.test.ts +187 -15
  429. package/src/core/codemirror/language/cell-code.ts +114 -0
  430. package/src/core/codemirror/language/extension.ts +52 -35
  431. package/src/core/codemirror/language/languages/markdown.ts +8 -6
  432. package/src/core/codemirror/language/languages/python.ts +16 -10
  433. package/src/core/codemirror/language/languages/sql/sql.ts +16 -24
  434. package/src/core/codemirror/language/panel/panel.tsx +1 -0
  435. package/src/core/codemirror/language/types.ts +7 -7
  436. package/src/core/codemirror/language/utils.ts +9 -39
  437. package/src/core/codemirror/lsp/__tests__/notebook-lsp.test.ts +1 -2
  438. package/src/core/codemirror/lsp/notebook-lsp.ts +6 -5
  439. package/src/core/codemirror/lsp/utils.ts +1 -1
  440. package/src/core/codemirror/readonly/__tests__/extension.test.ts +1 -1
  441. package/src/core/codemirror/readonly/extension.ts +2 -2
  442. package/src/core/codemirror/rtc/extension.ts +24 -13
  443. package/src/core/codemirror/rtc/loro/awareness.ts +34 -19
  444. package/src/core/codemirror/rtc/loro/sync.ts +1 -0
  445. package/src/core/codemirror/utils.ts +1 -0
  446. package/src/core/config/__tests__/app-config.test.ts +64 -0
  447. package/src/core/config/__tests__/config-schema.test.ts +13 -6
  448. package/src/core/config/config-schema.ts +13 -3
  449. package/src/core/config/config.ts +26 -12
  450. package/src/core/config/pair.ts +5 -3
  451. package/src/core/datasets/__tests__/data-source-discovery.test.ts +5 -5
  452. package/src/core/datasets/data-source-discovery.ts +4 -6
  453. package/src/core/documentation/doc-lookup.ts +1 -1
  454. package/src/core/edit-app.tsx +21 -28
  455. package/src/core/errors/error-entries.ts +4 -1
  456. package/src/core/export/hooks.ts +1 -1
  457. package/src/core/export/layout.ts +2 -2
  458. package/src/core/hotkeys/actions.ts +2 -4
  459. package/src/core/islands/__tests__/bootstrap.test.ts +69 -17
  460. package/src/core/islands/__tests__/bridge.test.ts +72 -25
  461. package/src/core/islands/bootstrap.ts +46 -53
  462. package/src/core/islands/bridge.ts +74 -23
  463. package/src/core/islands/components/__tests__/IslandControls.test.tsx +7 -5
  464. package/src/core/islands/components/web-components.tsx +1 -1
  465. package/src/core/islands/worker/__tests__/controller.test.ts +62 -4
  466. package/src/core/islands/worker/controller.ts +4 -10
  467. package/src/core/islands/worker/worker.tsx +33 -33
  468. package/src/core/kernel/__tests__/RuntimeState.test.ts +1 -1
  469. package/src/core/kernel/__tests__/handlers.test.ts +7 -153
  470. package/src/core/kernel/handlers.ts +19 -72
  471. package/src/core/meta/__tests__/code-visibility.test.tsx +78 -70
  472. package/src/core/meta/code-visibility.ts +18 -9
  473. package/src/core/network/__tests__/connection.test.ts +1 -1
  474. package/src/core/network/__tests__/requests-lazy.test.ts +1 -3
  475. package/src/core/network/connection-notice.ts +1 -1
  476. package/src/core/network/connection.ts +5 -3
  477. package/src/core/network/requests-lazy.ts +1 -4
  478. package/src/core/network/requests-network.ts +0 -25
  479. package/src/core/network/requests-static.ts +0 -3
  480. package/src/core/network/requests-toasting.tsx +0 -3
  481. package/src/core/network/types.ts +0 -12
  482. package/src/core/network/useConnectionNotice.ts +8 -2
  483. package/src/core/notebook/__tests__/operations.test.ts +51 -0
  484. package/src/core/notebook/attachments.ts +38 -0
  485. package/src/core/notebook/document/__tests__/codemirror.test.ts +586 -0
  486. package/src/core/notebook/document/__tests__/draft.test.ts +363 -0
  487. package/src/core/notebook/document/__tests__/fixtures.ts +92 -0
  488. package/src/core/notebook/document/__tests__/hold.test.ts +88 -0
  489. package/src/core/notebook/document/__tests__/hooks.test.tsx +391 -0
  490. package/src/core/notebook/document/__tests__/lifecycle-scenarios.test.ts +61 -0
  491. package/src/core/notebook/document/__tests__/model.test.ts +247 -0
  492. package/src/core/notebook/document/__tests__/protocol-fixtures.ts +38 -0
  493. package/src/core/notebook/document/__tests__/recovery-scenarios.test.ts +230 -0
  494. package/src/core/notebook/document/__tests__/scenario-fixture.ts +43 -0
  495. package/src/core/notebook/document/__tests__/scenarios.test.ts +205 -0
  496. package/src/core/notebook/document/__tests__/state.test.ts +459 -0
  497. package/src/core/notebook/document/__tests__/submission.test.ts +203 -0
  498. package/src/core/notebook/document/__tests__/test-host.test.ts +92 -0
  499. package/src/core/notebook/document/__tests__/test-host.ts +247 -0
  500. package/src/core/notebook/document/__tests__/test-notebook.ts +149 -0
  501. package/src/core/notebook/document/__tests__/user.ts +61 -0
  502. package/src/core/notebook/document/codemirror.ts +216 -0
  503. package/src/core/notebook/document/draft.ts +502 -0
  504. package/src/core/notebook/document/hooks.tsx +118 -0
  505. package/src/core/notebook/document/index.ts +24 -0
  506. package/src/core/notebook/document/model.ts +105 -0
  507. package/src/core/notebook/document/select.ts +41 -0
  508. package/src/core/notebook/document/state.ts +378 -0
  509. package/src/core/notebook/document/sync.ts +418 -0
  510. package/src/core/notebook/index.ts +19 -0
  511. package/src/core/notebook/operations.ts +30 -0
  512. package/src/core/notebook/replica/AGENTS.md +11 -0
  513. package/src/core/notebook/replica/__tests__/attachments.test.ts +56 -0
  514. package/src/core/notebook/replica/__tests__/document.test.ts +126 -0
  515. package/src/core/notebook/replica/__tests__/fixtures.ts +79 -0
  516. package/src/core/notebook/replica/__tests__/replica.test.ts +550 -0
  517. package/src/core/notebook/replica/backend.ts +19 -0
  518. package/src/core/{websocket/types.ts → notebook/replica/connection.ts} +10 -15
  519. package/src/core/notebook/replica/index.ts +13 -0
  520. package/src/core/notebook/replica/replica.ts +286 -0
  521. package/src/core/notebook/replica/resources/attachments.ts +22 -0
  522. package/src/core/notebook/replica/resources/document.ts +101 -0
  523. package/src/core/packages/__tests__/useSandboxController.test.tsx +1 -1
  524. package/src/core/packages/useSandboxController.ts +1 -1
  525. package/src/core/run-app.tsx +48 -51
  526. package/src/core/runtime/__tests__/adapter.test.ts +1 -1
  527. package/src/core/runtime/__tests__/createWsUrl.test.ts +77 -7
  528. package/src/core/runtime/__tests__/runtime.test.ts +33 -15
  529. package/src/core/runtime/adapter.ts +1 -1
  530. package/src/core/runtime/config.ts +2 -2
  531. package/src/core/runtime/runtime.ts +39 -30
  532. package/src/core/saving/__tests__/state.test.ts +111 -0
  533. package/src/core/saving/__tests__/useAutoSave.test.tsx +67 -0
  534. package/src/core/saving/filename.ts +1 -1
  535. package/src/core/saving/save-component.tsx +28 -36
  536. package/src/core/saving/state.ts +38 -39
  537. package/src/core/saving/useAutoSave.ts +17 -53
  538. package/src/core/session/__tests__/backend.test.ts +236 -0
  539. package/src/core/session/__tests__/useNotebookReplica.hook.test.tsx +184 -0
  540. package/src/core/{websocket/__tests__/useMarimoKernelConnection.hook.test.tsx → session/__tests__/useNotebookSession.hook.test.tsx} +252 -185
  541. package/src/core/session/active-notebook.ts +22 -0
  542. package/src/core/session/backend.ts +123 -0
  543. package/src/core/session/embedded.ts +7 -0
  544. package/src/core/session/types.ts +17 -0
  545. package/src/core/session/useNotebookReplica.ts +74 -0
  546. package/src/core/{websocket/useMarimoKernelConnection.tsx → session/useNotebookSession.tsx} +120 -273
  547. package/src/core/state/__tests__/jotai.test.tsx +108 -0
  548. package/src/core/state/jotai.ts +1 -1
  549. package/src/core/static/__tests__/files.test.ts +38 -10
  550. package/src/core/static/files.ts +2 -0
  551. package/src/core/{websocket/__tests__/useMarimoKernelConnection.test.ts → transports/__tests__/connection-state.test.ts} +16 -30
  552. package/src/core/transports/__tests__/server.test.ts +408 -0
  553. package/src/core/transports/__tests__/static.test.ts +306 -0
  554. package/src/core/transports/__tests__/wasm.test.ts +166 -0
  555. package/src/core/transports/connection-state.ts +63 -0
  556. package/src/core/transports/server.ts +152 -0
  557. package/src/core/transports/static.ts +166 -0
  558. package/src/core/transports/wasm.ts +97 -0
  559. package/src/core/wasm/__tests__/bridge.test.ts +214 -50
  560. package/src/core/wasm/__tests__/worker.test.ts +187 -0
  561. package/src/core/wasm/bridge.ts +75 -69
  562. package/src/core/wasm/rpc.ts +15 -3
  563. package/src/core/wasm/store.ts +3 -3
  564. package/src/core/wasm/worker/bootstrap.ts +55 -51
  565. package/src/core/wasm/worker/getMarimoWheel.ts +1 -1
  566. package/src/core/wasm/worker/save-worker.ts +9 -10
  567. package/src/core/wasm/worker/types.ts +29 -18
  568. package/src/core/wasm/worker/worker.ts +88 -44
  569. package/src/css/globals.css +66 -22
  570. package/src/hooks/__tests__/useAsyncData.test.tsx +148 -0
  571. package/src/hooks/__tests__/useDebounce.test.tsx +51 -0
  572. package/src/hooks/__tests__/useDeepCompareMemoize.test.tsx +39 -0
  573. package/src/hooks/__tests__/useDelayElapsed.test.tsx +28 -0
  574. package/src/hooks/__tests__/useInternalStateWithSync.test.tsx +20 -0
  575. package/src/hooks/__tests__/useScript.test.tsx +77 -0
  576. package/src/hooks/__tests__/useStateForKey.test.tsx +257 -0
  577. package/src/hooks/useAsyncData.ts +3 -3
  578. package/src/hooks/useDataSourceDiscovery.ts +6 -4
  579. package/src/hooks/useDebounce.ts +6 -10
  580. package/src/hooks/useDeepCompareMemoize.ts +1 -1
  581. package/src/hooks/useDelayElapsed.ts +14 -10
  582. package/src/hooks/useElapsedTime.ts +3 -3
  583. package/src/hooks/useEventListener.ts +1 -2
  584. package/src/hooks/useInputHistory.ts +4 -7
  585. package/src/hooks/useInternalStateWithSync.ts +6 -6
  586. package/src/hooks/useResizeObserver.ts +4 -7
  587. package/src/hooks/useScript.ts +67 -70
  588. package/src/hooks/useStateForKey.ts +93 -0
  589. package/src/mount.tsx +23 -14
  590. package/src/plugins/__tests__/plugin-object-schemas.test.ts +104 -0
  591. package/src/plugins/core/registerReactComponent.tsx +1 -1
  592. package/src/plugins/impl/CodeEditorPlugin.tsx +1 -1
  593. package/src/plugins/impl/DataEditorPlugin.tsx +1 -1
  594. package/src/plugins/impl/DataTablePlugin.tsx +69 -37
  595. package/src/plugins/impl/DropdownPlugin.tsx +2 -6
  596. package/src/plugins/impl/FileBrowserPlugin.tsx +4 -16
  597. package/src/plugins/impl/FormPlugin.tsx +1 -1
  598. package/src/plugins/impl/RangeSliderPlugin.tsx +16 -2
  599. package/src/plugins/impl/RefreshPlugin.tsx +1 -0
  600. package/src/plugins/impl/SliderPlugin.tsx +6 -8
  601. package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +471 -1
  602. package/src/plugins/impl/__tests__/FileBrowserPlugin.test.tsx +45 -0
  603. package/src/plugins/impl/__tests__/RangeSliderPlugin.test.tsx +109 -0
  604. package/src/plugins/impl/__tests__/SliderPlugin.test.tsx +24 -3
  605. package/src/plugins/impl/anywidget/__tests__/registry.test.ts +153 -0
  606. package/src/plugins/impl/anywidget/__tests__/widget-binding.test.ts +241 -2
  607. package/src/plugins/impl/anywidget/model.ts +3 -1
  608. package/src/plugins/impl/anywidget/registry.ts +18 -14
  609. package/src/plugins/impl/anywidget/runtime.ts +21 -9
  610. package/src/plugins/impl/anywidget/widget-binding.ts +160 -82
  611. package/src/plugins/impl/chat/__tests__/chat-prompts.test.tsx +93 -0
  612. package/src/plugins/impl/chat/chat-ui.tsx +14 -11
  613. package/src/plugins/impl/code/any-language-editor.tsx +2 -2
  614. package/src/plugins/impl/data-editor/glide-data-editor.tsx +8 -5
  615. package/src/plugins/impl/data-editor/glide-utils.ts +3 -1
  616. package/src/plugins/impl/data-explorer/ConnectedDataExplorerComponent.tsx +1 -1
  617. package/src/plugins/impl/data-explorer/components/column-summary.tsx +4 -2
  618. package/src/plugins/impl/data-explorer/state/reducer.ts +1 -1
  619. package/src/plugins/impl/data-frames/DataFramePlugin.tsx +1 -2
  620. package/src/plugins/impl/data-frames/__tests__/sidebar.test.tsx +160 -0
  621. package/src/plugins/impl/data-frames/forms/__tests__/__snapshots__/form.test.tsx.snap +66 -66
  622. package/src/plugins/impl/data-frames/panel.tsx +43 -16
  623. package/src/plugins/impl/mpl-interactive/MplInteractivePlugin.tsx +5 -3
  624. package/src/plugins/impl/panel/PanelPlugin.tsx +16 -17
  625. package/src/plugins/impl/panel/__tests__/utils.test.ts +39 -1
  626. package/src/plugins/impl/panel/utils.ts +17 -0
  627. package/src/plugins/impl/plotly/PlotlyPlugin.tsx +2 -5
  628. package/src/plugins/impl/plotly/selection.ts +25 -19
  629. package/src/plugins/impl/vega/VegaPlugin.tsx +2 -3
  630. package/src/plugins/impl/vega/make-selectable.ts +4 -7
  631. package/src/plugins/layout/RoutesPlugin.tsx +1 -1
  632. package/src/stories/__tests__/editor-story.test.tsx +33 -0
  633. package/src/stories/button.stories.tsx +1 -0
  634. package/src/stories/cell.stories.tsx +2 -5
  635. package/src/stories/data-table.stories.tsx +31 -0
  636. package/src/stories/editor.stories.tsx +24 -17
  637. package/src/stories/form-wrapper.stories.tsx +2 -2
  638. package/src/stories/layout/vertical/one-column.stories.tsx +2 -5
  639. package/src/stories/runtime-story.tsx +23 -17
  640. package/src/stories/select.stories.tsx +2 -2
  641. package/src/stories/switchable-multi-select.stories.tsx +1 -1
  642. package/src/utils/__tests__/arrays.test.ts +35 -0
  643. package/src/utils/__tests__/batch-requests.test.ts +3 -3
  644. package/src/utils/__tests__/{createReducer.test.ts → createReducer.test.tsx} +29 -0
  645. package/src/utils/__tests__/dates.test.ts +116 -0
  646. package/src/utils/__tests__/numbers.test.ts +105 -1
  647. package/src/utils/__tests__/objects.test.ts +21 -0
  648. package/src/utils/arrays.ts +11 -2
  649. package/src/utils/blob.ts +1 -1
  650. package/src/utils/createReducer.ts +53 -26
  651. package/src/utils/dates.ts +94 -0
  652. package/src/utils/maps.ts +0 -3
  653. package/src/utils/numbers.ts +79 -18
  654. package/src/utils/objects.ts +4 -9
  655. package/src/utils/once.ts +19 -19
  656. package/src/utils/tracer.ts +9 -5
  657. package/src/utils/zod-utils.ts +1 -3
  658. package/dist/any-language-editor-B9REGlD9.js +0 -76
  659. package/dist/assets/__vite-browser-external-BXYkiFsk.js +0 -1
  660. package/dist/assets/worker-HPY9fbhz.js +0 -114
  661. package/dist/error-entries-CzoYkIQZ.js +0 -56
  662. package/dist/feedback-button-fND8YT59.js +0 -380
  663. package/dist/mermaid-HWGCJPDP-ByCsZnYF.js +0 -2
  664. package/dist/precisionRound-DMIVQrt2.js +0 -97
  665. package/src/components/editor/navigation/__tests__/state.test.ts +0 -81
  666. package/src/components/editor/navigation/state.ts +0 -65
  667. package/src/core/ai/tools/__tests__/run-cells-tool.test.ts +0 -656
  668. package/src/core/ai/tools/run-cells-tool.ts +0 -303
  669. package/src/core/cells/__tests__/apply-transaction.test.ts +0 -991
  670. package/src/core/cells/__tests__/document-changes.test.ts +0 -1140
  671. package/src/core/cells/__tests__/document-roundtrip.test.ts +0 -392
  672. package/src/core/cells/document-changes.ts +0 -944
  673. package/src/core/host/AGENTS.md +0 -6
  674. package/src/core/host/__tests__/protocol.test.ts +0 -214
  675. package/src/core/host/protocol.ts +0 -1943
  676. package/src/core/state/__tests__/jotai.test.ts +0 -25
  677. package/src/core/wasm/worker/message-buffer.ts +0 -33
  678. package/src/core/websocket/__tests__/useWebSocket.test.ts +0 -49
  679. package/src/core/websocket/transports/__tests__/basic.test.ts +0 -275
  680. package/src/core/websocket/transports/__tests__/sse.test.ts +0 -338
  681. package/src/core/websocket/transports/__tests__/transport.test.ts +0 -308
  682. package/src/core/websocket/transports/__tests__/ws.test.ts +0 -125
  683. package/src/core/websocket/transports/basic.ts +0 -102
  684. package/src/core/websocket/transports/sse.ts +0 -308
  685. package/src/core/websocket/transports/transport.ts +0 -61
  686. package/src/core/websocket/transports/ws.ts +0 -98
  687. package/src/core/websocket/useWebSocket.tsx +0 -88
  688. package/src/utils/__tests__/id-tree.test.ts +0 -2145
  689. package/src/utils/id-tree.tsx +0 -1134
  690. /package/src/core/{websocket → session}/connection-utils.ts +0 -0
@@ -21,14 +21,9 @@ import {
21
21
  vi,
22
22
  } from "vitest";
23
23
 
24
- vi.mock("@/core/websocket/useWebSocket", async () => {
25
- const actual =
26
- await vi.importActual<typeof import("../useWebSocket")>("../useWebSocket");
27
- return {
28
- ...actual,
29
- useConnectionTransport: vi.fn(),
30
- };
31
- });
24
+ vi.mock("../backend", () => ({
25
+ createEditorBackend: vi.fn(),
26
+ }));
32
27
 
33
28
  vi.mock("@/core/runtime/config", async () => {
34
29
  const actual = await vi.importActual<typeof import("@/core/runtime/config")>(
@@ -41,6 +36,7 @@ vi.mock("@/core/runtime/config", async () => {
41
36
  });
42
37
 
43
38
  import { MockNotebook } from "@/__mocks__/notebook";
39
+ import { Deferred } from "@/utils/Deferred";
44
40
  import { cellId } from "@/__tests__/branded";
45
41
  import { alertAtom, getPackageAlert } from "@/core/alerts/state";
46
42
  import { notebookAtom } from "@/core/cells/cells";
@@ -51,47 +47,108 @@ import type { NotificationPayload } from "@/core/kernel/messages";
51
47
  import { useRuntimeManager } from "@/core/runtime/config";
52
48
  import { initialRunCompletedAtom } from "../../kernel/state";
53
49
  import { connectionAtom, startupProgressAtom } from "../../network/connection";
54
- import type { SessionId } from "../../kernel/session";
50
+ import { generateSessionId } from "../../kernel/session";
51
+ import { RuntimeManager } from "../../runtime/runtime";
55
52
  import { WebSocketClosedReason, ConnectionState } from "../types";
56
- import type { IConnectionTransport } from "../transports/transport";
57
- import { useMarimoKernelConnection } from "../useMarimoKernelConnection";
58
- import { useConnectionTransport } from "../useWebSocket";
59
-
60
- interface MockTransport {
61
- readyState: 0 | 1 | 2 | 3;
62
- reconnect: Mock<IConnectionTransport["reconnect"]>;
63
- close: Mock<IConnectionTransport["close"]>;
64
- send: Mock<IConnectionTransport["send"]>;
65
- addEventListener: Mock<IConnectionTransport["addEventListener"]>;
66
- removeEventListener: Mock<IConnectionTransport["removeEventListener"]>;
53
+ import { classifyCloseEvent } from "../../transports/connection-state";
54
+ import { createEditorBackend } from "../backend";
55
+ import {
56
+ fakeBackend,
57
+ fixtureMessages,
58
+ } from "../../notebook/replica/__tests__/fixtures";
59
+ import { useNotebookSession } from "../useNotebookSession";
60
+ import { type NotebookTransport, StreamStopped } from "@marimo-team/client";
61
+
62
+ const SESSION_ID = generateSessionId();
63
+
64
+ /** One connection the fake transport opened. */
65
+ interface Attempt {
66
+ open(): void;
67
+ receive(message: unknown): void;
68
+ drop(): void;
69
+ stop(error: StreamStopped): void;
70
+ }
71
+
72
+ /** A transport the test drives, one connection at a time. */
73
+ function makeTransport() {
74
+ const attempts: Attempt[] = [];
75
+ const transport: NotebookTransport = ({ signal }, handlers) =>
76
+ new Promise((resolve, reject) => {
77
+ attempts.push({
78
+ open: handlers.open,
79
+ receive: handlers.message,
80
+ drop: resolve,
81
+ stop: reject,
82
+ });
83
+ signal.addEventListener("abort", () => resolve(), { once: true });
84
+ });
85
+ return { transport, attempts };
67
86
  }
68
87
 
69
- function makeTransport(
70
- readyState: 0 | 1 | 2 | 3 = WebSocket.CLOSED,
71
- ): MockTransport {
88
+ type FakeTransport = ReturnType<typeof makeTransport>;
89
+
90
+ /** Use a fake transport for the next session the hook follows. */
91
+ function withTransport(fake: FakeTransport = makeTransport()) {
92
+ vi.mocked(createEditorBackend).mockImplementation(({ checkHealth }) =>
93
+ fakeBackend({ transport: fake.transport, checkHealth }),
94
+ );
95
+ return fake;
96
+ }
97
+
98
+ /** Let the stream open its first connection. */
99
+ const started = () => act(() => Promise.resolve());
100
+
101
+ /** Drive the latest connection the fake transport opened. */
102
+ function connection(fake: FakeTransport) {
103
+ const current = () => {
104
+ const attempt = fake.attempts.at(-1);
105
+ if (attempt === undefined) {
106
+ throw new Error("No connection was opened");
107
+ }
108
+ return attempt;
109
+ };
72
110
  return {
73
- readyState,
74
- reconnect: vi.fn(),
75
- close: vi.fn(),
76
- send: vi.fn(),
77
- addEventListener: vi.fn(),
78
- removeEventListener: vi.fn(),
111
+ open: () => current().open(),
112
+ receive: (message: unknown) => current().receive(message),
113
+ /** Deliver a notification as the session routes frame it. */
114
+ send: (notification: unknown) =>
115
+ current().receive({ event: "marimo", data: { marimo: notification } }),
116
+ /** End the connection so the stream reconnects. */
117
+ drop: () => current().drop(),
118
+ /** Close the connection with a reason the editor treats as final. */
119
+ close: (reason: string) =>
120
+ current().stop(
121
+ new StreamStopped(reason, {
122
+ cause: classifyCloseEvent({ reason }).status,
123
+ }),
124
+ ),
79
125
  };
80
126
  }
81
127
 
82
128
  function makeRuntimeManager(
83
129
  reconcileFromHealth = vi.fn<() => Promise<boolean>>().mockResolvedValue(true),
84
130
  ) {
85
- return {
131
+ const runtime = new RuntimeManager(
132
+ { url: "http://localhost", lazy: true },
133
+ true,
134
+ );
135
+ vi.spyOn(runtime, "reconcileFromHealth").mockImplementation(
86
136
  reconcileFromHealth,
87
- probeHealth: vi.fn().mockResolvedValue(true),
88
- getWsURL: () => new URL("ws://localhost/ws"),
89
- waitForHealthy: vi.fn().mockResolvedValue(undefined),
90
- isSameOrigin: true,
91
- };
137
+ );
138
+ vi.spyOn(runtime, "probeHealth").mockResolvedValue(true);
139
+ vi.spyOn(runtime, "getWsURL").mockReturnValue(new URL("ws://localhost/ws"));
140
+ // Keep the initial connection pending; these tests exercise session retries.
141
+ vi.spyOn(runtime, "waitForHealthy").mockReturnValue(
142
+ new Deferred<void>().promise,
143
+ );
144
+ vi.spyOn(runtime, "isSameOrigin", "get").mockReturnValue(false);
145
+ return runtime;
92
146
  }
93
147
 
94
- function renderConnectionHook(store: ReturnType<typeof createStore>) {
148
+ function renderConnectionHook(
149
+ store: ReturnType<typeof createStore>,
150
+ { reactStrictMode = false } = {},
151
+ ) {
95
152
  const wrapper: React.FC<React.PropsWithChildren> = ({ children }) => (
96
153
  <JotaiProvider store={store}>
97
154
  <ErrorBoundary fallback={null}>{children}</ErrorBoundary>
@@ -99,22 +156,21 @@ function renderConnectionHook(store: ReturnType<typeof createStore>) {
99
156
  );
100
157
  return renderHook(
101
158
  () =>
102
- useMarimoKernelConnection({
103
- sessionId: "test-session" as SessionId,
159
+ useNotebookSession({
160
+ sessionId: SESSION_ID,
104
161
  autoInstantiate: false,
105
- setCells: () => {},
106
162
  }),
107
- { wrapper },
163
+ { wrapper, reactStrictMode },
108
164
  );
109
165
  }
110
166
 
111
- describe("useMarimoKernelConnection.reconnect()", () => {
112
- let transport: MockTransport;
167
+ describe("useNotebookSession.reconnect()", () => {
168
+ let fake: FakeTransport;
113
169
  let isHealthy: Mock<() => Promise<boolean>>;
114
170
  let store: ReturnType<typeof createStore>;
115
171
 
116
172
  beforeEach(() => {
117
- transport = makeTransport(WebSocket.CLOSED);
173
+ fake = withTransport();
118
174
  isHealthy = vi.fn<() => Promise<boolean>>().mockResolvedValue(true);
119
175
  store = createStore();
120
176
  store.set(connectionAtom, {
@@ -122,59 +178,53 @@ describe("useMarimoKernelConnection.reconnect()", () => {
122
178
  code: WebSocketClosedReason.KERNEL_DISCONNECTED,
123
179
  reason: "kernel not found",
124
180
  });
125
- vi.mocked(useConnectionTransport).mockReturnValue(transport);
126
- vi.mocked(useRuntimeManager).mockReturnValue(
127
- makeRuntimeManager(isHealthy) as unknown as ReturnType<
128
- typeof useRuntimeManager
129
- >,
130
- );
181
+ vi.mocked(useRuntimeManager).mockReturnValue(makeRuntimeManager(isHealthy));
131
182
  });
132
183
 
133
- function renderUseHook() {
134
- return renderConnectionHook(store);
184
+ async function renderStopped() {
185
+ const hook = renderConnectionHook(store);
186
+ await started();
187
+ await act(async () => connection(fake).close("MARIMO_SHUTDOWN"));
188
+ return hook;
135
189
  }
136
190
 
137
- it("is a no-op when the transport is already OPEN", async () => {
138
- transport.readyState = WebSocket.OPEN;
139
- const { result } = renderUseHook();
140
- await act(async () => {
141
- await result.current.reconnect();
142
- });
143
- expect(isHealthy).not.toHaveBeenCalled();
144
- expect(transport.reconnect).not.toHaveBeenCalled();
145
- });
146
-
147
- it("is a no-op when the transport is already CONNECTING", async () => {
148
- transport.readyState = WebSocket.CONNECTING;
149
- const { result } = renderUseHook();
150
- await act(async () => {
151
- await result.current.reconnect();
152
- });
153
- expect(isHealthy).not.toHaveBeenCalled();
154
- expect(transport.reconnect).not.toHaveBeenCalled();
155
- });
191
+ it.each(["connecting", "connected"])(
192
+ "is a no-op while the stream is %s",
193
+ async (state) => {
194
+ const { result } = renderConnectionHook(store);
195
+ await started();
196
+ if (state === "connected") {
197
+ act(() => connection(fake).open());
198
+ }
199
+ await act(async () => {
200
+ await result.current.reconnect();
201
+ });
202
+ expect(isHealthy).not.toHaveBeenCalled();
203
+ expect(fake.attempts).toHaveLength(1);
204
+ },
205
+ );
156
206
 
157
207
  it("probes /health and reconnects when the runtime is healthy", async () => {
158
208
  isHealthy.mockResolvedValue(true);
159
- const { result } = renderUseHook();
209
+ const { result } = await renderStopped();
160
210
  await act(async () => {
161
211
  await result.current.reconnect();
162
212
  });
163
213
  expect(isHealthy).toHaveBeenCalledOnce();
164
- expect(transport.reconnect).toHaveBeenCalledOnce();
214
+ expect(fake.attempts).toHaveLength(2);
165
215
  expect(store.get(connectionAtom)).toEqual({
166
216
  state: ConnectionState.CONNECTING,
167
217
  });
168
218
  });
169
219
 
170
- it("transitions to CLOSED and does not call ws.reconnect when the probe fails", async () => {
220
+ it("transitions to CLOSED and does not reconnect when the probe fails", async () => {
171
221
  isHealthy.mockResolvedValue(false);
172
- const { result } = renderUseHook();
222
+ const { result } = await renderStopped();
173
223
  await act(async () => {
174
224
  await result.current.reconnect();
175
225
  });
176
226
  expect(isHealthy).toHaveBeenCalledOnce();
177
- expect(transport.reconnect).not.toHaveBeenCalled();
227
+ expect(fake.attempts).toHaveLength(1);
178
228
  expect(store.get(connectionAtom)).toEqual({
179
229
  state: ConnectionState.CLOSED,
180
230
  code: WebSocketClosedReason.KERNEL_DISCONNECTED,
@@ -183,30 +233,85 @@ describe("useMarimoKernelConnection.reconnect()", () => {
183
233
  });
184
234
  });
185
235
 
186
- describe("useMarimoKernelConnection messages", () => {
187
- it("records completion of the initial run", () => {
236
+ describe("useNotebookSession document model", () => {
237
+ it("keeps drafts while the connection drops and discards them when the session ends", async () => {
238
+ const fake = withTransport();
239
+ vi.mocked(useRuntimeManager).mockReturnValue(makeRuntimeManager());
188
240
  const store = createStore();
189
- vi.mocked(useConnectionTransport).mockClear();
190
- vi.mocked(useConnectionTransport).mockReturnValue(
191
- makeTransport(WebSocket.OPEN),
192
- );
193
- vi.mocked(useRuntimeManager).mockReturnValue(
194
- makeRuntimeManager() as unknown as ReturnType<typeof useRuntimeManager>,
195
- );
196
- renderConnectionHook(store);
241
+ const { result, unmount } = renderConnectionHook(store, {
242
+ reactStrictMode: true,
243
+ });
244
+ await started();
245
+ act(() => {
246
+ connection(fake).open();
247
+ fixtureMessages("notebook-events.sse").forEach(connection(fake).receive);
248
+ });
249
+ const model = result.current.documentModel;
250
+ const code = () => store.get(model.documentAtom)?.cells.get("c1")?.code;
251
+ act(() => store.set(model.editAtom, (tx) => tx.setCode("c1", "local")));
252
+ expect(code()).toBe("local");
197
253
 
198
- const options = vi.mocked(useConnectionTransport).mock.calls.at(-1)?.[0];
199
- expect(store.get(initialRunCompletedAtom)).toBe(false);
254
+ await act(async () => connection(fake).drop());
255
+ expect(result.current.documentModel).toBe(model);
256
+ expect(code()).toBe("local");
257
+
258
+ unmount();
259
+ expect(code()).toBeUndefined();
260
+ await expect(model.flush()).rejects.toThrow("not started");
261
+ });
262
+
263
+ it("follows the host document into the notebook's cells", async () => {
264
+ const fake = withTransport();
265
+ vi.mocked(useRuntimeManager).mockReturnValue(makeRuntimeManager());
266
+ const store = createStore();
267
+ const { result, unmount } = renderConnectionHook(store);
268
+ await started();
200
269
  act(() => {
201
- options?.onMessage(
202
- new MessageEvent("message", {
203
- data: JSON.stringify({
204
- op: "completed-run",
205
- data: { op: "completed-run", run_id: null },
206
- }),
207
- }),
208
- );
270
+ connection(fake).open();
271
+ fixtureMessages("notebook-events.sse").forEach(connection(fake).receive);
272
+ });
273
+
274
+ let notebook = store.get(notebookAtom);
275
+ expect(notebook.cellIds).toEqual(["c1", "c3", "k7f2"]);
276
+ expect(notebook.cellData[cellId("k7f2")]).toMatchObject({
277
+ code: "y = x * 2",
278
+ config: { hide_code: true },
209
279
  });
280
+
281
+ const model = result.current.documentModel;
282
+ act(() => store.set(model.editAtom, (tx) => tx.setCode("c3", "x = 42")));
283
+ notebook = store.get(notebookAtom);
284
+ expect(notebook.cellData[cellId("c3")].code).toBe("x = 42");
285
+
286
+ unmount();
287
+ });
288
+ });
289
+
290
+ describe("useNotebookSession messages", () => {
291
+ let store: ReturnType<typeof createStore>;
292
+ let fake: FakeTransport;
293
+
294
+ beforeEach(async () => {
295
+ store = createStore();
296
+ fake = withTransport();
297
+ vi.mocked(useRuntimeManager).mockReturnValue(makeRuntimeManager());
298
+ renderConnectionHook(store);
299
+ await started();
300
+ });
301
+
302
+ function receive(payload: unknown) {
303
+ act(() => connection(fake).receive(payload));
304
+ }
305
+
306
+ it("records completion of the initial run", () => {
307
+ const notification = {
308
+ op: "completed-run",
309
+ run_id: null,
310
+ outcome: "succeeded",
311
+ error: null,
312
+ } satisfies NotificationPayload["data"];
313
+ expect(store.get(initialRunCompletedAtom)).toBe(false);
314
+ receive({ id: "42", event: "marimo", data: { marimo: notification } });
210
315
  expect(store.get(initialRunCompletedAtom)).toBe(true);
211
316
  });
212
317
  });
@@ -216,47 +321,30 @@ it.each(["kernel-ready", "reconnected"])(
216
321
  async (op) => {
217
322
  const store = createStore();
218
323
  store.set(connectionAtom, { state: ConnectionState.CONNECTING });
219
- vi.mocked(useConnectionTransport).mockClear();
220
- vi.mocked(useConnectionTransport).mockReturnValue(
221
- makeTransport(WebSocket.OPEN),
222
- );
223
- vi.mocked(useRuntimeManager).mockReturnValue(
224
- makeRuntimeManager() as unknown as ReturnType<typeof useRuntimeManager>,
225
- );
324
+ const fake = withTransport();
325
+ vi.mocked(useRuntimeManager).mockReturnValue(makeRuntimeManager());
226
326
  renderConnectionHook(store);
227
- const options = vi.mocked(useConnectionTransport).mock.calls.at(-1)![0];
327
+ await started();
328
+ const conn = connection(fake);
228
329
 
229
- await act(async () => {
230
- await options.onOpen(new Event("open"));
231
- });
330
+ await act(async () => conn.open());
232
331
  expect(store.get(connectionAtom).state).toBe(ConnectionState.CONNECTING);
233
332
 
234
- act(() => {
235
- options.onMessage(
236
- new MessageEvent("message", {
237
- data: JSON.stringify({
238
- op,
239
- data: {
240
- op,
241
- cell_ids: [],
242
- codes: [],
243
- names: [],
244
- configs: [],
245
- layout: null,
246
- resumed: true,
247
- ui_values: {},
248
- last_executed_code: {},
249
- last_execution_time: {},
250
- app_config: { width: "normal" },
251
- kiosk: false,
252
- capabilities: { terminal: false },
253
- auto_instantiated: false,
254
- consumer_capabilities: { edit: true, interact: true },
255
- },
256
- }),
257
- }),
258
- );
259
- });
333
+ act(() =>
334
+ conn.send({
335
+ op,
336
+ layout: null,
337
+ resumed: true,
338
+ ui_values: {},
339
+ last_executed_code: {},
340
+ last_execution_time: {},
341
+ app_config: { width: "normal" },
342
+ kiosk: false,
343
+ capabilities: { terminal: false },
344
+ auto_instantiated: false,
345
+ consumer_capabilities: { edit: true, interact: true },
346
+ }),
347
+ );
260
348
  expect(store.get(connectionAtom).state).toBe(ConnectionState.OPEN);
261
349
  },
262
350
  );
@@ -265,19 +353,15 @@ describe("connection notice", () => {
265
353
  beforeEach(() => vi.useFakeTimers());
266
354
  afterEach(() => vi.useRealTimers());
267
355
 
268
- function renderNotice() {
356
+ async function renderNotice() {
269
357
  const store = createStore();
270
358
  store.set(connectionAtom, { state: ConnectionState.CONNECTING });
271
- const transport = makeTransport(WebSocket.OPEN);
272
- vi.mocked(useConnectionTransport).mockReturnValue(transport);
273
- vi.mocked(useRuntimeManager).mockReturnValue(
274
- makeRuntimeManager() as unknown as ReturnType<typeof useRuntimeManager>,
275
- );
359
+ const fake = withTransport();
360
+ vi.mocked(useRuntimeManager).mockReturnValue(makeRuntimeManager());
276
361
  const Connection = () => {
277
- const { reconnect } = useMarimoKernelConnection({
278
- sessionId: "test-session" as SessionId,
362
+ const { reconnect } = useNotebookSession({
363
+ sessionId: SESSION_ID,
279
364
  autoInstantiate: false,
280
- setCells: () => {},
281
365
  });
282
366
  return (
283
367
  <ConnectionNotice
@@ -293,20 +377,17 @@ describe("connection notice", () => {
293
377
  </ErrorBoundary>
294
378
  </JotaiProvider>,
295
379
  );
296
- const options = vi.mocked(useConnectionTransport).mock.calls.at(-1)![0];
380
+ await started();
381
+ const conn = connection(fake);
382
+ // Messages arrive on an open connection.
383
+ act(() => conn.open());
297
384
  const send = (data: NotificationPayload["data"]) =>
298
- act(() => {
299
- options.onMessage(
300
- new MessageEvent("message", {
301
- data: JSON.stringify({ op: data.op, data }),
302
- }),
303
- );
304
- });
305
- return { store, transport, options, send };
385
+ act(() => conn.send(data));
386
+ return { store, fake, conn, send };
306
387
  }
307
388
 
308
- it("keeps elapsed time across startup phases and leaves reconnection to the footer once cells are available", () => {
309
- const { store, options, send } = renderNotice();
389
+ it("keeps elapsed time across startup phases and leaves reconnection to the footer once cells are available", async () => {
390
+ const { store, conn, send } = await renderNotice();
310
391
  send({
311
392
  op: "startup-progress",
312
393
  phase: "preparing-environment",
@@ -346,7 +427,7 @@ describe("connection notice", () => {
346
427
  expect(
347
428
  screen.queryByRole("region", { name: "Notebook startup" }),
348
429
  ).not.toBeInTheDocument();
349
- act(() => options.onClose(new CloseEvent("close")));
430
+ await act(async () => conn.drop());
350
431
  act(() => vi.advanceTimersByTime(500));
351
432
  expect(store.get(connectionAtom)).toEqual({
352
433
  state: ConnectionState.CONNECTING,
@@ -357,8 +438,8 @@ describe("connection notice", () => {
357
438
  ).not.toBeInTheDocument();
358
439
  });
359
440
 
360
- it("restores startup output before appending live chunks and resets it for a new attempt", () => {
361
- const { store, send } = renderNotice();
441
+ it("restores startup output before appending live chunks and resets it for a new attempt", async () => {
442
+ const { store, send } = await renderNotice();
362
443
  const snapshot = {
363
444
  op: "startup-progress",
364
445
  phase: "starting-kernel",
@@ -384,7 +465,7 @@ describe("connection notice", () => {
384
465
  });
385
466
 
386
467
  it("restores completed startup output without putting a connected kernel back into startup", async () => {
387
- const { store, options, send } = renderNotice();
468
+ const { store, conn, send } = await renderNotice();
388
469
  send({ op: "reconnected" });
389
470
  send({
390
471
  op: "startup-progress",
@@ -402,13 +483,15 @@ describe("connection notice", () => {
402
483
  screen.queryByRole("region", { name: "Notebook startup" }),
403
484
  ).not.toBeInTheDocument();
404
485
 
405
- // A new transport starts from the next session's authoritative snapshot.
406
- await act(async () => options.onOpen(new Event("open")));
486
+ // A new connection starts from the next session's authoritative snapshot.
487
+ await act(async () => conn.drop());
488
+ await act(async () => vi.advanceTimersByTime(2000));
489
+ await act(async () => conn.open());
407
490
  expect(store.get(startupProgressAtom)).toBeNull();
408
491
  });
409
492
 
410
- it("does not flash a notice when startup finishes within the delay", () => {
411
- const { send } = renderNotice();
493
+ it("does not flash a notice when startup finishes within the delay", async () => {
494
+ const { send } = await renderNotice();
412
495
  send({
413
496
  op: "startup-progress",
414
497
  phase: "preparing-environment",
@@ -436,16 +519,11 @@ describe("connection notice", () => {
436
519
  it.each(["preparing-environment", "starting-kernel"] as const)(
437
520
  "keeps a %s failure available until the user retries",
438
521
  async (phase) => {
439
- const { store, transport, options, send } = renderNotice();
522
+ const { store, fake, conn, send } = await renderNotice();
440
523
  send({ op: "startup-progress", phase, logs: "", log_mode: "replace" });
441
524
  const error = "A full diagnostic\nwith <stderr> details";
442
525
  send({ op: "kernel-startup-error", error });
443
- transport.readyState = WebSocket.CLOSED;
444
- act(() =>
445
- options.onClose(
446
- new CloseEvent("close", { reason: "MARIMO_KERNEL_STARTUP_ERROR" }),
447
- ),
448
- );
526
+ await act(async () => conn.close("MARIMO_KERNEL_STARTUP_ERROR"));
449
527
  expect(screen.getByRole("status")).toHaveTextContent(
450
528
  "Kernel failed to start",
451
529
  );
@@ -456,11 +534,11 @@ describe("connection notice", () => {
456
534
  expect(screen.getByLabelText("Error details")).toHaveTextContent(error, {
457
535
  normalizeWhitespace: false,
458
536
  });
459
- expect(transport.reconnect).not.toHaveBeenCalled();
537
+ expect(fake.attempts).toHaveLength(1);
460
538
  await act(async () =>
461
539
  fireEvent.click(screen.getByRole("button", { name: "Try again" })),
462
540
  );
463
- expect(transport.reconnect).toHaveBeenCalledOnce();
541
+ expect(fake.attempts).toHaveLength(2);
464
542
  expect(store.get(kernelStartupErrorAtom)).toBeNull();
465
543
  expect(
466
544
  screen.queryByRole("button", { name: "Try again" }),
@@ -480,25 +558,14 @@ describe("connection notice", () => {
480
558
  );
481
559
  });
482
560
 
483
- it("replaces environment state from snapshots, then continues live progress", () => {
561
+ it("replaces environment state from snapshots, then continues live progress", async () => {
484
562
  const store = createStore();
485
- vi.mocked(useConnectionTransport).mockClear();
486
- vi.mocked(useConnectionTransport).mockReturnValue(
487
- makeTransport(WebSocket.OPEN),
488
- );
489
- vi.mocked(useRuntimeManager).mockReturnValue(
490
- makeRuntimeManager() as unknown as ReturnType<typeof useRuntimeManager>,
491
- );
563
+ const fake = withTransport();
564
+ vi.mocked(useRuntimeManager).mockReturnValue(makeRuntimeManager());
492
565
  renderConnectionHook(store);
493
- const options = vi.mocked(useConnectionTransport).mock.calls.at(-1)?.[0];
566
+ await started();
494
567
  function receive(data: NotificationPayload["data"]) {
495
- act(() =>
496
- options?.onMessage(
497
- new MessageEvent("message", {
498
- data: JSON.stringify({ op: data.op, data }),
499
- }),
500
- ),
501
- );
568
+ act(() => connection(fake).send(data));
502
569
  }
503
570
  receive({
504
571
  op: "environment-operation",
@@ -0,0 +1,22 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { atom } from "jotai";
4
+ import type { DocumentModel } from "../notebook/document";
5
+ import type { NotebookReplica } from "../notebook/replica";
6
+
7
+ /** The replica owned by the mounted notebook session. */
8
+ export const activeNotebookReplicaAtom = atom<NotebookReplica | null>(null);
9
+
10
+ /** The document model owned by the mounted notebook session. */
11
+ export const activeDocumentModelAtom = atom<DocumentModel | null>(null);
12
+
13
+ /**
14
+ * The document the host last confirmed, or null while no notebook is
15
+ * connected. Facts the host owns read this one, such as permission, the
16
+ * app config, saved state and whether source is available. What the user
17
+ * is editing reads the model's document, which adds this browser's drafts.
18
+ */
19
+ export const confirmedDocumentAtom = atom((get) => {
20
+ const replica = get(activeNotebookReplicaAtom);
21
+ return replica === null ? null : get(replica.documentAtom);
22
+ });