@marimo-team/frontend 0.25.3-dev4 → 0.25.3-dev40

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 (288) hide show
  1. package/dist/assets/CellStatus-BGIWF94J.js +1 -0
  2. package/dist/assets/{ConnectedDataExplorerComponent-t8fH_TNc.js → ConnectedDataExplorerComponent-D9UK8o9A.js} +1 -1
  3. package/dist/assets/{ErrorBoundary-Gl5nCA8C.js → ErrorBoundary-hliG40Pk.js} +2 -2
  4. package/dist/assets/JsonOutput-9tmC-p6h.js +11 -0
  5. package/dist/assets/{LazyAnyLanguageCodeMirror-CKcCbG2G.js → LazyAnyLanguageCodeMirror-BPYE76jR.js} +2 -2
  6. package/dist/assets/MarimoErrorOutput-BaHdbtck.js +10 -0
  7. package/dist/assets/{RenderHTML-BbOqmHy3.js → RenderHTML-B1FzB5NY.js} +1 -1
  8. package/dist/assets/{RunButton-CK3LhnP5.js → RunButton-DpGokdF1.js} +1 -1
  9. package/dist/assets/{add-cell-with-ai-8UBC0xwR.js → add-cell-with-ai-BYWG0iKO.js} +13 -13
  10. package/dist/assets/{add-connection-dialog-DXQnd6eB.js → add-connection-dialog-BWbUVPJk.js} +2 -2
  11. package/dist/assets/{agent-panel-DdkVL-Kx.js → agent-panel-DKNiQrAl.js} +5 -5
  12. package/dist/assets/ai-model-dropdown-CIg9eu47.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-BKEG4x9u.js → app-config-button-BvkL2eSY.js} +1 -1
  16. package/dist/assets/arrays-0R2reF2y.js +1 -0
  17. package/dist/assets/{blob-D5nfNHOo.js → blob-BQfLiwKr.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-32lVhib7.js → cell-editor-BKKUv4d5.js} +9 -9
  22. package/dist/assets/{cell-link-LSth_8hT.js → cell-link-B_QkRkm-.js} +1 -1
  23. package/dist/assets/{cells-BRkCaSsh.js → cells-CdH98VSn.js} +31 -31
  24. package/dist/assets/{chat-display-Dnagy4Nv.js → chat-display-CNbRSebo.js} +1 -1
  25. package/dist/assets/{chat-panel-DN9vsbgS.js → chat-panel-HbEKDf0c.js} +2 -2
  26. package/dist/assets/{chat-ui-Df8Vcj6n.js → chat-ui-BwMev8Bf.js} +1 -1
  27. package/dist/assets/{circle-DXf-VWZG.js → circle-B6D_6D8w.js} +1 -1
  28. package/dist/assets/code-visibility-sQ37CBNo.js +1 -0
  29. package/dist/assets/coerce-DWjFeXOY.js +1 -0
  30. package/dist/assets/{column-preview-CHK_DNrs.js → column-preview-CsEBzIUE.js} +1 -1
  31. package/dist/assets/command-palette-BzZ86pUz.js +1 -0
  32. package/dist/assets/{common-Cu6CDbuw.js → common-S2v8dsEy.js} +1 -1
  33. package/dist/assets/{components-CGw16R_N.js → components-D-TnFWjo.js} +1 -1
  34. package/dist/assets/{components-CP_UoFtT.js → components-DrU8dOzy.js} +1 -1
  35. package/dist/assets/{components-CXfO-F47.js → components-lDm0qi2t.js} +1 -1
  36. package/dist/assets/config-BoBwH7CU.js +1 -0
  37. package/dist/assets/config-CYsbZpwa.js +104 -0
  38. package/dist/assets/config-DGX7JYor.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-Cq6x_5ML.js → datasource-BA7YF4eo.js} +1 -1
  43. package/dist/assets/{dates-CfmdTL6B.js → dates-CQMwFko2.js} +1 -1
  44. package/dist/assets/{dependency-graph-panel--Kgtktcw.js → dependency-graph-panel-DsjI61Qa.js} +1 -1
  45. package/dist/assets/{dist-iaThVDkA.js → dist-jlhMTKo_.js} +1 -1
  46. package/dist/assets/{documentation-panel-BHmbeznw.js → documentation-panel-hk3IdeXy.js} +1 -1
  47. package/dist/assets/{download-DCJXy66H.js → download-CCZkq0y4.js} +1 -1
  48. package/dist/assets/{dropdown-menu-C1rnl-0o.js → dropdown-menu-DS6umAnH.js} +1 -1
  49. package/dist/assets/edit-page-BgS6tjth.js +9 -0
  50. package/dist/assets/{error-banner-Cd69vvyX.js → error-banner-B5kzVf_0.js} +1 -1
  51. package/dist/assets/{error-entries-Dk3VZ-OK.js → error-entries-BVXrGOTZ.js} +1 -1
  52. package/dist/assets/{error-panel-BPRYmC5J.js → error-panel-DYethUUR.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-DDzObtF2.js +1 -0
  56. package/dist/assets/{feedback-button-Bik2-e33.js → feedback-button-DaNc3uJa.js} +2 -2
  57. package/dist/assets/{file-explorer-panel-BmekFu7T.js → file-explorer-panel-CqnzSSYc.js} +1 -1
  58. package/dist/assets/{file-name-input-Cdc-TVqO.js → file-name-input-D4AvwZV9.js} +1 -1
  59. package/dist/assets/{floating-outline-XD9FAV-f.js → floating-outline-CTy-Iakk.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-Dm6mGwBi.js → gallery-page-ByjlvM6k.js} +1 -1
  64. package/dist/assets/{ganttDiagram-EL5Y4UJY-2n7Ig5Jm.js → ganttDiagram-EL5Y4UJY-CYqkCfFG.js} +1 -1
  65. package/dist/assets/{glide-data-editor-J9CAb70d.js → glide-data-editor-CABBj2b0.js} +2 -2
  66. package/dist/assets/{glide-utils-CIrAxdjv.js → glide-utils-HFqhvWHI.js} +2 -2
  67. package/dist/assets/{home-page-s3LUizSn.js → home-page-DejRSjeJ.js} +3 -3
  68. package/dist/assets/hooks-BJgCcVXZ.js +1 -0
  69. package/dist/assets/{html-to-image-DK9Zl1lU.js → html-to-image-D_0rd3_B.js} +1 -1
  70. package/dist/assets/index-ChfDZJzP.css +2 -0
  71. package/dist/assets/index-PDJQd7X6.js +30 -0
  72. package/dist/assets/{input-BoBxR50a.js → input-ByHt6aoV.js} +1 -1
  73. package/dist/assets/{kiosk-mode-BZECfg9k.js → kiosk-mode-BGmMSc7A.js} +1 -1
  74. package/dist/assets/{layout-B_GMOySr.js → layout-Ct0rjb-5.js} +5 -5
  75. package/dist/assets/linear-Bma8J3iM.js +1 -0
  76. package/dist/assets/links-CXcpNIM0.js +1 -0
  77. package/dist/assets/{logs-panel-ABM16htT.js → logs-panel-TyeqEvOY.js} +1 -1
  78. package/dist/assets/{markdown-renderer-BG3sp2rs.js → markdown-renderer-BFyD6Jhs.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-BgEBmqyL.js → name-cell-input-D-0VF3qd.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-DCXQ3_F6.js → outline-panel-DMTjk2WC.js} +1 -1
  86. package/dist/assets/{packages-panel-BMGOmZkR.js → packages-panel-DrT69k9L.js} +1 -1
  87. package/dist/assets/{panel-accordion-state-C1sdPp0m.js → panel-accordion-state-Ban6j---.js} +1 -1
  88. package/dist/assets/panels-BLYQRkEc.js +4 -0
  89. package/dist/assets/precisionRound-BxlUs8G4.js +1 -0
  90. package/dist/assets/process-output-DzpQYlpY.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-Do50PHI8.js → readonly-code-display-Cyn3k5TX.js} +1 -1
  94. package/dist/assets/{readonly-python-code-DCSMIsTX.js → readonly-python-code-DrXDDAnA.js} +1 -1
  95. package/dist/assets/{renderShortcut-BkVJQ_Zh.js → renderShortcut-H60uiHBp.js} +1 -1
  96. package/dist/assets/{reveal-component-CLE6vB1Z.js → reveal-component-_lKImPWP.js} +3 -3
  97. package/dist/assets/{run-page-Depd6lEE.js → run-page-7UNg--Rj.js} +1 -1
  98. package/dist/assets/{runs-BLp5jezv.js → runs-6lJxKV1w.js} +1 -1
  99. package/dist/assets/{runtime-details-state-CSPobEdR.js → runtime-details-state-CKOKhMhv.js} +1 -1
  100. package/dist/assets/{schemas-Dv42wLFq.js → schemas-ChRqMOTL.js} +1 -1
  101. package/dist/assets/{scratchpad-panel-lc6jmpjv.js → scratchpad-panel-C7h4MgqN.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-CmLZsNfp.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-CLiWXc5y.js +1 -0
  107. package/dist/assets/{snippets-panel-Bh1oTw4m.js → snippets-panel-Wrqaorxa.js} +1 -1
  108. package/dist/assets/{spec-BfSBcO5B.js → spec-uCpVvaAR.js} +1 -1
  109. package/dist/assets/{state-uwRvJLb8.js → state-BY4_P6ko.js} +1 -1
  110. package/dist/assets/{state-BAJYRhmO.js → state-C0irhlOZ.js} +1 -1
  111. package/dist/assets/{state-DxQnC_1I.js → state-Ck7FMD5A.js} +1 -1
  112. package/dist/assets/{state-C3oDoyFe.js → state-DaWgt-rn.js} +1 -1
  113. package/dist/assets/state-TVREh4u4.js +3 -0
  114. package/dist/assets/{state-XsPO8hNi.js → state-_uXVmwuN.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-1h011wlH.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-Dp5gyJ7l.js} +3 -3
  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-fUNLyKuj.js +1 -0
  125. package/dist/assets/{tracing-panel-PeRXHD8_.js → tracing-panel-Cx4OqOoP.js} +2 -2
  126. package/dist/assets/{tree-actions-GM5E_bUs.js → tree-actions-B1vsdenY.js} +1 -1
  127. package/dist/assets/{use-restore-focus-BxEGwIbk.js → use-restore-focus-DdS7Tl0F.js} +1 -1
  128. package/dist/assets/{useAddCell-GR7eEQrT.js → useAddCell-DZ-50qEG.js} +1 -1
  129. package/dist/assets/useAsyncData-DhhdOGGd.js +1 -0
  130. package/dist/assets/{useBoolean-BDD6_xQQ.js → useBoolean-5g-bBaYF.js} +1 -1
  131. package/dist/assets/{useCellActionButton-B-9eG5z_.js → useCellActionButton-DINnRstv.js} +1 -1
  132. package/dist/assets/{useDeleteCell-2_bMClHd.js → useDeleteCell-CREJXVNp.js} +1 -1
  133. package/dist/assets/{useDependencyPanelTab-k3C3M18S.js → useDependencyPanelTab-Cw5VT16Q.js} +1 -1
  134. package/dist/assets/{useEventListener-D9cftcHj.js → useEventListener-CjDeLqFC.js} +1 -1
  135. package/dist/assets/{useIframeCapabilities-CwTkczVN.js → useIframeCapabilities-Czr9e4uG.js} +1 -1
  136. package/dist/assets/{useInstallPackage-iX1eufdl.js → useInstallPackage-Cw0dVELh.js} +1 -1
  137. package/dist/assets/{useInterval--x3s1VpI.js → useInterval-DJdQmXnq.js} +1 -1
  138. package/dist/assets/useNotebookActions-B4av4mLc.js +1 -0
  139. package/dist/assets/{useRunCells-B2vUhREg.js → useRunCells-BHBMk9zW.js} +1 -1
  140. package/dist/assets/{useSplitCell-DEb03htp.js → useSplitCell-DAgwZV0q.js} +1 -1
  141. package/dist/assets/{useTheme-COhSHkCj.js → useTheme-DLtPc-39.js} +1 -1
  142. package/dist/assets/{utils-CaQYvazl.js → utils-DfElfBpz.js} +1 -1
  143. package/dist/assets/vega-component-pQE2mKc1.js +1 -0
  144. package/dist/assets/{vega-embed-container-DJPNkHPb.js → vega-embed-container-DtArSJz1.js} +1 -1
  145. package/dist/assets/vega-loader.browser-Bu25TKax.js +6 -0
  146. package/dist/assets/{write-secret-modal-DzKAInL2.js → write-secret-modal-usRSi1bb.js} +1 -1
  147. package/dist/assets/{xychartDiagram-S5SC5T6Z-KbGzG6-u.js → xychartDiagram-S5SC5T6Z-CeRnBS5e.js} +1 -1
  148. package/dist/index.html +76 -77
  149. package/package.json +4 -2
  150. package/src/__tests__/fixtures/{pair-preview.ts → pair-config.ts} +3 -2
  151. package/src/__tests__/mount.test.ts +28 -8
  152. package/src/components/app-config/ai-config.tsx +5 -1
  153. package/src/components/data-table/__tests__/date-popover.test.tsx +72 -0
  154. package/src/components/data-table/__tests__/export-actions.test.tsx +329 -6
  155. package/src/components/data-table/column-visibility-dropdown.tsx +8 -13
  156. package/src/components/data-table/columns.tsx +2 -1
  157. package/src/components/data-table/date-popover.tsx +6 -4
  158. package/src/components/data-table/export-actions.tsx +192 -116
  159. package/src/components/data-table/row-viewer-panel/row-viewer.tsx +2 -2
  160. package/src/components/data-table/schemas.ts +14 -36
  161. package/src/components/editor/Output.tsx +9 -2
  162. package/src/components/editor/__tests__/dynamic-favicon.test.tsx +111 -18
  163. package/src/components/editor/actions/__tests__/pair-with-agent-commands.test.ts +80 -252
  164. package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +186 -34
  165. package/src/components/editor/actions/pair-with-agent-commands.ts +42 -51
  166. package/src/components/editor/actions/pair-with-agent-modal.tsx +96 -36
  167. package/src/components/editor/ai/add-cell-with-ai.tsx +3 -3
  168. package/src/components/editor/chrome/panels/error-panel.tsx +1 -0
  169. package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +366 -0
  170. package/src/components/editor/chrome/wrapper/footer-item.tsx +32 -24
  171. package/src/components/editor/chrome/wrapper/footer-items/attachment-status.tsx +237 -0
  172. package/src/components/editor/chrome/wrapper/footer.tsx +2 -0
  173. package/src/components/editor/connections/components.tsx +1 -1
  174. package/src/components/editor/controls/__tests__/command-palette.test.tsx +53 -0
  175. package/src/components/editor/controls/command-palette.tsx +22 -10
  176. package/src/components/editor/dynamic-favicon.tsx +21 -40
  177. package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +537 -0
  178. package/src/components/editor/errors/send-error-report-button.tsx +69 -0
  179. package/src/components/editor/output/MarimoErrorOutput.tsx +21 -5
  180. package/src/components/editor/output/MarimoTracebackOutput.tsx +36 -21
  181. package/src/components/editor/output/__tests__/ansi-reduce.test.ts +18 -0
  182. package/src/components/editor/output/ansi-reduce.ts +6 -0
  183. package/src/components/pages/home-page.tsx +4 -4
  184. package/src/components/tracing/tracing.tsx +2 -2
  185. package/src/core/attachments/__tests__/toast.test.tsx +58 -0
  186. package/src/core/attachments/state.ts +18 -0
  187. package/src/core/attachments/toast.ts +15 -0
  188. package/src/core/cells/__tests__/cells.test.ts +52 -0
  189. package/src/core/cells/__tests__/error-report.test.ts +139 -0
  190. package/src/core/cells/__tests__/notebook-outline.test.ts +113 -0
  191. package/src/core/cells/__tests__/session.test.ts +50 -1
  192. package/src/core/cells/cells.ts +2 -1
  193. package/src/core/cells/error-report.ts +63 -0
  194. package/src/core/cells/pending-delete-service.ts +2 -6
  195. package/src/core/cells/session.ts +28 -1
  196. package/src/core/config/pair.ts +5 -3
  197. package/src/core/datasets/__tests__/data-source-discovery.test.ts +4 -4
  198. package/src/core/datasets/data-source-discovery.ts +4 -6
  199. package/src/core/edit-app.tsx +2 -2
  200. package/src/core/host/AGENTS.md +5 -0
  201. package/src/core/host/__tests__/attachments.test.ts +57 -0
  202. package/src/core/host/__tests__/document.test.ts +124 -0
  203. package/src/core/host/__tests__/events.test.ts +46 -0
  204. package/src/core/host/__tests__/fixtures.ts +39 -0
  205. package/src/core/host/__tests__/notebook-client.test.ts +433 -0
  206. package/src/core/host/__tests__/protocol.test.ts +15 -0
  207. package/src/core/host/__tests__/stream.test.ts +209 -0
  208. package/src/core/host/connection.ts +81 -0
  209. package/src/core/host/events.ts +61 -0
  210. package/src/core/host/notebook-client.ts +290 -0
  211. package/src/core/host/protocol.ts +11 -6
  212. package/src/core/host/resources/attachments.ts +19 -0
  213. package/src/core/host/resources/document.ts +101 -0
  214. package/src/core/host/resources/resource.ts +47 -0
  215. package/src/core/host/stream.ts +211 -0
  216. package/src/core/hotkeys/actions.ts +2 -4
  217. package/src/core/network/connection.ts +4 -2
  218. package/src/core/run-app.tsx +2 -2
  219. package/src/core/runtime/__tests__/runtime.test.ts +55 -11
  220. package/src/core/runtime/runtime.ts +18 -5
  221. package/src/core/runtime/types.ts +2 -0
  222. package/src/core/websocket/__tests__/{useMarimoKernelConnection.test.ts → connection-state.test.ts} +15 -17
  223. package/src/core/websocket/__tests__/{useWebSocket.test.ts → createConnectionTransport.test.ts} +1 -1
  224. package/src/core/websocket/__tests__/useNotebookClient.hook.test.tsx +170 -0
  225. package/src/core/websocket/__tests__/{useMarimoKernelConnection.hook.test.tsx → useNotebookSession.hook.test.tsx} +181 -65
  226. package/src/core/websocket/connection-state.ts +70 -0
  227. package/src/core/websocket/state.ts +7 -0
  228. package/src/core/websocket/transports/__tests__/sse.test.ts +1 -4
  229. package/src/core/websocket/transports/create.ts +31 -0
  230. package/src/core/websocket/transports/transport.ts +12 -23
  231. package/src/core/websocket/types.ts +9 -39
  232. package/src/core/websocket/useNotebookClient.ts +88 -0
  233. package/src/core/websocket/{useMarimoKernelConnection.tsx → useNotebookSession.tsx} +59 -225
  234. package/src/hooks/__tests__/useAsyncData.test.tsx +148 -0
  235. package/src/hooks/useAsyncData.ts +3 -3
  236. package/src/hooks/useDataSourceDiscovery.ts +6 -4
  237. package/src/hooks/useElapsedTime.ts +3 -3
  238. package/src/mount.tsx +7 -3
  239. package/src/plugins/core/registerReactComponent.tsx +1 -1
  240. package/src/plugins/impl/DropdownPlugin.tsx +2 -6
  241. package/src/plugins/impl/FormPlugin.tsx +1 -1
  242. package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +84 -0
  243. package/src/plugins/impl/code/any-language-editor.tsx +2 -2
  244. package/src/plugins/impl/data-editor/glide-data-editor.tsx +8 -5
  245. package/src/plugins/impl/data-editor/glide-utils.ts +3 -1
  246. package/src/plugins/impl/vega/make-selectable.ts +3 -6
  247. package/src/utils/__tests__/arrays.test.ts +35 -0
  248. package/src/utils/__tests__/numbers.test.ts +48 -1
  249. package/src/utils/__tests__/objects.test.ts +21 -0
  250. package/src/utils/arrays.ts +10 -0
  251. package/src/utils/numbers.ts +28 -17
  252. package/src/utils/objects.ts +3 -7
  253. package/src/utils/once.ts +19 -19
  254. package/dist/assets/CellStatus-BP2mavAq.js +0 -1
  255. package/dist/assets/JsonOutput-DxHKOpPG.js +0 -11
  256. package/dist/assets/MarimoErrorOutput-D60XkCny.js +0 -6
  257. package/dist/assets/ai-model-dropdown-B0z6-zhp.js +0 -5
  258. package/dist/assets/any-language-editor-BLCAD3yx.js +0 -1
  259. package/dist/assets/api-BeC_VSGv.js +0 -1
  260. package/dist/assets/arrays-BqUx5USx.js +0 -1
  261. package/dist/assets/code-visibility-B-_GvwR5.js +0 -1
  262. package/dist/assets/coerce-CpE6kUdX.js +0 -1
  263. package/dist/assets/command-palette-Cf7WX-nr.js +0 -1
  264. package/dist/assets/config-BOKzA3tF.js +0 -1
  265. package/dist/assets/config-DX8Y2WbJ.js +0 -1
  266. package/dist/assets/config-VPSq73n8.js +0 -1
  267. package/dist/assets/edit-page-BT74CF4G.js +0 -9
  268. package/dist/assets/feedback-button-B2gKC5cn.js +0 -1
  269. package/dist/assets/hooks-RnjuFmEx.js +0 -1
  270. package/dist/assets/index-Bcdzex-3.css +0 -2
  271. package/dist/assets/index-D-kY3FU1.js +0 -29
  272. package/dist/assets/linear-0eij_aAG.js +0 -1
  273. package/dist/assets/links-DkkL9DVa.js +0 -1
  274. package/dist/assets/numbers-Mk3-CcoU.js +0 -1
  275. package/dist/assets/pair-CaQyK6En.js +0 -1
  276. package/dist/assets/panels-Dn59o8ay.js +0 -4
  277. package/dist/assets/precisionRound-Bq_nmf5u.js +0 -1
  278. package/dist/assets/process-output-D0KGq04R.js +0 -1
  279. package/dist/assets/react-vega-C3rYGtP6.js +0 -189
  280. package/dist/assets/session-panel-BxS5hiG2.js +0 -1
  281. package/dist/assets/state-_Qai_QyX.js +0 -3
  282. package/dist/assets/tooltip-mT3CGest.js +0 -61
  283. package/dist/assets/tracing-Di6pUmX8.js +0 -1
  284. package/dist/assets/useAsyncData-C7aJJz0Y.js +0 -1
  285. package/dist/assets/useNotebookActions-DIc4xTzY.js +0 -1
  286. package/dist/assets/vega-component-CnadTgoE.js +0 -1
  287. package/dist/assets/vega-loader.browser-CLRkNgm7.js +0 -6
  288. package/src/core/websocket/useWebSocket.tsx +0 -88
@@ -174,8 +174,11 @@ describe("notebookStateFromSession", () => {
174
174
  channel: "marimo-error",
175
175
  data: [
176
176
  {
177
- type: "unknown",
177
+ type: "exception",
178
+ exception_type: "ValueError",
178
179
  msg: "Something went wrong",
180
+ raising_cell: null,
181
+ traceback: "Traceback line 1",
179
182
  },
180
183
  ],
181
184
  mimetype: "application/vnd.marimo+error",
@@ -183,6 +186,52 @@ describe("notebookStateFromSession", () => {
183
186
  });
184
187
  });
185
188
 
189
+ it("restores matching code from the session", () => {
190
+ const code = "raise ValueError('cached failure')";
191
+ const session = createSession([
192
+ createSessionCell(
193
+ "cell-1",
194
+ [
195
+ {
196
+ type: "error",
197
+ ename: "ValueError",
198
+ evalue: "cached failure",
199
+ traceback: ["Traceback\n", "ValueError: cached failure"],
200
+ code,
201
+ },
202
+ ],
203
+ [
204
+ {
205
+ type: "stream",
206
+ name: "stderr",
207
+ text: "ValueError: cached <failure>",
208
+ mimetype: "application/vnd.marimo+traceback",
209
+ traceback_format: "text",
210
+ code,
211
+ },
212
+ ],
213
+ ),
214
+ ]);
215
+ const state = notebookStateFromSession(session, null);
216
+ invariant(state, "state is null");
217
+ expect(state.cellRuntime[CELL_1].output).toMatchObject({
218
+ code,
219
+ data: [
220
+ {
221
+ type: "exception",
222
+ exception_type: "ValueError",
223
+ msg: "cached failure",
224
+ traceback: "Traceback\nValueError: cached failure",
225
+ },
226
+ ],
227
+ });
228
+ expect(state.cellRuntime[CELL_1].consoleOutputs[0].code).toBe(code);
229
+ expect(state.cellRuntime[CELL_1].consoleOutputs[0]).toMatchObject({
230
+ traceback_format: "text",
231
+ data: "ValueError: cached <failure>",
232
+ });
233
+ });
234
+
186
235
  it("handles data output in session cell", () => {
187
236
  const dataOutput = {
188
237
  type: "data" as const,
@@ -1761,7 +1761,8 @@ export const cellErrorsAtom = atom((get) => {
1761
1761
  });
1762
1762
 
1763
1763
  export const notebookOutline = atom((get) => {
1764
- const { cellIds, cellRuntime } = get(notebookAtom);
1764
+ const cellIds = get(cellIdsAtom);
1765
+ const cellRuntime = get(cellsRuntimeAtom);
1765
1766
  const outlines = cellIds.inOrderIds.map(
1766
1767
  (cellId) => cellRuntime[cellId].outline,
1767
1768
  );
@@ -0,0 +1,63 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { describeError } from "@/core/errors/error-entries";
4
+ import type { MarimoError } from "@/core/kernel/messages";
5
+ import { sanitizeHtml } from "@/plugins/core/sanitize-html";
6
+ import { parseHtmlContent } from "@/utils/dom";
7
+ import type { CellId } from "./ids";
8
+
9
+ export interface CellOutputMessage<Output> {
10
+ type: "cell-output";
11
+ cell_id: CellId;
12
+ code?: string;
13
+ output: Output;
14
+ }
15
+
16
+ export interface ErrorMessageOutput {
17
+ type: "error";
18
+ instructions: string;
19
+ message: string;
20
+ traceback?: string;
21
+ }
22
+
23
+ export const ERROR_MESSAGE_INSTRUCTIONS = "Fix this notebook error.";
24
+
25
+ export function buildErrorReport(
26
+ cellId: CellId,
27
+ evidence: { code?: string } & (
28
+ | { errors: MarimoError[] }
29
+ | { traceback: string; tracebackFormat: "html" | "text" }
30
+ ),
31
+ ): CellOutputMessage<ErrorMessageOutput> {
32
+ let message: string;
33
+ let traceback: string;
34
+ if ("errors" in evidence) {
35
+ message = evidence.errors.map(describeError).join("\n");
36
+ traceback = evidence.errors
37
+ .flatMap((error) =>
38
+ "traceback" in error && error.traceback
39
+ ? [parseHtmlContent(sanitizeHtml(error.traceback))]
40
+ : [],
41
+ )
42
+ .join("\n\n");
43
+ } else {
44
+ traceback =
45
+ evidence.tracebackFormat === "html"
46
+ ? parseHtmlContent(sanitizeHtml(evidence.traceback))
47
+ : evidence.traceback;
48
+ message = traceback.split("\n").findLast((line) => line.length > 0) ?? "";
49
+ }
50
+
51
+ // Put repair intent before evidence so it survives prefix truncation.
52
+ return {
53
+ type: "cell-output",
54
+ cell_id: cellId,
55
+ output: {
56
+ type: "error",
57
+ instructions: ERROR_MESSAGE_INSTRUCTIONS,
58
+ message,
59
+ ...(traceback ? { traceback } : {}),
60
+ },
61
+ ...(evidence.code === undefined ? {} : { code: evidence.code }),
62
+ };
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",
@@ -223,6 +235,9 @@ function createCellRuntimeFromSession(
223
235
  timestamp: DEFAULT_TIMESTAMP,
224
236
  };
225
237
  }
238
+ if (runtimeState.output && primaryOutput.code !== undefined) {
239
+ runtimeState.output.code = primaryOutput.code;
240
+ }
226
241
  }
227
242
 
228
243
  const consoleOutputs = sessionCell.console || [];
@@ -237,7 +252,13 @@ function createCellRuntimeFromSession(
237
252
  channel: "media",
238
253
  data: consoleOutput.data,
239
254
  mimetype: consoleOutput.mimetype,
255
+ ...(consoleOutput.traceback_format === undefined
256
+ ? {}
257
+ : { traceback_format: consoleOutput.traceback_format }),
240
258
  timestamp: DEFAULT_TIMESTAMP,
259
+ ...(consoleOutput.code === undefined
260
+ ? {}
261
+ : { code: consoleOutput.code }),
241
262
  };
242
263
  }
243
264
  // Handle StreamOutput (type: "stream")
@@ -245,7 +266,13 @@ function createCellRuntimeFromSession(
245
266
  channel: consoleOutput.name === "stderr" ? "stderr" : "stdout",
246
267
  data: consoleOutput.text,
247
268
  mimetype: consoleOutput.mimetype ?? "text/plain",
269
+ ...(consoleOutput.traceback_format === undefined
270
+ ? {}
271
+ : { traceback_format: consoleOutput.traceback_format }),
248
272
  timestamp: DEFAULT_TIMESTAMP,
273
+ ...(consoleOutput.code === undefined
274
+ ? {}
275
+ : { code: consoleOutput.code }),
249
276
  };
250
277
  }),
251
278
  };
@@ -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);
@@ -4,3 +4,8 @@ Production code may import host modules, `jotai/vanilla`, `zod`, and
4
4
  `@/utils/Logger`. Enforced by `marimo/host-imports`. Tests are exempt.
