@marimo-team/frontend 0.25.3-dev5 → 0.25.3-dev55

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 (343) hide show
  1. package/dist/assets/CellStatus-cD3S0wEA.js +1 -0
  2. package/dist/assets/{ConnectedDataExplorerComponent-t8fH_TNc.js → ConnectedDataExplorerComponent-D9UK8o9A.js} +1 -1
  3. package/dist/assets/{ErrorBoundary-B4n9EnHB.js → ErrorBoundary-DJhsFCsX.js} +2 -2
  4. package/dist/assets/JsonOutput-Pm6OCP3J.js +11 -0
  5. package/dist/assets/{LazyAnyLanguageCodeMirror-CKcCbG2G.js → LazyAnyLanguageCodeMirror-BPYE76jR.js} +2 -2
  6. package/dist/assets/MarimoErrorOutput-DlEjLCZ-.js +10 -0
  7. package/dist/assets/{RenderHTML-9bMgbYmo.js → RenderHTML-CfQ6i3QV.js} +1 -1
  8. package/dist/assets/RunButton-DvuvkgrG.js +1 -0
  9. package/dist/assets/{add-cell-with-ai-C_SruRWS.js → add-cell-with-ai-BhmuohAY.js} +7 -7
  10. package/dist/assets/{add-connection-dialog-CoE_PhuD.js → add-connection-dialog-D7EYSraq.js} +2 -2
  11. package/dist/assets/{agent-panel-0b3k4QzJ.js → agent-panel-GFhlWVub.js} +3 -3
  12. package/dist/assets/ai-model-dropdown-YT1HiXiK.js +13 -0
  13. package/dist/assets/{alert-dialog-BaPGzeOK.js → alert-dialog-C-sWmKva.js} +1 -1
  14. package/dist/assets/any-language-editor-DUMe1UCk.js +1 -0
  15. package/dist/assets/{app-config-button-iXdCWvfC.js → app-config-button-C_whRVO8.js} +1 -1
  16. package/dist/assets/arrays-0R2reF2y.js +1 -0
  17. package/dist/assets/{blob-iOBwGaP9.js → blob-e6VlOmQP.js} +1 -1
  18. package/dist/assets/{button-COI-NGrx.js → button-D8gVKwe-.js} +1 -1
  19. package/dist/assets/{cache-panel-DTY1dqvi.js → cache-panel-Byolv2ht.js} +1 -1
  20. package/dist/assets/{capabilities-BJvf-MD4.js → capabilities-Pq1YNPlK.js} +1 -1
  21. package/dist/assets/{cell-editor-CR-tZNnc.js → cell-editor-3cVIcyHK.js} +6 -6
  22. package/dist/assets/cell-link-DFqfW-Ym.js +1 -0
  23. package/dist/assets/{cells-rA_jsZCj.js → cells-T9_Y4Jlv.js} +56 -56
  24. package/dist/assets/{chat-display-luF7psRA.js → chat-display-BtaARI-N.js} +1 -1
  25. package/dist/assets/chat-panel-eT2lfQrB.js +4 -0
  26. package/dist/assets/chat-ui-nNFa2LdU.js +4 -0
  27. package/dist/assets/{circle-DXf-VWZG.js → circle-B6D_6D8w.js} +1 -1
  28. package/dist/assets/code-visibility-CV3S4xuf.js +1 -0
  29. package/dist/assets/coerce-DWjFeXOY.js +1 -0
  30. package/dist/assets/{column-preview-uosAAL4X.js → column-preview-C6uXRJry.js} +1 -1
  31. package/dist/assets/command-palette-OZPTmtR-.js +1 -0
  32. package/dist/assets/{common-lisZ0l6a.js → common-DeeLgjWQ.js} +1 -1
  33. package/dist/assets/{components-CP_UoFtT.js → components-DrU8dOzy.js} +1 -1
  34. package/dist/assets/{components-S7ZwOrvO.js → components-DyAF3_Nf.js} +1 -1
  35. package/dist/assets/{components-CXfO-F47.js → components-lDm0qi2t.js} +1 -1
  36. package/dist/assets/config-CjQ4WJnJ.js +1 -0
  37. package/dist/assets/config-DGX7JYor.js +1 -0
  38. package/dist/assets/config-DzdxZb17.js +1 -0
  39. package/dist/assets/{copy-D8O_CYCG.js → copy-KlhazlhB.js} +1 -1
  40. package/dist/assets/{copy-icon-BIiRXh_3.js → copy-icon-8-a5vovc.js} +1 -1
  41. package/dist/assets/{createReducer-DgRODTW6.js → createReducer-HKXUbqyz.js} +1 -1
  42. package/dist/assets/{datasource-V0-52XOh.js → datasource-gqFgG9Qd.js} +1 -1
  43. package/dist/assets/{dates-CfmdTL6B.js → dates-CQMwFko2.js} +1 -1
  44. package/dist/assets/{dependency-graph-panel-B0uP-_id.js → dependency-graph-panel-BFFxLgR6.js} +1 -1
  45. package/dist/assets/{dist-iaThVDkA.js → dist-jlhMTKo_.js} +1 -1
  46. package/dist/assets/{documentation-panel-DVl_ebWy.js → documentation-panel-BBpGbT-f.js} +1 -1
  47. package/dist/assets/{download-Bx55LMki.js → download-C8Gw-D0Y.js} +1 -1
  48. package/dist/assets/{dropdown-menu-C1rnl-0o.js → dropdown-menu-DS6umAnH.js} +1 -1
  49. package/dist/assets/{edit-page-CGyjT-CQ.js → edit-page-B0lN_Rib.js} +6 -6
  50. package/dist/assets/{error-banner-Cd69vvyX.js → error-banner-B5kzVf_0.js} +1 -1
  51. package/dist/assets/{error-entries-DOWpdGvI.js → error-entries-uyqOG5tg.js} +1 -1
  52. package/dist/assets/{error-panel-C-uxLOHF.js → error-panel-Cli5zPWi.js} +1 -1
  53. package/dist/assets/{errors-DCrsa1nH.js → errors-CNsgAw6m.js} +1 -1
  54. package/dist/assets/{es-Cqst59WX.js → es-N9SHpmG7.js} +1 -1
  55. package/dist/assets/{feedback-button-B99ThNJW.js → feedback-button-CvrwBGr7.js} +2 -2
  56. package/dist/assets/feedback-button-DknbRCN_.js +1 -0
  57. package/dist/assets/file-explorer-panel-BSU5ultY.js +1 -0
  58. package/dist/assets/{file-name-input-ChnBsDE_.js → file-name-input-CDrRlIiX.js} +1 -1
  59. package/dist/assets/{floating-outline-MYqQW4Vp.js → floating-outline-GQD9km0W.js} +1 -1
  60. package/dist/assets/{formats-yZ06h1Pl.js → formats-zylfwejq.js} +1 -1
  61. package/dist/assets/{formatting-BqrP-n49.js → formatting-uom1es4b.js} +1 -1
  62. package/dist/assets/{fullscreen-B9tHcfpV.js → fullscreen-BWTs8pYZ.js} +1 -1
  63. package/dist/assets/{gallery-page-DSL0J4md.js → gallery-page-CxmV1JLF.js} +1 -1
  64. package/dist/assets/{ganttDiagram-EL5Y4UJY-2n7Ig5Jm.js → ganttDiagram-EL5Y4UJY-CYqkCfFG.js} +1 -1
  65. package/dist/assets/{glide-data-editor-B1MWlKeM.js → glide-data-editor-B1kpoetH.js} +2 -2
  66. package/dist/assets/{glide-utils-CIrAxdjv.js → glide-utils-HFqhvWHI.js} +2 -2
  67. package/dist/assets/{home-page-B1M3X9Yf.js → home-page-KnJyha6O.js} +3 -3
  68. package/dist/assets/{hooks-Bj0C0zrZ.js → hooks-w6zKs3CJ.js} +1 -1
  69. package/dist/assets/{html-to-image-DK9Zl1lU.js → html-to-image-D_0rd3_B.js} +1 -1
  70. package/dist/assets/index-Bu8_lPMx.js +30 -0
  71. package/dist/assets/index-evFWHKmM.css +2 -0
  72. package/dist/assets/{input-BoBxR50a.js → input-BZdRe7pb.js} +1 -1
  73. package/dist/assets/{kiosk-mode-BZECfg9k.js → kiosk-mode-BGmMSc7A.js} +1 -1
  74. package/dist/assets/layout-sYdHI-lW.js +9 -0
  75. package/dist/assets/linear-Bma8J3iM.js +1 -0
  76. package/dist/assets/links-CXcpNIM0.js +1 -0
  77. package/dist/assets/{logs-panel-y0XnfulJ.js → logs-panel-CdUc5vtA.js} +1 -1
  78. package/dist/assets/{markdown-renderer-Q5AhbRU3.js → markdown-renderer-pKc_bFac.js} +1 -1
  79. package/dist/assets/{menu-items-BrwnTgyK.js → menu-items-BgUyNUpu.js} +1 -1
  80. package/dist/assets/{mermaid-a3xYymTy.js → mermaid-B6mqMm5_.js} +2 -2
  81. package/dist/assets/{name-cell-input-BNOnF6-0.js → name-cell-input-csH3Cy54.js} +1 -1
  82. package/dist/assets/numbers-CsZVw3Dc.js +1 -0
  83. package/dist/assets/{objects-snlMn4yk.js → objects-rLPlQ3X_.js} +1 -1
  84. package/dist/assets/{once-Haz_uxEL.js → once-6KNTRW0u.js} +1 -1
  85. package/dist/assets/{outline-panel-Be7stWwv.js → outline-panel-DhsHYTuo.js} +1 -1
  86. package/dist/assets/packages-panel-lkNX6Oov.js +1 -0
  87. package/dist/assets/{panel-accordion-state-GeMEdCPw.js → panel-accordion-state-Bco6nsUI.js} +1 -1
  88. package/dist/assets/panels-B2UqDbHy.js +107 -0
  89. package/dist/assets/precisionRound-BxlUs8G4.js +1 -0
  90. package/dist/assets/process-output-B2-bT4cq.js +1 -0
  91. package/dist/assets/{quadrantDiagram-AXDQQJYC-yBhCDI7E.js → quadrantDiagram-AXDQQJYC-D9KH5Zge.js} +1 -1
  92. package/dist/assets/react-vega-BTNeboR2.js +189 -0
  93. package/dist/assets/{readonly-code-display-YULOXf4h.js → readonly-code-display-Dsp6vWPh.js} +1 -1
  94. package/dist/assets/{readonly-python-code-CadgXgRu.js → readonly-python-code-B81AjzXM.js} +1 -1
  95. package/dist/assets/{renderShortcut-BkVJQ_Zh.js → renderShortcut-H60uiHBp.js} +1 -1
  96. package/dist/assets/{reveal-component-B9FLjTg_.js → reveal-component-CrQRQhW4.js} +37 -37
  97. package/dist/assets/run-page-BGzpa3D9.js +1 -0
  98. package/dist/assets/{runs-BLp5jezv.js → runs-6lJxKV1w.js} +1 -1
  99. package/dist/assets/{runtime-details-state-DuChzsA6.js → runtime-details-state-CznIe40L.js} +1 -1
  100. package/dist/assets/{schemas-Dv42wLFq.js → schemas-ChRqMOTL.js} +1 -1
  101. package/dist/assets/{scratchpad-panel-DtB0A8xj.js → scratchpad-panel-ClLxdqdK.js} +1 -1
  102. package/dist/assets/{scroll-to-bottom-button-1FyowpWV.js → scroll-to-bottom-button-CIkON2WJ.js} +1 -1
  103. package/dist/assets/{secrets-panel-Bctbtkrp.js → secrets-panel-Dh3v7XPE.js} +1 -1
  104. package/dist/assets/{select-DTBn5eTb.js → select-3CVGlYrC.js} +1 -1
  105. package/dist/assets/{session-DqoWmb7X.js → session-B8Y-GJnS.js} +1 -1
  106. package/dist/assets/{session-panel-B6tA28BD.js → session-panel-C7-wmZhc.js} +1 -1
  107. package/dist/assets/{snippets-panel-CQ1dn3lA.js → snippets-panel-BARpOPyS.js} +1 -1
  108. package/dist/assets/{spec-BfSBcO5B.js → spec-uCpVvaAR.js} +1 -1
  109. package/dist/assets/{state-BUsmcyaT.js → state-BRYi026f.js} +3 -3
  110. package/dist/assets/{state-uwRvJLb8.js → state-BY4_P6ko.js} +1 -1
  111. package/dist/assets/{state-Bfh_2SLl.js → state-Bye6rCo4.js} +1 -1
  112. package/dist/assets/{state-BAJYRhmO.js → state-C0irhlOZ.js} +1 -1
  113. package/dist/assets/{state-DxQnC_1I.js → state-Ck7FMD5A.js} +1 -1
  114. package/dist/assets/{state-C3oDoyFe.js → state-DaWgt-rn.js} +1 -1
  115. package/dist/assets/{strings-B2vv-ENv.js → strings-C25fhMR_.js} +1 -1
  116. package/dist/assets/{swiper-component-a07domrZ.js → swiper-component-CRGRy5_O.js} +1 -1
  117. package/dist/assets/{switch-CTaiAjrz.js → switch-IoWE8PbW.js} +1 -1
  118. package/dist/assets/{tabs-BuQlZFbt.js → tabs-CY8E4r9U.js} +1 -1
  119. package/dist/assets/{terminal-Ck_vK-34.js → terminal-Bhcn_y1N.js} +1 -1
  120. package/dist/assets/{time-MgToa8VM.js → time-DswSXBDk.js} +1 -1
  121. package/dist/assets/{toast-BGzLGnvw.js → toast-Gzxi-le_.js} +1 -1
  122. package/dist/assets/tooltip-Brya4Ghn.js +61 -0
  123. package/dist/assets/{tooltip-C9TuY18o.js → tooltip-CkGm8X7m.js} +1 -1
  124. package/dist/assets/tracing-BW2ZVnXs.js +1 -0
  125. package/dist/assets/{tracing-panel-dgnr2fIN.js → tracing-panel-UKdh_YXC.js} +2 -2
  126. package/dist/assets/{tree-actions-DAd80MbP.js → tree-actions-Cb-nUK1F.js} +1 -1
  127. package/dist/assets/{use-restore-focus-BxEGwIbk.js → use-restore-focus-DdS7Tl0F.js} +1 -1
  128. package/dist/assets/{useAddCell-DGstJK0S.js → useAddCell-Bielffe0.js} +1 -1
  129. package/dist/assets/useAsyncData-DhhdOGGd.js +1 -0
  130. package/dist/assets/useBoolean-CMgj9hnq.js +1 -0
  131. package/dist/assets/{useCellActionButton-DY9RlJTb.js → useCellActionButton-Cu34Zs6x.js} +1 -1
  132. package/dist/assets/useDebounce-k9QTpQ9-.js +1 -0
  133. package/dist/assets/useDelayElapsed-BlsDT4TV.js +1 -0
  134. package/dist/assets/{useDeleteCell-DFBGlSQk.js → useDeleteCell-BV4VemCv.js} +1 -1
  135. package/dist/assets/{useDependencyPanelTab-DydxsE5d.js → useDependencyPanelTab-BEGqRiWi.js} +1 -1
  136. package/dist/assets/{useEventListener-D9cftcHj.js → useEventListener-CjDeLqFC.js} +1 -1
  137. package/dist/assets/{useIframeCapabilities-CwTkczVN.js → useIframeCapabilities-Czr9e4uG.js} +1 -1
  138. package/dist/assets/{useInstallPackage-iX1eufdl.js → useInstallPackage-C7neMPwh.js} +1 -1
  139. package/dist/assets/{useInterval--x3s1VpI.js → useInterval-DJdQmXnq.js} +1 -1
  140. package/dist/assets/{useNotebookActions-CupIPSlg.js → useNotebookActions-OLol05t9.js} +1 -1
  141. package/dist/assets/{useRunCells-BFjFX3hz.js → useRunCells-BFeZMi7f.js} +1 -1
  142. package/dist/assets/{useSplitCell-Tqv5pPtL.js → useSplitCell-DllkNFke.js} +1 -1
  143. package/dist/assets/useStateForKey-CGMJQT8x.js +1 -0
  144. package/dist/assets/{useTheme-COhSHkCj.js → useTheme-DLtPc-39.js} +1 -1
  145. package/dist/assets/{utils-CaQYvazl.js → utils-DfElfBpz.js} +1 -1
  146. package/dist/assets/vega-component-D7JFthDD.js +1 -0
  147. package/dist/assets/{vega-embed-container-DJPNkHPb.js → vega-embed-container-DtArSJz1.js} +1 -1
  148. package/dist/assets/vega-loader.browser-Bu25TKax.js +6 -0
  149. package/dist/assets/{write-secret-modal-DzKAInL2.js → write-secret-modal-7DzehGyr.js} +1 -1
  150. package/dist/assets/{xychartDiagram-S5SC5T6Z-KbGzG6-u.js → xychartDiagram-S5SC5T6Z-CeRnBS5e.js} +1 -1
  151. package/dist/index.html +76 -74
  152. package/package.json +4 -4
  153. package/src/__tests__/fixtures/{pair-preview.ts → pair-config.ts} +3 -2
  154. package/src/__tests__/host-imports.test.ts +1 -0
  155. package/src/__tests__/mount.test.ts +8 -8
  156. package/src/components/app-config/ai-config.tsx +6 -2
  157. package/src/components/chat/__tests__/message-queue.test.tsx +17 -0
  158. package/src/components/chat/chat-panel.tsx +3 -7
  159. package/src/components/chat/chat-utils.ts +4 -8
  160. package/src/components/data-table/__tests__/context-menu-interaction.test.tsx +130 -0
  161. package/src/components/data-table/__tests__/date-popover.test.tsx +72 -0
  162. package/src/components/data-table/__tests__/export-actions.test.tsx +434 -6
  163. package/src/components/data-table/charts/charts.tsx +14 -19
  164. package/src/components/data-table/column-visibility-dropdown.tsx +8 -13
  165. package/src/components/data-table/columns.tsx +2 -1
  166. package/src/components/data-table/context-menu.tsx +36 -20
  167. package/src/components/data-table/date-popover.tsx +6 -4
  168. package/src/components/data-table/export-actions.tsx +267 -163
  169. package/src/components/data-table/row-viewer-panel/row-viewer.tsx +2 -2
  170. package/src/components/data-table/schemas.ts +14 -36
  171. package/src/components/editor/Output.tsx +5 -1
  172. package/src/components/editor/__tests__/dynamic-favicon.test.tsx +111 -18
  173. package/src/components/editor/actions/__tests__/pair-with-agent-commands.test.ts +80 -252
  174. package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +210 -34
  175. package/src/components/editor/actions/pair-with-agent-commands.ts +42 -51
  176. package/src/components/editor/actions/pair-with-agent-modal.tsx +100 -37
  177. package/src/components/editor/ai/add-cell-with-ai.tsx +3 -3
  178. package/src/components/editor/cell/CreateCellButton.tsx +8 -10
  179. package/src/components/editor/cell/__tests__/useShouldShowInterrupt.test.ts +39 -0
  180. package/src/components/editor/cell/code/__tests__/language-toggle.test.tsx +54 -0
  181. package/src/components/editor/cell/code/cell-editor.tsx +1 -1
  182. package/src/components/editor/cell/code/language-toggle.tsx +8 -7
  183. package/src/components/editor/cell/useShouldShowInterrupt.ts +2 -12
  184. package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +30 -1
  185. package/src/components/editor/chrome/panels/packages-panel.tsx +4 -7
  186. package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +277 -78
  187. package/src/components/editor/chrome/wrapper/footer-items/attachment-status.tsx +196 -41
  188. package/src/components/editor/connections/components.tsx +1 -1
  189. package/src/components/editor/controls/__tests__/command-palette.test.tsx +53 -0
  190. package/src/components/editor/controls/command-palette.tsx +22 -10
  191. package/src/components/editor/dynamic-favicon.tsx +21 -40
  192. package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +139 -36
  193. package/src/components/editor/errors/send-error-report-button.tsx +10 -7
  194. package/src/components/editor/file-tree/__tests__/file-explorer-interactions.test.tsx +6 -0
  195. package/src/components/editor/file-tree/__tests__/use-file-search.test.tsx +91 -0
  196. package/src/components/editor/file-tree/file-explorer.tsx +10 -5
  197. package/src/components/editor/file-tree/file-viewer.tsx +4 -2
  198. package/src/components/editor/file-tree/use-file-search.ts +35 -21
  199. package/src/components/editor/output/MarimoTracebackOutput.tsx +28 -22
  200. package/src/components/editor/output/console/ConsoleOutput.tsx +8 -18
  201. package/src/components/icons/__tests__/large-spinner.test.tsx +39 -0
  202. package/src/components/icons/large-spinner.tsx +3 -14
  203. package/src/components/pages/home-page.tsx +4 -4
  204. package/src/components/slides/slide-cell-view.tsx +1 -1
  205. package/src/components/tracing/tracing.tsx +2 -2
  206. package/src/core/attachments/__tests__/toast.test.tsx +58 -0
  207. package/src/core/attachments/state.ts +5 -8
  208. package/src/core/attachments/toast.ts +15 -0
  209. package/src/core/cells/__tests__/error-report.test.ts +53 -3
  210. package/src/core/cells/__tests__/notebook-outline.test.ts +113 -0
  211. package/src/core/cells/__tests__/session.test.ts +18 -3
  212. package/src/core/cells/cells.ts +2 -1
  213. package/src/core/cells/error-report.ts +14 -6
  214. package/src/core/cells/pending-delete-service.ts +2 -6
  215. package/src/core/cells/session.ts +19 -1
  216. package/src/core/config/pair.ts +5 -3
  217. package/src/core/datasets/__tests__/data-source-discovery.test.ts +4 -4
  218. package/src/core/datasets/data-source-discovery.ts +4 -6
  219. package/src/core/edit-app.tsx +2 -2
  220. package/src/core/host/AGENTS.md +11 -2
  221. package/src/core/host/__tests__/attachments.test.ts +56 -0
  222. package/src/core/host/__tests__/document.test.ts +126 -0
  223. package/src/core/host/__tests__/events.test.ts +49 -0
  224. package/src/core/host/__tests__/fixtures.ts +41 -0
  225. package/src/core/host/__tests__/notebook-client.test.ts +496 -0
  226. package/src/core/host/connection.ts +81 -0
  227. package/src/core/host/events.ts +68 -0
  228. package/src/core/host/notebook-client.ts +316 -0
  229. package/src/core/host/resources/attachments.ts +19 -0
  230. package/src/core/host/resources/document.ts +101 -0
  231. package/src/core/hotkeys/actions.ts +2 -4
  232. package/src/core/islands/__tests__/bootstrap.test.ts +0 -26
  233. package/src/core/islands/bootstrap.ts +0 -1
  234. package/src/core/network/connection.ts +4 -2
  235. package/src/core/network/useConnectionNotice.ts +8 -2
  236. package/src/core/run-app.tsx +2 -2
  237. package/src/core/runtime/__tests__/runtime.test.ts +5 -12
  238. package/src/core/runtime/runtime.ts +5 -5
  239. package/src/core/static/files.ts +2 -0
  240. package/src/core/websocket/__tests__/{useMarimoKernelConnection.test.ts → connection-state.test.ts} +15 -17
  241. package/src/core/websocket/__tests__/{useWebSocket.test.ts → createConnectionTransport.test.ts} +1 -1
  242. package/src/core/websocket/__tests__/useNotebookClient.hook.test.tsx +149 -0
  243. package/src/core/websocket/__tests__/{useMarimoKernelConnection.hook.test.tsx → useNotebookSession.hook.test.tsx} +168 -253
  244. package/src/core/websocket/connection-state.ts +70 -0
  245. package/src/core/websocket/state.ts +7 -0
  246. package/src/core/websocket/transports/__tests__/sse.test.ts +18 -4
  247. package/src/core/websocket/transports/create.ts +31 -0
  248. package/src/core/websocket/transports/sse.ts +14 -2
  249. package/src/core/websocket/transports/transport.ts +12 -23
  250. package/src/core/websocket/types.ts +9 -39
  251. package/src/core/websocket/useNotebookClient.ts +78 -0
  252. package/src/core/websocket/{useMarimoKernelConnection.tsx → useNotebookSession.tsx} +53 -233
  253. package/src/hooks/__tests__/useAsyncData.test.tsx +148 -0
  254. package/src/hooks/__tests__/useDebounce.test.tsx +51 -0
  255. package/src/hooks/__tests__/useDeepCompareMemoize.test.tsx +39 -0
  256. package/src/hooks/__tests__/useDelayElapsed.test.tsx +28 -0
  257. package/src/hooks/__tests__/useInternalStateWithSync.test.tsx +20 -0
  258. package/src/hooks/__tests__/useScript.test.tsx +77 -0
  259. package/src/hooks/__tests__/useStateForKey.test.tsx +257 -0
  260. package/src/hooks/useAsyncData.ts +3 -3
  261. package/src/hooks/useDataSourceDiscovery.ts +6 -4
  262. package/src/hooks/useDebounce.ts +5 -8
  263. package/src/hooks/useDeepCompareMemoize.ts +1 -1
  264. package/src/hooks/useDelayElapsed.ts +14 -10
  265. package/src/hooks/useElapsedTime.ts +3 -3
  266. package/src/hooks/useInputHistory.ts +4 -7
  267. package/src/hooks/useInternalStateWithSync.ts +6 -6
  268. package/src/hooks/useResizeObserver.ts +4 -7
  269. package/src/hooks/useScript.ts +67 -70
  270. package/src/hooks/useStateForKey.ts +93 -0
  271. package/src/mount.tsx +3 -3
  272. package/src/plugins/core/registerReactComponent.tsx +1 -1
  273. package/src/plugins/impl/DataTablePlugin.tsx +14 -15
  274. package/src/plugins/impl/DropdownPlugin.tsx +2 -6
  275. package/src/plugins/impl/FileBrowserPlugin.tsx +4 -16
  276. package/src/plugins/impl/FormPlugin.tsx +1 -1
  277. package/src/plugins/impl/SliderPlugin.tsx +6 -8
  278. package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +201 -1
  279. package/src/plugins/impl/__tests__/FileBrowserPlugin.test.tsx +45 -0
  280. package/src/plugins/impl/__tests__/SliderPlugin.test.tsx +24 -3
  281. package/src/plugins/impl/anywidget/__tests__/registry.test.ts +2 -0
  282. package/src/plugins/impl/anywidget/__tests__/widget-binding.test.ts +241 -2
  283. package/src/plugins/impl/anywidget/runtime.ts +21 -9
  284. package/src/plugins/impl/anywidget/widget-binding.ts +160 -82
  285. package/src/plugins/impl/chat/__tests__/chat-prompts.test.tsx +93 -0
  286. package/src/plugins/impl/chat/chat-ui.tsx +10 -9
  287. package/src/plugins/impl/code/any-language-editor.tsx +2 -2
  288. package/src/plugins/impl/data-editor/glide-data-editor.tsx +8 -5
  289. package/src/plugins/impl/data-editor/glide-utils.ts +3 -1
  290. package/src/plugins/impl/mpl-interactive/MplInteractivePlugin.tsx +5 -3
  291. package/src/plugins/impl/panel/PanelPlugin.tsx +15 -16
  292. package/src/plugins/impl/panel/__tests__/utils.test.ts +39 -1
  293. package/src/plugins/impl/panel/utils.ts +17 -0
  294. package/src/plugins/impl/vega/make-selectable.ts +3 -6
  295. package/src/stories/__tests__/editor-story.test.tsx +33 -0
  296. package/src/stories/editor.stories.tsx +24 -17
  297. package/src/utils/__tests__/arrays.test.ts +35 -0
  298. package/src/utils/__tests__/numbers.test.ts +48 -1
  299. package/src/utils/__tests__/objects.test.ts +21 -0
  300. package/src/utils/arrays.ts +10 -0
  301. package/src/utils/numbers.ts +28 -17
  302. package/src/utils/objects.ts +3 -7
  303. package/src/utils/once.ts +19 -19
  304. package/dist/assets/CellStatus-nZpuDkm7.js +0 -1
  305. package/dist/assets/JsonOutput-d-k8v_17.js +0 -11
  306. package/dist/assets/MarimoErrorOutput-CLYUxiKW.js +0 -10
  307. package/dist/assets/RunButton-CFQUNN5c.js +0 -1
  308. package/dist/assets/ai-model-dropdown-C6GHHEdR.js +0 -5
  309. package/dist/assets/any-language-editor-BLCAD3yx.js +0 -1
  310. package/dist/assets/arrays-BqUx5USx.js +0 -1
  311. package/dist/assets/cell-link-1CynTVGQ.js +0 -1
  312. package/dist/assets/chat-panel-DDDTnLvB.js +0 -4
  313. package/dist/assets/chat-ui-nV_m4Btr.js +0 -4
  314. package/dist/assets/code-visibility-B-_GvwR5.js +0 -1
  315. package/dist/assets/coerce-CpE6kUdX.js +0 -1
  316. package/dist/assets/command-palette-BZ13RKDJ.js +0 -1
  317. package/dist/assets/config-DX8Y2WbJ.js +0 -1
  318. package/dist/assets/config-FzDtjF1-.js +0 -1
  319. package/dist/assets/config-VPSq73n8.js +0 -1
  320. package/dist/assets/feedback-button-DlqZkJwz.js +0 -1
  321. package/dist/assets/file-explorer-panel-D3CphidS.js +0 -1
  322. package/dist/assets/index-CXIE-QEG.css +0 -2
  323. package/dist/assets/index-DnIgeCyQ.js +0 -29
  324. package/dist/assets/layout-DBuMeWh0.js +0 -9
  325. package/dist/assets/linear-0eij_aAG.js +0 -1
  326. package/dist/assets/links-DkkL9DVa.js +0 -1
  327. package/dist/assets/numbers-Mk3-CcoU.js +0 -1
  328. package/dist/assets/packages-panel-9Qs1npCZ.js +0 -1
  329. package/dist/assets/panels-B9x935By.js +0 -4
  330. package/dist/assets/precisionRound-Bq_nmf5u.js +0 -1
  331. package/dist/assets/process-output-DNFKMODr.js +0 -1
  332. package/dist/assets/react-vega-C3rYGtP6.js +0 -189
  333. package/dist/assets/run-page-CjdGaic3.js +0 -1
  334. package/dist/assets/tooltip-mT3CGest.js +0 -61
  335. package/dist/assets/tracing-BARlJ_Dk.js +0 -1
  336. package/dist/assets/useAsyncData-C7aJJz0Y.js +0 -1
  337. package/dist/assets/useBoolean-BDD6_xQQ.js +0 -1
  338. package/dist/assets/useDebounce-B_ccBZTp.js +0 -1
  339. package/dist/assets/vega-component-CnadTgoE.js +0 -1
  340. package/dist/assets/vega-loader.browser-CLRkNgm7.js +0 -6
  341. package/src/core/host/__tests__/protocol.test.ts +0 -214
  342. package/src/core/host/protocol.ts +0 -1943
  343. package/src/core/websocket/useWebSocket.tsx +0 -88
