@marimo-team/islands 0.25.3-dev141 → 0.25.3-dev142

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 (472) hide show
  1. package/dist/{ConnectedDataExplorerComponent-CNSxdKyJ.js → ConnectedDataExplorerComponent-BAiSVaHj.js} +17 -15
  2. package/dist/{ErrorBoundary-Cg59O3xL.js → ErrorBoundary-DxdeETpm.js} +16 -16
  3. package/dist/{abnfDiagram-VCTEODGH-DQDjNXt3.js → abnfDiagram-VCTEODGH-BBHN92-F.js} +2 -2
  4. package/dist/any-language-editor-3E24SnCV.js +75 -0
  5. package/dist/{architectureDiagram-5GKGNRK7-Nmls7Wxn.js → architectureDiagram-5GKGNRK7-C0Rd64qh.js} +1 -1
  6. package/dist/{arrays-CN1djC4k.js → arrays-R6JsImiT.js} +8 -0
  7. package/dist/assets/__vite-browser-external-BgQNn4dS.js +1 -0
  8. package/dist/assets/worker-FKe3RHT4.js +113 -0
  9. package/dist/{button-CvjkwdSk.js → button-BQRdqQi1.js} +199 -192
  10. package/dist/{capabilities-CQ5IjhzL.js → capabilities-CI4xUPsp.js} +2 -2
  11. package/dist/{chat-ui-BDpymeoe.js → chat-ui-CvB8Map3.js} +1195 -1195
  12. package/dist/{check-EDwSwP89.js → check-SpOMoCLP.js} +1 -1
  13. package/dist/{chunk-IMKFNOWR-CoIFEo-1.js → chunk-IMKFNOWR-hJXey5p2.js} +1 -1
  14. package/dist/{chunk-SHT3W25Y-lHiGx3ZC.js → chunk-SHT3W25Y-JMzTmmVW.js} +1 -1
  15. package/dist/{chunk-SVP7TREG-BvsHIrY_.js → chunk-SVP7TREG-BIyoHTXi.js} +1 -1
  16. package/dist/{chunk-TICWLB2K-BC8YIm_n.js → chunk-TICWLB2K-CCzH2Uwa.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-DB1kOPS6.js} +2 -2
  19. package/dist/{classDiagram-v2-VYDZK3BY-D9bfbcC3.js → classDiagram-v2-VYDZK3BY-CPaFL0OB.js} +2 -2
  20. package/dist/{code-visibility-Da3iitn_.js → code-visibility-CO8abjLo.js} +17 -17
  21. package/dist/{common-C6FHcu5p.js → common-CbFJQhaI.js} +6341 -5870
  22. package/dist/{copy-icon-Ft7t9bqz.js → copy-icon-C1trdvVt.js} +3 -3
  23. package/dist/{cynefinDiagram-5FMLGOSQ-CgWra7wt.js → cynefinDiagram-5FMLGOSQ-CF93uliH.js} +1 -1
  24. package/dist/{dagre-GXQ25YYZ-CFZ8-35c.js → dagre-GXQ25YYZ-BBB-_p9D.js} +1 -1
  25. package/dist/{data-grid-overlay-editor-mJt3Pg4S.js → data-grid-overlay-editor-DBjVUS2K.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-BynCnbyt.js} +1 -1
  28. package/dist/{diagram-UQ7AKVKN-CQPQ6pP_.js → diagram-UQ7AKVKN-svOxikMa.js} +1 -1
  29. package/dist/{diagram-VX7I27RA-Cf3b0llz.js → diagram-VX7I27RA-BVQSnPx0.js} +1 -1
  30. package/dist/{diagram-Z3DM3KII-Bt4QLrpn.js → diagram-Z3DM3KII-nR1J0BH5.js} +1 -1
  31. package/dist/{dialog-CEBFZNyc.js → dialog-DgeqyrEZ.js} +3 -3
  32. package/dist/{dist-DpBpA_n1.js → dist-Lm-D-Vr6.js} +3 -3
  33. package/dist/{dropdown-menu-CP_qCYHC.js → dropdown-menu-GWcGCcxE.js} +1277 -1229
  34. package/dist/{ebnfDiagram-PWID7BFC-D3NAyPFY.js → ebnfDiagram-PWID7BFC-C9tq9-Qo.js} +2 -2
  35. package/dist/{erDiagram-RLTQ6QDP-Tlu09_Nm.js → erDiagram-RLTQ6QDP-BiaeB7Fi.js} +1 -1
  36. package/dist/{error-banner-Da600cwz.js → error-banner-D40pmfQQ.js} +86 -81
  37. package/dist/{cells-C8iCYvLU.js → error-entries-CmFDQ9CA.js} +1718 -1621
  38. package/dist/{feedback-button-DaPwHIdM.js → feedback-button-CWQoIUKQ.js} +122 -123
  39. package/dist/{flowDiagram-HODETNUW-CAYbpJmV.js → flowDiagram-HODETNUW-f-sLm87k.js} +2 -2
  40. package/dist/{formats-BGh2thRc.js → formats-CCxaLPNs.js} +3 -3
  41. package/dist/{ganttDiagram-EL5Y4UJY-BQT16JSc.js → ganttDiagram-EL5Y4UJY-Dh_4cBJZ.js} +2 -2
  42. package/dist/{glide-data-editor-Ctz2mH6k.js → glide-data-editor-BxT8eGip.js} +370 -368
  43. package/dist/{glide-utils-DJyqh0EM.js → glide-utils-Dz_F4pAY.js} +16 -16
  44. package/dist/{globals-EpFoRj_E.js → globals-f0UxWZkr.js} +3 -3
  45. package/dist/{highlighted-body-KPVGNVTW-zwjq21tf.js → highlighted-body-KPVGNVTW-3Ziwfz-V.js} +2 -2
  46. package/dist/{html-to-image-CZY1pIZH.js → html-to-image-BrC8OJcj.js} +634 -597
  47. package/dist/{infoDiagram-27XIBGKW-XrDxhr0x.js → infoDiagram-27XIBGKW-BHrWywBe.js} +1 -1
  48. package/dist/{ishikawaDiagram-5VMMS53U-CD1muzov.js → ishikawaDiagram-5VMMS53U-Da4s0Reg.js} +1 -1
  49. package/dist/{kanban-definition-UXKFOSKX-Dg9W142_.js → kanban-definition-UXKFOSKX-DDf1GVLj.js} +1 -1
  50. package/dist/{label-BDKF3Afc.js → label-D9_-zSg-.js} +3 -3
  51. package/dist/{linear-Cnaq3Tfb.js → linear-DWERh7eI.js} +121 -73
  52. package/dist/{loader-BQOfjTIJ.js → loader-DOVB0Syl.js} +12 -12
  53. package/dist/main.js +7548 -7226
  54. package/dist/{mermaid-CiAXjLpA.js → mermaid-ClwTt7OD.js} +38 -38
  55. package/dist/mermaid-HWGCJPDP-Du3-HPQA.js +2 -0
  56. package/dist/{mindmap-definition-YA3MSWOX-DgCidgYJ.js → mindmap-definition-YA3MSWOX-D60oMZtT.js} +1 -1
  57. package/dist/{number-overlay-editor-CtxrummL.js → number-overlay-editor-Dso2H7DV.js} +3 -3
  58. package/dist/{objects-CzUgoMIF.js → objects-DCmMvZtn.js} +1 -3
  59. package/dist/{once-BtE-wKRL.js → once-eG8Vmvjd.js} +1 -1
  60. package/dist/{pegDiagram-XKGWAZYB-coOYYKB6.js → pegDiagram-XKGWAZYB-B9wFpMga.js} +2 -2
  61. package/dist/{pieDiagram-E7YTZNPT-BnXTc3bj.js → pieDiagram-E7YTZNPT-BZwvMuBT.js} +1 -1
  62. package/dist/precisionRound-DhAzeRcm.js +49 -0
  63. package/dist/{process-output-e4jj9Ijt.js → process-output-Bw-vzyoy.js} +4 -4
  64. package/dist/{quadrantDiagram-AXDQQJYC-Ccw0xMjN.js → quadrantDiagram-AXDQQJYC-Blrm7V7_.js} +1 -1
  65. package/dist/{railroadDiagram-O6MQD6OU-B28-u83n.js → railroadDiagram-O6MQD6OU-DqhZGsN2.js} +2 -2
  66. package/dist/{react-vega-BED0DjiB.js → react-vega-C4T9Rb_w.js} +12284 -12270
  67. package/dist/{requirementDiagram-BXWQKSXE-CdYCTMu0.js → requirementDiagram-BXWQKSXE-CL5dYARV.js} +1 -1
  68. package/dist/{reveal-component-DrIXvR_7.js → reveal-component-DMglhlng.js} +14 -14
  69. package/dist/{select-BButFi-d.js → select-CkF_GBxO.js} +537 -458
  70. package/dist/{sequenceDiagram-WJ2MYXX4-CM3aRAe_.js → sequenceDiagram-WJ2MYXX4-BSUCRd2x.js} +1 -1
  71. package/dist/{spec-BwarBpUJ.js → spec-DiSh_qJC.js} +41 -11
  72. package/dist/{stateDiagram-D77RDMKH-DOZUMVj8.js → stateDiagram-D77RDMKH-B-1Hvyb0.js} +2 -2
  73. package/dist/{stateDiagram-v2-MP3YSRHH-CISYxma6.js → stateDiagram-v2-MP3YSRHH--HtiR4yB.js} +2 -2
  74. package/dist/style.css +1 -1
  75. package/dist/{swimlanes-42K2YHIH-BkSppxp8.js → swimlanes-42K2YHIH-Bdfhrd-2.js} +1 -1
  76. package/dist/{swimlanesDiagram-VR7AAH4N-c8G5pGxC.js → swimlanesDiagram-VR7AAH4N-BJIfxSz0.js} +2 -2
  77. package/dist/{swiper-component-DKntyn8p.js → swiper-component-BkCm-kLg.js} +2 -2
  78. package/dist/{time-BieKwdYS.js → time-BaMbJs4z.js} +18 -18
  79. package/dist/{timeline-definition-24CTP7MA-CaMQayqK.js → timeline-definition-24CTP7MA-Cs4EfFD3.js} +1 -1
  80. package/dist/{toDate-BNJf01u1.js → toDate-DGvRHwTk.js} +57 -54
  81. package/dist/{toast-B9UMEi8u.js → toast-Btbve3Lm.js} +231 -213
  82. package/dist/{tooltip-BO1REbqs.js → tooltip-CJ4Rqasp.js} +327 -303
  83. package/dist/{use-restore-focus-BEM7sFWl.js → use-restore-focus-CmGwRNId.js} +1482 -1433
  84. package/dist/{useAsyncData-Deh5kszT.js → useAsyncData-BmVgnMP5.js} +15 -14
  85. package/dist/{useDateFormatter-D8XruNGG.js → useDateFormatter-eCL_SdeG.js} +2 -2
  86. package/dist/{useDeepCompareMemoize-DXwzRINI.js → useDeepCompareMemoize-Ch_AeGu6.js} +1 -1
  87. package/dist/{useIframeCapabilities-DBETvvPl.js → useIframeCapabilities-CSdWn6NP.js} +1 -1
  88. package/dist/{useLifecycle-j7D9uD1o.js → useLifecycle-B5BX921H.js} +3 -3
  89. package/dist/{useTheme-CRnjkku0.js → useTheme-Bq9pi_vz.js} +6 -6
  90. package/dist/{vega-component-DG29TAB7.js → vega-component-BgyZ-COt.js} +147 -147
  91. package/dist/{vega-embed-container-BNHE18G9.js → vega-embed-container--LYZqguO.js} +2 -2
  92. package/dist/{vega-loader.browser-DNST6uVf.js → vega-loader.browser-B9Pz0EGP.js} +298 -507
  93. package/dist/{vennDiagram-4TSXK5OY-CF8pfaK3.js → vennDiagram-4TSXK5OY-DRu0vRjO.js} +1 -1
  94. package/dist/{wardleyDiagram-VM6X3IG4-dGPu39d-.js → wardleyDiagram-VM6X3IG4-DzbDxwDY.js} +1 -1
  95. package/dist/{xychartDiagram-S5SC5T6Z-CP6-9q56.js → xychartDiagram-S5SC5T6Z-CLWj6ZFQ.js} +2 -2
  96. package/package.json +15 -12
  97. package/src/__tests__/fixtures/{pair-preview.ts → pair-config.ts} +3 -2
  98. package/src/__tests__/host-imports.test.ts +101 -0
  99. package/src/__tests__/module-entry.test.ts +32 -0
  100. package/src/__tests__/mount.test.ts +8 -8
  101. package/src/components/ai/__tests__/ai-model-dropdown.test.tsx +63 -0
  102. package/src/components/ai/ai-model-dropdown.tsx +11 -6
  103. package/src/components/app-config/ai-config.tsx +7 -2
  104. package/src/components/chat/__tests__/message-queue.test.tsx +17 -0
  105. package/src/components/chat/acp/__tests__/session-tabs.test.tsx +43 -0
  106. package/src/components/chat/acp/__tests__/state.test.ts +2 -2
  107. package/src/components/chat/acp/blocks.tsx +3 -2
  108. package/src/components/chat/acp/session-tabs.tsx +8 -3
  109. package/src/components/chat/chat-components.tsx +2 -0
  110. package/src/components/chat/chat-panel.tsx +9 -8
  111. package/src/components/chat/chat-utils.ts +4 -8
  112. package/src/components/data-table/TableTopBar.tsx +2 -0
  113. package/src/components/data-table/__tests__/chart-spec-model.test.ts +195 -195
  114. package/src/components/data-table/__tests__/column-explorer.test.tsx +27 -1
  115. package/src/components/data-table/__tests__/columns.test.tsx +105 -0
  116. package/src/components/data-table/__tests__/context-menu-interaction.test.tsx +130 -0
  117. package/src/components/data-table/__tests__/context-menu.test.ts +176 -0
  118. package/src/components/data-table/__tests__/data-table.test.tsx +1 -0
  119. package/src/components/data-table/__tests__/date-filter-inputs.test.tsx +24 -1
  120. package/src/components/data-table/__tests__/date-popover.test.tsx +72 -0
  121. package/src/components/data-table/__tests__/export-actions.test.tsx +434 -6
  122. package/src/components/data-table/__tests__/keyboard-selection.test.tsx +188 -0
  123. package/src/components/data-table/__tests__/search-highlight.test.tsx +109 -0
  124. package/src/components/data-table/__tests__/url-detector.test.tsx +74 -0
  125. package/src/components/data-table/cell-selection/feature.ts +2 -2
  126. package/src/components/data-table/cell-selection/types.ts +2 -2
  127. package/src/components/data-table/charts/__tests__/chart-panel.test.tsx +74 -1
  128. package/src/components/data-table/charts/__tests__/spec-snapshot.test.ts +20 -20
  129. package/src/components/data-table/charts/__tests__/spec.test.ts +78 -52
  130. package/src/components/data-table/charts/__tests__/storage.test.ts +4 -1
  131. package/src/components/data-table/charts/chart-spec/spec.ts +68 -43
  132. package/src/components/data-table/charts/charts.tsx +40 -30
  133. package/src/components/data-table/charts/storage.ts +16 -5
  134. package/src/components/data-table/column-explorer-panel/column-explorer.tsx +1 -1
  135. package/src/components/data-table/column-summary/chart-spec-model.tsx +23 -16
  136. package/src/components/data-table/column-summary/column-summary.tsx +2 -4
  137. package/src/components/data-table/column-visibility-dropdown.tsx +8 -13
  138. package/src/components/data-table/columns.tsx +45 -46
  139. package/src/components/data-table/context-menu.tsx +161 -46
  140. package/src/components/data-table/data-table.tsx +13 -1
  141. package/src/components/data-table/date-filter-inputs.tsx +15 -42
  142. package/src/components/data-table/date-popover.tsx +6 -4
  143. package/src/components/data-table/export-actions.tsx +267 -163
  144. package/src/components/data-table/range-focus/atoms.ts +4 -6
  145. package/src/components/data-table/range-focus/use-cell-range-selection.ts +65 -38
  146. package/src/components/data-table/range-focus/use-scroll-into-view.ts +21 -2
  147. package/src/components/data-table/renderers.tsx +16 -7
  148. package/src/components/data-table/row-viewer-panel/__tests__/row-viewer.test.tsx +24 -1
  149. package/src/components/data-table/row-viewer-panel/row-viewer.tsx +4 -4
  150. package/src/components/data-table/schemas.ts +14 -36
  151. package/src/components/data-table/search-highlight.tsx +79 -0
  152. package/src/components/data-table/types.ts +1 -0
  153. package/src/components/data-table/url-detector.tsx +49 -9
  154. package/src/components/data-table/utils.ts +18 -0
  155. package/src/components/datasources/__tests__/column-actions.test.tsx +97 -0
  156. package/src/components/datasources/__tests__/column-preview.test.tsx +98 -5
  157. package/src/components/datasources/__tests__/datasources-interactions.test.tsx +181 -0
  158. package/src/components/datasources/__tests__/table-list.test.tsx +118 -0
  159. package/src/components/datasources/__tests__/utils.test.ts +57 -6
  160. package/src/components/datasources/column-preview.tsx +13 -4
  161. package/src/components/datasources/datasources.tsx +51 -39
  162. package/src/components/datasources/utils.ts +32 -23
  163. package/src/components/debugger/debugger-code.tsx +5 -0
  164. package/src/components/dependency-graph/dependency-graph-tree.tsx +8 -8
  165. package/src/components/dependency-graph/elements.ts +24 -21
  166. package/src/components/dependency-graph/panels.tsx +2 -1
  167. package/src/components/dependency-graph/utils/__tests__/layout.test.ts +18 -11
  168. package/src/components/editor/Output.tsx +9 -2
  169. package/src/components/editor/SortableCell.tsx +1 -3
  170. package/src/components/editor/__tests__/dynamic-favicon.test.tsx +111 -18
  171. package/src/components/editor/__tests__/package-alert.test.tsx +22 -1
  172. package/src/components/editor/actions/__tests__/pair-with-agent-commands.test.ts +80 -252
  173. package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +210 -34
  174. package/src/components/editor/actions/pair-with-agent-commands.ts +42 -51
  175. package/src/components/editor/actions/pair-with-agent-modal.tsx +100 -37
  176. package/src/components/editor/ai/add-cell-with-ai.tsx +3 -3
  177. package/src/components/editor/ai/ai-completion-editor.tsx +1 -0
  178. package/src/components/editor/alerts/startup-logs-alert.tsx +1 -0
  179. package/src/components/editor/cell/CreateCellButton.tsx +8 -10
  180. package/src/components/editor/cell/DeleteButton.tsx +1 -0
  181. package/src/components/editor/cell/__tests__/useShouldShowInterrupt.test.ts +39 -0
  182. package/src/components/editor/cell/code/__tests__/language-toggle.test.tsx +54 -0
  183. package/src/components/editor/cell/code/cell-editor.tsx +1 -1
  184. package/src/components/editor/cell/code/language-toggle.tsx +8 -7
  185. package/src/components/editor/cell/collapse.tsx +5 -3
  186. package/src/components/editor/cell/useShouldShowInterrupt.ts +2 -12
  187. package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +30 -1
  188. package/src/components/editor/chrome/panels/cache-panel.tsx +1 -0
  189. package/src/components/editor/chrome/panels/error-panel.tsx +1 -0
  190. package/src/components/editor/chrome/panels/outline/__tests__/outline-keyboard.test.tsx +84 -0
  191. package/src/components/editor/chrome/panels/outline/floating-outline.tsx +39 -14
  192. package/src/components/editor/chrome/panels/outline/useActiveOutline.tsx +3 -2
  193. package/src/components/editor/chrome/panels/packages-panel.tsx +4 -7
  194. package/src/components/editor/chrome/panels/secrets-panel.tsx +1 -0
  195. package/src/components/editor/chrome/panels/snippets-panel.tsx +5 -1
  196. package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +366 -0
  197. package/src/components/editor/chrome/wrapper/__tests__/pending-ai-cells.test.tsx +58 -1
  198. package/src/components/editor/chrome/wrapper/footer-item.tsx +32 -24
  199. package/src/components/editor/chrome/wrapper/footer-items/attachment-status.tsx +237 -0
  200. package/src/components/editor/chrome/wrapper/footer-items/runtime-settings.tsx +1 -7
  201. package/src/components/editor/chrome/wrapper/footer.tsx +2 -0
  202. package/src/components/editor/chrome/wrapper/pending-ai-cells.tsx +12 -2
  203. package/src/components/editor/chrome/wrapper/storage.ts +1 -1
  204. package/src/components/editor/connections/components.tsx +1 -1
  205. package/src/components/editor/controls/__tests__/command-palette.test.tsx +53 -0
  206. package/src/components/editor/controls/__tests__/keyboard-shortcuts.test.tsx +61 -0
  207. package/src/components/editor/controls/command-palette.tsx +22 -10
  208. package/src/components/editor/controls/keyboard-shortcuts.tsx +14 -5
  209. package/src/components/editor/dynamic-favicon.tsx +21 -40
  210. package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +537 -0
  211. package/src/components/editor/errors/send-error-report-button.tsx +69 -0
  212. package/src/components/editor/file-tree/__tests__/file-explorer-interactions.test.tsx +6 -0
  213. package/src/components/editor/file-tree/__tests__/file-explorer-navigation.test.tsx +47 -0
  214. package/src/components/editor/file-tree/__tests__/use-file-search.test.tsx +91 -0
  215. package/src/components/editor/file-tree/file-explorer.tsx +45 -8
  216. package/src/components/editor/file-tree/file-name-input.tsx +1 -0
  217. package/src/components/editor/file-tree/file-viewer.tsx +4 -2
  218. package/src/components/editor/file-tree/use-file-search.ts +35 -21
  219. package/src/components/editor/header/status.tsx +28 -5
  220. package/src/components/editor/links/__tests__/cell-link.test.tsx +146 -0
  221. package/src/components/editor/links/cell-link.tsx +14 -5
  222. package/src/components/editor/navigation/focus-utils.ts +4 -4
  223. package/src/components/editor/notebook-banner.tsx +1 -0
  224. package/src/components/editor/notebook-cell.tsx +11 -2
  225. package/src/components/editor/output/MarimoErrorOutput.tsx +21 -5
  226. package/src/components/editor/output/MarimoTracebackOutput.tsx +54 -37
  227. package/src/components/editor/output/__tests__/ansi-reduce.test.ts +18 -0
  228. package/src/components/editor/output/__tests__/traceback.test.tsx +27 -1
  229. package/src/components/editor/output/ansi-reduce.ts +36 -17
  230. package/src/components/editor/output/console/ConsoleOutput.tsx +19 -25
  231. package/src/components/editor/package-alert.tsx +14 -7
  232. package/src/components/editor/renderers/grid-layout/grid-layout.tsx +12 -6
  233. package/src/components/editor/renderers/vertical-layout/sidebar/toggle.tsx +7 -1
  234. package/src/components/home/components.tsx +17 -31
  235. package/src/components/icons/__tests__/large-spinner.test.tsx +39 -0
  236. package/src/components/icons/large-spinner.tsx +3 -14
  237. package/src/components/icons/spinner.tsx +1 -1
  238. package/src/components/mcp/mcp-status-indicator.tsx +1 -1
  239. package/src/components/pages/__tests__/home-page.test.tsx +116 -0
  240. package/src/components/pages/home-page.tsx +164 -91
  241. package/src/components/scratchpad/scratchpad.test.tsx +95 -0
  242. package/src/components/scratchpad/scratchpad.tsx +16 -3
  243. package/src/components/slides/reveal-component.tsx +4 -2
  244. package/src/components/slides/slide-cell-view.tsx +1 -1
  245. package/src/components/slides/slide-form.tsx +1 -0
  246. package/src/components/tracing/tracing-keyboard.test.tsx +85 -0
  247. package/src/components/tracing/tracing.tsx +45 -41
  248. package/src/components/ui/__tests__/draggable-popover.test.tsx +24 -0
  249. package/src/components/ui/__tests__/range-slider.test.tsx +165 -0
  250. package/src/components/ui/alert-dialog.tsx +1 -1
  251. package/src/components/ui/draggable-popover.tsx +24 -2
  252. package/src/components/ui/range-slider.tsx +22 -9
  253. package/src/core/ai/context/providers/datasource.ts +6 -2
  254. package/src/core/ai/tools/base.ts +16 -7
  255. package/src/core/ai/tools/edit-notebook-tool.ts +47 -45
  256. package/src/core/ai/tools/registry.ts +13 -8
  257. package/src/core/ai/tools/sample-tool.ts +6 -7
  258. package/src/core/attachments/__tests__/toast.test.tsx +58 -0
  259. package/src/core/attachments/state.ts +18 -0
  260. package/src/core/attachments/toast.ts +15 -0
  261. package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +4 -0
  262. package/src/core/cells/__tests__/cell.test.ts +39 -0
  263. package/src/core/cells/__tests__/cells.test.ts +55 -1
  264. package/src/core/cells/__tests__/collapseConsoleOutputs.test.ts +54 -0
  265. package/src/core/cells/__tests__/error-report.test.ts +139 -0
  266. package/src/core/cells/__tests__/notebook-outline.test.ts +113 -0
  267. package/src/core/cells/__tests__/session.test.ts +167 -37
  268. package/src/core/cells/cell.ts +14 -3
  269. package/src/core/cells/cells.ts +3 -1
  270. package/src/core/cells/error-report.ts +63 -0
  271. package/src/core/cells/pending-delete-service.ts +2 -6
  272. package/src/core/cells/session.ts +29 -2
  273. package/src/core/cells/types.ts +10 -1
  274. package/src/core/codemirror/__tests__/setup.test.ts +29 -29
  275. package/src/core/codemirror/cells/__tests__/extensions.test.ts +15 -0
  276. package/src/core/codemirror/cells/extensions.ts +7 -3
  277. package/src/core/codemirror/keymaps/vim.ts +2 -6
  278. package/src/core/codemirror/language/__tests__/sql.test.ts +22 -20
  279. package/src/core/codemirror/language/extension.ts +8 -8
  280. package/src/core/codemirror/language/languages/markdown.ts +8 -6
  281. package/src/core/codemirror/language/languages/python.ts +16 -10
  282. package/src/core/codemirror/language/languages/sql/sql.ts +9 -7
  283. package/src/core/codemirror/language/panel/panel.tsx +1 -0
  284. package/src/core/codemirror/language/types.ts +7 -7
  285. package/src/core/codemirror/rtc/extension.ts +24 -12
  286. package/src/core/codemirror/rtc/loro/awareness.ts +34 -19
  287. package/src/core/codemirror/rtc/loro/sync.ts +1 -0
  288. package/src/core/codemirror/utils.ts +1 -0
  289. package/src/core/config/pair.ts +5 -3
  290. package/src/core/datasets/__tests__/data-source-discovery.test.ts +4 -4
  291. package/src/core/datasets/data-source-discovery.ts +4 -6
  292. package/src/core/document/__tests__/codemirror.test.ts +485 -0
  293. package/src/core/document/__tests__/draft.test.ts +322 -0
  294. package/src/core/document/__tests__/fixtures.ts +92 -0
  295. package/src/core/document/__tests__/hooks.test.tsx +386 -0
  296. package/src/core/document/__tests__/lifecycle-scenarios.test.ts +61 -0
  297. package/src/core/document/__tests__/model.test.ts +191 -0
  298. package/src/core/document/__tests__/protocol-fixtures.ts +38 -0
  299. package/src/core/document/__tests__/recovery-scenarios.test.ts +230 -0
  300. package/src/core/document/__tests__/scenario-fixture.ts +43 -0
  301. package/src/core/document/__tests__/scenarios.test.ts +205 -0
  302. package/src/core/document/__tests__/state.test.ts +438 -0
  303. package/src/core/document/__tests__/submission.test.ts +203 -0
  304. package/src/core/document/__tests__/test-host.test.ts +92 -0
  305. package/src/core/document/__tests__/test-host.ts +247 -0
  306. package/src/core/document/__tests__/test-notebook.ts +149 -0
  307. package/src/core/document/__tests__/user.ts +61 -0
  308. package/src/core/document/codemirror.ts +311 -0
  309. package/src/core/document/draft.ts +472 -0
  310. package/src/core/document/hooks.tsx +118 -0
  311. package/src/core/document/index.ts +15 -0
  312. package/src/core/document/model.ts +55 -0
  313. package/src/core/document/select.ts +41 -0
  314. package/src/core/document/state.ts +374 -0
  315. package/src/core/document/sync.ts +369 -0
  316. package/src/core/edit-app.tsx +2 -2
  317. package/src/core/host/AGENTS.md +12 -0
  318. package/src/core/host/__tests__/attachments.test.ts +56 -0
  319. package/src/core/host/__tests__/document.test.ts +126 -0
  320. package/src/core/host/__tests__/fixtures.ts +79 -0
  321. package/src/core/host/__tests__/replica.test.ts +550 -0
  322. package/src/core/host/__tests__/static.test.ts +175 -0
  323. package/src/core/host/__tests__/wasm.test.ts +166 -0
  324. package/src/core/host/backend.ts +19 -0
  325. package/src/core/host/connection.ts +42 -0
  326. package/src/core/host/index.ts +14 -0
  327. package/src/core/host/replica.ts +286 -0
  328. package/src/core/host/resources/attachments.ts +22 -0
  329. package/src/core/host/resources/document.ts +101 -0
  330. package/src/core/host/static.ts +77 -0
  331. package/src/core/host/wasm.ts +97 -0
  332. package/src/core/hotkeys/actions.ts +2 -4
  333. package/src/core/islands/__tests__/bootstrap.test.ts +17 -17
  334. package/src/core/islands/__tests__/bridge.test.ts +31 -26
  335. package/src/core/islands/bootstrap.ts +22 -51
  336. package/src/core/islands/bridge.ts +31 -20
  337. package/src/core/islands/components/__tests__/IslandControls.test.tsx +7 -5
  338. package/src/core/islands/worker/__tests__/controller.test.ts +62 -4
  339. package/src/core/islands/worker/controller.ts +4 -10
  340. package/src/core/islands/worker/worker.tsx +33 -33
  341. package/src/core/kernel/__tests__/RuntimeState.test.ts +1 -1
  342. package/src/core/network/connection.ts +4 -2
  343. package/src/core/network/useConnectionNotice.ts +8 -2
  344. package/src/core/run-app.tsx +2 -2
  345. package/src/core/runtime/__tests__/createWsUrl.test.ts +77 -7
  346. package/src/core/runtime/__tests__/runtime.test.ts +33 -15
  347. package/src/core/runtime/runtime.ts +39 -30
  348. package/src/core/saving/save-component.tsx +12 -3
  349. package/src/core/state/__tests__/jotai.test.tsx +108 -0
  350. package/src/core/state/jotai.ts +1 -1
  351. package/src/core/static/__tests__/files.test.ts +38 -10
  352. package/src/core/static/files.ts +2 -0
  353. package/src/core/wasm/__tests__/bridge.test.ts +213 -50
  354. package/src/core/wasm/__tests__/worker.test.ts +187 -0
  355. package/src/core/wasm/bridge.ts +81 -52
  356. package/src/core/wasm/rpc.ts +15 -3
  357. package/src/core/wasm/store.ts +3 -3
  358. package/src/core/wasm/worker/bootstrap.ts +55 -51
  359. package/src/core/wasm/worker/getMarimoWheel.ts +1 -1
  360. package/src/core/wasm/worker/save-worker.ts +9 -10
  361. package/src/core/wasm/worker/types.ts +29 -18
  362. package/src/core/wasm/worker/worker.ts +97 -44
  363. package/src/core/websocket/__tests__/backend.test.ts +235 -0
  364. package/src/core/websocket/__tests__/{useMarimoKernelConnection.test.ts → connection-state.test.ts} +15 -29
  365. package/src/core/websocket/__tests__/useNotebookReplica.hook.test.tsx +141 -0
  366. package/src/core/websocket/__tests__/{useMarimoKernelConnection.hook.test.tsx → useNotebookSession.hook.test.tsx} +197 -184
  367. package/src/core/websocket/backend.ts +129 -0
  368. package/src/core/websocket/connection-state.ts +60 -0
  369. package/src/core/websocket/state.ts +7 -0
  370. package/src/core/websocket/transports/__tests__/notebook.test.ts +405 -0
  371. package/src/core/websocket/transports/notebook.ts +159 -0
  372. package/src/core/websocket/types.ts +9 -39
  373. package/src/core/websocket/useNotebookReplica.ts +69 -0
  374. package/src/core/websocket/{useMarimoKernelConnection.tsx → useNotebookSession.tsx} +63 -228
  375. package/src/css/globals.css +5 -0
  376. package/src/hooks/__tests__/useAsyncData.test.tsx +148 -0
  377. package/src/hooks/__tests__/useDebounce.test.tsx +51 -0
  378. package/src/hooks/__tests__/useDeepCompareMemoize.test.tsx +39 -0
  379. package/src/hooks/__tests__/useDelayElapsed.test.tsx +28 -0
  380. package/src/hooks/__tests__/useInternalStateWithSync.test.tsx +20 -0
  381. package/src/hooks/__tests__/useScript.test.tsx +77 -0
  382. package/src/hooks/__tests__/useStateForKey.test.tsx +257 -0
  383. package/src/hooks/useAsyncData.ts +3 -3
  384. package/src/hooks/useDataSourceDiscovery.ts +6 -4
  385. package/src/hooks/useDebounce.ts +6 -10
  386. package/src/hooks/useDeepCompareMemoize.ts +1 -1
  387. package/src/hooks/useDelayElapsed.ts +14 -10
  388. package/src/hooks/useElapsedTime.ts +3 -3
  389. package/src/hooks/useEventListener.ts +1 -2
  390. package/src/hooks/useInputHistory.ts +4 -7
  391. package/src/hooks/useInternalStateWithSync.ts +6 -6
  392. package/src/hooks/useResizeObserver.ts +4 -7
  393. package/src/hooks/useScript.ts +67 -70
  394. package/src/hooks/useStateForKey.ts +93 -0
  395. package/src/mount.tsx +3 -3
  396. package/src/plugins/__tests__/plugin-object-schemas.test.ts +104 -0
  397. package/src/plugins/core/registerReactComponent.tsx +1 -1
  398. package/src/plugins/impl/CodeEditorPlugin.tsx +1 -1
  399. package/src/plugins/impl/DataEditorPlugin.tsx +1 -1
  400. package/src/plugins/impl/DataTablePlugin.tsx +29 -33
  401. package/src/plugins/impl/DropdownPlugin.tsx +2 -6
  402. package/src/plugins/impl/FileBrowserPlugin.tsx +4 -16
  403. package/src/plugins/impl/FormPlugin.tsx +1 -1
  404. package/src/plugins/impl/RangeSliderPlugin.tsx +16 -2
  405. package/src/plugins/impl/RefreshPlugin.tsx +1 -0
  406. package/src/plugins/impl/SliderPlugin.tsx +6 -8
  407. package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +201 -1
  408. package/src/plugins/impl/__tests__/FileBrowserPlugin.test.tsx +45 -0
  409. package/src/plugins/impl/__tests__/RangeSliderPlugin.test.tsx +109 -0
  410. package/src/plugins/impl/__tests__/SliderPlugin.test.tsx +24 -3
  411. package/src/plugins/impl/anywidget/__tests__/registry.test.ts +2 -0
  412. package/src/plugins/impl/anywidget/__tests__/widget-binding.test.ts +241 -2
  413. package/src/plugins/impl/anywidget/runtime.ts +21 -9
  414. package/src/plugins/impl/anywidget/widget-binding.ts +160 -82
  415. package/src/plugins/impl/chat/__tests__/chat-prompts.test.tsx +93 -0
  416. package/src/plugins/impl/chat/chat-ui.tsx +14 -11
  417. package/src/plugins/impl/code/any-language-editor.tsx +2 -2
  418. package/src/plugins/impl/data-editor/glide-data-editor.tsx +8 -5
  419. package/src/plugins/impl/data-editor/glide-utils.ts +3 -1
  420. package/src/plugins/impl/data-explorer/ConnectedDataExplorerComponent.tsx +1 -1
  421. package/src/plugins/impl/data-explorer/components/column-summary.tsx +4 -2
  422. package/src/plugins/impl/data-explorer/state/reducer.ts +1 -1
  423. package/src/plugins/impl/data-frames/DataFramePlugin.tsx +1 -2
  424. package/src/plugins/impl/data-frames/__tests__/sidebar.test.tsx +160 -0
  425. package/src/plugins/impl/data-frames/forms/__tests__/__snapshots__/form.test.tsx.snap +66 -66
  426. package/src/plugins/impl/data-frames/panel.tsx +43 -16
  427. package/src/plugins/impl/mpl-interactive/MplInteractivePlugin.tsx +5 -3
  428. package/src/plugins/impl/panel/PanelPlugin.tsx +16 -17
  429. package/src/plugins/impl/panel/__tests__/utils.test.ts +39 -1
  430. package/src/plugins/impl/panel/utils.ts +17 -0
  431. package/src/plugins/impl/plotly/PlotlyPlugin.tsx +2 -5
  432. package/src/plugins/impl/plotly/selection.ts +25 -19
  433. package/src/plugins/impl/vega/VegaPlugin.tsx +2 -3
  434. package/src/plugins/impl/vega/make-selectable.ts +4 -7
  435. package/src/plugins/layout/RoutesPlugin.tsx +1 -1
  436. package/src/stories/__tests__/editor-story.test.tsx +33 -0
  437. package/src/stories/button.stories.tsx +1 -0
  438. package/src/stories/editor.stories.tsx +24 -17
  439. package/src/stories/form-wrapper.stories.tsx +2 -2
  440. package/src/stories/select.stories.tsx +2 -2
  441. package/src/stories/switchable-multi-select.stories.tsx +1 -1
  442. package/src/utils/__tests__/arrays.test.ts +35 -0
  443. package/src/utils/__tests__/batch-requests.test.ts +3 -3
  444. package/src/utils/__tests__/dates.test.ts +116 -0
  445. package/src/utils/__tests__/numbers.test.ts +105 -1
  446. package/src/utils/__tests__/objects.test.ts +21 -0
  447. package/src/utils/arrays.ts +10 -0
  448. package/src/utils/blob.ts +1 -1
  449. package/src/utils/dates.ts +94 -0
  450. package/src/utils/numbers.ts +79 -18
  451. package/src/utils/objects.ts +3 -7
  452. package/src/utils/once.ts +19 -19
  453. package/src/utils/tracer.ts +9 -5
  454. package/src/utils/zod-utils.ts +1 -3
  455. package/dist/any-language-editor-B9REGlD9.js +0 -76
  456. package/dist/assets/__vite-browser-external-BXYkiFsk.js +0 -1
  457. package/dist/assets/worker-HPY9fbhz.js +0 -114
  458. package/dist/error-entries-CzoYkIQZ.js +0 -56
  459. package/dist/mermaid-HWGCJPDP-ByCsZnYF.js +0 -2
  460. package/dist/precisionRound-DMIVQrt2.js +0 -97
  461. package/src/core/state/__tests__/jotai.test.ts +0 -25
  462. package/src/core/wasm/worker/message-buffer.ts +0 -33
  463. package/src/core/websocket/__tests__/useWebSocket.test.ts +0 -49
  464. package/src/core/websocket/transports/__tests__/basic.test.ts +0 -275
  465. package/src/core/websocket/transports/__tests__/sse.test.ts +0 -338
  466. package/src/core/websocket/transports/__tests__/transport.test.ts +0 -308
  467. package/src/core/websocket/transports/__tests__/ws.test.ts +0 -125
  468. package/src/core/websocket/transports/basic.ts +0 -102
  469. package/src/core/websocket/transports/sse.ts +0 -308
  470. package/src/core/websocket/transports/transport.ts +0 -61
  471. package/src/core/websocket/transports/ws.ts +0 -98
  472. package/src/core/websocket/useWebSocket.tsx +0 -88
