@marimo-team/islands 0.24.3-dev9 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (440) hide show
  1. package/dist/{ConnectedDataExplorerComponent-C9OF_75w.js → ConnectedDataExplorerComponent-KVhjDMSe.js} +1066 -1066
  2. package/dist/ErrorBoundary-DnNkbB_U.js +200 -0
  3. package/dist/{ImageComparisonComponent-2a9TMMm2.js → ImageComparisonComponent-Dlm5aWnH.js} +4 -4
  4. package/dist/{Plot-C-9MQeGP.js → Plot-CTyjbQDT.js} +21032 -20666
  5. package/dist/_baseMerge-BiGRwuqv.js +165 -0
  6. package/dist/{abnfDiagram-N423BO3Z-CQKwzX8t.js → abnfDiagram-VCTEODGH-CpN0FRda.js} +17 -18
  7. package/dist/{any-language-editor-DmQ7dyCx.js → any-language-editor-CycIVIeP.js} +17 -17
  8. package/dist/arc-7ra3iNdN.js +81 -0
  9. package/dist/architecture-7GRP2DOG-BWLylgiA.js +3 -0
  10. package/dist/{architectureDiagram-T3A2C74G-CncrDpfa.js → architectureDiagram-5GKGNRK7-DDyj5SRB.js} +25 -25
  11. package/dist/assets/__vite-browser-external-C9iR87Zl.js +1 -0
  12. package/dist/assets/worker-D7hWi02M.js +109 -0
  13. package/dist/blockDiagram-I7D4REHJ-BbXEXJRu.js +1922 -0
  14. package/dist/{button-CKBkqR9J.js → button-DdXAm8iz.js} +5 -5
  15. package/dist/{c4Diagram-5PPSVZJV-DarUk3Yq.js → c4Diagram-7LVT6UL2-DcLsbQmR.js} +696 -668
  16. package/dist/{capabilities-CMBBJTcc.js → capabilities-B3ce_GNu.js} +1 -1
  17. package/dist/{html-to-image-BtFMe_Nq.js → cells-DYIkSxs6.js} +6757 -10137
  18. package/dist/{channel-CyiYUzJv.js → channel-B5XnTarr.js} +1 -1
  19. package/dist/chat-ui-BXj3uYjy.js +5786 -0
  20. package/dist/{check-CHig47QV.js → check-B7cIK6gb.js} +1 -1
  21. package/dist/{chunk-2Q5K7J3B-BMuJl2jB.js → chunk-2Q5K7J3B-BMkUkeuy.js} +2 -2
  22. package/dist/chunk-4HAMMTFA-D2qv8E0X.js +4188 -0
  23. package/dist/{chunk-5VM5RSS4-Bcq0ftdy.js → chunk-5VM5RSS4-DKsmPCL6.js} +2 -2
  24. package/dist/{chunk-NSK5VX7P-CDTMcJB0.js → chunk-75Z2AOVW-5_HsvXwr.js} +23 -23
  25. package/dist/{chunk-I66GZJ75-BqyTCAPf.js → chunk-DU6HZSFF-BUgAHvnM.js} +21 -4
  26. package/dist/{chunk-2GRJ4B5K-DgO8Qudq.js → chunk-F27PBJKO-DHmYWZXr.js} +4 -4
  27. package/dist/{chunk-KEIR6QF5-C4KX3XYh.js → chunk-FOHPRMQF-BpYR8RqP.js} +4 -4
  28. package/dist/{chunk-WRU74C26-BpWsbiDK.js → chunk-GMAD6QVW-Czynz8aA.js} +486 -258
  29. package/dist/chunk-GVQU2GXP-yQEdulRS.js +37 -0
  30. package/dist/{chunk-5RXB4S5H-tLNUVDdO.js → chunk-IMKFNOWR-BfwTebD0.js} +11 -11
  31. package/dist/{chunk-JWPE2WC7-BJwjGUnF.js → chunk-JWPE2WC7-DJvv1t8U.js} +2 -2
  32. package/dist/chunk-L3NEJ4N5-DwRWnDDK.js +245 -0
  33. package/dist/{chunk-7Z6QIM7H-Dc18hFxH.js → chunk-OSK3NFVY-BBKU_Ign.js} +222 -144
  34. package/dist/{chunk-W5SLKNZC-DLnvpEtV.js → chunk-P2QGCYS3-C0iGU29N.js} +20 -20
  35. package/dist/{chunk-KBJHAD2P-BuqWqkcW.js → chunk-POPQ4Y6H-BCbS3Ozc.js} +4 -4
  36. package/dist/{chunk-4I5QYGJK-DpytHuZQ.js → chunk-PWAF6VOD-CTFCL0jI.js} +4 -4
  37. package/dist/{chunk-JQJVKLGR-CgqCwe8E.js → chunk-SHT3W25Y-BspJEJ5H.js} +109 -63
  38. package/dist/{chunk-6Q2QTUOP-DmTcEBMn.js → chunk-SVP7TREG-DX3rFNTe.js} +5 -5
  39. package/dist/{chunk-GF5L2VYU-B0wgCQgS.js → chunk-TICWLB2K-fVF7Uujg.js} +13 -13
  40. package/dist/{chunk-XXDRQBXY-D73H4IN0.js → chunk-XXDRQBXY-Dnl5VBqG.js} +3 -3
  41. package/dist/{chunk-Y2CYZVJY-DoegUj_e.js → chunk-Y2CYZVJY-DKKS1VyV.js} +1 -1
  42. package/dist/{chunk-5FQGJX7Z-B85XCPv6.js → chunk-YOKDWASO-DrWbKnxA.js} +6925 -5460
  43. package/dist/classDiagram-ZZMXUADV-Fe-V14Sc.js +30 -0
  44. package/dist/classDiagram-v2-VYDZK3BY-xTDg7lOs.js +30 -0
  45. package/dist/code-visibility-DElzk_hh.js +138 -0
  46. package/dist/{common-Cv-4hMa0.js → common-B4-ih9IA.js} +8838 -12645
  47. package/dist/{compiler-runtime-CAH9RLw2.js → compiler-runtime-Cj8ZWiNS.js} +2 -2
  48. package/dist/copy-icon-CLdENY__.js +190 -0
  49. package/dist/{cose-bilkent-JH36ORCC-FUWHkXFX.js → cose-bilkent-JH36ORCC-_LyRKVqm.js} +2 -2
  50. package/dist/cubic-OaEBhQPK.js +1404 -0
  51. package/dist/cynefin-OW5HDTMX-obq-i80l.js +3 -0
  52. package/dist/{cynefinDiagram-MW4NZA55-BXSFK93J.js → cynefinDiagram-5FMLGOSQ-CFQrqzSX.js} +10 -10
  53. package/dist/{dagre-CGyalH38.js → dagre-BGSfXNEb.js} +678 -537
  54. package/dist/dagre-GXQ25YYZ-xHNAUTyG.js +425 -0
  55. package/dist/{data-grid-overlay-editor-DZM4BAaI.js → data-grid-overlay-editor-CRH6JQPE.js} +8 -9
  56. package/dist/{data-grid-types-BnZwWG8z.js → data-grid-types-BucVmDDp.js} +1 -1
  57. package/dist/{defaultLocale-DKaU_opG.js → defaultLocale-JI_lnoCe.js} +1 -1
  58. package/dist/{diagram-7IWD3JNH-BjfzDNvj.js → diagram-S7CK7UJ4-Bt1rPbAx.js} +40 -39
  59. package/dist/{diagram-UB23O5K3-DDg-7wLA.js → diagram-UQ7AKVKN-D1sq5FoZ.js} +10 -10
  60. package/dist/{diagram-B4RE2ZJO-CKccR9eE.js → diagram-VSXAHHWV-BKpmG23x.js} +10 -10
  61. package/dist/{diagram-Q27KOJAE-Zhz01ss7.js → diagram-VX7I27RA-BMwGFVD6.js} +21 -21
  62. package/dist/{diagram-LBJQPF4R-CJ-bOruh.js → diagram-Z3DM3KII--KLDR9Pl.js} +10 -10
  63. package/dist/dialog-CgNq6y9-.js +94 -0
  64. package/dist/{dist-dAqZAPcF.js → dist-1HDa70oE.js} +3 -3
  65. package/dist/{dist-Re9sOPOx.js → dist-9x1ZRe-e.js} +2 -2
  66. package/dist/{dist-DEeLqRlU.js → dist-B1vYLK28.js} +1 -1
  67. package/dist/{dist-C5Bn52W4.js → dist-B8D7McG3.js} +3 -3
  68. package/dist/{dist-_XvgytiS.js → dist-BHCHt1NI.js} +1 -1
  69. package/dist/{dist-CRjAdnqF.js → dist-BRxfaJgs.js} +3 -3
  70. package/dist/{dist-Byj2bV8o.js → dist-BVGtK7Ht.js} +1 -1
  71. package/dist/{dist-BIN5LKyE.js → dist-BaHjhkpZ.js} +2 -2
  72. package/dist/{dist-DVFbIwc5.js → dist-BtKIs9N-.js} +1 -1
  73. package/dist/{dist-BrQGcfQ9.js → dist-C2KzV-vf.js} +2 -2
  74. package/dist/{dist-DJFiGP2k.js → dist-C2wDlYdD.js} +2 -2
  75. package/dist/{dist-BVRM2y1P.js → dist-C8xMLVYi.js} +2 -2
  76. package/dist/dist-CMh_W4y4.js +538 -0
  77. package/dist/{extends-BPB6_lTK.js → dist-CO1AwwG_.js} +516 -561
  78. package/dist/{dist-BzN60Z2s.js → dist-CPGxfVGX.js} +1 -1
  79. package/dist/{dist-C7v-9nUK.js → dist-Cm3iAFMD.js} +3 -3
  80. package/dist/{dist-CGAxEiaM.js → dist-Cs2FijYk.js} +2 -2
  81. package/dist/{dist-hXd65f0_.js → dist-D1PpnuZB.js} +1 -1
  82. package/dist/{dist-VdvbxG72.js → dist-DHOTQar0.js} +1 -1
  83. package/dist/{dist-DcgzXCfy.js → dist-DPcEtqmG.js} +2 -2
  84. package/dist/{dist-D4Z_qdP4.js → dist-Df0wKI0F.js} +1 -1
  85. package/dist/{dist-qhLI-SHd.js → dist-DszxuxS1.js} +2 -2
  86. package/dist/{dist-BEmkrGoQ.js → dist-Kxgjxakw.js} +1 -1
  87. package/dist/{dist-BbSMhhD2.js → dist-KzZ7WELx.js} +2 -2
  88. package/dist/{dist-jWUab2d1.js → dist-lA1Cttkb.js} +1 -1
  89. package/dist/dropdown-menu-CZgjYSAh.js +4502 -0
  90. package/dist/{ebnfDiagram-BXEA7PRR-Dm75pCAS.js → ebnfDiagram-PWID7BFC-WnG4cvSQ.js} +17 -18
  91. package/dist/{erDiagram-JOGREHBK-rVDG3VSQ.js → erDiagram-RLTQ6QDP-DhzKiYHc.js} +1037 -798
  92. package/dist/error-banner-B94dcDmP.js +391 -0
  93. package/dist/error-entries-C0NsUqXl.js +56 -0
  94. package/dist/{esm-CR1nhb1c.js → esm-BElI3qKH.js} +22 -22
  95. package/dist/{esm-C1d0g6Bv.js → esm-G4Fhsgng.js} +51 -37
  96. package/dist/{esm-CesmlDrs.js → esm-h8f7-1CJ.js} +423 -423
  97. package/dist/eventmodeling-NTZA5JFV-D7nP7Uyu.js +3 -0
  98. package/dist/feedback-button-cQcMqpEP.js +380 -0
  99. package/dist/flowDiagram-HODETNUW-6dMdB8pR.js +16 -0
  100. package/dist/{formats-CslNOKy0.js → formats-BR5iGb4s.js} +8 -8
  101. package/dist/{ganttDiagram-PKOTCBZU-9D-LXF6P.js → ganttDiagram-EL5Y4UJY-DbzEBLbl.js} +15 -15
  102. package/dist/gitGraph-4MIJSDKK-Dy9uQXGh.js +3 -0
  103. package/dist/{gitGraphDiagram-DS77QQ5N-BfpsXTLf.js → gitGraphDiagram-WWUBYQGX-BMf0iKi1.js} +10 -10
  104. package/dist/{glide-data-editor-sPZ8fUFn.js → glide-data-editor-DYOxfSzI.js} +2143 -1910
  105. package/dist/{glide-utils-NShAPPtn.js → glide-utils-B3rUT5PI.js} +15 -15
  106. package/dist/globals-BOoLHa7X.js +19 -0
  107. package/dist/highlighted-body-KPVGNVTW-Ad3MKZ18.js +38 -0
  108. package/dist/html-to-image-CeYYvQNb.js +3272 -0
  109. package/dist/info-A6RAGUB7-DKoayiHY.js +3 -0
  110. package/dist/{infoDiagram-6WML65LV-DqCTXHVy.js → infoDiagram-27XIBGKW-BagtCX_5.js} +7 -7
  111. package/dist/{ishikawaDiagram-WSZJBQD7-BCUYmZ1v.js → ishikawaDiagram-5VMMS53U-DByDehYy.js} +8 -8
  112. package/dist/{journeyDiagram-NVQOT4AX-BXB6M4zN.js → journeyDiagram-3NMN7TZE-D2LcKDUO.js} +8 -8
  113. package/dist/{jsx-runtime-CpoKNRgu.js → jsx-runtime-DgDPJV9S.js} +1 -1
  114. package/dist/{kanban-definition-27J2QSJJ-CGRhrDUX.js → kanban-definition-UXKFOSKX-BxwkbBI_.js} +29 -29
  115. package/dist/katex-DFBiSucW.js +21840 -0
  116. package/dist/{label-lwzzBHUW.js → label-BJ49YTvk.js} +6 -6
  117. package/dist/{linear-D5r_1YqD.js → linear-Cnaq3Tfb.js} +4 -4
  118. package/dist/{loader-VZ__bv4g.js → loader-D2W4oM-3.js} +15 -15
  119. package/dist/main.js +8113 -13475
  120. package/dist/mermaid-DPNuhemn.js +3084 -0
  121. package/dist/mermaid-HWGCJPDP-DYCzSILp.js +2 -0
  122. package/dist/mermaid-parser.core-DxuSfYwJ.js +638 -0
  123. package/dist/{mhchem-BjwrxcV-.js → mhchem-DWCF06w9.js} +1 -1
  124. package/dist/{micromark-factory-space-DruVGzeG.js → micromark-factory-space-DGxAzmw8.js} +2 -2
  125. package/dist/{mindmap-definition-FAOFIHXS-C9gRhzZq.js → mindmap-definition-YA3MSWOX-Cgvojw3y.js} +17 -17
  126. package/dist/{node-sql-parser-DesT1xNw.js → node-sql-parser-BiZOJ-FS.js} +0 -3
  127. package/dist/{number-overlay-editor-De2CabLL.js → number-overlay-editor-DZp5bEmI.js} +5 -5
  128. package/dist/objectWithoutPropertiesLoose-BRDcTsfB.js +23 -0
  129. package/dist/packet-AYTQ26CC-Yw7ajchJ.js +3 -0
  130. package/dist/{pegDiagram-VL7TDLO6-Df9IUjMD.js → pegDiagram-XKGWAZYB-DahHhYyN.js} +19 -20
  131. package/dist/{pick-SnHlmIBN.js → pick-CJeoxYOS.js} +1 -1
  132. package/dist/pie-WAS4IAKB-BKdKOAkL.js +3 -0
  133. package/dist/{pieDiagram-7S7Q4E2Y-BmRzQ8ZP.js → pieDiagram-E7YTZNPT-CCtX0oCK.js} +29 -31
  134. package/dist/{process-output-CPy0YKUe.js → process-output-BCTjlyuH.js} +7 -6
  135. package/dist/purify.es-DgKyQviY.js +805 -0
  136. package/dist/{quadrantDiagram-CIZ2JOQS-wFBvWcxS.js → quadrantDiagram-AXDQQJYC-DftINo6g.js} +5 -5
  137. package/dist/radar-RG4KPBEZ-ScR_D_Ts.js +3 -0
  138. package/dist/railroad-74A4TZTK-DQvPSdo5.js +3 -0
  139. package/dist/railroad-abnf-HS5TGJTU-CsJQG2O8.js +3 -0
  140. package/dist/railroad-ebnf-LZEXJU2U-BgEobkSn.js +3 -0
  141. package/dist/railroad-peg-WCYAUIDC-DjLAjofI.js +3 -0
  142. package/dist/{railroadDiagram-AXF67PYL-DxbIatiN.js → railroadDiagram-O6MQD6OU-Du5NsPUb.js} +17 -18
  143. package/dist/{react-dom-C94wmq5w.js → react-dom-BUgJ-ab_.js} +3 -3
  144. package/dist/{react-DMWcHin-.js → react-ofJbxM4x.js} +2 -2
  145. package/dist/{react-vega-DSD5aT7_.js → react-vega-5AcMW8gG.js} +10012 -9369
  146. package/dist/{requirementDiagram-LRYGKXZP-X_x-Y713.js → requirementDiagram-BXWQKSXE-0pmmxL6_.js} +20 -19
  147. package/dist/{reveal-component-CVsYlb9J.js → reveal-component-C9h2Lubu.js} +1303 -1237
  148. package/dist/{sankeyDiagram-W5VNT64P-BYJALbbm.js → sankeyDiagram-P5KCCOFB-CDKR5TJH.js} +4 -4
  149. package/dist/{select-3qHpFKF9.js → select-C6plhmZr.js} +317 -562
  150. package/dist/{sequenceDiagram-SI44F4Z6-BiPBe3Sv.js → sequenceDiagram-WJ2MYXX4-DB-ChWwU.js} +11 -11
  151. package/dist/{sizeCapture-X5ZJPWSS-BKLDeh5H.js → sizeCapture-INFHLROL-Dx5SpkQF.js} +2 -2
  152. package/dist/{spec-CutWdqo3.js → spec-CAYwFeV6.js} +4 -4
  153. package/dist/{src-C31ngt98.js → src-CA_m4ONl.js} +298 -303
  154. package/dist/{stateDiagram-OKZ733FA-B7QyNBxH.js → stateDiagram-D77RDMKH-orN6AwMx.js} +19 -20
  155. package/dist/stateDiagram-v2-MP3YSRHH-o05_rJN8.js +30 -0
  156. package/dist/style.css +2 -2
  157. package/dist/{swimlanes-SLNWSIFB-B4f8VP3b.js → swimlanes-42K2YHIH-BkSDcqDT.js} +1501 -1591
  158. package/dist/swimlanesDiagram-VR7AAH4N-D_nFQfRc.js +30 -0
  159. package/dist/{swiper-component-C90YjhaV.js → swiper-component-IOUowglM.js} +39 -26
  160. package/dist/{time-aWGfjN8x.js → time-BieKwdYS.js} +42 -42
  161. package/dist/{timeline-definition-Z64GVDOM-Dacq_5lr.js → timeline-definition-24CTP7MA-BuqJUIRZ.js} +7 -7
  162. package/dist/{toDate-CpXBeDq0.js → toDate-WXg1yLcI.js} +7 -7
  163. package/dist/toast-DvY70eFX.js +1834 -0
  164. package/dist/tooltip-qB8NlSqV.js +1742 -0
  165. package/dist/treeView-Q6P3EWNA-DuRV97YF.js +3 -0
  166. package/dist/treemap-WGGIJYW6-AsLq-uwC.js +3 -0
  167. package/dist/use-restore-focus-D5D9xEjZ.js +5946 -0
  168. package/dist/{use-vega-container-remeasure-DUW4tRxq.js → use-vega-container-remeasure-4DwJbPWu.js} +2 -2
  169. package/dist/{useAsyncData-CH6-CgJF.js → useAsyncData-BD6AStMt.js} +3 -3
  170. package/dist/{useDateFormatter-Cp9Jdxls.js → useDateFormatter-BRTPsUK7.js} +8 -8
  171. package/dist/{useDeepCompareMemoize-JC87C_jO.js → useDeepCompareMemoize-D_HNdyvI.js} +2 -2
  172. package/dist/{useIframeCapabilities-De3C-9b4.js → useIframeCapabilities-CVtNkI3Y.js} +2 -2
  173. package/dist/{useLifecycle-B4RGlyf0.js → useLifecycle-DPgiiwwU.js} +6 -6
  174. package/dist/useNumberFormatter-BfDpa21j.js +251 -0
  175. package/dist/useTheme-DzXmYZ5V.js +904 -0
  176. package/dist/{vega-component-ovBXgp91.js → vega-component-D0QXopU8.js} +34 -34
  177. package/dist/{vega-embed-container-TJ5z5dZw.js → vega-embed-container-CbnTccZi.js} +6 -6
  178. package/dist/vega-loader.browser-D95WfUcj.js +1139 -0
  179. package/dist/{vennDiagram-T6HMQDX7-D-Rcq3Iu.js → vennDiagram-4TSXK5OY-CTUlJ2PK.js} +9 -9
  180. package/dist/wardley-WFR3VGLG-Na-tFhQJ.js +3 -0
  181. package/dist/{wardleyDiagram-T6FBY63Y-Z-Og7_Wd.js → wardleyDiagram-VM6X3IG4-3evLUb0M.js} +10 -10
  182. package/dist/{xychartDiagram-ELKLHX3M-BDJ2dITA.js → xychartDiagram-S5SC5T6Z-DL2E3K8-.js} +248 -139
  183. package/package.json +54 -57
  184. package/src/__mocks__/notebook.ts +1 -0
  185. package/src/__mocks__/requests.ts +7 -0
  186. package/src/__tests__/__snapshots__/CellStatus.test.tsx.snap +12 -12
  187. package/src/__tests__/fixtures/pair-preview.ts +19 -0
  188. package/src/__tests__/mount.test.ts +18 -0
  189. package/src/components/app-config/optional-features.tsx +6 -0
  190. package/src/components/chat/acp/__tests__/agent-panel-initialization.test.tsx +394 -0
  191. package/src/components/chat/acp/__tests__/agent-panel.test.tsx +272 -0
  192. package/src/components/chat/acp/__tests__/state.test.ts +2 -2
  193. package/src/components/chat/acp/agent-panel.tsx +115 -74
  194. package/src/components/chat/acp/state.ts +1 -1
  195. package/src/components/data-table/__tests__/filters.test.ts +21 -21
  196. package/src/components/data-table/charts/__tests__/spec.test.ts +5 -10
  197. package/src/components/data-table/column-explorer-panel/column-explorer.tsx +2 -6
  198. package/src/components/data-table/column-summary/chart-spec-model.tsx +1 -1
  199. package/src/components/data-table/columns.tsx +64 -67
  200. package/src/components/editor/RecoveryButton.tsx +1 -0
  201. package/src/components/editor/__tests__/dynamic-favicon.test.tsx +36 -2
  202. package/src/components/editor/actions/__tests__/pair-with-agent-commands.test.ts +115 -0
  203. package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +218 -0
  204. package/src/components/editor/actions/pair-with-agent-commands.ts +46 -9
  205. package/src/components/editor/actions/pair-with-agent-modal.tsx +49 -23
  206. package/src/components/editor/ai/add-cell-with-ai.tsx +1 -0
  207. package/src/components/editor/ai/ai-completion-editor.tsx +3 -1
  208. package/src/components/editor/alerts/__tests__/connection-notice.test.tsx +186 -0
  209. package/src/components/editor/alerts/__tests__/startup-progress.test.tsx +185 -0
  210. package/src/components/editor/alerts/connecting-alert.tsx +1 -66
  211. package/src/components/editor/alerts/connection-notice.tsx +186 -0
  212. package/src/components/editor/alerts/startup-output.tsx +173 -0
  213. package/src/components/editor/alerts/startup-progress.tsx +204 -0
  214. package/src/components/editor/app-container.tsx +9 -3
  215. package/src/components/editor/boundary/ErrorBoundary.tsx +38 -16
  216. package/src/components/editor/boundary/__tests__/ErrorBoundary.test.tsx +311 -0
  217. package/src/components/editor/cell/__tests__/toolbar.test.tsx +73 -0
  218. package/src/components/editor/cell/code/__tests__/rtc-editor-config.test.ts +50 -0
  219. package/src/components/editor/cell/code/cell-editor.tsx +41 -42
  220. package/src/components/editor/cell/toolbar.tsx +11 -0
  221. package/src/components/editor/chrome/components/__tests__/feedback-button.test.tsx +94 -0
  222. package/src/components/editor/chrome/components/feedback-button.tsx +63 -42
  223. package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +164 -8
  224. package/src/components/editor/chrome/panels/__tests__/sandbox-details.test.tsx +358 -0
  225. package/src/components/editor/chrome/panels/__tests__/sandbox-recovery.test.tsx +168 -0
  226. package/src/components/editor/chrome/panels/packages-panel.tsx +113 -175
  227. package/src/components/editor/chrome/panels/sandbox-controller.tsx +106 -0
  228. package/src/components/editor/chrome/panels/sandbox-details-state.ts +38 -0
  229. package/src/components/editor/chrome/panels/sandbox-panel.tsx +230 -0
  230. package/src/components/editor/chrome/panels/sandbox-toggle.tsx +52 -0
  231. package/src/components/editor/chrome/wrapper/app-chrome.tsx +7 -0
  232. package/src/components/editor/chrome/wrapper/footer-items/backend-status.tsx +3 -1
  233. package/src/components/editor/code/__tests__/readonly-diff.test.tsx +65 -0
  234. package/src/components/editor/code/readonly-diff.tsx +1 -1
  235. package/src/components/editor/connections/database/__tests__/__snapshots__/as-code.test.ts.snap +98 -98
  236. package/src/components/editor/connections/database/__tests__/as-code.test.ts +11 -5
  237. package/src/components/editor/dynamic-favicon.tsx +17 -10
  238. package/src/components/editor/file-tree/__tests__/file-explorer-navigation.test.tsx +13 -11
  239. package/src/components/editor/file-tree/__tests__/tree-actions.test.tsx +54 -0
  240. package/src/components/editor/file-tree/tree-actions.tsx +11 -14
  241. package/src/components/editor/header/__tests__/status.test.tsx +8 -20
  242. package/src/components/editor/header/filename-input.tsx +2 -0
  243. package/src/components/editor/header/status.tsx +5 -3
  244. package/src/components/editor/navigation/clipboard.ts +1 -0
  245. package/src/components/editor/notebook-cell.tsx +4 -2
  246. package/src/components/editor/output/JsonOutput.tsx +182 -632
  247. package/src/components/editor/output/Outputs.css +49 -17
  248. package/src/components/editor/output/__tests__/JsonOutput-interactions.test.tsx +254 -0
  249. package/src/components/editor/output/__tests__/JsonOutput-mimetype.test.tsx +41 -46
  250. package/src/components/editor/output/__tests__/{json-output.test.ts → json-output-formatting.test.ts} +113 -75
  251. package/src/components/editor/output/__tests__/json-output-pagination.test.ts +252 -0
  252. package/src/components/editor/output/json-output/formatting.ts +167 -0
  253. package/src/components/editor/output/json-output/pagination.ts +155 -0
  254. package/src/components/editor/output/json-output/renderers.tsx +196 -0
  255. package/src/components/editor/output/json-output/themes.ts +52 -0
  256. package/src/components/editor/package-alert.tsx +112 -66
  257. package/src/components/editor/renderers/cell-array.tsx +1 -5
  258. package/src/components/editor/renderers/slides-layout/__tests__/plugin.test.ts +3 -7
  259. package/src/components/slides/slide-form.tsx +1 -0
  260. package/src/components/slides/slide-notes-editor.tsx +1 -0
  261. package/src/components/tracing/tracing-spec.ts +1 -1
  262. package/src/components/ui/__tests__/badge.test.ts +6 -4
  263. package/src/components/ui/use-toast.ts +8 -0
  264. package/src/core/MarimoApp.tsx +0 -2
  265. package/src/core/ai/context/providers/__tests__/error.test.ts +16 -22
  266. package/src/core/alerts/__tests__/environment.test.ts +183 -0
  267. package/src/core/alerts/__tests__/state.test.tsx +240 -0
  268. package/src/core/alerts/environment.ts +57 -0
  269. package/src/core/alerts/state.ts +155 -99
  270. package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +296 -259
  271. package/src/core/cells/__tests__/apply-transaction.test.ts +34 -0
  272. package/src/core/cells/__tests__/cell.test.ts +61 -0
  273. package/src/core/cells/__tests__/cells.test.ts +182 -37
  274. package/src/core/cells/__tests__/document-changes.test.ts +39 -1
  275. package/src/core/cells/__tests__/outputs.test.tsx +117 -0
  276. package/src/core/cells/__tests__/pending-delete-service.test.tsx +4 -3
  277. package/src/core/cells/__tests__/session.test.ts +7 -0
  278. package/src/core/cells/cell.ts +9 -1
  279. package/src/core/cells/cells.ts +13 -18
  280. package/src/core/cells/document-changes.ts +11 -1
  281. package/src/core/cells/outputs.ts +66 -9
  282. package/src/core/cells/pending-delete-service.ts +12 -16
  283. package/src/core/cells/session.ts +2 -0
  284. package/src/core/cells/types.ts +2 -0
  285. package/src/core/codemirror/find-replace/__tests__/navigate.test.ts +8 -16
  286. package/src/core/codemirror/language/__tests__/sql.test.ts +70 -2
  287. package/src/core/codemirror/language/embedded/__tests__/embedded-python.test.ts +4 -8
  288. package/src/core/codemirror/language/languages/sql/sql.ts +16 -1
  289. package/src/core/codemirror/rtc/extension.ts +5 -2
  290. package/src/core/config/pair.ts +19 -0
  291. package/src/core/datasets/__tests__/data-source.test.ts +2 -0
  292. package/src/core/datasets/data-source-connections.ts +15 -10
  293. package/src/core/edit-app.tsx +8 -5
  294. package/src/core/errors/state.ts +1 -2
  295. package/src/core/export/__tests__/hooks.test.ts +1 -6
  296. package/src/core/islands/bootstrap.ts +3 -1
  297. package/src/core/islands/bridge.ts +3 -0
  298. package/src/core/islands/worker-factory.ts +2 -12
  299. package/src/core/kernel/messages.ts +2 -2
  300. package/src/core/network/__tests__/requests-network.test.ts +4 -3
  301. package/src/core/network/connection-notice.ts +117 -0
  302. package/src/core/network/connection.ts +19 -0
  303. package/src/core/network/requests-lazy.ts +3 -0
  304. package/src/core/network/requests-network.ts +12 -0
  305. package/src/core/network/requests-static.ts +3 -0
  306. package/src/core/network/requests-toasting.tsx +3 -0
  307. package/src/core/network/resolve.ts +2 -1
  308. package/src/core/network/types.ts +7 -0
  309. package/src/core/network/useConnectionNotice.ts +53 -0
  310. package/src/core/packages/__tests__/usePackageAction.test.tsx +129 -0
  311. package/src/core/packages/__tests__/useSandboxController.test.tsx +246 -0
  312. package/src/core/packages/package-data.ts +35 -0
  313. package/src/core/packages/sandbox-state.ts +67 -0
  314. package/src/core/packages/toast-components.tsx +11 -0
  315. package/src/core/packages/useInstallPackage.ts +1 -1
  316. package/src/core/packages/usePackageAction.ts +51 -0
  317. package/src/core/packages/usePackageDependencies.ts +22 -0
  318. package/src/core/packages/useSandboxController.ts +208 -0
  319. package/src/core/run-app.tsx +2 -0
  320. package/src/core/static/__tests__/files.test.ts +12 -30
  321. package/src/core/wasm/__tests__/bootstrap.test.ts +63 -0
  322. package/src/core/wasm/__tests__/bridge.test.ts +13 -4
  323. package/src/core/wasm/__tests__/fs.test.ts +22 -0
  324. package/src/core/wasm/__tests__/runtime-config.test.ts +65 -0
  325. package/src/core/wasm/bridge.ts +35 -23
  326. package/src/core/wasm/rpc.ts +2 -0
  327. package/src/core/wasm/runtime-config.ts +31 -0
  328. package/src/core/wasm/worker/bootstrap.ts +21 -11
  329. package/src/core/wasm/worker/constants.ts +1 -0
  330. package/src/core/wasm/worker/fs.ts +4 -0
  331. package/src/core/wasm/worker/getController.ts +3 -1
  332. package/src/core/wasm/worker/save-worker.ts +10 -9
  333. package/src/core/wasm/worker/types.ts +6 -4
  334. package/src/core/wasm/worker/worker.ts +9 -9
  335. package/src/core/websocket/__tests__/useMarimoKernelConnection.hook.test.tsx +404 -3
  336. package/src/core/websocket/__tests__/useMarimoKernelConnection.test.ts +8 -6
  337. package/src/core/websocket/transports/transport.ts +1 -1
  338. package/src/core/websocket/transports/ws.ts +1 -1
  339. package/src/core/websocket/types.ts +11 -1
  340. package/src/core/websocket/useMarimoKernelConnection.tsx +63 -16
  341. package/src/hooks/useDebounce.ts +2 -0
  342. package/src/mount.tsx +3 -0
  343. package/src/plugins/core/BadPlugin.tsx +3 -14
  344. package/src/plugins/core/RenderHTML.tsx +22 -2
  345. package/src/plugins/core/__test__/RenderHTML.test.ts +187 -0
  346. package/src/plugins/core/registerReactComponent.tsx +0 -1
  347. package/src/plugins/impl/FileBrowserPlugin.tsx +10 -4
  348. package/src/plugins/impl/FormPlugin.tsx +2 -0
  349. package/src/plugins/impl/MatrixPlugin.tsx +2 -0
  350. package/src/plugins/impl/RangeSliderPlugin.tsx +2 -0
  351. package/src/plugins/impl/SliderPlugin.tsx +2 -0
  352. package/src/plugins/impl/__tests__/FileBrowserPlugin.test.tsx +50 -1
  353. package/src/plugins/impl/data-editor/__tests__/glide-data-editor.test.tsx +23 -18
  354. package/src/plugins/impl/data-frames/panel.tsx +1 -0
  355. package/src/plugins/impl/image-comparison/ImageComparisonComponent.tsx +1 -1
  356. package/src/plugins/impl/image-comparison/__tests__/ImageComparisonComponent.test.tsx +15 -0
  357. package/src/plugins/impl/vega/__tests__/make-selectable.test.ts +42 -48
  358. package/src/plugins/layout/AccordionPlugin.tsx +7 -2
  359. package/src/plugins/layout/JsonOutputPlugin.tsx +7 -10
  360. package/src/plugins/layout/__test__/AccordionPlugin.test.tsx +66 -0
  361. package/src/stories/sandbox.stories.tsx +488 -0
  362. package/src/utils/__tests__/path.test.ts +6 -5
  363. package/src/utils/__tests__/worker.test.ts +81 -0
  364. package/src/utils/worker.ts +22 -0
  365. package/dist/ErrorBoundary-CR-ZQN_c.js +0 -125
  366. package/dist/_baseMerge-Dv8bBloN.js +0 -73
  367. package/dist/_initCloneObject---vediN2.js +0 -95
  368. package/dist/arc-DOVLrCPd.js +0 -82
  369. package/dist/architecture-TIHT7OUA-MiJpPpQW.js +0 -3
  370. package/dist/array-CApNbSU9.js +0 -6
  371. package/dist/assets/__vite-browser-external-BdKGjRZ4.js +0 -1
  372. package/dist/assets/worker-DlIBfp9S.js +0 -98
  373. package/dist/blockDiagram-VBNYF7ZC-Bc--I65C.js +0 -3158
  374. package/dist/chat-ui-BSwr409J.js +0 -8455
  375. package/dist/chunk-3NCLNEKW-BIp_lB_4.js +0 -14
  376. package/dist/chunk-5FCAYU7R-C_HO9A1J.js +0 -23
  377. package/dist/chunk-5HE753X5-Bixm_MV-.js +0 -38
  378. package/dist/chunk-5JV3BV7I-CH5J3iZY.js +0 -54
  379. package/dist/chunk-5TONJI2A-DG6gDiSS.js +0 -62
  380. package/dist/chunk-7BUUIJ7U-5afmIPeo.js +0 -82
  381. package/dist/chunk-BIQX33UG-7TMBUf89.js +0 -23
  382. package/dist/chunk-CQNSW5MT-Cnqqsxt-.js +0 -38
  383. package/dist/chunk-CYSBUYHQ-BAbymFHR.js +0 -23
  384. package/dist/chunk-EMLP6XTP-Bsz-QL77.js +0 -23
  385. package/dist/chunk-J7OUQ5F2-CM_meqZH.js +0 -57
  386. package/dist/chunk-JG7HCLWE-DUnCDgPk.js +0 -62
  387. package/dist/chunk-MOZMSUNE-BG6IHvzT.js +0 -35
  388. package/dist/chunk-OSBZ3O6U-CYwj_bvR.js +0 -23
  389. package/dist/chunk-QBLGF6JB-B4vJ4j8J.js +0 -23
  390. package/dist/chunk-QR6OTTB3-D4wH2ldi.js +0 -3738
  391. package/dist/chunk-R7FJI6CG-Blf8FcmZ.js +0 -64
  392. package/dist/chunk-RYQCIY6F-BWbIlBIH.js +0 -241
  393. package/dist/chunk-U6XO7XAA-COw445DS.js +0 -63
  394. package/dist/chunk-UBXNYLIW-BHigVU4H.js +0 -32
  395. package/dist/chunk-YOTPTUD7-Dn044Q_d.js +0 -30
  396. package/dist/chunk-ZIRB5QZD-BR4TMfOa.js +0 -1153
  397. package/dist/classDiagram-JCYQIIEL-DmXSLxe_.js +0 -30
  398. package/dist/classDiagram-v2-OCEON4UE-CooqVexA.js +0 -30
  399. package/dist/click-outside-container-D_URcm3D.js +0 -167
  400. package/dist/code-block-37QAKDTI-CQrzBOr-.js +0 -107
  401. package/dist/copy-CD7S58CY.js +0 -141
  402. package/dist/cynefin-VYW2F7L2-CeHAxOEX.js +0 -3
  403. package/dist/dagre-VZM6K2ZE-CJggWPTJ.js +0 -328
  404. package/dist/dist-3_o3T78I.js +0 -1360
  405. package/dist/dist-ABQjhuek.js +0 -64
  406. package/dist/dist-CO5R6VMi.js +0 -538
  407. package/dist/dropdown-menu-DhCfcscX.js +0 -5858
  408. package/dist/emotion-is-prop-valid.esm-M57VsT8j.js +0 -14
  409. package/dist/error-banner-CsXbEEjo.js +0 -611
  410. package/dist/eventmodeling-45OFAUF4-B_lWZ-Pd.js +0 -3
  411. package/dist/flowDiagram-UKHOOZJN-g1AmrZzM.js +0 -17
  412. package/dist/gitGraph-TEB2WS4Q-Dj-eXUph.js +0 -3
  413. package/dist/info-DKCQHKI2-Bhb-pe2e.js +0 -3
  414. package/dist/line-CuKjIQa1.js +0 -34
  415. package/dist/map-BJpOfPlL.js +0 -149
  416. package/dist/math-BXtKgYah.js +0 -10
  417. package/dist/mermaid-4DMBBIKO-Ddp9TgNp.js +0 -2
  418. package/dist/mermaid-Bi23Ibnw.js +0 -1173
  419. package/dist/mermaid-parser.core-Din9FvSi.js +0 -99
  420. package/dist/packet-7NZHBO7P-fbkIIyKE.js +0 -3
  421. package/dist/path-BNHuHZP8.js +0 -87
  422. package/dist/pie-RZYD4A2V-CLRIsxY1.js +0 -3
  423. package/dist/purify.es-CB-YO1Hn.js +0 -774
  424. package/dist/radar-I7S5WNFK-BGzCbgXu.js +0 -3
  425. package/dist/railroad-3IZDKUUU-CvQIIcuU.js +0 -3
  426. package/dist/railroad-abnf-AHOZXSZD-CMwtlNtu.js +0 -3
  427. package/dist/railroad-ebnf-EBAXGLYW-n1QUOAsh.js +0 -3
  428. package/dist/railroad-peg-LSFZ7HO6-PjnrpmCs.js +0 -3
  429. package/dist/schemas-DzK6U94N.js +0 -6057
  430. package/dist/stateDiagram-v2-UEYNNEHI-BTRUs8Il.js +0 -30
  431. package/dist/step-CKczO72Q.js +0 -670
  432. package/dist/swimlanesDiagram-ULZ7WXOC-NBmgBl9G.js +0 -31
  433. package/dist/timer-DpDyj9G0.js +0 -596
  434. package/dist/tooltip-Dt77sBeT.js +0 -390
  435. package/dist/treeView-QDETBFTQ-DU8LwHuf.js +0 -3
  436. package/dist/treemap-6X3UGDF4-DUk4-k_O.js +0 -3
  437. package/dist/useTheme-CVJ0UOf2.js +0 -841
  438. package/dist/vega-loader.browser-DEggZzni.js +0 -1083
  439. package/dist/wardley-OPB4EBWU-Db-AaGas.js +0 -3
  440. package/src/components/editor/output/EmotionCacheProvider.tsx +0 -32
