@marimo-team/islands 0.25.3-dev141 → 0.25.3-dev143

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 (472) hide show
  1. package/dist/{ConnectedDataExplorerComponent-CNSxdKyJ.js → ConnectedDataExplorerComponent-BAiSVaHj.js} +17 -15
  2. package/dist/{ErrorBoundary-Cg59O3xL.js → ErrorBoundary-BRBKqg2U.js} +16 -16
  3. package/dist/{abnfDiagram-VCTEODGH-DQDjNXt3.js → abnfDiagram-VCTEODGH-BBHN92-F.js} +2 -2
  4. package/dist/any-language-editor-3E24SnCV.js +75 -0
  5. package/dist/{architectureDiagram-5GKGNRK7-Nmls7Wxn.js → architectureDiagram-5GKGNRK7-C0Rd64qh.js} +1 -1
  6. package/dist/{arrays-CN1djC4k.js → arrays-R6JsImiT.js} +8 -0
  7. package/dist/assets/__vite-browser-external-BgQNn4dS.js +1 -0
  8. package/dist/assets/worker-FKe3RHT4.js +113 -0
  9. package/dist/{button-CvjkwdSk.js → button-BQRdqQi1.js} +199 -192
  10. package/dist/{capabilities-CQ5IjhzL.js → capabilities-CI4xUPsp.js} +2 -2
  11. package/dist/{chat-ui-BDpymeoe.js → chat-ui-CvB8Map3.js} +1195 -1195
  12. package/dist/{check-EDwSwP89.js → check-SpOMoCLP.js} +1 -1
  13. package/dist/{chunk-IMKFNOWR-CoIFEo-1.js → chunk-IMKFNOWR-hJXey5p2.js} +1 -1
  14. package/dist/{chunk-SHT3W25Y-lHiGx3ZC.js → chunk-SHT3W25Y-JMzTmmVW.js} +1 -1
  15. package/dist/{chunk-SVP7TREG-BvsHIrY_.js → chunk-SVP7TREG-BIyoHTXi.js} +1 -1
  16. package/dist/{chunk-TICWLB2K-BC8YIm_n.js → chunk-TICWLB2K-CCzH2Uwa.js} +1 -1
  17. package/dist/{chunk-YOKDWASO-DPZk8vhS.js → chunk-YOKDWASO-DKplTrBY.js} +20 -20
  18. package/dist/{classDiagram-ZZMXUADV-Bt98lkkv.js → classDiagram-ZZMXUADV-DB1kOPS6.js} +2 -2
  19. package/dist/{classDiagram-v2-VYDZK3BY-D9bfbcC3.js → classDiagram-v2-VYDZK3BY-CPaFL0OB.js} +2 -2
  20. package/dist/{code-visibility-Da3iitn_.js → code-visibility-CAgcNzoX.js} +17 -17
  21. package/dist/{common-C6FHcu5p.js → common-BovIV1xq.js} +6341 -5870
  22. package/dist/{copy-icon-Ft7t9bqz.js → copy-icon-C1trdvVt.js} +3 -3
  23. package/dist/{cynefinDiagram-5FMLGOSQ-CgWra7wt.js → cynefinDiagram-5FMLGOSQ-CF93uliH.js} +1 -1
  24. package/dist/{dagre-GXQ25YYZ-CFZ8-35c.js → dagre-GXQ25YYZ-BBB-_p9D.js} +1 -1
  25. package/dist/{data-grid-overlay-editor-mJt3Pg4S.js → data-grid-overlay-editor-CBsr8Yo0.js} +5 -5
  26. package/dist/{data-grid-types-BH_r7g7l.js → data-grid-types-0PJ-9N5n.js} +1 -1
  27. package/dist/{diagram-S7CK7UJ4-lCNQrW7d.js → diagram-S7CK7UJ4-BynCnbyt.js} +1 -1
  28. package/dist/{diagram-UQ7AKVKN-CQPQ6pP_.js → diagram-UQ7AKVKN-svOxikMa.js} +1 -1
  29. package/dist/{diagram-VX7I27RA-Cf3b0llz.js → diagram-VX7I27RA-BVQSnPx0.js} +1 -1
  30. package/dist/{diagram-Z3DM3KII-Bt4QLrpn.js → diagram-Z3DM3KII-nR1J0BH5.js} +1 -1
  31. package/dist/{dialog-CEBFZNyc.js → dialog-DgeqyrEZ.js} +3 -3
  32. package/dist/{dist-DpBpA_n1.js → dist-Lm-D-Vr6.js} +3 -3
  33. package/dist/{dropdown-menu-CP_qCYHC.js → dropdown-menu-GWcGCcxE.js} +1277 -1229
  34. package/dist/{ebnfDiagram-PWID7BFC-D3NAyPFY.js → ebnfDiagram-PWID7BFC-C9tq9-Qo.js} +2 -2
  35. package/dist/{erDiagram-RLTQ6QDP-Tlu09_Nm.js → erDiagram-RLTQ6QDP-BiaeB7Fi.js} +1 -1
  36. package/dist/{error-banner-Da600cwz.js → error-banner-D40pmfQQ.js} +86 -81
  37. package/dist/{cells-C8iCYvLU.js → error-entries-CmFDQ9CA.js} +1718 -1621
  38. package/dist/{feedback-button-DaPwHIdM.js → feedback-button-B_9o6DdV.js} +122 -123
  39. package/dist/{flowDiagram-HODETNUW-CAYbpJmV.js → flowDiagram-HODETNUW-f-sLm87k.js} +2 -2
  40. package/dist/{formats-BGh2thRc.js → formats-CCxaLPNs.js} +3 -3
  41. package/dist/{ganttDiagram-EL5Y4UJY-BQT16JSc.js → ganttDiagram-EL5Y4UJY-Dh_4cBJZ.js} +2 -2
  42. package/dist/{glide-data-editor-Ctz2mH6k.js → glide-data-editor-i7BEAfhs.js} +370 -368
  43. package/dist/{glide-utils-DJyqh0EM.js → glide-utils-Dz_F4pAY.js} +16 -16
  44. package/dist/{globals-EpFoRj_E.js → globals-BDzA_Re9.js} +3 -3
  45. package/dist/{highlighted-body-KPVGNVTW-zwjq21tf.js → highlighted-body-KPVGNVTW-3Ziwfz-V.js} +2 -2
  46. package/dist/{html-to-image-CZY1pIZH.js → html-to-image-BrC8OJcj.js} +634 -597
  47. package/dist/{infoDiagram-27XIBGKW-XrDxhr0x.js → infoDiagram-27XIBGKW-BHrWywBe.js} +1 -1
  48. package/dist/{ishikawaDiagram-5VMMS53U-CD1muzov.js → ishikawaDiagram-5VMMS53U-Da4s0Reg.js} +1 -1
  49. package/dist/{kanban-definition-UXKFOSKX-Dg9W142_.js → kanban-definition-UXKFOSKX-DDf1GVLj.js} +1 -1
  50. package/dist/{label-BDKF3Afc.js → label-D9_-zSg-.js} +3 -3
  51. package/dist/{linear-Cnaq3Tfb.js → linear-DWERh7eI.js} +121 -73
  52. package/dist/{loader-BQOfjTIJ.js → loader-DOVB0Syl.js} +12 -12
  53. package/dist/main.js +7548 -7226
  54. package/dist/{mermaid-CiAXjLpA.js → mermaid-ClwTt7OD.js} +38 -38
  55. package/dist/mermaid-HWGCJPDP-Du3-HPQA.js +2 -0
  56. package/dist/{mindmap-definition-YA3MSWOX-DgCidgYJ.js → mindmap-definition-YA3MSWOX-D60oMZtT.js} +1 -1
  57. package/dist/{number-overlay-editor-CtxrummL.js → number-overlay-editor-D9WjQlOL.js} +3 -3
  58. package/dist/{objects-CzUgoMIF.js → objects-DCmMvZtn.js} +1 -3
  59. package/dist/{once-BtE-wKRL.js → once-eG8Vmvjd.js} +1 -1
  60. package/dist/{pegDiagram-XKGWAZYB-coOYYKB6.js → pegDiagram-XKGWAZYB-B9wFpMga.js} +2 -2
  61. package/dist/{pieDiagram-E7YTZNPT-BnXTc3bj.js → pieDiagram-E7YTZNPT-BZwvMuBT.js} +1 -1
  62. package/dist/precisionRound-DhAzeRcm.js +49 -0
  63. package/dist/{process-output-e4jj9Ijt.js → process-output-Bw-vzyoy.js} +4 -4
  64. package/dist/{quadrantDiagram-AXDQQJYC-Ccw0xMjN.js → quadrantDiagram-AXDQQJYC-Blrm7V7_.js} +1 -1
  65. package/dist/{railroadDiagram-O6MQD6OU-B28-u83n.js → railroadDiagram-O6MQD6OU-DqhZGsN2.js} +2 -2
  66. package/dist/{react-vega-BED0DjiB.js → react-vega-C4T9Rb_w.js} +12284 -12270
  67. package/dist/{requirementDiagram-BXWQKSXE-CdYCTMu0.js → requirementDiagram-BXWQKSXE-CL5dYARV.js} +1 -1
  68. package/dist/{reveal-component-DrIXvR_7.js → reveal-component-BxP2jlPx.js} +14 -14
  69. package/dist/{select-BButFi-d.js → select-CkF_GBxO.js} +537 -458
  70. package/dist/{sequenceDiagram-WJ2MYXX4-CM3aRAe_.js → sequenceDiagram-WJ2MYXX4-BSUCRd2x.js} +1 -1
  71. package/dist/{spec-BwarBpUJ.js → spec-DiSh_qJC.js} +41 -11
  72. package/dist/{stateDiagram-D77RDMKH-DOZUMVj8.js → stateDiagram-D77RDMKH-B-1Hvyb0.js} +2 -2
  73. package/dist/{stateDiagram-v2-MP3YSRHH-CISYxma6.js → stateDiagram-v2-MP3YSRHH--HtiR4yB.js} +2 -2
  74. package/dist/style.css +1 -1
  75. package/dist/{swimlanes-42K2YHIH-BkSppxp8.js → swimlanes-42K2YHIH-Bdfhrd-2.js} +1 -1
  76. package/dist/{swimlanesDiagram-VR7AAH4N-c8G5pGxC.js → swimlanesDiagram-VR7AAH4N-BJIfxSz0.js} +2 -2
  77. package/dist/{swiper-component-DKntyn8p.js → swiper-component-BkCm-kLg.js} +2 -2
  78. package/dist/{time-BieKwdYS.js → time-BaMbJs4z.js} +18 -18
  79. package/dist/{timeline-definition-24CTP7MA-CaMQayqK.js → timeline-definition-24CTP7MA-Cs4EfFD3.js} +1 -1
  80. package/dist/{toDate-BNJf01u1.js → toDate-DGvRHwTk.js} +57 -54
  81. package/dist/{toast-B9UMEi8u.js → toast-Btbve3Lm.js} +231 -213
  82. package/dist/{tooltip-BO1REbqs.js → tooltip-CJ4Rqasp.js} +327 -303
  83. package/dist/{use-restore-focus-BEM7sFWl.js → use-restore-focus-CmGwRNId.js} +1482 -1433
  84. package/dist/{useAsyncData-Deh5kszT.js → useAsyncData-BmVgnMP5.js} +15 -14
  85. package/dist/{useDateFormatter-D8XruNGG.js → useDateFormatter-eCL_SdeG.js} +2 -2
  86. package/dist/{useDeepCompareMemoize-DXwzRINI.js → useDeepCompareMemoize-Ch_AeGu6.js} +1 -1
  87. package/dist/{useIframeCapabilities-DBETvvPl.js → useIframeCapabilities-CSdWn6NP.js} +1 -1
  88. package/dist/{useLifecycle-j7D9uD1o.js → useLifecycle-B5BX921H.js} +3 -3
  89. package/dist/{useTheme-CRnjkku0.js → useTheme-Bq9pi_vz.js} +6 -6
  90. package/dist/{vega-component-DG29TAB7.js → vega-component-BgyZ-COt.js} +147 -147
  91. package/dist/{vega-embed-container-BNHE18G9.js → vega-embed-container--LYZqguO.js} +2 -2
  92. package/dist/{vega-loader.browser-DNST6uVf.js → vega-loader.browser-B9Pz0EGP.js} +298 -507
  93. package/dist/{vennDiagram-4TSXK5OY-CF8pfaK3.js → vennDiagram-4TSXK5OY-DRu0vRjO.js} +1 -1
  94. package/dist/{wardleyDiagram-VM6X3IG4-dGPu39d-.js → wardleyDiagram-VM6X3IG4-DzbDxwDY.js} +1 -1
  95. package/dist/{xychartDiagram-S5SC5T6Z-CP6-9q56.js → xychartDiagram-S5SC5T6Z-CLWj6ZFQ.js} +2 -2
  96. package/package.json +15 -12
  97. package/src/__tests__/fixtures/{pair-preview.ts → pair-config.ts} +3 -2
  98. package/src/__tests__/host-imports.test.ts +101 -0
  99. package/src/__tests__/module-entry.test.ts +32 -0
  100. package/src/__tests__/mount.test.ts +8 -8
  101. package/src/components/ai/__tests__/ai-model-dropdown.test.tsx +63 -0
  102. package/src/components/ai/ai-model-dropdown.tsx +11 -6
  103. package/src/components/app-config/ai-config.tsx +7 -2
  104. package/src/components/chat/__tests__/message-queue.test.tsx +17 -0
  105. package/src/components/chat/acp/__tests__/session-tabs.test.tsx +43 -0
  106. package/src/components/chat/acp/__tests__/state.test.ts +2 -2
  107. package/src/components/chat/acp/blocks.tsx +3 -2
  108. package/src/components/chat/acp/session-tabs.tsx +8 -3
  109. package/src/components/chat/chat-components.tsx +2 -0
  110. package/src/components/chat/chat-panel.tsx +9 -8
  111. package/src/components/chat/chat-utils.ts +4 -8
  112. package/src/components/data-table/TableTopBar.tsx +2 -0
  113. package/src/components/data-table/__tests__/chart-spec-model.test.ts +195 -195
  114. package/src/components/data-table/__tests__/column-explorer.test.tsx +27 -1
  115. package/src/components/data-table/__tests__/columns.test.tsx +105 -0
  116. package/src/components/data-table/__tests__/context-menu-interaction.test.tsx +130 -0
  117. package/src/components/data-table/__tests__/context-menu.test.ts +176 -0
  118. package/src/components/data-table/__tests__/data-table.test.tsx +1 -0
  119. package/src/components/data-table/__tests__/date-filter-inputs.test.tsx +24 -1
  120. package/src/components/data-table/__tests__/date-popover.test.tsx +72 -0
  121. package/src/components/data-table/__tests__/export-actions.test.tsx +434 -6
  122. package/src/components/data-table/__tests__/keyboard-selection.test.tsx +188 -0
  123. package/src/components/data-table/__tests__/search-highlight.test.tsx +109 -0
  124. package/src/components/data-table/__tests__/url-detector.test.tsx +74 -0
  125. package/src/components/data-table/cell-selection/feature.ts +2 -2
  126. package/src/components/data-table/cell-selection/types.ts +2 -2
  127. package/src/components/data-table/charts/__tests__/chart-panel.test.tsx +74 -1
  128. package/src/components/data-table/charts/__tests__/spec-snapshot.test.ts +20 -20
  129. package/src/components/data-table/charts/__tests__/spec.test.ts +78 -52
  130. package/src/components/data-table/charts/__tests__/storage.test.ts +4 -1
  131. package/src/components/data-table/charts/chart-spec/spec.ts +68 -43
  132. package/src/components/data-table/charts/charts.tsx +40 -30
  133. package/src/components/data-table/charts/storage.ts +16 -5
  134. package/src/components/data-table/column-explorer-panel/column-explorer.tsx +1 -1
  135. package/src/components/data-table/column-summary/chart-spec-model.tsx +23 -16
  136. package/src/components/data-table/column-summary/column-summary.tsx +2 -4
  137. package/src/components/data-table/column-visibility-dropdown.tsx +8 -13
  138. package/src/components/data-table/columns.tsx +45 -46
  139. package/src/components/data-table/context-menu.tsx +161 -46
  140. package/src/components/data-table/data-table.tsx +13 -1
  141. package/src/components/data-table/date-filter-inputs.tsx +15 -42
  142. package/src/components/data-table/date-popover.tsx +6 -4
  143. package/src/components/data-table/export-actions.tsx +267 -163
  144. package/src/components/data-table/range-focus/atoms.ts +4 -6
  145. package/src/components/data-table/range-focus/use-cell-range-selection.ts +65 -38
  146. package/src/components/data-table/range-focus/use-scroll-into-view.ts +21 -2
  147. package/src/components/data-table/renderers.tsx +16 -7
  148. package/src/components/data-table/row-viewer-panel/__tests__/row-viewer.test.tsx +24 -1
  149. package/src/components/data-table/row-viewer-panel/row-viewer.tsx +4 -4
  150. package/src/components/data-table/schemas.ts +14 -36
  151. package/src/components/data-table/search-highlight.tsx +79 -0
  152. package/src/components/data-table/types.ts +1 -0
  153. package/src/components/data-table/url-detector.tsx +49 -9
  154. package/src/components/data-table/utils.ts +18 -0
  155. package/src/components/datasources/__tests__/column-actions.test.tsx +97 -0
  156. package/src/components/datasources/__tests__/column-preview.test.tsx +98 -5
  157. package/src/components/datasources/__tests__/datasources-interactions.test.tsx +181 -0
  158. package/src/components/datasources/__tests__/table-list.test.tsx +118 -0
  159. package/src/components/datasources/__tests__/utils.test.ts +57 -6
  160. package/src/components/datasources/column-preview.tsx +13 -4
  161. package/src/components/datasources/datasources.tsx +51 -39
  162. package/src/components/datasources/utils.ts +32 -23
  163. package/src/components/debugger/debugger-code.tsx +5 -0
  164. package/src/components/dependency-graph/dependency-graph-tree.tsx +8 -8
  165. package/src/components/dependency-graph/elements.ts +24 -21
  166. package/src/components/dependency-graph/panels.tsx +2 -1
  167. package/src/components/dependency-graph/utils/__tests__/layout.test.ts +18 -11
  168. package/src/components/editor/Output.tsx +9 -2
  169. package/src/components/editor/SortableCell.tsx +1 -3
  170. package/src/components/editor/__tests__/dynamic-favicon.test.tsx +111 -18
  171. package/src/components/editor/__tests__/package-alert.test.tsx +22 -1
  172. package/src/components/editor/actions/__tests__/pair-with-agent-commands.test.ts +80 -252
  173. package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +210 -34
  174. package/src/components/editor/actions/pair-with-agent-commands.ts +42 -51
  175. package/src/components/editor/actions/pair-with-agent-modal.tsx +100 -37
  176. package/src/components/editor/ai/add-cell-with-ai.tsx +3 -3
  177. package/src/components/editor/ai/ai-completion-editor.tsx +1 -0
  178. package/src/components/editor/alerts/startup-logs-alert.tsx +1 -0
  179. package/src/components/editor/cell/CreateCellButton.tsx +8 -10
  180. package/src/components/editor/cell/DeleteButton.tsx +1 -0
  181. package/src/components/editor/cell/__tests__/useShouldShowInterrupt.test.ts +39 -0
  182. package/src/components/editor/cell/code/__tests__/language-toggle.test.tsx +54 -0
  183. package/src/components/editor/cell/code/cell-editor.tsx +1 -1
  184. package/src/components/editor/cell/code/language-toggle.tsx +8 -7
  185. package/src/components/editor/cell/collapse.tsx +5 -3
  186. package/src/components/editor/cell/useShouldShowInterrupt.ts +2 -12
  187. package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +30 -1
  188. package/src/components/editor/chrome/panels/cache-panel.tsx +1 -0
  189. package/src/components/editor/chrome/panels/error-panel.tsx +1 -0
  190. package/src/components/editor/chrome/panels/outline/__tests__/outline-keyboard.test.tsx +84 -0
  191. package/src/components/editor/chrome/panels/outline/floating-outline.tsx +39 -14
  192. package/src/components/editor/chrome/panels/outline/useActiveOutline.tsx +3 -2
  193. package/src/components/editor/chrome/panels/packages-panel.tsx +4 -7
  194. package/src/components/editor/chrome/panels/secrets-panel.tsx +1 -0
  195. package/src/components/editor/chrome/panels/snippets-panel.tsx +5 -1
  196. package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +366 -0
  197. package/src/components/editor/chrome/wrapper/__tests__/pending-ai-cells.test.tsx +58 -1
  198. package/src/components/editor/chrome/wrapper/footer-item.tsx +32 -24
  199. package/src/components/editor/chrome/wrapper/footer-items/attachment-status.tsx +237 -0
  200. package/src/components/editor/chrome/wrapper/footer-items/runtime-settings.tsx +1 -7
  201. package/src/components/editor/chrome/wrapper/footer.tsx +2 -0
  202. package/src/components/editor/chrome/wrapper/pending-ai-cells.tsx +12 -2
  203. package/src/components/editor/chrome/wrapper/storage.ts +1 -1
  204. package/src/components/editor/connections/components.tsx +1 -1
  205. package/src/components/editor/controls/__tests__/command-palette.test.tsx +53 -0
  206. package/src/components/editor/controls/__tests__/keyboard-shortcuts.test.tsx +61 -0
  207. package/src/components/editor/controls/command-palette.tsx +22 -10
  208. package/src/components/editor/controls/keyboard-shortcuts.tsx +14 -5
  209. package/src/components/editor/dynamic-favicon.tsx +21 -40
  210. package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +537 -0
  211. package/src/components/editor/errors/send-error-report-button.tsx +69 -0
  212. package/src/components/editor/file-tree/__tests__/file-explorer-interactions.test.tsx +6 -0
  213. package/src/components/editor/file-tree/__tests__/file-explorer-navigation.test.tsx +47 -0
  214. package/src/components/editor/file-tree/__tests__/use-file-search.test.tsx +91 -0
  215. package/src/components/editor/file-tree/file-explorer.tsx +45 -8
  216. package/src/components/editor/file-tree/file-name-input.tsx +1 -0
  217. package/src/components/editor/file-tree/file-viewer.tsx +4 -2
  218. package/src/components/editor/file-tree/use-file-search.ts +35 -21
  219. package/src/components/editor/header/status.tsx +28 -5
  220. package/src/components/editor/links/__tests__/cell-link.test.tsx +146 -0
  221. package/src/components/editor/links/cell-link.tsx +14 -5
  222. package/src/components/editor/navigation/focus-utils.ts +4 -4
  223. package/src/components/editor/notebook-banner.tsx +1 -0
  224. package/src/components/editor/notebook-cell.tsx +11 -2
  225. package/src/components/editor/output/MarimoErrorOutput.tsx +21 -5
  226. package/src/components/editor/output/MarimoTracebackOutput.tsx +54 -37
  227. package/src/components/editor/output/__tests__/ansi-reduce.test.ts +18 -0
  228. package/src/components/editor/output/__tests__/traceback.test.tsx +27 -1
  229. package/src/components/editor/output/ansi-reduce.ts +36 -17
  230. package/src/components/editor/output/console/ConsoleOutput.tsx +19 -25
  231. package/src/components/editor/package-alert.tsx +14 -7
  232. package/src/components/editor/renderers/grid-layout/grid-layout.tsx +12 -6
  233. package/src/components/editor/renderers/vertical-layout/sidebar/toggle.tsx +7 -1
  234. package/src/components/home/components.tsx +17 -31
  235. package/src/components/icons/__tests__/large-spinner.test.tsx +39 -0
  236. package/src/components/icons/large-spinner.tsx +3 -14
  237. package/src/components/icons/spinner.tsx +1 -1
  238. package/src/components/mcp/mcp-status-indicator.tsx +1 -1
  239. package/src/components/pages/__tests__/home-page.test.tsx +116 -0
  240. package/src/components/pages/home-page.tsx +164 -91
  241. package/src/components/scratchpad/scratchpad.test.tsx +95 -0
  242. package/src/components/scratchpad/scratchpad.tsx +16 -3
  243. package/src/components/slides/reveal-component.tsx +4 -2
  244. package/src/components/slides/slide-cell-view.tsx +1 -1
  245. package/src/components/slides/slide-form.tsx +1 -0
  246. package/src/components/tracing/tracing-keyboard.test.tsx +85 -0
  247. package/src/components/tracing/tracing.tsx +45 -41
  248. package/src/components/ui/__tests__/draggable-popover.test.tsx +24 -0
  249. package/src/components/ui/__tests__/range-slider.test.tsx +165 -0
  250. package/src/components/ui/alert-dialog.tsx +1 -1
  251. package/src/components/ui/draggable-popover.tsx +24 -2
  252. package/src/components/ui/range-slider.tsx +22 -9
  253. package/src/core/ai/context/providers/datasource.ts +6 -2
  254. package/src/core/ai/tools/base.ts +16 -7
  255. package/src/core/ai/tools/edit-notebook-tool.ts +47 -45
  256. package/src/core/ai/tools/registry.ts +13 -8
  257. package/src/core/ai/tools/sample-tool.ts +6 -7
  258. package/src/core/attachments/__tests__/toast.test.tsx +58 -0
  259. package/src/core/attachments/state.ts +18 -0
  260. package/src/core/attachments/toast.ts +15 -0
  261. package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +4 -0
  262. package/src/core/cells/__tests__/cell.test.ts +39 -0
  263. package/src/core/cells/__tests__/cells.test.ts +55 -1
  264. package/src/core/cells/__tests__/collapseConsoleOutputs.test.ts +54 -0
  265. package/src/core/cells/__tests__/error-report.test.ts +139 -0
  266. package/src/core/cells/__tests__/notebook-outline.test.ts +113 -0
  267. package/src/core/cells/__tests__/session.test.ts +167 -37
  268. package/src/core/cells/cell.ts +14 -3
  269. package/src/core/cells/cells.ts +3 -1
  270. package/src/core/cells/error-report.ts +63 -0
  271. package/src/core/cells/pending-delete-service.ts +2 -6
  272. package/src/core/cells/session.ts +29 -2
  273. package/src/core/cells/types.ts +10 -1
  274. package/src/core/codemirror/__tests__/setup.test.ts +29 -29
  275. package/src/core/codemirror/cells/__tests__/extensions.test.ts +15 -0
  276. package/src/core/codemirror/cells/extensions.ts +7 -3
  277. package/src/core/codemirror/keymaps/vim.ts +2 -6
  278. package/src/core/codemirror/language/__tests__/sql.test.ts +22 -20
  279. package/src/core/codemirror/language/extension.ts +8 -8
  280. package/src/core/codemirror/language/languages/markdown.ts +8 -6
  281. package/src/core/codemirror/language/languages/python.ts +16 -10
  282. package/src/core/codemirror/language/languages/sql/sql.ts +9 -7
  283. package/src/core/codemirror/language/panel/panel.tsx +1 -0
  284. package/src/core/codemirror/language/types.ts +7 -7
  285. package/src/core/codemirror/rtc/extension.ts +24 -12
  286. package/src/core/codemirror/rtc/loro/awareness.ts +34 -19
  287. package/src/core/codemirror/rtc/loro/sync.ts +1 -0
  288. package/src/core/codemirror/utils.ts +1 -0
  289. package/src/core/config/pair.ts +5 -3
  290. package/src/core/datasets/__tests__/data-source-discovery.test.ts +4 -4
  291. package/src/core/datasets/data-source-discovery.ts +4 -6
  292. package/src/core/document/__tests__/codemirror.test.ts +485 -0
  293. package/src/core/document/__tests__/draft.test.ts +322 -0
  294. package/src/core/document/__tests__/fixtures.ts +92 -0
  295. package/src/core/document/__tests__/hooks.test.tsx +386 -0
  296. package/src/core/document/__tests__/lifecycle-scenarios.test.ts +61 -0
  297. package/src/core/document/__tests__/model.test.ts +191 -0
  298. package/src/core/document/__tests__/protocol-fixtures.ts +38 -0
  299. package/src/core/document/__tests__/recovery-scenarios.test.ts +230 -0
  300. package/src/core/document/__tests__/scenario-fixture.ts +43 -0
  301. package/src/core/document/__tests__/scenarios.test.ts +205 -0
  302. package/src/core/document/__tests__/state.test.ts +438 -0
  303. package/src/core/document/__tests__/submission.test.ts +203 -0
  304. package/src/core/document/__tests__/test-host.test.ts +92 -0
  305. package/src/core/document/__tests__/test-host.ts +247 -0
  306. package/src/core/document/__tests__/test-notebook.ts +149 -0
  307. package/src/core/document/__tests__/user.ts +61 -0
  308. package/src/core/document/codemirror.ts +311 -0
  309. package/src/core/document/draft.ts +472 -0
  310. package/src/core/document/hooks.tsx +118 -0
  311. package/src/core/document/index.ts +15 -0
  312. package/src/core/document/model.ts +55 -0
  313. package/src/core/document/select.ts +41 -0
  314. package/src/core/document/state.ts +374 -0
  315. package/src/core/document/sync.ts +369 -0
  316. package/src/core/edit-app.tsx +2 -2
  317. package/src/core/host/AGENTS.md +12 -0
  318. package/src/core/host/__tests__/attachments.test.ts +56 -0
  319. package/src/core/host/__tests__/document.test.ts +126 -0
  320. package/src/core/host/__tests__/fixtures.ts +79 -0
  321. package/src/core/host/__tests__/replica.test.ts +550 -0
  322. package/src/core/host/__tests__/static.test.ts +175 -0
  323. package/src/core/host/__tests__/wasm.test.ts +166 -0
  324. package/src/core/host/backend.ts +19 -0
  325. package/src/core/host/connection.ts +42 -0
  326. package/src/core/host/index.ts +14 -0
  327. package/src/core/host/replica.ts +286 -0
  328. package/src/core/host/resources/attachments.ts +22 -0
  329. package/src/core/host/resources/document.ts +101 -0
  330. package/src/core/host/static.ts +77 -0
  331. package/src/core/host/wasm.ts +97 -0
  332. package/src/core/hotkeys/actions.ts +2 -4
  333. package/src/core/islands/__tests__/bootstrap.test.ts +17 -17
  334. package/src/core/islands/__tests__/bridge.test.ts +31 -26
  335. package/src/core/islands/bootstrap.ts +22 -51
  336. package/src/core/islands/bridge.ts +31 -20
  337. package/src/core/islands/components/__tests__/IslandControls.test.tsx +7 -5
  338. package/src/core/islands/worker/__tests__/controller.test.ts +62 -4
  339. package/src/core/islands/worker/controller.ts +4 -10
  340. package/src/core/islands/worker/worker.tsx +33 -33
  341. package/src/core/kernel/__tests__/RuntimeState.test.ts +1 -1
  342. package/src/core/network/connection.ts +4 -2
  343. package/src/core/network/useConnectionNotice.ts +8 -2
  344. package/src/core/run-app.tsx +2 -2
  345. package/src/core/runtime/__tests__/createWsUrl.test.ts +77 -7
  346. package/src/core/runtime/__tests__/runtime.test.ts +33 -15
  347. package/src/core/runtime/runtime.ts +39 -30
  348. package/src/core/saving/save-component.tsx +12 -3
  349. package/src/core/state/__tests__/jotai.test.tsx +108 -0
  350. package/src/core/state/jotai.ts +1 -1
  351. package/src/core/static/__tests__/files.test.ts +38 -10
  352. package/src/core/static/files.ts +2 -0
  353. package/src/core/wasm/__tests__/bridge.test.ts +213 -50
  354. package/src/core/wasm/__tests__/worker.test.ts +187 -0
  355. package/src/core/wasm/bridge.ts +81 -52
  356. package/src/core/wasm/rpc.ts +15 -3
  357. package/src/core/wasm/store.ts +3 -3
  358. package/src/core/wasm/worker/bootstrap.ts +55 -51
  359. package/src/core/wasm/worker/getMarimoWheel.ts +1 -1
  360. package/src/core/wasm/worker/save-worker.ts +9 -10
  361. package/src/core/wasm/worker/types.ts +29 -18
  362. package/src/core/wasm/worker/worker.ts +97 -44
  363. package/src/core/websocket/__tests__/backend.test.ts +235 -0
  364. package/src/core/websocket/__tests__/{useMarimoKernelConnection.test.ts → connection-state.test.ts} +15 -29
  365. package/src/core/websocket/__tests__/useNotebookReplica.hook.test.tsx +141 -0
  366. package/src/core/websocket/__tests__/{useMarimoKernelConnection.hook.test.tsx → useNotebookSession.hook.test.tsx} +197 -184
  367. package/src/core/websocket/backend.ts +129 -0
  368. package/src/core/websocket/connection-state.ts +60 -0
  369. package/src/core/websocket/state.ts +7 -0
  370. package/src/core/websocket/transports/__tests__/notebook.test.ts +405 -0
  371. package/src/core/websocket/transports/notebook.ts +159 -0
  372. package/src/core/websocket/types.ts +9 -39
  373. package/src/core/websocket/useNotebookReplica.ts +69 -0
  374. package/src/core/websocket/{useMarimoKernelConnection.tsx → useNotebookSession.tsx} +63 -228
  375. package/src/css/globals.css +5 -0
  376. package/src/hooks/__tests__/useAsyncData.test.tsx +148 -0
  377. package/src/hooks/__tests__/useDebounce.test.tsx +51 -0
  378. package/src/hooks/__tests__/useDeepCompareMemoize.test.tsx +39 -0
  379. package/src/hooks/__tests__/useDelayElapsed.test.tsx +28 -0
  380. package/src/hooks/__tests__/useInternalStateWithSync.test.tsx +20 -0
  381. package/src/hooks/__tests__/useScript.test.tsx +77 -0
  382. package/src/hooks/__tests__/useStateForKey.test.tsx +257 -0
  383. package/src/hooks/useAsyncData.ts +3 -3
  384. package/src/hooks/useDataSourceDiscovery.ts +6 -4
  385. package/src/hooks/useDebounce.ts +6 -10
  386. package/src/hooks/useDeepCompareMemoize.ts +1 -1
  387. package/src/hooks/useDelayElapsed.ts +14 -10
  388. package/src/hooks/useElapsedTime.ts +3 -3
  389. package/src/hooks/useEventListener.ts +1 -2
  390. package/src/hooks/useInputHistory.ts +4 -7
  391. package/src/hooks/useInternalStateWithSync.ts +6 -6
  392. package/src/hooks/useResizeObserver.ts +4 -7
  393. package/src/hooks/useScript.ts +67 -70
  394. package/src/hooks/useStateForKey.ts +93 -0
  395. package/src/mount.tsx +3 -3
  396. package/src/plugins/__tests__/plugin-object-schemas.test.ts +104 -0
  397. package/src/plugins/core/registerReactComponent.tsx +1 -1
  398. package/src/plugins/impl/CodeEditorPlugin.tsx +1 -1
  399. package/src/plugins/impl/DataEditorPlugin.tsx +1 -1
  400. package/src/plugins/impl/DataTablePlugin.tsx +29 -33
  401. package/src/plugins/impl/DropdownPlugin.tsx +2 -6
  402. package/src/plugins/impl/FileBrowserPlugin.tsx +4 -16
  403. package/src/plugins/impl/FormPlugin.tsx +1 -1
  404. package/src/plugins/impl/RangeSliderPlugin.tsx +16 -2
  405. package/src/plugins/impl/RefreshPlugin.tsx +1 -0
  406. package/src/plugins/impl/SliderPlugin.tsx +6 -8
  407. package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +201 -1
  408. package/src/plugins/impl/__tests__/FileBrowserPlugin.test.tsx +45 -0
  409. package/src/plugins/impl/__tests__/RangeSliderPlugin.test.tsx +109 -0
  410. package/src/plugins/impl/__tests__/SliderPlugin.test.tsx +24 -3
  411. package/src/plugins/impl/anywidget/__tests__/registry.test.ts +2 -0
  412. package/src/plugins/impl/anywidget/__tests__/widget-binding.test.ts +241 -2
  413. package/src/plugins/impl/anywidget/runtime.ts +21 -9
  414. package/src/plugins/impl/anywidget/widget-binding.ts +160 -82
  415. package/src/plugins/impl/chat/__tests__/chat-prompts.test.tsx +93 -0
  416. package/src/plugins/impl/chat/chat-ui.tsx +14 -11
  417. package/src/plugins/impl/code/any-language-editor.tsx +2 -2
  418. package/src/plugins/impl/data-editor/glide-data-editor.tsx +8 -5
  419. package/src/plugins/impl/data-editor/glide-utils.ts +3 -1
  420. package/src/plugins/impl/data-explorer/ConnectedDataExplorerComponent.tsx +1 -1
  421. package/src/plugins/impl/data-explorer/components/column-summary.tsx +4 -2
  422. package/src/plugins/impl/data-explorer/state/reducer.ts +1 -1
  423. package/src/plugins/impl/data-frames/DataFramePlugin.tsx +1 -2
  424. package/src/plugins/impl/data-frames/__tests__/sidebar.test.tsx +160 -0
  425. package/src/plugins/impl/data-frames/forms/__tests__/__snapshots__/form.test.tsx.snap +66 -66
  426. package/src/plugins/impl/data-frames/panel.tsx +43 -16
  427. package/src/plugins/impl/mpl-interactive/MplInteractivePlugin.tsx +5 -3
  428. package/src/plugins/impl/panel/PanelPlugin.tsx +16 -17
  429. package/src/plugins/impl/panel/__tests__/utils.test.ts +39 -1
  430. package/src/plugins/impl/panel/utils.ts +17 -0
  431. package/src/plugins/impl/plotly/PlotlyPlugin.tsx +2 -5
  432. package/src/plugins/impl/plotly/selection.ts +25 -19
  433. package/src/plugins/impl/vega/VegaPlugin.tsx +2 -3
  434. package/src/plugins/impl/vega/make-selectable.ts +4 -7
  435. package/src/plugins/layout/RoutesPlugin.tsx +1 -1
  436. package/src/stories/__tests__/editor-story.test.tsx +33 -0
  437. package/src/stories/button.stories.tsx +1 -0
  438. package/src/stories/editor.stories.tsx +24 -17
  439. package/src/stories/form-wrapper.stories.tsx +2 -2
  440. package/src/stories/select.stories.tsx +2 -2
  441. package/src/stories/switchable-multi-select.stories.tsx +1 -1
  442. package/src/utils/__tests__/arrays.test.ts +35 -0
  443. package/src/utils/__tests__/batch-requests.test.ts +3 -3
  444. package/src/utils/__tests__/dates.test.ts +116 -0
  445. package/src/utils/__tests__/numbers.test.ts +105 -1
  446. package/src/utils/__tests__/objects.test.ts +21 -0
  447. package/src/utils/arrays.ts +10 -0
  448. package/src/utils/blob.ts +1 -1
  449. package/src/utils/dates.ts +94 -0
  450. package/src/utils/numbers.ts +79 -18
  451. package/src/utils/objects.ts +3 -7
  452. package/src/utils/once.ts +19 -19
  453. package/src/utils/tracer.ts +9 -5
  454. package/src/utils/zod-utils.ts +1 -3
  455. package/dist/any-language-editor-B9REGlD9.js +0 -76
  456. package/dist/assets/__vite-browser-external-BXYkiFsk.js +0 -1
  457. package/dist/assets/worker-HPY9fbhz.js +0 -114
  458. package/dist/error-entries-CzoYkIQZ.js +0 -56
  459. package/dist/mermaid-HWGCJPDP-ByCsZnYF.js +0 -2
  460. package/dist/precisionRound-DMIVQrt2.js +0 -97
  461. package/src/core/state/__tests__/jotai.test.ts +0 -25
  462. package/src/core/wasm/worker/message-buffer.ts +0 -33
  463. package/src/core/websocket/__tests__/useWebSocket.test.ts +0 -49
  464. package/src/core/websocket/transports/__tests__/basic.test.ts +0 -275
  465. package/src/core/websocket/transports/__tests__/sse.test.ts +0 -338
  466. package/src/core/websocket/transports/__tests__/transport.test.ts +0 -308
  467. package/src/core/websocket/transports/__tests__/ws.test.ts +0 -125
  468. package/src/core/websocket/transports/basic.ts +0 -102
  469. package/src/core/websocket/transports/sse.ts +0 -308
  470. package/src/core/websocket/transports/transport.ts +0 -61
  471. package/src/core/websocket/transports/ws.ts +0 -98
  472. package/src/core/websocket/useWebSocket.tsx +0 -88
