@marimo-team/frontend 0.25.3-dev10 → 0.25.3-dev100

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 (452) hide show
  1. package/dist/assets/CellStatus-fK4n2NPc.js +1 -0
  2. package/dist/assets/{ConnectedDataExplorerComponent-t8fH_TNc.js → ConnectedDataExplorerComponent-Dft8R6l5.js} +1 -1
  3. package/dist/assets/{ErrorBoundary-B4n9EnHB.js → ErrorBoundary-CAEGdoFW.js} +2 -2
  4. package/dist/assets/JsonOutput-CyAKmZDD.js +11 -0
  5. package/dist/assets/{LazyAnyLanguageCodeMirror-CKcCbG2G.js → LazyAnyLanguageCodeMirror-BPYE76jR.js} +2 -2
  6. package/dist/assets/MarimoErrorOutput-CgZvP0p0.js +10 -0
  7. package/dist/assets/{RenderHTML-9bMgbYmo.js → RenderHTML-DQvxDDNS.js} +1 -1
  8. package/dist/assets/RunButton-BBDnDL6G.js +1 -0
  9. package/dist/assets/__vite-browser-external-8vDMR0Kk.js +1 -0
  10. package/dist/assets/__vite-browser-external-mKcnhO20.js +1 -0
  11. package/dist/assets/{add-cell-with-ai-f_MnLCwI.js → add-cell-with-ai-Clg5FELx.js} +7 -7
  12. package/dist/assets/{add-connection-dialog-CoE_PhuD.js → add-connection-dialog-BqdWmD-7.js} +2 -2
  13. package/dist/assets/{agent-panel-BWZz24jK.js → agent-panel-5RxG5Y82.js} +3 -3
  14. package/dist/assets/ai-model-dropdown-Bvl1ktQq.js +13 -0
  15. package/dist/assets/{alert-dialog-BaPGzeOK.js → alert-dialog-C-sWmKva.js} +1 -1
  16. package/dist/assets/any-language-editor-DUMe1UCk.js +1 -0
  17. package/dist/assets/{app-config-button-BhU0ulYs.js → app-config-button-0QTeQ2Mv.js} +1 -1
  18. package/dist/assets/arrays-0R2reF2y.js +1 -0
  19. package/dist/assets/{blob-iOBwGaP9.js → blob-5VcB8K_t.js} +1 -1
  20. package/dist/assets/{button-COI-NGrx.js → button-D8gVKwe-.js} +1 -1
  21. package/dist/assets/{cache-panel-DTY1dqvi.js → cache-panel-BV-c7ed0.js} +1 -1
  22. package/dist/assets/{capabilities-BJvf-MD4.js → capabilities-Pq1YNPlK.js} +1 -1
  23. package/dist/assets/cell-editor-Byw5y-oI.js +42 -0
  24. package/dist/assets/cell-link-C5yYyzuF.js +1 -0
  25. package/dist/assets/{cells-rA_jsZCj.js → cells-D_11MjVB.js} +60 -60
  26. package/dist/assets/{chat-display-BmEPmhDP.js → chat-display-BCR_ySiV.js} +1 -1
  27. package/dist/assets/chat-panel-RPRvBsaU.js +4 -0
  28. package/dist/assets/chat-ui-CGviVaRA.js +4 -0
  29. package/dist/assets/{circle-DXf-VWZG.js → circle-B6D_6D8w.js} +1 -1
  30. package/dist/assets/code-visibility-DYDagVy7.js +1 -0
  31. package/dist/assets/coerce-DWjFeXOY.js +1 -0
  32. package/dist/assets/column-preview-xe533-Cn.js +1 -0
  33. package/dist/assets/command-palette-BB9bWdWp.js +1 -0
  34. package/dist/assets/{common-lisZ0l6a.js → common-CaeItbcI.js} +1 -1
  35. package/dist/assets/components-CBE8cOLK.js +1 -0
  36. package/dist/assets/{components-CP_UoFtT.js → components-DCo7mAWO.js} +1 -1
  37. package/dist/assets/{components-Dvd1GnD6.js → components-G6frbw0c.js} +1 -1
  38. package/dist/assets/config-CD1IuZFJ.js +1 -0
  39. package/dist/assets/{config-DJLAC21v.js → config-DMz7oN0r.js} +1 -1
  40. package/dist/assets/config-DpY86_Yq.js +1 -0
  41. package/dist/assets/{copy-D8O_CYCG.js → copy-KlhazlhB.js} +1 -1
  42. package/dist/assets/{copy-icon-BIiRXh_3.js → copy-icon-8-a5vovc.js} +1 -1
  43. package/dist/assets/{createReducer-DgRODTW6.js → createReducer-CaI_Oa0v.js} +1 -1
  44. package/dist/assets/{datasource-Dr1xNKUn.js → datasource-deuXnlU-.js} +1 -1
  45. package/dist/assets/{dates-CfmdTL6B.js → dates-CQMwFko2.js} +1 -1
  46. package/dist/assets/{dependency-graph-panel-B0uP-_id.js → dependency-graph-panel-CIykLnJe.js} +2 -2
  47. package/dist/assets/{dist-iaThVDkA.js → dist-jlhMTKo_.js} +1 -1
  48. package/dist/assets/{documentation-panel-DVl_ebWy.js → documentation-panel-2PQ8kQLa.js} +1 -1
  49. package/dist/assets/{download-Bx55LMki.js → download-dK52bLxZ.js} +1 -1
  50. package/dist/assets/{dropdown-menu-C1rnl-0o.js → dropdown-menu-DS6umAnH.js} +1 -1
  51. package/dist/assets/edit-page-DrElzw10.js +9 -0
  52. package/dist/assets/{error-banner-Cd69vvyX.js → error-banner-B5kzVf_0.js} +1 -1
  53. package/dist/assets/{error-entries-DOWpdGvI.js → error-entries-3GVk2KjS.js} +1 -1
  54. package/dist/assets/{error-panel-DpbNKSLs.js → error-panel-CzDRGPeC.js} +1 -1
  55. package/dist/assets/{errors-DCrsa1nH.js → errors-CNsgAw6m.js} +1 -1
  56. package/dist/assets/{es-Cqst59WX.js → es-N9SHpmG7.js} +1 -1
  57. package/dist/assets/{feedback-button-B99ThNJW.js → feedback-button-C5NgWpFg.js} +2 -2
  58. package/dist/assets/feedback-button-CONwLFpw.js +1 -0
  59. package/dist/assets/file-explorer-panel-B50xDnTZ.js +1 -0
  60. package/dist/assets/{file-name-input-ChnBsDE_.js → file-name-input-DqwMSgGU.js} +1 -1
  61. package/dist/assets/{floating-outline-MYqQW4Vp.js → floating-outline-ChmAuvxz.js} +1 -1
  62. package/dist/assets/{formats-yZ06h1Pl.js → formats-zylfwejq.js} +1 -1
  63. package/dist/assets/{formatting-BqrP-n49.js → formatting-uom1es4b.js} +1 -1
  64. package/dist/assets/{fullscreen-B9tHcfpV.js → fullscreen-BWTs8pYZ.js} +1 -1
  65. package/dist/assets/{gallery-page-DSL0J4md.js → gallery-page-CiL8aMS1.js} +1 -1
  66. package/dist/assets/{ganttDiagram-EL5Y4UJY-2n7Ig5Jm.js → ganttDiagram-EL5Y4UJY-CYqkCfFG.js} +1 -1
  67. package/dist/assets/{glide-data-editor-B1MWlKeM.js → glide-data-editor-CqRclBkp.js} +2 -2
  68. package/dist/assets/{glide-utils-CIrAxdjv.js → glide-utils-HFqhvWHI.js} +2 -2
  69. package/dist/assets/home-page-BgZVl4dP.js +1 -0
  70. package/dist/assets/{hooks-BIogTGCr.js → hooks-DM5n7Osw.js} +1 -1
  71. package/dist/assets/{html-to-image-DK9Zl1lU.js → html-to-image-D_0rd3_B.js} +1 -1
  72. package/dist/assets/index-BnhSnwvB.css +2 -0
  73. package/dist/assets/index-EG2n8Oht.js +30 -0
  74. package/dist/assets/{input-BoBxR50a.js → input-DmHMEZGh.js} +1 -1
  75. package/dist/assets/kiosk-mode-DFS6HopF.js +1 -0
  76. package/dist/assets/layout-BpP8nTjb.js +9 -0
  77. package/dist/assets/linear-Bma8J3iM.js +1 -0
  78. package/dist/assets/links-DX2w97N9.js +1 -0
  79. package/dist/assets/{logs-panel-y0XnfulJ.js → logs-panel-B5eVAZtH.js} +1 -1
  80. package/dist/assets/{markdown-renderer-Q5AhbRU3.js → markdown-renderer-BWUzqkJM.js} +1 -1
  81. package/dist/assets/{menu-items-BrwnTgyK.js → menu-items-BgUyNUpu.js} +1 -1
  82. package/dist/assets/{mermaid-a3xYymTy.js → mermaid-BKXlWk4F.js} +2 -2
  83. package/dist/assets/{name-cell-input-BNOnF6-0.js → name-cell-input-DXW68gRW.js} +1 -1
  84. package/dist/assets/numbers-CsZVw3Dc.js +1 -0
  85. package/dist/assets/{objects-snlMn4yk.js → objects-rLPlQ3X_.js} +1 -1
  86. package/dist/assets/{once-Haz_uxEL.js → once-6KNTRW0u.js} +1 -1
  87. package/dist/assets/{outline-panel-Be7stWwv.js → outline-panel-DPxJhJ_A.js} +1 -1
  88. package/dist/assets/{package-data-dv4FBHxR.js → package-data-CKPmBDtg.js} +1 -1
  89. package/dist/assets/packages-panel-gBE2FWQr.js +1 -0
  90. package/dist/assets/{panel-accordion-state-GeMEdCPw.js → panel-accordion-state-CCF5Gxnc.js} +1 -1
  91. package/dist/assets/panels-Dr6qMCvU.js +105 -0
  92. package/dist/assets/precisionRound-BxlUs8G4.js +1 -0
  93. package/dist/assets/process-output-Cw9IDcKQ.js +1 -0
  94. package/dist/assets/{quadrantDiagram-AXDQQJYC-yBhCDI7E.js → quadrantDiagram-AXDQQJYC-D9KH5Zge.js} +1 -1
  95. package/dist/assets/react-vega-BTNeboR2.js +189 -0
  96. package/dist/assets/{readonly-code-display-YULOXf4h.js → readonly-code-display-DYcZQ28T.js} +1 -1
  97. package/dist/assets/{readonly-python-code-CadgXgRu.js → readonly-python-code-RFuQP50v.js} +1 -1
  98. package/dist/assets/{renderShortcut-BkVJQ_Zh.js → renderShortcut-rUwGqKgC.js} +1 -1
  99. package/dist/assets/{requests-lt0JK8Jt.js → requests-BIFR-h4M.js} +1 -1
  100. package/dist/assets/requests-DuLJN9Tf.js +1 -0
  101. package/dist/assets/{reveal-component-BVAR7ayZ.js → reveal-component-yRrDmCQz.js} +37 -37
  102. package/dist/assets/run-page-B69M4-zE.js +1 -0
  103. package/dist/assets/{runs-BLp5jezv.js → runs-C49hleBy.js} +1 -1
  104. package/dist/assets/{runtime-details-state-DuChzsA6.js → runtime-details-state-pobtu6TO.js} +1 -1
  105. package/dist/assets/{save-worker-BbUSwZRG.js → save-worker-pCkSHhuz.js} +1 -1
  106. package/dist/assets/{schemas-Dv42wLFq.js → schemas-ChRqMOTL.js} +1 -1
  107. package/dist/assets/scratchpad-panel-H44_pvb_.js +1 -0
  108. package/dist/assets/{scroll-to-bottom-button-1FyowpWV.js → scroll-to-bottom-button-CIkON2WJ.js} +1 -1
  109. package/dist/assets/{secrets-panel-Bctbtkrp.js → secrets-panel-bcfEKxRx.js} +1 -1
  110. package/dist/assets/{select-DTBn5eTb.js → select-3CVGlYrC.js} +1 -1
  111. package/dist/assets/{session-DqoWmb7X.js → session-CMLIzZKw.js} +1 -1
  112. package/dist/assets/session-panel-Bnd1iXkO.js +1 -0
  113. package/dist/assets/{snippets-panel-CQ1dn3lA.js → snippets-panel-DKApxKx9.js} +1 -1
  114. package/dist/assets/{spec-BfSBcO5B.js → spec-uCpVvaAR.js} +1 -1
  115. package/dist/assets/{state-uwRvJLb8.js → state-3mcyTS9q.js} +1 -1
  116. package/dist/assets/state-B_MCKzBL.js +1 -0
  117. package/dist/assets/state-Bl9BiY_s.js +3 -0
  118. package/dist/assets/{state-C3oDoyFe.js → state-C5WAeuTu.js} +1 -1
  119. package/dist/assets/{state-DxQnC_1I.js → state-Cv2JW3l9.js} +1 -1
  120. package/dist/assets/{state-Bfh_2SLl.js → state-Dv059B3A.js} +1 -1
  121. package/dist/assets/{strings-B2vv-ENv.js → strings-C25fhMR_.js} +1 -1
  122. package/dist/assets/{swiper-component-a07domrZ.js → swiper-component-CRGRy5_O.js} +1 -1
  123. package/dist/assets/{switch-CTaiAjrz.js → switch-DYjBfpNJ.js} +1 -1
  124. package/dist/assets/{tabs-BuQlZFbt.js → tabs-CY8E4r9U.js} +1 -1
  125. package/dist/assets/{terminal-Ck_vK-34.js → terminal-Ca-kubKb.js} +1 -1
  126. package/dist/assets/{time-MgToa8VM.js → time-DswSXBDk.js} +1 -1
  127. package/dist/assets/{toast-BGzLGnvw.js → toast-Gzxi-le_.js} +1 -1
  128. package/dist/assets/tooltip-Brya4Ghn.js +61 -0
  129. package/dist/assets/{tooltip-C9TuY18o.js → tooltip-CkGm8X7m.js} +1 -1
  130. package/dist/assets/tracing-Clwy4SlW.js +1 -0
  131. package/dist/assets/tracing-panel-BwjLG_aC.js +2 -0
  132. package/dist/assets/{tree-actions-DAd80MbP.js → tree-actions-BZNTQ12N.js} +1 -1
  133. package/dist/assets/{use-restore-focus-BxEGwIbk.js → use-restore-focus-DdS7Tl0F.js} +1 -1
  134. package/dist/assets/{useAddCell-DGstJK0S.js → useAddCell-Cjuqk2YR.js} +1 -1
  135. package/dist/assets/useAsyncData-qvoPYHtx.js +1 -0
  136. package/dist/assets/useBoolean-Dd2nWXFJ.js +1 -0
  137. package/dist/assets/useCellActionButton-MoWLvuGe.js +1 -0
  138. package/dist/assets/useDebounce-DwTQ_1Pu.js +1 -0
  139. package/dist/assets/{useDeepCompareMemoize-DFIVN9dn.js → useDeepCompareMemoize-B2DVK2De.js} +1 -1
  140. package/dist/assets/useDelayElapsed-BlsDT4TV.js +1 -0
  141. package/dist/assets/{useDeleteCell-DFBGlSQk.js → useDeleteCell-BC-MW7d1.js} +1 -1
  142. package/dist/assets/{useDependencyPanelTab-DydxsE5d.js → useDependencyPanelTab-CZU7i-B3.js} +1 -1
  143. package/dist/assets/{useEvent-Dfcbgjhg.js → useEvent-BDjgaBtz.js} +1 -1
  144. package/dist/assets/{useEventListener-D9cftcHj.js → useEventListener-CjDeLqFC.js} +1 -1
  145. package/dist/assets/{useIframeCapabilities-CwTkczVN.js → useIframeCapabilities-Czr9e4uG.js} +1 -1
  146. package/dist/assets/{useInstallPackage-iX1eufdl.js → useInstallPackage-wcDq9hYT.js} +1 -1
  147. package/dist/assets/{useInterval--x3s1VpI.js → useInterval-DJdQmXnq.js} +1 -1
  148. package/dist/assets/useNotebookActions-wFxVdRXQ.js +1 -0
  149. package/dist/assets/{useRunCells-BFjFX3hz.js → useRunCells-ByaWxoIt.js} +1 -1
  150. package/dist/assets/{useSplitCell-Tqv5pPtL.js → useSplitCell-YJIystwv.js} +1 -1
  151. package/dist/assets/useStateForKey-CGMJQT8x.js +1 -0
  152. package/dist/assets/{useTheme-COhSHkCj.js → useTheme-BAA27lh7.js} +1 -1
  153. package/dist/assets/{utils-CaQYvazl.js → utils-DfElfBpz.js} +1 -1
  154. package/dist/assets/vega-component-BeyxAJad.js +1 -0
  155. package/dist/assets/{vega-embed-container-DJPNkHPb.js → vega-embed-container-DtArSJz1.js} +1 -1
  156. package/dist/assets/vega-loader.browser-Bu25TKax.js +6 -0
  157. package/dist/assets/{worker-DTQ7oH2A.js → worker-bSCjCvwz.js} +1 -1
  158. package/dist/assets/{write-secret-modal-DzKAInL2.js → write-secret-modal-BEx-EUlh.js} +1 -1
  159. package/dist/assets/{xychartDiagram-S5SC5T6Z-KbGzG6-u.js → xychartDiagram-S5SC5T6Z-CeRnBS5e.js} +1 -1
  160. package/dist/index.html +80 -78
  161. package/package.json +7 -7
  162. package/src/__tests__/host-imports.test.ts +1 -0
  163. package/src/components/app-config/ai-config.tsx +7 -2
  164. package/src/components/chat/__tests__/message-queue.test.tsx +17 -0
  165. package/src/components/chat/acp/__tests__/state.test.ts +2 -2
  166. package/src/components/chat/acp/blocks.tsx +2 -2
  167. package/src/components/chat/chat-components.tsx +2 -0
  168. package/src/components/chat/chat-panel.tsx +9 -8
  169. package/src/components/chat/chat-utils.ts +4 -8
  170. package/src/components/data-table/__tests__/column-explorer.test.tsx +27 -1
  171. package/src/components/data-table/__tests__/columns.test.tsx +105 -0
  172. package/src/components/data-table/__tests__/context-menu-interaction.test.tsx +130 -0
  173. package/src/components/data-table/__tests__/date-popover.test.tsx +72 -0
  174. package/src/components/data-table/__tests__/export-actions.test.tsx +105 -0
  175. package/src/components/data-table/__tests__/search-highlight.test.tsx +109 -0
  176. package/src/components/data-table/__tests__/url-detector.test.tsx +74 -0
  177. package/src/components/data-table/cell-selection/feature.ts +2 -2
  178. package/src/components/data-table/cell-selection/types.ts +2 -2
  179. package/src/components/data-table/charts/__tests__/chart-panel.test.tsx +74 -1
  180. package/src/components/data-table/charts/__tests__/storage.test.ts +4 -1
  181. package/src/components/data-table/charts/charts.tsx +35 -25
  182. package/src/components/data-table/charts/storage.ts +16 -5
  183. package/src/components/data-table/column-explorer-panel/column-explorer.tsx +1 -1
  184. package/src/components/data-table/column-summary/column-summary.tsx +2 -4
  185. package/src/components/data-table/column-visibility-dropdown.tsx +8 -13
  186. package/src/components/data-table/columns.tsx +45 -46
  187. package/src/components/data-table/context-menu.tsx +36 -20
  188. package/src/components/data-table/data-table.tsx +7 -1
  189. package/src/components/data-table/date-popover.tsx +6 -4
  190. package/src/components/data-table/export-actions.tsx +76 -48
  191. package/src/components/data-table/row-viewer-panel/row-viewer.tsx +2 -2
  192. package/src/components/data-table/search-highlight.tsx +79 -0
  193. package/src/components/data-table/types.ts +1 -0
  194. package/src/components/data-table/url-detector.tsx +49 -9
  195. package/src/components/datasources/__tests__/column-actions.test.tsx +97 -0
  196. package/src/components/datasources/__tests__/column-preview.test.tsx +63 -2
  197. package/src/components/datasources/__tests__/table-list.test.tsx +118 -0
  198. package/src/components/datasources/column-preview.tsx +8 -2
  199. package/src/components/datasources/datasources.tsx +47 -35
  200. package/src/components/debugger/debugger-code.tsx +5 -0
  201. package/src/components/dependency-graph/panels.tsx +2 -1
  202. package/src/components/editor/SortableCell.tsx +1 -3
  203. package/src/components/editor/__tests__/dynamic-favicon.test.tsx +111 -18
  204. package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +24 -0
  205. package/src/components/editor/actions/pair-with-agent-modal.tsx +18 -15
  206. package/src/components/editor/ai/add-cell-with-ai.tsx +3 -3
  207. package/src/components/editor/ai/ai-completion-editor.tsx +1 -0
  208. package/src/components/editor/alerts/startup-logs-alert.tsx +1 -0
  209. package/src/components/editor/cell/CreateCellButton.tsx +8 -10
  210. package/src/components/editor/cell/DeleteButton.tsx +1 -0
  211. package/src/components/editor/cell/__tests__/useShouldShowInterrupt.test.ts +39 -0
  212. package/src/components/editor/cell/code/__tests__/language-toggle.test.tsx +54 -0
  213. package/src/components/editor/cell/code/cell-editor.tsx +1 -1
  214. package/src/components/editor/cell/code/language-toggle.tsx +8 -7
  215. package/src/components/editor/cell/useShouldShowInterrupt.ts +2 -12
  216. package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +30 -1
  217. package/src/components/editor/chrome/panels/cache-panel.tsx +1 -0
  218. package/src/components/editor/chrome/panels/packages-panel.tsx +4 -7
  219. package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +12 -18
  220. package/src/components/editor/chrome/wrapper/__tests__/pending-ai-cells.test.tsx +58 -1
  221. package/src/components/editor/chrome/wrapper/footer-items/runtime-settings.tsx +1 -7
  222. package/src/components/editor/chrome/wrapper/pending-ai-cells.tsx +12 -2
  223. package/src/components/editor/chrome/wrapper/storage.ts +1 -1
  224. package/src/components/editor/connections/components.tsx +1 -1
  225. package/src/components/editor/controls/__tests__/command-palette.test.tsx +53 -0
  226. package/src/components/editor/controls/command-palette.tsx +22 -10
  227. package/src/components/editor/dynamic-favicon.tsx +21 -40
  228. package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +12 -23
  229. package/src/components/editor/file-tree/__tests__/file-explorer-interactions.test.tsx +6 -0
  230. package/src/components/editor/file-tree/__tests__/use-file-search.test.tsx +91 -0
  231. package/src/components/editor/file-tree/file-explorer.tsx +10 -5
  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/notebook-banner.tsx +1 -0
  235. package/src/components/editor/output/MarimoTracebackOutput.tsx +2 -2
  236. package/src/components/editor/output/ansi-reduce.ts +6 -0
  237. package/src/components/editor/output/console/ConsoleOutput.tsx +19 -25
  238. package/src/components/editor/package-alert.tsx +6 -4
  239. package/src/components/editor/renderers/vertical-layout/sidebar/toggle.tsx +7 -1
  240. package/src/components/home/components.tsx +17 -31
  241. package/src/components/icons/__tests__/large-spinner.test.tsx +39 -0
  242. package/src/components/icons/large-spinner.tsx +3 -14
  243. package/src/components/icons/spinner.tsx +1 -1
  244. package/src/components/mcp/mcp-status-indicator.tsx +1 -1
  245. package/src/components/pages/__tests__/home-page.test.tsx +116 -0
  246. package/src/components/pages/home-page.tsx +164 -91
  247. package/src/components/scratchpad/scratchpad.tsx +2 -2
  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.tsx +2 -2
  252. package/src/components/ui/alert-dialog.tsx +1 -1
  253. package/src/core/ai/context/providers/datasource.ts +6 -2
  254. package/src/core/attachments/state.ts +5 -8
  255. package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +4 -0
  256. package/src/core/cells/__tests__/cell.test.ts +39 -0
  257. package/src/core/cells/__tests__/cells.test.ts +3 -1
  258. package/src/core/cells/__tests__/collapseConsoleOutputs.test.ts +54 -0
  259. package/src/core/cells/__tests__/notebook-outline.test.ts +113 -0
  260. package/src/core/cells/cell.ts +14 -3
  261. package/src/core/cells/cells.ts +3 -1
  262. package/src/core/cells/pending-delete-service.ts +2 -6
  263. package/src/core/cells/session.ts +1 -1
  264. package/src/core/cells/types.ts +10 -1
  265. package/src/core/codemirror/__tests__/setup.test.ts +1 -1
  266. package/src/core/codemirror/keymaps/vim.ts +2 -6
  267. package/src/core/codemirror/language/languages/markdown.ts +1 -1
  268. package/src/core/codemirror/language/languages/python.ts +3 -3
  269. package/src/core/codemirror/language/panel/panel.tsx +1 -0
  270. package/src/core/datasets/__tests__/data-source-discovery.test.ts +4 -4
  271. package/src/core/datasets/data-source-discovery.ts +4 -6
  272. package/src/core/document/__tests__/draft.test.ts +322 -0
  273. package/src/core/document/__tests__/fixtures.ts +92 -0
  274. package/src/core/document/__tests__/model.test.ts +483 -0
  275. package/src/core/document/__tests__/state.test.ts +446 -0
  276. package/src/core/document/__tests__/submission.test.ts +346 -0
  277. package/src/core/document/draft.ts +472 -0
  278. package/src/core/document/index.ts +10 -0
  279. package/src/core/document/model.ts +53 -0
  280. package/src/core/document/state.ts +401 -0
  281. package/src/core/document/sync.ts +359 -0
  282. package/src/core/edit-app.tsx +2 -2
  283. package/src/core/host/AGENTS.md +12 -2
  284. package/src/core/host/__tests__/attachments.test.ts +56 -0
  285. package/src/core/host/__tests__/document.test.ts +126 -0
  286. package/src/core/host/__tests__/fixtures.ts +41 -0
  287. package/src/core/host/__tests__/notebook-client.test.ts +570 -0
  288. package/src/core/host/connection.ts +42 -0
  289. package/src/core/host/notebook-client.ts +297 -0
  290. package/src/core/host/resources/attachments.ts +22 -0
  291. package/src/core/host/resources/document.ts +101 -0
  292. package/src/core/hotkeys/actions.ts +2 -4
  293. package/src/core/islands/__tests__/bootstrap.test.ts +0 -26
  294. package/src/core/islands/bootstrap.ts +0 -1
  295. package/src/core/kernel/__tests__/RuntimeState.test.ts +1 -1
  296. package/src/core/network/connection.ts +4 -2
  297. package/src/core/network/useConnectionNotice.ts +8 -2
  298. package/src/core/run-app.tsx +2 -2
  299. package/src/core/runtime/__tests__/createWsUrl.test.ts +77 -7
  300. package/src/core/runtime/__tests__/runtime.test.ts +18 -17
  301. package/src/core/runtime/runtime.ts +26 -30
  302. package/src/core/state/__tests__/jotai.test.tsx +108 -0
  303. package/src/core/state/jotai.ts +1 -1
  304. package/src/core/static/files.ts +2 -0
  305. package/src/core/wasm/bridge.ts +2 -10
  306. package/src/core/wasm/store.ts +3 -3
  307. package/src/core/wasm/worker/bootstrap.ts +1 -1
  308. package/src/core/wasm/worker/getMarimoWheel.ts +1 -1
  309. package/src/core/websocket/__tests__/{useMarimoKernelConnection.test.ts → connection-state.test.ts} +15 -29
  310. package/src/core/websocket/__tests__/useNotebookClient.hook.test.tsx +223 -0
  311. package/src/core/websocket/__tests__/useNotebookSession.hook.test.tsx +606 -0
  312. package/src/core/websocket/connection-state.ts +60 -0
  313. package/src/core/websocket/state.ts +7 -0
  314. package/src/core/websocket/transports/__tests__/notebook.test.ts +502 -0
  315. package/src/core/websocket/transports/notebook.ts +213 -0
  316. package/src/core/websocket/types.ts +9 -39
  317. package/src/core/websocket/useNotebookClient.ts +89 -0
  318. package/src/core/websocket/{useMarimoKernelConnection.tsx → useNotebookSession.tsx} +53 -233
  319. package/src/css/globals.css +5 -0
  320. package/src/hooks/__tests__/useAsyncData.test.tsx +148 -0
  321. package/src/hooks/__tests__/useDebounce.test.tsx +51 -0
  322. package/src/hooks/__tests__/useDeepCompareMemoize.test.tsx +39 -0
  323. package/src/hooks/__tests__/useDelayElapsed.test.tsx +28 -0
  324. package/src/hooks/__tests__/useInternalStateWithSync.test.tsx +20 -0
  325. package/src/hooks/__tests__/useScript.test.tsx +77 -0
  326. package/src/hooks/__tests__/useStateForKey.test.tsx +257 -0
  327. package/src/hooks/useAsyncData.ts +3 -3
  328. package/src/hooks/useDataSourceDiscovery.ts +6 -4
  329. package/src/hooks/useDebounce.ts +5 -8
  330. package/src/hooks/useDeepCompareMemoize.ts +1 -1
  331. package/src/hooks/useDelayElapsed.ts +14 -10
  332. package/src/hooks/useElapsedTime.ts +3 -3
  333. package/src/hooks/useInputHistory.ts +4 -7
  334. package/src/hooks/useInternalStateWithSync.ts +6 -6
  335. package/src/hooks/useResizeObserver.ts +4 -7
  336. package/src/hooks/useScript.ts +67 -70
  337. package/src/hooks/useStateForKey.ts +93 -0
  338. package/src/plugins/core/registerReactComponent.tsx +1 -1
  339. package/src/plugins/impl/CodeEditorPlugin.tsx +1 -1
  340. package/src/plugins/impl/DataTablePlugin.tsx +14 -15
  341. package/src/plugins/impl/DropdownPlugin.tsx +2 -6
  342. package/src/plugins/impl/FileBrowserPlugin.tsx +4 -16
  343. package/src/plugins/impl/FormPlugin.tsx +1 -1
  344. package/src/plugins/impl/RangeSliderPlugin.tsx +0 -1
  345. package/src/plugins/impl/RefreshPlugin.tsx +1 -0
  346. package/src/plugins/impl/SliderPlugin.tsx +6 -8
  347. package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +117 -1
  348. package/src/plugins/impl/__tests__/FileBrowserPlugin.test.tsx +45 -0
  349. package/src/plugins/impl/__tests__/SliderPlugin.test.tsx +24 -3
  350. package/src/plugins/impl/anywidget/__tests__/registry.test.ts +2 -0
  351. package/src/plugins/impl/anywidget/__tests__/widget-binding.test.ts +241 -2
  352. package/src/plugins/impl/anywidget/runtime.ts +21 -9
  353. package/src/plugins/impl/anywidget/widget-binding.ts +160 -82
  354. package/src/plugins/impl/chat/__tests__/chat-prompts.test.tsx +93 -0
  355. package/src/plugins/impl/chat/chat-ui.tsx +12 -10
  356. package/src/plugins/impl/code/any-language-editor.tsx +2 -2
  357. package/src/plugins/impl/data-editor/glide-data-editor.tsx +8 -5
  358. package/src/plugins/impl/data-editor/glide-utils.ts +3 -1
  359. package/src/plugins/impl/data-explorer/ConnectedDataExplorerComponent.tsx +1 -1
  360. package/src/plugins/impl/data-explorer/state/reducer.ts +1 -1
  361. package/src/plugins/impl/data-frames/DataFramePlugin.tsx +0 -1
  362. package/src/plugins/impl/mpl-interactive/MplInteractivePlugin.tsx +5 -3
  363. package/src/plugins/impl/panel/PanelPlugin.tsx +16 -17
  364. package/src/plugins/impl/panel/__tests__/utils.test.ts +39 -1
  365. package/src/plugins/impl/panel/utils.ts +17 -0
  366. package/src/plugins/impl/vega/make-selectable.ts +4 -7
  367. package/src/plugins/layout/RoutesPlugin.tsx +1 -1
  368. package/src/stories/__tests__/editor-story.test.tsx +33 -0
  369. package/src/stories/button.stories.tsx +1 -0
  370. package/src/stories/editor.stories.tsx +24 -17
  371. package/src/stories/form-wrapper.stories.tsx +2 -2
  372. package/src/stories/select.stories.tsx +2 -2
  373. package/src/stories/switchable-multi-select.stories.tsx +1 -1
  374. package/src/utils/__tests__/arrays.test.ts +35 -0
  375. package/src/utils/__tests__/batch-requests.test.ts +3 -3
  376. package/src/utils/__tests__/numbers.test.ts +48 -1
  377. package/src/utils/__tests__/objects.test.ts +21 -0
  378. package/src/utils/arrays.ts +10 -0
  379. package/src/utils/blob.ts +1 -1
  380. package/src/utils/numbers.ts +28 -17
  381. package/src/utils/objects.ts +3 -7
  382. package/src/utils/once.ts +19 -19
  383. package/src/utils/zod-utils.ts +1 -3
  384. package/dist/assets/CellStatus-nZpuDkm7.js +0 -1
  385. package/dist/assets/JsonOutput-BxB0NL8w.js +0 -11
  386. package/dist/assets/MarimoErrorOutput-BTy4MOzX.js +0 -10
  387. package/dist/assets/RunButton-CFQUNN5c.js +0 -1
  388. package/dist/assets/__vite-browser-external-CYO_P3S8.js +0 -1
  389. package/dist/assets/__vite-browser-external-FeDVGZU8.js +0 -1
  390. package/dist/assets/ai-model-dropdown-zm-2fXzf.js +0 -13
  391. package/dist/assets/any-language-editor-BLCAD3yx.js +0 -1
  392. package/dist/assets/arrays-BqUx5USx.js +0 -1
  393. package/dist/assets/cell-editor-CAj1UKxx.js +0 -21
  394. package/dist/assets/cell-link-1CynTVGQ.js +0 -1
  395. package/dist/assets/chat-panel-Ce2Xh7dM.js +0 -4
  396. package/dist/assets/chat-ui-DTy98rP3.js +0 -4
  397. package/dist/assets/circle-play-CYIb0qT6.js +0 -1
  398. package/dist/assets/code-visibility-B-_GvwR5.js +0 -1
  399. package/dist/assets/coerce-CpE6kUdX.js +0 -1
  400. package/dist/assets/column-preview-DU6dT80y.js +0 -1
  401. package/dist/assets/command-palette-BMqFKXVr.js +0 -1
  402. package/dist/assets/components-CXfO-F47.js +0 -1
  403. package/dist/assets/config-DX8Y2WbJ.js +0 -1
  404. package/dist/assets/config-VPSq73n8.js +0 -1
  405. package/dist/assets/edit-page-DlfCLUaq.js +0 -9
  406. package/dist/assets/feedback-button-DlqZkJwz.js +0 -1
  407. package/dist/assets/file-explorer-panel-Bo9eN3rW.js +0 -1
  408. package/dist/assets/home-page-KrhQ55Vg.js +0 -4
  409. package/dist/assets/index-6b4mf8Ai.css +0 -2
  410. package/dist/assets/index-B3fcrmuf.js +0 -30
  411. package/dist/assets/kiosk-mode-BZECfg9k.js +0 -1
  412. package/dist/assets/layout-DOpdUwym.js +0 -9
  413. package/dist/assets/linear-0eij_aAG.js +0 -1
  414. package/dist/assets/link-NuFVR7G9.js +0 -1
  415. package/dist/assets/links-DkkL9DVa.js +0 -1
  416. package/dist/assets/numbers-Mk3-CcoU.js +0 -1
  417. package/dist/assets/packages-panel-ClxOAEGy.js +0 -1
  418. package/dist/assets/panels-DfCmzLtT.js +0 -4
  419. package/dist/assets/precisionRound-Bq_nmf5u.js +0 -1
  420. package/dist/assets/process-output-DNFKMODr.js +0 -1
  421. package/dist/assets/react-vega-C3rYGtP6.js +0 -189
  422. package/dist/assets/requests-CgSY1X4a.js +0 -1
  423. package/dist/assets/run-page-Cxu1yV8Q.js +0 -1
  424. package/dist/assets/scratchpad-panel-By-ybhjg.js +0 -1
  425. package/dist/assets/session-panel-BmW6PH4z.js +0 -1
  426. package/dist/assets/state-BAJYRhmO.js +0 -1
  427. package/dist/assets/state-z3dlofZB.js +0 -3
  428. package/dist/assets/tooltip-mT3CGest.js +0 -61
  429. package/dist/assets/tracing-BARlJ_Dk.js +0 -1
  430. package/dist/assets/tracing-panel-dgnr2fIN.js +0 -2
  431. package/dist/assets/useAsyncData-C7aJJz0Y.js +0 -1
  432. package/dist/assets/useBoolean-BDD6_xQQ.js +0 -1
  433. package/dist/assets/useCellActionButton-DY9RlJTb.js +0 -1
  434. package/dist/assets/useDebounce-B_ccBZTp.js +0 -1
  435. package/dist/assets/useNotebookActions-DfvB7RD_.js +0 -1
  436. package/dist/assets/vega-component-CnadTgoE.js +0 -1
  437. package/dist/assets/vega-loader.browser-CLRkNgm7.js +0 -6
  438. package/dist/assets/ws-BXWY541V.js +0 -22
  439. package/src/core/host/__tests__/protocol.test.ts +0 -214
  440. package/src/core/host/protocol.ts +0 -1943
  441. package/src/core/state/__tests__/jotai.test.ts +0 -25
  442. package/src/core/websocket/__tests__/useMarimoKernelConnection.hook.test.tsx +0 -789
  443. package/src/core/websocket/__tests__/useWebSocket.test.ts +0 -49
  444. package/src/core/websocket/transports/__tests__/basic.test.ts +0 -275
  445. package/src/core/websocket/transports/__tests__/sse.test.ts +0 -338
  446. package/src/core/websocket/transports/__tests__/transport.test.ts +0 -308
  447. package/src/core/websocket/transports/__tests__/ws.test.ts +0 -125
  448. package/src/core/websocket/transports/basic.ts +0 -102
  449. package/src/core/websocket/transports/sse.ts +0 -308
  450. package/src/core/websocket/transports/transport.ts +0 -61
  451. package/src/core/websocket/transports/ws.ts +0 -98
  452. package/src/core/websocket/useWebSocket.tsx +0 -88