@@ -20,15 +20,13 @@
20
20
  @apply text-link;
21
21
  }
22
22
 
23
- .output {
24
- position: relative;
25
- }
26
-
27
23
  .output > * {
28
24
  max-width: 100%;
29
25
  }
30
26
 
31
27
  .output {
28
+ position: relative;
29
+
32
30
  /* Unset max-width on .markdown. Instead, we will apply
33
31
  to individual elements so it does not affect UI elements in markdown. */
34
32
  .markdown {
@@ -65,30 +63,64 @@
65
63
  .marimo-json-output {
66
64
  padding-top: 2px;
67
65
  padding-bottom: 2px;
68
- /* content-visibility is set by material UI;
69
- * when set on an element that has overflow auto, causes abrupt jumps when
70
- * scrolling in Chrome */
71
- content-visibility: unset;
72
- /* Coarse hack to stop children from overflowing; doesn't seem to affect
73
- * margin collapse. */
74
66
  overflow: auto;
75
67
  @apply text-xs;
76
68
 
77
- .data-key-pair:not(:last-child) {
69
+ .w-rjv-wrap > :not(:last-child) {
78
70
  margin-top: 0.2rem;
79
71
  margin-bottom: 0.2rem;
80
72
  }
81
73
 
82
- .copy-button {
83
- @apply invisible;
74
+ .w-rjv-wrap {
75
+ padding-left: 0.9rem !important;
76
+ margin-left: 0.3rem !important;
77
+ }
78
+
79
+ .w-rjv-line > span:first-child,
80
+ .w-rjv-colon,
81
+ .w-rjv-inner > span,
82
+ &.w-rjv-inner > span {
83
+ opacity: 0.8;
84
+ letter-spacing: 0.03125rem;
85
+ line-height: 1.5;
86
+ }
87
+
88
+ .w-rjv-inner > div:last-child:not(.w-rjv-wrap),
89
+ &.w-rjv-inner > div:last-child:not(.w-rjv-wrap) {
90
+ line-height: 1.5;
91
+ padding-left: 0 !important;
92
+ }
93
+
94
+ .w-rjv-line > button:only-child {
95
+ line-height: 1.5;
96
+ }
97
+
98
+ .w-rjv-arrow {
99
+ transition: transform 0.3s !important;
84
100
  }
85
101
 
86
- .data-key-pair:hover > .copy-button {
87
- @apply visible;
102
+ @media (prefers-reduced-motion: reduce) {
103
+ .w-rjv-arrow {
104
+ transition: none !important;
105
+ }
106
+ }
107
+
108
+ .w-rjv-object-size {
109
+ padding-left: 4px !important;
110
+ }
111
+
112
+ .copy-button {
113
+ opacity: 0;
88
114
  }
89
115
 
90
- .data-key-pair:hover > .data-key > .copy-button {
91
- @apply visible;
116
+ .copy-button:focus-visible,
117
+ .w-rjv-line:hover > .copy-button,
118
+ .w-rjv-inner:hover > span > .copy-button,
119
+ /* Root element has both .marimo-json-output and .w-rjv-inner on the same node */
120
+ &.w-rjv-inner:hover
121
+ > span
122
+ > .copy-button {
123
+ opacity: 1;
92
124
  }
93
125
  }
94
126
 
@@ -0,0 +1,254 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+ import { fireEvent, render, waitFor } from "@testing-library/react";
3
+ import { describe, expect, it, vi } from "vitest";
4
+ import { copyToClipboard } from "@/utils/copy";
5
+ import { JsonOutput } from "../JsonOutput";
6
+
7
+ vi.mock("@/utils/copy", () => ({
8
+ copyToClipboard: vi.fn().mockResolvedValue(undefined),
9
+ }));
10
+
11
+ describe("JsonOutput interactions", () => {
12
+ it("copies all descendants of a paginated parent and shows the real size", async () => {
13
+ const data = { rows: Array.from({ length: 120 }, (_, i) => i) };
14
+ const { getAllByRole, getByText } = render(
15
+ <JsonOutput data={data} valueTypes="json" />,
16
+ );
17
+ expect(getByText("120 Items")).toBeInTheDocument();
18
+ fireEvent.click(getAllByRole("button", { name: "Copy to clipboard" })[0]);
19
+ await waitFor(() =>
20
+ expect(copyToClipboard).toHaveBeenLastCalledWith(
21
+ JSON.stringify(data, null, 2),
22
+ ),
23
+ );
24
+ });
25
+
26
+ it("paginates independently and resets when the data changes", () => {
27
+ const values = Array.from({ length: 70 }, (_, i) => i);
28
+ const { getAllByRole, queryByRole, rerender } = render(
29
+ <JsonOutput data={{ "a.b": values, a: { b: values } }} />,
30
+ );
31
+ fireEvent.click(getAllByRole("button", { name: "... 40 more items" })[0]);
32
+ expect(getAllByRole("button", { name: "... 40 more items" })).toHaveLength(
33
+ 1,
34
+ );
35
+ expect(
36
+ queryByRole("button", { name: "... 10 more items" }),
37
+ ).toBeInTheDocument();
38
+ rerender(<JsonOutput data={{ "a.b": values, a: { b: values } }} />);
39
+ expect(
40
+ queryByRole("button", { name: "... 10 more items" }),
41
+ ).not.toBeInTheDocument();
42
+ expect(getAllByRole("button", { name: "... 40 more items" })).toHaveLength(
43
+ 2,
44
+ );
45
+ });
46
+
47
+ it("exposes expansion controls and long text as buttons", () => {
48
+ const text = "a".repeat(150);
49
+ const { getByRole, getAllByRole, queryByText } = render(
50
+ <JsonOutput data={{ text, plain: `text/plain:${text}` }} />,
51
+ );
52
+ const root = getByRole("button", { name: "Collapse root" });
53
+ expect(root).toHaveAttribute("aria-expanded", "true");
54
+ fireEvent.click(root);
55
+ expect(queryByText("plain")).not.toBeInTheDocument();
56
+ const collapsedRoot = getByRole("button", { name: "Expand root" });
57
+ expect(collapsedRoot).toHaveAttribute("aria-expanded", "false");
58
+ fireEvent.click(collapsedRoot);
59
+ expect(getByRole("button", { name: "Collapse root" })).toHaveAttribute(
60
+ "aria-expanded",
61
+ "true",
62
+ );
63
+ expect(queryByText("plain")).toBeInTheDocument();
64
+ const buttons = getAllByRole("button", { name: `${"a".repeat(100)}...` });
65
+ expect(buttons).toHaveLength(2);
66
+ fireEvent.keyDown(buttons[0], { key: "Enter" });
67
+ expect(buttons[0]).toHaveAttribute("aria-expanded", "true");
68
+ fireEvent.keyDown(buttons[0], { key: " " });
69
+ expect(buttons[0]).toHaveAttribute("aria-expanded", "false");
70
+ });
71
+ it("initially collapses containers below the fifth level", () => {
72
+ const data = {
73
+ one: { two: { three: { four: { five: { leaf: "value" } } } } },
74
+ };
75
+ const { getByRole, queryByText, getByText } = render(
76
+ <JsonOutput data={data} />,
77
+ );
78
+ expect(queryByText("leaf")).not.toBeInTheDocument();
79
+ const collapsed = getByRole("button", { name: "Expand five" });
80
+ expect(collapsed).toHaveAttribute("aria-expanded", "false");
81
+ fireEvent.click(collapsed);
82
+ expect(getByText("leaf")).toBeInTheDocument();
83
+ const expanded = getByRole("button", { name: "Collapse five" });
84
+ expect(expanded).toHaveAttribute("aria-expanded", "true");
85
+ fireEvent.click(expanded);
86
+ expect(queryByText("leaf")).not.toBeInTheDocument();
87
+ expect(getByRole("button", { name: "Expand five" })).toHaveAttribute(
88
+ "aria-expanded",
89
+ "false",
90
+ );
91
+ });
92
+ it.each([null, undefined, true, 42, Number.NaN, "text"])(
93
+ "renders scalar %j without tree state",
94
+ (data) => {
95
+ const { container } = render(<JsonOutput data={data} />);
96
+ expect(container.textContent).toBe(JSON.stringify(data) ?? "");
97
+ expect(container.querySelector(".marimo-json-output")).toBeNull();
98
+ },
99
+ );
100
+
101
+ it("updates Python and JSON renderers when the mode changes", () => {
102
+ const data = {
103
+ yes: true,
104
+ no: false,
105
+ none: null,
106
+ "text/plain+int:2": "value",
107
+ };
108
+ const { container, rerender } = render(<JsonOutput data={data} />);
109
+ expect(container.textContent).toContain("True");
110
+ expect(container.textContent).not.toContain("text/plain+int:2");
111
+ rerender(<JsonOutput data={data} valueTypes="json" />);
112
+ expect(container.textContent).toContain("true");
113
+ expect(container.textContent).toContain("false");
114
+ expect(container.textContent).toContain("null");
115
+ expect(container.textContent).not.toContain("True");
116
+ expect(container.textContent).toContain('"text/plain+int:2"');
117
+ rerender(<JsonOutput data={data} valueTypes="python" />);
118
+ expect(container.textContent).toContain("True");
119
+ expect(container.textContent).not.toContain("text/plain+int:2");
120
+ });
121
+
122
+ it.each(["python", "json"] as const)(
123
+ "copies marker-like user strings in %s mode",
124
+ async (valueTypes) => {
125
+ const data = {
126
+ marker: "\u0000show_more\u000042|$",
127
+ replacement: "<marimo-replace>None</marimo-replace>",
128
+ };
129
+ const { getAllByRole } = render(
130
+ <JsonOutput data={data} valueTypes={valueTypes} />,
131
+ );
132
+ expect(
133
+ getAllByRole("button", { name: "Copy to clipboard" }),
134
+ ).toHaveLength(3);
135
+ fireEvent.click(getAllByRole("button", { name: "Copy to clipboard" })[0]);
136
+ await waitFor(() =>
137
+ expect(copyToClipboard).toHaveBeenLastCalledWith(
138
+ JSON.stringify(data, null, 2),
139
+ ),
140
+ );
141
+ },
142
+ );
143
+
144
+ it("paginates arrays after expanding beyond the former depth cutoff", () => {
145
+ const data = {
146
+ a: {
147
+ b: {
148
+ c: {
149
+ d: { e: { f: { rows: Array.from({ length: 100 }, (_, i) => i) } } },
150
+ },
151
+ },
152
+ },
153
+ };
154
+ const { getByRole, getByText, queryByRole } = render(
155
+ <JsonOutput data={data} />,
156
+ );
157
+ for (const name of ["e", "f", "rows"]) {
158
+ fireEvent.click(getByRole("button", { name: `Expand ${name}` }));
159
+ }
160
+ expect(getByText("100 Items")).toBeInTheDocument();
161
+ fireEvent.click(getByRole("button", { name: "... 70 more items" }));
162
+ expect(
163
+ getByRole("button", { name: "... 40 more items" }),
164
+ ).toBeInTheDocument();
165
+ expect(
166
+ queryByRole("button", { name: "... 70 more items" }),
167
+ ).not.toBeInTheDocument();
168
+ });
169
+ });
170
+
171
+ describe("JsonOutput interaction regressions", () => {
172
+ it("removes the page control after the last page without collapsing the tree", () => {
173
+ const data = Array.from({ length: 31 }, (_, i) => i);
174
+ const { getByRole, queryByRole, getByText } = render(
175
+ <JsonOutput data={data} />,
176
+ );
177
+ fireEvent.click(getByRole("button", { name: "... 1 more items" }));
178
+ expect(
179
+ queryByRole("button", { name: /more items/ }),
180
+ ).not.toBeInTheDocument();
181
+ expect(getByRole("button", { name: "Collapse root" })).toBeInTheDocument();
182
+ expect(getByText("31 Items")).toBeInTheDocument();
183
+ });
184
+
185
+ it("does not reuse a previous dataset's page size", () => {
186
+ const matrix = Array.from({ length: 10 }, () =>
187
+ Array.from({ length: 60 }, (_, i) => i),
188
+ );
189
+ const { rerender, getByRole, queryByRole } = render(
190
+ <JsonOutput data={matrix} />,
191
+ );
192
+ const data = Array.from({ length: 70 }, (_, i) => i);
193
+ rerender(<JsonOutput data={data} />);
194
+ fireEvent.click(getByRole("button", { name: "... 40 more items" }));
195
+ expect(
196
+ getByRole("button", { name: "... 10 more items" }),
197
+ ).toBeInTheDocument();
198
+ expect(
199
+ queryByRole("button", { name: "... 35 more items" }),
200
+ ).not.toBeInTheDocument();
201
+ });
202
+
203
+ it("does not copy stale data or use the previous display mode", async () => {
204
+ const { rerender, getAllByRole, getByRole } = render(
205
+ <JsonOutput data={{ old: true }} />,
206
+ );
207
+ const data = { fresh: false, text: "text/plain:literal" };
208
+ rerender(<JsonOutput data={data} valueTypes="json" />);
209
+ fireEvent.click(getAllByRole("button", { name: "Copy to clipboard" })[0]);
210
+ await waitFor(() =>
211
+ expect(copyToClipboard).toHaveBeenLastCalledWith(
212
+ JSON.stringify(data, null, 2),
213
+ ),
214
+ );
215
+ expect(getByRole("button", { name: "Collapse root" })).toBeInTheDocument();
216
+ rerender(<JsonOutput data={data} valueTypes="python" />);
217
+ fireEvent.click(getAllByRole("button", { name: "Copy to clipboard" })[0]);
218
+ await waitFor(() =>
219
+ expect(copyToClipboard).toHaveBeenLastCalledWith(
220
+ '{\n "fresh": False,\n "text": "literal"\n}',
221
+ ),
222
+ );
223
+ });
224
+
225
+ it("does not retain decoded MIME text after switching to JSON", () => {
226
+ const data = {
227
+ tuple: "text/plain+tuple:[42]",
228
+ html: "text/html:<b>rich</b>",
229
+ };
230
+ const { container, rerender, getAllByRole } = render(
231
+ <JsonOutput data={data} />,
232
+ );
233
+ expect(container.querySelector("b")).toHaveTextContent("rich");
234
+ expect(getAllByRole("button", { name: "Copy to clipboard" })).toHaveLength(
235
+ 2,
236
+ );
237
+ rerender(<JsonOutput data={data} valueTypes="json" />);
238
+ expect(container.querySelector("b")).toBeNull();
239
+ expect(container.textContent).toContain('"tuple":"text/plain+tuple:[42]"');
240
+ expect(container.textContent).toContain('"html":"text/html:<b>rich</b>"');
241
+ expect(getAllByRole("button", { name: "Copy to clipboard" })).toHaveLength(
242
+ 3,
243
+ );
244
+ });
245
+
246
+ it("ignores unrelated keys on long-text controls", () => {
247
+ const text = "x".repeat(150);
248
+ const { getByRole } = render(<JsonOutput data={{ text }} />);
249
+ const button = getByRole("button", { name: `${"x".repeat(100)}...` });
250
+ fireEvent.keyDown(button, { key: "Escape" });
251
+ fireEvent.keyDown(button, { key: "ArrowDown" });
252
+ expect(button).toHaveAttribute("aria-expanded", "false");
253
+ });
254
+ });
@@ -1,27 +1,10 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
  import { render } from "@testing-library/react";
3
- import { beforeAll, describe, expect, it, vi } from "vitest";
3
+ import { describe, expect, it } from "vitest";
4
4
  import { JsonOutput } from "../JsonOutput";
5
5
 
6
- // Mock window.matchMedia for JsonViewer
7
- beforeAll(() => {
8
- Object.defineProperty(window, "matchMedia", {
9
- writable: true,
10
- value: vi.fn().mockImplementation((query) => ({
11
- matches: false,
12
- media: query,
13
- onchange: null,
14
- addListener: vi.fn(),
15
- removeListener: vi.fn(),
16
- addEventListener: vi.fn(),
17
- removeEventListener: vi.fn(),
18
- dispatchEvent: vi.fn(),
19
- })),
20
- });
21
- });
22
-
23
- describe("JsonOutput with enhanced mimetype handling", () => {
24
- it("should render data with various mimetypes without crashing", () => {
6
+ describe("JsonOutput MIME rendering", () => {
7
+ it("renders text, rich content, and Python collections", () => {
25
8
  const data = {
26
9
  text: "text/plain:Hello",
27
10
  html: "text/html:<strong>Bold</strong>",
@@ -35,16 +18,42 @@ describe("JsonOutput with enhanced mimetype handling", () => {
35
18
 
36
19
  const { container } = render(<JsonOutput data={data} format="auto" />);
37
20
 
38
- // Verify component renders without crashing
39
- expect(container).toBeInTheDocument();
40
- expect(container.querySelector(".marimo-json-output")).toBeInTheDocument();
21
+ expect(container.textContent).toContain('"text":Hello');
22
+ expect(container.querySelector("strong")).toHaveTextContent("Bold");
23
+ expect(container.querySelector("img")).toHaveAttribute(
24
+ "src",
25
+ "data:image/png;base64,xyz...",
26
+ );
27
+ expect(container.textContent).toContain('"set":{1, 2, 3}');
28
+ expect(container.textContent).toContain('"tuple":(10, 20)');
29
+ expect(container.textContent).toContain("application/custom:data");
30
+ expect(container.textContent).toContain('"boolean":True');
31
+ });
32
+
33
+ it("keeps MIME strings literal in JSON mode", () => {
34
+ const { container } = render(
35
+ <JsonOutput
36
+ data={{ value: "text/plain+tuple:[42]" }}
37
+ valueTypes="json"
38
+ />,
39
+ );
40
+ expect(container.textContent).toContain('"value":"text/plain+tuple:[42]"');
41
+ });
42
+
43
+ it("renders singleton tuples and escaped collection strings", () => {
44
+ const { container } = render(
45
+ <JsonOutput
46
+ data={{
47
+ tuple: "text/plain+tuple:[42]",
48
+ set: 'text/plain+set:["text/plain:literal"]',
49
+ }}
50
+ />,
51
+ );
52
+ expect(container.textContent).toContain('"tuple":(42,)');
53
+ expect(container.textContent).toContain('"set":{"text/plain:literal"}');
41
54
  });
42
55
 
43
56
  it("renders encoded non-string keys with Python-style affordances", () => {
44
- // Server-side `_key_formatter` encodes non-string dict keys with
45
- // mimetype prefixes; the frontend `keyRenderer` must decode them
46
- // so users see the original Python types (unquoted ints, parens for
47
- // tuples, etc.) instead of the raw encoded strings.
48
57
  const data = {
49
58
  "text/plain+int:2": "int_val",
50
59
  "text/plain+float:2.5": "float_val",
@@ -59,34 +68,23 @@ describe("JsonOutput with enhanced mimetype handling", () => {
59
68
  const { container } = render(<JsonOutput data={data} format="tree" />);
60
69
  const text = container.textContent ?? "";
61
70
 
62
- // `text/plain+str:` is the escape prefix — must never survive in output.
63
71
  expect(text).not.toContain("text/plain+str:");
64
- // Other encoded prefixes must not leak as-is. (They can still appear
65
- // inside the unescaped original string key `"text/plain+int:2"`,
66
- // which is intentional — but not for types *other* than int.)
67
72
  expect(text).not.toContain("text/plain+bool:True");
68
73
  expect(text).not.toContain("text/plain+tuple:[");
69
74
  expect(text).not.toContain("text/plain+frozenset:[");
70
75
  expect(text).not.toContain("text/plain+none:");
71
76
 
72
- // Decoded visual forms are present with Python-style affordances.
73
77
  expect(text).toContain('None:"none_val"');
74
78
  expect(text).toContain('True:"bool_val"');
75
79
  expect(text).toContain('2:"int_val"');
76
80
  expect(text).toContain('2.5:"float_val"');
77
81
  expect(text).toContain('(1, 2):"tuple_val"');
78
82
  expect(text).toContain('frozenset({3, 4}):"fs_val"');
79
- // Escaped str key renders as the original literal string (quoted).
80
83
  expect(text).toContain('"text/plain+int:2":"escaped_str_val"');
81
- // Plain string key unchanged.
82
84
  expect(text).toContain('"plain":"unchanged"');
83
85
  });
84
86
 
85
87
  it("renders 1-tuple and empty-frozenset keys with correct Python syntax", () => {
86
- // Regressions caught in review: `(1)` is not a tuple (needs `(1,)`),
87
- // and `frozenset({})` reads like constructing from an empty dict
88
- // (should be `frozenset()`). Locks in the tree-view rendering so these
89
- // don't slip back.
90
88
  const data = {
91
89
  "text/plain+tuple:[42]": "one_tuple",
92
90
  "text/plain+tuple:[]": "empty_tuple",
@@ -97,15 +95,13 @@ describe("JsonOutput with enhanced mimetype handling", () => {
97
95
  const { container } = render(<JsonOutput data={data} format="tree" />);
98
96
  const text = container.textContent ?? "";
99
97
 
100
- expect(text).toContain('(42,):"one_tuple"'); // trailing comma
98
+ expect(text).toContain('(42,):"one_tuple"');
101
99
  expect(text).toContain('():"empty_tuple"');
102
- expect(text).toContain('frozenset():"empty_fs"'); // not `frozenset({})`
100
+ expect(text).toContain('frozenset():"empty_fs"');
103
101
  expect(text).toContain('frozenset({1}):"one_fs"');
104
102
  });
105
103
 
106
104
  it("quotes integer-like string keys to distinguish them from int keys", () => {
107
- // Without this, `"2"` and the decoded int `2` look identical — the
108
- // textea viewer drops quotes from integer-like string keys by default.
109
105
  const data = {
110
106
  "2": "string_two",
111
107
  "text/plain+int:2": "int_two",
@@ -114,9 +110,8 @@ describe("JsonOutput with enhanced mimetype handling", () => {
114
110
  const { container } = render(<JsonOutput data={data} format="tree" />);
115
111
  const text = container.textContent ?? "";
116
112
 
117
- expect(text).toContain('"2":"string_two"'); // quoted
118
- expect(text).toContain('2:"int_two"'); // unquoted
119
- // Non-integer string keys still render without our intervention.
120
- expect(text).not.toContain("text/plain+"); // prefix stripped from int key
113
+ expect(text).toContain('"2":"string_two"');
114
+ expect(text).toContain('2:"int_two"');
115
+ expect(text).not.toContain("text/plain+");
121
116
  });
122
117
  });