@marimo-team/islands 0.25.3-dev15 → 0.25.3-dev150

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 (502) hide show
  1. package/dist/{ConnectedDataExplorerComponent-CNMliVzw.js → ConnectedDataExplorerComponent-BAiSVaHj.js} +17 -15
  2. package/dist/{ErrorBoundary-z1rqMOCq.js → ErrorBoundary-C2DyVAla.js} +16 -16
  3. package/dist/{abnfDiagram-VCTEODGH-D-Ez91gV.js → abnfDiagram-VCTEODGH-BBHN92-F.js} +2 -2
  4. package/dist/any-language-editor-3E24SnCV.js +75 -0
  5. package/dist/{architectureDiagram-5GKGNRK7-CTpcNtO8.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-DaHynk8g.js → chat-ui-CvB8Map3.js} +1142 -1141
  12. package/dist/{check-EDwSwP89.js → check-SpOMoCLP.js} +1 -1
  13. package/dist/{chunk-IMKFNOWR-DGxi-zeo.js → chunk-IMKFNOWR-hJXey5p2.js} +1 -1
  14. package/dist/{chunk-SHT3W25Y-CV9J3rAB.js → chunk-SHT3W25Y-JMzTmmVW.js} +1 -1
  15. package/dist/{chunk-SVP7TREG-BL-9Fqgg.js → chunk-SVP7TREG-BIyoHTXi.js} +1 -1
  16. package/dist/{chunk-TICWLB2K-Cu_XMjDM.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-BXXV13Hq.js → classDiagram-ZZMXUADV-DB1kOPS6.js} +2 -2
  19. package/dist/{classDiagram-v2-VYDZK3BY-B7FXJ1Cp.js → classDiagram-v2-VYDZK3BY-CPaFL0OB.js} +2 -2
  20. package/dist/{code-visibility-BFMjPeTv.js → code-visibility-DIEAT978.js} +6 -6
  21. package/dist/{common-JHV-U5GC.js → common-BA_SF-5o.js} +6827 -6647
  22. package/dist/{copy-icon-Ft7t9bqz.js → copy-icon-C1trdvVt.js} +3 -3
  23. package/dist/{cynefinDiagram-5FMLGOSQ-CH96rwba.js → cynefinDiagram-5FMLGOSQ-CF93uliH.js} +1 -1
  24. package/dist/{dagre-GXQ25YYZ-BD3PmpFY.js → dagre-GXQ25YYZ-BBB-_p9D.js} +1 -1
  25. package/dist/{data-grid-overlay-editor-BkL0DXBi.js → data-grid-overlay-editor-Byuku-gi.js} +5 -5
  26. package/dist/{data-grid-types-BH_r7g7l.js → data-grid-types-0PJ-9N5n.js} +1 -1
  27. package/dist/{diagram-S7CK7UJ4-BsSJJldB.js → diagram-S7CK7UJ4-BynCnbyt.js} +1 -1
  28. package/dist/{diagram-UQ7AKVKN-muQtNZc4.js → diagram-UQ7AKVKN-svOxikMa.js} +1 -1
  29. package/dist/{diagram-VX7I27RA-CdhuZAqR.js → diagram-VX7I27RA-BVQSnPx0.js} +1 -1
  30. package/dist/{diagram-Z3DM3KII-BD8mXjOj.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-BIN1sweE.js → ebnfDiagram-PWID7BFC-C9tq9-Qo.js} +2 -2
  35. package/dist/{erDiagram-RLTQ6QDP-Ctp5cfoN.js → erDiagram-RLTQ6QDP-BiaeB7Fi.js} +1 -1
  36. package/dist/{error-banner-D-WCIiMt.js → error-banner-D40pmfQQ.js} +83 -83
  37. package/dist/{error-entries-Bv6bZPF3.js → error-entries-CmFDQ9CA.js} +1018 -977
  38. package/dist/{feedback-button-do3RdfYX.js → feedback-button-BwgAjZDg.js} +23 -23
  39. package/dist/{flowDiagram-HODETNUW-3EfLrlFb.js → flowDiagram-HODETNUW-f-sLm87k.js} +2 -2
  40. package/dist/{formats-27CW_UP4.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-B2KXsLT5.js → glide-data-editor-BCX4vz1V.js} +370 -368
  43. package/dist/{glide-utils-DJyqh0EM.js → glide-utils-Dz_F4pAY.js} +16 -16
  44. package/dist/{globals-Cnjg2Own.js → globals-2FASSt-d.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-Bf8QMGi4.js → html-to-image-BrC8OJcj.js} +632 -595
  47. package/dist/{infoDiagram-27XIBGKW-DipQoHxl.js → infoDiagram-27XIBGKW-BHrWywBe.js} +1 -1
  48. package/dist/{ishikawaDiagram-5VMMS53U-DgFMjSGz.js → ishikawaDiagram-5VMMS53U-Da4s0Reg.js} +1 -1
  49. package/dist/{kanban-definition-UXKFOSKX-w1ACVjgl.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 +7553 -7232
  54. package/dist/{mermaid-CHo49Yv-.js → mermaid-ClwTt7OD.js} +38 -38
  55. package/dist/mermaid-HWGCJPDP-Du3-HPQA.js +2 -0
  56. package/dist/{mindmap-definition-YA3MSWOX-CrT0KSfX.js → mindmap-definition-YA3MSWOX-D60oMZtT.js} +1 -1
  57. package/dist/{number-overlay-editor-DBsAEYkJ.js → number-overlay-editor-6JHsXwu3.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-BqvywNJA.js → pegDiagram-XKGWAZYB-B9wFpMga.js} +2 -2
  61. package/dist/{pieDiagram-E7YTZNPT-BfxJ_XCc.js → pieDiagram-E7YTZNPT-BZwvMuBT.js} +1 -1
  62. package/dist/precisionRound-DhAzeRcm.js +49 -0
  63. package/dist/{process-output-BZg56CCb.js → process-output-Bw-vzyoy.js} +5 -5
  64. package/dist/{quadrantDiagram-AXDQQJYC-Ccw0xMjN.js → quadrantDiagram-AXDQQJYC-Blrm7V7_.js} +1 -1
  65. package/dist/{railroadDiagram-O6MQD6OU-8vFLVTPu.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-a_MxGvxe.js → requirementDiagram-BXWQKSXE-CL5dYARV.js} +1 -1
  68. package/dist/{reveal-component-IkMCCFzA.js → reveal-component-BGe2tbbp.js} +14 -14
  69. package/dist/{select-BButFi-d.js → select-CkF_GBxO.js} +537 -458
  70. package/dist/{sequenceDiagram-WJ2MYXX4-CXFMnxGh.js → sequenceDiagram-WJ2MYXX4-BSUCRd2x.js} +1 -1
  71. package/dist/{spec-BwarBpUJ.js → spec-DiSh_qJC.js} +41 -11
  72. package/dist/{stateDiagram-D77RDMKH-DQeA_CwI.js → stateDiagram-D77RDMKH-B-1Hvyb0.js} +2 -2
  73. package/dist/{stateDiagram-v2-MP3YSRHH-BxxQucm1.js → stateDiagram-v2-MP3YSRHH--HtiR4yB.js} +2 -2
  74. package/dist/style.css +1 -1
  75. package/dist/{swimlanes-42K2YHIH-Do6L-cYg.js → swimlanes-42K2YHIH-Bdfhrd-2.js} +1 -1
  76. package/dist/{swimlanesDiagram-VR7AAH4N-DUzPvSVU.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-D3c46WM5.js → timeline-definition-24CTP7MA-Cs4EfFD3.js} +1 -1
  80. package/dist/{toDate-UtiwmnH2.js → toDate-DGvRHwTk.js} +19 -18
  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-BeF9dfyO.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-BSZD8JcZ.js → vennDiagram-4TSXK5OY-DRu0vRjO.js} +1 -1
  94. package/dist/{wardleyDiagram-VM6X3IG4-48cUWkfA.js → wardleyDiagram-VM6X3IG4-DzbDxwDY.js} +1 -1
  95. package/dist/{xychartDiagram-S5SC5T6Z-CswYgYeu.js → xychartDiagram-S5SC5T6Z-CLWj6ZFQ.js} +2 -2
  96. package/package.json +16 -16
  97. package/src/__tests__/module-entry.test.ts +44 -0
  98. package/src/__tests__/mount.test.ts +1 -21
  99. package/src/__tests__/{host-imports.test.ts → replica-imports.test.ts} +12 -11
  100. package/src/components/ai/__tests__/ai-model-dropdown.test.tsx +63 -0
  101. package/src/components/ai/ai-model-dropdown.tsx +11 -6
  102. package/src/components/app-config/ai-config.tsx +7 -2
  103. package/src/components/chat/__tests__/message-queue.test.tsx +17 -0
  104. package/src/components/chat/acp/__tests__/session-tabs.test.tsx +43 -0
  105. package/src/components/chat/acp/__tests__/state.test.ts +2 -2
  106. package/src/components/chat/acp/blocks.tsx +3 -2
  107. package/src/components/chat/acp/session-tabs.tsx +8 -3
  108. package/src/components/chat/chat-components.tsx +2 -0
  109. package/src/components/chat/chat-panel.tsx +9 -8
  110. package/src/components/chat/chat-utils.ts +4 -8
  111. package/src/components/data-table/TableTopBar.tsx +2 -0
  112. package/src/components/data-table/__tests__/chart-spec-model.test.ts +195 -195
  113. package/src/components/data-table/__tests__/column-explorer.test.tsx +27 -1
  114. package/src/components/data-table/__tests__/columns.test.tsx +105 -0
  115. package/src/components/data-table/__tests__/context-menu-interaction.test.tsx +130 -0
  116. package/src/components/data-table/__tests__/data-table.test.tsx +1 -0
  117. package/src/components/data-table/__tests__/date-popover.test.tsx +72 -0
  118. package/src/components/data-table/__tests__/export-actions.test.tsx +105 -0
  119. package/src/components/data-table/__tests__/keyboard-selection.test.tsx +188 -0
  120. package/src/components/data-table/__tests__/search-highlight.test.tsx +109 -0
  121. package/src/components/data-table/__tests__/url-detector.test.tsx +74 -0
  122. package/src/components/data-table/cell-selection/feature.ts +2 -2
  123. package/src/components/data-table/cell-selection/types.ts +2 -2
  124. package/src/components/data-table/charts/__tests__/chart-panel.test.tsx +74 -1
  125. package/src/components/data-table/charts/__tests__/spec-snapshot.test.ts +20 -20
  126. package/src/components/data-table/charts/__tests__/spec.test.ts +78 -52
  127. package/src/components/data-table/charts/__tests__/storage.test.ts +4 -1
  128. package/src/components/data-table/charts/chart-spec/spec.ts +68 -43
  129. package/src/components/data-table/charts/charts.tsx +40 -30
  130. package/src/components/data-table/charts/storage.ts +16 -5
  131. package/src/components/data-table/column-explorer-panel/column-explorer.tsx +1 -1
  132. package/src/components/data-table/column-summary/chart-spec-model.tsx +23 -16
  133. package/src/components/data-table/column-summary/column-summary.tsx +2 -4
  134. package/src/components/data-table/column-visibility-dropdown.tsx +8 -13
  135. package/src/components/data-table/columns.tsx +45 -46
  136. package/src/components/data-table/context-menu.tsx +36 -20
  137. package/src/components/data-table/data-table.tsx +13 -1
  138. package/src/components/data-table/date-popover.tsx +6 -4
  139. package/src/components/data-table/export-actions.tsx +76 -48
  140. package/src/components/data-table/range-focus/atoms.ts +4 -6
  141. package/src/components/data-table/range-focus/use-cell-range-selection.ts +65 -38
  142. package/src/components/data-table/range-focus/use-scroll-into-view.ts +21 -2
  143. package/src/components/data-table/renderers.tsx +16 -7
  144. package/src/components/data-table/row-viewer-panel/__tests__/row-viewer.test.tsx +24 -1
  145. package/src/components/data-table/row-viewer-panel/row-viewer.tsx +4 -4
  146. package/src/components/data-table/search-highlight.tsx +79 -0
  147. package/src/components/data-table/types.ts +1 -0
  148. package/src/components/data-table/url-detector.tsx +49 -9
  149. package/src/components/datasources/__tests__/column-actions.test.tsx +97 -0
  150. package/src/components/datasources/__tests__/column-preview.test.tsx +98 -5
  151. package/src/components/datasources/__tests__/datasources-interactions.test.tsx +181 -0
  152. package/src/components/datasources/__tests__/table-list.test.tsx +118 -0
  153. package/src/components/datasources/__tests__/utils.test.ts +57 -6
  154. package/src/components/datasources/column-preview.tsx +13 -4
  155. package/src/components/datasources/datasources.tsx +51 -39
  156. package/src/components/datasources/utils.ts +32 -23
  157. package/src/components/debugger/debugger-code.tsx +5 -0
  158. package/src/components/dependency-graph/dependency-graph-tree.tsx +8 -8
  159. package/src/components/dependency-graph/elements.ts +24 -21
  160. package/src/components/dependency-graph/panels.tsx +2 -1
  161. package/src/components/dependency-graph/utils/__tests__/layout.test.ts +18 -11
  162. package/src/components/editor/SortableCell.tsx +1 -3
  163. package/src/{core/attachments/__tests__/toast.test.tsx → components/editor/__tests__/agent-toast.test.tsx} +1 -1
  164. package/src/components/editor/__tests__/dynamic-favicon.test.tsx +111 -18
  165. package/src/components/editor/__tests__/package-alert.test.tsx +22 -1
  166. package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +24 -0
  167. package/src/components/editor/actions/pair-with-agent-modal.tsx +18 -15
  168. package/src/components/editor/actions/useRestartKernel.tsx +1 -1
  169. package/src/components/editor/ai/add-cell-with-ai.tsx +3 -3
  170. package/src/components/editor/ai/ai-completion-editor.tsx +1 -0
  171. package/src/components/editor/alerts/__tests__/connection-notice.test.tsx +1 -1
  172. package/src/components/editor/alerts/connection-notice.tsx +1 -1
  173. package/src/components/editor/alerts/startup-logs-alert.tsx +1 -0
  174. package/src/components/editor/app-container.tsx +1 -1
  175. package/src/components/editor/boundary/__tests__/ErrorBoundary.test.tsx +1 -1
  176. package/src/components/editor/cell/CreateCellButton.tsx +10 -12
  177. package/src/components/editor/cell/DeleteButton.tsx +3 -2
  178. package/src/components/editor/cell/RunButton.tsx +2 -2
  179. package/src/components/editor/cell/StopButton.tsx +2 -2
  180. package/src/components/editor/cell/__tests__/useShouldShowInterrupt.test.ts +39 -0
  181. package/src/components/editor/cell/code/__tests__/language-toggle.test.tsx +54 -0
  182. package/src/components/editor/cell/code/cell-editor.tsx +2 -2
  183. package/src/components/editor/cell/code/language-toggle.tsx +8 -7
  184. package/src/components/editor/cell/collapse.tsx +5 -3
  185. package/src/components/editor/cell/useShouldShowInterrupt.ts +2 -12
  186. package/src/components/editor/chrome/components/__tests__/feedback-button.test.tsx +1 -1
  187. package/src/components/editor/chrome/components/feedback-button.tsx +1 -1
  188. package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +31 -2
  189. package/src/components/editor/chrome/panels/__tests__/runtime-details.test.tsx +1 -1
  190. package/src/components/editor/chrome/panels/__tests__/sandbox-recovery.test.tsx +1 -1
  191. package/src/components/editor/chrome/panels/cache-panel.tsx +1 -0
  192. package/src/components/editor/chrome/panels/outline/__tests__/outline-keyboard.test.tsx +84 -0
  193. package/src/components/editor/chrome/panels/outline/floating-outline.tsx +39 -14
  194. package/src/components/editor/chrome/panels/outline/useActiveOutline.tsx +3 -2
  195. package/src/components/editor/chrome/panels/packages-panel.tsx +4 -7
  196. package/src/components/editor/chrome/panels/secrets-panel.tsx +1 -0
  197. package/src/components/editor/chrome/panels/snippets-panel.tsx +5 -1
  198. package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +9 -18
  199. package/src/components/editor/chrome/wrapper/__tests__/pending-ai-cells.test.tsx +58 -1
  200. package/src/components/editor/chrome/wrapper/footer-items/attachment-status.tsx +4 -4
  201. package/src/components/editor/chrome/wrapper/footer-items/backend-status.tsx +1 -1
  202. package/src/components/editor/chrome/wrapper/footer-items/lsp-status.tsx +1 -1
  203. package/src/components/editor/chrome/wrapper/footer-items/machine-stats.tsx +1 -1
  204. package/src/components/editor/chrome/wrapper/footer-items/runtime-settings.tsx +1 -7
  205. package/src/components/editor/chrome/wrapper/pending-ai-cells.tsx +12 -2
  206. package/src/components/editor/chrome/wrapper/storage.ts +1 -1
  207. package/src/components/editor/connections/components.tsx +1 -1
  208. package/src/components/editor/controls/Controls.tsx +2 -2
  209. package/src/components/editor/controls/__tests__/command-palette.test.tsx +53 -0
  210. package/src/components/editor/controls/__tests__/keyboard-shortcuts.test.tsx +61 -0
  211. package/src/components/editor/controls/__tests__/notebook-menu-dropdown.test.tsx +1 -1
  212. package/src/components/editor/controls/command-palette.tsx +22 -10
  213. package/src/components/editor/controls/keyboard-shortcuts.tsx +14 -5
  214. package/src/components/editor/dynamic-favicon.tsx +21 -40
  215. package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +9 -23
  216. package/src/components/editor/errors/send-error-report-button.tsx +3 -6
  217. package/src/components/editor/file-tree/__tests__/file-explorer-interactions.test.tsx +6 -0
  218. package/src/components/editor/file-tree/__tests__/file-explorer-navigation.test.tsx +47 -0
  219. package/src/components/editor/file-tree/__tests__/use-file-search.test.tsx +91 -0
  220. package/src/components/editor/file-tree/file-explorer.tsx +45 -8
  221. package/src/components/editor/file-tree/file-name-input.tsx +1 -0
  222. package/src/components/editor/file-tree/file-viewer.tsx +4 -2
  223. package/src/components/editor/file-tree/use-file-search.ts +35 -21
  224. package/src/components/editor/header/__tests__/status.test.tsx +5 -3
  225. package/src/components/editor/header/app-header.tsx +1 -1
  226. package/src/components/editor/header/status.tsx +40 -20
  227. package/src/components/editor/links/__tests__/cell-link.test.tsx +146 -0
  228. package/src/components/editor/links/cell-link.tsx +14 -5
  229. package/src/components/editor/navigation/focus-utils.ts +4 -4
  230. package/src/components/editor/notebook-banner.tsx +1 -0
  231. package/src/components/editor/notebook-cell.tsx +12 -3
  232. package/src/components/editor/output/MarimoTracebackOutput.tsx +19 -17
  233. package/src/components/editor/output/__tests__/traceback.test.tsx +27 -1
  234. package/src/components/editor/output/ansi-reduce.ts +33 -20
  235. package/src/components/editor/output/console/ConsoleOutput.tsx +19 -25
  236. package/src/components/editor/package-alert.tsx +14 -7
  237. package/src/components/editor/renderers/grid-layout/grid-layout.tsx +12 -6
  238. package/src/components/editor/renderers/vertical-layout/sidebar/toggle.tsx +7 -1
  239. package/src/components/home/components.tsx +17 -31
  240. package/src/components/icons/__tests__/large-spinner.test.tsx +39 -0
  241. package/src/components/icons/large-spinner.tsx +3 -14
  242. package/src/components/icons/spinner.tsx +1 -1
  243. package/src/components/mcp/mcp-status-indicator.tsx +1 -1
  244. package/src/components/pages/__tests__/home-page.test.tsx +116 -0
  245. package/src/components/pages/home-page.tsx +164 -91
  246. package/src/components/scratchpad/scratchpad.test.tsx +95 -0
  247. package/src/components/scratchpad/scratchpad.tsx +16 -3
  248. package/src/components/slides/reveal-component.tsx +4 -2
  249. package/src/components/slides/slide-cell-view.tsx +1 -1
  250. package/src/components/slides/slide-form.tsx +1 -0
  251. package/src/components/tracing/tracing-keyboard.test.tsx +85 -0
  252. package/src/components/tracing/tracing.tsx +45 -41
  253. package/src/components/ui/__tests__/draggable-popover.test.tsx +24 -0
  254. package/src/components/ui/__tests__/range-slider.test.tsx +165 -0
  255. package/src/components/ui/alert-dialog.tsx +1 -1
  256. package/src/components/ui/draggable-popover.tsx +24 -2
  257. package/src/components/ui/range-slider.tsx +22 -9
  258. package/src/core/ai/context/providers/datasource.ts +6 -2
  259. package/src/core/ai/tools/base.ts +16 -7
  260. package/src/core/ai/tools/edit-notebook-tool.ts +47 -45
  261. package/src/core/ai/tools/registry.ts +13 -8
  262. package/src/core/ai/tools/sample-tool.ts +6 -7
  263. package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +4 -0
  264. package/src/core/cells/__tests__/cell.test.ts +39 -0
  265. package/src/core/cells/__tests__/cells.test.ts +3 -1
  266. package/src/core/cells/__tests__/collapseConsoleOutputs.test.ts +54 -0
  267. package/src/core/cells/__tests__/notebook-outline.test.ts +113 -0
  268. package/src/core/cells/__tests__/session.test.ts +117 -36
  269. package/src/core/cells/cell.ts +14 -3
  270. package/src/core/cells/cells.ts +3 -1
  271. package/src/core/cells/pending-delete-service.ts +2 -6
  272. package/src/core/cells/session.ts +1 -1
  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/readonly/__tests__/extension.test.ts +1 -1
  286. package/src/core/codemirror/readonly/extension.ts +2 -2
  287. package/src/core/codemirror/rtc/extension.ts +24 -12
  288. package/src/core/codemirror/rtc/loro/awareness.ts +34 -19
  289. package/src/core/codemirror/rtc/loro/sync.ts +1 -0
  290. package/src/core/codemirror/utils.ts +1 -0
  291. package/src/core/datasets/__tests__/data-source-discovery.test.ts +5 -5
  292. package/src/core/datasets/data-source-discovery.ts +4 -6
  293. package/src/core/edit-app.tsx +2 -2
  294. package/src/core/export/hooks.ts +1 -1
  295. package/src/core/hotkeys/actions.ts +2 -4
  296. package/src/core/islands/__tests__/bootstrap.test.ts +16 -42
  297. package/src/core/islands/__tests__/bridge.test.ts +31 -26
  298. package/src/core/islands/bootstrap.ts +22 -52
  299. package/src/core/islands/bridge.ts +31 -20
  300. package/src/core/islands/components/__tests__/IslandControls.test.tsx +7 -5
  301. package/src/core/islands/worker/__tests__/controller.test.ts +62 -4
  302. package/src/core/islands/worker/controller.ts +4 -10
  303. package/src/core/islands/worker/worker.tsx +33 -33
  304. package/src/core/kernel/__tests__/RuntimeState.test.ts +1 -1
  305. package/src/core/network/__tests__/connection.test.ts +1 -1
  306. package/src/core/network/connection-notice.ts +1 -1
  307. package/src/core/network/connection.ts +5 -3
  308. package/src/core/network/requests-lazy.ts +1 -1
  309. package/src/core/network/useConnectionNotice.ts +8 -2
  310. package/src/core/notebook/attachments.ts +38 -0
  311. package/src/core/notebook/document/__tests__/codemirror.test.ts +485 -0
  312. package/src/core/notebook/document/__tests__/draft.test.ts +322 -0
  313. package/src/core/notebook/document/__tests__/fixtures.ts +92 -0
  314. package/src/core/notebook/document/__tests__/hooks.test.tsx +391 -0
  315. package/src/core/notebook/document/__tests__/lifecycle-scenarios.test.ts +61 -0
  316. package/src/core/notebook/document/__tests__/model.test.ts +191 -0
  317. package/src/core/notebook/document/__tests__/protocol-fixtures.ts +38 -0
  318. package/src/core/notebook/document/__tests__/recovery-scenarios.test.ts +230 -0
  319. package/src/core/notebook/document/__tests__/scenario-fixture.ts +43 -0
  320. package/src/core/notebook/document/__tests__/scenarios.test.ts +205 -0
  321. package/src/core/notebook/document/__tests__/state.test.ts +438 -0
  322. package/src/core/notebook/document/__tests__/submission.test.ts +203 -0
  323. package/src/core/notebook/document/__tests__/test-host.test.ts +92 -0
  324. package/src/core/notebook/document/__tests__/test-host.ts +247 -0
  325. package/src/core/notebook/document/__tests__/test-notebook.ts +149 -0
  326. package/src/core/notebook/document/__tests__/user.ts +61 -0
  327. package/src/core/notebook/document/codemirror.ts +311 -0
  328. package/src/core/notebook/document/draft.ts +472 -0
  329. package/src/core/notebook/document/hooks.tsx +118 -0
  330. package/src/core/notebook/document/index.ts +15 -0
  331. package/src/core/notebook/document/model.ts +55 -0
  332. package/src/core/notebook/document/select.ts +41 -0
  333. package/src/core/notebook/document/state.ts +374 -0
  334. package/src/core/notebook/document/sync.ts +369 -0
  335. package/src/core/notebook/index.ts +18 -0
  336. package/src/core/notebook/replica/AGENTS.md +11 -0
  337. package/src/core/notebook/replica/__tests__/attachments.test.ts +56 -0
  338. package/src/core/notebook/replica/__tests__/document.test.ts +126 -0
  339. package/src/core/notebook/replica/__tests__/fixtures.ts +79 -0
  340. package/src/core/notebook/replica/__tests__/replica.test.ts +550 -0
  341. package/src/core/notebook/replica/backend.ts +19 -0
  342. package/src/core/notebook/replica/connection.ts +42 -0
  343. package/src/core/notebook/replica/index.ts +13 -0
  344. package/src/core/notebook/replica/replica.ts +286 -0
  345. package/src/core/notebook/replica/resources/attachments.ts +22 -0
  346. package/src/core/notebook/replica/resources/document.ts +101 -0
  347. package/src/core/packages/__tests__/useSandboxController.test.tsx +1 -1
  348. package/src/core/packages/useSandboxController.ts +1 -1
  349. package/src/core/run-app.tsx +3 -3
  350. package/src/core/runtime/__tests__/adapter.test.ts +1 -1
  351. package/src/core/runtime/__tests__/createWsUrl.test.ts +77 -7
  352. package/src/core/runtime/__tests__/runtime.test.ts +32 -58
  353. package/src/core/runtime/adapter.ts +1 -1
  354. package/src/core/runtime/config.ts +2 -2
  355. package/src/core/runtime/runtime.ts +38 -42
  356. package/src/core/runtime/types.ts +0 -2
  357. package/src/core/saving/filename.ts +1 -1
  358. package/src/core/saving/save-component.tsx +13 -4
  359. package/src/core/saving/useAutoSave.ts +1 -1
  360. package/src/core/session/__tests__/backend.test.ts +235 -0
  361. package/src/core/session/__tests__/useNotebookReplica.hook.test.tsx +183 -0
  362. package/src/core/session/__tests__/useNotebookSession.hook.test.tsx +609 -0
  363. package/src/core/session/active-notebook.ts +7 -0
  364. package/src/core/session/backend.ts +125 -0
  365. package/src/core/{websocket → session}/types.ts +2 -2
  366. package/src/core/session/useNotebookReplica.ts +72 -0
  367. package/src/core/{websocket → session}/useNotebookSession.tsx +18 -25
  368. package/src/core/state/__tests__/jotai.test.tsx +108 -0
  369. package/src/core/state/jotai.ts +1 -1
  370. package/src/core/static/__tests__/files.test.ts +38 -10
  371. package/src/core/static/files.ts +2 -0
  372. package/src/core/{websocket → transports}/__tests__/connection-state.test.ts +1 -13
  373. package/src/core/transports/__tests__/server.test.ts +408 -0
  374. package/src/core/transports/__tests__/static.test.ts +175 -0
  375. package/src/core/transports/__tests__/wasm.test.ts +166 -0
  376. package/src/core/{websocket → transports}/connection-state.ts +9 -16
  377. package/src/core/transports/server.ts +152 -0
  378. package/src/core/transports/static.ts +84 -0
  379. package/src/core/transports/wasm.ts +97 -0
  380. package/src/core/wasm/__tests__/bridge.test.ts +213 -50
  381. package/src/core/wasm/__tests__/worker.test.ts +187 -0
  382. package/src/core/wasm/bridge.ts +81 -52
  383. package/src/core/wasm/rpc.ts +15 -3
  384. package/src/core/wasm/store.ts +3 -3
  385. package/src/core/wasm/worker/bootstrap.ts +55 -51
  386. package/src/core/wasm/worker/getMarimoWheel.ts +1 -1
  387. package/src/core/wasm/worker/save-worker.ts +9 -10
  388. package/src/core/wasm/worker/types.ts +29 -18
  389. package/src/core/wasm/worker/worker.ts +97 -44
  390. package/src/css/globals.css +5 -0
  391. package/src/hooks/__tests__/useAsyncData.test.tsx +148 -0
  392. package/src/hooks/__tests__/useDebounce.test.tsx +51 -0
  393. package/src/hooks/__tests__/useDeepCompareMemoize.test.tsx +39 -0
  394. package/src/hooks/__tests__/useDelayElapsed.test.tsx +28 -0
  395. package/src/hooks/__tests__/useInternalStateWithSync.test.tsx +20 -0
  396. package/src/hooks/__tests__/useScript.test.tsx +77 -0
  397. package/src/hooks/__tests__/useStateForKey.test.tsx +257 -0
  398. package/src/hooks/useAsyncData.ts +3 -3
  399. package/src/hooks/useDataSourceDiscovery.ts +6 -4
  400. package/src/hooks/useDebounce.ts +6 -10
  401. package/src/hooks/useDeepCompareMemoize.ts +1 -1
  402. package/src/hooks/useDelayElapsed.ts +14 -10
  403. package/src/hooks/useElapsedTime.ts +3 -3
  404. package/src/hooks/useEventListener.ts +1 -2
  405. package/src/hooks/useInputHistory.ts +4 -7
  406. package/src/hooks/useInternalStateWithSync.ts +6 -6
  407. package/src/hooks/useResizeObserver.ts +4 -7
  408. package/src/hooks/useScript.ts +67 -70
  409. package/src/hooks/useStateForKey.ts +93 -0
  410. package/src/mount.tsx +1 -5
  411. package/src/plugins/__tests__/plugin-object-schemas.test.ts +104 -0
  412. package/src/plugins/core/registerReactComponent.tsx +1 -1
  413. package/src/plugins/impl/CodeEditorPlugin.tsx +1 -1
  414. package/src/plugins/impl/DataEditorPlugin.tsx +1 -1
  415. package/src/plugins/impl/DataTablePlugin.tsx +29 -33
  416. package/src/plugins/impl/DropdownPlugin.tsx +2 -6
  417. package/src/plugins/impl/FileBrowserPlugin.tsx +4 -16
  418. package/src/plugins/impl/FormPlugin.tsx +1 -1
  419. package/src/plugins/impl/RangeSliderPlugin.tsx +16 -2
  420. package/src/plugins/impl/RefreshPlugin.tsx +1 -0
  421. package/src/plugins/impl/SliderPlugin.tsx +6 -8
  422. package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +117 -1
  423. package/src/plugins/impl/__tests__/FileBrowserPlugin.test.tsx +45 -0
  424. package/src/plugins/impl/__tests__/RangeSliderPlugin.test.tsx +109 -0
  425. package/src/plugins/impl/__tests__/SliderPlugin.test.tsx +24 -3
  426. package/src/plugins/impl/anywidget/__tests__/registry.test.ts +2 -0
  427. package/src/plugins/impl/anywidget/__tests__/widget-binding.test.ts +241 -2
  428. package/src/plugins/impl/anywidget/runtime.ts +21 -9
  429. package/src/plugins/impl/anywidget/widget-binding.ts +160 -82
  430. package/src/plugins/impl/chat/__tests__/chat-prompts.test.tsx +93 -0
  431. package/src/plugins/impl/chat/chat-ui.tsx +14 -11
  432. package/src/plugins/impl/code/any-language-editor.tsx +2 -2
  433. package/src/plugins/impl/data-editor/glide-data-editor.tsx +8 -5
  434. package/src/plugins/impl/data-editor/glide-utils.ts +3 -1
  435. package/src/plugins/impl/data-explorer/ConnectedDataExplorerComponent.tsx +1 -1
  436. package/src/plugins/impl/data-explorer/components/column-summary.tsx +4 -2
  437. package/src/plugins/impl/data-explorer/state/reducer.ts +1 -1
  438. package/src/plugins/impl/data-frames/DataFramePlugin.tsx +1 -2
  439. package/src/plugins/impl/data-frames/__tests__/sidebar.test.tsx +160 -0
  440. package/src/plugins/impl/data-frames/forms/__tests__/__snapshots__/form.test.tsx.snap +66 -66
  441. package/src/plugins/impl/data-frames/panel.tsx +43 -16
  442. package/src/plugins/impl/mpl-interactive/MplInteractivePlugin.tsx +5 -3
  443. package/src/plugins/impl/panel/PanelPlugin.tsx +16 -17
  444. package/src/plugins/impl/panel/__tests__/utils.test.ts +39 -1
  445. package/src/plugins/impl/panel/utils.ts +17 -0
  446. package/src/plugins/impl/plotly/PlotlyPlugin.tsx +2 -5
  447. package/src/plugins/impl/plotly/selection.ts +25 -19
  448. package/src/plugins/impl/vega/VegaPlugin.tsx +2 -3
  449. package/src/plugins/impl/vega/make-selectable.ts +4 -7
  450. package/src/plugins/layout/RoutesPlugin.tsx +1 -1
  451. package/src/stories/__tests__/editor-story.test.tsx +33 -0
  452. package/src/stories/button.stories.tsx +1 -0
  453. package/src/stories/cell.stories.tsx +1 -1
  454. package/src/stories/editor.stories.tsx +24 -17
  455. package/src/stories/form-wrapper.stories.tsx +2 -2
  456. package/src/stories/layout/vertical/one-column.stories.tsx +1 -1
  457. package/src/stories/runtime-story.tsx +1 -1
  458. package/src/stories/select.stories.tsx +2 -2
  459. package/src/stories/switchable-multi-select.stories.tsx +1 -1
  460. package/src/utils/__tests__/arrays.test.ts +35 -0
  461. package/src/utils/__tests__/batch-requests.test.ts +3 -3
  462. package/src/utils/__tests__/numbers.test.ts +105 -1
  463. package/src/utils/__tests__/objects.test.ts +21 -0
  464. package/src/utils/arrays.ts +11 -2
  465. package/src/utils/blob.ts +1 -1
  466. package/src/utils/maps.ts +0 -3
  467. package/src/utils/numbers.ts +79 -18
  468. package/src/utils/objects.ts +4 -9
  469. package/src/utils/once.ts +19 -19
  470. package/src/utils/tracer.ts +9 -5
  471. package/src/utils/zod-utils.ts +1 -3
  472. package/dist/any-language-editor-DZ_YTVus.js +0 -76
  473. package/dist/assets/__vite-browser-external-BXYkiFsk.js +0 -1
  474. package/dist/assets/worker-HPY9fbhz.js +0 -114
  475. package/dist/mermaid-HWGCJPDP-ByCsZnYF.js +0 -2
  476. package/dist/precisionRound-DMIVQrt2.js +0 -97
  477. package/src/core/attachments/state.ts +0 -21
  478. package/src/core/host/AGENTS.md +0 -6
  479. package/src/core/host/__tests__/events.test.ts +0 -46
  480. package/src/core/host/__tests__/notebook-client.test.ts +0 -191
  481. package/src/core/host/__tests__/protocol.test.ts +0 -214
  482. package/src/core/host/connection.ts +0 -81
  483. package/src/core/host/events.ts +0 -60
  484. package/src/core/host/notebook-client.ts +0 -226
  485. package/src/core/host/protocol.ts +0 -1943
  486. package/src/core/state/__tests__/jotai.test.ts +0 -25
  487. package/src/core/wasm/worker/message-buffer.ts +0 -33
  488. package/src/core/websocket/__tests__/createConnectionTransport.test.ts +0 -49
  489. package/src/core/websocket/__tests__/useNotebookClient.hook.test.tsx +0 -140
  490. package/src/core/websocket/__tests__/useNotebookSession.hook.test.tsx +0 -883
  491. package/src/core/websocket/transports/__tests__/basic.test.ts +0 -275
  492. package/src/core/websocket/transports/__tests__/sse.test.ts +0 -335
  493. package/src/core/websocket/transports/__tests__/transport.test.ts +0 -308
  494. package/src/core/websocket/transports/__tests__/ws.test.ts +0 -125
  495. package/src/core/websocket/transports/basic.ts +0 -102
  496. package/src/core/websocket/transports/create.ts +0 -31
  497. package/src/core/websocket/transports/sse.ts +0 -308
  498. package/src/core/websocket/transports/transport.ts +0 -50
  499. package/src/core/websocket/transports/ws.ts +0 -98
  500. package/src/core/websocket/useNotebookClient.ts +0 -80
  501. /package/src/{core/attachments/toast.ts → components/editor/agent-toast.ts} +0 -0
  502. /package/src/core/{websocket → session}/connection-utils.ts +0 -0