@@ -8,8 +8,9 @@ import { RuntimeManager } from "../runtime";
8
8
  import type { RuntimeConfig } from "../types";
9
9
 
10
10
  // Mock the session module
11
- vi.mock("@/core/kernel/session", () => ({
12
- getSessionId: () => "test-session-id" as SessionId,
11
+ vi.mock("@/core/kernel/session", async (importOriginal) => ({
12
+ ...(await importOriginal<typeof import("@/core/kernel/session")>()),
13
+ getSessionId: () => "test-session-id",
13
14
  }));
14
15
 
15
16
  vi.mock("@/utils/Logger", () => ({ Logger: Mocks.quietLogger() }));
@@ -59,6 +60,7 @@ describe("RuntimeManager", () => {
59
60
  expect(url.protocol).toBe("wss:");
60
61
  expect(url.hostname).toBe("example.com");
61
62
  expect(url.pathname).toBe("/ws");
63
+ expect(url.searchParams.has("types")).toBe(false);
62
64
  expect(url.searchParams.get("session_id")).toBe(sessionId);
63
65
  });
64
66
 
@@ -219,15 +221,6 @@ describe("RuntimeManager", () => {
219
221
  });
220
222
 
221
223
  describe("getSseURL", () => {
222
- it("should return an http(s) URL with the session ID", () => {
223
- const runtime = new RuntimeManager(mockConfig);
224
- const url = runtime.getSseURL("1234" as SessionId);
225
-
226
- expect(url.protocol).toBe("https:");
227
- expect(url.pathname).toBe("/sse");
228
- expect(url.searchParams.get("session_id")).toBe("1234");
229
- });
230
-
231
224
  it("should never put the auth token in the URL, even cross-origin", () => {
232
225
  // SSE requests can send headers, so auth travels in the
233
226
  // Authorization header; a token in the URL would leak into
@@ -256,7 +249,8 @@ describe("RuntimeManager", () => {
256
249
  const url = runtime.getSseURL("s_123" as SessionId);
257
250
 
258
251
  expect(url.searchParams.get("access_token")).toBeNull();
259
- expect(url.searchParams.get("foo")).toBe("bar");
252
+ expect(url.searchParams.get("query_params")).toBe("foo=bar");
253
+ expect(url.toString()).not.toContain("page-token");
260
254
  } finally {
261
255
  window.history.pushState({}, "", original);
262
256
  }
@@ -676,9 +670,16 @@ describe("RuntimeManager", () => {
676
670
  expect(httpUrl.searchParams.get("base_param")).toBe("existing");
677
671
 
678
672
  // Should preserve all window-level query params (including custom ones)
679
- expect(wsUrl.searchParams.get("custom_param")).toBe("value123");
680
- expect(wsUrl.searchParams.get("user_token")).toBe("abc");
681
- expect(wsUrl.searchParams.get("theme")).toBe("dark");
673
+ expect(
674
+ Object.fromEntries(
675
+ new URLSearchParams(wsUrl.searchParams.get("query_params") ?? ""),
676
+ ),
677
+ ).toEqual({
678
+ base_param: "existing",
679
+ custom_param: "value123",
680
+ user_token: "abc",
681
+ theme: "dark",
682
+ });
682
683
  expect(httpUrl.searchParams.get("custom_param")).toBe("value123");
683
684
  expect(httpUrl.searchParams.get("user_token")).toBe("abc");
684
685
  expect(httpUrl.searchParams.get("theme")).toBe("dark");
@@ -737,7 +738,7 @@ describe("RuntimeManager", () => {
737
738
  );
738
739
  });
739
740
 
740
- it("should throw when creating WebSocket URLs from blob URLs", () => {
741
+ it("warns when creating WebSocket URLs from blob URLs", () => {
741
742
  const runtime = new RuntimeManager({
742
743
  url: "blob:https://example.com/12345678-1234-1234-1234-123456789abc",
743
744
  lazy: true,
@@ -745,7 +746,7 @@ describe("RuntimeManager", () => {
745
746
  const sessionId = "test" as SessionId;
746
747
 
747
748
  expect(runtime.getWsURL(sessionId).toString()).toMatchInlineSnapshot(
748
- `"blob:https://example.com/12345678-1234-1234-1234-123456789abc?session_id=test"`,
749
+ `"blob:https://example.com/12345678-1234-1234-1234-123456789abc?query_params=&session_id=test"`,
749
750
  );
750
751
  expect(Logger.warn).toHaveBeenCalledOnce();
751
752
  });
@@ -9,6 +9,8 @@ import { isStaticNotebook } from "../static/static-state";
9
9
  import { isWasm } from "../wasm/utils";
10
10
  import type { RuntimeConfig } from "./types";
11
11
 
12
+ const knownQueryParams = new Set(Object.values(KnownQueryParams));
13
+
12
14
  export class RuntimeManager {
13
15
  private initialHealthyCheck = new Deferred<void>();
14
16
  private config: RuntimeConfig;
@@ -71,10 +73,7 @@ export class RuntimeManager {
71
73
  }
72
74
 
73
75
  for (const [key, value] of currentParams.entries()) {
74
- if (
75
- restrictToKnownQueryParams &&
76
- !Object.values(KnownQueryParams).includes(key)
77
- ) {
76
+ if (restrictToKnownQueryParams && !knownQueryParams.has(key)) {
78
77
  continue;
79
78
  }
80
79
  baseUrl.searchParams.set(key, value);
@@ -129,32 +128,32 @@ export class RuntimeManager {
129
128
  return asWsUrl(this.appendCrossOriginAuth(url).toString());
130
129
  }
131
130
 
132
- /**
133
- * Search params for a session connection: the base URL's params, merged
134
- * with the current page's, plus the session id.
135
- */
136
- private getSessionSearchParams(sessionId: SessionId): URLSearchParams {
137
- const baseUrl = new URL(this.config.url);
138
- const searchParams = new URLSearchParams(baseUrl.search);
139
-
140
- // Merge in current page's query parameters
141
- const currentParams = new URLSearchParams(window.location.search);
142
- currentParams.forEach((value, key) => {
143
- // Don't override base URL params
144
- if (!searchParams.has(key)) {
145
- searchParams.set(key, value);
146
- }
147
- });
148
-
149
- searchParams.set(KnownQueryParams.sessionId, sessionId);
150
- return searchParams;
131
+ /** Keep notebook page parameters separate from connection options. */
132
+ private getSessionURL(path: string, sessionId: SessionId): URL {
133
+ const url = this.formatHttpURL({ path });
134
+ const notebookParams = new URLSearchParams(this.httpURL.search);
135
+ const pageParams = new URLSearchParams(window.location.search);
136
+ for (const key of pageParams.keys()) {
137
+ notebookParams.delete(key);
138
+ }
139
+ for (const [key, value] of pageParams) {
140
+ notebookParams.append(key, value);
141
+ }
142
+ // These parameters are internal and are not exposed by mo.query_params().
143
+ notebookParams.delete(KnownQueryParams.accessToken);
144
+ notebookParams.delete("refresh_token");
145
+ notebookParams.delete(KnownQueryParams.sessionId);
146
+ url.searchParams.set("query_params", notebookParams.toString());
147
+ url.searchParams.set(KnownQueryParams.sessionId, sessionId);
148
+ return url;
151
149
  }
152
150
 
153
151
  /**
154
152
  * The WebSocket URL of the runtime.
155
153
  */
156
154
  public getWsURL(sessionId: SessionId): URL {
157
- return this.formatWsURL("/ws", this.getSessionSearchParams(sessionId));
155
+ const url = this.getSessionURL("/ws", sessionId);
156
+ return asWsUrl(this.appendCrossOriginAuth(url).toString());
158
157
  }
159
158
 
160
159
  /**
@@ -164,11 +163,7 @@ export class RuntimeManager {
164
163
  * (see `headers()`) instead of the URL.
165
164
  */
166
165
  public getSseURL(sessionId: SessionId): URL {
167
- const url = this.formatHttpURL({
168
- path: "/sse",
169
- searchParams: this.getSessionSearchParams(sessionId),
170
- restrictToKnownQueryParams: false,
171
- });
166
+ const url = this.getSessionURL("/sse", sessionId);
172
167
  // The page URL can still carry access_token (merged in from the
173
168
  // current page's params, before auth cleanup strips it); never
174
169
  // forward it — a token in the URL leaks into proxy/server logs.
@@ -180,7 +175,8 @@ export class RuntimeManager {
180
175
  * The WebSocket Sync URL of the runtime, for real-time updates.
181
176
  */
182
177
  public getWsSyncURL(sessionId: SessionId): URL {
183
- return this.formatWsURL("/ws_sync", this.getSessionSearchParams(sessionId));
178
+ const url = this.getSessionURL("/ws_sync", sessionId);
179
+ return asWsUrl(this.appendCrossOriginAuth(url).toString());
184
180
  }
185
181
 
186
182
  /**
@@ -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;
@@ -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
  */
@@ -38,8 +38,6 @@ import type {
38
38
  } from "../network/types";
39
39
  import { filenameAtom } from "../saving/file-state";
40
40
  import { store } from "../state/jotai";
41
- import { BasicTransport } from "../websocket/transports/basic";
42
- import type { IConnectionTransport } from "../websocket/transports/transport";
43
41
  import { PyodideRouter } from "./router";
44
42
  import { getWorkerRPC } from "./rpc";
45
43
  import { getWasmRuntimeConfig } from "./runtime-config";
@@ -409,7 +407,7 @@ export class PyodideBridge implements RunRequests, EditRequests {
409
407
  return response as Snippets;
410
408
  };
411
409
 
412
- public openFile: EditRequests["openFile"] = async ({ path }) => {
410
+ public openFile: EditRequests["openFile"] = async () => {
413
411
  const url = createShareableLink({
414
412
  code: null,
415
413
  baseUrl: window.location.origin,
@@ -453,7 +451,7 @@ export class PyodideBridge implements RunRequests, EditRequests {
453
451
  return null;
454
452
  };
455
453
 
456
- public sendInstantiate: RunRequests["sendInstantiate"] = async (request) => {
454
+ public sendInstantiate: RunRequests["sendInstantiate"] = async () => {
457
455
  return null;
458
456
  };
459
457
 
@@ -719,12 +717,6 @@ export class PyodideBridge implements RunRequests, EditRequests {
719
717
  }
720
718
  }
721
719
 
722
- export function createPyodideConnection(): IConnectionTransport {
723
- return BasicTransport.withProducerCallback((callback) => {
724
- PyodideBridge.INSTANCE.attachMessageConsumer(callback);
725
- });
726
- }
727
-
728
720
  // Compose the worker name. The optional "::controller" suffix tells
729
721
  // getController.ts that the host page provides a custom /wasm/controller.js
730
722
  // and that the dynamic import should be attempted. Hosts opt in by setting
@@ -41,7 +41,7 @@ const urlFileStore: FileStore = {
41
41
  };
42
42
 
43
43
  export const domElementFileStore: FileStore = {
44
- saveFile(contents: string) {
44
+ saveFile(_contents: string) {
45
45
  // Do nothing
46
46
  },
47
47
  readFile() {
@@ -54,7 +54,7 @@ export const domElementFileStore: FileStore = {
54
54
  };
55
55
 
56
56
  const remoteDefaultFileStore: FileStore = {
57
- saveFile(contents: string) {
57
+ saveFile(_contents: string) {
58
58
  // Do nothing
59
59
  },
60
60
  readFile() {
@@ -79,7 +79,7 @@ export const mountConfigFileStore: FileStore = {
79
79
  };
80
80
 
81
81
  const emptyFileStore: FileStore = {
82
- saveFile(contents: string) {
82
+ saveFile(_contents: string) {
83
83
  // Do nothing
84
84
  },
85
85
  readFile() {
@@ -85,7 +85,7 @@ export class DefaultWasmController implements WasmController {
85
85
  packages: [
86
86
  "micropip",
87
87
  "msgspec",
88
- getMarimoWheel(opts.version),
88
+ getMarimoWheel(),
89
89
  "Markdown",
90
90
  "pymdown-extensions",
91
91
  "narwhals",
@@ -1,6 +1,6 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
- export function getMarimoWheel(version: string) {
3
+ export function getMarimoWheel() {
4
4
  if (import.meta.env.DEV && import.meta.env.VITE_WASM_MARIMO_PREBUILT_WHEEL) {
5
5
  return "marimo-base";
6
6
  }
@@ -3,7 +3,7 @@
3
3
  import { afterEach, describe, expect, it, vi } from "vitest";
4
4
  import { Logger } from "@/utils/Logger";
5
5
  import { WebSocketClosedReason, ConnectionState } from "../types";
6
- import { classifyCloseEvent } from "../useMarimoKernelConnection";
6
+ import { classifyCloseEvent } from "../connection-state";
7
7
 
8
8
  function classify(reason: string | undefined) {
9
9
  return classifyCloseEvent({ reason });
@@ -13,8 +13,10 @@ describe("classifyCloseEvent", () => {
13
13
  describe("transient closes (default branch)", () => {
14
14
  it("retries on empty/undefined reason", () => {
15
15
  const decision = classify(undefined);
16
- expect(decision.kind).toBe("retry");
17
- expect(decision.status).toEqual({ state: ConnectionState.CONNECTING });
16
+ expect(decision).toEqual({
17
+ kind: "retry",
18
+ status: { state: ConnectionState.CONNECTING },
19
+ });
18
20
  });
19
21
 
20
22
  it("treats unknown reason strings as transient and logs a warning", () => {
@@ -40,41 +42,25 @@ describe("classifyCloseEvent", () => {
40
42
  // terminal or a rejected client would reconnect forever.
41
43
  "MARIMO_UNAUTHORIZED",
42
44
  "MARIMO_KIOSK_NOT_ALLOWED",
43
- ])("%s → terminal with KERNEL_DISCONNECTED, closes transport", (reason) => {
45
+ ])("%s → terminal with KERNEL_DISCONNECTED", (reason) => {
44
46
  const decision = classify(reason);
45
- expect(decision.kind).toBe("terminal");
46
- expect(decision.status).toMatchObject({
47
- state: ConnectionState.CLOSED,
48
- code: WebSocketClosedReason.KERNEL_DISCONNECTED,
49
- });
50
47
  expect(decision).toMatchObject({
51
48
  kind: "terminal",
52
- closeTransport: true,
49
+ status: {
50
+ state: ConnectionState.CLOSED,
51
+ code: WebSocketClosedReason.KERNEL_DISCONNECTED,
52
+ },
53
53
  });
54
54
  });
55
55
 
56
- it("MARIMO_KERNEL_STARTUP_ERROR → terminal + closeTransport", () => {
56
+ it("MARIMO_KERNEL_STARTUP_ERROR → terminal", () => {
57
57
  const decision = classify("MARIMO_KERNEL_STARTUP_ERROR");
58
- expect(decision.kind).toBe("terminal");
59
- expect(decision.status).toMatchObject({
60
- state: ConnectionState.CLOSED,
61
- code: WebSocketClosedReason.KERNEL_STARTUP_ERROR,
62
- });
63
58
  expect(decision).toMatchObject({
64
59
  kind: "terminal",
65
- closeTransport: true,
66
- });
67
- });
68
- });
69
-
70
- describe("transport exhaustion", () => {
71
- it("MARIMO_TRANSPORT_EXHAUSTED → gave-up with KERNEL_DISCONNECTED", () => {
72
- const decision = classify("MARIMO_TRANSPORT_EXHAUSTED");
73
- expect(decision.kind).toBe("gave-up");
74
- expect(decision.status).toEqual({
75
- state: ConnectionState.CLOSED,
76
- code: WebSocketClosedReason.KERNEL_DISCONNECTED,
77
- reason: "kernel not found",
60
+ status: {
61
+ state: ConnectionState.CLOSED,
62
+ code: WebSocketClosedReason.KERNEL_STARTUP_ERROR,
63
+ },
78
64
  });
79
65
  });
80
66
  });