@@ -6,26 +6,29 @@ import { sanitizeHtml } from "@/plugins/core/sanitize-html";
6
6
  import { parseHtmlContent } from "@/utils/dom";
7
7
  import type { CellId } from "./ids";
8
8
 
9
- export interface CellOutputHandoff<Output> {
9
+ export interface CellOutputMessage<Output> {
10
10
  type: "cell-output";
11
11
  cell_id: CellId;
12
12
  code?: string;
13
13
  output: Output;
14
14
  }
15
15
 
16
- export interface ErrorHandoffOutput {
16
+ export interface ErrorMessageOutput {
17
17
  type: "error";
18
+ instructions: string;
18
19
  message: string;
19
20
  traceback?: string;
20
21
  }
21
22
 
23
+ export const ERROR_MESSAGE_INSTRUCTIONS = "Fix this notebook error.";
24
+
22
25
  export function buildErrorReport(
23
26
  cellId: CellId,
24
27
  evidence: { code?: string } & (
25
28
  | { errors: MarimoError[] }
26
- | { traceback: string }
29
+ | { traceback: string; tracebackFormat: "html" | "text" }
27
30
  ),
28
- ): CellOutputHandoff<ErrorHandoffOutput> {
31
+ ): CellOutputMessage<ErrorMessageOutput> {
29
32
  let message: string;
30
33
  let traceback: string;
31
34
  if ("errors" in evidence) {
@@ -38,18 +41,23 @@ export function buildErrorReport(
38
41
  )
39
42
  .join("\n\n");
40
43
  } else {
41
- traceback = parseHtmlContent(sanitizeHtml(evidence.traceback));
44
+ traceback =
45
+ evidence.tracebackFormat === "html"
46
+ ? parseHtmlContent(sanitizeHtml(evidence.traceback))
47
+ : evidence.traceback;
42
48
  message = traceback.split("\n").findLast((line) => line.length > 0) ?? "";
43
49
  }
44
50
 
51
+ // Put repair intent before evidence so it survives prefix truncation.
45
52
  return {
46
53
  type: "cell-output",
47
54
  cell_id: cellId,
48
- ...(evidence.code === undefined ? {} : { code: evidence.code }),
49
55
  output: {
50
56
  type: "error",
57
+ instructions: ERROR_MESSAGE_INSTRUCTIONS,
51
58
  message,
52
59
  ...(traceback ? { traceback } : {}),
53
60
  },
61
+ ...(evidence.code === undefined ? {} : { code: evidence.code }),
54
62
  };
55
63
  }
@@ -134,12 +134,8 @@ export function usePendingDeleteService() {
134
134
  submit({ cellIds, deleteCell, deleteManyCells, store });
135
135
  },
136
136
  clear,
137
- get idle() {
138
- return entries.size === 0;
139
- },
140
- get shouldConfirmDelete() {
141
- return entries.size > 1;
142
- },
137
+ idle: entries.size === 0,
138
+ shouldConfirmDelete: entries.size > 1,
143
139
  };
144
140
  }
