@marimo-team/islands 0.25.3-dev17 → 0.25.3-dev170

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 (547) hide show
  1. package/dist/{ConnectedDataExplorerComponent-CNMliVzw.js → ConnectedDataExplorerComponent-BbSXV-Xa.js} +21 -19
  2. package/dist/{ErrorBoundary-jaGao4LA.js → ErrorBoundary-NE86qEWZ.js} +18 -18
  3. package/dist/{abnfDiagram-VCTEODGH-D-Ez91gV.js → abnfDiagram-VCTEODGH-D7n07nXf.js} +2 -2
  4. package/dist/any-language-editor-3E24SnCV.js +75 -0
  5. package/dist/{architectureDiagram-5GKGNRK7-CTpcNtO8.js → architectureDiagram-5GKGNRK7-D6CjRib5.js} +1 -1
  6. package/dist/{arrays-CN1djC4k.js → arrays-R6JsImiT.js} +8 -0
  7. package/dist/assets/__vite-browser-external-DVeFf2e7.js +1 -0
  8. package/dist/assets/worker-C1pfNBsx.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-CC7GekpG.js} +1144 -1143
  12. package/dist/{check-EDwSwP89.js → check-SpOMoCLP.js} +1 -1
  13. package/dist/{chunk-IMKFNOWR-DGxi-zeo.js → chunk-IMKFNOWR-6dtPz7uS.js} +1 -1
  14. package/dist/{chunk-SHT3W25Y-CV9J3rAB.js → chunk-SHT3W25Y-BwnFNKDA.js} +1 -1
  15. package/dist/{chunk-SVP7TREG-BL-9Fqgg.js → chunk-SVP7TREG-D2eV4vBJ.js} +1 -1
  16. package/dist/{chunk-TICWLB2K-Cu_XMjDM.js → chunk-TICWLB2K-BDs4-_-r.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-DCJnf_8v.js} +2 -2
  19. package/dist/{classDiagram-v2-VYDZK3BY-B7FXJ1Cp.js → classDiagram-v2-VYDZK3BY-BOmSzPBD.js} +2 -2
  20. package/dist/{code-visibility-Bub8tnaA.js → code-visibility-Bzvc6ajl.js} +25 -25
  21. package/dist/{common-CdT0nyN1.js → common-Ciw4zkh2.js} +6933 -6754
  22. package/dist/{copy-icon-Ft7t9bqz.js → copy-icon-C1trdvVt.js} +3 -3
  23. package/dist/{cynefinDiagram-5FMLGOSQ-CH96rwba.js → cynefinDiagram-5FMLGOSQ-Bln_Soky.js} +1 -1
  24. package/dist/{dagre-GXQ25YYZ-BD3PmpFY.js → dagre-GXQ25YYZ-D-nf7WzW.js} +1 -1
  25. package/dist/{data-grid-overlay-editor-Dbcss6_2.js → data-grid-overlay-editor-AQREmxI7.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-PsgSMkY2.js} +1 -1
  28. package/dist/{diagram-UQ7AKVKN-muQtNZc4.js → diagram-UQ7AKVKN-DfwfUuSw.js} +1 -1
  29. package/dist/{diagram-VX7I27RA-CdhuZAqR.js → diagram-VX7I27RA-C9_e-oMc.js} +1 -1
  30. package/dist/{diagram-Z3DM3KII-BD8mXjOj.js → diagram-Z3DM3KII-CWLa5tuY.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-B6ftyzOg.js} +1277 -1229
  34. package/dist/{ebnfDiagram-PWID7BFC-BIN1sweE.js → ebnfDiagram-PWID7BFC-B1QRM58v.js} +2 -2
  35. package/dist/{erDiagram-RLTQ6QDP-Ctp5cfoN.js → erDiagram-RLTQ6QDP-DJK_FqvW.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-DgK3wUJR.js} +5728 -5638
  38. package/dist/{feedback-button-B3Wrnsf_.js → feedback-button-8a6SETmT.js} +23 -23
  39. package/dist/{flowDiagram-HODETNUW-3EfLrlFb.js → flowDiagram-HODETNUW-Uxc25XSJ.js} +2 -2
  40. package/dist/{formats-27CW_UP4.js → formats-DQCyPd5Z.js} +3 -3
  41. package/dist/{ganttDiagram-EL5Y4UJY-BQT16JSc.js → ganttDiagram-EL5Y4UJY-Dh_4cBJZ.js} +2 -2
  42. package/dist/{glide-data-editor-BDYKJdqK.js → glide-data-editor-B70oke_v.js} +370 -368
  43. package/dist/{glide-utils-DJyqh0EM.js → glide-utils-Dz_F4pAY.js} +16 -16
  44. package/dist/{globals-DGTE1S0_.js → globals-Cxd5hNlI.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-D4yG5Ru0.js} +631 -594
  47. package/dist/{infoDiagram-27XIBGKW-DipQoHxl.js → infoDiagram-27XIBGKW-DmjxqwL0.js} +1 -1
  48. package/dist/{ishikawaDiagram-5VMMS53U-DgFMjSGz.js → ishikawaDiagram-5VMMS53U-NgCLHqQi.js} +1 -1
  49. package/dist/{kanban-definition-UXKFOSKX-w1ACVjgl.js → kanban-definition-UXKFOSKX-CZaG-iam.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 +7689 -7342
  54. package/dist/{mermaid-CHo49Yv-.js → mermaid-CSvw7c71.js} +38 -38
  55. package/dist/mermaid-HWGCJPDP-Du3-HPQA.js +2 -0
  56. package/dist/{mindmap-definition-YA3MSWOX-CrT0KSfX.js → mindmap-definition-YA3MSWOX-DJYQBDH_.js} +1 -1
  57. package/dist/{number-overlay-editor-S8UJY0i5.js → number-overlay-editor-ChK-SKUk.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-CHKO_dAe.js} +2 -2
  61. package/dist/{pieDiagram-E7YTZNPT-BfxJ_XCc.js → pieDiagram-E7YTZNPT-DVevbMAo.js} +1 -1
  62. package/dist/precisionRound-DhAzeRcm.js +49 -0
  63. package/dist/{process-output-BZg56CCb.js → process-output-DxvBAeL3.js} +4 -4
  64. package/dist/{quadrantDiagram-AXDQQJYC-Ccw0xMjN.js → quadrantDiagram-AXDQQJYC-Blrm7V7_.js} +1 -1
  65. package/dist/{railroadDiagram-O6MQD6OU-8vFLVTPu.js → railroadDiagram-O6MQD6OU-Detj1kcB.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-BLFD0T_Z.js} +1 -1
  68. package/dist/{reveal-component-De4Pvf4V.js → reveal-component-CXMXeDZD.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-BZBgatQV.js} +1 -1
  71. package/dist/{spec-BwarBpUJ.js → spec-Mnkn3XEr.js} +41 -11
  72. package/dist/{stateDiagram-D77RDMKH-DQeA_CwI.js → stateDiagram-D77RDMKH-4BZW9-6_.js} +2 -2
  73. package/dist/{stateDiagram-v2-MP3YSRHH-BxxQucm1.js → stateDiagram-v2-MP3YSRHH-C4sKC8Iq.js} +2 -2
  74. package/dist/style.css +1 -1
  75. package/dist/{swimlanes-42K2YHIH-Do6L-cYg.js → swimlanes-42K2YHIH-SL5yeGe2.js} +1 -1
  76. package/dist/{swimlanesDiagram-VR7AAH4N-DUzPvSVU.js → swimlanesDiagram-VR7AAH4N-DqPmAYUv.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-BfXUMWkq.js} +1 -1
  80. package/dist/{toDate-UtiwmnH2.js → toDate-TMubKIVL.js} +37 -36
  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-DjMGFzFy.js} +15 -14
  85. package/dist/{useDateFormatter-D8XruNGG.js → useDateFormatter-eCL_SdeG.js} +2 -2
  86. package/dist/{useDeepCompareMemoize-DXwzRINI.js → useDeepCompareMemoize-BQR5TJot.js} +1 -1
  87. package/dist/{useIframeCapabilities-DBETvvPl.js → useIframeCapabilities-CSdWn6NP.js} +1 -1
  88. package/dist/{useLifecycle-j7D9uD1o.js → useLifecycle-C_GvKWbQ.js} +28 -25
  89. package/dist/{useTheme-CRnjkku0.js → useTheme-Bns2gJZ6.js} +224 -220
  90. package/dist/{vega-component-BeF9dfyO.js → vega-component-BsVptuhz.js} +148 -148
  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-CH65znGa.js} +1 -1
  94. package/dist/{wardleyDiagram-VM6X3IG4-48cUWkfA.js → wardleyDiagram-VM6X3IG4-z9pWKb2_.js} +1 -1
  95. package/dist/{xychartDiagram-S5SC5T6Z-CswYgYeu.js → xychartDiagram-S5SC5T6Z-MxyaWjjq.js} +2 -2
  96. package/package.json +16 -16
  97. package/src/__mocks__/requests.ts +0 -1
  98. package/src/__tests__/module-entry.test.ts +44 -0
  99. package/src/__tests__/mount.test.ts +1 -21
  100. package/src/__tests__/{host-imports.test.ts → replica-imports.test.ts} +12 -11
  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/app-config/user-config-form.tsx +35 -0
  105. package/src/components/chat/__tests__/message-queue.test.tsx +17 -0
  106. package/src/components/chat/acp/__tests__/session-tabs.test.tsx +43 -0
  107. package/src/components/chat/acp/__tests__/state.test.ts +2 -2
  108. package/src/components/chat/acp/blocks.tsx +3 -2
  109. package/src/components/chat/acp/session-tabs.tsx +8 -3
  110. package/src/components/chat/chat-components.tsx +2 -0
  111. package/src/components/chat/chat-panel.tsx +9 -8
  112. package/src/components/chat/chat-utils.ts +4 -8
  113. package/src/components/data-table/TableBottomBar.tsx +4 -1
  114. package/src/components/data-table/TableTopBar.tsx +2 -0
  115. package/src/components/data-table/__tests__/chart-spec-model.test.ts +195 -195
  116. package/src/components/data-table/__tests__/column-explorer.test.tsx +27 -1
  117. package/src/components/data-table/__tests__/columns.test.tsx +105 -0
  118. package/src/components/data-table/__tests__/context-menu-interaction.test.tsx +130 -0
  119. package/src/components/data-table/__tests__/data-table.test.tsx +1 -0
  120. package/src/components/data-table/__tests__/date-popover.test.tsx +72 -0
  121. package/src/components/data-table/__tests__/export-actions.test.tsx +105 -0
  122. package/src/components/data-table/__tests__/keyboard-selection.test.tsx +188 -0
  123. package/src/components/data-table/__tests__/pagination.test.tsx +28 -1
  124. package/src/components/data-table/__tests__/search-highlight.test.tsx +109 -0
  125. package/src/components/data-table/__tests__/url-detector.test.tsx +74 -0
  126. package/src/components/data-table/cell-selection/feature.ts +2 -2
  127. package/src/components/data-table/cell-selection/types.ts +2 -2
  128. package/src/components/data-table/charts/__tests__/chart-panel.test.tsx +74 -1
  129. package/src/components/data-table/charts/__tests__/spec-snapshot.test.ts +20 -20
  130. package/src/components/data-table/charts/__tests__/spec.test.ts +78 -52
  131. package/src/components/data-table/charts/__tests__/storage.test.ts +4 -1
  132. package/src/components/data-table/charts/chart-spec/spec.ts +68 -43
  133. package/src/components/data-table/charts/charts.tsx +40 -30
  134. package/src/components/data-table/charts/storage.ts +16 -5
  135. package/src/components/data-table/column-explorer-panel/column-explorer.tsx +1 -1
  136. package/src/components/data-table/column-summary/chart-spec-model.tsx +23 -16
  137. package/src/components/data-table/column-summary/column-summary.tsx +2 -4
  138. package/src/components/data-table/column-visibility-dropdown.tsx +8 -13
  139. package/src/components/data-table/columns.tsx +45 -46
  140. package/src/components/data-table/context-menu.tsx +36 -20
  141. package/src/components/data-table/data-table.tsx +17 -1
  142. package/src/components/data-table/date-popover.tsx +6 -4
  143. package/src/components/data-table/export-actions.tsx +76 -48
  144. package/src/components/data-table/pagination.tsx +44 -33
  145. package/src/components/data-table/range-focus/atoms.ts +4 -6
  146. package/src/components/data-table/range-focus/use-cell-range-selection.ts +65 -38
  147. package/src/components/data-table/range-focus/use-scroll-into-view.ts +21 -2
  148. package/src/components/data-table/renderers.tsx +16 -7
  149. package/src/components/data-table/row-viewer-panel/__tests__/row-viewer.test.tsx +24 -1
  150. package/src/components/data-table/row-viewer-panel/row-viewer.tsx +4 -4
  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/datasources/__tests__/column-actions.test.tsx +97 -0
  155. package/src/components/datasources/__tests__/column-preview.test.tsx +98 -5
  156. package/src/components/datasources/__tests__/datasources-interactions.test.tsx +181 -0
  157. package/src/components/datasources/__tests__/table-list.test.tsx +118 -0
  158. package/src/components/datasources/__tests__/utils.test.ts +57 -6
  159. package/src/components/datasources/column-preview.tsx +13 -4
  160. package/src/components/datasources/datasources.tsx +51 -39
  161. package/src/components/datasources/utils.ts +32 -23
  162. package/src/components/debugger/debugger-code.tsx +5 -0
  163. package/src/components/dependency-graph/dependency-graph-tree.tsx +8 -8
  164. package/src/components/dependency-graph/elements.ts +24 -21
  165. package/src/components/dependency-graph/panels.tsx +2 -1
  166. package/src/components/dependency-graph/utils/__tests__/layout.test.ts +18 -11
  167. package/src/components/editor/SortableCell.tsx +1 -3
  168. package/src/{core/attachments/__tests__/toast.test.tsx → components/editor/__tests__/agent-toast.test.tsx} +1 -1
  169. package/src/components/editor/__tests__/data-attributes.test.tsx +6 -2
  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-modal.test.tsx +24 -0
  173. package/src/components/editor/actions/pair-with-agent-modal.tsx +18 -15
  174. package/src/components/editor/actions/useRestartKernel.tsx +1 -1
  175. package/src/components/editor/ai/add-cell-with-ai.tsx +3 -3
  176. package/src/components/editor/ai/ai-completion-editor.tsx +1 -0
  177. package/src/components/editor/alerts/__tests__/connection-notice.test.tsx +1 -1
  178. package/src/components/editor/alerts/connection-notice.tsx +1 -1
  179. package/src/components/editor/alerts/startup-logs-alert.tsx +1 -0
  180. package/src/components/editor/app-container.tsx +1 -1
  181. package/src/components/editor/boundary/__tests__/ErrorBoundary.test.tsx +1 -1
  182. package/src/components/editor/cell/CreateCellButton.tsx +10 -12
  183. package/src/components/editor/cell/DeleteButton.tsx +3 -2
  184. package/src/components/editor/cell/RunButton.tsx +2 -2
  185. package/src/components/editor/cell/StagedAICell.tsx +10 -2
  186. package/src/components/editor/cell/StopButton.tsx +2 -2
  187. package/src/components/editor/cell/__tests__/useShouldShowInterrupt.test.ts +39 -0
  188. package/src/components/editor/cell/code/__tests__/language-toggle.test.tsx +54 -0
  189. package/src/components/editor/cell/code/cell-editor.tsx +9 -16
  190. package/src/components/editor/cell/code/language-toggle.tsx +8 -7
  191. package/src/components/editor/cell/collapse.tsx +5 -3
  192. package/src/components/editor/cell/useShouldShowInterrupt.ts +2 -12
  193. package/src/components/editor/chrome/components/__tests__/feedback-button.test.tsx +1 -1
  194. package/src/components/editor/chrome/components/feedback-button.tsx +1 -1
  195. package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +31 -2
  196. package/src/components/editor/chrome/panels/__tests__/runtime-details.test.tsx +1 -1
  197. package/src/components/editor/chrome/panels/__tests__/sandbox-recovery.test.tsx +1 -1
  198. package/src/components/editor/chrome/panels/cache-panel.tsx +1 -0
  199. package/src/components/editor/chrome/panels/outline/__tests__/outline-keyboard.test.tsx +84 -0
  200. package/src/components/editor/chrome/panels/outline/floating-outline.tsx +39 -14
  201. package/src/components/editor/chrome/panels/outline/useActiveOutline.tsx +3 -2
  202. package/src/components/editor/chrome/panels/packages-panel.tsx +4 -7
  203. package/src/components/editor/chrome/panels/secrets-panel.tsx +1 -0
  204. package/src/components/editor/chrome/panels/snippets-panel.tsx +5 -1
  205. package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +9 -18
  206. package/src/components/editor/chrome/wrapper/__tests__/document-sync-status.test.tsx +92 -0
  207. package/src/components/editor/chrome/wrapper/__tests__/pending-ai-cells.test.tsx +58 -1
  208. package/src/components/editor/chrome/wrapper/footer-items/attachment-status.tsx +4 -4
  209. package/src/components/editor/chrome/wrapper/footer-items/backend-status.tsx +1 -1
  210. package/src/components/editor/chrome/wrapper/footer-items/document-sync-status.tsx +54 -0
  211. package/src/components/editor/chrome/wrapper/footer-items/lsp-status.tsx +1 -1
  212. package/src/components/editor/chrome/wrapper/footer-items/machine-stats.tsx +1 -1
  213. package/src/components/editor/chrome/wrapper/footer-items/runtime-settings.tsx +1 -7
  214. package/src/components/editor/chrome/wrapper/footer.tsx +2 -0
  215. package/src/components/editor/chrome/wrapper/pending-ai-cells.tsx +12 -2
  216. package/src/components/editor/chrome/wrapper/storage.ts +1 -1
  217. package/src/components/editor/connections/components.tsx +1 -1
  218. package/src/components/editor/controls/Controls.tsx +2 -2
  219. package/src/components/editor/controls/__tests__/command-palette.test.tsx +53 -0
  220. package/src/components/editor/controls/__tests__/keyboard-shortcuts.test.tsx +61 -0
  221. package/src/components/editor/controls/__tests__/notebook-menu-dropdown.test.tsx +1 -1
  222. package/src/components/editor/controls/command-palette.tsx +22 -10
  223. package/src/components/editor/controls/keyboard-shortcuts.tsx +14 -5
  224. package/src/components/editor/dynamic-favicon.tsx +21 -40
  225. package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +9 -23
  226. package/src/components/editor/errors/send-error-report-button.tsx +3 -6
  227. package/src/components/editor/file-tree/__tests__/file-explorer-interactions.test.tsx +6 -0
  228. package/src/components/editor/file-tree/__tests__/file-explorer-navigation.test.tsx +47 -0
  229. package/src/components/editor/file-tree/__tests__/use-file-search.test.tsx +91 -0
  230. package/src/components/editor/file-tree/file-explorer.tsx +45 -8
  231. package/src/components/editor/file-tree/file-name-input.tsx +1 -0
  232. package/src/components/editor/file-tree/file-viewer.tsx +4 -2
  233. package/src/components/editor/file-tree/use-file-search.ts +35 -21
  234. package/src/components/editor/header/__tests__/status.test.tsx +5 -3
  235. package/src/components/editor/header/app-header.tsx +1 -1
  236. package/src/components/editor/header/status.tsx +40 -20
  237. package/src/components/editor/links/__tests__/cell-link.test.tsx +146 -0
  238. package/src/components/editor/links/cell-link.tsx +14 -5
  239. package/src/components/editor/navigation/focus-utils.ts +4 -4
  240. package/src/components/editor/notebook-banner.tsx +1 -0
  241. package/src/components/editor/notebook-cell.tsx +12 -3
  242. package/src/components/editor/output/MarimoErrorOutput.tsx +2 -1
  243. package/src/components/editor/output/MarimoTracebackOutput.tsx +19 -17
  244. package/src/components/editor/output/Outputs.css +8 -0
  245. package/src/components/editor/output/__tests__/traceback.test.tsx +27 -1
  246. package/src/components/editor/output/ansi-reduce.ts +33 -20
  247. package/src/components/editor/output/console/ConsoleOutput.tsx +19 -25
  248. package/src/components/editor/package-alert.tsx +14 -7
  249. package/src/components/editor/renderers/grid-layout/grid-layout.tsx +12 -6
  250. package/src/components/editor/renderers/vertical-layout/sidebar/toggle.tsx +7 -1
  251. package/src/components/home/components.tsx +17 -31
  252. package/src/components/icons/__tests__/large-spinner.test.tsx +39 -0
  253. package/src/components/icons/large-spinner.tsx +3 -14
  254. package/src/components/icons/spinner.tsx +1 -1
  255. package/src/components/mcp/mcp-status-indicator.tsx +1 -1
  256. package/src/components/pages/__tests__/home-page.test.tsx +116 -0
  257. package/src/components/pages/home-page.tsx +164 -91
  258. package/src/components/scratchpad/scratchpad.test.tsx +95 -0
  259. package/src/components/scratchpad/scratchpad.tsx +16 -3
  260. package/src/components/slides/reveal-component.tsx +4 -2
  261. package/src/components/slides/slide-cell-view.tsx +1 -1
  262. package/src/components/slides/slide-form.tsx +1 -0
  263. package/src/components/tracing/tracing-keyboard.test.tsx +85 -0
  264. package/src/components/tracing/tracing.tsx +45 -41
  265. package/src/components/ui/__tests__/draggable-popover.test.tsx +24 -0
  266. package/src/components/ui/__tests__/range-slider.test.tsx +165 -0
  267. package/src/components/ui/alert-dialog.tsx +1 -1
  268. package/src/components/ui/draggable-popover.tsx +24 -2
  269. package/src/components/ui/range-slider.tsx +22 -9
  270. package/src/core/ai/__tests__/staged-cells.test.ts +8 -5
  271. package/src/core/ai/context/providers/datasource.ts +6 -2
  272. package/src/core/ai/staged-cells.ts +7 -2
  273. package/src/core/ai/tools/__tests__/edit-notebook-tool.test.ts +3 -15
  274. package/src/core/ai/tools/base.ts +16 -7
  275. package/src/core/ai/tools/edit-notebook-tool.ts +61 -48
  276. package/src/core/ai/tools/registry.ts +13 -8
  277. package/src/core/ai/tools/sample-tool.ts +6 -7
  278. package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +4 -0
  279. package/src/core/cells/__tests__/cell.test.ts +39 -0
  280. package/src/core/cells/__tests__/cells.test.ts +3 -298
  281. package/src/core/cells/__tests__/collapseConsoleOutputs.test.ts +54 -0
  282. package/src/core/cells/__tests__/document.test.ts +410 -0
  283. package/src/core/cells/__tests__/flush-document.test.ts +40 -0
  284. package/src/core/cells/__tests__/notebook-outline.test.ts +108 -0
  285. package/src/core/cells/__tests__/session.test.ts +117 -36
  286. package/src/core/cells/__tests__/with-document-save.test.ts +67 -0
  287. package/src/core/cells/cell.ts +14 -3
  288. package/src/core/cells/cells.ts +30 -159
  289. package/src/core/cells/columns.ts +38 -0
  290. package/src/core/cells/document.ts +360 -0
  291. package/src/core/cells/pending-delete-service.ts +2 -6
  292. package/src/core/cells/session.ts +1 -1
  293. package/src/core/cells/types.ts +22 -1
  294. package/src/core/codemirror/__tests__/setup.test.ts +29 -29
  295. package/src/core/codemirror/ai/__tests__/resources.test.ts +117 -0
  296. package/src/core/codemirror/ai/resources.ts +1 -1
  297. package/src/core/codemirror/cells/__tests__/extensions.test.ts +42 -1
  298. package/src/core/codemirror/cells/extensions.ts +21 -3
  299. package/src/core/codemirror/format.ts +6 -5
  300. package/src/core/codemirror/keymaps/vim.ts +2 -6
  301. package/src/core/codemirror/language/__tests__/sql.test.ts +22 -20
  302. package/src/core/codemirror/language/__tests__/utils.test.ts +187 -15
  303. package/src/core/codemirror/language/cell-code.ts +114 -0
  304. package/src/core/codemirror/language/extension.ts +52 -35
  305. package/src/core/codemirror/language/languages/markdown.ts +8 -6
  306. package/src/core/codemirror/language/languages/python.ts +16 -10
  307. package/src/core/codemirror/language/languages/sql/sql.ts +16 -24
  308. package/src/core/codemirror/language/panel/panel.tsx +1 -0
  309. package/src/core/codemirror/language/types.ts +7 -7
  310. package/src/core/codemirror/language/utils.ts +9 -39
  311. package/src/core/codemirror/lsp/notebook-lsp.ts +6 -5
  312. package/src/core/codemirror/readonly/__tests__/extension.test.ts +1 -1
  313. package/src/core/codemirror/readonly/extension.ts +2 -2
  314. package/src/core/codemirror/rtc/extension.ts +24 -13
  315. package/src/core/codemirror/rtc/loro/awareness.ts +34 -19
  316. package/src/core/codemirror/rtc/loro/sync.ts +1 -0
  317. package/src/core/codemirror/utils.ts +1 -0
  318. package/src/core/config/__tests__/config-schema.test.ts +5 -3
  319. package/src/core/config/config-schema.ts +3 -0
  320. package/src/core/datasets/__tests__/data-source-discovery.test.ts +5 -5
  321. package/src/core/datasets/data-source-discovery.ts +4 -6
  322. package/src/core/edit-app.tsx +6 -5
  323. package/src/core/errors/error-entries.ts +3 -0
  324. package/src/core/export/hooks.ts +1 -1
  325. package/src/core/export/layout.ts +2 -2
  326. package/src/core/hotkeys/actions.ts +2 -4
  327. package/src/core/islands/__tests__/bootstrap.test.ts +16 -42
  328. package/src/core/islands/__tests__/bridge.test.ts +31 -26
  329. package/src/core/islands/bootstrap.ts +22 -52
  330. package/src/core/islands/bridge.ts +31 -21
  331. package/src/core/islands/components/__tests__/IslandControls.test.tsx +7 -5
  332. package/src/core/islands/worker/__tests__/controller.test.ts +62 -4
  333. package/src/core/islands/worker/controller.ts +4 -10
  334. package/src/core/islands/worker/worker.tsx +33 -33
  335. package/src/core/kernel/__tests__/RuntimeState.test.ts +1 -1
  336. package/src/core/network/__tests__/connection.test.ts +1 -1
  337. package/src/core/network/connection-notice.ts +1 -1
  338. package/src/core/network/connection.ts +5 -3
  339. package/src/core/network/requests-lazy.ts +1 -2
  340. package/src/core/network/requests-network.ts +0 -9
  341. package/src/core/network/requests-static.ts +0 -1
  342. package/src/core/network/requests-toasting.tsx +0 -1
  343. package/src/core/network/types.ts +0 -5
  344. package/src/core/network/useConnectionNotice.ts +8 -2
  345. package/src/core/notebook/attachments.ts +38 -0
  346. package/src/core/notebook/document/__tests__/codemirror.test.ts +547 -0
  347. package/src/core/notebook/document/__tests__/draft.test.ts +322 -0
  348. package/src/core/notebook/document/__tests__/fixtures.ts +92 -0
  349. package/src/core/notebook/document/__tests__/hold.test.ts +88 -0
  350. package/src/core/notebook/document/__tests__/hooks.test.tsx +391 -0
  351. package/src/core/notebook/document/__tests__/lifecycle-scenarios.test.ts +61 -0
  352. package/src/core/notebook/document/__tests__/model.test.ts +247 -0
  353. package/src/core/notebook/document/__tests__/protocol-fixtures.ts +38 -0
  354. package/src/core/notebook/document/__tests__/recovery-scenarios.test.ts +230 -0
  355. package/src/core/notebook/document/__tests__/scenario-fixture.ts +43 -0
  356. package/src/core/notebook/document/__tests__/scenarios.test.ts +205 -0
  357. package/src/core/notebook/document/__tests__/state.test.ts +438 -0
  358. package/src/core/notebook/document/__tests__/submission.test.ts +203 -0
  359. package/src/core/notebook/document/__tests__/test-host.test.ts +92 -0
  360. package/src/core/notebook/document/__tests__/test-host.ts +247 -0
  361. package/src/core/notebook/document/__tests__/test-notebook.ts +149 -0
  362. package/src/core/notebook/document/__tests__/user.ts +61 -0
  363. package/src/core/notebook/document/codemirror.ts +216 -0
  364. package/src/core/notebook/document/draft.ts +472 -0
  365. package/src/core/notebook/document/hooks.tsx +118 -0
  366. package/src/core/notebook/document/index.ts +23 -0
  367. package/src/core/notebook/document/model.ts +105 -0
  368. package/src/core/notebook/document/select.ts +41 -0
  369. package/src/core/notebook/document/state.ts +374 -0
  370. package/src/core/notebook/document/sync.ts +418 -0
  371. package/src/core/notebook/index.ts +18 -0
  372. package/src/core/notebook/replica/AGENTS.md +11 -0
  373. package/src/core/notebook/replica/__tests__/attachments.test.ts +56 -0
  374. package/src/core/notebook/replica/__tests__/document.test.ts +126 -0
  375. package/src/core/notebook/replica/__tests__/fixtures.ts +79 -0
  376. package/src/core/notebook/replica/__tests__/replica.test.ts +550 -0
  377. package/src/core/notebook/replica/backend.ts +19 -0
  378. package/src/core/notebook/replica/connection.ts +42 -0
  379. package/src/core/notebook/replica/index.ts +13 -0
  380. package/src/core/notebook/replica/replica.ts +286 -0
  381. package/src/core/notebook/replica/resources/attachments.ts +22 -0
  382. package/src/core/notebook/replica/resources/document.ts +101 -0
  383. package/src/core/packages/__tests__/useSandboxController.test.tsx +1 -1
  384. package/src/core/packages/useSandboxController.ts +1 -1
  385. package/src/core/run-app.tsx +47 -44
  386. package/src/core/runtime/__tests__/adapter.test.ts +1 -1
  387. package/src/core/runtime/__tests__/createWsUrl.test.ts +77 -7
  388. package/src/core/runtime/__tests__/runtime.test.ts +32 -58
  389. package/src/core/runtime/adapter.ts +1 -1
  390. package/src/core/runtime/config.ts +2 -2
  391. package/src/core/runtime/runtime.ts +38 -42
  392. package/src/core/runtime/types.ts +0 -2
  393. package/src/core/saving/filename.ts +1 -1
  394. package/src/core/saving/save-component.tsx +10 -4
  395. package/src/core/saving/useAutoSave.ts +1 -1
  396. package/src/core/session/__tests__/backend.test.ts +235 -0
  397. package/src/core/session/__tests__/useNotebookReplica.hook.test.tsx +183 -0
  398. package/src/core/session/__tests__/useNotebookSession.hook.test.tsx +669 -0
  399. package/src/core/session/active-notebook.ts +11 -0
  400. package/src/core/session/backend.ts +125 -0
  401. package/src/core/{websocket → session}/types.ts +2 -2
  402. package/src/core/session/useNotebookReplica.ts +72 -0
  403. package/src/core/{websocket → session}/useNotebookSession.tsx +56 -43
  404. package/src/core/state/__tests__/jotai.test.tsx +108 -0
  405. package/src/core/state/jotai.ts +1 -1
  406. package/src/core/static/__tests__/files.test.ts +38 -10
  407. package/src/core/static/files.ts +2 -0
  408. package/src/core/{websocket → transports}/__tests__/connection-state.test.ts +1 -13
  409. package/src/core/transports/__tests__/server.test.ts +408 -0
  410. package/src/core/transports/__tests__/static.test.ts +175 -0
  411. package/src/core/transports/__tests__/wasm.test.ts +166 -0
  412. package/src/core/{websocket → transports}/connection-state.ts +9 -16
  413. package/src/core/transports/server.ts +152 -0
  414. package/src/core/transports/static.ts +84 -0
  415. package/src/core/transports/wasm.ts +97 -0
  416. package/src/core/wasm/__tests__/bridge.test.ts +213 -50
  417. package/src/core/wasm/__tests__/worker.test.ts +187 -0
  418. package/src/core/wasm/bridge.ts +75 -53
  419. package/src/core/wasm/rpc.ts +15 -3
  420. package/src/core/wasm/store.ts +3 -3
  421. package/src/core/wasm/worker/bootstrap.ts +55 -51
  422. package/src/core/wasm/worker/getMarimoWheel.ts +1 -1
  423. package/src/core/wasm/worker/save-worker.ts +9 -10
  424. package/src/core/wasm/worker/types.ts +29 -18
  425. package/src/core/wasm/worker/worker.ts +88 -44
  426. package/src/css/globals.css +10 -0
  427. package/src/hooks/__tests__/useAsyncData.test.tsx +148 -0
  428. package/src/hooks/__tests__/useDebounce.test.tsx +51 -0
  429. package/src/hooks/__tests__/useDeepCompareMemoize.test.tsx +39 -0
  430. package/src/hooks/__tests__/useDelayElapsed.test.tsx +28 -0
  431. package/src/hooks/__tests__/useInternalStateWithSync.test.tsx +20 -0
  432. package/src/hooks/__tests__/useScript.test.tsx +77 -0
  433. package/src/hooks/__tests__/useStateForKey.test.tsx +257 -0
  434. package/src/hooks/useAsyncData.ts +3 -3
  435. package/src/hooks/useDataSourceDiscovery.ts +6 -4
  436. package/src/hooks/useDebounce.ts +6 -10
  437. package/src/hooks/useDeepCompareMemoize.ts +1 -1
  438. package/src/hooks/useDelayElapsed.ts +14 -10
  439. package/src/hooks/useElapsedTime.ts +3 -3
  440. package/src/hooks/useEventListener.ts +1 -2
  441. package/src/hooks/useInputHistory.ts +4 -7
  442. package/src/hooks/useInternalStateWithSync.ts +6 -6
  443. package/src/hooks/useResizeObserver.ts +4 -7
  444. package/src/hooks/useScript.ts +67 -70
  445. package/src/hooks/useStateForKey.ts +93 -0
  446. package/src/mount.tsx +1 -5
  447. package/src/plugins/__tests__/plugin-object-schemas.test.ts +104 -0
  448. package/src/plugins/core/registerReactComponent.tsx +1 -1
  449. package/src/plugins/impl/CodeEditorPlugin.tsx +1 -1
  450. package/src/plugins/impl/DataEditorPlugin.tsx +1 -1
  451. package/src/plugins/impl/DataTablePlugin.tsx +69 -37
  452. package/src/plugins/impl/DropdownPlugin.tsx +2 -6
  453. package/src/plugins/impl/FileBrowserPlugin.tsx +4 -16
  454. package/src/plugins/impl/FormPlugin.tsx +1 -1
  455. package/src/plugins/impl/RangeSliderPlugin.tsx +16 -2
  456. package/src/plugins/impl/RefreshPlugin.tsx +1 -0
  457. package/src/plugins/impl/SliderPlugin.tsx +6 -8
  458. package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +387 -1
  459. package/src/plugins/impl/__tests__/FileBrowserPlugin.test.tsx +45 -0
  460. package/src/plugins/impl/__tests__/RangeSliderPlugin.test.tsx +109 -0
  461. package/src/plugins/impl/__tests__/SliderPlugin.test.tsx +24 -3
  462. package/src/plugins/impl/anywidget/__tests__/registry.test.ts +153 -0
  463. package/src/plugins/impl/anywidget/__tests__/widget-binding.test.ts +241 -2
  464. package/src/plugins/impl/anywidget/model.ts +3 -1
  465. package/src/plugins/impl/anywidget/registry.ts +18 -14
  466. package/src/plugins/impl/anywidget/runtime.ts +21 -9
  467. package/src/plugins/impl/anywidget/widget-binding.ts +160 -82
  468. package/src/plugins/impl/chat/__tests__/chat-prompts.test.tsx +93 -0
  469. package/src/plugins/impl/chat/chat-ui.tsx +14 -11
  470. package/src/plugins/impl/code/any-language-editor.tsx +2 -2
  471. package/src/plugins/impl/data-editor/glide-data-editor.tsx +8 -5
  472. package/src/plugins/impl/data-editor/glide-utils.ts +3 -1
  473. package/src/plugins/impl/data-explorer/ConnectedDataExplorerComponent.tsx +1 -1
  474. package/src/plugins/impl/data-explorer/components/column-summary.tsx +4 -2
  475. package/src/plugins/impl/data-explorer/state/reducer.ts +1 -1
  476. package/src/plugins/impl/data-frames/DataFramePlugin.tsx +1 -2
  477. package/src/plugins/impl/data-frames/__tests__/sidebar.test.tsx +160 -0
  478. package/src/plugins/impl/data-frames/forms/__tests__/__snapshots__/form.test.tsx.snap +66 -66
  479. package/src/plugins/impl/data-frames/panel.tsx +43 -16
  480. package/src/plugins/impl/mpl-interactive/MplInteractivePlugin.tsx +5 -3
  481. package/src/plugins/impl/panel/PanelPlugin.tsx +16 -17
  482. package/src/plugins/impl/panel/__tests__/utils.test.ts +39 -1
  483. package/src/plugins/impl/panel/utils.ts +17 -0
  484. package/src/plugins/impl/plotly/PlotlyPlugin.tsx +2 -5
  485. package/src/plugins/impl/plotly/selection.ts +25 -19
  486. package/src/plugins/impl/vega/VegaPlugin.tsx +2 -3
  487. package/src/plugins/impl/vega/make-selectable.ts +4 -7
  488. package/src/plugins/layout/RoutesPlugin.tsx +1 -1
  489. package/src/stories/__tests__/editor-story.test.tsx +33 -0
  490. package/src/stories/button.stories.tsx +1 -0
  491. package/src/stories/cell.stories.tsx +1 -1
  492. package/src/stories/data-table.stories.tsx +31 -0
  493. package/src/stories/editor.stories.tsx +24 -17
  494. package/src/stories/form-wrapper.stories.tsx +2 -2
  495. package/src/stories/layout/vertical/one-column.stories.tsx +1 -1
  496. package/src/stories/runtime-story.tsx +1 -1
  497. package/src/stories/select.stories.tsx +2 -2
  498. package/src/stories/switchable-multi-select.stories.tsx +1 -1
  499. package/src/utils/__tests__/arrays.test.ts +35 -0
  500. package/src/utils/__tests__/batch-requests.test.ts +3 -3
  501. package/src/utils/__tests__/{createReducer.test.ts → createReducer.test.tsx} +29 -0
  502. package/src/utils/__tests__/numbers.test.ts +105 -1
  503. package/src/utils/__tests__/objects.test.ts +21 -0
  504. package/src/utils/arrays.ts +11 -2
  505. package/src/utils/blob.ts +1 -1
  506. package/src/utils/createReducer.ts +53 -26
  507. package/src/utils/maps.ts +0 -3
  508. package/src/utils/numbers.ts +79 -18
  509. package/src/utils/objects.ts +4 -9
  510. package/src/utils/once.ts +19 -19
  511. package/src/utils/tracer.ts +9 -5
  512. package/src/utils/zod-utils.ts +1 -3
  513. package/dist/any-language-editor-DZ_YTVus.js +0 -76
  514. package/dist/assets/__vite-browser-external-BXYkiFsk.js +0 -1
  515. package/dist/assets/worker-HPY9fbhz.js +0 -114
  516. package/dist/mermaid-HWGCJPDP-ByCsZnYF.js +0 -2
  517. package/dist/precisionRound-DMIVQrt2.js +0 -97
  518. package/src/core/attachments/state.ts +0 -21
  519. package/src/core/cells/__tests__/apply-transaction.test.ts +0 -991
  520. package/src/core/cells/__tests__/document-changes.test.ts +0 -1140
  521. package/src/core/cells/__tests__/document-roundtrip.test.ts +0 -392
  522. package/src/core/cells/document-changes.ts +0 -944
  523. package/src/core/host/AGENTS.md +0 -6
  524. package/src/core/host/__tests__/events.test.ts +0 -46
  525. package/src/core/host/__tests__/notebook-client.test.ts +0 -191
  526. package/src/core/host/__tests__/protocol.test.ts +0 -214
  527. package/src/core/host/connection.ts +0 -81
  528. package/src/core/host/events.ts +0 -60
  529. package/src/core/host/notebook-client.ts +0 -226
  530. package/src/core/host/protocol.ts +0 -1943
  531. package/src/core/state/__tests__/jotai.test.ts +0 -25
  532. package/src/core/wasm/worker/message-buffer.ts +0 -33
  533. package/src/core/websocket/__tests__/createConnectionTransport.test.ts +0 -49
  534. package/src/core/websocket/__tests__/useNotebookClient.hook.test.tsx +0 -140
  535. package/src/core/websocket/__tests__/useNotebookSession.hook.test.tsx +0 -883
  536. package/src/core/websocket/transports/__tests__/basic.test.ts +0 -275
  537. package/src/core/websocket/transports/__tests__/sse.test.ts +0 -335
  538. package/src/core/websocket/transports/__tests__/transport.test.ts +0 -308
  539. package/src/core/websocket/transports/__tests__/ws.test.ts +0 -125
  540. package/src/core/websocket/transports/basic.ts +0 -102
  541. package/src/core/websocket/transports/create.ts +0 -31
  542. package/src/core/websocket/transports/sse.ts +0 -308
  543. package/src/core/websocket/transports/transport.ts +0 -50
  544. package/src/core/websocket/transports/ws.ts +0 -98
  545. package/src/core/websocket/useNotebookClient.ts +0 -80
  546. /package/src/{core/attachments/toast.ts → components/editor/agent-toast.ts} +0 -0
  547. /package/src/core/{websocket → session}/connection-utils.ts +0 -0
