@marimo-team/islands 0.24.3-dev4 → 0.24.3-dev41

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 (370) hide show
  1. package/dist/{ConnectedDataExplorerComponent-C9OF_75w.js → ConnectedDataExplorerComponent-KVhjDMSe.js} +1066 -1066
  2. package/dist/ErrorBoundary-Cg8Ff5uL.js +200 -0
  3. package/dist/{ImageComparisonComponent-2a9TMMm2.js → ImageComparisonComponent-RspMflu6.js} +3 -3
  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-BajL8aoc.js +1 -0
  12. package/dist/assets/worker-BemUy60C.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-BhNoIBSU.js → cells-BSgNCBZ8.js} +6740 -10126
  18. package/dist/{channel-CyiYUzJv.js → channel-B5XnTarr.js} +1 -1
  19. package/dist/chat-ui-CygzVJ9s.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-BB9okzzY.js +138 -0
  46. package/dist/{common-BDlauhCU.js → common-aCHtzRnQ.js} +8853 -12653
  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-yiwy9Vea.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-iJn-Nryx.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-CfL-qWDi.js +380 -0
  99. package/dist/flowDiagram-HODETNUW-6dMdB8pR.js +16 -0
  100. package/dist/{formats-CJMVUfb3.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-MNFAmb3B.js} +2143 -1910
  105. package/dist/{glide-utils-NShAPPtn.js → glide-utils-B3rUT5PI.js} +15 -15
  106. package/dist/globals-DZfCplvV.js +19 -0
  107. package/dist/highlighted-body-KPVGNVTW-Ad3MKZ18.js +38 -0
  108. package/dist/html-to-image-kpuRfdfZ.js +3253 -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 +8105 -13485
  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-BZuSsCs5.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-CpK6fX_C.js → process-output-DFe2tVIY.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-BthxSdRc.js → reveal-component-ONiOVUUx.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-D-Rx3ux5.js → toDate-WXg1yLcI.js} +10 -9
  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-CPehZbUb.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__/requests.ts +7 -0
  185. package/src/__tests__/__snapshots__/CellStatus.test.tsx.snap +12 -12
  186. package/src/components/chat/acp/__tests__/agent-panel.test.tsx +270 -0
  187. package/src/components/chat/acp/agent-panel.tsx +45 -44
  188. package/src/components/data-table/column-explorer-panel/column-explorer.tsx +2 -6
  189. package/src/components/data-table/column-summary/chart-spec-model.tsx +1 -1
  190. package/src/components/data-table/column-visibility-dropdown.tsx +9 -3
  191. package/src/components/data-table/columns.tsx +64 -67
  192. package/src/components/datasources/components.tsx +4 -2
  193. package/src/components/editor/RecoveryButton.tsx +1 -0
  194. package/src/components/editor/__tests__/dynamic-favicon.test.tsx +36 -2
  195. package/src/components/editor/alerts/connecting-alert.tsx +1 -66
  196. package/src/components/editor/alerts/connection-notice.tsx +140 -0
  197. package/src/components/editor/app-container.tsx +9 -3
  198. package/src/components/editor/boundary/ErrorBoundary.tsx +38 -16
  199. package/src/components/editor/boundary/__tests__/ErrorBoundary.test.tsx +311 -0
  200. package/src/components/editor/cell/code/__tests__/rtc-editor-config.test.ts +50 -0
  201. package/src/components/editor/cell/code/cell-editor.tsx +41 -42
  202. package/src/components/editor/chrome/components/__tests__/feedback-button.test.tsx +94 -0
  203. package/src/components/editor/chrome/components/feedback-button.tsx +63 -42
  204. package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +114 -7
  205. package/src/components/editor/chrome/panels/__tests__/sandbox-recovery.test.tsx +165 -0
  206. package/src/components/editor/chrome/panels/packages-panel.tsx +93 -166
  207. package/src/components/editor/chrome/panels/sandbox-controller.tsx +106 -0
  208. package/src/components/editor/chrome/panels/sandbox-panel.tsx +173 -0
  209. package/src/components/editor/chrome/wrapper/footer-items/backend-status.tsx +3 -1
  210. package/src/components/editor/code/__tests__/readonly-diff.test.tsx +65 -0
  211. package/src/components/editor/code/readonly-diff.tsx +1 -1
  212. package/src/components/editor/dynamic-favicon.tsx +17 -10
  213. package/src/components/editor/file-tree/__tests__/tree-actions.test.tsx +54 -0
  214. package/src/components/editor/file-tree/tree-actions.tsx +11 -14
  215. package/src/components/editor/header/__tests__/status.test.tsx +8 -20
  216. package/src/components/editor/header/filename-input.tsx +2 -0
  217. package/src/components/editor/header/status.tsx +5 -3
  218. package/src/components/editor/notebook-cell.tsx +4 -2
  219. package/src/components/editor/output/JsonOutput.tsx +182 -632
  220. package/src/components/editor/output/Outputs.css +50 -17
  221. package/src/components/editor/output/__tests__/JsonOutput-interactions.test.tsx +254 -0
  222. package/src/components/editor/output/__tests__/JsonOutput-mimetype.test.tsx +41 -46
  223. package/src/components/editor/output/__tests__/{json-output.test.ts → json-output-formatting.test.ts} +113 -75
  224. package/src/components/editor/output/__tests__/json-output-pagination.test.ts +252 -0
  225. package/src/components/editor/output/json-output/formatting.ts +167 -0
  226. package/src/components/editor/output/json-output/pagination.ts +155 -0
  227. package/src/components/editor/output/json-output/renderers.tsx +196 -0
  228. package/src/components/editor/output/json-output/themes.ts +52 -0
  229. package/src/components/editor/renderers/cell-array.tsx +1 -5
  230. package/src/components/home/components.tsx +17 -0
  231. package/src/components/pages/gallery-page.tsx +2 -4
  232. package/src/components/pages/home-page.tsx +2 -4
  233. package/src/components/slides/slide-form.tsx +1 -0
  234. package/src/components/slides/slide-notes-editor.tsx +1 -0
  235. package/src/components/terminal/terminal.tsx +4 -4
  236. package/src/components/tracing/tracing-spec.ts +1 -1
  237. package/src/components/ui/table.tsx +1 -1
  238. package/src/components/ui/use-toast.ts +8 -0
  239. package/src/core/MarimoApp.tsx +0 -2
  240. package/src/core/cells/__tests__/cell.test.ts +61 -0
  241. package/src/core/cells/cell.ts +9 -1
  242. package/src/core/cells/pending-delete-service.ts +12 -16
  243. package/src/core/codemirror/language/__tests__/sql.test.ts +70 -2
  244. package/src/core/codemirror/language/languages/sql/sql.ts +16 -1
  245. package/src/core/codemirror/rtc/extension.ts +5 -2
  246. package/src/core/datasets/__tests__/data-source.test.ts +2 -0
  247. package/src/core/datasets/data-source-connections.ts +15 -10
  248. package/src/core/edit-app.tsx +8 -5
  249. package/src/core/errors/state.ts +1 -2
  250. package/src/core/islands/__tests__/parse.test.ts +140 -7
  251. package/src/core/islands/bootstrap.ts +1 -0
  252. package/src/core/islands/bridge.ts +3 -0
  253. package/src/core/islands/parse.ts +20 -0
  254. package/src/core/network/connection-notice.ts +84 -0
  255. package/src/core/network/requests-lazy.ts +3 -0
  256. package/src/core/network/requests-network.ts +12 -0
  257. package/src/core/network/requests-static.ts +3 -0
  258. package/src/core/network/requests-toasting.tsx +3 -0
  259. package/src/core/network/resolve.ts +2 -1
  260. package/src/core/network/types.ts +7 -0
  261. package/src/core/packages/__tests__/usePackageAction.test.tsx +129 -0
  262. package/src/core/packages/__tests__/useSandboxController.test.tsx +165 -0
  263. package/src/core/packages/package-data.ts +35 -0
  264. package/src/core/packages/sandbox-state.ts +15 -0
  265. package/src/core/packages/toast-components.tsx +11 -0
  266. package/src/core/packages/useInstallPackage.ts +1 -1
  267. package/src/core/packages/usePackageAction.ts +51 -0
  268. package/src/core/packages/usePackageDependencies.ts +22 -0
  269. package/src/core/packages/useSandboxController.ts +188 -0
  270. package/src/core/run-app.tsx +2 -0
  271. package/src/core/runtime/__tests__/runtime.test.ts +11 -0
  272. package/src/core/runtime/runtime.ts +7 -2
  273. package/src/core/wasm/bridge.ts +11 -0
  274. package/src/core/websocket/__tests__/useMarimoKernelConnection.hook.test.tsx +212 -2
  275. package/src/core/websocket/transports/transport.ts +1 -1
  276. package/src/core/websocket/transports/ws.ts +1 -1
  277. package/src/core/websocket/types.ts +11 -1
  278. package/src/core/websocket/useMarimoKernelConnection.tsx +43 -8
  279. package/src/css/app/Cell.css +2 -0
  280. package/src/hooks/useDebounce.ts +2 -0
  281. package/src/plugins/core/BadPlugin.tsx +3 -14
  282. package/src/plugins/core/RenderHTML.tsx +22 -2
  283. package/src/plugins/core/__test__/RenderHTML.test.ts +187 -0
  284. package/src/plugins/core/registerReactComponent.tsx +0 -1
  285. package/src/plugins/impl/FormPlugin.tsx +2 -0
  286. package/src/plugins/impl/MatrixPlugin.tsx +2 -0
  287. package/src/plugins/impl/RangeSliderPlugin.tsx +2 -0
  288. package/src/plugins/impl/SliderPlugin.tsx +2 -0
  289. package/src/plugins/impl/data-editor/__tests__/glide-data-editor.test.tsx +23 -18
  290. package/src/plugins/impl/data-frames/panel.tsx +1 -0
  291. package/src/plugins/layout/AccordionPlugin.tsx +7 -2
  292. package/src/plugins/layout/JsonOutputPlugin.tsx +7 -10
  293. package/src/plugins/layout/__test__/AccordionPlugin.test.tsx +66 -0
  294. package/src/stories/sandbox.stories.tsx +308 -0
  295. package/dist/ErrorBoundary-CR-ZQN_c.js +0 -125
  296. package/dist/_baseMerge-Dv8bBloN.js +0 -73
  297. package/dist/_initCloneObject---vediN2.js +0 -95
  298. package/dist/arc-DOVLrCPd.js +0 -82
  299. package/dist/architecture-TIHT7OUA-MiJpPpQW.js +0 -3
  300. package/dist/array-CApNbSU9.js +0 -6
  301. package/dist/assets/__vite-browser-external-BdKGjRZ4.js +0 -1
  302. package/dist/assets/worker-DlIBfp9S.js +0 -98
  303. package/dist/blockDiagram-VBNYF7ZC-Bc--I65C.js +0 -3158
  304. package/dist/chat-ui-DjTMZRVP.js +0 -8455
  305. package/dist/chunk-3NCLNEKW-BIp_lB_4.js +0 -14
  306. package/dist/chunk-5FCAYU7R-C_HO9A1J.js +0 -23
  307. package/dist/chunk-5HE753X5-Bixm_MV-.js +0 -38
  308. package/dist/chunk-5JV3BV7I-CH5J3iZY.js +0 -54
  309. package/dist/chunk-5TONJI2A-DG6gDiSS.js +0 -62
  310. package/dist/chunk-7BUUIJ7U-5afmIPeo.js +0 -82
  311. package/dist/chunk-BIQX33UG-7TMBUf89.js +0 -23
  312. package/dist/chunk-CQNSW5MT-Cnqqsxt-.js +0 -38
  313. package/dist/chunk-CYSBUYHQ-BAbymFHR.js +0 -23
  314. package/dist/chunk-EMLP6XTP-Bsz-QL77.js +0 -23
  315. package/dist/chunk-J7OUQ5F2-CM_meqZH.js +0 -57
  316. package/dist/chunk-JG7HCLWE-DUnCDgPk.js +0 -62
  317. package/dist/chunk-MOZMSUNE-BG6IHvzT.js +0 -35
  318. package/dist/chunk-OSBZ3O6U-CYwj_bvR.js +0 -23
  319. package/dist/chunk-QBLGF6JB-B4vJ4j8J.js +0 -23
  320. package/dist/chunk-QR6OTTB3-D4wH2ldi.js +0 -3738
  321. package/dist/chunk-R7FJI6CG-Blf8FcmZ.js +0 -64
  322. package/dist/chunk-RYQCIY6F-BWbIlBIH.js +0 -241
  323. package/dist/chunk-U6XO7XAA-COw445DS.js +0 -63
  324. package/dist/chunk-UBXNYLIW-BHigVU4H.js +0 -32
  325. package/dist/chunk-YOTPTUD7-Dn044Q_d.js +0 -30
  326. package/dist/chunk-ZIRB5QZD-BR4TMfOa.js +0 -1153
  327. package/dist/classDiagram-JCYQIIEL-DmXSLxe_.js +0 -30
  328. package/dist/classDiagram-v2-OCEON4UE-CooqVexA.js +0 -30
  329. package/dist/click-outside-container-D_URcm3D.js +0 -167
  330. package/dist/code-block-37QAKDTI-CQrzBOr-.js +0 -107
  331. package/dist/copy-CD7S58CY.js +0 -141
  332. package/dist/cynefin-VYW2F7L2-CeHAxOEX.js +0 -3
  333. package/dist/dagre-VZM6K2ZE-CJggWPTJ.js +0 -328
  334. package/dist/dist-3_o3T78I.js +0 -1360
  335. package/dist/dist-ABQjhuek.js +0 -64
  336. package/dist/dist-CO5R6VMi.js +0 -538
  337. package/dist/dropdown-menu-DhCfcscX.js +0 -5858
  338. package/dist/emotion-is-prop-valid.esm-M57VsT8j.js +0 -14
  339. package/dist/error-banner-CsXbEEjo.js +0 -611
  340. package/dist/eventmodeling-45OFAUF4-B_lWZ-Pd.js +0 -3
  341. package/dist/flowDiagram-UKHOOZJN-g1AmrZzM.js +0 -17
  342. package/dist/gitGraph-TEB2WS4Q-Dj-eXUph.js +0 -3
  343. package/dist/info-DKCQHKI2-Bhb-pe2e.js +0 -3
  344. package/dist/line-CuKjIQa1.js +0 -34
  345. package/dist/map-BJpOfPlL.js +0 -149
  346. package/dist/math-BXtKgYah.js +0 -10
  347. package/dist/mermaid-4DMBBIKO-Ddp9TgNp.js +0 -2
  348. package/dist/mermaid-Bi23Ibnw.js +0 -1173
  349. package/dist/mermaid-parser.core-Din9FvSi.js +0 -99
  350. package/dist/packet-7NZHBO7P-fbkIIyKE.js +0 -3
  351. package/dist/path-BNHuHZP8.js +0 -87
  352. package/dist/pie-RZYD4A2V-CLRIsxY1.js +0 -3
  353. package/dist/purify.es-CB-YO1Hn.js +0 -774
  354. package/dist/radar-I7S5WNFK-BGzCbgXu.js +0 -3
  355. package/dist/railroad-3IZDKUUU-CvQIIcuU.js +0 -3
  356. package/dist/railroad-abnf-AHOZXSZD-CMwtlNtu.js +0 -3
  357. package/dist/railroad-ebnf-EBAXGLYW-n1QUOAsh.js +0 -3
  358. package/dist/railroad-peg-LSFZ7HO6-PjnrpmCs.js +0 -3
  359. package/dist/schemas-DzK6U94N.js +0 -6057
  360. package/dist/stateDiagram-v2-UEYNNEHI-BTRUs8Il.js +0 -30
  361. package/dist/step-CKczO72Q.js +0 -670
  362. package/dist/swimlanesDiagram-ULZ7WXOC-NBmgBl9G.js +0 -31
  363. package/dist/timer-DpDyj9G0.js +0 -596
  364. package/dist/tooltip-Dt77sBeT.js +0 -390
  365. package/dist/treeView-QDETBFTQ-DU8LwHuf.js +0 -3
  366. package/dist/treemap-6X3UGDF4-DUk4-k_O.js +0 -3
  367. package/dist/useTheme-CVJ0UOf2.js +0 -841
  368. package/dist/vega-loader.browser-DEggZzni.js +0 -1083
  369. package/dist/wardley-OPB4EBWU-Db-AaGas.js +0 -3
  370. package/src/components/editor/output/EmotionCacheProvider.tsx +0 -32