5
5
 
6
6
  Define interfaces here and inject application behavior through adapters.
7
+
8
+ `stream.ts` reduces stream events into one state. Each kind of object is a
9
+ `Resource` in `resources/` with pure handlers keyed by event name. The stream
10
+ handles phase, notebook identity, and control events itself. The client holds
11
+ one state atom and exposes derived atoms.
@@ -0,0 +1,57 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { describe, expect, it } from "vitest";
4
+ import { attachments } from "../resources/attachments";
5
+ import { createStream } from "../stream";
6
+ import { fixtureEvents } from "./fixtures";
7
+
8
+ const stream = createStream({ 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(stream.reduce, stream.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 = stream.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 = stream.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 = stream.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("retains records for replay and rebuilds them after reset", () => {
44
+ const state = confirmed();
45
+ expect(stream.resume(state)).toBe(state);
46
+ const reset = stream.reduce(state, { event: "reset", data: {} });
47
+ expect(reset.resources.attachments).toEqual([]);
48
+ const replacement = snapshot
49
+ .filter(
50
+ (event) =>
51
+ event.event !== "attachment" ||
52
+ event.data.attachment.id === records[0].id,
53
+ )
54
+ .reduce(stream.reduce, reset);
55
+ expect(replacement.resources.attachments).toEqual([records[0]]);
56
+ });
57
+ });
@@ -0,0 +1,124 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { describe, expect, it } from "vitest";
4
+ import type { NotebookEvent } from "../events";
5
+ import { document } from "../resources/document";
6
+ import { createStream } from "../stream";
7
+ import { fixtureEvents } from "./fixtures";
8
+
9
+ const stream = createStream({ document });
10
+ const snapshot = fixtureEvents("notebook-events.sse");
11
+ const edits = fixtureEvents("edits.sse");
12
+
13
+ function replay(events: readonly NotebookEvent[], from = stream.initial) {
14
+ return events.reduce((state, event) => stream.reduce(state, event), from);
15
+ }
16
+
17
+ const confirmed = () => replay(snapshot);
18
+ const current = (state: ReturnType<typeof replay>) =>
19
+ state.resources.document.current;
20
+
21
+ describe("document resource", () => {
22
+ it("publishes the snapshot at ready, then each completed edit and save", () => {
23
+ const ready = snapshot.findIndex((event) => event.event === "ready");
24
+ const opening = replay(snapshot.slice(0, ready));
25
+ expect(current(opening)).toBeNull();
26
+ const first = current(replay(snapshot.slice(ready), opening));
27
+ expect(first?.revision.order).toEqual(["c1", "c3", "k7f2"]);
28
+ expect([...first!.cells.keys()]).toEqual(first?.revision.order);
29
+
30
+ const [edit, save] = edits.filter((event) => event.event === "document");
31
+ const pending = replay(
32
+ edits.filter((event) => event.event !== "document"),
33
+ confirmed(),
34
+ );
35
+ expect(current(pending)).toEqual(first);
36
+ const changed = stream.reduce(pending, edit);
37
+ expect(current(changed)?.revision).toEqual(edit.data.document);
38
+ expect([...current(changed)!.cells.keys()].toSorted()).toEqual(
39
+ edit.data.document.order.toSorted(),
40
+ );
41
+ const saved = current(stream.reduce(changed, save));
42
+ expect(saved?.revision).toEqual(save.data.document);
43
+ expect(saved?.cells.get("c3")?.code).toBe("x = 22");
44
+ expect(saved?.cells.has("k7f2")).toBe(false);
45
+ expect(saved?.revision.saved_etag).toBe(saved?.revision.etag);
46
+ expect(first?.cells.get("c3")?.code).toBe("x = 21");
47
+ expect(first?.cells.has("k7f2")).toBe(true);
48
+ });
49
+
50
+ it("applies order-only changes and distinguishes an empty notebook from no document", () => {
51
+ const state = confirmed();
52
+ const revision = current(state)!.revision;
53
+ const reordered = {
54
+ ...revision,
55
+ order: revision.order.toReversed(),
56
+ version: revision.version + 1,
57
+ };
58
+ const changed = stream.reduce(state, {
59
+ event: "document",
60
+ data: { document: reordered },
61
+ id: "200",
62
+ });
63
+ expect(current(changed)).toEqual({
64
+ revision: reordered,
65
+ cells: current(state)?.cells,
66
+ });
67
+
68
+ const empty = { ...revision, order: [] };
69
+ const opening = stream.reduce(stream.initial, {
70
+ event: "document",
71
+ data: { document: empty },
72
+ });
73
+ expect(current(opening)).toBeNull();
74
+ expect(
75
+ current(stream.reduce(opening, { event: "ready", data: {}, id: "0" })),
76
+ ).toEqual({ revision: empty, cells: new Map() });
77
+ expect(
78
+ current(
79
+ stream.reduce(stream.initial, { event: "ready", data: {}, id: "0" }),
80
+ ),
81
+ ).toBeNull();
82
+ });
83
+
84
+ it("keeps the document while a live update is pending and after a replay completes it", () => {
85
+ const state = confirmed();
86
+ const [cell, , removed, published] = edits;
87
+ if (published.event !== "document") {
88
+ throw new Error("Fixture does not publish a document");
89
+ }
90
+ const partial = replay([cell, removed], state);
91
+ expect(current(partial)).toBe(current(state));
92
+ expect(stream.resume(partial)).toBe(partial);
93
+ const completed = replay(
94
+ [published, { event: "ready", data: {}, id: "113" }],
95
+ partial,
96
+ );
97
+ expect(current(completed)?.revision).toEqual(published.data.document);
98
+ });
99
+
100
+ it("removes a notebook without waiting for individual cell removals", () => {
101
+ const removed = stream.reduce(confirmed(), {
102
+ event: "notebook.removed",
103
+ data: { id: "n1", time: "2026-10-01T09:30:00Z" },
104
+ id: "200",
105
+ });
106
+ expect(current(removed)).toBeNull();
107
+ });
108
+
109
+ it("rejects records that cannot form a complete revision", () => {
110
+ const state = confirmed();
111
+ const revision = current(state)!.revision;
112
+ expect(() =>
113
+ stream.reduce(state, {
114
+ event: "document",
115
+ data: { document: { ...revision, order: ["missing"] } },
116
+ id: "200",
117
+ }),
118
+ ).toThrow("Document records do not match");
119
+ const partial = stream.reduce(state, edits[0]);
120
+ expect(() =>
121
+ stream.reduce(partial, { event: "ready", data: {}, id: "200" }),
122
+ ).toThrow("incomplete document update");
123
+ });
124
+ });
@@ -0,0 +1,46 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { describe, expect, it } from "vitest";
4
+ import { z } from "zod";
5
+ import { 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
+ const host = { event: "marimo", data: { marimo: notification } };
14
+ expect(parseNotebookEvent({ data: notification })).toEqual(host);
15
+ expect(parseNotebookEvent({ ...host, id: "42" })).toEqual({
16
+ ...host,
17
+ id: "42",
18
+ });
19
+ });
20
+
21
+ it("retains unknown event cursors without interpreting their payloads", () => {
22
+ expect(
23
+ parseNotebookEvent({ event: "future", id: "42", data: null }),
24
+ ).toEqual({ event: "unknown", name: "future", id: "42" });
25
+ });
26
+
27
+ it("decodes known events outside the client's subscription filters", () => {
28
+ const event = {
29
+ event: "run.removed",
30
+ id: "42",
31
+ data: { id: "r1", time: "2026-10-19T12:00:00Z" },
32
+ };
33
+ expect(parseNotebookEvent(event)).toEqual(event);
34
+ });
35
+
36
+ it.each([
37
+ { event: "cell", id: "42", data: { cell: { id: "c1" } } },
38
+ { event: "run.removed", id: "42", data: {} },
39
+ { event: "ready", data: {} },
40
+ { event: "marimo", data: {} },
41
+ {},
42
+ null,
43
+ ])("rejects malformed known messages: %j", (message) => {
44
+ expect(() => parseNotebookEvent(message)).toThrow(z.ZodError);
45
+ });
46
+ });
@@ -0,0 +1,39 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import NodeFs from "node:fs";
4
+ import NodeUrl from "node:url";
5
+ import { parseNotebookEvent, type NotebookEvent } from "../events";
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 notebook events. */
37
+ export function fixtureEvents(name: string): NotebookEvent[] {
38
+ return fixtureMessages(name).map(parseNotebookEvent);
39
+ }