@@ -0,0 +1,125 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ /** The backend the editor follows, chosen once for its mode. */
4
+
5
+ import { NotebookClient, StreamStopped } from "@marimo-team/client";
6
+ import type { NotebookId } from "@marimo-team/client/protocol";
7
+ import type { NotebookBackend } from "../notebook/replica";
8
+ import { PyodideBridge } from "../wasm/bridge";
9
+ import { isWasm } from "../wasm/utils";
10
+ import { sessionSse, websocket, withHeaders } from "../transports/server";
11
+ import { idle } from "../transports/static";
12
+
13
+ /** How the editor follows its notebook. */
14
+ export interface EditorBackendOptions {
15
+ static: boolean;
16
+ transportType: "websocket" | "sse";
17
+ /** The session route, read before each connection. */
18
+ url: () => string;
19
+ /** The runtime's headers, read before each request. */
20
+ headers: () => Record<string, string>;
21
+ /** The host URL of a notebook the stream reported. */
22
+ notebookUrl: (notebookId: NotebookId) => URL;
23
+ /** Wait until the host can accept the first connection. */
24
+ waitUntilReady: () => Promise<void>;
25
+ /** Whether the host is reachable before an explicit reconnect. */
26
+ checkHealth: () => Promise<boolean>;
27
+ }
28
+
29
+ /**
30
+ * Read the session route once the host has started.
31
+ *
32
+ * Every connection waits for the same startup. Once it settles, even by
33
+ * giving up, connections read the route directly. An explicit reconnect
34
+ * checks the host's health instead.
35
+ */
36
+ function afterStartup({
37
+ url,
38
+ waitUntilReady,
39
+ }: Pick<EditorBackendOptions, "url" | "waitUntilReady">) {
40
+ let startup: Promise<void> | undefined;
41
+ let settled = false;
42
+ return async () => {
43
+ if (!settled) {
44
+ startup ??= waitUntilReady().finally(() => {
45
+ settled = true;
46
+ });
47
+ try {
48
+ await startup;
49
+ } catch (error) {
50
+ throw new StreamStopped("Healthy connection never made", {
51
+ cause: error,
52
+ });
53
+ }
54
+ }
55
+ return url();
56
+ };
57
+ }
58
+
59
+ /**
60
+ * Follow the notebook running in the WASM worker.
61
+ *
62
+ * The worker's backend exists once the worker has started, so the first
63
+ * connection creates it. Commands are only for a notebook its stream reported.
64
+ */
65
+ function wasm(): NotebookBackend {
66
+ let backend: Promise<NotebookBackend> | undefined;
67
+ let started: NotebookBackend | undefined;
68
+ return {
69
+ transport: async (attempt, handlers) => {
70
+ backend ??= PyodideBridge.INSTANCE.createNotebookBackend();
71
+ try {
72
+ started = await backend;
73
+ } catch (error) {
74
+ throw new StreamStopped("The WASM notebook did not start", {
75
+ cause: error,
76
+ });
77
+ }
78
+ return started.transport(attempt, handlers);
79
+ },
80
+ notebook: (id) => {
81
+ if (started === undefined) {
82
+ throw new Error("The WASM notebook has not started");
83
+ }
84
+ return started.notebook(id);
85
+ },
86
+ };
87
+ }
88
+
89
+ /** Commands for a notebook the editor cannot change through its backend. */
90
+ function uneditable(): ReturnType<NotebookBackend["notebook"]> {
91
+ const unsupported: NotebookClient["save"] = async () => ({
92
+ ok: false,
93
+ status: 501,
94
+ problem: {
95
+ type: "tag:marimo.io,2026:host/unsupported-operation",
96
+ title: "Document mutations are unavailable on this connection",
97
+ status: 501,
98
+ },
99
+ });
100
+ return { edit: () => unsupported(), save: unsupported };
101
+ }
102
+
103
+ /** Choose the backend for the editor's notebook. */
104
+ export function createEditorBackend(
105
+ options: EditorBackendOptions,
106
+ ): NotebookBackend {
107
+ if (options.static) {
108
+ return { transport: idle, notebook: uneditable };
109
+ }
110
+ if (isWasm()) {
111
+ return wasm();
112
+ }
113
+ const url = afterStartup(options);
114
+ return {
115
+ transport:
116
+ options.transportType === "sse"
117
+ ? sessionSse(url, options.headers)
118
+ : websocket(url),
119
+ notebook: (id) =>
120
+ new NotebookClient(options.notebookUrl(id), {
121
+ fetch: withHeaders(options.headers),
122
+ }),
123
+ checkHealth: options.checkHealth,
124
+ };
125
+ }
@@ -1,12 +1,12 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
- import type { ConnectionStatus as HostConnectionStatus } from "../host/connection";
3
+ import type { ConnectionStatus as HostConnectionStatus } from "../notebook/replica";
4
4
  import type { NotificationMessageData } from "../kernel/messages";