@@ -0,0 +1,22 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import type { Attachment } from "@marimo-team/client/protocol";
4
+ import type { NotebookResource } from "@marimo-team/client";
5
+
6
+ /** Attachment records retained with the stream's resume cursor. */
7
+ export const attachments: NotebookResource<
8
+ readonly Attachment[],
9
+ "attachment"
10
+ > = {
11
+ initial: [],
12
+ on: {
13
+ attachment: (state, { data: { attachment } }) =>
14
+ state.some((item) => item.id === attachment.id)
15
+ ? state.map((item) => (item.id === attachment.id ? attachment : item))
16
+ : [...state, attachment],
17
+ "attachment.removed": (state, { data }) =>
18
+ state.some((item) => item.id === data.id)
19
+ ? state.filter((item) => item.id !== data.id)
20
+ : state,
21
+ },
22
+ };
@@ -0,0 +1,101 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import type * as Protocol from "@marimo-team/client/protocol";
4
+ import type { NotebookResource } from "@marimo-team/client";
5
+
6
+ /**
7
+ * The notebook's content at one revision.
8
+ *
9
+ * The revision record carries the cell order, app config, etags, and
10
+ * version. Each cell in its order has a record in `cells`.
11
+ */
12
+ export interface Document {
13
+ readonly revision: Protocol.Document;
14
+ readonly cells: ReadonlyMap<Protocol.CellId, Protocol.Cell>;
15
+ }
16
+
17
+ /** The records of a revision still being received. */
18
+ interface PendingDocument {
19
+ /** Null until the `document` event that closes the group arrives. */
20
+ readonly revision: Protocol.Document | null;
21
+ readonly cells: ReadonlyMap<Protocol.CellId, Protocol.Cell>;
22
+ }
23
+
24
+ /**
25
+ * The confirmed document and the revision being assembled after it.
26
+ *
27
+ * A snapshot's records are pending until `ready`. A live revision is pending
28
+ * from its first cell until its `document` event.
29
+ */
30
+ interface DocumentState {
31
+ readonly current: Document | null;
32
+ readonly pending: PendingDocument | null;
33
+ }
34
+
35
+ function pendingOf(state: DocumentState): PendingDocument {
36
+ return (
37
+ state.pending ?? {
38
+ revision: state.current?.revision ?? null,
39
+ cells: state.current?.cells ?? new Map(),
40
+ }
41
+ );
42
+ }
43
+
44
+ /** Confirm a revision whose records are complete. */
45
+ function publish(
46
+ revision: Protocol.Document,
47
+ cells: ReadonlyMap<Protocol.CellId, Protocol.Cell>,
48
+ ): DocumentState {
49
+ const order = new Set(revision.order);
50
+ if (
51
+ order.size !== revision.order.length ||
52
+ order.size !== cells.size ||
53
+ [...order].some((id) => !cells.has(id)) ||
54
+ [...cells.values()].some((cell) => cell.version > revision.version)
55
+ ) {
56
+ throw new Error("Document records do not match its revision and order");
57
+ }
58
+ return { current: { revision, cells }, pending: null };
59
+ }
60
+
61
+ export const document: NotebookResource<DocumentState, "cell" | "document"> = {
62
+ initial: { current: null, pending: null },
63
+ on: {
64
+ cell: (state, { data }) => {
65
+ const pending = pendingOf(state);
66
+ return {
67
+ ...state,
68
+ pending: {
69
+ revision: pending.revision,
70
+ cells: new Map(pending.cells).set(data.cell.id, data.cell),
71
+ },
72
+ };
73
+ },
74
+ "cell.removed": (state, { data }) => {
75
+ const pending = pendingOf(state);
76
+ if (!pending.cells.has(data.id)) {
77
+ return state;
78
+ }
79
+ const cells = new Map(pending.cells);
80
+ cells.delete(data.id);
81
+ return { ...state, pending: { ...pending, cells } };
82
+ },
83
+ document: (state, { data }, { phase }) => {
84
+ const { cells } = pendingOf(state);
85
+ return phase === "snapshot"
86
+ ? { ...state, pending: { revision: data.document, cells } }
87
+ : publish(data.document, cells);
88
+ },
89
+ ready: (state, _event, { phase }) => {
90
+ if (state.pending === null) {
91
+ return state;
92
+ }
93
+ if (phase === "live") {
94
+ throw new Error("Stream ended with an incomplete document update");
95
+ }
96
+ return state.pending.revision === null
97
+ ? { current: null, pending: null }
98
+ : publish(state.pending.revision, state.pending.cells);
99
+ },
100
+ },
101
+ };
@@ -0,0 +1,77 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ /** A backend that serves a document snapshot without a host. */
4
+
5
+ import type { NotebookClient } from "@marimo-team/client";
6
+ import type * as Protocol from "@marimo-team/client/protocol";
7
+ import type { NotebookBackend } from "./backend";
8
+
9
+ /** A document snapshot with its own identity and revision metadata. */
10
+ export interface StaticNotebookSnapshot {
11
+ readonly notebook: Omit<Protocol.Notebook, "operations">;
12
+ readonly document: Protocol.Document;
13
+ readonly cells: readonly Protocol.Cell[];
14
+ }
15
+
16
+ /**
17
+ * Serve a document without a server, runtime, or mutation operations.
18
+ * Callers and readers must treat the snapshot records as immutable.
19
+ */
20
+ export function createStaticNotebookBackend(
21
+ snapshot: StaticNotebookSnapshot,
22
+ ): NotebookBackend {
23
+ const frames: Protocol.Event[] = [
24
+ {
25
+ event: "notebook",
26
+ data: { notebook: { ...snapshot.notebook, operations: [] } },
27
+ },
28
+ ...snapshot.cells.map((cell): Protocol.CellEvent => ({
29
+ event: "cell",
30
+ data: { cell },
31
+ })),
32
+ { event: "document", data: { document: snapshot.document } },
33
+ { event: "ready", id: "static", data: {} },
34
+ ];
35
+
36
+ return {
37
+ transport: ({ cursor, signal }, { open, message }) =>
38
+ new Promise((resolve) => {
39
+ if (signal.aborted) {
40
+ resolve();
41
+ return;
42
+ }
43
+ // Finishing would ask NotebookEvents to reconnect. A snapshot stays open
44
+ // until its reader closes, even though there are no more updates.
45
+ signal.addEventListener("abort", () => resolve(), { once: true });
46
+ open();
47
+ if (cursor !== null && !signal.aborted) {
48
+ message({ event: "reset", data: {} });
49
+ }
50
+ for (const frame of frames) {
51
+ if (signal.aborted) {
52
+ return;
53
+ }
54
+ message(frame);
55
+ }
56
+ }),
57
+ notebook: () => {
58
+ const unsupported: NotebookClient["save"] = async (options) => {
59
+ options?.signal?.throwIfAborted();
60
+ return {
61
+ ok: false,
62
+ status: 501,
63
+ problem: {
64
+ type: "tag:marimo.io,2026:host/unsupported-operation",
65
+ title: "Unsupported operation",
66
+ status: 501,
67
+ detail: "Static notebooks do not support mutations",
68
+ },
69
+ };
70
+ };
71
+ return {
72
+ edit: (_changes, options) => unsupported(options),
73
+ save: unsupported,
74
+ };
75
+ },
76
+ };
77
+ }
@@ -0,0 +1,97 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ /** A backend for the notebook running in the WASM worker. */
4
+
5
+ import {
6
+ NotebookClient,
7
+ StreamStopped,
8
+ type unstable_NotebookCall,
9
+ } from "@marimo-team/client";
10
+ import type { NotebookBackend } from "./backend";
11
+
12
+ /** The worker owns the notebook; this interface only carries commands and events. */
13
+ export interface WasmNotebookBridge {
14
+ subscribe(
15
+ cursor: string | null,
16
+ emit: (event: unknown) => void,
17
+ signal: AbortSignal,
18
+ ): Promise<() => void>;
19
+ /** Deliver a call to the worker and resolve with its status and body. */
20
+ call(call: unstable_NotebookCall): Promise<unknown>;
21
+ }
22
+
23
+ /** Stop waiting on cancellation; a command already sent may still commit. */
24
+ function abortable<T>(
25
+ send: () => Promise<T>,
26
+ signal?: AbortSignal,
27
+ ): Promise<T> {
28
+ if (!signal) {
29
+ return send();
30
+ }
31
+ signal.throwIfAborted();
32
+ return new Promise((resolve, reject) => {
33
+ const abort = () => reject(signal.reason);
34
+ signal.addEventListener("abort", abort, { once: true });
35
+ send()
36
+ .then(resolve, reject)
37
+ .finally(() => signal.removeEventListener("abort", abort));
38
+ });
39
+ }
40
+
41
+ export function createWasmNotebookBackend(
42
+ bridge: WasmNotebookBridge,
43
+ ): NotebookBackend {
44
+ return {
45
+ transport: async ({ cursor, signal }, { open, message }) => {
46
+ if (signal.aborted) {
47
+ return;
48
+ }
49
+ let unsubscribe: (() => void) | undefined;
50
+ let finish!: () => void;
51
+ const closed = new Promise<void>((resolve) => {
52
+ finish = resolve;
53
+ });
54
+ const abort = () => {
55
+ unsubscribe?.();
56
+ finish();
57
+ };
58
+ signal.addEventListener("abort", abort, { once: true });
59
+ try {
60
+ open();
61
+ // Keep the pending subscription so a close during startup still releases it.
62
+ await abortable(
63
+ () =>
64
+ bridge
65
+ .subscribe(
66
+ cursor,
67
+ (event) => {
68
+ if (!signal.aborted) {
69
+ message(event);
70
+ }
71
+ },
72
+ signal,
73
+ )
74
+ .then((close) => {
75
+ unsubscribe = close;
76
+ if (signal.aborted) {
77
+ close();
78
+ }
79
+ }),
80
+ signal,
81
+ );
82
+ await closed;
83
+ } catch (error) {
84
+ if (!signal.aborted) {
85
+ unsubscribe?.();
86
+ throw new StreamStopped("Could not follow the WASM notebook", {
87
+ cause: error,
88
+ });
89
+ }
90
+ } finally {
91
+ signal.removeEventListener("abort", abort);
92
+ }
93
+ },
94
+ notebook: (notebookId) =>
95
+ NotebookClient.unstable_over((call) => bridge.call(call), { notebookId }),
96
+ };
97
+ }
@@ -2,6 +2,7 @@
2
2
  import { atom, useAtomValue, useSetAtom } from "jotai";
