@marimo-team/islands 0.24.3-dev5 → 0.24.3-dev52

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 (384) hide show
  1. package/dist/{ConnectedDataExplorerComponent-C9OF_75w.js → ConnectedDataExplorerComponent-KVhjDMSe.js} +1066 -1066
  2. package/dist/ErrorBoundary-Bfp74qVG.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-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-qtEE1_Zi.js +138 -0
  46. package/dist/{common-xHoep60C.js → common-BmP252R5.js} +8839 -12646
  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-DtaTjDfe.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-D_YQdd5e.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-CV4eSJ7N.js} +2143 -1910
  105. package/dist/{glide-utils-NShAPPtn.js → glide-utils-B3rUT5PI.js} +15 -15
  106. package/dist/globals-DdrpcXQY.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-DXWRr4gK.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-DwuC-61O.js → reveal-component-DLxDt6wQ.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/__tests__/fixtures/pair-preview.ts +19 -0
  187. package/src/__tests__/mount.test.ts +18 -0
  188. package/src/components/chat/acp/__tests__/agent-panel.test.tsx +270 -0
  189. package/src/components/chat/acp/agent-panel.tsx +45 -44
  190. package/src/components/data-table/column-explorer-panel/column-explorer.tsx +2 -6
  191. package/src/components/data-table/column-summary/chart-spec-model.tsx +1 -1
  192. package/src/components/data-table/columns.tsx +64 -67
  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/actions/__tests__/pair-with-agent-commands.test.ts +115 -0
  196. package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +218 -0
  197. package/src/components/editor/actions/pair-with-agent-commands.ts +46 -9
  198. package/src/components/editor/actions/pair-with-agent-modal.tsx +49 -23
  199. package/src/components/editor/ai/add-cell-with-ai.tsx +1 -0
  200. package/src/components/editor/ai/ai-completion-editor.tsx +3 -1
  201. package/src/components/editor/alerts/connecting-alert.tsx +1 -66
  202. package/src/components/editor/alerts/connection-notice.tsx +140 -0
  203. package/src/components/editor/app-container.tsx +9 -3
  204. package/src/components/editor/boundary/ErrorBoundary.tsx +38 -16
  205. package/src/components/editor/boundary/__tests__/ErrorBoundary.test.tsx +311 -0
  206. package/src/components/editor/cell/code/__tests__/rtc-editor-config.test.ts +50 -0
  207. package/src/components/editor/cell/code/cell-editor.tsx +41 -42
  208. package/src/components/editor/chrome/components/__tests__/feedback-button.test.tsx +94 -0
  209. package/src/components/editor/chrome/components/feedback-button.tsx +63 -42
  210. package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +114 -7
  211. package/src/components/editor/chrome/panels/__tests__/sandbox-recovery.test.tsx +165 -0
  212. package/src/components/editor/chrome/panels/packages-panel.tsx +93 -166
  213. package/src/components/editor/chrome/panels/sandbox-controller.tsx +106 -0
  214. package/src/components/editor/chrome/panels/sandbox-panel.tsx +173 -0
  215. package/src/components/editor/chrome/wrapper/footer-items/backend-status.tsx +3 -1
  216. package/src/components/editor/code/__tests__/readonly-diff.test.tsx +65 -0
  217. package/src/components/editor/code/readonly-diff.tsx +1 -1
  218. package/src/components/editor/connections/database/__tests__/__snapshots__/as-code.test.ts.snap +98 -98
  219. package/src/components/editor/connections/database/__tests__/as-code.test.ts +11 -5
  220. package/src/components/editor/dynamic-favicon.tsx +17 -10
  221. package/src/components/editor/file-tree/__tests__/tree-actions.test.tsx +54 -0
  222. package/src/components/editor/file-tree/tree-actions.tsx +11 -14
  223. package/src/components/editor/header/__tests__/status.test.tsx +8 -20
  224. package/src/components/editor/header/filename-input.tsx +2 -0
  225. package/src/components/editor/header/status.tsx +5 -3
  226. package/src/components/editor/notebook-cell.tsx +4 -2
  227. package/src/components/editor/output/JsonOutput.tsx +182 -632
  228. package/src/components/editor/output/Outputs.css +50 -17
  229. package/src/components/editor/output/__tests__/JsonOutput-interactions.test.tsx +254 -0
  230. package/src/components/editor/output/__tests__/JsonOutput-mimetype.test.tsx +41 -46
  231. package/src/components/editor/output/__tests__/{json-output.test.ts → json-output-formatting.test.ts} +113 -75
  232. package/src/components/editor/output/__tests__/json-output-pagination.test.ts +252 -0
  233. package/src/components/editor/output/json-output/formatting.ts +167 -0
  234. package/src/components/editor/output/json-output/pagination.ts +155 -0
  235. package/src/components/editor/output/json-output/renderers.tsx +196 -0
  236. package/src/components/editor/output/json-output/themes.ts +52 -0
  237. package/src/components/editor/renderers/cell-array.tsx +1 -5
  238. package/src/components/home/components.tsx +17 -0
  239. package/src/components/pages/gallery-page.tsx +2 -4
  240. package/src/components/pages/home-page.tsx +2 -4
  241. package/src/components/slides/slide-form.tsx +1 -0
  242. package/src/components/slides/slide-notes-editor.tsx +1 -0
  243. package/src/components/terminal/terminal.tsx +4 -4
  244. package/src/components/tracing/tracing-spec.ts +1 -1
  245. package/src/components/ui/table.tsx +1 -1
  246. package/src/components/ui/use-toast.ts +8 -0
  247. package/src/core/MarimoApp.tsx +0 -2
  248. package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +259 -259
  249. package/src/core/cells/__tests__/cell.test.ts +61 -0
  250. package/src/core/cells/__tests__/cells.test.ts +37 -37
  251. package/src/core/cells/cell.ts +9 -1
  252. package/src/core/cells/pending-delete-service.ts +12 -16
  253. package/src/core/codemirror/language/__tests__/sql.test.ts +70 -2
  254. package/src/core/codemirror/language/languages/sql/sql.ts +16 -1
  255. package/src/core/codemirror/rtc/extension.ts +5 -2
  256. package/src/core/config/pair.ts +19 -0
  257. package/src/core/datasets/__tests__/data-source.test.ts +2 -0
  258. package/src/core/datasets/data-source-connections.ts +15 -10
  259. package/src/core/edit-app.tsx +8 -5
  260. package/src/core/errors/state.ts +1 -2
  261. package/src/core/islands/__tests__/parse.test.ts +140 -7
  262. package/src/core/islands/bootstrap.ts +1 -0
  263. package/src/core/islands/bridge.ts +3 -0
  264. package/src/core/islands/parse.ts +20 -0
  265. package/src/core/network/connection-notice.ts +84 -0
  266. package/src/core/network/requests-lazy.ts +3 -0
  267. package/src/core/network/requests-network.ts +12 -0
  268. package/src/core/network/requests-static.ts +3 -0
  269. package/src/core/network/requests-toasting.tsx +3 -0
  270. package/src/core/network/resolve.ts +2 -1
  271. package/src/core/network/types.ts +7 -0
  272. package/src/core/packages/__tests__/usePackageAction.test.tsx +129 -0
  273. package/src/core/packages/__tests__/useSandboxController.test.tsx +165 -0
  274. package/src/core/packages/package-data.ts +35 -0
  275. package/src/core/packages/sandbox-state.ts +15 -0
  276. package/src/core/packages/toast-components.tsx +11 -0
  277. package/src/core/packages/useInstallPackage.ts +1 -1
  278. package/src/core/packages/usePackageAction.ts +51 -0
  279. package/src/core/packages/usePackageDependencies.ts +22 -0
  280. package/src/core/packages/useSandboxController.ts +188 -0
  281. package/src/core/run-app.tsx +2 -0
  282. package/src/core/runtime/__tests__/runtime.test.ts +11 -0
  283. package/src/core/runtime/runtime.ts +7 -2
  284. package/src/core/wasm/bridge.ts +11 -0
  285. package/src/core/websocket/__tests__/useMarimoKernelConnection.hook.test.tsx +212 -2
  286. package/src/core/websocket/transports/transport.ts +1 -1
  287. package/src/core/websocket/transports/ws.ts +1 -1
  288. package/src/core/websocket/types.ts +11 -1
  289. package/src/core/websocket/useMarimoKernelConnection.tsx +43 -8
  290. package/src/css/app/Cell.css +2 -0
  291. package/src/hooks/useDebounce.ts +2 -0
  292. package/src/mount.tsx +3 -0
  293. package/src/plugins/core/BadPlugin.tsx +3 -14
  294. package/src/plugins/core/RenderHTML.tsx +22 -2
  295. package/src/plugins/core/__test__/RenderHTML.test.ts +187 -0
  296. package/src/plugins/core/registerReactComponent.tsx +0 -1
  297. package/src/plugins/impl/FormPlugin.tsx +2 -0
  298. package/src/plugins/impl/MatrixPlugin.tsx +2 -0
  299. package/src/plugins/impl/RangeSliderPlugin.tsx +2 -0
  300. package/src/plugins/impl/SliderPlugin.tsx +2 -0
  301. package/src/plugins/impl/data-editor/__tests__/glide-data-editor.test.tsx +23 -18
  302. package/src/plugins/impl/data-frames/panel.tsx +1 -0
  303. package/src/plugins/impl/image-comparison/ImageComparisonComponent.tsx +1 -1
  304. package/src/plugins/impl/image-comparison/__tests__/ImageComparisonComponent.test.tsx +15 -0
  305. package/src/plugins/layout/AccordionPlugin.tsx +7 -2
  306. package/src/plugins/layout/JsonOutputPlugin.tsx +7 -10
  307. package/src/plugins/layout/__test__/AccordionPlugin.test.tsx +66 -0
  308. package/src/stories/sandbox.stories.tsx +308 -0
  309. package/dist/ErrorBoundary-CR-ZQN_c.js +0 -125
  310. package/dist/_baseMerge-Dv8bBloN.js +0 -73
  311. package/dist/_initCloneObject---vediN2.js +0 -95
  312. package/dist/arc-DOVLrCPd.js +0 -82
  313. package/dist/architecture-TIHT7OUA-MiJpPpQW.js +0 -3
  314. package/dist/array-CApNbSU9.js +0 -6
  315. package/dist/assets/__vite-browser-external-BdKGjRZ4.js +0 -1
  316. package/dist/assets/worker-DlIBfp9S.js +0 -98
  317. package/dist/blockDiagram-VBNYF7ZC-Bc--I65C.js +0 -3158
  318. package/dist/chat-ui-DjTMZRVP.js +0 -8455
  319. package/dist/chunk-3NCLNEKW-BIp_lB_4.js +0 -14
  320. package/dist/chunk-5FCAYU7R-C_HO9A1J.js +0 -23
  321. package/dist/chunk-5HE753X5-Bixm_MV-.js +0 -38
  322. package/dist/chunk-5JV3BV7I-CH5J3iZY.js +0 -54
  323. package/dist/chunk-5TONJI2A-DG6gDiSS.js +0 -62
  324. package/dist/chunk-7BUUIJ7U-5afmIPeo.js +0 -82
  325. package/dist/chunk-BIQX33UG-7TMBUf89.js +0 -23
  326. package/dist/chunk-CQNSW5MT-Cnqqsxt-.js +0 -38
  327. package/dist/chunk-CYSBUYHQ-BAbymFHR.js +0 -23
  328. package/dist/chunk-EMLP6XTP-Bsz-QL77.js +0 -23
  329. package/dist/chunk-J7OUQ5F2-CM_meqZH.js +0 -57
  330. package/dist/chunk-JG7HCLWE-DUnCDgPk.js +0 -62
  331. package/dist/chunk-MOZMSUNE-BG6IHvzT.js +0 -35
  332. package/dist/chunk-OSBZ3O6U-CYwj_bvR.js +0 -23
  333. package/dist/chunk-QBLGF6JB-B4vJ4j8J.js +0 -23
  334. package/dist/chunk-QR6OTTB3-D4wH2ldi.js +0 -3738
  335. package/dist/chunk-R7FJI6CG-Blf8FcmZ.js +0 -64
  336. package/dist/chunk-RYQCIY6F-BWbIlBIH.js +0 -241
  337. package/dist/chunk-U6XO7XAA-COw445DS.js +0 -63
  338. package/dist/chunk-UBXNYLIW-BHigVU4H.js +0 -32
  339. package/dist/chunk-YOTPTUD7-Dn044Q_d.js +0 -30
  340. package/dist/chunk-ZIRB5QZD-BR4TMfOa.js +0 -1153
  341. package/dist/classDiagram-JCYQIIEL-DmXSLxe_.js +0 -30
  342. package/dist/classDiagram-v2-OCEON4UE-CooqVexA.js +0 -30
  343. package/dist/click-outside-container-D_URcm3D.js +0 -167
  344. package/dist/code-block-37QAKDTI-CQrzBOr-.js +0 -107
  345. package/dist/copy-CD7S58CY.js +0 -141
  346. package/dist/cynefin-VYW2F7L2-CeHAxOEX.js +0 -3
  347. package/dist/dagre-VZM6K2ZE-CJggWPTJ.js +0 -328
  348. package/dist/dist-3_o3T78I.js +0 -1360
  349. package/dist/dist-ABQjhuek.js +0 -64
  350. package/dist/dist-CO5R6VMi.js +0 -538
  351. package/dist/dropdown-menu-DhCfcscX.js +0 -5858
  352. package/dist/emotion-is-prop-valid.esm-M57VsT8j.js +0 -14
  353. package/dist/error-banner-CsXbEEjo.js +0 -611
  354. package/dist/eventmodeling-45OFAUF4-B_lWZ-Pd.js +0 -3
  355. package/dist/flowDiagram-UKHOOZJN-g1AmrZzM.js +0 -17
  356. package/dist/gitGraph-TEB2WS4Q-Dj-eXUph.js +0 -3
  357. package/dist/info-DKCQHKI2-Bhb-pe2e.js +0 -3
  358. package/dist/line-CuKjIQa1.js +0 -34
  359. package/dist/map-BJpOfPlL.js +0 -149
  360. package/dist/math-BXtKgYah.js +0 -10
  361. package/dist/mermaid-4DMBBIKO-Ddp9TgNp.js +0 -2
  362. package/dist/mermaid-Bi23Ibnw.js +0 -1173
  363. package/dist/mermaid-parser.core-Din9FvSi.js +0 -99
  364. package/dist/packet-7NZHBO7P-fbkIIyKE.js +0 -3
  365. package/dist/path-BNHuHZP8.js +0 -87
  366. package/dist/pie-RZYD4A2V-CLRIsxY1.js +0 -3
  367. package/dist/purify.es-CB-YO1Hn.js +0 -774
  368. package/dist/radar-I7S5WNFK-BGzCbgXu.js +0 -3
  369. package/dist/railroad-3IZDKUUU-CvQIIcuU.js +0 -3
  370. package/dist/railroad-abnf-AHOZXSZD-CMwtlNtu.js +0 -3
  371. package/dist/railroad-ebnf-EBAXGLYW-n1QUOAsh.js +0 -3
  372. package/dist/railroad-peg-LSFZ7HO6-PjnrpmCs.js +0 -3
  373. package/dist/schemas-DzK6U94N.js +0 -6057
  374. package/dist/stateDiagram-v2-UEYNNEHI-BTRUs8Il.js +0 -30
  375. package/dist/step-CKczO72Q.js +0 -670
  376. package/dist/swimlanesDiagram-ULZ7WXOC-NBmgBl9G.js +0 -31
  377. package/dist/timer-DpDyj9G0.js +0 -596
  378. package/dist/tooltip-Dt77sBeT.js +0 -390
  379. package/dist/treeView-QDETBFTQ-DU8LwHuf.js +0 -3
  380. package/dist/treemap-6X3UGDF4-DUk4-k_O.js +0 -3
  381. package/dist/useTheme-CVJ0UOf2.js +0 -841
  382. package/dist/vega-loader.browser-DEggZzni.js +0 -1083
  383. package/dist/wardley-OPB4EBWU-Db-AaGas.js +0 -3
  384. package/src/components/editor/output/EmotionCacheProvider.tsx +0 -32