5
5
 
6
6
  export {
7
7
  ConnectionState,
8
8
  ConnectionClosedReason as WebSocketClosedReason,
9
- } from "../host/connection";
9
+ } from "../notebook/replica";
10
10
 
11
11
  export type ConnectionPhase =
12
12
  | NotificationMessageData<"startup-progress">["phase"]
@@ -0,0 +1,72 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ /** The editor session's replica, following its backend while mounted. */
4
+
5
+ import { useStore } from "jotai";
6
+ import { useEffect, useMemo } from "react";
7
+ import useEvent from "react-use-event-hook";
8
+ import type {
9
+ NotificationMessage,
10
+ NotificationPayload,
11
+ } from "../kernel/messages";
12
+ import {
13
+ createNotebookReplica,
14
+ type NotebookReplica,
15
+ } from "../notebook/replica";
16
+ import { activeNotebookReplicaAtom } from "./active-notebook";
17
+ import { createEditorBackend, type EditorBackendOptions } from "./backend";
18
+
19
+ type Options = EditorBackendOptions & {
20
+ onNotification: (notification: NotificationPayload) => void;
21
+ };
22
+
23
+ export function useNotebookReplica(options: Options): NotebookReplica {
24
+ const store = useStore();
25
+ const { static: isStatic, transportType } = options;
26
+ const replica = useMemo(() => createNotebookReplica(store), [store]);
27
+ const url = useEvent(options.url);
28
+ const headers = useEvent(options.headers);
29
+ const notebookUrl = useEvent(options.notebookUrl);
30
+ const waitUntilReady = useEvent(options.waitUntilReady);
31
+ const checkHealth = useEvent(options.checkHealth);
32
+ const onNotification = useEvent(options.onNotification);
33
+ // Opens nothing until the replica follows it.
34
+ const backend = useMemo(
35
+ () =>
36
+ createEditorBackend({
37
+ static: isStatic,
38
+ transportType,
39
+ url,
40
+ headers,
41
+ notebookUrl,
42
+ waitUntilReady,
43
+ checkHealth,
44
+ }),
45
+ [
46
+ isStatic,
47
+ transportType,
48
+ url,
49
+ headers,
50
+ notebookUrl,
51
+ waitUntilReady,
52
+ checkHealth,
53
+ ],
54
+ );
55
+
56
+ useEffect(() => {
57
+ store.set(activeNotebookReplicaAtom, replica);
58
+ const disconnect = replica.connect(backend, {
59
+ // The host validates the envelope, not the native notification contents.
60
+ onNotification: (notification) =>
61
+ onNotification({ data: notification as NotificationMessage }),
62
+ });
63
+ return () => {
64
+ disconnect();
65
+ if (store.get(activeNotebookReplicaAtom) === replica) {
66
+ store.set(activeNotebookReplicaAtom, null);
67
+ }
68
+ };
69
+ }, [replica, backend, store, onNotification]);
70
+
71
+ return replica;
72
+ }
@@ -2,18 +2,14 @@
2
2
 
