@marimo-team/islands 0.24.3-dev9 → 0.25.0

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 (440) hide show
  1. package/dist/{ConnectedDataExplorerComponent-C9OF_75w.js → ConnectedDataExplorerComponent-KVhjDMSe.js} +1066 -1066
  2. package/dist/ErrorBoundary-DnNkbB_U.js +200 -0
  3. package/dist/{ImageComparisonComponent-2a9TMMm2.js → ImageComparisonComponent-Dlm5aWnH.js} +4 -4
  4. package/dist/{Plot-C-9MQeGP.js → Plot-CTyjbQDT.js} +21032 -20666
  5. package/dist/_baseMerge-BiGRwuqv.js +165 -0
  6. package/dist/{abnfDiagram-N423BO3Z-CQKwzX8t.js → abnfDiagram-VCTEODGH-CpN0FRda.js} +17 -18
  7. package/dist/{any-language-editor-DmQ7dyCx.js → any-language-editor-CycIVIeP.js} +17 -17
  8. package/dist/arc-7ra3iNdN.js +81 -0
  9. package/dist/architecture-7GRP2DOG-BWLylgiA.js +3 -0
  10. package/dist/{architectureDiagram-T3A2C74G-CncrDpfa.js → architectureDiagram-5GKGNRK7-DDyj5SRB.js} +25 -25
  11. package/dist/assets/__vite-browser-external-C9iR87Zl.js +1 -0
  12. package/dist/assets/worker-D7hWi02M.js +109 -0
  13. package/dist/blockDiagram-I7D4REHJ-BbXEXJRu.js +1922 -0
  14. package/dist/{button-CKBkqR9J.js → button-DdXAm8iz.js} +5 -5
  15. package/dist/{c4Diagram-5PPSVZJV-DarUk3Yq.js → c4Diagram-7LVT6UL2-DcLsbQmR.js} +696 -668
  16. package/dist/{capabilities-CMBBJTcc.js → capabilities-B3ce_GNu.js} +1 -1
  17. package/dist/{html-to-image-BtFMe_Nq.js → cells-DYIkSxs6.js} +6757 -10137
  18. package/dist/{channel-CyiYUzJv.js → channel-B5XnTarr.js} +1 -1
  19. package/dist/chat-ui-BXj3uYjy.js +5786 -0
  20. package/dist/{check-CHig47QV.js → check-B7cIK6gb.js} +1 -1
  21. package/dist/{chunk-2Q5K7J3B-BMuJl2jB.js → chunk-2Q5K7J3B-BMkUkeuy.js} +2 -2
  22. package/dist/chunk-4HAMMTFA-D2qv8E0X.js +4188 -0
  23. package/dist/{chunk-5VM5RSS4-Bcq0ftdy.js → chunk-5VM5RSS4-DKsmPCL6.js} +2 -2
  24. package/dist/{chunk-NSK5VX7P-CDTMcJB0.js → chunk-75Z2AOVW-5_HsvXwr.js} +23 -23
  25. package/dist/{chunk-I66GZJ75-BqyTCAPf.js → chunk-DU6HZSFF-BUgAHvnM.js} +21 -4
  26. package/dist/{chunk-2GRJ4B5K-DgO8Qudq.js → chunk-F27PBJKO-DHmYWZXr.js} +4 -4
  27. package/dist/{chunk-KEIR6QF5-C4KX3XYh.js → chunk-FOHPRMQF-BpYR8RqP.js} +4 -4
  28. package/dist/{chunk-WRU74C26-BpWsbiDK.js → chunk-GMAD6QVW-Czynz8aA.js} +486 -258
  29. package/dist/chunk-GVQU2GXP-yQEdulRS.js +37 -0
  30. package/dist/{chunk-5RXB4S5H-tLNUVDdO.js → chunk-IMKFNOWR-BfwTebD0.js} +11 -11
  31. package/dist/{chunk-JWPE2WC7-BJwjGUnF.js → chunk-JWPE2WC7-DJvv1t8U.js} +2 -2
  32. package/dist/chunk-L3NEJ4N5-DwRWnDDK.js +245 -0
  33. package/dist/{chunk-7Z6QIM7H-Dc18hFxH.js → chunk-OSK3NFVY-BBKU_Ign.js} +222 -144
  34. package/dist/{chunk-W5SLKNZC-DLnvpEtV.js → chunk-P2QGCYS3-C0iGU29N.js} +20 -20
  35. package/dist/{chunk-KBJHAD2P-BuqWqkcW.js → chunk-POPQ4Y6H-BCbS3Ozc.js} +4 -4
  36. package/dist/{chunk-4I5QYGJK-DpytHuZQ.js → chunk-PWAF6VOD-CTFCL0jI.js} +4 -4
  37. package/dist/{chunk-JQJVKLGR-CgqCwe8E.js → chunk-SHT3W25Y-BspJEJ5H.js} +109 -63
  38. package/dist/{chunk-6Q2QTUOP-DmTcEBMn.js → chunk-SVP7TREG-DX3rFNTe.js} +5 -5
  39. package/dist/{chunk-GF5L2VYU-B0wgCQgS.js → chunk-TICWLB2K-fVF7Uujg.js} +13 -13
  40. package/dist/{chunk-XXDRQBXY-D73H4IN0.js → chunk-XXDRQBXY-Dnl5VBqG.js} +3 -3
  41. package/dist/{chunk-Y2CYZVJY-DoegUj_e.js → chunk-Y2CYZVJY-DKKS1VyV.js} +1 -1
  42. package/dist/{chunk-5FQGJX7Z-B85XCPv6.js → chunk-YOKDWASO-DrWbKnxA.js} +6925 -5460
  43. package/dist/classDiagram-ZZMXUADV-Fe-V14Sc.js +30 -0
  44. package/dist/classDiagram-v2-VYDZK3BY-xTDg7lOs.js +30 -0
  45. package/dist/code-visibility-DElzk_hh.js +138 -0
  46. package/dist/{common-Cv-4hMa0.js → common-B4-ih9IA.js} +8838 -12645
  47. package/dist/{compiler-runtime-CAH9RLw2.js → compiler-runtime-Cj8ZWiNS.js} +2 -2
  48. package/dist/copy-icon-CLdENY__.js +190 -0
  49. package/dist/{cose-bilkent-JH36ORCC-FUWHkXFX.js → cose-bilkent-JH36ORCC-_LyRKVqm.js} +2 -2
  50. package/dist/cubic-OaEBhQPK.js +1404 -0
  51. package/dist/cynefin-OW5HDTMX-obq-i80l.js +3 -0
  52. package/dist/{cynefinDiagram-MW4NZA55-BXSFK93J.js → cynefinDiagram-5FMLGOSQ-CFQrqzSX.js} +10 -10
  53. package/dist/{dagre-CGyalH38.js → dagre-BGSfXNEb.js} +678 -537
  54. package/dist/dagre-GXQ25YYZ-xHNAUTyG.js +425 -0
  55. package/dist/{data-grid-overlay-editor-DZM4BAaI.js → data-grid-overlay-editor-CRH6JQPE.js} +8 -9
  56. package/dist/{data-grid-types-BnZwWG8z.js → data-grid-types-BucVmDDp.js} +1 -1
  57. package/dist/{defaultLocale-DKaU_opG.js → defaultLocale-JI_lnoCe.js} +1 -1
  58. package/dist/{diagram-7IWD3JNH-BjfzDNvj.js → diagram-S7CK7UJ4-Bt1rPbAx.js} +40 -39
  59. package/dist/{diagram-UB23O5K3-DDg-7wLA.js → diagram-UQ7AKVKN-D1sq5FoZ.js} +10 -10
  60. package/dist/{diagram-B4RE2ZJO-CKccR9eE.js → diagram-VSXAHHWV-BKpmG23x.js} +10 -10
  61. package/dist/{diagram-Q27KOJAE-Zhz01ss7.js → diagram-VX7I27RA-BMwGFVD6.js} +21 -21
  62. package/dist/{diagram-LBJQPF4R-CJ-bOruh.js → diagram-Z3DM3KII--KLDR9Pl.js} +10 -10
  63. package/dist/dialog-CgNq6y9-.js +94 -0
  64. package/dist/{dist-dAqZAPcF.js → dist-1HDa70oE.js} +3 -3
  65. package/dist/{dist-Re9sOPOx.js → dist-9x1ZRe-e.js} +2 -2
  66. package/dist/{dist-DEeLqRlU.js → dist-B1vYLK28.js} +1 -1
  67. package/dist/{dist-C5Bn52W4.js → dist-B8D7McG3.js} +3 -3
  68. package/dist/{dist-_XvgytiS.js → dist-BHCHt1NI.js} +1 -1
  69. package/dist/{dist-CRjAdnqF.js → dist-BRxfaJgs.js} +3 -3
  70. package/dist/{dist-Byj2bV8o.js → dist-BVGtK7Ht.js} +1 -1
  71. package/dist/{dist-BIN5LKyE.js → dist-BaHjhkpZ.js} +2 -2
  72. package/dist/{dist-DVFbIwc5.js → dist-BtKIs9N-.js} +1 -1
  73. package/dist/{dist-BrQGcfQ9.js → dist-C2KzV-vf.js} +2 -2
  74. package/dist/{dist-DJFiGP2k.js → dist-C2wDlYdD.js} +2 -2
  75. package/dist/{dist-BVRM2y1P.js → dist-C8xMLVYi.js} +2 -2
  76. package/dist/dist-CMh_W4y4.js +538 -0
  77. package/dist/{extends-BPB6_lTK.js → dist-CO1AwwG_.js} +516 -561
  78. package/dist/{dist-BzN60Z2s.js → dist-CPGxfVGX.js} +1 -1
  79. package/dist/{dist-C7v-9nUK.js → dist-Cm3iAFMD.js} +3 -3
  80. package/dist/{dist-CGAxEiaM.js → dist-Cs2FijYk.js} +2 -2
  81. package/dist/{dist-hXd65f0_.js → dist-D1PpnuZB.js} +1 -1
  82. package/dist/{dist-VdvbxG72.js → dist-DHOTQar0.js} +1 -1
  83. package/dist/{dist-DcgzXCfy.js → dist-DPcEtqmG.js} +2 -2
  84. package/dist/{dist-D4Z_qdP4.js → dist-Df0wKI0F.js} +1 -1
  85. package/dist/{dist-qhLI-SHd.js → dist-DszxuxS1.js} +2 -2
  86. package/dist/{dist-BEmkrGoQ.js → dist-Kxgjxakw.js} +1 -1
  87. package/dist/{dist-BbSMhhD2.js → dist-KzZ7WELx.js} +2 -2
  88. package/dist/{dist-jWUab2d1.js → dist-lA1Cttkb.js} +1 -1
  89. package/dist/dropdown-menu-CZgjYSAh.js +4502 -0
  90. package/dist/{ebnfDiagram-BXEA7PRR-Dm75pCAS.js → ebnfDiagram-PWID7BFC-WnG4cvSQ.js} +17 -18
  91. package/dist/{erDiagram-JOGREHBK-rVDG3VSQ.js → erDiagram-RLTQ6QDP-DhzKiYHc.js} +1037 -798
  92. package/dist/error-banner-B94dcDmP.js +391 -0
  93. package/dist/error-entries-C0NsUqXl.js +56 -0
  94. package/dist/{esm-CR1nhb1c.js → esm-BElI3qKH.js} +22 -22
  95. package/dist/{esm-C1d0g6Bv.js → esm-G4Fhsgng.js} +51 -37
  96. package/dist/{esm-CesmlDrs.js → esm-h8f7-1CJ.js} +423 -423
  97. package/dist/eventmodeling-NTZA5JFV-D7nP7Uyu.js +3 -0
  98. package/dist/feedback-button-cQcMqpEP.js +380 -0
  99. package/dist/flowDiagram-HODETNUW-6dMdB8pR.js +16 -0
  100. package/dist/{formats-CslNOKy0.js → formats-BR5iGb4s.js} +8 -8
  101. package/dist/{ganttDiagram-PKOTCBZU-9D-LXF6P.js → ganttDiagram-EL5Y4UJY-DbzEBLbl.js} +15 -15
  102. package/dist/gitGraph-4MIJSDKK-Dy9uQXGh.js +3 -0
  103. package/dist/{gitGraphDiagram-DS77QQ5N-BfpsXTLf.js → gitGraphDiagram-WWUBYQGX-BMf0iKi1.js} +10 -10
  104. package/dist/{glide-data-editor-sPZ8fUFn.js → glide-data-editor-DYOxfSzI.js} +2143 -1910
  105. package/dist/{glide-utils-NShAPPtn.js → glide-utils-B3rUT5PI.js} +15 -15
  106. package/dist/globals-BOoLHa7X.js +19 -0
  107. package/dist/highlighted-body-KPVGNVTW-Ad3MKZ18.js +38 -0
  108. package/dist/html-to-image-CeYYvQNb.js +3272 -0
  109. package/dist/info-A6RAGUB7-DKoayiHY.js +3 -0
  110. package/dist/{infoDiagram-6WML65LV-DqCTXHVy.js → infoDiagram-27XIBGKW-BagtCX_5.js} +7 -7
  111. package/dist/{ishikawaDiagram-WSZJBQD7-BCUYmZ1v.js → ishikawaDiagram-5VMMS53U-DByDehYy.js} +8 -8
  112. package/dist/{journeyDiagram-NVQOT4AX-BXB6M4zN.js → journeyDiagram-3NMN7TZE-D2LcKDUO.js} +8 -8
  113. package/dist/{jsx-runtime-CpoKNRgu.js → jsx-runtime-DgDPJV9S.js} +1 -1
  114. package/dist/{kanban-definition-27J2QSJJ-CGRhrDUX.js → kanban-definition-UXKFOSKX-BxwkbBI_.js} +29 -29
  115. package/dist/katex-DFBiSucW.js +21840 -0
  116. package/dist/{label-lwzzBHUW.js → label-BJ49YTvk.js} +6 -6
  117. package/dist/{linear-D5r_1YqD.js → linear-Cnaq3Tfb.js} +4 -4
  118. package/dist/{loader-VZ__bv4g.js → loader-D2W4oM-3.js} +15 -15
  119. package/dist/main.js +8113 -13475
  120. package/dist/mermaid-DPNuhemn.js +3084 -0
  121. package/dist/mermaid-HWGCJPDP-DYCzSILp.js +2 -0
  122. package/dist/mermaid-parser.core-DxuSfYwJ.js +638 -0
  123. package/dist/{mhchem-BjwrxcV-.js → mhchem-DWCF06w9.js} +1 -1
  124. package/dist/{micromark-factory-space-DruVGzeG.js → micromark-factory-space-DGxAzmw8.js} +2 -2
  125. package/dist/{mindmap-definition-FAOFIHXS-C9gRhzZq.js → mindmap-definition-YA3MSWOX-Cgvojw3y.js} +17 -17
  126. package/dist/{node-sql-parser-DesT1xNw.js → node-sql-parser-BiZOJ-FS.js} +0 -3
  127. package/dist/{number-overlay-editor-De2CabLL.js → number-overlay-editor-DZp5bEmI.js} +5 -5
  128. package/dist/objectWithoutPropertiesLoose-BRDcTsfB.js +23 -0
  129. package/dist/packet-AYTQ26CC-Yw7ajchJ.js +3 -0
  130. package/dist/{pegDiagram-VL7TDLO6-Df9IUjMD.js → pegDiagram-XKGWAZYB-DahHhYyN.js} +19 -20
  131. package/dist/{pick-SnHlmIBN.js → pick-CJeoxYOS.js} +1 -1
  132. package/dist/pie-WAS4IAKB-BKdKOAkL.js +3 -0
  133. package/dist/{pieDiagram-7S7Q4E2Y-BmRzQ8ZP.js → pieDiagram-E7YTZNPT-CCtX0oCK.js} +29 -31
  134. package/dist/{process-output-CPy0YKUe.js → process-output-BCTjlyuH.js} +7 -6
  135. package/dist/purify.es-DgKyQviY.js +805 -0
  136. package/dist/{quadrantDiagram-CIZ2JOQS-wFBvWcxS.js → quadrantDiagram-AXDQQJYC-DftINo6g.js} +5 -5
  137. package/dist/radar-RG4KPBEZ-ScR_D_Ts.js +3 -0
  138. package/dist/railroad-74A4TZTK-DQvPSdo5.js +3 -0
  139. package/dist/railroad-abnf-HS5TGJTU-CsJQG2O8.js +3 -0
  140. package/dist/railroad-ebnf-LZEXJU2U-BgEobkSn.js +3 -0
  141. package/dist/railroad-peg-WCYAUIDC-DjLAjofI.js +3 -0
  142. package/dist/{railroadDiagram-AXF67PYL-DxbIatiN.js → railroadDiagram-O6MQD6OU-Du5NsPUb.js} +17 -18
  143. package/dist/{react-dom-C94wmq5w.js → react-dom-BUgJ-ab_.js} +3 -3
  144. package/dist/{react-DMWcHin-.js → react-ofJbxM4x.js} +2 -2
  145. package/dist/{react-vega-DSD5aT7_.js → react-vega-5AcMW8gG.js} +10012 -9369
  146. package/dist/{requirementDiagram-LRYGKXZP-X_x-Y713.js → requirementDiagram-BXWQKSXE-0pmmxL6_.js} +20 -19
  147. package/dist/{reveal-component-CVsYlb9J.js → reveal-component-C9h2Lubu.js} +1303 -1237
  148. package/dist/{sankeyDiagram-W5VNT64P-BYJALbbm.js → sankeyDiagram-P5KCCOFB-CDKR5TJH.js} +4 -4
  149. package/dist/{select-3qHpFKF9.js → select-C6plhmZr.js} +317 -562
  150. package/dist/{sequenceDiagram-SI44F4Z6-BiPBe3Sv.js → sequenceDiagram-WJ2MYXX4-DB-ChWwU.js} +11 -11
  151. package/dist/{sizeCapture-X5ZJPWSS-BKLDeh5H.js → sizeCapture-INFHLROL-Dx5SpkQF.js} +2 -2
  152. package/dist/{spec-CutWdqo3.js → spec-CAYwFeV6.js} +4 -4
  153. package/dist/{src-C31ngt98.js → src-CA_m4ONl.js} +298 -303
  154. package/dist/{stateDiagram-OKZ733FA-B7QyNBxH.js → stateDiagram-D77RDMKH-orN6AwMx.js} +19 -20
  155. package/dist/stateDiagram-v2-MP3YSRHH-o05_rJN8.js +30 -0
  156. package/dist/style.css +2 -2
  157. package/dist/{swimlanes-SLNWSIFB-B4f8VP3b.js → swimlanes-42K2YHIH-BkSDcqDT.js} +1501 -1591
  158. package/dist/swimlanesDiagram-VR7AAH4N-D_nFQfRc.js +30 -0
  159. package/dist/{swiper-component-C90YjhaV.js → swiper-component-IOUowglM.js} +39 -26
  160. package/dist/{time-aWGfjN8x.js → time-BieKwdYS.js} +42 -42
  161. package/dist/{timeline-definition-Z64GVDOM-Dacq_5lr.js → timeline-definition-24CTP7MA-BuqJUIRZ.js} +7 -7
  162. package/dist/{toDate-CpXBeDq0.js → toDate-WXg1yLcI.js} +7 -7
  163. package/dist/toast-DvY70eFX.js +1834 -0
  164. package/dist/tooltip-qB8NlSqV.js +1742 -0
  165. package/dist/treeView-Q6P3EWNA-DuRV97YF.js +3 -0
  166. package/dist/treemap-WGGIJYW6-AsLq-uwC.js +3 -0
  167. package/dist/use-restore-focus-D5D9xEjZ.js +5946 -0
  168. package/dist/{use-vega-container-remeasure-DUW4tRxq.js → use-vega-container-remeasure-4DwJbPWu.js} +2 -2
  169. package/dist/{useAsyncData-CH6-CgJF.js → useAsyncData-BD6AStMt.js} +3 -3
  170. package/dist/{useDateFormatter-Cp9Jdxls.js → useDateFormatter-BRTPsUK7.js} +8 -8
  171. package/dist/{useDeepCompareMemoize-JC87C_jO.js → useDeepCompareMemoize-D_HNdyvI.js} +2 -2
  172. package/dist/{useIframeCapabilities-De3C-9b4.js → useIframeCapabilities-CVtNkI3Y.js} +2 -2
  173. package/dist/{useLifecycle-B4RGlyf0.js → useLifecycle-DPgiiwwU.js} +6 -6
  174. package/dist/useNumberFormatter-BfDpa21j.js +251 -0
  175. package/dist/useTheme-DzXmYZ5V.js +904 -0
  176. package/dist/{vega-component-ovBXgp91.js → vega-component-D0QXopU8.js} +34 -34
  177. package/dist/{vega-embed-container-TJ5z5dZw.js → vega-embed-container-CbnTccZi.js} +6 -6
  178. package/dist/vega-loader.browser-D95WfUcj.js +1139 -0
  179. package/dist/{vennDiagram-T6HMQDX7-D-Rcq3Iu.js → vennDiagram-4TSXK5OY-CTUlJ2PK.js} +9 -9
  180. package/dist/wardley-WFR3VGLG-Na-tFhQJ.js +3 -0
  181. package/dist/{wardleyDiagram-T6FBY63Y-Z-Og7_Wd.js → wardleyDiagram-VM6X3IG4-3evLUb0M.js} +10 -10
  182. package/dist/{xychartDiagram-ELKLHX3M-BDJ2dITA.js → xychartDiagram-S5SC5T6Z-DL2E3K8-.js} +248 -139
  183. package/package.json +54 -57
  184. package/src/__mocks__/notebook.ts +1 -0
  185. package/src/__mocks__/requests.ts +7 -0
  186. package/src/__tests__/__snapshots__/CellStatus.test.tsx.snap +12 -12
  187. package/src/__tests__/fixtures/pair-preview.ts +19 -0
  188. package/src/__tests__/mount.test.ts +18 -0
  189. package/src/components/app-config/optional-features.tsx +6 -0
  190. package/src/components/chat/acp/__tests__/agent-panel-initialization.test.tsx +394 -0
  191. package/src/components/chat/acp/__tests__/agent-panel.test.tsx +272 -0
  192. package/src/components/chat/acp/__tests__/state.test.ts +2 -2
  193. package/src/components/chat/acp/agent-panel.tsx +115 -74
  194. package/src/components/chat/acp/state.ts +1 -1
  195. package/src/components/data-table/__tests__/filters.test.ts +21 -21
  196. package/src/components/data-table/charts/__tests__/spec.test.ts +5 -10
  197. package/src/components/data-table/column-explorer-panel/column-explorer.tsx +2 -6
  198. package/src/components/data-table/column-summary/chart-spec-model.tsx +1 -1
  199. package/src/components/data-table/columns.tsx +64 -67
  200. package/src/components/editor/RecoveryButton.tsx +1 -0
  201. package/src/components/editor/__tests__/dynamic-favicon.test.tsx +36 -2
  202. package/src/components/editor/actions/__tests__/pair-with-agent-commands.test.ts +115 -0
  203. package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +218 -0
  204. package/src/components/editor/actions/pair-with-agent-commands.ts +46 -9
  205. package/src/components/editor/actions/pair-with-agent-modal.tsx +49 -23
  206. package/src/components/editor/ai/add-cell-with-ai.tsx +1 -0
  207. package/src/components/editor/ai/ai-completion-editor.tsx +3 -1
  208. package/src/components/editor/alerts/__tests__/connection-notice.test.tsx +186 -0
  209. package/src/components/editor/alerts/__tests__/startup-progress.test.tsx +185 -0
  210. package/src/components/editor/alerts/connecting-alert.tsx +1 -66
  211. package/src/components/editor/alerts/connection-notice.tsx +186 -0
  212. package/src/components/editor/alerts/startup-output.tsx +173 -0
  213. package/src/components/editor/alerts/startup-progress.tsx +204 -0
  214. package/src/components/editor/app-container.tsx +9 -3
  215. package/src/components/editor/boundary/ErrorBoundary.tsx +38 -16
  216. package/src/components/editor/boundary/__tests__/ErrorBoundary.test.tsx +311 -0
  217. package/src/components/editor/cell/__tests__/toolbar.test.tsx +73 -0
  218. package/src/components/editor/cell/code/__tests__/rtc-editor-config.test.ts +50 -0
  219. package/src/components/editor/cell/code/cell-editor.tsx +41 -42
  220. package/src/components/editor/cell/toolbar.tsx +11 -0
  221. package/src/components/editor/chrome/components/__tests__/feedback-button.test.tsx +94 -0
  222. package/src/components/editor/chrome/components/feedback-button.tsx +63 -42
  223. package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +164 -8
  224. package/src/components/editor/chrome/panels/__tests__/sandbox-details.test.tsx +358 -0
  225. package/src/components/editor/chrome/panels/__tests__/sandbox-recovery.test.tsx +168 -0
  226. package/src/components/editor/chrome/panels/packages-panel.tsx +113 -175
  227. package/src/components/editor/chrome/panels/sandbox-controller.tsx +106 -0
  228. package/src/components/editor/chrome/panels/sandbox-details-state.ts +38 -0
  229. package/src/components/editor/chrome/panels/sandbox-panel.tsx +230 -0
  230. package/src/components/editor/chrome/panels/sandbox-toggle.tsx +52 -0
  231. package/src/components/editor/chrome/wrapper/app-chrome.tsx +7 -0
  232. package/src/components/editor/chrome/wrapper/footer-items/backend-status.tsx +3 -1
  233. package/src/components/editor/code/__tests__/readonly-diff.test.tsx +65 -0
  234. package/src/components/editor/code/readonly-diff.tsx +1 -1
  235. package/src/components/editor/connections/database/__tests__/__snapshots__/as-code.test.ts.snap +98 -98
  236. package/src/components/editor/connections/database/__tests__/as-code.test.ts +11 -5
  237. package/src/components/editor/dynamic-favicon.tsx +17 -10
  238. package/src/components/editor/file-tree/__tests__/file-explorer-navigation.test.tsx +13 -11
  239. package/src/components/editor/file-tree/__tests__/tree-actions.test.tsx +54 -0
  240. package/src/components/editor/file-tree/tree-actions.tsx +11 -14
  241. package/src/components/editor/header/__tests__/status.test.tsx +8 -20
  242. package/src/components/editor/header/filename-input.tsx +2 -0
  243. package/src/components/editor/header/status.tsx +5 -3
  244. package/src/components/editor/navigation/clipboard.ts +1 -0
  245. package/src/components/editor/notebook-cell.tsx +4 -2
  246. package/src/components/editor/output/JsonOutput.tsx +182 -632
  247. package/src/components/editor/output/Outputs.css +49 -17
  248. package/src/components/editor/output/__tests__/JsonOutput-interactions.test.tsx +254 -0
  249. package/src/components/editor/output/__tests__/JsonOutput-mimetype.test.tsx +41 -46
  250. package/src/components/editor/output/__tests__/{json-output.test.ts → json-output-formatting.test.ts} +113 -75
  251. package/src/components/editor/output/__tests__/json-output-pagination.test.ts +252 -0
  252. package/src/components/editor/output/json-output/formatting.ts +167 -0
  253. package/src/components/editor/output/json-output/pagination.ts +155 -0
  254. package/src/components/editor/output/json-output/renderers.tsx +196 -0
  255. package/src/components/editor/output/json-output/themes.ts +52 -0
  256. package/src/components/editor/package-alert.tsx +112 -66
  257. package/src/components/editor/renderers/cell-array.tsx +1 -5
  258. package/src/components/editor/renderers/slides-layout/__tests__/plugin.test.ts +3 -7
  259. package/src/components/slides/slide-form.tsx +1 -0
  260. package/src/components/slides/slide-notes-editor.tsx +1 -0
  261. package/src/components/tracing/tracing-spec.ts +1 -1
  262. package/src/components/ui/__tests__/badge.test.ts +6 -4
  263. package/src/components/ui/use-toast.ts +8 -0
  264. package/src/core/MarimoApp.tsx +0 -2
  265. package/src/core/ai/context/providers/__tests__/error.test.ts +16 -22
  266. package/src/core/alerts/__tests__/environment.test.ts +183 -0
  267. package/src/core/alerts/__tests__/state.test.tsx +240 -0
  268. package/src/core/alerts/environment.ts +57 -0
  269. package/src/core/alerts/state.ts +155 -99
  270. package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +296 -259
  271. package/src/core/cells/__tests__/apply-transaction.test.ts +34 -0
  272. package/src/core/cells/__tests__/cell.test.ts +61 -0
  273. package/src/core/cells/__tests__/cells.test.ts +182 -37
  274. package/src/core/cells/__tests__/document-changes.test.ts +39 -1
  275. package/src/core/cells/__tests__/outputs.test.tsx +117 -0
  276. package/src/core/cells/__tests__/pending-delete-service.test.tsx +4 -3
  277. package/src/core/cells/__tests__/session.test.ts +7 -0
  278. package/src/core/cells/cell.ts +9 -1
  279. package/src/core/cells/cells.ts +13 -18
  280. package/src/core/cells/document-changes.ts +11 -1
  281. package/src/core/cells/outputs.ts +66 -9
  282. package/src/core/cells/pending-delete-service.ts +12 -16
  283. package/src/core/cells/session.ts +2 -0
  284. package/src/core/cells/types.ts +2 -0
  285. package/src/core/codemirror/find-replace/__tests__/navigate.test.ts +8 -16
  286. package/src/core/codemirror/language/__tests__/sql.test.ts +70 -2
  287. package/src/core/codemirror/language/embedded/__tests__/embedded-python.test.ts +4 -8
  288. package/src/core/codemirror/language/languages/sql/sql.ts +16 -1
  289. package/src/core/codemirror/rtc/extension.ts +5 -2
  290. package/src/core/config/pair.ts +19 -0
  291. package/src/core/datasets/__tests__/data-source.test.ts +2 -0
  292. package/src/core/datasets/data-source-connections.ts +15 -10
  293. package/src/core/edit-app.tsx +8 -5
  294. package/src/core/errors/state.ts +1 -2
  295. package/src/core/export/__tests__/hooks.test.ts +1 -6
  296. package/src/core/islands/bootstrap.ts +3 -1
  297. package/src/core/islands/bridge.ts +3 -0
  298. package/src/core/islands/worker-factory.ts +2 -12
  299. package/src/core/kernel/messages.ts +2 -2
  300. package/src/core/network/__tests__/requests-network.test.ts +4 -3
  301. package/src/core/network/connection-notice.ts +117 -0
  302. package/src/core/network/connection.ts +19 -0
  303. package/src/core/network/requests-lazy.ts +3 -0
  304. package/src/core/network/requests-network.ts +12 -0
  305. package/src/core/network/requests-static.ts +3 -0
  306. package/src/core/network/requests-toasting.tsx +3 -0
  307. package/src/core/network/resolve.ts +2 -1
  308. package/src/core/network/types.ts +7 -0
  309. package/src/core/network/useConnectionNotice.ts +53 -0
  310. package/src/core/packages/__tests__/usePackageAction.test.tsx +129 -0
  311. package/src/core/packages/__tests__/useSandboxController.test.tsx +246 -0
  312. package/src/core/packages/package-data.ts +35 -0
  313. package/src/core/packages/sandbox-state.ts +67 -0
  314. package/src/core/packages/toast-components.tsx +11 -0
  315. package/src/core/packages/useInstallPackage.ts +1 -1
  316. package/src/core/packages/usePackageAction.ts +51 -0
  317. package/src/core/packages/usePackageDependencies.ts +22 -0
  318. package/src/core/packages/useSandboxController.ts +208 -0
  319. package/src/core/run-app.tsx +2 -0
  320. package/src/core/static/__tests__/files.test.ts +12 -30
  321. package/src/core/wasm/__tests__/bootstrap.test.ts +63 -0
  322. package/src/core/wasm/__tests__/bridge.test.ts +13 -4
  323. package/src/core/wasm/__tests__/fs.test.ts +22 -0
  324. package/src/core/wasm/__tests__/runtime-config.test.ts +65 -0
  325. package/src/core/wasm/bridge.ts +35 -23
  326. package/src/core/wasm/rpc.ts +2 -0
  327. package/src/core/wasm/runtime-config.ts +31 -0
  328. package/src/core/wasm/worker/bootstrap.ts +21 -11
  329. package/src/core/wasm/worker/constants.ts +1 -0
  330. package/src/core/wasm/worker/fs.ts +4 -0
  331. package/src/core/wasm/worker/getController.ts +3 -1
  332. package/src/core/wasm/worker/save-worker.ts +10 -9
  333. package/src/core/wasm/worker/types.ts +6 -4
  334. package/src/core/wasm/worker/worker.ts +9 -9
  335. package/src/core/websocket/__tests__/useMarimoKernelConnection.hook.test.tsx +404 -3
  336. package/src/core/websocket/__tests__/useMarimoKernelConnection.test.ts +8 -6
  337. package/src/core/websocket/transports/transport.ts +1 -1
  338. package/src/core/websocket/transports/ws.ts +1 -1
  339. package/src/core/websocket/types.ts +11 -1
  340. package/src/core/websocket/useMarimoKernelConnection.tsx +63 -16
  341. package/src/hooks/useDebounce.ts +2 -0
  342. package/src/mount.tsx +3 -0
  343. package/src/plugins/core/BadPlugin.tsx +3 -14
  344. package/src/plugins/core/RenderHTML.tsx +22 -2
  345. package/src/plugins/core/__test__/RenderHTML.test.ts +187 -0
  346. package/src/plugins/core/registerReactComponent.tsx +0 -1
  347. package/src/plugins/impl/FileBrowserPlugin.tsx +10 -4
  348. package/src/plugins/impl/FormPlugin.tsx +2 -0
  349. package/src/plugins/impl/MatrixPlugin.tsx +2 -0
  350. package/src/plugins/impl/RangeSliderPlugin.tsx +2 -0
  351. package/src/plugins/impl/SliderPlugin.tsx +2 -0
  352. package/src/plugins/impl/__tests__/FileBrowserPlugin.test.tsx +50 -1
  353. package/src/plugins/impl/data-editor/__tests__/glide-data-editor.test.tsx +23 -18
  354. package/src/plugins/impl/data-frames/panel.tsx +1 -0
  355. package/src/plugins/impl/image-comparison/ImageComparisonComponent.tsx +1 -1
  356. package/src/plugins/impl/image-comparison/__tests__/ImageComparisonComponent.test.tsx +15 -0
  357. package/src/plugins/impl/vega/__tests__/make-selectable.test.ts +42 -48
  358. package/src/plugins/layout/AccordionPlugin.tsx +7 -2
  359. package/src/plugins/layout/JsonOutputPlugin.tsx +7 -10
  360. package/src/plugins/layout/__test__/AccordionPlugin.test.tsx +66 -0
  361. package/src/stories/sandbox.stories.tsx +488 -0
  362. package/src/utils/__tests__/path.test.ts +6 -5
  363. package/src/utils/__tests__/worker.test.ts +81 -0
  364. package/src/utils/worker.ts +22 -0
  365. package/dist/ErrorBoundary-CR-ZQN_c.js +0 -125
  366. package/dist/_baseMerge-Dv8bBloN.js +0 -73
  367. package/dist/_initCloneObject---vediN2.js +0 -95
  368. package/dist/arc-DOVLrCPd.js +0 -82
  369. package/dist/architecture-TIHT7OUA-MiJpPpQW.js +0 -3
  370. package/dist/array-CApNbSU9.js +0 -6
  371. package/dist/assets/__vite-browser-external-BdKGjRZ4.js +0 -1
  372. package/dist/assets/worker-DlIBfp9S.js +0 -98
  373. package/dist/blockDiagram-VBNYF7ZC-Bc--I65C.js +0 -3158
  374. package/dist/chat-ui-BSwr409J.js +0 -8455
  375. package/dist/chunk-3NCLNEKW-BIp_lB_4.js +0 -14
  376. package/dist/chunk-5FCAYU7R-C_HO9A1J.js +0 -23
  377. package/dist/chunk-5HE753X5-Bixm_MV-.js +0 -38
  378. package/dist/chunk-5JV3BV7I-CH5J3iZY.js +0 -54
  379. package/dist/chunk-5TONJI2A-DG6gDiSS.js +0 -62
  380. package/dist/chunk-7BUUIJ7U-5afmIPeo.js +0 -82
  381. package/dist/chunk-BIQX33UG-7TMBUf89.js +0 -23
  382. package/dist/chunk-CQNSW5MT-Cnqqsxt-.js +0 -38
  383. package/dist/chunk-CYSBUYHQ-BAbymFHR.js +0 -23
  384. package/dist/chunk-EMLP6XTP-Bsz-QL77.js +0 -23
  385. package/dist/chunk-J7OUQ5F2-CM_meqZH.js +0 -57
  386. package/dist/chunk-JG7HCLWE-DUnCDgPk.js +0 -62
  387. package/dist/chunk-MOZMSUNE-BG6IHvzT.js +0 -35
  388. package/dist/chunk-OSBZ3O6U-CYwj_bvR.js +0 -23
  389. package/dist/chunk-QBLGF6JB-B4vJ4j8J.js +0 -23
  390. package/dist/chunk-QR6OTTB3-D4wH2ldi.js +0 -3738
  391. package/dist/chunk-R7FJI6CG-Blf8FcmZ.js +0 -64
  392. package/dist/chunk-RYQCIY6F-BWbIlBIH.js +0 -241
  393. package/dist/chunk-U6XO7XAA-COw445DS.js +0 -63
  394. package/dist/chunk-UBXNYLIW-BHigVU4H.js +0 -32
  395. package/dist/chunk-YOTPTUD7-Dn044Q_d.js +0 -30
  396. package/dist/chunk-ZIRB5QZD-BR4TMfOa.js +0 -1153
  397. package/dist/classDiagram-JCYQIIEL-DmXSLxe_.js +0 -30
  398. package/dist/classDiagram-v2-OCEON4UE-CooqVexA.js +0 -30
  399. package/dist/click-outside-container-D_URcm3D.js +0 -167
  400. package/dist/code-block-37QAKDTI-CQrzBOr-.js +0 -107
  401. package/dist/copy-CD7S58CY.js +0 -141
  402. package/dist/cynefin-VYW2F7L2-CeHAxOEX.js +0 -3
  403. package/dist/dagre-VZM6K2ZE-CJggWPTJ.js +0 -328
  404. package/dist/dist-3_o3T78I.js +0 -1360
  405. package/dist/dist-ABQjhuek.js +0 -64
  406. package/dist/dist-CO5R6VMi.js +0 -538
  407. package/dist/dropdown-menu-DhCfcscX.js +0 -5858
  408. package/dist/emotion-is-prop-valid.esm-M57VsT8j.js +0 -14
  409. package/dist/error-banner-CsXbEEjo.js +0 -611
  410. package/dist/eventmodeling-45OFAUF4-B_lWZ-Pd.js +0 -3
  411. package/dist/flowDiagram-UKHOOZJN-g1AmrZzM.js +0 -17
  412. package/dist/gitGraph-TEB2WS4Q-Dj-eXUph.js +0 -3
  413. package/dist/info-DKCQHKI2-Bhb-pe2e.js +0 -3
  414. package/dist/line-CuKjIQa1.js +0 -34
  415. package/dist/map-BJpOfPlL.js +0 -149
  416. package/dist/math-BXtKgYah.js +0 -10
  417. package/dist/mermaid-4DMBBIKO-Ddp9TgNp.js +0 -2
  418. package/dist/mermaid-Bi23Ibnw.js +0 -1173
  419. package/dist/mermaid-parser.core-Din9FvSi.js +0 -99
  420. package/dist/packet-7NZHBO7P-fbkIIyKE.js +0 -3
  421. package/dist/path-BNHuHZP8.js +0 -87
  422. package/dist/pie-RZYD4A2V-CLRIsxY1.js +0 -3
  423. package/dist/purify.es-CB-YO1Hn.js +0 -774
  424. package/dist/radar-I7S5WNFK-BGzCbgXu.js +0 -3
  425. package/dist/railroad-3IZDKUUU-CvQIIcuU.js +0 -3
  426. package/dist/railroad-abnf-AHOZXSZD-CMwtlNtu.js +0 -3
  427. package/dist/railroad-ebnf-EBAXGLYW-n1QUOAsh.js +0 -3
  428. package/dist/railroad-peg-LSFZ7HO6-PjnrpmCs.js +0 -3
  429. package/dist/schemas-DzK6U94N.js +0 -6057
  430. package/dist/stateDiagram-v2-UEYNNEHI-BTRUs8Il.js +0 -30
  431. package/dist/step-CKczO72Q.js +0 -670
  432. package/dist/swimlanesDiagram-ULZ7WXOC-NBmgBl9G.js +0 -31
  433. package/dist/timer-DpDyj9G0.js +0 -596
  434. package/dist/tooltip-Dt77sBeT.js +0 -390
  435. package/dist/treeView-QDETBFTQ-DU8LwHuf.js +0 -3
  436. package/dist/treemap-6X3UGDF4-DUk4-k_O.js +0 -3
  437. package/dist/useTheme-CVJ0UOf2.js +0 -841
  438. package/dist/vega-loader.browser-DEggZzni.js +0 -1083
  439. package/dist/wardley-OPB4EBWU-Db-AaGas.js +0 -3
  440. package/src/components/editor/output/EmotionCacheProvider.tsx +0 -32