@@ -0,0 +1,140 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+ import { useAtomValue } from "jotai";
3
+ import { AlertCircleIcon, ArrowRightIcon } from "lucide-react";
4
+ import { useState } from "react";
5
+ import { Spinner } from "@/components/icons/spinner";
6
+ import { Button } from "@/components/ui/button";
7
+ import { hasCellsAtom } from "@/core/cells/cells";
8
+ import type { AppConfig } from "@/core/config/config-schema";
9
+ import { connectionAtom } from "@/core/network/connection";
10
+ import { connectionNoticeAtom } from "@/core/network/connection-notice";
11
+ import { WebSocketState } from "@/core/websocket/types";
12
+ import { useDelayElapsed } from "@/hooks/useDelayElapsed";
13
+ import { useInterval } from "@/hooks/useInterval";
14
+ import { cn } from "@/utils/cn";
15
+ import { SandboxErrorOutput } from "../chrome/panels/sandbox-panel";
16
+ import { chromeAtom, useChromeActions } from "../chrome/state";
17
+ import { VerticalLayoutWrapper } from "../renderers/vertical-layout/vertical-layout-wrapper";
18
+
19
+ export const ConnectionNotice = ({
20
+ appConfig,
21
+ onRetry,
22
+ }: {
23
+ appConfig: AppConfig;
24
+ onRetry: () => void;
25
+ }) => {
26
+ const notice = useAtomValue(connectionNoticeAtom);
27
+ const connection = useAtomValue(connectionAtom);
28
+ const hasCells = useAtomValue(hasCellsAtom);
29
+ const chrome = useAtomValue(chromeAtom);
30
+ const packagesOpen =
31
+ (chrome.isSidebarOpen && chrome.selectedPanel === "packages") ||
32
+ (chrome.isDeveloperPanelOpen &&
33
+ chrome.selectedDeveloperPanelTab === "packages");
34
+ const elapsed = useDelayElapsed(notice?.pending ? 500 : 0);
35
+ const { openApplication } = useChromeActions();
36
+
37
+ // Reconnection uses the footer indicator while existing cells stay visible.
38
+ if (
39
+ !notice ||
40
+ !elapsed ||
41
+ (hasCells &&
42
+ connection.state === WebSocketState.CONNECTING &&
43
+ connection.phase === "reconnecting")
44
+ ) {
45
+ return null;
46
+ }
47
+
48
+ return (
49
+ <VerticalLayoutWrapper
50
+ appConfig={appConfig}
51
+ className="shrink-0 pb-0 sm:pb-0 print:hidden"
52
+ innerClassName="pb-0 sm:pb-0 pr-4"
53
+ >
54
+ <section
55
+ aria-label="Notebook startup"
56
+ className={cn(
57
+ "text-muted-foreground",
58
+ hasCells
59
+ ? "mt-4 mb-8 border rounded-lg px-5 py-5"
60
+ : "max-w-sm mx-auto mt-16 sm:mt-24 px-4",
61
+ hasCells && !notice.pending && "border-(--red-7)",
62
+ )}
63
+ >
64
+ <div role="status" className="flex items-center gap-2.5">
65
+ {notice.pending ? (
66
+ <Spinner className="size-4 shrink-0" aria-hidden={true} />
67
+ ) : (
68
+ <AlertCircleIcon
69
+ className="size-4 shrink-0 text-(--red-11)"
70
+ aria-hidden={true}
71
+ />
72
+ )}
73
+ <h2
74
+ className={cn(
75
+ "text-base",
76
+ notice.pending
77
+ ? "font-normal text-foreground"
78
+ : "font-medium text-(--red-11)",
79
+ )}
80
+ >
81
+ {notice.title}
82
+ </h2>
83
+ </div>
84
+ <div className="ml-6.5">
85
+ <p
86
+ className={cn(
87
+ "mt-3 text-sm leading-7",
88
+ !notice.pending && "text-foreground",
89
+ )}
90
+ >
91
+ {notice.description}
92
+ </p>
93
+ {!notice.sandbox && notice.error && (
94
+ <SandboxErrorOutput error={notice.error} />
95
+ )}
96
+ <div className="mt-5 flex flex-wrap items-center gap-x-6 gap-y-3">
97
+ {!packagesOpen && (notice.sandbox || notice.pending) && (
98
+ <Button
99
+ variant="text"
100
+ size="xs"
101
+ className="h-auto p-0 text-xs gap-1.5"
102
+ onClick={() => openApplication("packages")}
103
+ >
104
+ {notice.pending ? "View setup details" : "Open Packages"}
105
+ <ArrowRightIcon className="size-3" aria-hidden={true} />
106
+ </Button>
107
+ )}
108
+ {!notice.pending && !notice.sandbox && (
109
+ <Button variant="outline" size="xs" onClick={onRetry}>
110
+ Try again
111
+ </Button>
112
+ )}
113
+ {notice.pending && <StartupElapsedTime />}
114
+ </div>
115
+ </div>
116
+ </section>
117
+ </VerticalLayoutWrapper>
118
+ );
119
+ };
120
+
121
+ function StartupElapsedTime() {
122
+ const [startedAt] = useState(Date.now);
123
+ const [seconds, setSeconds] = useState(0);
124
+ useInterval(() => setSeconds(Math.floor((Date.now() - startedAt) / 1000)), {
125
+ delayMs: 1000,
126
+ whenVisible: true,
127
+ });
128
+ const duration =
129
+ seconds < 60
130
+ ? `${seconds}s`
131
+ : `${Math.floor(seconds / 60)}m ${seconds % 60}s`;
132
+ return (
133
+ <span
134
+ className="text-xs text-muted-foreground/70 tabular-nums"
135
+ aria-live="off"
136
+ >
137
+ Elapsed {duration}
138
+ </span>
139
+ );
140
+ }
@@ -4,8 +4,11 @@ import type React from "react";
4
4
  import type { PropsWithChildren } from "react";