@@ -2,11 +2,7 @@
2
2
 
3
3
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
4
4
  import { Mocks } from "@/__mocks__/common";
5
- import {
6
- generateSessionId,
7
- isSessionId,
8
- type SessionId,
9
- } from "@/core/kernel/session";
5
+ import type { SessionId } from "@/core/kernel/session";
10
6
  import { Logger } from "@/utils/Logger";
11
7
  import { RuntimeManager } from "../runtime";
12
8
  import type { RuntimeConfig } from "../types";
@@ -56,38 +52,6 @@ describe("RuntimeManager", () => {
56
52
  });
57
53
 
58
54
  describe("getWsURL", () => {
59
- it("uses host notebook events with the existing session and auth parameters", () => {
60
- const original = window.location.href;
61
- window.history.replaceState({}, "", "/?types=run");
62
- try {
63
- const runtime = new RuntimeManager({
64
- ...mockConfig,
65
- url: "https://example.com/nested?file=notebook.py",
66
- notebookId: "nb_123",
67
- });
68
- const sessionId = "s_123456";
69
- if (!isSessionId(sessionId)) {
70
- throw new Error("Invalid test session ID");
71
- }
72
- const url = runtime.getWsURL(sessionId);
73
- expect({
74
- endpoint: `${url.origin}${url.pathname}`,
75
- params: Object.fromEntries(url.searchParams),
76
- }).toEqual({
77
- endpoint:
78
- "wss://example.com/nested/api/marimo/v1/notebooks/nb_123/events/ws",
79
- params: {
80
- file: "notebook.py",
81
- types: "marimo",
82
- session_id: sessionId,
83
- access_token: "test-token",
84
- },
85
- });
86
- } finally {
87
- window.history.replaceState({}, "", original);
88
- }
89
- });
90
-
91
55
  it("should return WebSocket URL with session ID for https", () => {
92
56
  const runtime = new RuntimeManager(mockConfig);
93
57
  const sessionId = "1234" as SessionId;
@@ -256,22 +220,24 @@ describe("RuntimeManager", () => {
256
220
  });
257
221
  });
258
222
 
259
- describe("getSseURL", () => {
260
- it.each([undefined, "nb_123"])(
261
- "uses the legacy session stream when notebookId is %s",
262
- (notebookId) => {
263
- const runtime = new RuntimeManager({
264
- ...mockConfig,
265
- url: "https://example.com/nested?file=notebook.py",
266
- notebookId,
267
- });
268
- const sessionId = generateSessionId();
269
- expect(runtime.getSseURL(sessionId).toString()).toBe(
270
- `https://example.com/nested/sse?file=notebook.py&session_id=${sessionId}`,
271
- );
272
- },
273
- );
223
+ describe("getNotebookURL", () => {
224
+ it("addresses the notebook below the host API without the auth token", () => {
225
+ const runtime = new RuntimeManager(
226
+ {
227
+ url: "https://sandbox.example.com/base",
228
+ lazy: true,
229
+ authToken: "my-secret-token",
230
+ },
231
+ true,
232
+ );
233
+ const url = runtime.getNotebookURL("n/1");
274
234
 
235
+ expect(url.pathname).toBe("/base/api/marimo/v1/notebooks/n%2F1");
236
+ expect(url.toString()).not.toContain("my-secret-token");
237
+ });
238
+ });
239
+
240
+ describe("getSseURL", () => {
275
241
  it("should never put the auth token in the URL, even cross-origin", () => {
276
242
  // SSE requests can send headers, so auth travels in the
277
243
  // Authorization header; a token in the URL would leak into
@@ -300,7 +266,8 @@ describe("RuntimeManager", () => {
300
266
  const url = runtime.getSseURL("s_123" as SessionId);
301
267
 
302
268
  expect(url.searchParams.get("access_token")).toBeNull();
303
- expect(url.searchParams.get("foo")).toBe("bar");
269
+ expect(url.searchParams.get("query_params")).toBe("foo=bar");
270
+ expect(url.toString()).not.toContain("page-token");
304
271
  } finally {
305
272
  window.history.pushState({}, "", original);
306
273
  }
@@ -720,9 +687,16 @@ describe("RuntimeManager", () => {
720
687
  expect(httpUrl.searchParams.get("base_param")).toBe("existing");
721
688
 
722
689
  // Should preserve all window-level query params (including custom ones)
723
- expect(wsUrl.searchParams.get("custom_param")).toBe("value123");
724
- expect(wsUrl.searchParams.get("user_token")).toBe("abc");
725
- expect(wsUrl.searchParams.get("theme")).toBe("dark");
690
+ expect(
691
+ Object.fromEntries(
692
+ new URLSearchParams(wsUrl.searchParams.get("query_params") ?? ""),
693
+ ),
694
+ ).toEqual({
695
+ base_param: "existing",
696
+ custom_param: "value123",
697
+ user_token: "abc",
698
+ theme: "dark",
699
+ });
726
700
  expect(httpUrl.searchParams.get("custom_param")).toBe("value123");
727
701
  expect(httpUrl.searchParams.get("user_token")).toBe("abc");
728
702
  expect(httpUrl.searchParams.get("theme")).toBe("dark");
@@ -781,7 +755,7 @@ describe("RuntimeManager", () => {
781
755
  );
782
756
  });
783
757
 
784
- it("should throw when creating WebSocket URLs from blob URLs", () => {
758
+ it("warns when creating WebSocket URLs from blob URLs", () => {
785
759
  const runtime = new RuntimeManager({
786
760
  url: "blob:https://example.com/12345678-1234-1234-1234-123456789abc",
787
761
  lazy: true,
@@ -789,7 +763,7 @@ describe("RuntimeManager", () => {
789
763
  const sessionId = "test" as SessionId;
790
764
 
791
765
  expect(runtime.getWsURL(sessionId).toString()).toMatchInlineSnapshot(
792
- `"blob:https://example.com/12345678-1234-1234-1234-123456789abc?session_id=test"`,
766
+ `"blob:https://example.com/12345678-1234-1234-1234-123456789abc?query_params=&session_id=test"`,
793
767
  );
794
768
  expect(Logger.warn).toHaveBeenCalledOnce();
795
769
  });
@@ -8,7 +8,7 @@ import { connectionAtom } from "../network/connection";
8
8
  import { isStaticNotebook } from "../static/static-state";
9
9
  import { wasmInitStateAtom } from "../wasm/state";
10
10
  import { isWasm } from "../wasm/utils";
11
- import { ConnectionState } from "../websocket/types";
11
+ import { ConnectionState } from "../session/types";
12
12
 
13
13
  export type RuntimeKind = "wasm" | "remote" | "static" | "islands";
14
14
 
@@ -4,8 +4,8 @@ import useEvent from "react-use-event-hook";
4
4
  import { Logger } from "@/utils/Logger";
5
5
  import { connectionAtom } from "../network/connection";
6
6
  import { store } from "../state/jotai";
7
- import { isAppNotStarted } from "../websocket/connection-utils";
8
- import { ConnectionState } from "../websocket/types";
7
+ import { isAppNotStarted } from "../session/connection-utils";
8
+ import { ConnectionState } from "../session/types";
9
9
  import { RuntimeManager } from "./runtime";
10
10
  import type { RuntimeConfig } from "./types";
11
11
 
@@ -1,5 +1,6 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
+ import type { NotebookId } from "@marimo-team/client/protocol";
3
4
  import { Deferred } from "@/utils/Deferred";
4
5
  import { Logger } from "@/utils/Logger";
5
6
  import { KnownQueryParams } from "../constants";
@@ -9,7 +10,7 @@ import { isStaticNotebook } from "../static/static-state";
9
10
  import { isWasm } from "../wasm/utils";
10
11
  import type { RuntimeConfig } from "./types";
11
12
 
12
- const HOST_EVENT_TYPES = ["marimo"];
13
+ const knownQueryParams = new Set(Object.values(KnownQueryParams));
13
14
 
14
15
  export class RuntimeManager {
15
16
  private initialHealthyCheck = new Deferred<void>();
@@ -73,10 +74,7 @@ export class RuntimeManager {
73
74
  }
74
75
 
75
76
  for (const [key, value] of currentParams.entries()) {
76
- if (
77
- restrictToKnownQueryParams &&
78
- !Object.values(KnownQueryParams).includes(key)
79
- ) {
77
+ if (restrictToKnownQueryParams && !knownQueryParams.has(key)) {
80
78
  continue;
81
79
  }
82
80
  baseUrl.searchParams.set(key, value);
@@ -131,43 +129,32 @@ export class RuntimeManager {
131
129
  return asWsUrl(this.appendCrossOriginAuth(url).toString());
132
130
  }
133
131
 
134
- /**
135
- * Search params for a session connection: the base URL's params, merged
136
- * with the current page's, plus the session id.
137
- */
138
- private getSessionSearchParams(sessionId: SessionId): URLSearchParams {
139
- const baseUrl = new URL(this.config.url);
140
- const searchParams = new URLSearchParams(baseUrl.search);
141
-
142
- // Merge in current page's query parameters
143
- const currentParams = new URLSearchParams(window.location.search);
144
- currentParams.forEach((value, key) => {
145
- // Don't override base URL params
146
- if (!searchParams.has(key)) {
147
- searchParams.set(key, value);
148
- }
149
- });
150
-
151
- searchParams.set(KnownQueryParams.sessionId, sessionId);
152
- return searchParams;
132
+ /** Keep notebook page parameters separate from connection options. */
133
+ private getSessionURL(path: string, sessionId: SessionId): URL {
134
+ const url = this.formatHttpURL({ path });
135
+ const notebookParams = new URLSearchParams(this.httpURL.search);
136
+ const pageParams = new URLSearchParams(window.location.search);
137
+ for (const key of pageParams.keys()) {
138
+ notebookParams.delete(key);
139
+ }
140
+ for (const [key, value] of pageParams) {
141
+ notebookParams.append(key, value);
142
+ }
143
+ // These parameters are internal and are not exposed by mo.query_params().
144
+ notebookParams.delete(KnownQueryParams.accessToken);
145
+ notebookParams.delete("refresh_token");
146
+ notebookParams.delete(KnownQueryParams.sessionId);
147
+ url.searchParams.set("query_params", notebookParams.toString());
148
+ url.searchParams.set(KnownQueryParams.sessionId, sessionId);
149
+ return url;
153
150
  }
154
151
 
155
152
  /**
156
153
  * The WebSocket URL of the runtime.
157
154
  */
158
155
  public getWsURL(sessionId: SessionId): URL {
159
- const searchParams = this.getSessionSearchParams(sessionId);
160
- // TODO(MO-8039): Rediscover hosts and resolve notebooks on reconnect.
161
- const notebookId = this.config.notebookId;
162
- const path = notebookId
163
- ? `/api/marimo/v1/notebooks/${encodeURIComponent(notebookId)}/events/ws`
164
- : "/ws";
165
- const url = this.formatWsURL(path, searchParams);
166
- if (notebookId) {
167
- // TODO(MO-8034): Separate host options from notebook query params.
168
- url.searchParams.set("types", HOST_EVENT_TYPES.join(","));
169
- }
170
- return url;
156
+ const url = this.getSessionURL("/ws", sessionId);
157
+ return asWsUrl(this.appendCrossOriginAuth(url).toString());
171
158
  }
172
159
 
173
160
  /**
@@ -177,11 +164,7 @@ export class RuntimeManager {
177
164
  * (see `headers()`) instead of the URL.
178
165
  */
179
166
  public getSseURL(sessionId: SessionId): URL {
180
- const url = this.formatHttpURL({
181
- path: "/sse",
182
- searchParams: this.getSessionSearchParams(sessionId),
183
- restrictToKnownQueryParams: false,
184
- });
167
+ const url = this.getSessionURL("/sse", sessionId);
185
168
  // The page URL can still carry access_token (merged in from the
186
169
  // current page's params, before auth cleanup strips it); never
187
170
  // forward it — a token in the URL leaks into proxy/server logs.
@@ -189,11 +172,24 @@ export class RuntimeManager {
189
172
  return url;
190
173
  }
191
174
 
175
+ /**
176
+ * The host API URL of a notebook, below which its operations are served.
177
+ * Requests send auth in headers (see `headers()`), never in the URL.
178
+ */
179
+ public getNotebookURL(notebookId: NotebookId): URL {
180
+ const url = this.formatHttpURL({
181
+ path: `/api/marimo/v1/notebooks/${encodeURIComponent(notebookId)}`,
182
+ });
183
+ url.searchParams.delete(KnownQueryParams.accessToken);
184
+ return url;
185
+ }
186
+
192
187
  /**
193
188
  * The WebSocket Sync URL of the runtime, for real-time updates.
194
189
  */
195
190
  public getWsSyncURL(sessionId: SessionId): URL {
196
- return this.formatWsURL("/ws_sync", this.getSessionSearchParams(sessionId));
191
+ const url = this.getSessionURL("/ws_sync", sessionId);
192
+ return asWsUrl(this.appendCrossOriginAuth(url).toString());
197
193
  }
198
194
 
199
195
  /**
@@ -1,8 +1,6 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
3
  export interface RuntimeConfig {
4
- /** The host notebook for WebSocket events. SSE uses the legacy session stream. */
5
- readonly notebookId?: string;
6
4
  /**
7
5
  * The URL of the runtime server.
8
6
  */
@@ -9,7 +9,7 @@ import { getAppConfig } from "../config/config";
9
9
  import { KnownQueryParams } from "../constants";
10
10
  import { connectionAtom } from "../network/connection";
11
11
  import { useRequestClient } from "../network/requests";
12
- import { ConnectionState } from "../websocket/types";
12
+ import { ConnectionState } from "../session/types";
13
13
  import { filenameAtom } from "./file-state";
14
14
 
15
15
  export function useFilename() {
@@ -21,7 +21,10 @@ import { Label } from "../../components/ui/label";
21
21
  import { useEvent } from "../../hooks/useEvent";
22
22
  import { Logger } from "../../utils/Logger";
23
23
  import { getCellConfigs, getNotebook, useNotebook } from "../cells/cells";
24
- import { withDocumentSave } from "../cells/document-changes";
24
+ import {
25
+ flushDocumentChanges,
26
+ withDocumentSave,
27
+ } from "../cells/document-changes";
25
28
  import { notebookCells } from "../cells/utils";
26
29
  import { formatAll } from "../codemirror/format";
27
30
  import { autoSaveConfigAtom } from "../config/config";
@@ -31,7 +34,8 @@ import { layoutStateAtom } from "../layout/state";
31
34
  import { kioskModeAtom } from "../mode";
32
35
  import { connectionAtom } from "../network/connection";
33
36
  import { useRequestClient } from "../network/requests";
34
- import { ConnectionState } from "../websocket/types";
37
+ import { ConnectionState } from "../session/types";
38
+ import { isWasm } from "../wasm/utils";
35
39
  import { filenameAtom } from "./file-state";
36
40
  import { useFilename, useUpdateFilename } from "./filename";
37
41
  import { lastSavedNotebookAtom, needsSaveAtom } from "./state";
@@ -136,7 +140,7 @@ export function useSaveNotebook() {
136
140
  return;
137
141
  }
138
142
 
139
- const savedNotebook = await withDocumentSave(async () => {
143
+ const save = async () => {
140
144
  // Grab the latest notebook state after formatting and after every
141
145
  // earlier document transaction has reached the server.
142
146
  const notebook = getNotebook();
@@ -157,7 +161,12 @@ export function useSaveNotebook() {
157
161
  persist: true,
158
162
  });
159
163
  return { names: cellNames, codes, configs, layout };
160
- });
164
+ };
165
+ // TODO(MO-7955): Remove this conditional once all frontend saves use
166
+ // the host protocol. Host saves cannot recover edits from an editor snapshot.
167
+ const savedNotebook = isWasm()
168
+ ? await flushDocumentChanges().then(save)
169
+ : await withDocumentSave(save);
161
170
  setLastSavedNotebook(savedNotebook);
162
171
  }),
163
172
  );
@@ -2,7 +2,7 @@
2
2
  import { useEffect, useRef } from "react";
3
3
  import type { UserConfig } from "../config/config-schema";
4
4
  import type { CellConfig } from "../network/types";
5
- import { type ConnectionStatus, ConnectionState } from "../websocket/types";
5
+ import { type ConnectionStatus, ConnectionState } from "../session/types";
6
6
 
7
7
  export function useAutoSave(opts: {
8
8
  codes: string[];
@@ -0,0 +1,235 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { NotebookEvents, StreamStopped } from "@marimo-team/client";
4
+ import { createStore } from "jotai/vanilla";
5
+ import { afterEach, describe, expect, it, onTestFinished, vi } from "vitest";
6
+ import { Deferred } from "@/utils/Deferred";
7
+ import { ConnectionState } from "../../notebook/replica/connection";
8
+ import {
9
+ type FakeEdit,
10
+ type FakeSave,
11
+ fakeBackend,
12
+ } from "../../notebook/replica/__tests__/fixtures";
13
+ import { createNotebookReplica } from "../../notebook/replica/replica";
14
+ import { createEditorBackend } from "../backend";
15
+
16
+ const wasm = vi.hoisted(() => ({ enabled: false, create: vi.fn() }));
17
+ vi.mock("../../wasm/utils", () => ({ isWasm: () => wasm.enabled }));
18
+ vi.mock("../../wasm/bridge", () => ({
19
+ PyodideBridge: { INSTANCE: { createNotebookBackend: wasm.create } },
20
+ }));
21
+
22
+ afterEach(() => {
23
+ wasm.enabled = false;
24
+ wasm.create.mockReset();
25
+ vi.unstubAllGlobals();
26
+ });
27
+
28
+ const options = {
29
+ static: false,
30
+ transportType: "websocket" as const,
31
+ url: () => "ws://localhost/ws",
32
+ headers: () => ({}),
33
+ notebookUrl: (id: string) =>
34
+ new URL(`http://localhost/api/marimo/v1/notebooks/${id}`),
35
+ waitUntilReady: async () => undefined,
36
+ checkHealth: async () => true,
37
+ };
38
+
39
+ /** Record each socket the transport opens. They never connect. */
40
+ function sockets() {
41
+ const opened: string[] = [];
42
+ vi.stubGlobal(
43
+ "WebSocket",
44
+ class extends EventTarget {
45
+ public constructor(url: URL) {
46
+ super();
47
+ opened.push(String(url));
48
+ }
49
+ public close() {}
50
+ },
51
+ );
52
+ return opened;
53
+ }
54
+
55
+ describe("createEditorBackend", () => {
56
+ it.each([
57
+ ["a static notebook", { ...options, static: true }, "idle"],
58
+ ["WASM", options, "wasm"],
59
+ ["SSE", { ...options, transportType: "sse" as const }, "fetch"],
60
+ ["the default", options, "socket"],
61
+ ] as const)("follows %s", async (_name, chosen, expected) => {
62
+ wasm.enabled = expected === "wasm";
63
+ const worker = vi.fn(() => new Promise<void>(() => undefined));
64
+ wasm.create.mockResolvedValue(fakeBackend({ transport: worker }));
65
+ const fetch = vi.fn(() => new Promise<Response>(() => undefined));
66
+ const opened = sockets();
67
+ vi.stubGlobal("fetch", fetch);
68
+ const events = new NotebookEvents(createEditorBackend(chosen).transport);
69
+ await vi.waitFor(() =>
70
+ expect(
71
+ {
72
+ idle: events.status === "connected" && !wasm.create.mock.calls.length,
73
+ wasm: worker.mock.calls.length === 1,
74
+ fetch: fetch.mock.calls.length === 1,
75
+ socket: opened.length === 1,
76
+ }[expected],
77
+ ).toBe(true),
78
+ );
79
+ events.close();
80
+ });
81
+
82
+ it("edits a reported notebook on its host with the session's headers", async () => {
83
+ const fetch = vi.fn(
84
+ async (_input: RequestInfo | URL, _init?: RequestInit) =>
85
+ new Response(
86
+ JSON.stringify({ type: "conflict", title: "Conflict", status: 409 }),
87
+ {
88
+ status: 409,
89
+ headers: { "Content-Type": "application/problem+json" },
90
+ },
91
+ ),
92
+ );
93
+ vi.stubGlobal("fetch", fetch);
94
+ const backend = createEditorBackend({
95
+ ...options,
96
+ headers: () => ({
97
+ Authorization: "Bearer editor-token",
98
+ "Marimo-Server-Token": "server-token",
99
+ }),
100
+ });
101
+ const result = await backend
102
+ .notebook("n1")
103
+ .edit([], { idempotencyKey: "edit-1" });
104
+ expect(result).toMatchObject({ ok: false, status: 409 });
105
+ const [[input, init]] = fetch.mock.calls;
106
+ expect(String(input)).toBe(
107
+ "http://localhost/api/marimo/v1/notebooks/n1/edits",
108
+ );
109
+ const headers = new Headers(init?.headers);
110
+ expect(init?.method).toBe("POST");
111
+ expect(headers.get("Authorization")).toBe("Bearer editor-token");
112
+ expect(headers.get("Marimo-Server-Token")).toBe("server-token");
113
+ expect(headers.get("Idempotency-Key")).toBe("edit-1");
114
+ });
115
+
116
+ it("does not edit a static notebook", async () => {
117
+ const fetch = vi.fn();
118
+ vi.stubGlobal("fetch", fetch);
119
+ const result = await createEditorBackend({ ...options, static: true })
120
+ .notebook("n1")
121
+ .edit([], { idempotencyKey: "edit-1" });
122
+ expect(result).toMatchObject({ ok: false, status: 501 });
123
+ expect(fetch).not.toHaveBeenCalled();
124
+ });
125
+
126
+ it("routes WASM commands through the worker once its stream starts", async () => {
127
+ wasm.enabled = true;
128
+ const edit = vi.fn<FakeEdit>();
129
+ const save = vi.fn<FakeSave>();
130
+ const transport = vi.fn(async () => undefined);
131
+ wasm.create.mockResolvedValue(fakeBackend({ transport, edit, save }));
132
+ const backend = createEditorBackend(options);
133
+ expect(wasm.create).not.toHaveBeenCalled();
134
+ expect(() => backend.notebook("n1")).toThrow("has not started");
135
+
136
+ const attempt = { cursor: null, signal: new AbortController().signal };
137
+ const handlers = { open: vi.fn(), message: vi.fn() };
138
+ await backend.transport(attempt, handlers);
139
+ await backend.transport(attempt, handlers);
140
+ expect(wasm.create).toHaveBeenCalledOnce();
141
+ expect(transport).toHaveBeenCalledWith(attempt, handlers);
142
+ await backend.notebook("n1").edit([], { idempotencyKey: "edit-1" });
143
+ expect(edit).toHaveBeenCalledWith("n1", [], { idempotencyKey: "edit-1" });
144
+ await backend.notebook("n2").save({ idempotencyKey: "save-1" });
145
+ expect(save).toHaveBeenCalledWith("n2", { idempotencyKey: "save-1" });
146
+ });
147
+
148
+ it("stops following WASM when the worker does not start", async () => {
149
+ wasm.enabled = true;
150
+ wasm.create.mockRejectedValue(new Error("Initialization failed"));
151
+ await expect(
152
+ createEditorBackend(options).transport(
153
+ { cursor: null, signal: new AbortController().signal },
154
+ { open: vi.fn(), message: vi.fn() },
155
+ ),
156
+ ).rejects.toBeInstanceOf(StreamStopped);
157
+ });
158
+ });
159
+
160
+ describe("the session backend's startup", () => {
161
+ it.each([true, false])(
162
+ "rechecks health after failed startup (healthy: %s)",
163
+ async (healthy) => {
164
+ const opened = sockets();
165
+ const waitUntilReady = vi
166
+ .fn()
167
+ .mockRejectedValue(new Error("Startup gave up"));
168
+ const checkHealth = vi.fn().mockResolvedValue(healthy);
169
+ const store = createStore();
170
+ const client = createNotebookReplica(store);
171
+ onTestFinished(
172
+ client.connect(
173
+ createEditorBackend({ ...options, waitUntilReady, checkHealth }),
174
+ { onNotification: vi.fn() },
175
+ ),
176
+ );
177
+ await vi.waitFor(() =>
178
+ expect(store.get(client.connectionAtom).state).toBe(
179
+ ConnectionState.CLOSED,
180
+ ),
181
+ );
182
+
183
+ await client.reconnect();
184
+ await vi.waitFor(() => expect(opened).toHaveLength(healthy ? 1 : 0));
185
+ expect({
186
+ healthChecks: checkHealth.mock.calls.length,
187
+ startupWaits: waitUntilReady.mock.calls.length,
188
+ state: store.get(client.connectionAtom).state,
189
+ }).toEqual({
190
+ healthChecks: 1,
191
+ startupWaits: 1,
192
+ state: healthy ? ConnectionState.CONNECTING : ConnectionState.CLOSED,
193
+ });
194
+ },
195
+ );
196
+
197
+ it("shares one startup wait across connections", async () => {
198
+ const opened = sockets();
199
+ const ready = new Deferred<void>();
200
+ const waitUntilReady = vi.fn(() => ready.promise);
201
+ const backend = createEditorBackend({ ...options, waitUntilReady });
202
+ const client = createNotebookReplica(createStore());
203
+ // Strict Mode connects, disconnects, and connects again.
204
+ client.connect(backend, { onNotification: vi.fn() })();
205
+ onTestFinished(client.connect(backend, { onNotification: vi.fn() }));
206
+ await Promise.resolve();
207
+ expect(opened).toEqual([]);
208
+
209
+ ready.resolve();
210
+ await vi.waitFor(() => expect(opened).toHaveLength(1));
211
+ expect(waitUntilReady).toHaveBeenCalledOnce();
212
+ });
213
+
214
+ it("opens nothing for a connection that ended during startup", async () => {
215
+ const opened = sockets();
216
+ const ready = new Deferred<void>();
217
+ const store = createStore();
218
+ const client = createNotebookReplica(store);
219
+ const disconnect = client.connect(
220
+ createEditorBackend({ ...options, waitUntilReady: () => ready.promise }),
221
+ { onNotification: vi.fn() },
222
+ );
223
+ expect(store.get(client.connectionAtom)).toEqual({
224
+ state: ConnectionState.CONNECTING,
225
+ });
226
+ disconnect();
227
+ ready.resolve();
228
+ await ready.promise;
229
+ await Promise.resolve();
230
+ expect(opened).toEqual([]);
231
+ expect(store.get(client.connectionAtom)).toEqual({
232
+ state: ConnectionState.NOT_STARTED,
233
+ });
234
+ });
235
+ });