@marimo-team/islands 0.24.3-dev9 → 0.25.1-dev1

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-pXXIvlr3.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-BC_yF09p.js +138 -0
  46. package/dist/{common-Cv-4hMa0.js → common-Dv227ygK.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-B-I2K7z3.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-D2hUXe09.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-B851uguN.js} +2143 -1910
  105. package/dist/{glide-utils-NShAPPtn.js → glide-utils-B3rUT5PI.js} +15 -15
  106. package/dist/globals-CmijFoIU.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-BtdoxWiD.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-DQq7NUGJ.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
@@ -1,656 +1,206 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
-
2
+ import JsonView from "@uiw/react-json-view";
3
3
  import {
4
- booleanType,
5
- type DataItemProps,
6
- type DataType,
7
- defineDataType,
8
- floatType,
9
- intType,
10
- JsonViewer,
11
- type JsonViewerKeyRenderer,
12
- nullType,
13
- objectType,
14
- stringType,
15
- } from "@textea/json-viewer";
16
- import { CheckIcon, CopyIcon } from "lucide-react";
17
- import { memo, useState } from "react";
18
- import type { OutputMessage } from "@/core/kernel/messages";
4
+ type ComponentProps,
5
+ memo,
6
+ useCallback,
7
+ useMemo,
8
+ useState,
9
+ } from "react";
10
+ import { useEvent } from "@/hooks/useEvent";
11
+ import { useTheme } from "@/theme/useTheme";
19
12
  import { cn } from "@/utils/cn";
20
- import { copyToClipboard } from "@/utils/copy";
21
- import { jsonParseWithSpecialChar } from "@/utils/json/json-parser";
22
- import { isUrl } from "@/utils/urls";
23
- import { useTheme } from "../../../theme/useTheme";
24
- import { logNever } from "../../../utils/assertNever";
25
- import { OutputRenderer } from "../Output";
26
- import { HtmlOutput } from "./HtmlOutput";
27
- import { ImageOutput } from "./ImageOutput";
28
- import { VideoOutput } from "./VideoOutput";
13
+ import { CopyClipboardIcon } from "../../icons/copy-icon";
14
+ import { getCopyValue } from "./json-output/formatting";
15
+ import {
16
+ determineMaxDisplayLength,
17
+ PAGE_SIZE,
18
+ paginateJson,
19
+ shouldExpandNode,
20
+ } from "./json-output/pagination";
21
+ import {
22
+ COLLAPSED_TEXT_LENGTH,
23
+ JSON_NULL_RENDER,
24
+ PYTHON_FALSE_RENDER,
25
+ PYTHON_NULL_RENDER,
26
+ PYTHON_TRUE_RENDER,
27
+ renderArrow,
28
+ renderKeyName,
29
+ renderKeyQuote,
30
+ renderStringValue,
31
+ } from "./json-output/renderers";
32
+ import { marimoDarkTheme, marimoLightTheme } from "./json-output/themes";
29
33
 
30
34
  interface Props {
31
- /**
32
- * The data to display
33
- */
34
35
  data: unknown;
35
-
36
36
  format?: "auto" | "tree" | "raw";
37
-
38
- /**
39
- * A text label for the JSON viewer. If `false`, no label is used.
40
- */
37
+ /** If false, omit the root label. */
41
38
  name?: string | false;
42
-
43
39
  className?: string;
44
-
45
- /**
46
- * The value types to use for the JSON viewer.
47
- */
40
+ /** Python mode decodes MIME leaves and uses True/False/None. */
48
41
  valueTypes?: "json" | "python";
49
42
  }
50
43
 