5
5
  import type { AppConfig } from "@/core/config/config-schema";
6
6
  import { PyodideLoader } from "@/core/wasm/PyodideLoader";
7
- import { isAppClosed } from "@/core/websocket/connection-utils";
8
- import type { ConnectionStatus } from "@/core/websocket/types";
7
+ import {
8
+ type ConnectionStatus,
9
+ WebSocketClosedReason,
10
+ WebSocketState,
11
+ } from "@/core/websocket/types";
9
12
  import { cn } from "@/utils/cn";
10
13
  import { DynamicFavicon } from "./dynamic-favicon";
11
14
  import { StatusOverlay } from "./header/status";
@@ -44,7 +47,10 @@ export const AppContainer: React.FC<PropsWithChildren<Props>> = ({
44
47
  data-connection-state={connectionState}
45
48
  className={cn(
46
49
  "mathjax_ignore",
47
- isAppClosed(connectionState) && "disconnected",
50
+ connection.state === WebSocketState.CLOSED &&
51
+ connection.code !==
52
+ WebSocketClosedReason.KERNEL_STARTUP_ERROR &&
53
+ "disconnected",
48
54
  "bg-background w-full h-full text-textColor",
49
55
  "flex flex-col overflow-y-auto",
50
56
  width === "full" && "config-width-full",
@@ -1,11 +1,21 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
- import type { PropsWithChildren } from "react";
2
+ import { Provider } from "jotai";
3
+ import { type PropsWithChildren, lazy, Suspense, useState } from "react";
3
4
  import {
4
5
  type FallbackProps,
5
6
  ErrorBoundary as ReactErrorBoundary,
6
7
  } from "react-error-boundary";
7
- import { Constants } from "@/core/constants";
8
+ import { store } from "@/core/state/jotai";
8
9
  import { Button } from "../../ui/button";
10
+ import { Dialog, DialogTrigger } from "../../ui/dialog";
11
+ import { Toaster } from "../../ui/toaster";
12
+ import { TooltipProvider } from "../../ui/tooltip";
13
+
14
+ const FeedbackModal = lazy(() =>
15
+ import("../chrome/components/feedback-button").then((mod) => ({
16
+ default: mod.FeedbackModal,
17
+ })),
18
+ );
9
19
 
10
20
  export const ErrorBoundary: React.FC<PropsWithChildren> = (props) => {
11
21
  return (
@@ -16,26 +26,38 @@ export const ErrorBoundary: React.FC<PropsWithChildren> = (props) => {
16
26
  };
17
27
 
18
28
  const FallbackComponent: React.FC<FallbackProps> = (props) => {
29
+ const [open, setOpen] = useState(false);
30
+
19
31
  return (
20
- <div className="flex-1 flex items-center justify-center flex-col space-y-4 max-w-2xl mx-auto px-6">
32
+ <div className="flex-1 flex items-center justify-center flex-col space-y-4 max-w-2xl mx-auto px-6 pb-6">
21
33
  <h1 className="text-2xl font-bold">Something went wrong</h1>
22
34
  <pre className="text-xs bg-muted/40 border rounded-md p-4 max-w-[80%] whitespace-normal">
23
35
  {props.error?.message}
24
36
  </pre>
25
- <div>
26
- If this is an issue with marimo, please report it on{" "}
27
- <a href={Constants.issuesPage} target="_blank" className="underline">
28
- GitHub
29
- </a>
30
- .
37
+ <div className="flex flex-wrap items-center justify-center gap-2">
38
+ <Dialog open={open} onOpenChange={setOpen}>
39
+ <DialogTrigger asChild={true}>
40
+ <Button variant="outline">Report an issue</Button>
41
+ </DialogTrigger>
42
+ {open && (
43
+ <Provider store={store}>
44
+ <TooltipProvider>
45
+ <Suspense fallback={null}>
46
+ <FeedbackModal />
47
+ </Suspense>
48
+ <Toaster />
49
+ </TooltipProvider>
50
+ </Provider>
51
+ )}
52
+ </Dialog>
53
+ <Button
54
+ data-testid="reset-error-boundary-button"
55
+ onClick={props.resetErrorBoundary}
56
+ variant="outline"
57
+ >
58
+ Try again
59
+ </Button>
31
60
  </div>
32
- <Button
33
- data-testid="reset-error-boundary-button"
34
- onClick={props.resetErrorBoundary}
35
- variant="outline"
36
- >
37
- Try again
38
- </Button>
39
61
  </div>
40
62
  );
41
63
  };
@@ -0,0 +1,311 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { fireEvent, render, screen, waitFor } from "@testing-library/react";
4
+ import type { PropsWithChildren, ReactNode } from "react";
5
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
6
+ import { MockRequestClient } from "@/__mocks__/requests";
7
+ import { ModalProvider } from "@/components/modal/ImperativeModal";
8
+ import { TooltipProvider } from "@/components/ui/tooltip";
9
+ import { clearToasts } from "@/components/ui/use-toast";
10
+ import { initialNotebookState, notebookAtom } from "@/core/cells/cells";
11
+ import { viewStateAtom } from "@/core/mode";
12
+ import { connectionAtom } from "@/core/network/connection";
13
+ import { requestClientAtom } from "@/core/network/requests";
14
+ import type { EnvironmentInfo } from "@/core/network/types";
15
+ import { filenameAtom } from "@/core/saving/file-state";
16
+ import { store } from "@/core/state/jotai";
17
+ import { WebSocketState } from "@/core/websocket/types";
18
+ import { ErrorBoundary } from "../ErrorBoundary";
19
+
20
+ vi.mock("@/utils/copy", () => ({
21
+ copyToClipboard: vi.fn().mockResolvedValue(undefined),
22
+ }));
23
+
24
+ const environment: EnvironmentInfo = {
25
+ marimo: "1.2.3",
26
+ editable: false,
27
+ location: "~/.venv/site-packages/marimo",
28
+ OS: "Darwin",
29
+ "OS Version": "25.0",
30
+ Processor: "arm",
31
+ "Python Version": "3.12.9",
32
+ Locale: "en_US",
33
+ Binaries: { Browser: "chrome 140", Node: "v22", uv: "0.11" },
34
+ Dependencies: { click: "8.4.2" },
35
+ "Optional Dependencies": { pandas: "3.0.0" },
36
+ "Experimental Flags": {},
37
+ };
38
+
39
+ const CRASH_MESSAGE = "cell output crashed";
40
+
41
+ function CrashingChild(): never {
42
+ throw new Error(CRASH_MESSAGE);
43
+ }
44
+
45
+ function RecoverableChild({ crash }: { crash: boolean }) {
46
+ if (crash) {
47
+ throw new Error(CRASH_MESSAGE);
48
+ }
49
+ return <p>recovered child</p>;
50
+ }
51
+
52
+ function DescendantProviders({ children }: PropsWithChildren) {
53
+ return (
54
+ <TooltipProvider>
55
+ <ModalProvider>{children}</ModalProvider>
56
+ </TooltipProvider>
57
+ );
58
+ }
59
+
60
+ function isExpectedReactExceptionOutput(args: unknown[]): boolean {
61
+ return args.some((arg) => {
62
+ if (arg instanceof Error) {
63
+ return arg.message.includes(CRASH_MESSAGE);
64
+ }
65
+ return (
66
+ typeof arg === "string" &&
67
+ (arg.includes(CRASH_MESSAGE) ||
68
+ arg.includes("The above error occurred") ||
69
+ arg.includes("React will try to recreate this component tree"))
70
+ );
71
+ });
72
+ }
73
+
74
+ function suppressExpectedReactException() {
75
+ return vi.spyOn(console, "error").mockImplementation((...args) => {
76
+ if (isExpectedReactExceptionOutput(args)) {
77
+ return;
78
+ }
79
+ throw new Error(`Unexpected console.error: ${args.map(String).join(" ")}`);
80
+ });
81
+ }
82
+
83
+ let consoleError: ReturnType<typeof vi.spyOn> | undefined;
84
+
85
+ function renderCrashedBoundary(child: ReactNode = <CrashingChild />) {
86
+ consoleError?.mockRestore();
87
+ consoleError = suppressExpectedReactException();
88
+ return render(<ErrorBoundary>{child}</ErrorBoundary>);
89
+ }
90
+
91
+ function setRequestClient(
92
+ overrides?: Parameters<typeof MockRequestClient.create>[0],
93
+ ) {
94
+ const client = MockRequestClient.create(overrides);
95
+ store.set(requestClientAtom, client);
96
+ return client;
97
+ }
98
+
99
+ async function openReportDialog() {
100
+ const trigger = screen.getByRole("button", { name: "Report an issue" });
101
+ trigger.focus();
102
+ fireEvent.click(trigger);
103
+ return screen.findByRole("dialog");
104
+ }
105
+
106
+ async function expectReportButtonFocused() {
107
+ await waitFor(() => {
108
+ expect(
109
+ screen.getByRole("button", { name: "Report an issue" }),
110
+ ).toHaveFocus();
111
+ });
112
+ }
113
+
114
+ async function closeReportDialog(method: "escape" | "close") {
115
+ const dialog = await screen.findByRole("dialog");
116
+ if (method === "escape") {
117
+ fireEvent.keyDown(dialog, { key: "Escape" });
118
+ } else {
119
+ fireEvent.click(screen.getByRole("button", { name: "Close" }));
120
+ }
121
+ await waitFor(() => {
122
+ expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
123
+ });
124
+ await expectReportButtonFocused();
125
+ }
126
+
127
+ async function expectRealReportModal() {
128
+ const dialog = await openReportDialog();
129
+ expect(dialog).toHaveTextContent("Report an issue");
130
+ expect(screen.getByRole("link", { name: "Open GitHub issue" })).toBeVisible();
131
+ expect(
132
+ screen.getByRole("checkbox", { name: "Include errors" }),
133
+ ).toBeVisible();
134
+ expect(
135
+ screen.getByRole("checkbox", { name: "Include notebook code" }),
136
+ ).toBeVisible();
137
+ await screen.findByText("Environment details");
138
+ return dialog;
139
+ }
140
+
141
+ function resetSharedState() {
142
+ localStorage.clear();
143
+ clearToasts();
144
+ store.set(requestClientAtom, null);
145
+ store.set(notebookAtom, initialNotebookState());
146
+ store.set(viewStateAtom, { mode: "edit", cellAnchor: null });
147
+ store.set(connectionAtom, { state: WebSocketState.OPEN });
148
+ store.set(filenameAtom, "/project/example.py");
149
+ }
150
+
151
+ describe("ErrorBoundary report dialog", () => {
152
+ beforeEach(() => {
153
+ resetSharedState();
154
+ });
155
+
156
+ afterEach(() => {
157
+ consoleError?.mockRestore();
158
+ consoleError = undefined;
159
+ resetSharedState();
160
+ });
161
+
162
+ it("opens the report modal after a child render exception", async () => {
163
+ const { getEnvironmentInfo } = setRequestClient({
164
+ getEnvironmentInfo: vi.fn().mockResolvedValue(environment),
165
+ });
166
+
167
+ renderCrashedBoundary();
168
+
169
+ expect(screen.getByText("Something went wrong")).toBeVisible();
170
+ expect(screen.getByText("cell output crashed")).toBeVisible();
171
+ expect(screen.getByTestId("reset-error-boundary-button")).toBeVisible();
172
+ expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
173
+ expect(getEnvironmentInfo).not.toHaveBeenCalled();
174
+
175
+ await expectRealReportModal();
176
+ await waitFor(() => expect(getEnvironmentInfo).toHaveBeenCalledOnce());
177
+ });
178
+
179
+ it("still opens one report dialog after descendant providers disappear", async () => {
180
+ const { getEnvironmentInfo } = setRequestClient({
181
+ getEnvironmentInfo: vi.fn().mockResolvedValue(environment),
182
+ });
183
+
184
+ renderCrashedBoundary(
185
+ <DescendantProviders>
186
+ <CrashingChild />
187
+ </DescendantProviders>,
188
+ );
189
+
190
+ await expectRealReportModal();
191
+ expect(screen.getAllByRole("dialog")).toHaveLength(1);
192
+ await waitFor(() => expect(getEnvironmentInfo).toHaveBeenCalledOnce());
193
+ });
194
+
195
+ it("shows partial diagnostics when the request client is missing", async () => {
196
+ renderCrashedBoundary();
197
+
198
+ await openReportDialog();
199
+ await screen.findByText("Environment details");
200
+
201
+ expect(
202
+ screen.getByText("Server environment information unavailable"),
203
+ ).toBeVisible();
204
+ expect(
205
+ screen.queryByText("Loading environment details…"),
206
+ ).not.toBeInTheDocument();
207
+ expect(
208
+ screen.queryByRole("button", { name: "Retry" }),
209
+ ).not.toBeInTheDocument();
210
+ expect(
211
+ screen.getByRole("checkbox", { name: "Include notebook code" }),
212
+ ).toBeDisabled();
213
+ expect(screen.getByText("Notebook source is unavailable.")).toBeVisible();
214
+ const link = screen.getByRole("link", { name: "Open GitHub issue" });
215
+ expect(link.getAttribute("href") ?? "").toContain("&env=");
216
+ });
217
+
218
+ it("does not request environment or source while the report dialog is closed", async () => {
219
+ localStorage.setItem(
220
+ "marimo:issue-report:include-code",
221
+ JSON.stringify(true),
222
+ );
223
+ const { getEnvironmentInfo, readCode } = setRequestClient({
224
+ getEnvironmentInfo: vi.fn().mockResolvedValue(environment),
225
+ readCode: vi.fn().mockResolvedValue({ contents: "import marimo" }),
226
+ });
227
+
228
+ renderCrashedBoundary();
229
+
230
+ expect(screen.getByText("Something went wrong")).toBeVisible();
231
+ expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
232
+ expect(getEnvironmentInfo).not.toHaveBeenCalled();
233
+ expect(readCode).not.toHaveBeenCalled();
234
+ });
235
+
236
+ it("restores focus to the report button after closing with Escape", async () => {
237
+ setRequestClient({
238
+ getEnvironmentInfo: vi.fn().mockResolvedValue(environment),
239
+ });
240
+ renderCrashedBoundary();
241
+ await openReportDialog();
242
+ await closeReportDialog("escape");
243
+ expect(screen.getByText(CRASH_MESSAGE)).toBeVisible();
244
+ });
245
+
246
+ it("restores focus to the report button after closing with the close control", async () => {
247
+ setRequestClient({
248
+ getEnvironmentInfo: vi.fn().mockResolvedValue(environment),
249
+ });
250
+ renderCrashedBoundary();
251
+ await openReportDialog();
252
+ await closeReportDialog("close");
253
+ expect(screen.getByText(CRASH_MESSAGE)).toBeVisible();
254
+ });
255
+
256
+ it("reopens the report dialog while the original error remains", async () => {
257
+ setRequestClient({
258
+ getEnvironmentInfo: vi.fn().mockResolvedValue(environment),
259
+ });
260
+ renderCrashedBoundary();
261
+ await openReportDialog();
262
+ await closeReportDialog("escape");
263
+ expect(screen.getByText(CRASH_MESSAGE)).toBeVisible();
264
+ await expectRealReportModal();
265
+ expect(screen.getByText(CRASH_MESSAGE)).toBeVisible();
266
+ });
267
+
268
+ it("restores the child after try again once the failure is cleared", () => {
269
+ setRequestClient({
270
+ getEnvironmentInfo: vi.fn().mockResolvedValue(environment),
271
+ });
272
+ const view = renderCrashedBoundary(<RecoverableChild crash={true} />);
273
+ expect(screen.getByText("Something went wrong")).toBeVisible();
274
+
275
+ view.rerender(
276
+ <ErrorBoundary>
277
+ <RecoverableChild crash={false} />
278
+ </ErrorBoundary>,
279
+ );
280
+ fireEvent.click(screen.getByTestId("reset-error-boundary-button"));
281
+
282
+ expect(screen.getByText("recovered child")).toBeVisible();
283
+ expect(screen.queryByText("Something went wrong")).not.toBeInTheDocument();
284
+ });
285
+
286
+ it("does not read notebook source when opened with fresh inclusion preferences", async () => {
287
+ const { readCode } = setRequestClient({
288
+ getEnvironmentInfo: vi.fn().mockResolvedValue(environment),
289
+ readCode: vi.fn().mockResolvedValue({ contents: "import marimo" }),
290
+ });
291
+
292
+ renderCrashedBoundary();
293
+ await expectRealReportModal();
294
+
295
+ expect(readCode).not.toHaveBeenCalled();
296
+ });
297
+
298
+ it("renders the copy toast after the ancestor toaster is gone", async () => {
299
+ setRequestClient({
300
+ getEnvironmentInfo: vi.fn().mockResolvedValue(environment),
301
+ });
302
+ renderCrashedBoundary();
303
+ await expectRealReportModal();
304
+
305
+ fireEvent.click(
306
+ screen.getByRole("button", { name: "Copy environment JSON" }),
307
+ );
308
+
309
+ expect(await screen.findByText("Environment details copied")).toBeVisible();
310
+ });
311
+ });
@@ -0,0 +1,50 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { EditorState } from "@codemirror/state";
4
+ import { describe, expect, it } from "vitest";
5
+ import { composeRtcEditorConfig } from "../cell-editor";
6
+
7
+ describe("composeRtcEditorConfig", () => {
8
+ it("does not accumulate RTC extensions across reconfigurations", () => {
9
+ const baseExtension = EditorState.readOnly.of(false);
10
+ const baseExtensions = [baseExtension];
11
+ const firstRtc = {
12
+ code: "first RTC code",
13
+ extension: EditorState.tabSize.of(2),
14
+ };
15
+ const secondRtc = {
16
+ code: "second RTC code",
17
+ extension: EditorState.tabSize.of(4),
18
+ };
19
+
20
+ const firstConfig = composeRtcEditorConfig(
21
+ "local code",
22
+ baseExtensions,
23
+ firstRtc,
24
+ );
25
+ const secondConfig = composeRtcEditorConfig(
26
+ "local code",
27
+ baseExtensions,
28
+ secondRtc,
29
+ );
30
+
31
+ expect(baseExtensions).toEqual([baseExtension]);
32
+ expect(firstConfig).toEqual({
33
+ code: firstRtc.code,
34
+ extensions: [baseExtension, firstRtc.extension],
35
+ });
36
+ expect(secondConfig).toEqual({
37
+ code: secondRtc.code,
38
+ extensions: [baseExtension, secondRtc.extension],
39
+ });
40
+ });
41
+
42
+ it("reuses the base configuration when RTC is disabled", () => {
43
+ const extensions = [EditorState.readOnly.of(false)];
44
+
45
+ const config = composeRtcEditorConfig("local code", extensions, undefined);
46
+
47
+ expect(config).toEqual({ code: "local code", extensions });
48
+ expect(config.extensions).toBe(extensions);
49
+ });
50
+ });