@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.
- package/dist/{ConnectedDataExplorerComponent-C9OF_75w.js → ConnectedDataExplorerComponent-KVhjDMSe.js} +1066 -1066
- package/dist/ErrorBoundary-DnNkbB_U.js +200 -0
- package/dist/{ImageComparisonComponent-2a9TMMm2.js → ImageComparisonComponent-Dlm5aWnH.js} +4 -4
- package/dist/{Plot-C-9MQeGP.js → Plot-CTyjbQDT.js} +21032 -20666
- package/dist/_baseMerge-BiGRwuqv.js +165 -0
- package/dist/{abnfDiagram-N423BO3Z-CQKwzX8t.js → abnfDiagram-VCTEODGH-CpN0FRda.js} +17 -18
- package/dist/{any-language-editor-DmQ7dyCx.js → any-language-editor-CycIVIeP.js} +17 -17
- package/dist/arc-7ra3iNdN.js +81 -0
- package/dist/architecture-7GRP2DOG-BWLylgiA.js +3 -0
- package/dist/{architectureDiagram-T3A2C74G-CncrDpfa.js → architectureDiagram-5GKGNRK7-DDyj5SRB.js} +25 -25
- package/dist/assets/__vite-browser-external-C9iR87Zl.js +1 -0
- package/dist/assets/worker-D7hWi02M.js +109 -0
- package/dist/blockDiagram-I7D4REHJ-BbXEXJRu.js +1922 -0
- package/dist/{button-CKBkqR9J.js → button-DdXAm8iz.js} +5 -5
- package/dist/{c4Diagram-5PPSVZJV-DarUk3Yq.js → c4Diagram-7LVT6UL2-DcLsbQmR.js} +696 -668
- package/dist/{capabilities-CMBBJTcc.js → capabilities-B3ce_GNu.js} +1 -1
- package/dist/{html-to-image-BtFMe_Nq.js → cells-DYIkSxs6.js} +6757 -10137
- package/dist/{channel-CyiYUzJv.js → channel-B5XnTarr.js} +1 -1
- package/dist/chat-ui-BXj3uYjy.js +5786 -0
- package/dist/{check-CHig47QV.js → check-B7cIK6gb.js} +1 -1
- package/dist/{chunk-2Q5K7J3B-BMuJl2jB.js → chunk-2Q5K7J3B-BMkUkeuy.js} +2 -2
- package/dist/chunk-4HAMMTFA-D2qv8E0X.js +4188 -0
- package/dist/{chunk-5VM5RSS4-Bcq0ftdy.js → chunk-5VM5RSS4-DKsmPCL6.js} +2 -2
- package/dist/{chunk-NSK5VX7P-CDTMcJB0.js → chunk-75Z2AOVW-5_HsvXwr.js} +23 -23
- package/dist/{chunk-I66GZJ75-BqyTCAPf.js → chunk-DU6HZSFF-BUgAHvnM.js} +21 -4
- package/dist/{chunk-2GRJ4B5K-DgO8Qudq.js → chunk-F27PBJKO-DHmYWZXr.js} +4 -4
- package/dist/{chunk-KEIR6QF5-C4KX3XYh.js → chunk-FOHPRMQF-BpYR8RqP.js} +4 -4
- package/dist/{chunk-WRU74C26-BpWsbiDK.js → chunk-GMAD6QVW-Czynz8aA.js} +486 -258
- package/dist/chunk-GVQU2GXP-yQEdulRS.js +37 -0
- package/dist/{chunk-5RXB4S5H-tLNUVDdO.js → chunk-IMKFNOWR-BfwTebD0.js} +11 -11
- package/dist/{chunk-JWPE2WC7-BJwjGUnF.js → chunk-JWPE2WC7-DJvv1t8U.js} +2 -2
- package/dist/chunk-L3NEJ4N5-DwRWnDDK.js +245 -0
- package/dist/{chunk-7Z6QIM7H-Dc18hFxH.js → chunk-OSK3NFVY-BBKU_Ign.js} +222 -144
- package/dist/{chunk-W5SLKNZC-DLnvpEtV.js → chunk-P2QGCYS3-C0iGU29N.js} +20 -20
- package/dist/{chunk-KBJHAD2P-BuqWqkcW.js → chunk-POPQ4Y6H-BCbS3Ozc.js} +4 -4
- package/dist/{chunk-4I5QYGJK-DpytHuZQ.js → chunk-PWAF6VOD-CTFCL0jI.js} +4 -4
- package/dist/{chunk-JQJVKLGR-CgqCwe8E.js → chunk-SHT3W25Y-BspJEJ5H.js} +109 -63
- package/dist/{chunk-6Q2QTUOP-DmTcEBMn.js → chunk-SVP7TREG-DX3rFNTe.js} +5 -5
- package/dist/{chunk-GF5L2VYU-B0wgCQgS.js → chunk-TICWLB2K-fVF7Uujg.js} +13 -13
- package/dist/{chunk-XXDRQBXY-D73H4IN0.js → chunk-XXDRQBXY-Dnl5VBqG.js} +3 -3
- package/dist/{chunk-Y2CYZVJY-DoegUj_e.js → chunk-Y2CYZVJY-DKKS1VyV.js} +1 -1
- package/dist/{chunk-5FQGJX7Z-B85XCPv6.js → chunk-YOKDWASO-DrWbKnxA.js} +6925 -5460
- package/dist/classDiagram-ZZMXUADV-Fe-V14Sc.js +30 -0
- package/dist/classDiagram-v2-VYDZK3BY-xTDg7lOs.js +30 -0
- package/dist/code-visibility-DElzk_hh.js +138 -0
- package/dist/{common-Cv-4hMa0.js → common-B4-ih9IA.js} +8838 -12645
- package/dist/{compiler-runtime-CAH9RLw2.js → compiler-runtime-Cj8ZWiNS.js} +2 -2
- package/dist/copy-icon-CLdENY__.js +190 -0
- package/dist/{cose-bilkent-JH36ORCC-FUWHkXFX.js → cose-bilkent-JH36ORCC-_LyRKVqm.js} +2 -2
- package/dist/cubic-OaEBhQPK.js +1404 -0
- package/dist/cynefin-OW5HDTMX-obq-i80l.js +3 -0
- package/dist/{cynefinDiagram-MW4NZA55-BXSFK93J.js → cynefinDiagram-5FMLGOSQ-CFQrqzSX.js} +10 -10
- package/dist/{dagre-CGyalH38.js → dagre-BGSfXNEb.js} +678 -537
- package/dist/dagre-GXQ25YYZ-xHNAUTyG.js +425 -0
- package/dist/{data-grid-overlay-editor-DZM4BAaI.js → data-grid-overlay-editor-CRH6JQPE.js} +8 -9
- package/dist/{data-grid-types-BnZwWG8z.js → data-grid-types-BucVmDDp.js} +1 -1
- package/dist/{defaultLocale-DKaU_opG.js → defaultLocale-JI_lnoCe.js} +1 -1
- package/dist/{diagram-7IWD3JNH-BjfzDNvj.js → diagram-S7CK7UJ4-Bt1rPbAx.js} +40 -39
- package/dist/{diagram-UB23O5K3-DDg-7wLA.js → diagram-UQ7AKVKN-D1sq5FoZ.js} +10 -10
- package/dist/{diagram-B4RE2ZJO-CKccR9eE.js → diagram-VSXAHHWV-BKpmG23x.js} +10 -10
- package/dist/{diagram-Q27KOJAE-Zhz01ss7.js → diagram-VX7I27RA-BMwGFVD6.js} +21 -21
- package/dist/{diagram-LBJQPF4R-CJ-bOruh.js → diagram-Z3DM3KII--KLDR9Pl.js} +10 -10
- package/dist/dialog-CgNq6y9-.js +94 -0
- package/dist/{dist-dAqZAPcF.js → dist-1HDa70oE.js} +3 -3
- package/dist/{dist-Re9sOPOx.js → dist-9x1ZRe-e.js} +2 -2
- package/dist/{dist-DEeLqRlU.js → dist-B1vYLK28.js} +1 -1
- package/dist/{dist-C5Bn52W4.js → dist-B8D7McG3.js} +3 -3
- package/dist/{dist-_XvgytiS.js → dist-BHCHt1NI.js} +1 -1
- package/dist/{dist-CRjAdnqF.js → dist-BRxfaJgs.js} +3 -3
- package/dist/{dist-Byj2bV8o.js → dist-BVGtK7Ht.js} +1 -1
- package/dist/{dist-BIN5LKyE.js → dist-BaHjhkpZ.js} +2 -2
- package/dist/{dist-DVFbIwc5.js → dist-BtKIs9N-.js} +1 -1
- package/dist/{dist-BrQGcfQ9.js → dist-C2KzV-vf.js} +2 -2
- package/dist/{dist-DJFiGP2k.js → dist-C2wDlYdD.js} +2 -2
- package/dist/{dist-BVRM2y1P.js → dist-C8xMLVYi.js} +2 -2
- package/dist/dist-CMh_W4y4.js +538 -0
- package/dist/{extends-BPB6_lTK.js → dist-CO1AwwG_.js} +516 -561
- package/dist/{dist-BzN60Z2s.js → dist-CPGxfVGX.js} +1 -1
- package/dist/{dist-C7v-9nUK.js → dist-Cm3iAFMD.js} +3 -3
- package/dist/{dist-CGAxEiaM.js → dist-Cs2FijYk.js} +2 -2
- package/dist/{dist-hXd65f0_.js → dist-D1PpnuZB.js} +1 -1
- package/dist/{dist-VdvbxG72.js → dist-DHOTQar0.js} +1 -1
- package/dist/{dist-DcgzXCfy.js → dist-DPcEtqmG.js} +2 -2
- package/dist/{dist-D4Z_qdP4.js → dist-Df0wKI0F.js} +1 -1
- package/dist/{dist-qhLI-SHd.js → dist-DszxuxS1.js} +2 -2
- package/dist/{dist-BEmkrGoQ.js → dist-Kxgjxakw.js} +1 -1
- package/dist/{dist-BbSMhhD2.js → dist-KzZ7WELx.js} +2 -2
- package/dist/{dist-jWUab2d1.js → dist-lA1Cttkb.js} +1 -1
- package/dist/dropdown-menu-CZgjYSAh.js +4502 -0
- package/dist/{ebnfDiagram-BXEA7PRR-Dm75pCAS.js → ebnfDiagram-PWID7BFC-WnG4cvSQ.js} +17 -18
- package/dist/{erDiagram-JOGREHBK-rVDG3VSQ.js → erDiagram-RLTQ6QDP-DhzKiYHc.js} +1037 -798
- package/dist/error-banner-B94dcDmP.js +391 -0
- package/dist/error-entries-C0NsUqXl.js +56 -0
- package/dist/{esm-CR1nhb1c.js → esm-BElI3qKH.js} +22 -22
- package/dist/{esm-C1d0g6Bv.js → esm-G4Fhsgng.js} +51 -37
- package/dist/{esm-CesmlDrs.js → esm-h8f7-1CJ.js} +423 -423
- package/dist/eventmodeling-NTZA5JFV-D7nP7Uyu.js +3 -0
- package/dist/feedback-button-cQcMqpEP.js +380 -0
- package/dist/flowDiagram-HODETNUW-6dMdB8pR.js +16 -0
- package/dist/{formats-CslNOKy0.js → formats-BR5iGb4s.js} +8 -8
- package/dist/{ganttDiagram-PKOTCBZU-9D-LXF6P.js → ganttDiagram-EL5Y4UJY-DbzEBLbl.js} +15 -15
- package/dist/gitGraph-4MIJSDKK-Dy9uQXGh.js +3 -0
- package/dist/{gitGraphDiagram-DS77QQ5N-BfpsXTLf.js → gitGraphDiagram-WWUBYQGX-BMf0iKi1.js} +10 -10
- package/dist/{glide-data-editor-sPZ8fUFn.js → glide-data-editor-DYOxfSzI.js} +2143 -1910
- package/dist/{glide-utils-NShAPPtn.js → glide-utils-B3rUT5PI.js} +15 -15
- package/dist/globals-BOoLHa7X.js +19 -0
- package/dist/highlighted-body-KPVGNVTW-Ad3MKZ18.js +38 -0
- package/dist/html-to-image-CeYYvQNb.js +3272 -0
- package/dist/info-A6RAGUB7-DKoayiHY.js +3 -0
- package/dist/{infoDiagram-6WML65LV-DqCTXHVy.js → infoDiagram-27XIBGKW-BagtCX_5.js} +7 -7
- package/dist/{ishikawaDiagram-WSZJBQD7-BCUYmZ1v.js → ishikawaDiagram-5VMMS53U-DByDehYy.js} +8 -8
- package/dist/{journeyDiagram-NVQOT4AX-BXB6M4zN.js → journeyDiagram-3NMN7TZE-D2LcKDUO.js} +8 -8
- package/dist/{jsx-runtime-CpoKNRgu.js → jsx-runtime-DgDPJV9S.js} +1 -1
- package/dist/{kanban-definition-27J2QSJJ-CGRhrDUX.js → kanban-definition-UXKFOSKX-BxwkbBI_.js} +29 -29
- package/dist/katex-DFBiSucW.js +21840 -0
- package/dist/{label-lwzzBHUW.js → label-BJ49YTvk.js} +6 -6
- package/dist/{linear-D5r_1YqD.js → linear-Cnaq3Tfb.js} +4 -4
- package/dist/{loader-VZ__bv4g.js → loader-D2W4oM-3.js} +15 -15
- package/dist/main.js +8113 -13475
- package/dist/mermaid-DPNuhemn.js +3084 -0
- package/dist/mermaid-HWGCJPDP-DYCzSILp.js +2 -0
- package/dist/mermaid-parser.core-DxuSfYwJ.js +638 -0
- package/dist/{mhchem-BjwrxcV-.js → mhchem-DWCF06w9.js} +1 -1
- package/dist/{micromark-factory-space-DruVGzeG.js → micromark-factory-space-DGxAzmw8.js} +2 -2
- package/dist/{mindmap-definition-FAOFIHXS-C9gRhzZq.js → mindmap-definition-YA3MSWOX-Cgvojw3y.js} +17 -17
- package/dist/{node-sql-parser-DesT1xNw.js → node-sql-parser-BiZOJ-FS.js} +0 -3
- package/dist/{number-overlay-editor-De2CabLL.js → number-overlay-editor-DZp5bEmI.js} +5 -5
- package/dist/objectWithoutPropertiesLoose-BRDcTsfB.js +23 -0
- package/dist/packet-AYTQ26CC-Yw7ajchJ.js +3 -0
- package/dist/{pegDiagram-VL7TDLO6-Df9IUjMD.js → pegDiagram-XKGWAZYB-DahHhYyN.js} +19 -20
- package/dist/{pick-SnHlmIBN.js → pick-CJeoxYOS.js} +1 -1
- package/dist/pie-WAS4IAKB-BKdKOAkL.js +3 -0
- package/dist/{pieDiagram-7S7Q4E2Y-BmRzQ8ZP.js → pieDiagram-E7YTZNPT-CCtX0oCK.js} +29 -31
- package/dist/{process-output-CPy0YKUe.js → process-output-BCTjlyuH.js} +7 -6
- package/dist/purify.es-DgKyQviY.js +805 -0
- package/dist/{quadrantDiagram-CIZ2JOQS-wFBvWcxS.js → quadrantDiagram-AXDQQJYC-DftINo6g.js} +5 -5
- package/dist/radar-RG4KPBEZ-ScR_D_Ts.js +3 -0
- package/dist/railroad-74A4TZTK-DQvPSdo5.js +3 -0
- package/dist/railroad-abnf-HS5TGJTU-CsJQG2O8.js +3 -0
- package/dist/railroad-ebnf-LZEXJU2U-BgEobkSn.js +3 -0
- package/dist/railroad-peg-WCYAUIDC-DjLAjofI.js +3 -0
- package/dist/{railroadDiagram-AXF67PYL-DxbIatiN.js → railroadDiagram-O6MQD6OU-Du5NsPUb.js} +17 -18
- package/dist/{react-dom-C94wmq5w.js → react-dom-BUgJ-ab_.js} +3 -3
- package/dist/{react-DMWcHin-.js → react-ofJbxM4x.js} +2 -2
- package/dist/{react-vega-DSD5aT7_.js → react-vega-5AcMW8gG.js} +10012 -9369
- package/dist/{requirementDiagram-LRYGKXZP-X_x-Y713.js → requirementDiagram-BXWQKSXE-0pmmxL6_.js} +20 -19
- package/dist/{reveal-component-CVsYlb9J.js → reveal-component-C9h2Lubu.js} +1303 -1237
- package/dist/{sankeyDiagram-W5VNT64P-BYJALbbm.js → sankeyDiagram-P5KCCOFB-CDKR5TJH.js} +4 -4
- package/dist/{select-3qHpFKF9.js → select-C6plhmZr.js} +317 -562
- package/dist/{sequenceDiagram-SI44F4Z6-BiPBe3Sv.js → sequenceDiagram-WJ2MYXX4-DB-ChWwU.js} +11 -11
- package/dist/{sizeCapture-X5ZJPWSS-BKLDeh5H.js → sizeCapture-INFHLROL-Dx5SpkQF.js} +2 -2
- package/dist/{spec-CutWdqo3.js → spec-CAYwFeV6.js} +4 -4
- package/dist/{src-C31ngt98.js → src-CA_m4ONl.js} +298 -303
- package/dist/{stateDiagram-OKZ733FA-B7QyNBxH.js → stateDiagram-D77RDMKH-orN6AwMx.js} +19 -20
- package/dist/stateDiagram-v2-MP3YSRHH-o05_rJN8.js +30 -0
- package/dist/style.css +2 -2
- package/dist/{swimlanes-SLNWSIFB-B4f8VP3b.js → swimlanes-42K2YHIH-BkSDcqDT.js} +1501 -1591
- package/dist/swimlanesDiagram-VR7AAH4N-D_nFQfRc.js +30 -0
- package/dist/{swiper-component-C90YjhaV.js → swiper-component-IOUowglM.js} +39 -26
- package/dist/{time-aWGfjN8x.js → time-BieKwdYS.js} +42 -42
- package/dist/{timeline-definition-Z64GVDOM-Dacq_5lr.js → timeline-definition-24CTP7MA-BuqJUIRZ.js} +7 -7
- package/dist/{toDate-CpXBeDq0.js → toDate-WXg1yLcI.js} +7 -7
- package/dist/toast-DvY70eFX.js +1834 -0
- package/dist/tooltip-qB8NlSqV.js +1742 -0
- package/dist/treeView-Q6P3EWNA-DuRV97YF.js +3 -0
- package/dist/treemap-WGGIJYW6-AsLq-uwC.js +3 -0
- package/dist/use-restore-focus-D5D9xEjZ.js +5946 -0
- package/dist/{use-vega-container-remeasure-DUW4tRxq.js → use-vega-container-remeasure-4DwJbPWu.js} +2 -2
- package/dist/{useAsyncData-CH6-CgJF.js → useAsyncData-BD6AStMt.js} +3 -3
- package/dist/{useDateFormatter-Cp9Jdxls.js → useDateFormatter-BRTPsUK7.js} +8 -8
- package/dist/{useDeepCompareMemoize-JC87C_jO.js → useDeepCompareMemoize-D_HNdyvI.js} +2 -2
- package/dist/{useIframeCapabilities-De3C-9b4.js → useIframeCapabilities-CVtNkI3Y.js} +2 -2
- package/dist/{useLifecycle-B4RGlyf0.js → useLifecycle-DPgiiwwU.js} +6 -6
- package/dist/useNumberFormatter-BfDpa21j.js +251 -0
- package/dist/useTheme-DzXmYZ5V.js +904 -0
- package/dist/{vega-component-ovBXgp91.js → vega-component-D0QXopU8.js} +34 -34
- package/dist/{vega-embed-container-TJ5z5dZw.js → vega-embed-container-CbnTccZi.js} +6 -6
- package/dist/vega-loader.browser-D95WfUcj.js +1139 -0
- package/dist/{vennDiagram-T6HMQDX7-D-Rcq3Iu.js → vennDiagram-4TSXK5OY-CTUlJ2PK.js} +9 -9
- package/dist/wardley-WFR3VGLG-Na-tFhQJ.js +3 -0
- package/dist/{wardleyDiagram-T6FBY63Y-Z-Og7_Wd.js → wardleyDiagram-VM6X3IG4-3evLUb0M.js} +10 -10
- package/dist/{xychartDiagram-ELKLHX3M-BDJ2dITA.js → xychartDiagram-S5SC5T6Z-DL2E3K8-.js} +248 -139
- package/package.json +54 -57
- package/src/__mocks__/notebook.ts +1 -0
- package/src/__mocks__/requests.ts +7 -0
- package/src/__tests__/__snapshots__/CellStatus.test.tsx.snap +12 -12
- package/src/__tests__/fixtures/pair-preview.ts +19 -0
- package/src/__tests__/mount.test.ts +18 -0
- package/src/components/app-config/optional-features.tsx +6 -0
- package/src/components/chat/acp/__tests__/agent-panel-initialization.test.tsx +394 -0
- package/src/components/chat/acp/__tests__/agent-panel.test.tsx +272 -0
- package/src/components/chat/acp/__tests__/state.test.ts +2 -2
- package/src/components/chat/acp/agent-panel.tsx +115 -74
- package/src/components/chat/acp/state.ts +1 -1
- package/src/components/data-table/__tests__/filters.test.ts +21 -21
- package/src/components/data-table/charts/__tests__/spec.test.ts +5 -10
- package/src/components/data-table/column-explorer-panel/column-explorer.tsx +2 -6
- package/src/components/data-table/column-summary/chart-spec-model.tsx +1 -1
- package/src/components/data-table/columns.tsx +64 -67
- package/src/components/editor/RecoveryButton.tsx +1 -0
- package/src/components/editor/__tests__/dynamic-favicon.test.tsx +36 -2
- package/src/components/editor/actions/__tests__/pair-with-agent-commands.test.ts +115 -0
- package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +218 -0
- package/src/components/editor/actions/pair-with-agent-commands.ts +46 -9
- package/src/components/editor/actions/pair-with-agent-modal.tsx +49 -23
- package/src/components/editor/ai/add-cell-with-ai.tsx +1 -0
- package/src/components/editor/ai/ai-completion-editor.tsx +3 -1
- package/src/components/editor/alerts/__tests__/connection-notice.test.tsx +186 -0
- package/src/components/editor/alerts/__tests__/startup-progress.test.tsx +185 -0
- package/src/components/editor/alerts/connecting-alert.tsx +1 -66
- package/src/components/editor/alerts/connection-notice.tsx +186 -0
- package/src/components/editor/alerts/startup-output.tsx +173 -0
- package/src/components/editor/alerts/startup-progress.tsx +204 -0
- package/src/components/editor/app-container.tsx +9 -3
- package/src/components/editor/boundary/ErrorBoundary.tsx +38 -16
- package/src/components/editor/boundary/__tests__/ErrorBoundary.test.tsx +311 -0
- package/src/components/editor/cell/__tests__/toolbar.test.tsx +73 -0
- package/src/components/editor/cell/code/__tests__/rtc-editor-config.test.ts +50 -0
- package/src/components/editor/cell/code/cell-editor.tsx +41 -42
- package/src/components/editor/cell/toolbar.tsx +11 -0
- package/src/components/editor/chrome/components/__tests__/feedback-button.test.tsx +94 -0
- package/src/components/editor/chrome/components/feedback-button.tsx +63 -42
- package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +164 -8
- package/src/components/editor/chrome/panels/__tests__/sandbox-details.test.tsx +358 -0
- package/src/components/editor/chrome/panels/__tests__/sandbox-recovery.test.tsx +168 -0
- package/src/components/editor/chrome/panels/packages-panel.tsx +113 -175
- package/src/components/editor/chrome/panels/sandbox-controller.tsx +106 -0
- package/src/components/editor/chrome/panels/sandbox-details-state.ts +38 -0
- package/src/components/editor/chrome/panels/sandbox-panel.tsx +230 -0
- package/src/components/editor/chrome/panels/sandbox-toggle.tsx +52 -0
- package/src/components/editor/chrome/wrapper/app-chrome.tsx +7 -0
- package/src/components/editor/chrome/wrapper/footer-items/backend-status.tsx +3 -1
- package/src/components/editor/code/__tests__/readonly-diff.test.tsx +65 -0
- package/src/components/editor/code/readonly-diff.tsx +1 -1
- package/src/components/editor/connections/database/__tests__/__snapshots__/as-code.test.ts.snap +98 -98
- package/src/components/editor/connections/database/__tests__/as-code.test.ts +11 -5
- package/src/components/editor/dynamic-favicon.tsx +17 -10
- package/src/components/editor/file-tree/__tests__/file-explorer-navigation.test.tsx +13 -11
- package/src/components/editor/file-tree/__tests__/tree-actions.test.tsx +54 -0
- package/src/components/editor/file-tree/tree-actions.tsx +11 -14
- package/src/components/editor/header/__tests__/status.test.tsx +8 -20
- package/src/components/editor/header/filename-input.tsx +2 -0
- package/src/components/editor/header/status.tsx +5 -3
- package/src/components/editor/navigation/clipboard.ts +1 -0
- package/src/components/editor/notebook-cell.tsx +4 -2
- package/src/components/editor/output/JsonOutput.tsx +182 -632
- package/src/components/editor/output/Outputs.css +49 -17
- package/src/components/editor/output/__tests__/JsonOutput-interactions.test.tsx +254 -0
- package/src/components/editor/output/__tests__/JsonOutput-mimetype.test.tsx +41 -46
- package/src/components/editor/output/__tests__/{json-output.test.ts → json-output-formatting.test.ts} +113 -75
- package/src/components/editor/output/__tests__/json-output-pagination.test.ts +252 -0
- package/src/components/editor/output/json-output/formatting.ts +167 -0
- package/src/components/editor/output/json-output/pagination.ts +155 -0
- package/src/components/editor/output/json-output/renderers.tsx +196 -0
- package/src/components/editor/output/json-output/themes.ts +52 -0
- package/src/components/editor/package-alert.tsx +112 -66
- package/src/components/editor/renderers/cell-array.tsx +1 -5
- package/src/components/editor/renderers/slides-layout/__tests__/plugin.test.ts +3 -7
- package/src/components/slides/slide-form.tsx +1 -0
- package/src/components/slides/slide-notes-editor.tsx +1 -0
- package/src/components/tracing/tracing-spec.ts +1 -1
- package/src/components/ui/__tests__/badge.test.ts +6 -4
- package/src/components/ui/use-toast.ts +8 -0
- package/src/core/MarimoApp.tsx +0 -2
- package/src/core/ai/context/providers/__tests__/error.test.ts +16 -22
- package/src/core/alerts/__tests__/environment.test.ts +183 -0
- package/src/core/alerts/__tests__/state.test.tsx +240 -0
- package/src/core/alerts/environment.ts +57 -0
- package/src/core/alerts/state.ts +155 -99
- package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +296 -259
- package/src/core/cells/__tests__/apply-transaction.test.ts +34 -0
- package/src/core/cells/__tests__/cell.test.ts +61 -0
- package/src/core/cells/__tests__/cells.test.ts +182 -37
- package/src/core/cells/__tests__/document-changes.test.ts +39 -1
- package/src/core/cells/__tests__/outputs.test.tsx +117 -0
- package/src/core/cells/__tests__/pending-delete-service.test.tsx +4 -3
- package/src/core/cells/__tests__/session.test.ts +7 -0
- package/src/core/cells/cell.ts +9 -1
- package/src/core/cells/cells.ts +13 -18
- package/src/core/cells/document-changes.ts +11 -1
- package/src/core/cells/outputs.ts +66 -9
- package/src/core/cells/pending-delete-service.ts +12 -16
- package/src/core/cells/session.ts +2 -0
- package/src/core/cells/types.ts +2 -0
- package/src/core/codemirror/find-replace/__tests__/navigate.test.ts +8 -16
- package/src/core/codemirror/language/__tests__/sql.test.ts +70 -2
- package/src/core/codemirror/language/embedded/__tests__/embedded-python.test.ts +4 -8
- package/src/core/codemirror/language/languages/sql/sql.ts +16 -1
- package/src/core/codemirror/rtc/extension.ts +5 -2
- package/src/core/config/pair.ts +19 -0
- package/src/core/datasets/__tests__/data-source.test.ts +2 -0
- package/src/core/datasets/data-source-connections.ts +15 -10
- package/src/core/edit-app.tsx +8 -5
- package/src/core/errors/state.ts +1 -2
- package/src/core/export/__tests__/hooks.test.ts +1 -6
- package/src/core/islands/bootstrap.ts +3 -1
- package/src/core/islands/bridge.ts +3 -0
- package/src/core/islands/worker-factory.ts +2 -12
- package/src/core/kernel/messages.ts +2 -2
- package/src/core/network/__tests__/requests-network.test.ts +4 -3
- package/src/core/network/connection-notice.ts +117 -0
- package/src/core/network/connection.ts +19 -0
- package/src/core/network/requests-lazy.ts +3 -0
- package/src/core/network/requests-network.ts +12 -0
- package/src/core/network/requests-static.ts +3 -0
- package/src/core/network/requests-toasting.tsx +3 -0
- package/src/core/network/resolve.ts +2 -1
- package/src/core/network/types.ts +7 -0
- package/src/core/network/useConnectionNotice.ts +53 -0
- package/src/core/packages/__tests__/usePackageAction.test.tsx +129 -0
- package/src/core/packages/__tests__/useSandboxController.test.tsx +246 -0
- package/src/core/packages/package-data.ts +35 -0
- package/src/core/packages/sandbox-state.ts +67 -0
- package/src/core/packages/toast-components.tsx +11 -0
- package/src/core/packages/useInstallPackage.ts +1 -1
- package/src/core/packages/usePackageAction.ts +51 -0
- package/src/core/packages/usePackageDependencies.ts +22 -0
- package/src/core/packages/useSandboxController.ts +208 -0
- package/src/core/run-app.tsx +2 -0
- package/src/core/static/__tests__/files.test.ts +12 -30
- package/src/core/wasm/__tests__/bootstrap.test.ts +63 -0
- package/src/core/wasm/__tests__/bridge.test.ts +13 -4
- package/src/core/wasm/__tests__/fs.test.ts +22 -0
- package/src/core/wasm/__tests__/runtime-config.test.ts +65 -0
- package/src/core/wasm/bridge.ts +35 -23
- package/src/core/wasm/rpc.ts +2 -0
- package/src/core/wasm/runtime-config.ts +31 -0
- package/src/core/wasm/worker/bootstrap.ts +21 -11
- package/src/core/wasm/worker/constants.ts +1 -0
- package/src/core/wasm/worker/fs.ts +4 -0
- package/src/core/wasm/worker/getController.ts +3 -1
- package/src/core/wasm/worker/save-worker.ts +10 -9
- package/src/core/wasm/worker/types.ts +6 -4
- package/src/core/wasm/worker/worker.ts +9 -9
- package/src/core/websocket/__tests__/useMarimoKernelConnection.hook.test.tsx +404 -3
- package/src/core/websocket/__tests__/useMarimoKernelConnection.test.ts +8 -6
- package/src/core/websocket/transports/transport.ts +1 -1
- package/src/core/websocket/transports/ws.ts +1 -1
- package/src/core/websocket/types.ts +11 -1
- package/src/core/websocket/useMarimoKernelConnection.tsx +63 -16
- package/src/hooks/useDebounce.ts +2 -0
- package/src/mount.tsx +3 -0
- package/src/plugins/core/BadPlugin.tsx +3 -14
- package/src/plugins/core/RenderHTML.tsx +22 -2
- package/src/plugins/core/__test__/RenderHTML.test.ts +187 -0
- package/src/plugins/core/registerReactComponent.tsx +0 -1
- package/src/plugins/impl/FileBrowserPlugin.tsx +10 -4
- package/src/plugins/impl/FormPlugin.tsx +2 -0
- package/src/plugins/impl/MatrixPlugin.tsx +2 -0
- package/src/plugins/impl/RangeSliderPlugin.tsx +2 -0
- package/src/plugins/impl/SliderPlugin.tsx +2 -0
- package/src/plugins/impl/__tests__/FileBrowserPlugin.test.tsx +50 -1
- package/src/plugins/impl/data-editor/__tests__/glide-data-editor.test.tsx +23 -18
- package/src/plugins/impl/data-frames/panel.tsx +1 -0
- package/src/plugins/impl/image-comparison/ImageComparisonComponent.tsx +1 -1
- package/src/plugins/impl/image-comparison/__tests__/ImageComparisonComponent.test.tsx +15 -0
- package/src/plugins/impl/vega/__tests__/make-selectable.test.ts +42 -48
- package/src/plugins/layout/AccordionPlugin.tsx +7 -2
- package/src/plugins/layout/JsonOutputPlugin.tsx +7 -10
- package/src/plugins/layout/__test__/AccordionPlugin.test.tsx +66 -0
- package/src/stories/sandbox.stories.tsx +488 -0
- package/src/utils/__tests__/path.test.ts +6 -5
- package/src/utils/__tests__/worker.test.ts +81 -0
- package/src/utils/worker.ts +22 -0
- package/dist/ErrorBoundary-CR-ZQN_c.js +0 -125
- package/dist/_baseMerge-Dv8bBloN.js +0 -73
- package/dist/_initCloneObject---vediN2.js +0 -95
- package/dist/arc-DOVLrCPd.js +0 -82
- package/dist/architecture-TIHT7OUA-MiJpPpQW.js +0 -3
- package/dist/array-CApNbSU9.js +0 -6
- package/dist/assets/__vite-browser-external-BdKGjRZ4.js +0 -1
- package/dist/assets/worker-DlIBfp9S.js +0 -98
- package/dist/blockDiagram-VBNYF7ZC-Bc--I65C.js +0 -3158
- package/dist/chat-ui-BSwr409J.js +0 -8455
- package/dist/chunk-3NCLNEKW-BIp_lB_4.js +0 -14
- package/dist/chunk-5FCAYU7R-C_HO9A1J.js +0 -23
- package/dist/chunk-5HE753X5-Bixm_MV-.js +0 -38
- package/dist/chunk-5JV3BV7I-CH5J3iZY.js +0 -54
- package/dist/chunk-5TONJI2A-DG6gDiSS.js +0 -62
- package/dist/chunk-7BUUIJ7U-5afmIPeo.js +0 -82
- package/dist/chunk-BIQX33UG-7TMBUf89.js +0 -23
- package/dist/chunk-CQNSW5MT-Cnqqsxt-.js +0 -38
- package/dist/chunk-CYSBUYHQ-BAbymFHR.js +0 -23
- package/dist/chunk-EMLP6XTP-Bsz-QL77.js +0 -23
- package/dist/chunk-J7OUQ5F2-CM_meqZH.js +0 -57
- package/dist/chunk-JG7HCLWE-DUnCDgPk.js +0 -62
- package/dist/chunk-MOZMSUNE-BG6IHvzT.js +0 -35
- package/dist/chunk-OSBZ3O6U-CYwj_bvR.js +0 -23
- package/dist/chunk-QBLGF6JB-B4vJ4j8J.js +0 -23
- package/dist/chunk-QR6OTTB3-D4wH2ldi.js +0 -3738
- package/dist/chunk-R7FJI6CG-Blf8FcmZ.js +0 -64
- package/dist/chunk-RYQCIY6F-BWbIlBIH.js +0 -241
- package/dist/chunk-U6XO7XAA-COw445DS.js +0 -63
- package/dist/chunk-UBXNYLIW-BHigVU4H.js +0 -32
- package/dist/chunk-YOTPTUD7-Dn044Q_d.js +0 -30
- package/dist/chunk-ZIRB5QZD-BR4TMfOa.js +0 -1153
- package/dist/classDiagram-JCYQIIEL-DmXSLxe_.js +0 -30
- package/dist/classDiagram-v2-OCEON4UE-CooqVexA.js +0 -30
- package/dist/click-outside-container-D_URcm3D.js +0 -167
- package/dist/code-block-37QAKDTI-CQrzBOr-.js +0 -107
- package/dist/copy-CD7S58CY.js +0 -141
- package/dist/cynefin-VYW2F7L2-CeHAxOEX.js +0 -3
- package/dist/dagre-VZM6K2ZE-CJggWPTJ.js +0 -328
- package/dist/dist-3_o3T78I.js +0 -1360
- package/dist/dist-ABQjhuek.js +0 -64
- package/dist/dist-CO5R6VMi.js +0 -538
- package/dist/dropdown-menu-DhCfcscX.js +0 -5858
- package/dist/emotion-is-prop-valid.esm-M57VsT8j.js +0 -14
- package/dist/error-banner-CsXbEEjo.js +0 -611
- package/dist/eventmodeling-45OFAUF4-B_lWZ-Pd.js +0 -3
- package/dist/flowDiagram-UKHOOZJN-g1AmrZzM.js +0 -17
- package/dist/gitGraph-TEB2WS4Q-Dj-eXUph.js +0 -3
- package/dist/info-DKCQHKI2-Bhb-pe2e.js +0 -3
- package/dist/line-CuKjIQa1.js +0 -34
- package/dist/map-BJpOfPlL.js +0 -149
- package/dist/math-BXtKgYah.js +0 -10
- package/dist/mermaid-4DMBBIKO-Ddp9TgNp.js +0 -2
- package/dist/mermaid-Bi23Ibnw.js +0 -1173
- package/dist/mermaid-parser.core-Din9FvSi.js +0 -99
- package/dist/packet-7NZHBO7P-fbkIIyKE.js +0 -3
- package/dist/path-BNHuHZP8.js +0 -87
- package/dist/pie-RZYD4A2V-CLRIsxY1.js +0 -3
- package/dist/purify.es-CB-YO1Hn.js +0 -774
- package/dist/radar-I7S5WNFK-BGzCbgXu.js +0 -3
- package/dist/railroad-3IZDKUUU-CvQIIcuU.js +0 -3
- package/dist/railroad-abnf-AHOZXSZD-CMwtlNtu.js +0 -3
- package/dist/railroad-ebnf-EBAXGLYW-n1QUOAsh.js +0 -3
- package/dist/railroad-peg-LSFZ7HO6-PjnrpmCs.js +0 -3
- package/dist/schemas-DzK6U94N.js +0 -6057
- package/dist/stateDiagram-v2-UEYNNEHI-BTRUs8Il.js +0 -30
- package/dist/step-CKczO72Q.js +0 -670
- package/dist/swimlanesDiagram-ULZ7WXOC-NBmgBl9G.js +0 -31
- package/dist/timer-DpDyj9G0.js +0 -596
- package/dist/tooltip-Dt77sBeT.js +0 -390
- package/dist/treeView-QDETBFTQ-DU8LwHuf.js +0 -3
- package/dist/treemap-6X3UGDF4-DUk4-k_O.js +0 -3
- package/dist/useTheme-CVJ0UOf2.js +0 -841
- package/dist/vega-loader.browser-DEggZzni.js +0 -1083
- package/dist/wardley-OPB4EBWU-Db-AaGas.js +0 -3
- 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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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 {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
import {
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
<
|
|
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
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
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
|
-
|
|
502
|
-
|
|
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
|
-
|
|
651
|
-
|
|
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
|
-
|
|
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
|
}
|