@@ -41,10 +41,9 @@ export class DefaultWasmController implements WasmController {
41
41
  return this.pyodide;
42
42
  }
43
43
 
44
- async bootstrap(opts: {
45
- version: string;
46
- pyodideVersion: string;
47
- }): Promise<PyodideInterface> {
44
+ async bootstrap(
45
+ opts: Parameters<WasmController["bootstrap"]>[0],
46
+ ): Promise<PyodideInterface> {
48
47
  const pyodide = await this.loadPyodideAndPackages(opts);
49
48
 
50
49
  if (MAKE_SNAPSHOT) {
@@ -55,10 +54,9 @@ export class DefaultWasmController implements WasmController {
55
54
  return pyodide;
56
55
  }
57
56
 
58
- private async loadPyodideAndPackages(opts: {
59
- version: string;
60
- pyodideVersion: string;
61
- }): Promise<PyodideInterface> {
57
+ private async loadPyodideAndPackages(
58
+ opts: Parameters<WasmController["bootstrap"]>[0],
59
+ ): Promise<PyodideInterface> {
62
60
  if (!loadPyodide) {
63
61
  throw new Error("loadPyodide is not defined");
64
62
  }
@@ -68,7 +66,9 @@ export class DefaultWasmController implements WasmController {
68
66
  // Without this, this fails in Firefox with
69
67
  // `Could not extract indexURL path from pyodide module`
70
68
  // This fixes for Firefox and does not break Chrome/others
71
- const indexURL = `https://cdn.jsdelivr.net/pyodide/${opts.pyodideVersion}/full/`;
69
+ const indexURL =
70
+ opts.pyodideIndexUrl ??
71
+ `https://cdn.jsdelivr.net/pyodide/${opts.pyodideVersion}/full/`;
72
72
  const pyodide = await loadPyodide({
73
73
  // Perf: These get loaded while pyodide is being bootstrapped
74
74
  packages: [
@@ -81,16 +81,26 @@ export class DefaultWasmController implements WasmController {
81
81
  "packaging",
82
82
  ],
83
83
  _makeSnapshot: MAKE_SNAPSHOT,
84
- lockFileURL: `https://wasm.marimo.app/pyodide-lock.json?v=${opts.version}&pyodide=${opts.pyodideVersion}`,
84
+ lockFileURL:
85
+ opts.pyodideLockfileUrl ??
86
+ `https://wasm.marimo.app/pyodide-lock.json?v=${opts.version}&pyodide=${opts.pyodideVersion}`,
85
87
  indexURL,
86
88
  // Since Pyodide 0.28.0, when lockFileURL is set, the package base URL
87
89
  // defaults to the lockfile's URL (wasm.marimo.app) instead of indexURL.
88
90
  // Unlike Node, browsers get no CDN fallback on a failed fetch, so we
89
- // should pin packageBaseUrl back to the jsDelivr CDN to restore
91
+ // should pin packageBaseUrl to indexURL to restore
90
92
  // the resolution akin to pre-0.28.
91
93
  packageBaseUrl: indexURL,
92
94
  convertNullToNone: true,
93
95
  });
96
+ if (opts.pypiIndexUrl) {
97
+ const micropip = pyodide.pyimport("micropip");
98
+ try {
99
+ micropip.set_index_urls(opts.pypiIndexUrl);
100
+ } finally {
101
+ micropip.destroy();
102
+ }
103
+ }
94
104
  this.pyodide = pyodide;
95
105
  span.end("ok");
96
106
  return pyodide;
@@ -1,2 +1,3 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
+ export const CUSTOM_CONTROLLER_SUFFIX = "::controller";
2
3
  export const TRANSPORT_ID = "marimo-transport";
@@ -20,6 +20,10 @@ export const WasmFileSystem = {
20
20
  FS.chdir(HOME_DIR);
21
21
  },
22
22
  mountFS: (pyodide: PyodideInterface) => {
23
+ // Opaque origins (single-file exports) use the in-memory filesystem.
24
+ if (globalThis.origin === "null") {
25
+ return;
26
+ }
23
27
  const FS = getFS(pyodide);
24
28
  // Mount the filesystem
25
29
  FS.mount(pyodide.FS.filesystems.IDBFS, { root: "." }, HOME_DIR);
@@ -1,5 +1,6 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
  import { DefaultWasmController } from "./bootstrap";
3
+ import { CUSTOM_CONTROLLER_SUFFIX } from "./constants";
3
4
  import type { WasmController } from "./types";
4
5
 
5
6
  // Load the controller
@@ -8,7 +9,8 @@ export async function getController(version: string): Promise<WasmController> {
8
9
  // Hosts that provide a custom /wasm/controller.js opt in via the worker
9
10
  // name (see bridge.ts). Default: skip the dynamic import to avoid a
10
11
  // guaranteed-404 round trip on the standard build.
11
- const hasCustomController = self.name?.includes("::controller") ?? false;
12
+ const hasCustomController =
13
+ self.name?.includes(CUSTOM_CONTROLLER_SUFFIX) ?? false;
12
14
  if (!hasCustomController) {
13
15
  return new DefaultWasmController();
14
16
  }
@@ -9,9 +9,11 @@ import {
9
9
  } from "rpc-anywhere";
10
10
  import type { SaveNotebookRequest } from "@/core/network/types";
11
11
  import { decodeUtf8 } from "@/utils/strings";
12
+ import { Deferred } from "../../../utils/Deferred";
12
13
  import { prettyError } from "../../../utils/errors";
13
14
  import { Logger } from "../../../utils/Logger";
14
15
  import type { ParentSchema } from "../rpc";
16
+ import type { WasmRuntimeConfig } from "../runtime-config";
15
17
  import { TRANSPORT_ID } from "./constants";
16
18
  import { WasmFileSystem } from "./fs";
17
19
  import { getController } from "./getController";
@@ -25,18 +27,20 @@ declare const self: Window & {
25
27
  pyodide: PyodideInterface;
26
28
  };
27
29
 
30
+ const runtimeConfig = new Deferred<WasmRuntimeConfig>();
31
+
28
32
  // Initialize
29
33
  async function loadPyodideAndPackages() {
30
34
  try {
31
35
  // Import pyodide
32
- const marimoVersion = getMarimoVersion();
33
- const pyodideVersion = getPyodideVersion(marimoVersion);
36
+ const config = await runtimeConfig.promise;
37
+ const pyodideVersion = getPyodideVersion(config.version);
34
38
 
35
39
  // Bootstrap the controller
36
- const controller = await getController(marimoVersion);
40
+ const controller = await getController(config.version);
37
41
  self.controller = controller;
38
42
  self.pyodide = await controller.bootstrap({
39
- version: marimoVersion,
43
+ ...config,
40
44
  pyodideVersion: pyodideVersion,
41
45
  });
42
46
 
@@ -100,9 +104,6 @@ const rpc = createRPC<SaveWorkerSchema, ParentSchema>({
100
104
  requestHandler,
101
105
  });
102
106
 
103
- rpc.send("ready", {});
107
+ rpc.addMessageListener("bootstrap", (config) => runtimeConfig.resolve(config));
104
108
 
105
- function getMarimoVersion() {
106
- // Worker name is "<version>" or "<version>::<capability>" — see bridge.ts.
107
- return self.name.split("::")[0];
108
- }
109
+ rpc.send("ready", {});
@@ -1,5 +1,6 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
  import type { PyodideInterface } from "pyodide";
3
+ import type { WasmRuntimeConfig } from "../runtime-config";
3
4
  import type { UserConfig } from "@/core/config/config-schema";
4
5
  import type {
5
6
  CommandMessage,
@@ -43,10 +44,11 @@ export interface WasmController {
43
44
  * Prepare the wasm environment
44
45
  * @param opts.version - The marimo version
45
46
  */
46
- bootstrap(opts: {
47
- version: string;
48
- pyodideVersion: string;
49
- }): Promise<PyodideInterface>;
47
+ bootstrap(
48
+ opts: WasmRuntimeConfig & {
49
+ pyodideVersion: string;
50
+ },
51
+ ): Promise<PyodideInterface>;
50
52
  /**
51
53
  * Mount the filesystem
52
54
  * @param opts.code - The code to mount
@@ -22,6 +22,7 @@ import { prettyError } from "../../../utils/errors";
22
22
  import { invariant } from "../../../utils/invariant";
23
23
  import { Logger } from "../../../utils/Logger";
24
24
  import type { ParentSchema } from "../rpc";
25
+ import type { WasmRuntimeConfig } from "../runtime-config";
25
26
  import { TRANSPORT_ID } from "./constants";
26
27
  import { WasmFileSystem } from "./fs";
27
28
  import { getController } from "./getController";
@@ -47,18 +48,20 @@ declare const self: Window & {
47
48
 
48
49
  const workerInitSpan = t.startSpan("worker:init");
49
50
 
51
+ const runtimeConfig = new Deferred<WasmRuntimeConfig>();
52
+
50
53
  // Initialize pyodide
51
54
  async function loadPyodideAndPackages() {
52
55
  try {
53
- const marimoVersion = getMarimoVersion();
54
- const pyodideVersion = getPyodideVersion(marimoVersion);
55
- const controller = await t.wrapAsync(getController)(marimoVersion);
56
+ const config = await runtimeConfig.promise;
57
+ const pyodideVersion = getPyodideVersion(config.version);
58
+ const controller = await t.wrapAsync(getController)(config.version);
56
59
  self.controller = controller;
57
60
  rpc.send.initializingMessage({
58
61
  message: "Loading marimo...",
59
62
  });
60
63
  self.pyodide = await t.wrapAsync(controller.bootstrap.bind(controller))({
61
- version: marimoVersion,
64
+ ...config,
62
65
  pyodideVersion: pyodideVersion,
63
66
  });
64
67
  } catch (error) {
@@ -354,6 +357,8 @@ const rpc = createRPC<WorkerSchema, ParentSchema>({
354
357
  requestHandler,
355
358
  });
356
359
 
360
+ rpc.addMessageListener("bootstrap", (config) => runtimeConfig.resolve(config));
361
+
357
362
  rpc.send("ready", {});
358
363
 
359
364
  /// Listeners
@@ -374,9 +379,4 @@ const namesThatRequireSync = new Set<keyof RawBridge>([
374
379
  "update_file",
375
380
  ]);
376
381
 
377
- function getMarimoVersion() {
378
- // Worker name is "<version>" or "<version>::<capability>" — see bridge.ts.
379
- return self.name.split("::")[0];
380
- }
381
-
382
382
  const pyodideReadyPromise = t.wrapAsync(loadPyodideAndPackages)();
@@ -1,11 +1,25 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
  // @vitest-environment jsdom
3
3
 
4
- import { act, renderHook } from "@testing-library/react";
4
+ import {
5
+ act,
6
+ fireEvent,
7
+ render,
8
+ renderHook,
9
+ screen,
10
+ } from "@testing-library/react";
5
11
  import { createStore, Provider as JotaiProvider } from "jotai";
6
12
  import type React from "react";
7
13
  import { ErrorBoundary } from "react-error-boundary";
8
- import { beforeEach, describe, expect, it, type Mock, vi } from "vitest";
14
+ import {
15
+ afterEach,
16
+ beforeEach,
17
+ describe,
18
+ expect,
19
+ it,
20
+ type Mock,
21
+ vi,
22
+ } from "vitest";
9
23
 
10
24
  vi.mock("@/core/websocket/useWebSocket", async () => {
11
25
  const actual =
@@ -26,9 +40,17 @@ vi.mock("@/core/runtime/config", async () => {
26
40
  };
27
41
  });
28
42
 
43
+ import { MockNotebook } from "@/__mocks__/notebook";
44
+ import { cellId } from "@/__tests__/branded";
45
+ import { alertAtom, getPackageAlert } from "@/core/alerts/state";
46
+ import { notebookAtom } from "@/core/cells/cells";
47
+ import { AppConfigSchema } from "@/core/config/config-schema";
48
+ import { ConnectionNotice } from "@/components/editor/alerts/connection-notice";
49
+ import { kernelStartupErrorAtom } from "@/core/errors/state";
50
+ import type { NotificationPayload } from "@/core/kernel/messages";
29
51
  import { useRuntimeManager } from "@/core/runtime/config";
30
52
  import { initialRunCompletedAtom } from "../../kernel/state";
31
- import { connectionAtom } from "../../network/connection";
53
+ import { connectionAtom, startupProgressAtom } from "../../network/connection";
32
54
  import type { SessionId } from "../../kernel/session";
33
55
  import { WebSocketClosedReason, WebSocketState } from "../types";
34
56
  import type { IConnectionTransport } from "../transports/transport";
@@ -188,3 +210,382 @@ describe("useMarimoKernelConnection messages", () => {
188
210
  expect(store.get(initialRunCompletedAtom)).toBe(true);
189
211
  });
190
212
  });
213
+
214
+ it.each(["kernel-ready", "reconnected"])(
215
+ "waits for %s before marking the session connected",
216
+ async (op) => {
217
+ const store = createStore();
218
+ store.set(connectionAtom, { state: WebSocketState.CONNECTING });
219
+ vi.mocked(useConnectionTransport).mockClear();
220
+ vi.mocked(useConnectionTransport).mockReturnValue(
221
+ makeTransport(WebSocket.OPEN),
222
+ );
223
+ vi.mocked(useRuntimeManager).mockReturnValue(
224
+ makeRuntimeManager() as unknown as ReturnType<typeof useRuntimeManager>,
225
+ );
226
+ renderConnectionHook(store);
227
+ const options = vi.mocked(useConnectionTransport).mock.calls.at(-1)![0];
228
+
229
+ await act(async () => {
230
+ await options.onOpen(new Event("open"));
231
+ });
232
+ expect(store.get(connectionAtom).state).toBe(WebSocketState.CONNECTING);
233
+
234
+ act(() => {
235
+ options.onMessage(
236
+ new MessageEvent("message", {
237
+ data: JSON.stringify({
238
+ op,
239
+ data: {
240
+ op,
241
+ cell_ids: [],
242
+ codes: [],
243
+ names: [],
244
+ configs: [],
245
+ layout: null,
246
+ resumed: true,
247
+ ui_values: {},
248
+ last_executed_code: {},
249
+ last_execution_time: {},
250
+ app_config: { width: "normal" },
251
+ kiosk: false,
252
+ capabilities: { terminal: false },
253
+ auto_instantiated: false,
254
+ consumer_capabilities: { edit: true, interact: true },
255
+ },
256
+ }),
257
+ }),
258
+ );
259
+ });
260
+ expect(store.get(connectionAtom).state).toBe(WebSocketState.OPEN);
261
+ },
262
+ );
263
+
264
+ describe("connection notice", () => {
265
+ beforeEach(() => vi.useFakeTimers());
266
+ afterEach(() => vi.useRealTimers());
267
+
268
+ function renderNotice() {
269
+ const store = createStore();
270
+ store.set(connectionAtom, { state: WebSocketState.CONNECTING });
271
+ const transport = makeTransport(WebSocket.OPEN);
272
+ vi.mocked(useConnectionTransport).mockReturnValue(transport);
273
+ vi.mocked(useRuntimeManager).mockReturnValue(
274
+ makeRuntimeManager() as unknown as ReturnType<typeof useRuntimeManager>,
275
+ );
276
+ const Connection = () => {
277
+ const { reconnect } = useMarimoKernelConnection({
278
+ sessionId: "test-session" as SessionId,
279
+ autoInstantiate: false,
280
+ setCells: () => {},
281
+ });
282
+ return (
283
+ <ConnectionNotice
284
+ appConfig={AppConfigSchema.parse({})}
285
+ onRetry={reconnect}
286
+ />
287
+ );
288
+ };
289
+ render(
290
+ <JotaiProvider store={store}>
291
+ <ErrorBoundary fallback={null}>
292
+ <Connection />
293
+ </ErrorBoundary>
294
+ </JotaiProvider>,
295
+ );
296
+ const options = vi.mocked(useConnectionTransport).mock.calls.at(-1)![0];
297
+ const send = (data: NotificationPayload["data"]) =>
298
+ act(() => {
299
+ options.onMessage(
300
+ new MessageEvent("message", {
301
+ data: JSON.stringify({ op: data.op, data }),
302
+ }),
303
+ );
304
+ });
305
+ return { store, transport, options, send };
306
+ }
307
+
308
+ it("keeps elapsed time across startup phases and leaves reconnection to the footer once cells are available", () => {
309
+ const { store, options, send } = renderNotice();
310
+ send({
311
+ op: "startup-progress",
312
+ phase: "preparing-environment",
313
+ logs: "",
314
+ log_mode: "replace",
315
+ });
316
+ expect(
317
+ screen.queryByRole("region", { name: "Notebook startup" }),
318
+ ).not.toBeInTheDocument();
319
+ act(() => vi.advanceTimersByTime(500));
320
+ expect(screen.getByRole("status")).toHaveTextContent(
321
+ "Preparing environment",
322
+ );
323
+ act(() => vi.advanceTimersByTime(35_000));
324
+ expect(screen.getByText("Elapsed 35s")).toBeInTheDocument();
325
+ act(() =>
326
+ store.set(
327
+ notebookAtom,
328
+ MockNotebook.notebookState({
329
+ cellData: { [cellId("test")]: { code: "answer = 42" } },
330
+ }),
331
+ ),
332
+ );
333
+ send({
334
+ op: "startup-progress",
335
+ phase: "starting-kernel",
336
+ logs: "",
337
+ log_mode: "replace",
338
+ });
339
+ expect(screen.getByRole("status")).toHaveTextContent("Starting kernel");
340
+ expect(screen.getByText("Elapsed 35s")).toBeInTheDocument();
341
+ send({ op: "reconnected" });
342
+ expect(
343
+ screen.queryByRole("region", { name: "Notebook startup" }),
344
+ ).not.toBeInTheDocument();
345
+ act(() => options.onClose(new CloseEvent("close")));
346
+ act(() => vi.advanceTimersByTime(500));
347
+ expect(store.get(connectionAtom)).toEqual({
348
+ state: WebSocketState.CONNECTING,
349
+ phase: "reconnecting",
350
+ });
351
+ expect(
352
+ screen.queryByRole("region", { name: "Notebook startup" }),
353
+ ).not.toBeInTheDocument();
354
+ });
355
+
356
+ it("restores startup output before appending live chunks and resets it for a new attempt", () => {
357
+ const { store, send } = renderNotice();
358
+ const snapshot = {
359
+ op: "startup-progress",
360
+ phase: "starting-kernel",
361
+ logs: "Downloading runtime\n",
362
+ log_mode: "replace",
363
+ } as const;
364
+ send(snapshot);
365
+ send(snapshot);
366
+ const connection = store.get(connectionAtom);
367
+ send({ ...snapshot, logs: "Loading kernel\n", log_mode: "append" });
368
+ expect(store.get(startupProgressAtom)).toEqual({
369
+ phase: "starting-kernel",
370
+ logs: "Downloading runtime\nLoading kernel\n",
371
+ });
372
+ expect(store.get(connectionAtom)).toBe(connection);
373
+ expect(getPackageAlert(store.get(alertAtom))).toBeNull();
374
+
375
+ send({ ...snapshot, phase: "preparing-environment", logs: "" });
376
+ expect(store.get(startupProgressAtom)).toEqual({
377
+ phase: "preparing-environment",
378
+ logs: "",
379
+ });
380
+ });
381
+
382
+ it("restores completed startup output without putting a connected kernel back into startup", async () => {
383
+ const { store, options, send } = renderNotice();
384
+ send({ op: "reconnected" });
385
+ send({
386
+ op: "startup-progress",
387
+ phase: "starting-kernel",
388
+ logs: "Kernel startup output\n",
389
+ log_mode: "replace",
390
+ });
391
+ expect(store.get(connectionAtom)).toEqual({ state: WebSocketState.OPEN });
392
+ expect(store.get(startupProgressAtom)).toEqual({
393
+ phase: "starting-kernel",
394
+ logs: "Kernel startup output\n",
395
+ });
396
+ act(() => vi.advanceTimersByTime(500));
397
+ expect(
398
+ screen.queryByRole("region", { name: "Notebook startup" }),
399
+ ).not.toBeInTheDocument();
400
+
401
+ // A new transport starts from the next session's authoritative snapshot.
402
+ await act(async () => options.onOpen(new Event("open")));
403
+ expect(store.get(startupProgressAtom)).toBeNull();
404
+ });
405
+
406
+ it("does not flash a notice when startup finishes within the delay", () => {
407
+ const { send } = renderNotice();
408
+ send({
409
+ op: "startup-progress",
410
+ phase: "preparing-environment",
411
+ logs: "",
412
+ log_mode: "replace",
413
+ });
414
+ act(() => vi.advanceTimersByTime(200));
415
+ send({
416
+ op: "startup-progress",
417
+ phase: "starting-kernel",
418
+ logs: "",
419
+ log_mode: "replace",
420
+ });
421
+ act(() => vi.advanceTimersByTime(200));
422
+ expect(
423
+ screen.queryByRole("region", { name: "Notebook startup" }),
424
+ ).not.toBeInTheDocument();
425
+ send({ op: "reconnected" });
426
+ act(() => vi.advanceTimersByTime(500));
427
+ expect(
428
+ screen.queryByRole("region", { name: "Notebook startup" }),
429
+ ).not.toBeInTheDocument();
430
+ });
431
+
432
+ it.each([
433
+ ["preparing-environment", "Sandbox setup failed"],
434
+ ["starting-kernel", "Kernel failed to start"],
435
+ ] as const)(
436
+ "keeps a %s failure available until the user retries",
437
+ async (phase, title) => {
438
+ const { store, transport, options, send } = renderNotice();
439
+ send({ op: "startup-progress", phase, logs: "", log_mode: "replace" });
440
+ const error = "A full diagnostic\nwith <stderr> details";
441
+ send({ op: "kernel-startup-error", error });
442
+ transport.readyState = WebSocket.CLOSED;
443
+ act(() =>
444
+ options.onClose(
445
+ new CloseEvent("close", { reason: "MARIMO_KERNEL_STARTUP_ERROR" }),
446
+ ),
447
+ );
448
+ expect(screen.getByRole("status")).toHaveTextContent(title);
449
+ act(() => vi.advanceTimersByTime(30_000));
450
+ expect(screen.getByRole("status")).toHaveTextContent(title);
451
+ expect(screen.getByLabelText("Error details")).toHaveTextContent(error, {
452
+ normalizeWhitespace: false,
453
+ });
454
+ expect(transport.reconnect).not.toHaveBeenCalled();
455
+ await act(async () =>
456
+ fireEvent.click(screen.getByRole("button", { name: "Try again" })),
457
+ );
458
+ expect(transport.reconnect).toHaveBeenCalledOnce();
459
+ expect(store.get(kernelStartupErrorAtom)).toBeNull();
460
+ expect(
461
+ screen.queryByRole("button", { name: "Try again" }),
462
+ ).not.toBeInTheDocument();
463
+ send({
464
+ op: "startup-progress",
465
+ phase: "preparing-environment",
466
+ logs: "",
467
+ log_mode: "replace",
468
+ });
469
+ act(() => vi.advanceTimersByTime(500));
470
+ expect(screen.getByRole("status")).toHaveTextContent(
471
+ "Preparing environment",
472
+ );
473
+ expect(screen.getByText("Elapsed 0s")).toBeInTheDocument();
474
+ },
475
+ );
476
+ });
477
+
478
+ it("replaces environment state from snapshots, then continues live progress", () => {
479
+ const store = createStore();
480
+ vi.mocked(useConnectionTransport).mockClear();
481
+ vi.mocked(useConnectionTransport).mockReturnValue(
482
+ makeTransport(WebSocket.OPEN),
483
+ );
484
+ vi.mocked(useRuntimeManager).mockReturnValue(
485
+ makeRuntimeManager() as unknown as ReturnType<typeof useRuntimeManager>,
486
+ );
487
+ renderConnectionHook(store);
488
+ const options = vi.mocked(useConnectionTransport).mock.calls.at(-1)?.[0];
489
+ function receive(data: NotificationPayload["data"]) {
490
+ act(() =>
491
+ options?.onMessage(
492
+ new MessageEvent("message", {
493
+ data: JSON.stringify({ op: data.op, data }),
494
+ }),
495
+ ),
496
+ );
497
+ }
498
+ receive({
499
+ op: "environment-operation",
500
+ source: "kernel",
501
+ operation_id: "old",
502
+ action: "install",
503
+ status: { kind: "running" },
504
+ packages: { pandas: "running" },
505
+ logs: { pandas: "stale" },
506
+ log_mode: "append",
507
+ });
508
+ const operation = {
509
+ operation_id: "new",
510
+ action: "install",
511
+ source: "kernel",
512
+ status: { kind: "running" },
513
+ packages: { numpy: "running" },
514
+ logs: { numpy: "Downloading\n" },
515
+ } as const;
516
+ const serverOperation = {
517
+ ...operation,
518
+ operation_id: "server",
519
+ action: "install",
520
+ source: "server",
521
+ status: { kind: "succeeded" },
522
+ packages: { numpy: "succeeded" },
523
+ logs: { numpy: "Server logs\n" },
524
+ } as const;
525
+ receive({
526
+ op: "environment-state",
527
+ source: "server",
528
+ state: {
529
+ restart_required: false,
530
+ operations: [serverOperation],
531
+ },
532
+ });
533
+ const snapshot = {
534
+ op: "environment-state",
535
+ source: "kernel",
536
+ state: { restart_required: false, operations: [operation] },
537
+ } as const;
538
+ // Receiving a snapshot twice must not duplicate logs.
539
+ receive({
540
+ ...snapshot,
541
+ state: { ...snapshot.state, operations: [operation] },
542
+ });
543
+ receive({
544
+ ...snapshot,
545
+ state: { ...snapshot.state, operations: [operation] },
546
+ });
547
+ receive({
548
+ op: "environment-operation",
549
+ operation_id: "new",
550
+ action: "install",
551
+ source: "kernel",
552
+ status: { kind: "failed", error: "Network unavailable" },
553
+ packages: { numpy: "running" },
554
+ logs: { numpy: "Failed\n" },
555
+ log_mode: "append",
556
+ });
557
+ expect(getPackageAlert(store.get(alertAtom))).toEqual({
558
+ ...operation,
559
+ id: "new",
560
+ kind: "environment",
561
+ restartRequired: false,
562
+ status: { kind: "failed", error: "Network unavailable" },
563
+ logs: { numpy: "Downloading\nFailed\n" },
564
+ });
565
+ receive({
566
+ op: "environment-state",
567
+ source: "kernel",
568
+ state: {
569
+ restart_required: false,
570
+ operations: [],
571
+ },
572
+ });
573
+ // Clearing the active environment must not promote an old server success.
574
+ expect(getPackageAlert(store.get(alertAtom))).toBeNull();
575
+ expect(store.get(alertAtom).environments.server.operations).toEqual([
576
+ serverOperation,
577
+ ]);
578
+ receive({
579
+ op: "environment-state",
580
+ source: "server",
581
+ state: {
582
+ restart_required: false,
583
+ operations: [],
584
+ },
585
+ });
586
+ expect(getPackageAlert(store.get(alertAtom))).toBeNull();
587
+ expect(store.get(alertAtom).environments).toEqual({
588
+ kernel: { restart_required: false, operations: [] },
589
+ server: { restart_required: false, operations: [] },
590
+ });
591
+ });
@@ -47,9 +47,10 @@ describe("classifyCloseEvent", () => {
47
47
  state: WebSocketState.CLOSED,
48
48
  code: WebSocketClosedReason.KERNEL_DISCONNECTED,
49
49
  });
50
- if (decision.kind === "terminal") {
51
- expect(decision.closeTransport).toBe(true);
52
- }
50
+ expect(decision).toMatchObject({
51
+ kind: "terminal",
52
+ closeTransport: true,
53
+ });
53
54
  });
54
55
 
55
56
  it("MARIMO_KERNEL_STARTUP_ERROR → terminal + closeTransport", () => {
@@ -59,9 +60,10 @@ describe("classifyCloseEvent", () => {
59
60
  state: WebSocketState.CLOSED,
60
61
  code: WebSocketClosedReason.KERNEL_STARTUP_ERROR,
61
62
  });
62
- if (decision.kind === "terminal") {
63
- expect(decision.closeTransport).toBe(true);
64
- }
63
+ expect(decision).toMatchObject({
64
+ kind: "terminal",
65
+ closeTransport: true,
66
+ });
65
67
  });
66
68
  });
67
69