3
3
  import { invalidatePackageData } from "@/core/packages/package-data";
4
4
  import { useAtom, useAtomValue, useSetAtom, useStore } from "jotai";
5
- import { useLayoutEffect } from "react";
5
+ import { useEffect, useLayoutEffect, useMemo } from "react";
6
6
  import { useErrorBoundary } from "react-error-boundary";
7
7
  import { toast } from "@/components/ui/use-toast";
8
- import { attachmentsAtom } from "@/core/attachments/state";
9
8
  import { getNotebook, useCellActions } from "@/core/cells/cells";
10
- import { applyTransactionChanges } from "@/core/cells/document-changes";
11
9
  import { AUTOCOMPLETER } from "@/core/codemirror/completion/Autocompleter";
12
- import type {
13
- NotificationMessageData,
14
- NotificationPayload,
15
- } from "@/core/kernel/messages";
16
- import { useNotebookClient } from "@/core/websocket/useNotebookClient";
10
+ import { createDocumentModel } from "@/core/notebook/document";
11
+ import type { NotificationPayload } from "@/core/kernel/messages";
12
+ import { useNotebookReplica } from "@/core/session/useNotebookReplica";
17
13
  import { renderHTML } from "@/plugins/core/RenderHTML";
18
14
  import {
19
15
  handleWidgetMessage,
@@ -66,7 +62,7 @@ import { type LayoutState, useLayoutActions } from "../layout/state";
66
62
  import { kioskModeAtom } from "../mode";
67
63
  import { connectionAtom, startupProgressAtom } from "../network/connection";
68
64
  import type { RequestId } from "../network/DeferredRequestRegistry";
69
- import { runtimeConfigAtom, useRuntimeManager } from "../runtime/config";
65
+ import { useRuntimeManager } from "../runtime/config";
70
66
  import { SECRETS_REGISTRY } from "../secrets/request-registry";
71
67
  import { isStaticNotebook } from "../static/static-state";
72
68
  import {
@@ -76,7 +72,7 @@ import {
76
72
  import { useStorageActions } from "../storage/state";
77
73
  import { useVariablesActions } from "../variables/state";
78
74
  import type { VariableName } from "../variables/types";
79
- import { isWasm } from "../wasm/utils";
75
+ import { activeDocumentModelAtom } from "./active-notebook";
80
76
  import { WebSocketClosedReason, ConnectionState } from "./types";
81
77
 
82
78
  const SUPPORTS_LAZY_KERNELS = true;
@@ -102,17 +98,7 @@ export function useNotebookSession(opts: {
102
98
  const { showBoundary } = useErrorBoundary();
103
99
 
104
100
  const { handleCellMessage } = useCellActions();
105
- const actionsWithoutMiddleware = useCellActions({ skipMiddleware: true });
106
-
107
- const handleDocumentTransaction = (
108
- transaction: NotificationMessageData<"notebook-document-transaction">["transaction"],
109
- ) => {
110
- applyTransactionChanges(
111
- transaction.changes,
112
- actionsWithoutMiddleware,
113
- () => getNotebook().cellIds.inOrderIds,
114
- );
115
- };
101
+ const { reconcileDocument } = useCellActions({ skipMiddleware: true });
116
102
  const { addCellNotification } = useRunsActions();
117
103
  const setKernelState = useSetAtom(kernelStateAtom);
118
104
  const setInitialRunCompleted = useSetAtom(initialRunCompletedAtom);
@@ -136,11 +122,9 @@ export function useNotebookSession(opts: {
136
122
  const setStableSessionId = useSetAtom(stableSessionIdAtom);
137
123
  const setCapabilities = useSetAtom(capabilitiesAtom);
138
124
  const runtimeManager = useRuntimeManager();
139
- const { notebookId } = useAtomValue(runtimeConfigAtom);
140
125
  const transportType = useAtomValue(connectionTransportTypeAtom);
141
126
  const setCacheInfo = useSetAtom(cacheInfoAtom);
142
127
  const setKernelStartupError = useSetAtom(kernelStartupErrorAtom);
143
- const setAttachments = useSetAtom(attachmentsAtom);
144
128
  const {
145
129
  setNamespaces: setStorageNamespaces,
146
130
  filterFromVariables: filterStorageFromVariables,
@@ -148,9 +132,6 @@ export function useNotebookSession(opts: {
148
132
 
149
133
  const handleNotification = (msg: NotificationPayload) => {
150
134
  switch (msg.data.op) {
151
- case "attachments":
152
- setAttachments(msg.data.attachments);
153
- return;
154
135
  case "reload":
155
136
  reloadSafe();
156
137
  return;
@@ -185,7 +166,6 @@ export function useNotebookSession(opts: {
185
166
  existingCells,
186
167
  });
187
168
  if (!msg.data.resumed) {
188
- setAttachments([]);
189
169
  // A freshly started kernel may expose a different environment
190
170
  // (e.g. new env vars); re-run discovery instead of reusing stale
191
171
  // suggestions from the previous kernel instance.
@@ -271,13 +251,18 @@ export function useNotebookSession(opts: {
271
251
  filterStorageFromVariables(variableNames);
272
252
  return;
273
253
  case "variable-values":
274
- setMetadata(
275
- msg.data.variables.map((v) => ({
254
+ setMetadata([
255
+ ...(msg.data.removed ?? []).map((v) => ({
256
+ name: v.name as VariableName,
257
+ dataType: undefined,
258
+ value: undefined,
259
+ })),
260
+ ...msg.data.variables.map((v) => ({
276
261
  name: v.name as VariableName,
277
262
  dataType: v.datatype,
278
263
  value: v.value,
279
264
  })),
280
- );
265
+ ]);
281
266
  return;
282
267
  case "alert":
283
268
  toast({
@@ -398,7 +383,7 @@ export function useNotebookSession(opts: {
398
383
  );
399
384
  return;
400
385
  case "notebook-document-transaction":
401
- handleDocumentTransaction(msg.data.transaction);
386
+ // Edits reach the notebook through the host document.
402
387
  return;
403
388
  case "consumer-capabilities":
404
389
  setKioskMode(!msg.data.consumer_capabilities.edit);
@@ -416,25 +401,23 @@ export function useNotebookSession(opts: {
416
401
  variant: "danger",
417
402
  });
418
403
  };
419
- const notebook = useNotebookClient({
420
- sessionId,
404
+ const replica = useNotebookReplica({
421
405
  static: isStaticNotebook(),
422
406
  transportType,
423
- messageFormat:
424
- notebookId && transportType === "websocket" ? "host" : "session",
425
407
  url: () =>
426
408
  transportType === "sse"
427
409
  ? runtimeManager.getSseURL(sessionId).toString()
428
410
  : runtimeManager.getWsURL(sessionId).toString(),
429
411
  // SSE uses headers for auth. WebSockets carry the token in the URL.
430
412
  headers: () => runtimeManager.headers(),
413
+ notebookUrl: (notebookId) => runtimeManager.getNotebookURL(notebookId),
414
+ // Only the host's transports read the URL, so static and WASM never wait.
431
415
  waitUntilReady: async () => {
432
- if (!isStaticNotebook() && !isWasm() && !runtimeManager.isSameOrigin) {
416
+ if (!runtimeManager.isSameOrigin) {
433
417
  await runtimeManager.waitForHealthy();
434
418
  }
435
419
  },
436
420
  checkHealth: () => runtimeManager.reconcileFromHealth(),
437
- onMessageError,
438
421
  onNotification: (notification) => {
439
422
  try {
440
423
  handleNotification(notification);
@@ -445,14 +428,45 @@ export function useNotebookSession(opts: {
445
428
  },
446
429
  });
447
430
 
431
+ const documentModel = useMemo(
432
+ () =>
433
+ createDocumentModel({
434
+ store,
435
+ documentAtom: replica.documentAtom,
436
+ backendAtom: replica.backendAtom,
437
+ }),
438
+ [store, replica],
439
+ );
440
+ // Edits outlive reconnects and end with the session. The notebook's cells
441
+ // follow the document for as long as the session is mounted.
442
+ useEffect(() => {
443
+ const controller = new AbortController();
444
+ documentModel.start({ signal: controller.signal });
445
+ store.set(activeDocumentModelAtom, documentModel);
446
+ const follow = () => {
447
+ const document = store.get(documentModel.documentAtom);
448
+ if (document !== null) {
449
+ reconcileDocument(document);
450
+ }
451
+ };
452
+ const unsubscribe = store.sub(documentModel.documentAtom, follow);
453
+ follow();
454
+ return () => {
455
+ unsubscribe();
456
+ controller.abort();
457
+ if (store.get(activeDocumentModelAtom) === documentModel) {
458
+ store.set(activeDocumentModelAtom, null);
459
+ }
460
+ };
461
+ }, [documentModel, store, reconcileDocument]);
462
+
448
463
  // Subscribe before passive connection setup: static/WASM transports can emit synchronously.
449
464
  useLayoutEffect(() => {
450
465
  const updateConnection = () => {
451
- const status = store.get(notebook.connectionAtom);
466
+ const status = store.get(replica.connectionAtom);
452
467
  if (status.state === ConnectionState.NOT_STARTED) {
453
468
  return;
454
469
  }
455
- setAttachments([]);
456
470
  if (status.state === ConnectionState.OPEN) {
457
471
  updateStartupProgress(null);
458
472
  return; // kernel-ready, not the socket, establishes session readiness.
@@ -480,17 +494,16 @@ export function useNotebookSession(opts: {
480
494
  return status;
481
495
  });
482
496
  };
483
- const unsubscribe = store.sub(notebook.connectionAtom, updateConnection);
497
+ const unsubscribe = store.sub(replica.connectionAtom, updateConnection);
484
498
  updateConnection();
485
499
  return unsubscribe;
486
500
  }, [
487
- notebook,
501
+ replica,
488
502
  store,
489
503
  setConnection,
490
- setAttachments,
491
504
  setKernelStartupError,
492
505
  updateStartupProgress,
493
506
  ]);
494
507
 
495
- return { notebook, connection, reconnect: notebook.reconnect };
508
+ return { replica, documentModel, connection, reconnect: replica.reconnect };
496
509
  }
@@ -0,0 +1,108 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { act, renderHook } from "@testing-library/react";
4
+ import { atom, Provider } from "jotai";
5
+ import type React from "react";
6
+ import { describe, expect, it, vi } from "vitest";
7
+ import { store, useJotaiEffect, waitFor } from "../jotai";
8
+
9
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
10
+ <Provider store={store}>{children}</Provider>
11
+ );
12
+
13
+ describe("waitFor function", () => {
14
+ it("should resolve when the atom satisfies the predicate", async () => {
15
+ const testAtom = atom(0);
16
+ store.set(testAtom, 10);
17
+
18
+ const result = await waitFor(testAtom, (value) => value === 10);
19
+ expect(result).toBe(10);
20
+ });
21
+
22
+ it("should resolve when the atom changes to satisfy the predicate", async () => {
23
+ const testAtom = atom(0);
24
+
25
+ setTimeout(() => store.set(testAtom, 15), 5);
26
+ setTimeout(() => store.set(testAtom, 20), 10);
27
+
28
+ const result = await waitFor(testAtom, (value) => value === 20);
29
+ expect(result).toBe(20);
30
+ });
31
+ });
32
+
33
+ describe("useJotaiEffect", () => {
34
+ it("calls the effect with the new and previous values", () => {
35
+ const testAtom = atom(0);
36
+ const effect = vi.fn();
37
+ renderHook(() => useJotaiEffect(testAtom, effect), { wrapper });
38
+
39
+ act(() => store.set(testAtom, 1));
40
+ act(() => store.set(testAtom, 2));
41
+
42
+ expect(effect.mock.calls).toEqual([
43
+ [1, 0],
44
+ [2, 1],
45
+ ]);
46
+ });
47
+
48
+ it("calls only the latest callback after a rerender", () => {
49
+ const testAtom = atom(0);
50
+ const first = vi.fn();
51
+ const second = vi.fn();
52
+ const { rerender } = renderHook(
53
+ ({ effect }) => useJotaiEffect(testAtom, effect),
54
+ { initialProps: { effect: first }, wrapper },
55
+ );
56
+
57
+ rerender({ effect: second });
58
+ act(() => store.set(testAtom, 1));
59
+
60
+ expect(first).not.toHaveBeenCalled();
61
+ expect(second).toHaveBeenCalledTimes(1);
62
+ });
63
+
64
+ it("subscribes once per rerender with an inline callback", () => {
65
+ const testAtom = atom(0);
66
+ const calls: number[] = [];
67
+ const { rerender } = renderHook(
68
+ () => useJotaiEffect(testAtom, (value) => calls.push(value)),
69
+ { wrapper },
70
+ );
71
+
72
+ rerender();
73
+ rerender();
74
+ act(() => store.set(testAtom, 1));
75
+
76
+ expect(calls).toEqual([1]);
77
+ });
78
+
79
+ it("stops listening to the previous atom when the atom changes", () => {
80
+ const firstAtom = atom(0);
81
+ const secondAtom = atom(0);
82
+ const effect = vi.fn();
83
+ const { rerender } = renderHook(
84
+ ({ target }) => useJotaiEffect(target, effect),
85
+ { initialProps: { target: firstAtom }, wrapper },
86
+ );
87
+
88
+ rerender({ target: secondAtom });
89
+ act(() => store.set(firstAtom, 1));
90
+ expect(effect).not.toHaveBeenCalled();
91
+
92
+ act(() => store.set(secondAtom, 1));
93
+ expect(effect).toHaveBeenCalledTimes(1);
94
+ });
95
+
96
+ it("unsubscribes on unmount", () => {
97
+ const testAtom = atom(0);
98
+ const effect = vi.fn();
99
+ const { unmount } = renderHook(() => useJotaiEffect(testAtom, effect), {
100
+ wrapper,
101
+ });
102
+
103
+ unmount();
104
+ act(() => store.set(testAtom, 1));
105
+
106
+ expect(effect).not.toHaveBeenCalled();
107
+ });
108
+ });
@@ -42,7 +42,7 @@ export function useJotaiEffect<T>(
42
42
  const store = useStore();
43
43
  useEffect(() => {
44
44
  let prevValue = store.get(atom);
45
- store.sub(atom, () => {
45
+ return store.sub(atom, () => {
46
46
  const value = store.get(atom);
47
47
  effect(value, prevValue);
48
48
  prevValue = value;
@@ -654,19 +654,47 @@ describe("error handling and edge cases", () => {
654
654
  unpatch();
655
655
  });
656
656
 
657
- it("should handle malformed URLs gracefully", async () => {
658
- const virtualFiles = {};
659
- const unpatch = patchFetch(virtualFiles);
657
+ it("forwards relative URLs and options to the original fetch", async () => {
658
+ const response = new Response("relative content");
659
+ const originalFetch = vi.spyOn(window, "fetch").mockResolvedValue(response);
660
+ const unpatch = patchFetch({});
661
+ const options = { headers: { Accept: "text/plain" } };
662
+
663
+ try {
664
+ expect(await window.fetch("relative-file.txt", options)).toBe(response);
665
+ expect(originalFetch).toHaveBeenCalledExactlyOnceWith(
666
+ "relative-file.txt",
667
+ options,
668
+ );
669
+ } finally {
670
+ unpatch();
671
+ originalFetch.mockRestore();
672
+ }
673
+ });
660
674
 
661
- // Mock Logger.error to avoid test output
675
+ it("preserves the original fetch rejection for malformed URLs", async () => {
676
+ const error = new TypeError("Invalid URL");
677
+ const originalFetch = vi.spyOn(window, "fetch").mockRejectedValue(error);
662
678
  const loggerSpy = vi
663
679
  .spyOn(console, "error")
664
680
  .mockImplementation(Functions.NOOP);
665
-
666
- // This should catch the error and fallback to original fetch
667
- await window.fetch("not-a-valid-url").catch(() => undefined);
668
-
669
- unpatch();
670
- loggerSpy.mockRestore();
681
+ const unpatch = patchFetch({});
682
+ const options = { headers: { Accept: "text/plain" } };
683
+
684
+ try {
685
+ await expect(window.fetch("http://[", options)).rejects.toBe(error);
686
+ expect(originalFetch).toHaveBeenCalledExactlyOnceWith(
687
+ "http://[",
688
+ options,
689
+ );
690
+ expect(loggerSpy).toHaveBeenCalledWith(
691
+ "Error parsing URL",
692
+ expect.any(TypeError),
693
+ );
694
+ } finally {
695
+ unpatch();
696
+ originalFetch.mockRestore();
697
+ loggerSpy.mockRestore();
698
+ }
671
699
  });
672
700
  });
@@ -125,6 +125,8 @@ function withoutLeadingDot(path: string): string {
125
125
  /**
126
126
  * Resolve a URL to a blob URL if it's a virtual file, for use with dynamic import().
127
127
  * Unlike fetch, import() can't be patched, so we need to convert data URLs to blob URLs.
128
+ * The caller owns a returned URL that differs from the input and must revoke it
129
+ * once no module consumers or their cleanup callbacks need it.
128
130
  *
129
131
  * @returns The original URL if not a virtual file, or a blob URL if it is
130
132
  */
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { afterEach, describe, expect, it, vi } from "vitest";
4
4
  import { Logger } from "@/utils/Logger";
5
- import { WebSocketClosedReason, ConnectionState } from "../types";
5
+ import { WebSocketClosedReason, ConnectionState } from "../../session/types";
6
6
  import { classifyCloseEvent } from "../connection-state";
7
7
 
8
8
  function classify(reason: string | undefined) {
@@ -64,16 +64,4 @@ describe("classifyCloseEvent", () => {
64
64
  });
65
65
  });
66
66
  });
67
-
68
- describe("transport exhaustion", () => {
69
- it("MARIMO_TRANSPORT_EXHAUSTED → gave-up with KERNEL_DISCONNECTED", () => {
70
- const decision = classify("MARIMO_TRANSPORT_EXHAUSTED");
71
- expect(decision.kind).toBe("gave-up");
72
- expect(decision.status).toEqual({
73
- state: ConnectionState.CLOSED,
74
- code: WebSocketClosedReason.KERNEL_DISCONNECTED,
75
- reason: "kernel not found",
76
- });
77
- });
78
- });
79
67
  });