3
3
  import useEvent from "react-use-event-hook";
4
4
  import type { HotkeyAction } from "@/core/hotkeys/hotkeys";
5
+ import { Objects } from "@/utils/objects";
5
6
 
6
7
  /**
7
8
  * Map of registered keyboard shortcuts and their callbacks.
@@ -29,10 +30,7 @@ export function useSetRegisteredAction() {
29
30
  },
30
31
  ),
31
32
  unregisterAction: useEvent((shortcut: HotkeyAction) => {
32
- set((actions) => {
33
- const { [shortcut]: unused, ...rest } = actions;
34
- return rest;
35
- });
33
+ set((actions) => Objects.omit(actions, [shortcut]));
36
34
  }),
37
35
  };
38
36
  }
@@ -6,17 +6,17 @@ import { RuntimeState } from "@/core/kernel/RuntimeState";
6
6
  import { initializeIslands } from "../bootstrap";
7
7
  import type { IslandsPyodideBridge } from "../bridge";
8
8
  import { islandsHydratedAtom, islandsPendingInitialRunsAtom } from "../state";
9
- import type { IslandsKernelMessage } from "../worker/worker";
10
-
11
- function completedRun(sessionGeneration: number): IslandsKernelMessage {
12
- return {
13
- sessionGeneration,
14
- message: JSON.stringify({
15
- op: "completed-run",
16
- data: { op: "completed-run", run_id: null },
17
- }) as IslandsKernelMessage["message"],
18
- };
19
- }
9
+ import type { NotificationMessage } from "@/core/kernel/messages";
10
+
11
+ type Consumer = (
12
+ notification: NotificationMessage,
13
+ sessionGeneration: number,
14
+ ) => void;
15
+
16
+ const completedRun = {
17
+ op: "completed-run",
18
+ run_id: null,
19
+ } as NotificationMessage;
20
20
 
21
21
  describe("initializeIslands", () => {
22
22
  afterEach(vi.restoreAllMocks);
@@ -26,9 +26,9 @@ describe("initializeIslands", () => {
26
26
  root.innerHTML =
27
27
  '<marimo-island data-app-id="app-1" data-reactive="true"></marimo-island>';
28
28
 
29
- let consumeMessage: ((message: IslandsKernelMessage) => void) | undefined;
29
+ let consumeMessage: Consumer | undefined;
30
30
  const bridge = {
31
- consumeMessages: (consumer: (message: IslandsKernelMessage) => void) => {
31
+ consumeNotifications: (consumer: Consumer) => {
32
32
  consumeMessage = consumer;
33
33
  },
34
34
  sendComponentValues: vi.fn(),
@@ -48,14 +48,14 @@ describe("initializeIslands", () => {
48
48
  expect(store.get(islandsHydratedAtom)).toBe(false);
49
49
  store.set(islandsPendingInitialRunsAtom, new Set([1, 2]));
50
50
 
51
- consumeMessage?.(completedRun(1));
51
+ consumeMessage?.(completedRun, 1);
52
52
  expect(store.get(islandsHydratedAtom)).toBe(false);
53
53
 
54
- consumeMessage?.(completedRun(1));
55
- consumeMessage?.(completedRun(0));
54
+ consumeMessage?.(completedRun, 1);
55
+ consumeMessage?.(completedRun, 0);
56
56
  expect(store.get(islandsHydratedAtom)).toBe(false);
57
57
 
58
- consumeMessage?.(completedRun(2));
58
+ consumeMessage?.(completedRun, 2);
59
59
  expect(store.get(islandsHydratedAtom)).toBe(true);
60
60
  });
61
61
  });
@@ -10,7 +10,6 @@ import {
10
10
  } from "@/__tests__/branded";
11
11
  import { notebookAtom } from "@/core/cells/cells";
12
12
  import { islandsPendingInitialRunsAtom } from "@/core/islands/state";
13
- import type { IslandsKernelMessage } from "../worker/worker";
14
13
 
15
14
  type Base64String = components["schemas"]["Base64String"];
16
15
  interface TestIslandApp {
@@ -89,9 +88,6 @@ vi.mock("@/core/wasm/rpc", () => ({
89
88
  startSession: mockStartSessionRequest,
90
89
  stopSession: mockStopSessionRequest,
91
90
  },
92
- send: {
93
- consumerReady: vi.fn(),
94
- },
95
91
  },
96
92
  addMessageListener: vi.fn(
97
93
  (name: string, listener: (payload: never) => void) => {
@@ -177,17 +173,22 @@ describe("IslandsPyodideBridge", () => {
177
173
  });
178
174
  }
179
175
 
180
- function sendKernelMessage(sessionGeneration: number) {
181
- const listener = mockMessageListeners.get("kernelMessage");
176
+ function sendNotebookEvent(sessionGeneration: number, frame: unknown) {
177
+ const listener = mockMessageListeners.get("notebookEvent");
182
178
  if (!listener) {
183
- throw new Error("Missing kernel message listener");
179
+ throw new Error("Missing notebook event listener");
184
180
  }
185
- const message = {
186
- message: "" as IslandsKernelMessage["message"],
187
- sessionGeneration,
188
- };
189
- listener(message as never);
190
- return message;
181
+ listener({ event: JSON.stringify(frame), sessionGeneration } as never);
182
+ }
183
+
184
+ /** Send a notification and return the call the consumer should receive. */
185
+ function sendKernelMessage(sessionGeneration: number) {
186
+ const notification = { op: "interrupted" };
187
+ sendNotebookEvent(sessionGeneration, {
188
+ event: "marimo",
189
+ data: { marimo: notification },
190
+ });
191
+ return [notification, sessionGeneration];
191
192
  }
