@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.
- package/dist/{ConnectedDataExplorerComponent-C9OF_75w.js → ConnectedDataExplorerComponent-KVhjDMSe.js} +1066 -1066
- package/dist/ErrorBoundary-pXXIvlr3.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-BC_yF09p.js +138 -0
- package/dist/{common-Cv-4hMa0.js → common-Dv227ygK.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-B-I2K7z3.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-D2hUXe09.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-B851uguN.js} +2143 -1910
- package/dist/{glide-utils-NShAPPtn.js → glide-utils-B3rUT5PI.js} +15 -15
- package/dist/globals-CmijFoIU.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-BtdoxWiD.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-DQq7NUGJ.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,6 +1,6 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
import { describe, expect, it } from "vitest";
|
|
3
|
-
import {
|
|
3
|
+
import { getCopyValue } from "../json-output/formatting";
|
|
4
4
|
|
|
5
5
|
describe("getCopyValue", () => {
|
|
6
6
|
it("should handle strings without MIME prefixes", () => {
|
|
@@ -178,7 +178,6 @@ describe("getCopyValue", () => {
|
|
|
178
178
|
});
|
|
179
179
|
|
|
180
180
|
it("should handle empty set", () => {
|
|
181
|
-
// Empty set literal in Python is `set()`, not `{}` (which is a dict).
|
|
182
181
|
expect(getCopyValue("text/plain+set:[]")).toMatchInlineSnapshot(`"set()"`);
|
|
183
182
|
});
|
|
184
183
|
|
|
@@ -212,7 +211,7 @@ describe("getCopyValue", () => {
|
|
|
212
211
|
it("should handle tuples", () => {
|
|
213
212
|
const value = "text/plain+tuple:[1,2,3]";
|
|
214
213
|
const result = getCopyValue(value);
|
|
215
|
-
expect(result).toMatchInlineSnapshot(`"(1,2,3)"`);
|
|
214
|
+
expect(result).toMatchInlineSnapshot(`"(1, 2, 3)"`);
|
|
216
215
|
});
|
|
217
216
|
|
|
218
217
|
it("should handle tuples in mixed types", () => {
|
|
@@ -226,7 +225,7 @@ describe("getCopyValue", () => {
|
|
|
226
225
|
`
|
|
227
226
|
"{
|
|
228
227
|
"key1": 42,
|
|
229
|
-
"key2": (1,2,3),
|
|
228
|
+
"key2": (1, 2, 3),
|
|
230
229
|
"key3": True
|
|
231
230
|
}"
|
|
232
231
|
`,
|
|
@@ -240,8 +239,8 @@ describe("getCopyValue", () => {
|
|
|
240
239
|
expect(result).toMatchInlineSnapshot(`"18446744073709551616"`);
|
|
241
240
|
|
|
242
241
|
const nestedBigInt = {
|
|
243
|
-
key1: bigint,
|
|
244
|
-
key2: `text/plain+bigint:${bigint}`,
|
|
242
|
+
key1: bigint,
|
|
243
|
+
key2: `text/plain+bigint:${bigint}`,
|
|
245
244
|
key3: true,
|
|
246
245
|
};
|
|
247
246
|
const nestedResult = getCopyValue(nestedBigInt);
|
|
@@ -260,7 +259,7 @@ describe("getCopyValue", () => {
|
|
|
260
259
|
expect(bigintRawResult).toMatchInlineSnapshot(`"18446744073709551616"`);
|
|
261
260
|
|
|
262
261
|
const nestedBigIntRaw = {
|
|
263
|
-
key1: bigintRaw,
|
|
262
|
+
key1: bigintRaw,
|
|
264
263
|
key2: `text/plain+bigint:${bigintRaw}`,
|
|
265
264
|
key3: true,
|
|
266
265
|
};
|
|
@@ -277,64 +276,8 @@ describe("getCopyValue", () => {
|
|
|
277
276
|
});
|
|
278
277
|
});
|
|
279
278
|
|
|
280
|
-
describe("determineMaxDisplayLength", () => {
|
|
281
|
-
const sample2DArray = [
|
|
282
|
-
[1, 2, 3],
|
|
283
|
-
[4, 5, 6],
|
|
284
|
-
[7, 8, 9],
|
|
285
|
-
[10, 11, 12],
|
|
286
|
-
[13, 14, 15],
|
|
287
|
-
[16, 17, 18],
|
|
288
|
-
[19, 20, 21],
|
|
289
|
-
[22, 23, 24],
|
|
290
|
-
[25, 26, 27],
|
|
291
|
-
[28, 29, 30],
|
|
292
|
-
];
|
|
293
|
-
|
|
294
|
-
it("should return undefined for 1 level arrays", () => {
|
|
295
|
-
const value = [1, 2, 3];
|
|
296
|
-
const result = determineMaxDisplayLength(value);
|
|
297
|
-
expect(result).toBeUndefined();
|
|
298
|
-
});
|
|
299
|
-
|
|
300
|
-
it("should return undefined for 2 level arrays with less than 20 items", () => {
|
|
301
|
-
const value = sample2DArray;
|
|
302
|
-
const result = determineMaxDisplayLength(value);
|
|
303
|
-
expect(result).toBeUndefined();
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
it("should return 10 for 2 level arrays with more than 20 items", () => {
|
|
307
|
-
const longArray = Array.from({ length: 21 }, (_, i) => i);
|
|
308
|
-
const value = [...sample2DArray, longArray];
|
|
309
|
-
const result = determineMaxDisplayLength(value);
|
|
310
|
-
expect(result).toBe(10);
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
it("should return 5 for 2 level arrays with more than 50 items", () => {
|
|
314
|
-
const longArray = Array.from({ length: 51 }, (_, i) => i);
|
|
315
|
-
const value = [...sample2DArray, longArray];
|
|
316
|
-
const result = determineMaxDisplayLength(value);
|
|
317
|
-
expect(result).toBe(5);
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
it("should return 5 for 3 level arrays with more than 20 items", () => {
|
|
321
|
-
const longArray = Array.from({ length: 21 }, (_, i) => i);
|
|
322
|
-
const value = [[...sample2DArray], [...sample2DArray, longArray]];
|
|
323
|
-
const result = determineMaxDisplayLength(value);
|
|
324
|
-
expect(result).toBe(5);
|
|
325
|
-
});
|
|
326
|
-
});
|
|
327
|
-
|
|
328
279
|
describe("getCopyValue with encoded non-string keys", () => {
|
|
329
|
-
// Keys are encoded by _key_formatter in
|
|
330
|
-
// marimo/_output/formatters/structures.py. Frontend must round-trip them
|
|
331
|
-
// to Python literals in the copy output.
|
|
332
|
-
|
|
333
280
|
it("decodes int keys unquoted", () => {
|
|
334
|
-
// JS reorders integer-like string keys to the front of object iteration
|
|
335
|
-
// (spec-mandated), so `"2"` appears before `"text/plain+int:2"` here.
|
|
336
|
-
// This is pre-existing and unrelated to the encoding — both entries
|
|
337
|
-
// survive, which is the regression this guards.
|
|
338
281
|
const value = { "text/plain+int:2": "no", "2": "oh" };
|
|
339
282
|
expect(getCopyValue(value)).toMatchInlineSnapshot(`
|
|
340
283
|
"{
|
|
@@ -375,7 +318,6 @@ describe("getCopyValue with encoded non-string keys", () => {
|
|
|
375
318
|
});
|
|
376
319
|
|
|
377
320
|
it("emits 1-element tuple keys with a trailing comma (Python syntax)", () => {
|
|
378
|
-
// `(1)` is just `1` in Python — a 1-tuple needs `(1,)`.
|
|
379
321
|
const value = {
|
|
380
322
|
"text/plain+tuple:[1]": "one",
|
|
381
323
|
"text/plain+tuple:[]": "empty",
|
|
@@ -389,7 +331,6 @@ describe("getCopyValue with encoded non-string keys", () => {
|
|
|
389
331
|
});
|
|
390
332
|
|
|
391
333
|
it("emits empty frozenset keys as `frozenset()` not `frozenset({})`", () => {
|
|
392
|
-
// `frozenset({})` reads like it's constructing from an empty dict.
|
|
393
334
|
const value = {
|
|
394
335
|
"text/plain+frozenset:[]": "empty",
|
|
395
336
|
"text/plain+frozenset:[1]": "single",
|
|
@@ -418,12 +359,6 @@ describe("getCopyValue with encoded non-string keys", () => {
|
|
|
418
359
|
});
|
|
419
360
|
|
|
420
361
|
it("parses tuple/frozenset payloads containing bare NaN/Infinity", () => {
|
|
421
|
-
// Python's json.dumps emits bare `NaN`/`Infinity` inside the embedded
|
|
422
|
-
// tuple/frozenset payload strings (JSON spec violation, but ECMA-262-
|
|
423
|
-
// friendly via the fallback in jsonParseWithSpecialChar). The outer
|
|
424
|
-
// JSON stays strict because those tokens live inside a JSON string
|
|
425
|
-
// key/value. Regression for tuple-key payloads that previously broke
|
|
426
|
-
// the frontend's `JSON.parse` and threw.
|
|
427
362
|
const value = {
|
|
428
363
|
"text/plain+tuple:[NaN]": "tn",
|
|
429
364
|
"text/plain+tuple:[Infinity, -Infinity]": "ti",
|
|
@@ -439,10 +374,6 @@ describe("getCopyValue with encoded non-string keys", () => {
|
|
|
439
374
|
});
|
|
440
375
|
|
|
441
376
|
it("falls back to the raw payload for malformed tuple/frozenset", () => {
|
|
442
|
-
// `jsonParseWithSpecialChar` returns `{}` on parse failure rather
|
|
443
|
-
// than throwing; without an `Array.isArray` guard, the formatters
|
|
444
|
-
// would crash on `.length`/`.map`. Pass the raw payload through so
|
|
445
|
-
// a malformed wire form doesn't break the whole render.
|
|
446
377
|
const value = {
|
|
447
378
|
"text/plain+tuple:not a json list": "t",
|
|
448
379
|
k: "text/plain+frozenset:also broken",
|
|
@@ -513,3 +444,110 @@ describe("getCopyValue with application/ mimetypes", () => {
|
|
|
513
444
|
expect(result).toContain('"number": 42');
|
|
514
445
|
});
|
|
515
446
|
});
|
|
447
|
+
|
|
448
|
+
describe("Python copy fidelity", () => {
|
|
449
|
+
it.each([
|
|
450
|
+
"<marimo-replace>True</marimo-replace>",
|
|
451
|
+
"\u0000show_more\u000042|$",
|
|
452
|
+
'quotes " backslash \\ newline\n',
|
|
453
|
+
])("preserves literal text %j", (value) => {
|
|
454
|
+
expect(getCopyValue(value)).toBe(JSON.stringify(value));
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
it("preserves keys which used to collide after decoding", () => {
|
|
458
|
+
const data = JSON.parse(
|
|
459
|
+
'{"__proto__":1,"<marimo-replace>2</marimo-replace>":2,"text/plain+int:2":3}',
|
|
460
|
+
);
|
|
461
|
+
expect(getCopyValue(data)).toBe(
|
|
462
|
+
'{\n "__proto__": 1,\n "<marimo-replace>2</marimo-replace>": 2,\n 2: 3\n}',
|
|
463
|
+
);
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
it.each([
|
|
467
|
+
["text/plain+tuple:[42]", "(42,)"],
|
|
468
|
+
["text/plain+tuple:[]", "()"],
|
|
469
|
+
[
|
|
470
|
+
'text/plain+tuple:["quote\\\"","back\\\\slash",true,null]',
|
|
471
|
+
'("quote\\\"", "back\\\\slash", True, None)',
|
|
472
|
+
],
|
|
473
|
+
[
|
|
474
|
+
'text/plain+set:[true,null,"text/plain:literal"]',
|
|
475
|
+
'{True, None, "text/plain:literal"}',
|
|
476
|
+
],
|
|
477
|
+
['text/plain+tuple:["text/plain+int:2"]', '("text/plain+int:2",)'],
|
|
478
|
+
["text/plain+float:nan", "float('nan')"],
|
|
479
|
+
["text/plain+float:inf", "float('inf')"],
|
|
480
|
+
["text/plain+float:-inf", "-float('inf')"],
|
|
481
|
+
["text/plain+float:1.0", "1.0"],
|
|
482
|
+
])("copies %s as valid Python", (data, expected) => {
|
|
483
|
+
expect(getCopyValue(data)).toBe(expected);
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
it("distinguishes shared references from cycles", () => {
|
|
487
|
+
const shared = { x: true };
|
|
488
|
+
expect(getCopyValue([shared, shared])).toBe(
|
|
489
|
+
'[\n {\n "x": True\n },\n {\n "x": True\n }\n]',
|
|
490
|
+
);
|
|
491
|
+
const cyclic: unknown[] = [];
|
|
492
|
+
cyclic.push(cyclic);
|
|
493
|
+
expect(() => getCopyValue(cyclic)).toThrow("Cannot copy circular data");
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
it("copies sparse arrays and non-finite numbers as Python literals", () => {
|
|
497
|
+
const values = [0, Number.NaN, Infinity, -Infinity];
|
|
498
|
+
Reflect.deleteProperty(values, "0");
|
|
499
|
+
expect(getCopyValue(values)).toBe(
|
|
500
|
+
"[\n None,\n float('nan'),\n float('inf'),\n -float('inf')\n]",
|
|
501
|
+
);
|
|
502
|
+
});
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
describe("Python copy negative cases", () => {
|
|
506
|
+
it.each([
|
|
507
|
+
"text/plain+unknown:payload",
|
|
508
|
+
"text/plain+tuple",
|
|
509
|
+
"not-text/plain:payload",
|
|
510
|
+
"https://example.com:a:b",
|
|
511
|
+
])("does not decode unrecognized leaf %j", (value) => {
|
|
512
|
+
expect(getCopyValue(value)).toBe(JSON.stringify(value));
|
|
513
|
+
expect(getCopyValue({ [value]: "literal" })).toBe(
|
|
514
|
+
`{\n ${JSON.stringify(value)}: "literal"\n}`,
|
|
515
|
+
);
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
it.each(["tuple", "set", "frozenset"])(
|
|
519
|
+
"does not invent a %s from malformed or non-array JSON",
|
|
520
|
+
(kind) => {
|
|
521
|
+
for (const payload of ["[broken", "null", "42", '{"x":1}']) {
|
|
522
|
+
expect(getCopyValue(`text/plain+${kind}:${payload}`)).toBe(payload);
|
|
523
|
+
}
|
|
524
|
+
},
|
|
525
|
+
);
|
|
526
|
+
|
|
527
|
+
it("decodes escaped keys and text exactly once", () => {
|
|
528
|
+
const data = {
|
|
529
|
+
"text/plain+str:text/plain+str:text/plain+int:2":
|
|
530
|
+
"text/plain:text/plain+tuple:[42]",
|
|
531
|
+
};
|
|
532
|
+
expect(getCopyValue(data)).toBe(
|
|
533
|
+
'{\n "text/plain+str:text/plain+int:2": "text/plain+tuple:[42]"\n}',
|
|
534
|
+
);
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
it("does not decode dictionary keys inside raw tuple payloads", () => {
|
|
538
|
+
expect(
|
|
539
|
+
getCopyValue(
|
|
540
|
+
'text/plain+tuple:[{"text/plain+int:2":"text/plain:literal"}]',
|
|
541
|
+
),
|
|
542
|
+
).toBe('({"text/plain+int:2": "text/plain:literal"},)');
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
it("rejects indirect cycles without poisoning later copies", () => {
|
|
546
|
+
const data: Record<string, unknown> = { child: [] };
|
|
547
|
+
data.child = [data];
|
|
548
|
+
expect(() => getCopyValue(data)).toThrow("Cannot copy circular data");
|
|
549
|
+
expect(getCopyValue({ child: [true] })).toBe(
|
|
550
|
+
'{\n "child": [\n True\n ]\n}',
|
|
551
|
+
);
|
|
552
|
+
});
|
|
553
|
+
});
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
import {
|
|
4
|
+
determineMaxDisplayLength,
|
|
5
|
+
estimateExpandedLines,
|
|
6
|
+
paginateJson,
|
|
7
|
+
} from "../json-output/pagination";
|
|
8
|
+
|
|
9
|
+
describe("determineMaxDisplayLength", () => {
|
|
10
|
+
const sample2DArray = [
|
|
11
|
+
[1, 2, 3],
|
|
12
|
+
[4, 5, 6],
|
|
13
|
+
[7, 8, 9],
|
|
14
|
+
[10, 11, 12],
|
|
15
|
+
[13, 14, 15],
|
|
16
|
+
[16, 17, 18],
|
|
17
|
+
[19, 20, 21],
|
|
18
|
+
[22, 23, 24],
|
|
19
|
+
[25, 26, 27],
|
|
20
|
+
[28, 29, 30],
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
it("should return undefined for 1 level arrays", () => {
|
|
24
|
+
const value = [1, 2, 3];
|
|
25
|
+
const result = determineMaxDisplayLength(value);
|
|
26
|
+
expect(result).toBeUndefined();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("should return undefined for 2 level arrays with less than 20 items", () => {
|
|
30
|
+
const value = sample2DArray;
|
|
31
|
+
const result = determineMaxDisplayLength(value);
|
|
32
|
+
expect(result).toBeUndefined();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("should return 10 for 2 level arrays with more than 20 items", () => {
|
|
36
|
+
const longArray = Array.from({ length: 21 }, (_, i) => i);
|
|
37
|
+
const value = [...sample2DArray, longArray];
|
|
38
|
+
const result = determineMaxDisplayLength(value);
|
|
39
|
+
expect(result).toBe(10);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("should return 5 for 2 level arrays with more than 50 items", () => {
|
|
43
|
+
const longArray = Array.from({ length: 51 }, (_, i) => i);
|
|
44
|
+
const value = [...sample2DArray, longArray];
|
|
45
|
+
const result = determineMaxDisplayLength(value);
|
|
46
|
+
expect(result).toBe(5);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("should return 5 for 3 level arrays with more than 20 items", () => {
|
|
50
|
+
const longArray = Array.from({ length: 21 }, (_, i) => i);
|
|
51
|
+
const value = [[...sample2DArray], [...sample2DArray, longArray]];
|
|
52
|
+
const result = determineMaxDisplayLength(value);
|
|
53
|
+
expect(result).toBe(5);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe("estimateExpandedLines", () => {
|
|
58
|
+
it("scalars count as 1", () => {
|
|
59
|
+
expect(estimateExpandedLines(42, 100)).toBe(1);
|
|
60
|
+
expect(estimateExpandedLines(null, 100)).toBe(1);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("flat containers = child count", () => {
|
|
64
|
+
expect(estimateExpandedLines({ a: 1, b: 2, c: 3 }, 100)).toBe(3);
|
|
65
|
+
expect(estimateExpandedLines([1, 2, 3, 4], 100)).toBe(4);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("small nested children expand recursively", () => {
|
|
69
|
+
expect(estimateExpandedLines({ a: { x: 1, y: 2 }, b: 3 }, 100)).toBe(3);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("large children count as 1 collapsed line", () => {
|
|
73
|
+
const big = Array.from({ length: 20 }, (_, i) => i);
|
|
74
|
+
expect(estimateExpandedLines({ a: big, b: 1 }, 100)).toBe(2);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("bails at cap", () => {
|
|
78
|
+
const big = Array.from({ length: 500 }, (_, i) => i);
|
|
79
|
+
expect(estimateExpandedLines(big, 50)).toBe(50);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("bails at max depth", () => {
|
|
83
|
+
const data = { a: { b: { c: { d: { e: { f: 1 } } } } } };
|
|
84
|
+
expect(estimateExpandedLines(data, 100)).toBe(1);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe("paginateJson", () => {
|
|
89
|
+
const options = { limits: {}, pageSize: 30 };
|
|
90
|
+
|
|
91
|
+
it("preserves full originals and reports the remaining count", () => {
|
|
92
|
+
const rows = Array.from({ length: 100 }, (_, i) => i);
|
|
93
|
+
const data = { rows };
|
|
94
|
+
const tree = paginateJson(data, options);
|
|
95
|
+
const displayRows: unknown[] = Reflect.get(tree.data, "rows");
|
|
96
|
+
expect(displayRows.slice(0, 30)).toEqual(rows.slice(0, 30));
|
|
97
|
+
expect(tree.getPage(displayRows[30])).toEqual({
|
|
98
|
+
path: '["rows"]',
|
|
99
|
+
remaining: 70,
|
|
100
|
+
});
|
|
101
|
+
expect(tree.getOriginal(tree.data)).toBe(data);
|
|
102
|
+
expect(tree.getOriginal(displayRows)).toBe(rows);
|
|
103
|
+
expect(tree.getOriginal("value")).toBe("value");
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("paginates arrays and objects at the boundary", () => {
|
|
107
|
+
for (const count of [0, 1, 29, 30, 31, 60]) {
|
|
108
|
+
const values = Array.from({ length: count }, (_, i) => i);
|
|
109
|
+
for (const data of [
|
|
110
|
+
values,
|
|
111
|
+
Object.fromEntries(values.map((i) => [`k${i}`, i])),
|
|
112
|
+
]) {
|
|
113
|
+
const tree = paginateJson(data, options);
|
|
114
|
+
const displayed = Object.values(tree.data);
|
|
115
|
+
expect(displayed.slice(0, 30)).toEqual(values.slice(0, 30));
|
|
116
|
+
expect(displayed).toHaveLength(
|
|
117
|
+
Math.min(count, 30) + Number(count > 30),
|
|
118
|
+
);
|
|
119
|
+
expect(tree.getPage(displayed.at(-1))).toEqual(
|
|
120
|
+
count > 30 ? { path: "[]", remaining: count - 30 } : undefined,
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("keeps dotted, quoted, and empty keys distinct without reserving user strings", () => {
|
|
127
|
+
const values = Array.from({ length: 40 }, (_, i) => i);
|
|
128
|
+
const data = { "a.b": values, a: { b: values }, '"': values, "": values };
|
|
129
|
+
const tree = paginateJson(data, { ...options, limits: { '["a.b"]': 60 } });
|
|
130
|
+
expect(Reflect.get(tree.data, "a.b")).toEqual(values);
|
|
131
|
+
for (const path of [["a", "b"], ['"'], [""]]) {
|
|
132
|
+
const node = path.reduce(
|
|
133
|
+
(node: object, key) => Reflect.get(node, key),
|
|
134
|
+
tree.data,
|
|
135
|
+
);
|
|
136
|
+
expect(tree.getPage(Object.values(node).at(-1))).toEqual({
|
|
137
|
+
path: JSON.stringify(path),
|
|
138
|
+
remaining: 10,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
expect(tree.getPage("\u0000show_more\u000040|$")).toBeUndefined();
|
|
142
|
+
expect(tree.getPage(Symbol("show more"))).toBeUndefined();
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("preserves __proto__ and empty keys when adding an object page", () => {
|
|
146
|
+
const data = { "": 1, ["__proto__"]: 2, other: 3 };
|
|
147
|
+
const tree = paginateJson(data, { limits: {}, pageSize: 2 });
|
|
148
|
+
expect(Object.values(tree.data).slice(0, 2)).toEqual([1, 2]);
|
|
149
|
+
expect(tree.getOriginal(tree.data)).toBe(data);
|
|
150
|
+
expect(tree.getPage(Object.values(tree.data).at(-1))).toEqual({
|
|
151
|
+
path: "[]",
|
|
152
|
+
remaining: 1,
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("does not traverse hidden branches, and caches visible children", () => {
|
|
157
|
+
const read = vi.fn(() => [1, 2]);
|
|
158
|
+
const hidden = vi.fn(() => [3, 4]);
|
|
159
|
+
const data = {
|
|
160
|
+
get visible() {
|
|
161
|
+
return read();
|
|
162
|
+
},
|
|
163
|
+
get hidden() {
|
|
164
|
+
return hidden();
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
const tree = paginateJson(data, { limits: {}, pageSize: 1 });
|
|
168
|
+
expect(read).not.toHaveBeenCalled();
|
|
169
|
+
const first = Reflect.get(tree.data, "visible");
|
|
170
|
+
expect(Reflect.get(tree.data, "visible")).toBe(first);
|
|
171
|
+
expect(read).toHaveBeenCalledOnce();
|
|
172
|
+
expect(hidden).not.toHaveBeenCalled();
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it("paginates deep branches without a depth cutoff", () => {
|
|
176
|
+
const data = {
|
|
177
|
+
deep: {
|
|
178
|
+
more: {
|
|
179
|
+
levels: {
|
|
180
|
+
still: {
|
|
181
|
+
going: {
|
|
182
|
+
deeper: { rows: Array.from({ length: 100 }, (_, i) => i) },
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
const tree = paginateJson(data, options);
|
|
190
|
+
const keys = ["deep", "more", "levels", "still", "going", "deeper", "rows"];
|
|
191
|
+
const rows = keys.reduce(
|
|
192
|
+
(node: object, key) => Reflect.get(node, key),
|
|
193
|
+
tree.data,
|
|
194
|
+
);
|
|
195
|
+
expect(Object.values(rows)).toHaveLength(31);
|
|
196
|
+
expect(tree.getPage(Object.values(rows).at(-1))).toEqual({
|
|
197
|
+
path: JSON.stringify(keys),
|
|
198
|
+
remaining: 70,
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it("preserves non-JSON objects and does not mutate input", () => {
|
|
203
|
+
const date = new Date("2026-01-01");
|
|
204
|
+
const data = Object.freeze({ date, values: Object.freeze([1, 2, 3]) });
|
|
205
|
+
const tree = paginateJson(data, { limits: {}, pageSize: 2 });
|
|
206
|
+
expect(Reflect.get(tree.data, "date")).toBe(date);
|
|
207
|
+
expect(Object.values(Reflect.get(tree.data, "values"))).toHaveLength(3);
|
|
208
|
+
expect(data.values).toEqual([1, 2, 3]);
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
describe("pagination negative cases", () => {
|
|
213
|
+
it("does not overwrite empty or NUL keys with pagination controls", () => {
|
|
214
|
+
const data = { "": "empty", "\0": "nul", "\0\0": "two", last: "last" };
|
|
215
|
+
const tree = paginateJson(data, { limits: {}, pageSize: 3 });
|
|
216
|
+
expect(Object.values(tree.data).slice(0, 3)).toEqual([
|
|
217
|
+
"empty",
|
|
218
|
+
"nul",
|
|
219
|
+
"two",
|
|
220
|
+
]);
|
|
221
|
+
expect(tree.getPage(Object.values(tree.data).at(-1))).toEqual({
|
|
222
|
+
path: "[]",
|
|
223
|
+
remaining: 1,
|
|
224
|
+
});
|
|
225
|
+
expect(Object.keys(data)).toEqual(["", "\0", "\0\0", "last"]);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it("does not accept page markers from another tree", () => {
|
|
229
|
+
const first = paginateJson([1, 2], { limits: {}, pageSize: 1 });
|
|
230
|
+
const second = paginateJson([3, 4], { limits: {}, pageSize: 1 });
|
|
231
|
+
expect(second.getPage(Object.values(first.data).at(-1))).toBeUndefined();
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it("does not read values beyond the visible page", () => {
|
|
235
|
+
const data = {
|
|
236
|
+
visible: 1,
|
|
237
|
+
get hidden() {
|
|
238
|
+
throw new Error("hidden value read");
|
|
239
|
+
},
|
|
240
|
+
};
|
|
241
|
+
const tree = paginateJson(data, { limits: {}, pageSize: 1 });
|
|
242
|
+
expect(Object.values(tree.data)[0]).toBe(1);
|
|
243
|
+
expect(tree.getOriginal(tree.data)).toBe(data);
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it("does not expose inherited or non-enumerable fields", () => {
|
|
247
|
+
const data = Object.defineProperty({ visible: 1 }, "hidden", { value: 2 });
|
|
248
|
+
const tree = paginateJson(data, { limits: {}, pageSize: 30 });
|
|
249
|
+
expect(Object.entries(tree.data)).toEqual([["visible", 1]]);
|
|
250
|
+
expect(Reflect.get(tree.data, "toString")).toBeUndefined();
|
|
251
|
+
});
|
|
252
|
+
});
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
import { jsonParseWithSpecialChar } from "@/utils/json/json-parser";
|
|
3
|
+
|
|
4
|
+
export function splitLeaf(leaf: string): [string, string | undefined] {
|
|
5
|
+
const index = leaf.indexOf(":");
|
|
6
|
+
return index < 0
|
|
7
|
+
? [leaf, undefined]
|
|
8
|
+
: [leaf.slice(index + 1), leaf.slice(0, index)];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function formatFloat(value: string): string {
|
|
12
|
+
switch (value.toLowerCase()) {
|
|
13
|
+
case "nan":
|
|
14
|
+
return "float('nan')";
|
|
15
|
+
case "inf":
|
|
16
|
+
case "infinity":
|
|
17
|
+
return "float('inf')";
|
|
18
|
+
case "-inf":
|
|
19
|
+
case "-infinity":
|
|
20
|
+
return "-float('inf')";
|
|
21
|
+
default:
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function formatCollection(
|
|
27
|
+
payload: string,
|
|
28
|
+
kind: "tuple" | "set" | "frozenset",
|
|
29
|
+
): string {
|
|
30
|
+
const items = jsonParseWithSpecialChar<unknown>(payload);
|
|
31
|
+
// Older servers sent Python literals instead of JSON lists.
|
|
32
|
+
if (!Array.isArray(items)) {
|
|
33
|
+
return payload;
|
|
34
|
+
}
|
|
35
|
+
const inner = items.map((item) => formatPython(item, true)).join(", ");
|
|
36
|
+
switch (kind) {
|
|
37
|
+
case "tuple":
|
|
38
|
+
return `(${inner}${items.length === 1 ? "," : ""})`;
|
|
39
|
+
case "set":
|
|
40
|
+
return items.length ? `{${inner}}` : "set()";
|
|
41
|
+
case "frozenset":
|
|
42
|
+
return items.length ? `frozenset({${inner}})` : "frozenset()";
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const formatTuplePayload = (payload: string) =>
|
|
47
|
+
formatCollection(payload, "tuple");
|
|
48
|
+
export const formatSetPayload = (payload: string) =>
|
|
49
|
+
formatCollection(payload, "set");
|
|
50
|
+
export const formatFrozensetPayload = (payload: string) =>
|
|
51
|
+
formatCollection(payload, "frozenset");
|
|
52
|
+
|
|
53
|
+
// Matches the key encoding in marimo/_output/formatters/structures.py.
|
|
54
|
+
export function decodePythonKey(
|
|
55
|
+
key: unknown,
|
|
56
|
+
): { text: string; quoted: boolean } | undefined {
|
|
57
|
+
if (typeof key !== "string") {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
const [payload, mime] = splitLeaf(key);
|
|
61
|
+
switch (mime) {
|
|
62
|
+
case "text/plain+str":
|
|
63
|
+
return { text: payload, quoted: true };
|
|
64
|
+
case "text/plain+int":
|
|
65
|
+
case "text/plain+float":
|
|
66
|
+
return { text: payload, quoted: false };
|
|
67
|
+
case "text/plain+bool":
|
|
68
|
+
return { text: payload === "True" ? "True" : "False", quoted: false };
|
|
69
|
+
case "text/plain+none":
|
|
70
|
+
return { text: "None", quoted: false };
|
|
71
|
+
case "text/plain+tuple":
|
|
72
|
+
return { text: formatTuplePayload(payload), quoted: false };
|
|
73
|
+
case "text/plain+frozenset":
|
|
74
|
+
return { text: formatFrozensetPayload(payload), quoted: false };
|
|
75
|
+
default:
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function formatKey(key: string): string {
|
|
81
|
+
const decoded = decodePythonKey(key);
|
|
82
|
+
if (!decoded) {
|
|
83
|
+
return JSON.stringify(key);
|
|
84
|
+
}
|
|
85
|
+
if (decoded.quoted) {
|
|
86
|
+
return JSON.stringify(decoded.text);
|
|
87
|
+
}
|
|
88
|
+
return key.startsWith("text/plain+float:")
|
|
89
|
+
? formatFloat(decoded.text)
|
|
90
|
+
: decoded.text;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function formatString(value: string): string {
|
|
94
|
+
const [payload, mime] = splitLeaf(value);
|
|
95
|
+
switch (mime) {
|
|
96
|
+
case "text/plain+float":
|
|
97
|
+
return formatFloat(payload);
|
|
98
|
+
case "text/plain+bigint":
|
|
99
|
+
return BigInt(payload).toString();
|
|
100
|
+
case "text/plain+tuple":
|
|
101
|
+
return formatTuplePayload(payload);
|
|
102
|
+
case "text/plain+set":
|
|
103
|
+
return formatSetPayload(payload);
|
|
104
|
+
case "text/plain+frozenset":
|
|
105
|
+
return formatFrozensetPayload(payload);
|
|
106
|
+
case "text/plain":
|
|
107
|
+
case "text/html":
|
|
108
|
+
case "text/markdown":
|
|
109
|
+
return JSON.stringify(payload);
|
|
110
|
+
default:
|
|
111
|
+
return JSON.stringify(
|
|
112
|
+
mime && /^(image|video|application)\//.test(mime) ? payload : value,
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function formatPython(value: unknown, compact = false): string {
|
|
118
|
+
const ancestors = new WeakSet<object>();
|
|
119
|
+
function format(node: unknown, depth: number): string {
|
|
120
|
+
if (node == null) {
|
|
121
|
+
return "None";
|
|
122
|
+
}
|
|
123
|
+
switch (typeof node) {
|
|
124
|
+
case "string":
|
|
125
|
+
return compact ? JSON.stringify(node) : formatString(node);
|
|
126
|
+
case "boolean":
|
|
127
|
+
return node ? "True" : "False";
|
|
128
|
+
case "number":
|
|
129
|
+
return formatFloat(String(node));
|
|
130
|
+
case "bigint":
|
|
131
|
+
return node.toString();
|
|
132
|
+
case "object":
|
|
133
|
+
if (ancestors.has(node)) {
|
|
134
|
+
throw new TypeError("Cannot copy circular data");
|
|
135
|
+
}
|
|
136
|
+
ancestors.add(node);
|
|
137
|
+
try {
|
|
138
|
+
const array = Array.isArray(node);
|
|
139
|
+
const entries = array
|
|
140
|
+
? Array.from(node, (item) => format(item, depth + 1))
|
|
141
|
+
: Object.entries(node).map(
|
|
142
|
+
([key, item]) =>
|
|
143
|
+
`${compact ? JSON.stringify(key) : formatKey(key)}: ${format(item, depth + 1)}`,
|
|
144
|
+
);
|
|
145
|
+
const [open, close] = array ? ["[", "]"] : ["{", "}"];
|
|
146
|
+
if (entries.length === 0) {
|
|
147
|
+
return open + close;
|
|
148
|
+
}
|
|
149
|
+
if (compact) {
|
|
150
|
+
return `${open}${entries.join(", ")}${close}`;
|
|
151
|
+
}
|
|
152
|
+
const indent = " ".repeat(depth);
|
|
153
|
+
return `${open}\n${indent} ${entries.join(`,\n${indent} `)}\n${indent}${close}`;
|
|
154
|
+
} finally {
|
|
155
|
+
ancestors.delete(node);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
default:
|
|
159
|
+
return "None";
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return format(value, 0);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function getCopyValue(value: unknown): string {
|
|
166
|
+
return formatPython(value);
|
|
167
|
+
}
|