145
141
 
@@ -206,8 +206,20 @@ function createCellRuntimeFromSession(
206
206
  channel: "marimo-error",
207
207
  data: [
208
208
  {
209
- type: "unknown",
209
+ type: "exception",
210
+ exception_type: primaryOutput.ename,
210
211
  msg: primaryOutput.evalue,
212
+ raising_cell: null,
213
+ traceback:
214
+ primaryOutput.traceback.length === 0
215
+ ? null
216
+ : primaryOutput.traceback
217
+ .map((line, index, lines) =>
218
+ index < lines.length - 1 && !line.endsWith("\n")
219
+ ? `${line}\n`
220
+ : line,
221
+ )
222
+ .join(""),
211
223
  },
212
224
  ],
213
225
  mimetype: "application/vnd.marimo+error",
@@ -240,6 +252,9 @@ function createCellRuntimeFromSession(
240
252
  channel: "media",
241
253
  data: consoleOutput.data,
242
254
  mimetype: consoleOutput.mimetype,
255
+ ...(consoleOutput.traceback_format === undefined
256
+ ? {}
257
+ : { traceback_format: consoleOutput.traceback_format }),
243
258
  timestamp: DEFAULT_TIMESTAMP,
244
259
  ...(consoleOutput.code === undefined
245
260
  ? {}
@@ -251,6 +266,9 @@ function createCellRuntimeFromSession(
251
266
  channel: consoleOutput.name === "stderr" ? "stderr" : "stdout",
252
267
  data: consoleOutput.text,
253
268
  mimetype: consoleOutput.mimetype ?? "text/plain",
269
+ ...(consoleOutput.traceback_format === undefined
270
+ ? {}
271
+ : { traceback_format: consoleOutput.traceback_format }),
254
272
  timestamp: DEFAULT_TIMESTAMP,
255
273
  ...(consoleOutput.code === undefined
256
274
  ? {}
@@ -3,16 +3,18 @@
3
3
  import { atom } from "jotai";
4
4
  import { z } from "zod";
5
5
 
6
- export const pairPreviewSchema = z.object({
6
+ export const pairConfigSchema = z.object({
7
7
  command: z.string(),
8
+ hostEnabled: z.boolean().default(false),
8
9
  templates: z.object({
9
10
  prompt: z.string(),
11
+ notebook: z.string(),
10
12
  session: z.string(),
11
13
  token_file: z.string(),
12
14
  token: z.string(),
13
15
  }),
14
16
  });
15
17
 
16
- export type PairPreviewConfig = z.infer<typeof pairPreviewSchema>;
18
+ export type PairConfig = z.infer<typeof pairConfigSchema>;
17
19
 
18
- export const pairPreviewAtom = atom<PairPreviewConfig | undefined>(undefined);
20
+ export const pairConfigAtom = atom<PairConfig | undefined>(undefined);
@@ -185,8 +185,8 @@ describe("isDetectedSourceConnected", () => {
185
185
  });
186
186
  const emptySnapshot = {
187
187
  dialects: [] as string[],
188
- storageProtocols: [] as string[],
189
- storageBackendTypes: [] as string[],
188
+ storageProtocols: new Set<string>(),
189
+ storageBackendTypes: new Set<string>(),
190
190
  };
191
191
 
192
192
  it("does not hide a source when nothing is connected", () => {
@@ -242,7 +242,7 @@ describe("isDetectedSourceConnected", () => {
242
242
  expect(
243
243
  isDetectedSourceConnected(s3, {
244
244
  ...emptySnapshot,
245
- storageProtocols: ["s3"],
245
+ storageProtocols: new Set(["s3"]),
246
246
  }),
247
247
  ).toBe(true);
248
248
  });
@@ -262,7 +262,7 @@ describe("isDetectedSourceConnected", () => {
262
262
  expect(
263
263
  isDetectedSourceConnected(hf, {
264
264
  ...emptySnapshot,
265
- storageBackendTypes: ["huggingface"],
265
+ storageBackendTypes: new Set(["huggingface"]),
266
266
  }),
267
267
  ).toBe(true);
268
268
  });
@@ -27,8 +27,8 @@ export function matchesDiscoveryGroup(
27
27
  */
28
28
  export interface LiveConnectionSnapshot {
29
29
  dialects: readonly string[];
30
- storageProtocols: readonly string[];
31
- storageBackendTypes: readonly string[];
30
+ storageProtocols: ReadonlySet<string>;
31
+ storageBackendTypes: ReadonlySet<string>;
32
32
  }
33
33
 
34
34
  /**
@@ -47,15 +47,13 @@ export function isDetectedSourceConnected(
47
47
  }
48
48
 
49
49
  if (
50
- match.protocols.some((protocol) =>
51
- snapshot.storageProtocols.includes(protocol),
52
- )
50
+ match.protocols.some((protocol) => snapshot.storageProtocols.has(protocol))
53
51
  ) {
54
52
  return true;
55
53
  }
56
54
 
57
55
  return match.backendTypes.some((backendType) =>
58
- snapshot.storageBackendTypes.includes(backendType),
56
+ snapshot.storageBackendTypes.has(backendType),
59
57
  );
60
58
  }
61
59
 
@@ -41,7 +41,7 @@ import { viewStateAtom } from "./mode";
41
41
  import { useRequestClient } from "./network/requests";
42
42
  import { useFilename } from "./saving/filename";
43
43
  import { lastSavedNotebookAtom } from "./saving/state";
44
- import { useMarimoKernelConnection } from "./websocket/useMarimoKernelConnection";
44
+ import { useNotebookSession } from "./websocket/useNotebookSession";
45
45
 
46
46
  interface AppProps {
47
47
  /**
@@ -83,7 +83,7 @@ export const EditApp: React.FC<AppProps> = ({
83
83
  };
84
84
  }, []);
85
85
 
86
- const { connection, reconnect } = useMarimoKernelConnection({
86
+ const { connection, reconnect } = useNotebookSession({
87
87
  autoInstantiate: userConfig.runtime.auto_instantiate,
88
88
  setCells: (cells, layout) => {
89
89
  setCells(cells);
@@ -1,6 +1,15 @@
1
1
  # Host core
2
2
 
3
- Production code may import host modules, `jotai/vanilla`, `zod`, and
4
- `@/utils/Logger`. Enforced by `marimo/host-imports`. Tests are exempt.
3
+ Production code may import host modules, `@marimo-team/client`,
4
+ `jotai/vanilla`, `zod`, and `@/utils/Logger`. Enforced by
5
+ `marimo/host-imports`. Tests are exempt.
6
+
7
+ The protocol schemas and types live in `@marimo-team/client/protocol`.
8
+ The native `marimo` event is a marimo extension and is defined in `events.ts`.
5
9
 
6
10
  Define interfaces here and inject application behavior through adapters.
11
+
12
+ `stream.ts` reduces stream events into one state. Each kind of object is a
13
+ `Resource` in `resources/` with pure handlers keyed by event name. The stream
14
+ handles phase, notebook identity, and control events itself. The client holds
15
+ one state atom and exposes derived atoms.
@@ -0,0 +1,56 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { describe, expect, it } from "vitest";
4
+ import { attachments } from "../resources/attachments";
5
+ import { createReducer } from "@marimo-team/client/reducer";
6
+ import { fixtureEvents } from "./fixtures";
7
+
8
+ const reducer = createReducer({ attachments });
9
+ const snapshot = fixtureEvents("notebook-events.sse");
10
+ const records = snapshot.flatMap((event) =>
11
+ event.event === "attachment" ? [event.data.attachment] : [],
12
+ );
13
+ const confirmed = () => snapshot.reduce(reducer.reduce, reducer.initial);
14
+
15
+ describe("attachment resource", () => {
16
+ it("collects snapshot records, replaces by id, and removes attachments", () => {
17
+ const state = confirmed();
18
+ expect(state.resources.attachments).toEqual(records);
19
+ const [browser, agent] = records;
20
+ const renamed = { ...agent, name: "Renamed agent" };
21
+ const updated = reducer.reduce(state, {
22
+ event: "attachment",
23
+ id: "98",
24
+ data: { attachment: renamed },
25
+ });
26
+ expect(updated.resources.attachments).toEqual([browser, renamed]);
27
+ expect(state.resources.attachments).toEqual(records);
28
+
29
+ const removed = reducer.reduce(updated, {
30
+ event: "attachment.removed",
31
+ id: "99",
32
+ data: { id: browser.id, time: "2026-10-01T09:30:00Z" },
33
+ });
34
+ expect(removed.resources.attachments).toEqual([renamed]);
35
+ const repeated = reducer.reduce(removed, {
36
+ event: "attachment.removed",
37
+ id: "100",
38
+ data: { id: browser.id, time: "2026-10-01T09:30:00Z" },
39
+ });
40
+ expect(repeated.resources.attachments).toBe(removed.resources.attachments);
41
+ });
42
+
43
+ it("rebuilds records after reset", () => {
44
+ const state = confirmed();
45
+ const reset = reducer.reduce(state, { event: "reset", data: {} });
46
+ expect(reset.resources.attachments).toEqual([]);
47
+ const replacement = snapshot
48
+ .filter(
49
+ (event) =>
50
+ event.event !== "attachment" ||
51
+ event.data.attachment.id === records[0].id,
52
+ )
53
+ .reduce(reducer.reduce, reset);
54
+ expect(replacement.resources.attachments).toEqual([records[0]]);
55
+ });
56
+ });
@@ -0,0 +1,126 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { describe, expect, it } from "vitest";
4
+ import type * as Protocol from "@marimo-team/client/protocol";
5
+ import { document } from "../resources/document";
6
+ import { createReducer } from "@marimo-team/client/reducer";
7
+ import { fixtureEvents } from "./fixtures";
8
+
9
+ const reducer = createReducer({ document });
10
+ const snapshot = fixtureEvents("notebook-events.sse");
11
+ const edits = fixtureEvents("edits.sse");
12
+
13
+ function replay(
14
+ events: readonly Protocol.EventOutput[],
15
+ from = reducer.initial,
16
+ ) {
17
+ return events.reduce((state, event) => reducer.reduce(state, event), from);
18
+ }
19
+
20
+ const confirmed = () => replay(snapshot);
21
+ const current = (state: ReturnType<typeof replay>) =>
22
+ state.resources.document.current;
23
+
24
+ describe("document resource", () => {
25
+ it("publishes the snapshot at ready, then each completed edit and save", () => {
26
+ const ready = snapshot.findIndex((event) => event.event === "ready");
27
+ const opening = replay(snapshot.slice(0, ready));
28
+ expect(current(opening)).toBeNull();
29
+ const first = current(replay(snapshot.slice(ready), opening));
30
+ expect(first?.revision.order).toEqual(["c1", "c3", "k7f2"]);
31
+ expect([...first!.cells.keys()]).toEqual(first?.revision.order);
32
+
33
+ const [edit, save] = edits.filter((event) => event.event === "document");
34
+ const pending = replay(
35
+ edits.filter((event) => event.event !== "document"),
36
+ confirmed(),
37
+ );
38
+ expect(current(pending)).toEqual(first);
39
+ const changed = reducer.reduce(pending, edit);
40
+ expect(current(changed)?.revision).toEqual(edit.data.document);
41
+ expect([...current(changed)!.cells.keys()].toSorted()).toEqual(
42
+ edit.data.document.order.toSorted(),
43
+ );
44
+ const saved = current(reducer.reduce(changed, save));
45
+ expect(saved?.revision).toEqual(save.data.document);
46
+ expect(saved?.cells.get("c3")?.code).toBe("x = 22");
47
+ expect(saved?.cells.has("k7f2")).toBe(false);
48
+ expect(saved?.revision.saved_etag).toBe(saved?.revision.etag);
49
+ expect(first?.cells.get("c3")?.code).toBe("x = 21");
50
+ expect(first?.cells.has("k7f2")).toBe(true);
51
+ });
52
+
53
+ it("applies order-only changes and distinguishes an empty notebook from no document", () => {
54
+ const state = confirmed();
55
+ const revision = current(state)!.revision;
56
+ const reordered = {
57
+ ...revision,
58
+ order: revision.order.toReversed(),
59
+ version: revision.version + 1,
60
+ };
61
+ const changed = reducer.reduce(state, {
62
+ event: "document",
63
+ data: { document: reordered },
64
+ id: "200",
65
+ });
66
+ expect(current(changed)).toEqual({
67
+ revision: reordered,
68
+ cells: current(state)?.cells,
69
+ });
70
+
71
+ const empty = { ...revision, order: [] };
72
+ const opening = reducer.reduce(reducer.initial, {
73
+ event: "document",
74
+ data: { document: empty },
75
+ });
76
+ expect(current(opening)).toBeNull();
77
+ expect(
78
+ current(reducer.reduce(opening, { event: "ready", data: {}, id: "0" })),
79
+ ).toEqual({ revision: empty, cells: new Map() });
80
+ expect(
81
+ current(
82
+ reducer.reduce(reducer.initial, { event: "ready", data: {}, id: "0" }),
83
+ ),
84
+ ).toBeNull();
85
+ });
86
+
87
+ it("keeps the document while a live update is pending and after a replay completes it", () => {
88
+ const state = confirmed();
89
+ const [cell, , removed, published] = edits;
90
+ if (published.event !== "document") {
91
+ throw new Error("Fixture does not publish a document");
92
+ }
93
+ const partial = replay([cell, removed], state);
94
+ expect(current(partial)).toBe(current(state));
95
+ const completed = replay(
96
+ [published, { event: "ready", data: {}, id: "113" }],
97
+ partial,
98
+ );
99
+ expect(current(completed)?.revision).toEqual(published.data.document);
100
+ });
101
+
102
+ it("removes a notebook without waiting for individual cell removals", () => {
103
+ const removed = reducer.reduce(confirmed(), {
104
+ event: "notebook.removed",
105
+ data: { id: "n1", time: "2026-10-01T09:30:00Z" },
106
+ id: "200",
107
+ });
108
+ expect(current(removed)).toBeNull();
109
+ });
110
+
111
+ it("rejects records that cannot form a complete revision", () => {
112
+ const state = confirmed();
113
+ const revision = current(state)!.revision;
114
+ expect(() =>
115
+ reducer.reduce(state, {
116
+ event: "document",
117
+ data: { document: { ...revision, order: ["missing"] } },
118
+ id: "200",
119
+ }),
120
+ ).toThrow("Document records do not match");
121
+ const partial = reducer.reduce(state, edits[0]);
122
+ expect(() =>
123
+ reducer.reduce(partial, { event: "ready", data: {}, id: "200" }),
124
+ ).toThrow("incomplete document update");
125
+ });
126
+ });
@@ -0,0 +1,49 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { describe, expect, expectTypeOf, it } from "vitest";
4
+ import { z } from "zod";
5
+ import { type NotebookEvent, parseNotebookEvent } from "../events";
6
+
7
+ describe("notebook messages", () => {
8
+ it("normalizes session and host envelopes for native marimo messages", () => {
9
+ const notification = {
10
+ op: "variables",
11
+ variables: [{ name: "x", value: Number.NaN }],
12
+ };
13
+ expectTypeOf<
14
+ Extract<NotebookEvent, { event: "marimo" }>["data"]["marimo"]
15
+ >().toBeUnknown();
16
+ const host = { event: "marimo", data: { marimo: notification } };
17
+ expect(parseNotebookEvent({ data: notification })).toEqual(host);
18
+ expect(parseNotebookEvent({ ...host, id: "42" })).toEqual({
19
+ ...host,
20
+ id: "42",
21
+ });
22
+ });
23
+
24
+ it("retains unknown event cursors without interpreting their payloads", () => {
25
+ expect(
26
+ parseNotebookEvent({ event: "future", id: "42", data: null }),
27
+ ).toEqual({ event: "unknown", name: "future", id: "42" });
28
+ });
29
+
30
+ it("decodes known events outside the client's subscription filters", () => {
31
+ const event = {
32
+ event: "run.removed",
33
+ id: "42",
34
+ data: { id: "r1", time: "2026-10-19T12:00:00Z" },
35
+ };
36
+ expect(parseNotebookEvent(event)).toEqual(event);
37
+ });
38
+
39
+ it.each([
40
+ { event: "cell", id: "42", data: { cell: { id: "c1" } } },
41
+ { event: "run.removed", id: "42", data: {} },
42
+ { event: "ready", data: {} },
43
+ { event: "marimo", data: {} },
44
+ {},
45
+ null,
46
+ ])("rejects malformed known messages: %j", (message) => {
47
+ expect(() => parseNotebookEvent(message)).toThrow(z.ZodError);
48
+ });
49
+ });
@@ -0,0 +1,41 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import NodeFs from "node:fs";
4
+ import NodeUrl from "node:url";
5
+ import * as Protocol from "@marimo-team/client/protocol";
6
+
7
+ /** Read the protocol's portable examples as transport messages. */
8
+ export function fixtureMessages(name: string) {
9
+ const path = new NodeUrl.URL(
10
+ `../../../../../protocols/host/fixtures/${name}`,
11
+ import.meta.url,
12
+ );
13
+ return NodeFs.readFileSync(path, "utf8")
14
+ .split(/\r?\n\r?\n/)
15
+ .flatMap((frame) => {
16
+ const event = /^event: (.+)$/m.exec(frame)?.[1];
17
+ if (!event) {
18
+ return [];
19
+ }
20
+ const data = frame
21
+ .split(/\r?\n/)
22
+ .filter((line) => line.startsWith("data:"))
23
+ .map((line) => line.slice(5).trimStart())
24
+ .join("\n");
25
+ const payload: unknown = JSON.parse(data);
26
+ return [
27
+ {
28
+ event,
29
+ id: /^id: (.+)$/m.exec(frame)?.[1],
30
+ data: payload,
31
+ },
32
+ ];
33
+ });
34
+ }
35
+
36
+ /** Read the protocol's portable examples as decoded protocol events. */
37
+ export function fixtureEvents(name: string): Protocol.EventOutput[] {
38
+ return fixtureMessages(name).map(({ id, ...message }) =>
39
+ Protocol.Event.parse(id === undefined ? message : { ...message, id }),
40
+ );
41
+ }