@@ -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,44 +47,99 @@ 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 "../connection-state";
54
+ import { createEditorBackend } from "../backend";
55
+ import { fakeBackend } from "../../host/__tests__/fixtures";
56
+ import { useNotebookSession } from "../useNotebookSession";
57
+ import { type NotebookTransport, StreamStopped } from "@marimo-team/client";
58
+
59
+ const SESSION_ID = generateSessionId();
60
+
61
+ /** One connection the fake transport opened. */
62
+ interface Attempt {
63
+ open(): void;
64
+ receive(message: unknown): void;
65
+ drop(): void;
66
+ stop(error: StreamStopped): void;
67
+ }
68
+
69
+ /** A transport the test drives, one connection at a time. */
70
+ function makeTransport() {
71
+ const attempts: Attempt[] = [];
72
+ const transport: NotebookTransport = ({ signal }, handlers) =>
73
+ new Promise((resolve, reject) => {
74
+ attempts.push({
75
+ open: handlers.open,
76
+ receive: handlers.message,
77
+ drop: resolve,
78
+ stop: reject,
79
+ });
80
+ signal.addEventListener("abort", () => resolve(), { once: true });
81
+ });
82
+ return { transport, attempts };
83
+ }
84
+
85
+ type FakeTransport = ReturnType<typeof makeTransport>;
86
+
87
+ /** Use a fake transport for the next session the hook follows. */
88
+ function withTransport(fake: FakeTransport = makeTransport()) {
89
+ vi.mocked(createEditorBackend).mockImplementation(({ checkHealth }) =>
90
+ fakeBackend({ transport: fake.transport, checkHealth }),
91
+ );
92
+ return fake;
67
93
  }