51
- // oxlint-disable-next-line typescript/no-explicit-any
52
- const CopyButton: React.FC<DataItemProps<any>> = ({ value }) => {
53
- const skipCopy =
54
- typeof value === "string" &&
55
- (value.startsWith("text/html:") ||
56
- value.startsWith("image/") ||
57
- value.startsWith("video/"));
58
- const [copied, setCopied] = useState(false);
59
-
60
- const handleCopy = async (evt: React.MouseEvent) => {
61
- evt.stopPropagation();
62
- await copyToClipboard(value);
63
- setCopied(true);
64
- setTimeout(() => setCopied(false), 1000);
65
- };
66
-
67
- if (skipCopy) {
68
- return null;
69
- }
70
- return (
71
- <button
72
- onClick={handleCopy}
73
- className={cn(
74
- "inline-flex ml-2 copy-button rounded w-6 h-3 justify-center items-center relative",
75
- )}
76
- aria-label="Copy to clipboard"
77
- type="button"
78
- >
79
- {copied ? (
80
- <CheckIcon className="w-5 h-5 absolute -top-0.5 p-1 hover:bg-muted rounded" />
81
- ) : (
82
- <CopyIcon className="w-5 h-5 absolute -top-0.5 p-1 hover:bg-muted rounded" />
83
- )}
84
- </button>
85
- );
86
- };
87
-
88
- // oxlint-disable-next-line typescript/no-explicit-any
89
- const JSONCopyButton: React.FC<DataItemProps<any>> = (props) => {
90
- // if
91
- return <CopyButton {...props} value={JSON.stringify(props.value, null, 2)} />;
92
- };
93
-
94
- // oxlint-disable-next-line typescript/no-explicit-any
95
- const PyCopyButton: React.FC<DataItemProps<any>> = (props) => {
96
- return <CopyButton {...props} value={getCopyValue(props.value)} />;
97
- };
98
-
99
- /**
100
- * Output component for JSON data.
101
- */
102
- export const JsonOutput: React.FC<Props> = memo(
103
- ({
104
- data,
105
- format = "auto",
106
- name = false,
107
- valueTypes = "python",
108
- className,
109
- }) => {
110
- const { theme } = useTheme();
111
- if (format === "auto") {
112
- format = inferBestFormat(data);
113
- }
114
-
115
- const valueTypesMap: Record<string, typeof PYTHON_VALUE_TYPES> = {
116
- python: PYTHON_VALUE_TYPES,
117
- json: JSON_VALUE_TYPES,
118
- };
119
-
120
- switch (format) {
121
- case "tree":
122
- return (
123
- <JsonViewer
124
- className={cn("marimo-json-output", className)}
125
- rootName={name}
126
- theme={theme}
127
- displayDataTypes={false}
128
- value={data}
129
- style={{
130
- backgroundColor: "transparent",
131
- }}
132
- collapseStringsAfterLength={COLLAPSED_TEXT_LENGTH}
133
- // leave the default valueTypes as it was - 'python', only 'json' is changed
134
- valueTypes={valueTypesMap[valueTypes]}
135
- // Render dict keys that carry Python type info (e.g. `int`, `tuple`).
136
- // See `_key_formatter` in marimo/_output/formatters/structures.py.
137
- keyRenderer={valueTypes === "python" ? keyRenderer : undefined}
138
- // Don't group arrays, it will make the tree view look like there are nested arrays
139
- groupArraysAfterLength={Number.MAX_SAFE_INTEGER}
140
- // Built-in clipboard shifts content on hover
141
- // so we provide our own copy button
142
- enableClipboard={false}
143
- // Improve perf for large arrays
144
- maxDisplayLength={determineMaxDisplayLength(data)}
145
- />
146
- );
147
-
148
- case "raw":
149
- return <pre className={className}>{JSON.stringify(data, null, 2)}</pre>;
150
- default:
151
- logNever(format);
152
- return <pre className={className}>{JSON.stringify(data, null, 2)}</pre>;
153
- }
154
- },
155
- );
156
- JsonOutput.displayName = "JsonOutput";
157
-
158
- function inferBestFormat(data: unknown): "tree" | "raw" {
159
- return typeof data === "object" && data !== null ? "tree" : "raw";
160
- }
161
-
162
- const COLLAPSED_TEXT_LENGTH = 100;
163
- // Text with length > COLLAPSED_TEXT_LENGTH is collapsed by default, and can be expanded by clicking on it.
164
- const CollapsibleTextOutput = (props: { text: string }) => {
165
- const [isCollapsed, setIsCollapsed] = useState(true);
166
-
167
- // Doesn't need to be collapsed
168
- if (props.text.length <= COLLAPSED_TEXT_LENGTH) {
169
- return <span className="break-all">{props.text}</span>;
170
- }
171
-
172
- if (isCollapsed) {
44
+ export const JsonOutput = memo(function JsonOutput({
45
+ data,
46
+ format = "auto",
47
+ ...props
48
+ }: Props) {
49
+ if (format === "raw" || data === null || typeof data !== "object") {
173
50
  return (
174
- <span
175
- className="cursor-pointer hover:opacity-90 break-all"
176
- onClick={() => setIsCollapsed(false)}
177
- >
178
- {props.text.slice(0, COLLAPSED_TEXT_LENGTH)}
179
- {props.text.length > COLLAPSED_TEXT_LENGTH && "..."}
180
- </span>
51
+ <pre className={props.className}>{JSON.stringify(data, null, 2)}</pre>
181
52
  );
182
53
  }
183
-
184
- return (
185
- <span
186
- className="cursor-pointer hover:opacity-90 break-all"
187
- onClick={() => setIsCollapsed(true)}
188
- >
189
- {props.text}
190
- </span>
191
- );
192
- };
193
-
194
- type LeafRenderer = (
195
- data: string,
196
- mimeType: OutputMessage["mimetype"],
197
- ) => React.ReactNode;
198
-
199
- /**
200
- * Map from mimetype-prefix to render function.
201
- *
202
- * Render function takes leaf data as input.
203
- */
204
- const LEAF_RENDERERS: Record<string, LeafRenderer> = {
205
- "image/": (value) => <ImageOutput src={value} />,
206
- "video/": (value) => <VideoOutput src={value} />,
207
- "text/html:": (value) => (
208
- <HtmlOutput html={value} inline={true} alwaysSanitizeHtml={false} />
209
- ),
210
- "text/markdown:": (value) => (
211
- <HtmlOutput html={value} inline={true} alwaysSanitizeHtml={true} />
212
- ),
213
- "text/plain+float:": (value) => <span>{value}</span>,
214
- "text/plain+bigint:": (value) => <span>{value}</span>,
215
- "text/plain+set:": (value) => <span>{formatSetPayload(value)}</span>,
216
- "text/plain+frozenset:": (value) => (
217
- <span>{formatFrozensetPayload(value)}</span>
218
- ),
219
- "text/plain+tuple:": (value) => <span>{value}</span>,
220
- "text/plain:": (value) => <CollapsibleTextOutput text={value} />,
221
- "application/json:": (value) => (
222
- <JsonOutput data={JSON.parse(value)} format="auto" />
223
- ),
224
- "application/": (value, mimeType) => {
225
- return (
226
- <OutputRenderer
227
- message={{
228
- channel: "output",
229
- data: value,
230
- mimetype: mimeType,
231
- }}
232
- // The fallback is just re-constructing the leaf and rendering it as a span
233
- // This could be the case where mime-type parsing is a false positive
234
- renderFallback={() => (
235
- <span>
236
- {mimeType}:{value}
237
- </span>
238
- )}
239
- />
240
- );
241
- },
242
- };
243
-
244
- // oxlint-disable-next-line typescript/no-explicit-any
245
- const MIME_TYPES: DataType<any>[] = Object.entries(LEAF_RENDERERS).map(
246
- ([leafType, render]) => ({
247
- is: (value) => typeof value === "string" && value.startsWith(leafType),
248
- PostComponent: PyCopyButton,
249
- Component: (props) => renderLeaf(props.value, render),
250
- }),
251
- );
252
-
253
- const PYTHON_BOOLEAN_TYPE = defineDataType<boolean>({
254
- ...booleanType,
255
- PostComponent: PyCopyButton,
256
- Component: ({ value }) => <span>{value ? "True" : "False"}</span>,
257
- });
258
-
259
- const PYTHON_NONE_TYPE = defineDataType<null>({
260
- ...nullType,
261
- PostComponent: PyCopyButton,
262
- Component: () => <span>None</span>,
263
- });
264
-
265
- const JSON_BOOLEAN_TYPE = defineDataType<boolean>({
266
- ...booleanType,
267
- PostComponent: JSONCopyButton,
268
- });
269
-
270
- const JSON_NONE_TYPE = defineDataType<null>({
271
- ...nullType,
272
- PostComponent: JSONCopyButton,
273
- Component: () => <span>null</span>,
54
+ return <JsonTree {...props} data={data} />;
274
55
  });
275
56
 
276
- const URL_TYPE = defineDataType<string>({
277
- ...stringType,
278
- is: (value) => isUrl(value),
279
- PostComponent: PyCopyButton,
280
- Component: ({ value }) => (
281
- <a
282
- href={value}
283
- target="_blank"
284
- rel="noopener noreferrer"
285
- className="text-link hover:underline"
286
- >
287
- {value}
288
- </a>
289
- ),
290
- });
291
-
292
- const INTEGER_TYPE = defineDataType<number>({
293
- ...intType,
294
- PostComponent: JSONCopyButton,
295
- });
296
-
297
- const FLOAT_TYPE = defineDataType<number>({
298
- ...floatType,
299
- PostComponent: JSONCopyButton,
300
- });
301
-
302
- const FALLBACK_RENDERER = defineDataType<string>({
303
- ...stringType,
304
- PostComponent: PyCopyButton,
305
- });
306
-
307
- const OBJECT_TYPE = defineDataType<object>({
308
- ...objectType,
309
- PreComponent: (props) => (
310
- <>
311
- {objectType.PreComponent && <objectType.PreComponent {...props} />}
312
- <PyCopyButton {...props} />
313
- </>
314
- ),
315
- });
316
-
317
- const JSON_OBJECT_TYPE = defineDataType<object>({
318
- ...objectType,
319
- PreComponent: (props) => (
320
- <>
321
- {objectType.PreComponent && <objectType.PreComponent {...props} />}
322
- <JSONCopyButton {...props} />
323
- </>
324
- ),
325
- });
326
-
327
- const JSON_FALLBACK_RENDERER = defineDataType<string>({
328
- ...stringType,
329
- PostComponent: JSONCopyButton,
330
- });
331
-
332
- const PYTHON_VALUE_TYPES = [
333
- INTEGER_TYPE,
334
- PYTHON_BOOLEAN_TYPE,
335
- PYTHON_NONE_TYPE,
336
- ...MIME_TYPES,
337
- URL_TYPE,
338
- OBJECT_TYPE,
339
- FALLBACK_RENDERER,
340
- ].toReversed();
341
- // Last one wins, so we reverse the array.
342
-
343
- const JSON_VALUE_TYPES = [
344
- INTEGER_TYPE,
345
- FLOAT_TYPE,
346
- JSON_BOOLEAN_TYPE,
347
- JSON_NONE_TYPE,
348
- JSON_OBJECT_TYPE,
349
- JSON_FALLBACK_RENDERER,
350
- ].toReversed();
351
-
352
- function leafData(leaf: string): string {
353
- return leafDataAndMimeType(leaf)[0];
354
- }
355
-
356
- function leafDataAndMimeType(
357
- leaf: string,
358
- ): [string, OutputMessage["mimetype"] | undefined] {
359
- const delimIndex = leaf.indexOf(":");
360
- if (delimIndex === -1) {
361
- return [leaf, undefined];
362
- }
363
- return [
364
- leaf.slice(delimIndex + 1),
365
- leaf.slice(0, delimIndex) as OutputMessage["mimetype"],
366
- ];
367
- }
368
-
369
- /**
370
- * Render a leaf.
371
- *
372
- * Leaf must have the format
373
- *
374
- * <mimetype>:<data>
375
- *
376
- * where mimetype cannot contain ":".
377
- */
378
- function renderLeaf(leaf: string, render: LeafRenderer): React.ReactNode {
379
- const [data, mimeType] = leafDataAndMimeType(leaf);
380
- if (mimeType) {
381
- return render(data, mimeType);
382
- }
383
- return <span>{leaf}</span>;
384
- }
385
-
386
- // Prefix marking keys that carry encoded type information from Python.
387
- // See `_key_formatter` in marimo/_output/formatters/structures.py.
388
- const KEY_ENCODED_PREFIX = "text/plain+";
389
-
390
- // Format elements for a Python collection literal. Non-finite floats
391
- // (NaN / Infinity / -Infinity) parse as JS `number` via
392
- // `jsonParseWithSpecialChar`; `JSON.stringify` on those returns `null`,
393
- // so render them as the same `float(...)` literals we use for scalar
394
- // float keys (see `decodeKeyForCopy`).
395
- function formatCollectionItems(items: unknown[]): string {
396
- return items
397
- .map((x) => {
398
- if (typeof x === "number" && !Number.isFinite(x)) {
399
- if (Number.isNaN(x)) {
400
- return "float('nan')";
401
- }
402
- return x > 0 ? "float('inf')" : "-float('inf')";
57
+ type TreeProps = Omit<Props, "data" | "format"> & { data: object };
58
+ type RowRenderer = NonNullable<
59
+ ComponentProps<typeof JsonView.Row<"div">>["render"]
60
+ >;
61
+ type CountRenderer = NonNullable<
62
+ ComponentProps<typeof JsonView.CountInfo<"span">>["render"]
63
+ >;
64
+
65
+ function JsonTree({
66
+ data,
67
+ name = false,
68
+ valueTypes = "python",
69
+ className,
70
+ }: TreeProps) {
71
+ const { theme } = useTheme();
72
+ const isPython = valueTypes === "python";
73
+ const pageSize = determineMaxDisplayLength(data) ?? PAGE_SIZE;
74
+ const [pagination, setPagination] = useState<{
75
+ data: object;
76
+ limits: Record<string, number>;
77
+ }>({ data, limits: {} });
78
+ if (pagination.data !== data) {
79
+ setPagination({ data, limits: {} });
80
+ }
81
+ const { limits } = pagination;
82
+ const tree = useMemo(
83
+ () => paginateJson(data, { limits, pageSize }),
84
+ [data, limits, pageSize],
85
+ );
86
+ const showMore = useEvent((path: string) => {
87
+ setPagination((prev) => ({
88
+ ...prev,
89
+ limits: {
90
+ ...prev.limits,
91
+ [path]: (prev.limits[path] ?? pageSize) + pageSize,
92
+ },
93
+ }));
94
+ });
95
+
96
+ // Render callbacks need current render state; useEvent reads committed state.
97
+ const renderCopyButton = useCallback(
98
+ (value: unknown) => {
99
+ if (
100
+ isPython &&
101
+ typeof value === "string" &&
102
+ /^(text\/html:|image\/|video\/)/.test(value)
103
+ ) {
104
+ return null;
403
105
  }
404
- return JSON.stringify(x);
405
- })
406
- .join(", ");
407
- }
408
-
409
- // Format a JSON-list payload as a Python tuple literal. 1-element tuples
410
- // need a trailing comma — `(1)` is just `1` in Python, `(1,)` is the tuple.
411
- // Uses `jsonParseWithSpecialChar` so bare `NaN`/`Infinity`/`-Infinity`
412
- // emitted by Python's json.dumps round-trip cleanly.
413
- function formatTuplePayload(jsonList: string): string {
414
- const items = jsonParseWithSpecialChar<unknown[]>(jsonList);
415
- // `jsonParseWithSpecialChar` returns `{}` when both parse passes fail;
416
- // fall back to the raw payload so a malformed wire form doesn't crash
417
- // rendering/copy. Matches the defensive pattern in `formatSetPayload`.
418
- if (!Array.isArray(items)) {
419
- return jsonList;
420
- }
421
- if (items.length === 0) {
422
- return "()";
423
- }
424
- const inner = formatCollectionItems(items);
425
- if (items.length === 1) {
426
- return `(${inner},)`;
427
- }
428
- return `(${inner})`;
429
- }
430
-
431
- // Format a JSON-list payload as a Python frozenset literal. Empty → `frozenset()`
432
- // rather than `frozenset({})` (which reads like a dict).
433
- function formatFrozensetPayload(jsonList: string): string {
434
- const items = jsonParseWithSpecialChar<unknown[]>(jsonList);
435
- if (!Array.isArray(items)) {
436
- return jsonList;
437
- }
438
- if (items.length === 0) {
439
- return "frozenset()";
440
- }
441
- const inner = formatCollectionItems(items);
442
- return `frozenset({${inner}})`;
443
- }
444
-
445
- // Format a JSON-list payload as a Python set literal. Empty → `set()`
446
- // (not `{}`, which is a dict literal in Python).
447
- function formatSetPayload(jsonList: string): string {
448
- const items = jsonParseWithSpecialChar<unknown[]>(jsonList);
449
- if (!Array.isArray(items)) {
450
- // Back-compat: older wire form was `text/plain+set:{1, 2, 3}` (Python
451
- // set-literal string, not JSON). Pass it through as-is rather than crash.
452
- return jsonList;
453
- }
454
- if (items.length === 0) {
455
- return "set()";
456
- }
457
- const inner = formatCollectionItems(items);
458
- return `{${inner}}`;
459
- }
460
-
461
- // Renderers for decoded non-string keys. Visual affordances match Python:
462
- // unquoted primitives, parens for tuple, `frozenset({...})` for frozenset,
463
- // and the `text/plain+str:` escape re-quotes the original string.
464
- const KEY_DECODERS: Record<string, (data: string) => React.ReactNode> = {
465
- "text/plain+int:": (v) => <span>{v}</span>,
466
- "text/plain+float:": (v) => <span>{v}</span>,
467
- "text/plain+bool:": (v) => <span>{v === "True" ? "True" : "False"}</span>,
468
- "text/plain+none:": () => <span>None</span>,
469
- "text/plain+tuple:": (v) => <span>{formatTuplePayload(v)}</span>,
470
- "text/plain+frozenset:": (v) => <span>{formatFrozensetPayload(v)}</span>,
471
- "text/plain+str:": (v) => <span>"{v}"</span>,
472
- };
473
-
474
- function isEncodedKey(key: unknown): key is string {
475
- return typeof key === "string" && key.startsWith(KEY_ENCODED_PREFIX);
476
- }
477
-
478
- // `@textea/json-viewer` drops quotes from integer-like string keys, which
479
- // makes the string `"2"` visually identical to the decoded int `2`. Match
480
- // the same keys the viewer strips and render them with explicit quotes.
481
- const INT_LIKE_STRING = /^-?\d+$/;
482
-
483
- const keyRenderer: JsonViewerKeyRenderer = Object.assign(
484
- ({ path }: DataItemProps) => {
485
- const key = path[path.length - 1];
486
- if (typeof key !== "string") {
487
- return <span>{String(key)}</span>;
488
- }
489
- if (isEncodedKey(key)) {
490
- const [data, mimeType] = leafDataAndMimeType(key);
491
- const render = KEY_DECODERS[`${mimeType}:`];
492
- return render ? render(data) : <span>{key}</span>;
493
- }
494
- // Plain integer-like string — quote it so it's distinct from a decoded int.
495
- return <span>"{key}"</span>;
496
- },
497
- {
498
- when: ({ path }: DataItemProps) => {
499
- const key = path[path.length - 1];
500
106
  return (
501
- isEncodedKey(key) ||
502
- (typeof key === "string" && INT_LIKE_STRING.test(key))
107
+ <CopyClipboardIcon
108
+ value={() =>
109
+ isPython
110
+ ? getCopyValue(tree.getOriginal(value))
111
+ : JSON.stringify(tree.getOriginal(value), null, 2)
112
+ }
113
+ tooltip={false}
114
+ buttonClassName="inline-flex ml-2 copy-button rounded w-6 h-3 justify-center items-center relative"
115
+ className="w-5 h-5 absolute -top-0.5 p-1 hover:bg-muted rounded"
116
+ />
503
117
  );
504
118
  },
505
- },
506
- );
507
-
508
- const MIME_PREFIXES = Object.keys(LEAF_RENDERERS);
509
- const REPLACE_PREFIX = "<marimo-replace>";
510
- const REPLACE_SUFFIX = "</marimo-replace>";
511
- /**
512
- * Get the string representation (as Python) of a value.
513
- * - recursively handles lists and dictionaries
514
- * - trims mimetype prefix
515
- * - maps booleans to True and False
516
- * - maps null/undefined to None
517
- */
518
- function pythonJsonReplacer(_key: string, value: unknown): unknown {
519
- if (value == null) {
520
- return `${REPLACE_PREFIX}None${REPLACE_SUFFIX}`;
521
- }
522
- if (typeof value === "object") {
523
- return value;
524
- }
525
- if (typeof value === "bigint") {
526
- return `${REPLACE_PREFIX}${value}${REPLACE_SUFFIX}`;
527
- }
528
- if (Array.isArray(value)) {
529
- return value;
530
- }
531
- if (typeof value === "string") {
532
- // If float, we want to keep the quotes around the number.
533
- if (value.startsWith("text/plain+float:")) {
534
- return `${REPLACE_PREFIX}${leafData(value)}${REPLACE_SUFFIX}`;
535
- }
536
- if (value.startsWith("text/plain+bigint:")) {
537
- // Use BigInt to avoid precision loss
538
- const number = BigInt(leafData(value));
539
- return `${REPLACE_PREFIX}${number}${REPLACE_SUFFIX}`;
540
- }
541
- if (value.startsWith("text/plain+tuple:")) {
542
- // replace first and last characters [] with ()
543
- return `${REPLACE_PREFIX}(${leafData(value).slice(1, -1)})${REPLACE_SUFFIX}`;
544
- }
545
- if (value.startsWith("text/plain+set:")) {
546
- return `${REPLACE_PREFIX}${formatSetPayload(leafData(value))}${REPLACE_SUFFIX}`;
547
- }
548
- if (value.startsWith("text/plain+frozenset:")) {
549
- return `${REPLACE_PREFIX}${formatFrozensetPayload(leafData(value))}${REPLACE_SUFFIX}`;
550
- }
551
-
552
- if (MIME_PREFIXES.some((prefix) => value.startsWith(prefix))) {
553
- return leafData(value);
554
- }
555
- return value;
556
- }
557
- if (typeof value === "boolean") {
558
- return `${REPLACE_PREFIX}${value ? "True" : "False"}${REPLACE_SUFFIX}`;
559
- }
560
- return value;
561
- }
562
-
563
- // Rewrite an encoded key string into the Python literal that should appear
564
- // unquoted in the copy output. Wrapping in REPLACE_PREFIX/SUFFIX makes the
565
- // final regex pass strip the surrounding JSON quotes.
566
- function decodeKeyForCopy(key: string): string {
567
- const [data, mimeType] = leafDataAndMimeType(key);
568
- const wrap = (s: string) => `${REPLACE_PREFIX}${s}${REPLACE_SUFFIX}`;
569
- switch (`${mimeType}:`) {
570
- case "text/plain+int:":
571
- return wrap(data);
572
- case "text/plain+float:":
573
- if (data === "nan") {
574
- return wrap("float('nan')");
575
- }
576
- if (data === "inf") {
577
- return wrap("float('inf')");
578
- }
579
- if (data === "-inf") {
580
- return wrap("-float('inf')");
581
- }
582
- return wrap(data);
583
- case "text/plain+bool:":
584
- return wrap(data === "True" ? "True" : "False");
585
- case "text/plain+none:":
586
- return wrap("None");
587
- case "text/plain+tuple:":
588
- return wrap(formatTuplePayload(data));
589
- case "text/plain+frozenset:":
590
- return wrap(formatFrozensetPayload(data));
591
- case "text/plain+str:":
592
- // `data` is the original Python string; it stays quoted.
593
- return data;
594
- default:
595
- return key;
596
- }
597
- }
598
-
599
- function rewriteEncodedKeys(value: unknown): unknown {
600
- if (Array.isArray(value)) {
601
- return value.map(rewriteEncodedKeys);
602
- }
603
- if (typeof value === "object" && value !== null) {
604
- const out: Record<string, unknown> = {};
605
- for (const [k, v] of Object.entries(value)) {
606
- const newKey = isEncodedKey(k) ? decodeKeyForCopy(k) : k;
607
- out[newKey] = rewriteEncodedKeys(v);
608
- }
609
- return out;
610
- }
611
- return value;
612
- }
613
-
614
- export function getCopyValue(value: unknown): string {
615
- // Because this results in valid json, it adds quotes around None and True/False.
616
- // but we want to make this look like Python, so we remove the quotes.
617
- return JSON.stringify(rewriteEncodedKeys(value), pythonJsonReplacer, 2)
618
- .replaceAll(`"${REPLACE_PREFIX}`, "")
619
- .replaceAll(`${REPLACE_SUFFIX}"`, "");
620
- }
621
-
622
- /**
623
- * Determine the max display length for a given data.
624
- * - For 3D arrays, we return 5
625
- * - For 2D arrays, return undefined <= 20 items, 10 >= 20 items, 5 >= 50 items
626
- * - For 1D arrays and other types, we return undefined
627
- *
628
- * @param data - The data to determine the max display length for.
629
- * @returns The max display length, or undefined to use the default.
630
- */
631
- export function determineMaxDisplayLength(data: unknown): number | undefined {
632
- if (Array.isArray(data)) {
633
- const sampleElements = data.slice(0, 15);
634
-
635
- let maxLength = 0;
636
- for (const element of sampleElements) {
637
- if (Array.isArray(element)) {
638
- // Check for 3D arrays and return early
639
- const nextSample = element.slice(0, 5);
640
- for (const nextElement of nextSample) {
641
- if (Array.isArray(nextElement)) {
642
- return 5;
643
- }
644
- }
645
-
646
- maxLength = Math.max(maxLength, element.length);
647
- }
648
- }
119
+ [isPython, tree],
120
+ );
649
121
 
650
- if (maxLength <= 20) {
651
- return undefined;
652
- }
122
+ const renderString = useCallback(
123
+ (
124
+ props: Record<string, unknown>,
125
+ result: { type: string; value?: unknown },
126
+ ) => renderStringValue(props, result, valueTypes),
127
+ [valueTypes],
128
+ );
129
+ const renderRow = useCallback<RowRenderer>(
130
+ (props, { value }) => {
131
+ const page = tree.getPage(value);
132
+ return (
133
+ <div {...props}>
134
+ {page ? (
135
+ <button
136
+ type="button"
137
+ className="cursor-pointer text-link hover:underline"
138
+ onClick={(event) => {
139
+ event.stopPropagation();
140
+ showMore(page.path);
141
+ }}
142
+ >
143
+ ... {page.remaining} more items
144
+ </button>
145
+ ) : (
146
+ <>
147
+ {props.children}
148
+ {renderCopyButton(value)}
149
+ </>
150
+ )}
151
+ </div>
152
+ );
153
+ },
154
+ [tree, showMore, renderCopyButton],
155
+ );
156
+ const renderCount = useCallback<CountRenderer>(
157
+ (props, { value }) => {
158
+ const original = tree.getOriginal(value);
159
+ const count = Array.isArray(original)
160
+ ? original.length
161
+ : original && typeof original === "object"
162
+ ? Object.keys(original).length
163
+ : 0;
164
+ return (
165
+ <span {...props}>
166
+ {count} {count === 1 ? "Item" : "Items"}
167
+ </span>
168
+ );
169
+ },
170
+ [tree],
171
+ );
172
+ const renderContainerCopy = useCallback(
173
+ (_props: Record<string, unknown>, { value }: { value?: unknown }) =>
174
+ renderCopyButton(value),
175
+ [renderCopyButton],
176
+ );
653
177
 
654
- return maxLength >= 50 ? 5 : 10;
655
- }
178
+ return (
179
+ <JsonView
180
+ className={cn("marimo-json-output", className)}
181
+ keyName={name || undefined}
182
+ value={tree.data}
183
+ style={theme === "dark" ? marimoDarkTheme : marimoLightTheme}
184
+ displayDataTypes={false}
185
+ enableClipboard={false}
186
+ highlightUpdates={false}
187
+ shortenTextAfterLength={COLLAPSED_TEXT_LENGTH}
188
+ collapsed={false}
189
+ shouldExpandNodeInitially={shouldExpandNode}
190
+ >
191
+ <JsonView.String render={renderString} />
192
+ <JsonView.Colon style={{ marginRight: 4 }} />
193
+ <JsonView.Arrow render={renderArrow} />
194
+ <JsonView.CountInfo<"span"> render={renderCount} />
195
+ <JsonView.KeyName render={isPython ? renderKeyName : undefined} />
196
+ <JsonView.Quote render={isPython ? renderKeyQuote : undefined} />
197
+ <JsonView.True render={isPython ? PYTHON_TRUE_RENDER : undefined} />
198
+ <JsonView.False render={isPython ? PYTHON_FALSE_RENDER : undefined} />
199
+ <JsonView.Null
200
+ render={isPython ? PYTHON_NULL_RENDER : JSON_NULL_RENDER}
201
+ />
202
+ <JsonView.Row<"div"> render={renderRow} />
203
+ <JsonView.CountInfoExtra render={renderContainerCopy} />
204
+ </JsonView>
205
+ );
656
206
  }