192
193
 
193
194
  async function initializeSingleApp() {
@@ -300,7 +301,7 @@ describe("IslandsPyodideBridge", () => {
300
301
  describe("app lifecycle", () => {
301
302
  it("ignores messages after a session yields ownership", async () => {
302
303
  const consumeMessage = vi.fn();
303
- bridge.consumeMessages(consumeMessage);
304
+ bridge.consumeNotifications(consumeMessage);
304
305
  mockSingleApp();
305
306
  await bridge.initializeApps();
306
307
 
@@ -319,10 +320,7 @@ describe("IslandsPyodideBridge", () => {
319
320
  sendKernelMessage(1);
320
321
  const currentMessage = sendKernelMessage(2);
321
322
 
322
- expect(consumeMessage.mock.calls).toEqual([
323
- [firstMessage],
324
- [currentMessage],
325
- ]);
323
+ expect(consumeMessage.mock.calls).toEqual([firstMessage, currentMessage]);
326
324
 
327
325
  finishReplacement();
328
326
  await replacement;
@@ -330,7 +328,7 @@ describe("IslandsPyodideBridge", () => {
330
328
 
331
329
  it("restores message ownership when replacement fails", async () => {
332
330
  const consumeMessage = vi.fn();
333
- bridge.consumeMessages(consumeMessage);
331
+ bridge.consumeNotifications(consumeMessage);
334
332
  mockSingleApp();
335
333
  await bridge.initializeApps();
336
334
  mockReplaceSessionRequest.mockRejectedValueOnce(
@@ -344,12 +342,12 @@ describe("IslandsPyodideBridge", () => {
344
342
  const previousMessage = sendKernelMessage(1);
345
343
  sendKernelMessage(2);
346
344
 
347
- expect(consumeMessage.mock.calls).toEqual([[previousMessage]]);
345
+ expect(consumeMessage.mock.calls).toEqual([previousMessage]);
348
346
  });
349
347
 
350
348
  it("ignores messages while their session is stopping", async () => {
351
349
  const consumeMessage = vi.fn();
352
- bridge.consumeMessages(consumeMessage);
350
+ bridge.consumeNotifications(consumeMessage);
353
351
  mockSingleApp();
354
352
  await bridge.initializeApps();
355
353
  let finishStop!: () => void;
@@ -373,7 +371,7 @@ describe("IslandsPyodideBridge", () => {
373
371
 
374
372
  it("forwards messages from every app in a multi-app document", async () => {
375
373
  const consumeMessage = vi.fn();
376
- bridge.consumeMessages(consumeMessage);
374
+ bridge.consumeNotifications(consumeMessage);
377
375
  mockParseMarimoIslandApps.mockReturnValue([app(), app("app-2")]);
378
376
  mockCreateMarimoFile
379
377
  .mockReturnValueOnce("generated app 1")
@@ -385,10 +383,17 @@ describe("IslandsPyodideBridge", () => {
385
383
  const secondMessage = sendKernelMessage(2);
386
384
  sendKernelMessage(3);
387
385
 
388
- expect(consumeMessage.mock.calls).toEqual([
389
- [firstMessage],
390
- [secondMessage],
391
- ]);
386
+ expect(consumeMessage.mock.calls).toEqual([firstMessage, secondMessage]);
387
+ });
388
+
389
+ it("delivers only the notebook stream's marimo notifications", async () => {
390
+ const consumeMessage = vi.fn();
391
+ bridge.consumeNotifications(consumeMessage);
392
+ await initializeSingleApp();
393
+ sendNotebookEvent(1, { event: "ready", id: "3", data: {} });
394
+ const notification = sendKernelMessage(1);
395
+
396
+ expect(consumeMessage.mock.calls).toEqual([notification]);
392
397
  });
393
398
 
394
399
  it("holds controls until the first app session is ready", async () => {
@@ -9,11 +9,7 @@ import {
9
9
  } from "@/plugins/impl/anywidget/registry";
10
10
  import { initializePlugins } from "@/plugins/plugins";
11
11
  import { Functions } from "@/utils/functions";
12
- import {
13
- safeExtractSetUIElementMessageBuffers,
14
- type JsonString,
15
- } from "@/utils/json/base64";
16
- import { jsonParseWithSpecialChar } from "@/utils/json/json-parser";
12
+ import { safeExtractSetUIElementMessageBuffers } from "@/utils/json/base64";
17
13
  import { Logger } from "@/utils/Logger";
18
14
  import { logNever } from "@/utils/assertNever";
19
15
  import {
@@ -30,10 +26,7 @@ import {
30
26
  handleKernelReady,
31
27
  handleRemoveUIElements,
32
28
  } from "../kernel/handlers";
33
- import type {
34
- NotificationMessage,
35
- NotificationMessageType,
36
- } from "../kernel/messages";
29
+ import type { NotificationMessage } from "../kernel/messages";
37
30
  import { queryParamHandlers } from "../kernel/queryParamHandlers";
38
31
  import { RuntimeState } from "../kernel/RuntimeState";
39
32
  import { initialModeAtom } from "../mode";
@@ -126,19 +119,9 @@ export async function initializeIslands(
126
119
  }
127
120
  });
128
121
 
129
- // Wire up kernel message handling
130
- // The wire format is {"op": "...", "data": {"op": "...", ...}} — both
131
- // the envelope and the payload carry the op. The bridge types the message
132
- // as NotificationPayload (just {data}), but the actual wire format
133
- // includes the outer op too.
134
- // Worker metadata identifies the session that emitted the message.
135
- bridge.consumeMessages(({ message, sessionGeneration }) => {
136
- handleMessage(
137
- message as unknown as JsonString<IslandsNotificationMessage>,
138
- actions,
139
- store,
140
- sessionGeneration,
141
- );
122
+ // Each notification names the session that sent it.
123
+ bridge.consumeNotifications((notification, sessionGeneration) => {
124
+ handleNotification(notification, actions, store, sessionGeneration);
142
125
  });
143
126
 
144
127
  // Track first user interaction before initialization completes
@@ -154,27 +137,15 @@ export async function initializeIslands(
154
137
  RuntimeState.INSTANCE.start(bridge.sendComponentValues);
155
138
  }
156
139
 
157
- type IslandsNotificationMessage = {
158
- [K in NotificationMessageType]: {
159
- data: Extract<NotificationMessage, { op: K }>;
160
- op: K;
161
- };
162
- }[NotificationMessageType];
163
-
164
- /**
165
- * Handles a single message from the kernel.
166
- *
167
- * Wire format from Python: {"op": "<name>", "data": {"op": "<name>", ...}}
168
- */
140
+ /** Handles a single notification from the kernel. */
169
141
  // oxlint-disable-next-line marimo/prefer-object-params
170
- function handleMessage(
171
- message: JsonString<IslandsNotificationMessage>,
142
+ function handleNotification(
143
+ msg: NotificationMessage,
172
144
  actions: NotebookActions,
173
145
  store: Store,
174
146
  sessionGeneration: number,
175
147
  ): void {
176
148
  try {
177
- const msg = jsonParseWithSpecialChar(message);
178
149
  Logger.debug("Islands received message:", msg.op);
179
150
 
180
151
  switch (msg.op) {
@@ -223,7 +194,7 @@ function handleMessage(
223
194
  return;
224
195
 
225
196
  case "kernel-ready":
226
- handleKernelReady(msg.data, {
197
+ handleKernelReady(msg, {
227
198
  autoInstantiate: true,
228
199
  setCells: actions.setCells,
229
200
  setLayoutData: Functions.NOOP,
@@ -239,47 +210,47 @@ function handleMessage(
239
210
 
240
211
  case "send-ui-element-message":
241
212
  UI_ELEMENT_REGISTRY.broadcastMessage(
242
- msg.data.ui_element,
243
- msg.data.message,
244
- safeExtractSetUIElementMessageBuffers(msg.data),
213
+ msg.ui_element,
214
+ msg.message,
215
+ safeExtractSetUIElementMessageBuffers(msg),
245
216
  );
246
217
  return;
247
218
 
248
219
  case "remove-ui-elements":
249
- handleRemoveUIElements(msg.data);
220
+ handleRemoveUIElements(msg);
250
221
  return;
251
222
 
252
223
  case "function-call-result":
253
- FUNCTIONS_REGISTRY.resolve(msg.data.function_call_id, msg.data);
224
+ FUNCTIONS_REGISTRY.resolve(msg.function_call_id, msg);
254
225
  return;
255
226
 
256
227
  case "cell-op":
257
- handleCellNotificationeration(msg.data, actions.handleCellMessage);
228
+ handleCellNotificationeration(msg, actions.handleCellMessage);
258
229
  return;
259
230
 
260
231
  case "alert":
261
232
  toast({
262
- title: msg.data.title,
263
- description: renderHTML({ html: msg.data.description }),
264
- variant: msg.data.variant,
233
+ title: msg.title,
234
+ description: renderHTML({ html: msg.description }),
235
+ variant: msg.variant,
265
236
  });
266
237
  return;
267
238
 
268
239
  case "query-params-append":
269
- queryParamHandlers.append(msg.data);
240
+ queryParamHandlers.append(msg);
270
241
  return;
271
242
  case "query-params-set":
272
- queryParamHandlers.set(msg.data);
243
+ queryParamHandlers.set(msg);
273
244
  return;
274
245
  case "query-params-delete":
275
- queryParamHandlers.delete(msg.data);
246
+ queryParamHandlers.delete(msg);
276
247
  return;
277
248
  case "query-params-clear":
278
249
  queryParamHandlers.clear();
279
250
  return;
280
251
 
281
252
  case "model-lifecycle":
282
- handleWidgetMessage(WIDGET_REGISTRY, msg.data);
253
+ handleWidgetMessage(WIDGET_REGISTRY, msg);
283
254
  return;
284
255
 
285
256
  case "consumer-capabilities":