@@ -0,0 +1,188 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+ import { useAtom, useAtomValue, useSetAtom } from "jotai";
3
+ import { useEffect, useRef, useState } from "react";
4
+ import useEvent from "react-use-event-hook";
5
+ import { kernelStartupErrorAtom } from "@/core/errors/state";
6
+ import { connectionAtom } from "@/core/network/connection";
7
+ import { useRequestClient } from "@/core/network/requests";
8
+ import { useFilename } from "@/core/saving/filename";
9
+ import { waitFor } from "@/core/state/jotai";
10
+ import { isWasm } from "@/core/wasm/utils";
11
+ import { WebSocketState } from "@/core/websocket/types";
12
+ import { HTTPError, prettyError } from "@/utils/errors";
13
+ import { invalidatePackageData } from "./package-data";
14
+ import {
15
+ sandboxActionsAtom,
16
+ sandboxAtom,
17
+ sandboxSyncAtom,
18
+ } from "./sandbox-state";
19
+ import {
20
+ showPackageRestartToast,
21
+ showSandboxSyncToast,
22
+ } from "./toast-components";
23
+
24
+ /** Mount once with the editor so manifest drafts survive closing Packages. */
25
+ export function useSandboxController(onReconnect: () => Promise<void>) {
26
+ const requests = useRequestClient();
27
+ const filename = useFilename();
28
+ const connection = useAtomValue(connectionAtom);
29
+ const startupError = useAtomValue(kernelStartupErrorAtom);
30
+ const setSandbox = useSetAtom(sandboxAtom);
31
+ const [operation, setOperation] = useAtom(sandboxSyncAtom);
32
+ const setActions = useSetAtom(sandboxActionsAtom);
33
+ const [open, setOpen] = useState(false);
34
+ const [document, setDocument] = useState<{
35
+ original: string;
36
+ draft: string;
37
+ } | null>(null);
38
+ const [error, setError] = useState<string | null>(null);
39
+ const [saving, setSaving] = useState(false);
40
+ const [loading, setLoading] = useState(false);
41
+ const [conflict, setConflict] = useState(false);
42
+ const inFlight = useRef(false);
43
+ const dirty = document !== null && document.original !== document.draft;
44
+
45
+ useEffect(() => {
46
+ if (isWasm()) {
47
+ return;
48
+ }
49
+ let cancelled = false;
50
+ requests
51
+ .getSandbox({ fileKey: filename })
52
+ .then((value) => {
53
+ if (!cancelled) {
54
+ setSandbox(value);
55
+ }
56
+ })
57
+ .catch(() => {
58
+ /* A connection error is already shown by the editor. */
59
+ });
60
+ return () => {
61
+ cancelled = true;
62
+ };
63
+ }, [requests, filename, connection.state, setSandbox]);
64
+
65
+ const sync = useEvent(async () => {
66
+ if (inFlight.current || connection.state === WebSocketState.CONNECTING) {
67
+ return false;
68
+ }
69
+ inFlight.current = true;
70
+ setOperation((value) => ({ ...value, pending: true, error: null }));
71
+ try {
72
+ const result = await requests.syncSandbox({ fileKey: filename });
73
+ if (result.restartRequired) {
74
+ showPackageRestartToast();
75
+ }
76
+ if (!result.success) {
77
+ throw new Error(result.error ?? "Sandbox sync failed.");
78
+ }
79
+ if (result.reconnect) {
80
+ await onReconnect();
81
+ const status = await waitFor(
82
+ connectionAtom,
83
+ (value) =>
84
+ value.state === WebSocketState.OPEN ||
85
+ value.state === WebSocketState.CLOSED,
86
+ );
87
+ if (status.state === WebSocketState.CLOSED) {
88
+ return false;
89
+ }
90
+ }
91
+ if (!result.reconnect) {
92
+ invalidatePackageData();
93
+ }
94
+ showSandboxSyncToast();
95
+ return true;
96
+ } catch (cause) {
97
+ setOperation((value) => ({ ...value, error: prettyError(cause) }));
98
+ return false;
99
+ } finally {
100
+ inFlight.current = false;
101
+ setOperation((value) => ({ ...value, pending: false }));
102
+ }
103
+ });
104
+
105
+ const loadManifest = useEvent(async () => {
106
+ setLoading(true);
107
+ setError(null);
108
+ setConflict(false);
109
+ try {
110
+ const result = await requests.getSandbox({ fileKey: filename });
111
+ setSandbox(result);
112
+ if (result.manifest === null) {
113
+ throw new Error("No notebook manifest is available yet.");
114
+ }
115
+ setDocument({ original: result.manifest, draft: result.manifest });
116
+ } catch (cause) {
117
+ setError(prettyError(cause));
118
+ } finally {
119
+ setLoading(false);
120
+ }
121
+ });
122
+
123
+ const editManifest = useEvent(async () => {
124
+ setOpen(true);
125
+ if (!dirty) {
126
+ await loadManifest();
127
+ }
128
+ });
129
+
130
+ useEffect(() => {
131
+ setActions({ sync, editManifest });
132
+ return () => {
133
+ setActions(null);
134
+ };
135
+ }, [sync, editManifest, setActions]);
136
+
137
+ const saveAndSync = async () => {
138
+ if (!document || loading || saving || operation.pending) {
139
+ return;
140
+ }
141
+ setSaving(true);
142
+ setError(null);
143
+ try {
144
+ if (dirty) {
145
+ const result = await requests.updateManifest({
146
+ fileKey: filename,
147
+ contents: document.draft,
148
+ previous: document.original,
149
+ });
150
+ setSandbox(result);
151
+ if (result.manifest !== null) {
152
+ setDocument({ original: result.manifest, draft: result.manifest });
153
+ }
154
+ }
155
+ if (await sync()) {
156
+ setOpen(false);
157
+ }
158
+ } catch (cause) {
159
+ setError(prettyError(cause));
160
+ setConflict(cause instanceof HTTPError && cause.status === 409);
161
+ } finally {
162
+ setSaving(false);
163
+ }
164
+ };
165
+ const pending =
166
+ saving ||
167
+ loading ||
168
+ operation.pending ||
169
+ connection.state === WebSocketState.CONNECTING;
170
+ const diagnostic = error ?? operation.error ?? startupError;
171
+ return {
172
+ filename,
173
+ open,
174
+ setOpen,
175
+ draft: document?.draft ?? null,
176
+ setDraft: (draft: string) =>
177
+ setDocument((value) => value && { ...value, draft }),
178
+ dirty,
179
+ loading,
180
+ saving,
181
+ pending,
182
+ error,
183
+ diagnostic,
184
+ conflict,
185
+ loadManifest,
186
+ saveAndSync,
187
+ };
188
+ }
@@ -3,6 +3,7 @@
3
3
  import { atom, useAtomValue } from "jotai";