68
94
 
69
- function makeTransport(
70
- readyState: 0 | 1 | 2 | 3 = WebSocket.CLOSED,
71
- ): MockTransport {
95
+ /** Let the stream open its first connection. */
96
+ const started = () => act(() => Promise.resolve());
97
+
98
+ /** Drive the latest connection the fake transport opened. */
99
+ function connection(fake: FakeTransport) {
100
+ const current = () => {
101
+ const attempt = fake.attempts.at(-1);
102
+ if (attempt === undefined) {
103
+ throw new Error("No connection was opened");
104
+ }
105
+ return attempt;
106
+ };
72
107
  return {
73
- readyState,
74
- reconnect: vi.fn(),
75
- close: vi.fn(),
76
- send: vi.fn(),
77
- addEventListener: vi.fn(),
78
- removeEventListener: vi.fn(),
108
+ open: () => current().open(),
109
+ receive: (message: unknown) => current().receive(message),
110
+ /** Deliver a notification as the session routes frame it. */
111
+ send: (notification: unknown) =>
112
+ current().receive({ event: "marimo", data: { marimo: notification } }),
113
+ /** End the connection so the stream reconnects. */
114
+ drop: () => current().drop(),
115
+ /** Close the connection with a reason the editor treats as final. */
116
+ close: (reason: string) =>
117
+ current().stop(
118
+ new StreamStopped(reason, {
119
+ cause: classifyCloseEvent({ reason }).status,
120
+ }),
121
+ ),
79
122
  };
80
123
  }
81
124
 
82
125
  function makeRuntimeManager(
83
126
  reconcileFromHealth = vi.fn<() => Promise<boolean>>().mockResolvedValue(true),
84
127
  ) {
85
- return {
128
+ const runtime = new RuntimeManager(
129
+ { url: "http://localhost", lazy: true },
130
+ true,
131
+ );
132
+ vi.spyOn(runtime, "reconcileFromHealth").mockImplementation(
86
133
  reconcileFromHealth,
87
- probeHealth: vi.fn().mockResolvedValue(true),
88
- getWsURL: () => new URL("ws://localhost/ws"),
89
- waitForHealthy: vi.fn().mockResolvedValue(undefined),
90
- isSameOrigin: true,
91
- };
134
+ );
135
+ vi.spyOn(runtime, "probeHealth").mockResolvedValue(true);
136
+ vi.spyOn(runtime, "getWsURL").mockReturnValue(new URL("ws://localhost/ws"));
137
+ // Keep the initial connection pending; these tests exercise session retries.
138
+ vi.spyOn(runtime, "waitForHealthy").mockReturnValue(
139
+ new Deferred<void>().promise,
140
+ );
141
+ vi.spyOn(runtime, "isSameOrigin", "get").mockReturnValue(false);
142
+ return runtime;
92
143
  }
93
144
 
94
145
  function renderConnectionHook(store: ReturnType<typeof createStore>) {
@@ -99,22 +150,22 @@ function renderConnectionHook(store: ReturnType<typeof createStore>) {
99
150
  );
100
151
  return renderHook(
101
152
  () =>
102
- useMarimoKernelConnection({
103
- sessionId: "test-session" as SessionId,
153
+ useNotebookSession({
154
+ sessionId: SESSION_ID,
104
155
  autoInstantiate: false,
105
- setCells: () => {},
156
+ setCells: vi.fn(),
106
157
  }),
107
158
  { wrapper },
108
159
  );
109
160
  }
110
161
 
111
- describe("useMarimoKernelConnection.reconnect()", () => {
112
- let transport: MockTransport;
162
+ describe("useNotebookSession.reconnect()", () => {
163
+ let fake: FakeTransport;
113
164
  let isHealthy: Mock<() => Promise<boolean>>;
114
165
  let store: ReturnType<typeof createStore>;
115
166
 
116
167
  beforeEach(() => {
117
- transport = makeTransport(WebSocket.CLOSED);
168
+ fake = withTransport();
118
169
  isHealthy = vi.fn<() => Promise<boolean>>().mockResolvedValue(true);
119
170
  store = createStore();
120
171
  store.set(connectionAtom, {
@@ -122,59 +173,53 @@ describe("useMarimoKernelConnection.reconnect()", () => {
122
173
  code: WebSocketClosedReason.KERNEL_DISCONNECTED,
123
174
  reason: "kernel not found",
124
175
  });
125
- vi.mocked(useConnectionTransport).mockReturnValue(transport);
126
- vi.mocked(useRuntimeManager).mockReturnValue(
127
- makeRuntimeManager(isHealthy) as unknown as ReturnType<
128
- typeof useRuntimeManager
129
- >,
130
- );
176
+ vi.mocked(useRuntimeManager).mockReturnValue(makeRuntimeManager(isHealthy));
131
177
  });
132
178
 
133
- function renderUseHook() {
134
- return renderConnectionHook(store);
179
+ async function renderStopped() {
180
+ const hook = renderConnectionHook(store);
181
+ await started();
182
+ await act(async () => connection(fake).close("MARIMO_SHUTDOWN"));
183
+ return hook;
135
184
  }
136
185
 
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
- });
186
+ it.each(["connecting", "connected"])(
187
+ "is a no-op while the stream is %s",
188
+ async (state) => {
189
+ const { result } = renderConnectionHook(store);
190
+ await started();
191
+ if (state === "connected") {
192
+ act(() => connection(fake).open());
193
+ }
194
+ await act(async () => {
195
+ await result.current.reconnect();
196
+ });
197
+ expect(isHealthy).not.toHaveBeenCalled();
198
+ expect(fake.attempts).toHaveLength(1);
199
+ },
200
+ );
156
201
 
157
202
  it("probes /health and reconnects when the runtime is healthy", async () => {
158
203
  isHealthy.mockResolvedValue(true);
159
- const { result } = renderUseHook();
204
+ const { result } = await renderStopped();
160
205
  await act(async () => {
161
206
  await result.current.reconnect();
162
207
  });
163
208
  expect(isHealthy).toHaveBeenCalledOnce();
164
- expect(transport.reconnect).toHaveBeenCalledOnce();
209
+ expect(fake.attempts).toHaveLength(2);
165
210
  expect(store.get(connectionAtom)).toEqual({
166
211
  state: ConnectionState.CONNECTING,
167
212
  });
168
213
  });
169
214
 
170
- it("transitions to CLOSED and does not call ws.reconnect when the probe fails", async () => {
215
+ it("transitions to CLOSED and does not reconnect when the probe fails", async () => {
171
216
  isHealthy.mockResolvedValue(false);
172
- const { result } = renderUseHook();
217
+ const { result } = await renderStopped();
173
218
  await act(async () => {
174
219
  await result.current.reconnect();
175
220
  });
176
221
  expect(isHealthy).toHaveBeenCalledOnce();
177
- expect(transport.reconnect).not.toHaveBeenCalled();
222
+ expect(fake.attempts).toHaveLength(1);
178
223
  expect(store.get(connectionAtom)).toEqual({
179
224
  state: ConnectionState.CLOSED,
180
225
  code: WebSocketClosedReason.KERNEL_DISCONNECTED,
@@ -183,30 +228,31 @@ describe("useMarimoKernelConnection.reconnect()", () => {
183
228
  });
184
229
  });
185
230
 
186
- describe("useMarimoKernelConnection messages", () => {
187
- it("records completion of the initial run", () => {
188
- 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
- );
231
+ describe("useNotebookSession messages", () => {
232
+ let store: ReturnType<typeof createStore>;
233
+ let fake: FakeTransport;
234
+
235
+ beforeEach(async () => {
236
+ store = createStore();
237
+ fake = withTransport();
238
+ vi.mocked(useRuntimeManager).mockReturnValue(makeRuntimeManager());
196
239
  renderConnectionHook(store);
240
+ await started();
241
+ });
197
242
 
198
- const options = vi.mocked(useConnectionTransport).mock.calls.at(-1)?.[0];
243
+ function receive(payload: unknown) {
244
+ act(() => connection(fake).receive(payload));
245
+ }
246
+
247
+ it("records completion of the initial run", () => {
248
+ const notification = {
249
+ op: "completed-run",
250
+ run_id: null,
251
+ outcome: "succeeded",
252
+ error: null,
253
+ } satisfies NotificationPayload["data"];
199
254
  expect(store.get(initialRunCompletedAtom)).toBe(false);
200
- 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
- );
209
- });
255
+ receive({ id: "42", event: "marimo", data: { marimo: notification } });
210
256
  expect(store.get(initialRunCompletedAtom)).toBe(true);
211
257
  });
212
258
  });
@@ -216,47 +262,34 @@ it.each(["kernel-ready", "reconnected"])(
216
262
  async (op) => {
217
263
  const store = createStore();
218
264
  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
- );
265
+ const fake = withTransport();
266
+ vi.mocked(useRuntimeManager).mockReturnValue(makeRuntimeManager());
226
267
  renderConnectionHook(store);
227
- const options = vi.mocked(useConnectionTransport).mock.calls.at(-1)![0];
268
+ await started();
269
+ const conn = connection(fake);
228
270
 
229
- await act(async () => {
230
- await options.onOpen(new Event("open"));
231
- });
271
+ await act(async () => conn.open());
232
272
  expect(store.get(connectionAtom).state).toBe(ConnectionState.CONNECTING);
233
273
 
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
- });
274
+ act(() =>
275
+ conn.send({
276
+ op,
277
+ cell_ids: [],
278
+ codes: [],
279
+ names: [],
280
+ configs: [],
281
+ layout: null,
282
+ resumed: true,
283
+ ui_values: {},
284
+ last_executed_code: {},
285
+ last_execution_time: {},
286
+ app_config: { width: "normal" },
287
+ kiosk: false,
288
+ capabilities: { terminal: false },
289
+ auto_instantiated: false,
290
+ consumer_capabilities: { edit: true, interact: true },
291
+ }),
292
+ );
260
293
  expect(store.get(connectionAtom).state).toBe(ConnectionState.OPEN);
261
294
  },
262
295
  );
@@ -265,19 +298,16 @@ describe("connection notice", () => {
265
298
  beforeEach(() => vi.useFakeTimers());
266
299
  afterEach(() => vi.useRealTimers());
267
300
 
268
- function renderNotice() {
301
+ async function renderNotice() {
269
302
  const store = createStore();
270
303
  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
- );
304
+ const fake = withTransport();
305
+ vi.mocked(useRuntimeManager).mockReturnValue(makeRuntimeManager());
276
306
  const Connection = () => {
277
- const { reconnect } = useMarimoKernelConnection({
278
- sessionId: "test-session" as SessionId,
307
+ const { reconnect } = useNotebookSession({
308
+ sessionId: SESSION_ID,
279
309
  autoInstantiate: false,
280
- setCells: () => {},
310
+ setCells: vi.fn(),
281
311
  });
282
312
  return (
283
313
  <ConnectionNotice
@@ -293,20 +323,17 @@ describe("connection notice", () => {
293
323
  </ErrorBoundary>
294
324
  </JotaiProvider>,
295
325
  );
296
- const options = vi.mocked(useConnectionTransport).mock.calls.at(-1)![0];
326
+ await started();
327
+ const conn = connection(fake);
328
+ // Messages arrive on an open connection.
329
+ act(() => conn.open());
297
330
  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 };
331
+ act(() => conn.send(data));
332
+ return { store, fake, conn, send };
306
333
  }
307
334
 
308
- it("keeps elapsed time across startup phases and leaves reconnection to the footer once cells are available", () => {
309
- const { store, options, send } = renderNotice();
335
+ it("keeps elapsed time across startup phases and leaves reconnection to the footer once cells are available", async () => {
336
+ const { store, conn, send } = await renderNotice();
310
337
  send({
311
338
  op: "startup-progress",
312
339
  phase: "preparing-environment",
@@ -346,7 +373,7 @@ describe("connection notice", () => {
346
373
  expect(
347
374
  screen.queryByRole("region", { name: "Notebook startup" }),
348
375
  ).not.toBeInTheDocument();
349
- act(() => options.onClose(new CloseEvent("close")));
376
+ await act(async () => conn.drop());
350
377
  act(() => vi.advanceTimersByTime(500));
351
378
  expect(store.get(connectionAtom)).toEqual({
352
379
  state: ConnectionState.CONNECTING,
@@ -357,8 +384,8 @@ describe("connection notice", () => {
357
384
  ).not.toBeInTheDocument();
358
385
  });
359
386
 
360
- it("restores startup output before appending live chunks and resets it for a new attempt", () => {
361
- const { store, send } = renderNotice();
387
+ it("restores startup output before appending live chunks and resets it for a new attempt", async () => {
388
+ const { store, send } = await renderNotice();
362
389
  const snapshot = {
363
390
  op: "startup-progress",
364
391
  phase: "starting-kernel",
@@ -384,7 +411,7 @@ describe("connection notice", () => {
384
411
  });
385
412
 
386
413
  it("restores completed startup output without putting a connected kernel back into startup", async () => {
387
- const { store, options, send } = renderNotice();
414
+ const { store, conn, send } = await renderNotice();
388
415
  send({ op: "reconnected" });
389
416
  send({
390
417
  op: "startup-progress",
@@ -402,13 +429,15 @@ describe("connection notice", () => {
402
429
  screen.queryByRole("region", { name: "Notebook startup" }),
403
430
  ).not.toBeInTheDocument();
404
431
 
405
- // A new transport starts from the next session's authoritative snapshot.
406
- await act(async () => options.onOpen(new Event("open")));
432
+ // A new connection starts from the next session's authoritative snapshot.
433
+ await act(async () => conn.drop());
434
+ await act(async () => vi.advanceTimersByTime(2000));
435
+ await act(async () => conn.open());
407
436
  expect(store.get(startupProgressAtom)).toBeNull();
408
437
  });
409
438
 
410
- it("does not flash a notice when startup finishes within the delay", () => {
411
- const { send } = renderNotice();
439
+ it("does not flash a notice when startup finishes within the delay", async () => {
440
+ const { send } = await renderNotice();
412
441
  send({
413
442
  op: "startup-progress",
414
443
  phase: "preparing-environment",
@@ -436,16 +465,11 @@ describe("connection notice", () => {
436
465
  it.each(["preparing-environment", "starting-kernel"] as const)(
437
466
  "keeps a %s failure available until the user retries",
438
467
  async (phase) => {
439
- const { store, transport, options, send } = renderNotice();
468
+ const { store, fake, conn, send } = await renderNotice();
440
469
  send({ op: "startup-progress", phase, logs: "", log_mode: "replace" });
441
470
  const error = "A full diagnostic\nwith <stderr> details";
442
471
  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
- );
472
+ await act(async () => conn.close("MARIMO_KERNEL_STARTUP_ERROR"));
449
473
  expect(screen.getByRole("status")).toHaveTextContent(
450
474
  "Kernel failed to start",
451
475
  );
@@ -456,11 +480,11 @@ describe("connection notice", () => {
456
480
  expect(screen.getByLabelText("Error details")).toHaveTextContent(error, {
457
481
  normalizeWhitespace: false,
458
482
  });
459
- expect(transport.reconnect).not.toHaveBeenCalled();
483
+ expect(fake.attempts).toHaveLength(1);
460
484
  await act(async () =>
461
485
  fireEvent.click(screen.getByRole("button", { name: "Try again" })),
462
486
  );
463
- expect(transport.reconnect).toHaveBeenCalledOnce();
487
+ expect(fake.attempts).toHaveLength(2);
464
488
  expect(store.get(kernelStartupErrorAtom)).toBeNull();
465
489
  expect(
466
490
  screen.queryByRole("button", { name: "Try again" }),
@@ -480,25 +504,14 @@ describe("connection notice", () => {
480
504
  );
481
505
  });
482
506
 
483
- it("replaces environment state from snapshots, then continues live progress", () => {
507
+ it("replaces environment state from snapshots, then continues live progress", async () => {
484
508
  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
- );
509
+ const fake = withTransport();
510
+ vi.mocked(useRuntimeManager).mockReturnValue(makeRuntimeManager());
492
511
  renderConnectionHook(store);
493
- const options = vi.mocked(useConnectionTransport).mock.calls.at(-1)?.[0];
512
+ await started();
494
513
  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
- );
514
+ act(() => connection(fake).send(data));
502
515
  }
503
516
  receive({
504
517
  op: "environment-operation",