4
4
  import { ArrowLeftIcon } from "lucide-react";
5
5
  import { useEffect } from "react";
6
+ import { KernelStartupErrorModal } from "@/components/editor/KernelStartupErrorModal";
6
7
  import { AppContainer } from "@/components/editor/app-container";
7
8
  import { AppHeader } from "@/components/editor/header/app-header";
8
9
  import { ProgressiveBoundary } from "@/components/lifecycle/ProgressiveBoundary";
@@ -78,6 +79,7 @@ export const RunApp: React.FC<AppProps> = ({ appConfig, hideHeader }) => {
78
79
  width={appConfig.width}
79
80
  onReconnect={reconnect}
80
81
  >
82
+ <KernelStartupErrorModal />
81
83
  <AppHeader
82
84
  connection={connection}
83
85
  className={hideHeader ? "hidden" : "sm:pt-8"}
@@ -276,6 +276,17 @@ describe("RuntimeManager", () => {
276
276
  });
277
277
 
278
278
  describe("getTerminalWsURL", () => {
279
+ it("should include dimensions in the terminal handshake", () => {
280
+ const runtime = new RuntimeManager(mockConfig);
281
+ const url = runtime.getTerminalWsURL({ rows: 30, cols: 140 });
282
+
283
+ expect(Object.fromEntries(url.searchParams)).toEqual({
284
+ access_token: "test-token",
285
+ rows: "30",
286
+ cols: "140",
287
+ });
288
+ });
289
+
279
290
  it("should return terminal WebSocket URL", () => {
280
291
  const runtime = new RuntimeManager(mockConfig);
281
292
  const url = runtime.getTerminalWsURL();
@@ -183,8 +183,13 @@ export class RuntimeManager {
183
183
  /**
184
184
  * The WebSocket URL of the terminal.
185
185
  */
186
- getTerminalWsURL(): URL {
187
- return this.formatWsURL("/terminal/ws");
186
+ getTerminalWsURL(size?: { rows: number; cols: number }): URL {
187
+ const url = this.formatWsURL("/terminal/ws");
188
+ if (size) {
189
+ url.searchParams.set("rows", String(size.rows));
190
+ url.searchParams.set("cols", String(size.cols));
191
+ }
192
+ return url;
188
193
  }
189
194
 
190
195
  /**
@@ -640,6 +640,17 @@ export class PyodideBridge implements RunRequests, EditRequests {
640
640
  return response;
641
641
  };
642
642
 
643
+ getSandbox: EditRequests["getSandbox"] = async () => ({
644
+ backend: null,
645
+ manifest: null,
646
+ filename: null,
647
+ });
648
+ updateManifest: EditRequests["updateManifest"] = async () => {
649
+ throw new Error("Sandboxes are not supported in WebAssembly");
650
+ };
651
+ syncSandbox: EditRequests["syncSandbox"] = async () => {
652
+ throw new Error("Sandboxes are not supported in WebAssembly");
653
+ };
643
654
  getDependencyTree: EditRequests["getDependencyTree"] = async () => {
644
655
  // WASM doesn't support dependency trees yet
645
656
  return {
@@ -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,6 +40,13 @@ 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 { notebookAtom } from "@/core/cells/cells";
46
+ import { AppConfigSchema } from "@/core/config/config-schema";
47
+ import { ConnectionNotice } from "@/components/editor/alerts/connection-notice";
48
+ import { kernelStartupErrorAtom } from "@/core/errors/state";
49
+ import type { NotificationPayload } from "@/core/kernel/messages";
29
50
  import { useRuntimeManager } from "@/core/runtime/config";
30
51
  import { initialRunCompletedAtom } from "../../kernel/state";
31
52
  import { connectionAtom } from "../../network/connection";
@@ -188,3 +209,192 @@ describe("useMarimoKernelConnection messages", () => {
188
209
  expect(store.get(initialRunCompletedAtom)).toBe(true);
189
210
  });
190
211
  });
212
+
213
+ it.each(["kernel-ready", "reconnected"])(
214
+ "waits for %s before marking the session connected",
215
+ async (op) => {
216
+ const store = createStore();
217
+ store.set(connectionAtom, { state: WebSocketState.CONNECTING });
218
+ vi.mocked(useConnectionTransport).mockClear();
219
+ vi.mocked(useConnectionTransport).mockReturnValue(
220
+ makeTransport(WebSocket.OPEN),
221
+ );
222
+ vi.mocked(useRuntimeManager).mockReturnValue(
223
+ makeRuntimeManager() as unknown as ReturnType<typeof useRuntimeManager>,
224
+ );
225
+ renderConnectionHook(store);
226
+ const options = vi.mocked(useConnectionTransport).mock.calls.at(-1)![0];
227
+
228
+ await act(async () => {
229
+ await options.onOpen(new Event("open"));
230
+ });
231
+ expect(store.get(connectionAtom).state).toBe(WebSocketState.CONNECTING);
232
+
233
+ act(() => {
234
+ options.onMessage(
235
+ new MessageEvent("message", {
236
+ data: JSON.stringify({
237
+ op,
238
+ data: {
239
+ op,
240
+ cell_ids: [],
241
+ codes: [],
242
+ names: [],
243
+ configs: [],
244
+ layout: null,
245
+ resumed: true,
246
+ ui_values: {},
247
+ last_executed_code: {},
248
+ last_execution_time: {},
249
+ app_config: { width: "normal" },
250
+ kiosk: false,
251
+ capabilities: { terminal: false },
252
+ auto_instantiated: false,
253
+ consumer_capabilities: { edit: true, interact: true },
254
+ },
255
+ }),
256
+ }),
257
+ );
258
+ });
259
+ expect(store.get(connectionAtom).state).toBe(WebSocketState.OPEN);
260
+ },
261
+ );
262
+
263
+ describe("connection notice", () => {
264
+ beforeEach(() => vi.useFakeTimers());
265
+ afterEach(() => vi.useRealTimers());
266
+
267
+ function renderNotice() {
268
+ const store = createStore();
269
+ store.set(connectionAtom, { state: WebSocketState.CONNECTING });
270
+ const transport = makeTransport(WebSocket.OPEN);
271
+ vi.mocked(useConnectionTransport).mockReturnValue(transport);
272
+ vi.mocked(useRuntimeManager).mockReturnValue(
273
+ makeRuntimeManager() as unknown as ReturnType<typeof useRuntimeManager>,
274
+ );
275
+ const Connection = () => {
276
+ const { reconnect } = useMarimoKernelConnection({
277
+ sessionId: "test-session" as SessionId,
278
+ autoInstantiate: false,
279
+ setCells: () => {},
280
+ });
281
+ return (
282
+ <ConnectionNotice
283
+ appConfig={AppConfigSchema.parse({})}
284
+ onRetry={reconnect}
285
+ />
286
+ );
287
+ };
288
+ render(
289
+ <JotaiProvider store={store}>
290
+ <ErrorBoundary fallback={null}>
291
+ <Connection />
292
+ </ErrorBoundary>
293
+ </JotaiProvider>,
294
+ );
295
+ const options = vi.mocked(useConnectionTransport).mock.calls.at(-1)![0];
296
+ const send = (data: NotificationPayload["data"]) =>
297
+ act(() => {
298
+ options.onMessage(
299
+ new MessageEvent("message", {
300
+ data: JSON.stringify({ op: data.op, data }),
301
+ }),
302
+ );
303
+ });
304
+ return { store, transport, options, send };
305
+ }
306
+
307
+ it("keeps elapsed time across startup phases and leaves reconnection to the footer once cells are available", () => {
308
+ const { store, options, send } = renderNotice();
309
+ send({ op: "startup-progress", phase: "preparing-environment" });
310
+ expect(
311
+ screen.queryByRole("region", { name: "Notebook startup" }),
312
+ ).not.toBeInTheDocument();
313
+ act(() => vi.advanceTimersByTime(500));
314
+ expect(screen.getByRole("status")).toHaveTextContent(
315
+ "Preparing environment",
316
+ );
317
+ act(() => vi.advanceTimersByTime(35_000));
318
+ expect(screen.getByText("Elapsed 35s")).toBeInTheDocument();
319
+ act(() =>
320
+ store.set(
321
+ notebookAtom,
322
+ MockNotebook.notebookState({
323
+ cellData: { [cellId("test")]: { code: "answer = 42" } },
324
+ }),
325
+ ),
326
+ );
327
+ send({ op: "startup-progress", phase: "starting-kernel" });
328
+ expect(screen.getByRole("status")).toHaveTextContent("Starting notebook");
329
+ expect(screen.getByText("Elapsed 35s")).toBeInTheDocument();
330
+ send({ op: "reconnected" });
331
+ expect(
332
+ screen.queryByRole("region", { name: "Notebook startup" }),
333
+ ).not.toBeInTheDocument();
334
+ act(() => options.onClose(new CloseEvent("close")));
335
+ act(() => vi.advanceTimersByTime(500));
336
+ expect(store.get(connectionAtom)).toEqual({
337
+ state: WebSocketState.CONNECTING,
338
+ phase: "reconnecting",
339
+ });
340
+ expect(
341
+ screen.queryByRole("region", { name: "Notebook startup" }),
342
+ ).not.toBeInTheDocument();
343
+ });
344
+
345
+ it("does not flash a notice when startup finishes within the delay", () => {
346
+ const { send } = renderNotice();
347
+ send({ op: "startup-progress", phase: "preparing-environment" });
348
+ act(() => vi.advanceTimersByTime(200));
349
+ send({ op: "startup-progress", phase: "starting-kernel" });
350
+ act(() => vi.advanceTimersByTime(200));
351
+ expect(
352
+ screen.queryByRole("region", { name: "Notebook startup" }),
353
+ ).not.toBeInTheDocument();
354
+ send({ op: "reconnected" });
355
+ act(() => vi.advanceTimersByTime(500));
356
+ expect(
357
+ screen.queryByRole("region", { name: "Notebook startup" }),
358
+ ).not.toBeInTheDocument();
359
+ });
360
+
361
+ it.each([
362
+ ["preparing-environment", "Sandbox setup failed"],
363
+ ["starting-kernel", "Kernel failed to start"],
364
+ ] as const)(
365
+ "keeps a %s failure available until the user retries",
366
+ async (phase, title) => {
367
+ const { store, transport, options, send } = renderNotice();
368
+ send({ op: "startup-progress", phase });
369
+ const error = "A full diagnostic\nwith <stderr> details";
370
+ send({ op: "kernel-startup-error", error });
371
+ transport.readyState = WebSocket.CLOSED;
372
+ act(() =>
373
+ options.onClose(
374
+ new CloseEvent("close", { reason: "MARIMO_KERNEL_STARTUP_ERROR" }),
375
+ ),
376
+ );
377
+ expect(screen.getByRole("status")).toHaveTextContent(title);
378
+ act(() => vi.advanceTimersByTime(30_000));
379
+ expect(screen.getByRole("status")).toHaveTextContent(title);
380
+ expect(screen.getByLabelText("Error details")).toHaveTextContent(error, {
381
+ normalizeWhitespace: false,
382
+ });
383
+ expect(transport.reconnect).not.toHaveBeenCalled();
384
+ await act(async () =>
385
+ fireEvent.click(screen.getByRole("button", { name: "Try again" })),
386
+ );
387
+ expect(transport.reconnect).toHaveBeenCalledOnce();
388
+ expect(store.get(kernelStartupErrorAtom)).toBeNull();
389
+ expect(
390
+ screen.queryByRole("button", { name: "Try again" }),
391
+ ).not.toBeInTheDocument();
392
+ send({ op: "startup-progress", phase: "preparing-environment" });
393
+ act(() => vi.advanceTimersByTime(500));
394
+ expect(screen.getByRole("status")).toHaveTextContent(
395
+ "Preparing environment",
396
+ );
397
+ expect(screen.getByText("Elapsed 0s")).toBeInTheDocument();
398
+ },
399
+ );
400
+ });
@@ -15,7 +15,7 @@ export type ConnectionTransportCallback<T extends ConnectionEvent> = (
15
15
  export interface IConnectionTransport {
16
16
  reconnect(code?: number | undefined, reason?: string | undefined): void;
17
17
  close(): void;
18
- send(data: string | ArrayBuffer | Blob | ArrayBufferView): void;
18
+ send(data: string | ArrayBuffer | Blob | ArrayBufferView<ArrayBuffer>): void;
19
19
  addEventListener<T extends ConnectionEvent>(
20
20
  event: T,
21
21
  callback: ConnectionTransportCallback<T>,
@@ -42,7 +42,7 @@ export class WsTransport implements IConnectionTransport {
42
42
  this.inner.close();
43
43
  }
44
44
 
45
- send(data: string | ArrayBuffer | Blob | ArrayBufferView): void {
45
+ send(data: string | ArrayBuffer | Blob | ArrayBufferView<ArrayBuffer>): void {
46
46
  this.inner.send(data);
47
47
  }
48
48
 
@@ -1,5 +1,11 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
+ import type { NotificationMessageData } from "../kernel/messages";
4
+
5
+ type ConnectionPhase =
6
+ | NotificationMessageData<"startup-progress">["phase"]
7
+ | "reconnecting";
8
+
3
9
  // TODO: rename to ConnectionState
4
10
  export const WebSocketState = {
5
11
  NOT_STARTED: "NOT_STARTED",
@@ -28,10 +34,14 @@ export type ConnectionStatus =
28
34
  * Human-readable reason for closing the connection.
29
35
  */
30
36
  reason: string;
37
+ phase?: ConnectionPhase;
38
+ }
39
+ | {
40
+ state: typeof WebSocketState.CONNECTING;
41
+ phase?: ConnectionPhase;
31
42
  }
32
43
  | {
33
44
  state:
34
- | typeof WebSocketState.CONNECTING
35
45
  | typeof WebSocketState.OPEN
36
46
  | typeof WebSocketState.CLOSING
37
47
  | typeof WebSocketState.NOT_STARTED;
@@ -1,5 +1,6 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
+ import { invalidatePackageData } from "@/core/packages/package-data";
3
4
  import { useAtom, useAtomValue, useSetAtom } from "jotai";
4
5
  import { useRef } from "react";
5
6
  import { useErrorBoundary } from "react-error-boundary";
@@ -240,7 +241,15 @@ export function useMarimoKernelConnection(opts: {
240
241
  case "reload":
241
242
  reloadSafe();
242
243
  return;
244
+ case "startup-progress":
245
+ setConnection({
246
+ state: WebSocketState.CONNECTING,
247
+ phase: msg.data.phase,
248
+ });
249
+ return;
243
250
  case "kernel-ready": {
251
+ setKernelStartupError(null);
252
+ setConnection({ state: WebSocketState.OPEN });
244
253
  setInitialRunCompleted(
245
254
  Boolean(msg.data.resumed || msg.data.auto_instantiated),
246
255
  );
@@ -363,6 +372,16 @@ export function useMarimoKernelConnection(opts: {
363
372
  });
364
373
  return;
365
374
  case "installing-package-alert":
375
+ if (
376
+ msg.data.source !== "server" &&
377
+ msg.data.log_status !== "append" &&
378
+ msg.data.log_status !== "start" &&
379
+ Object.values(msg.data.packages).some(
380
+ (status) => status === "installed" || status === "restart-required",
381
+ )
382
+ ) {
383
+ invalidatePackageData();
384
+ }
366
385
  addPackageAlert({
367
386
  ...msg.data,
368
387
  kind: "installing",
@@ -439,6 +458,8 @@ export function useMarimoKernelConnection(opts: {
439
458
  return;
440
459
 
441
460
  case "reconnected":
461
+ setKernelStartupError(null);
462
+ setConnection({ state: WebSocketState.OPEN });
442
463
  return;
443
464
 
444
465
  case "focus-cell":
@@ -482,6 +503,7 @@ export function useMarimoKernelConnection(opts: {
482
503
  return;
483
504
  }
484
505
  shouldTryReconnecting.current = true;
506
+ setKernelStartupError(null);
485
507
  setConnection({ state: WebSocketState.CONNECTING });
486
508
  const healthy = await runtimeManager.reconcileFromHealth();
487
509
  if (!healthy) {
@@ -513,17 +535,11 @@ export function useMarimoKernelConnection(opts: {
513
535
  headers: () => runtimeManager.headers(),
514
536
 
515
537
  /**
516
- * Open callback. Set the connection status to open.
538
+ * The transport is open; kernel-ready establishes session readiness.
517
539
  */
518
540
  onOpen: async () => {
519
541
  // If we are open, we can reset our reconnecting flag.
520
542
  shouldTryReconnecting.current = true;
521
-
522
- // DO NOT COMMIT THIS UNCOMMENTED
523
- // Uncomment to emulate a slow connection
524
- // await new Promise((resolve) => setTimeout(resolve, 10_000));
525
-
526
- setConnection({ state: WebSocketState.OPEN });
527
543
  },
528
544
 
529
545
  /**
@@ -565,7 +581,26 @@ export function useMarimoKernelConnection(opts: {
565
581
  onClose: (e) => {
566
582
  Logger.warn("WebSocket closed", e.code, e.reason);
567
583
  const decision = classifyCloseEvent(e);
568
- setConnection(decision.status);
584
+ setConnection((previous) => {
585
+ const status = decision.status;
586
+ if (
587
+ status.state === WebSocketState.CLOSED &&
588
+ status.code === WebSocketClosedReason.KERNEL_STARTUP_ERROR &&
589
+ (previous.state === WebSocketState.CONNECTING ||
590
+ previous.state === WebSocketState.CLOSED)
591
+ ) {
592
+ return { ...status, phase: previous.phase };
593
+ }
594
+ if (
595
+ status.state === WebSocketState.CONNECTING &&
596
+ (previous.state === WebSocketState.OPEN ||
597
+ (previous.state === WebSocketState.CONNECTING &&
598
+ previous.phase === "reconnecting"))
599
+ ) {
600
+ return { ...status, phase: "reconnecting" };
601
+ }
602
+ return status;
603
+ });
569
604
  if (decision.kind === "terminal" && decision.closeTransport) {
570
605
  ws.close(); // close to prevent reconnecting
571
606
  return;
@@ -82,6 +82,7 @@
82
82
  .console-output-area {
83
83
  max-height: 610px;
84
84
  overflow: auto;
85
+ overscroll-behavior: contain;
85
86
  }
86
87
 
87
88
  /* Special case for particular components */
@@ -521,6 +522,7 @@
521
522
  display: flow-root;
522
523
 
523
524
  overflow: auto;
525
+ overscroll-behavior: contain;
524
526
  }
525
527
 
526
528
  .marimo-output-stale,
@@ -48,6 +48,8 @@ export function useDebounceControlledState<T>(opts: {
48
48
 
49
49
  // Handle external changes:
50
50
  useEffect(() => {
51
+ // TODO: Replace this synchronization with a controlled-state primitive.
52
+ // oxlint-disable-next-line react/no-deriving-state-in-effects -- Synchronize the debounced draft when the controlled value changes.
51
53
  setInternalValue(initialValue);
52
54
  }, [initialValue]);
53
55