@marimo-team/islands 0.23.7-dev4 → 0.23.7-dev43
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-PmilQqXR.js → ConnectedDataExplorerComponent-2lBNiUv6.js} +14 -14
- package/dist/{ErrorBoundary-Da4UeYxT.js → ErrorBoundary-D3wrPNma.js} +1 -1
- package/dist/{any-language-editor-BVR0l12r.js → any-language-editor-VWs_7v27.js} +15 -15
- package/dist/apl-Bdc61P1y.js +4 -0
- package/dist/{arc-CHF8PiiF.js → arc-DfkSnvZm.js} +2 -2
- package/dist/{architecture-7HQA4BMR-D0JB_3hE.js → architecture-7HQA4BMR-CS9jOrqM.js} +1 -1
- package/dist/{architectureDiagram-VXUJARFQ-BXQEUDtK.js → architectureDiagram-VXUJARFQ-CXVJxFhH.js} +16 -16
- package/dist/asciiarmor-DVRHDGzT.js +4 -0
- package/dist/asn1-BmuKfkfu.js +4 -0
- package/dist/assets/__vite-browser-external-CAdMKBac.js +1 -0
- package/dist/assets/worker-CpBbwbQo.js +73 -0
- package/dist/{blockDiagram-VD42YOAC-DhJe-Y9i.js → blockDiagram-VD42YOAC-DGDaxR8I.js} +11 -11
- package/dist/brainfuck-DPrRpyTV.js +4 -0
- package/dist/{button-CA5pI2YF.js → button-Dj4BTre0.js} +5 -0
- package/dist/{c4Diagram-YG6GDRKO-9dSfzOFR.js → c4Diagram-YG6GDRKO-C2hc6ne8.js} +5 -5
- package/dist/{capabilities-6laDasij.js → capabilities-C9rrYCzf.js} +1 -1
- package/dist/{channel-MqYIiKgS.js → channel-BBoIVUrJ.js} +1 -1
- package/dist/{chat-ui-B-gbqk_F.js → chat-ui-CufH8sfF.js} +626 -234
- package/dist/{check-CFM2mVDr.js → check-BcUIXnUT.js} +1 -1
- package/dist/{chunk-4BX2VUAB-BwfrWBqN.js → chunk-4BX2VUAB-CzXltWHN.js} +1 -1
- package/dist/{chunk-55IACEB6-D8THf2mi.js → chunk-55IACEB6-B-1mjMMC.js} +1 -1
- package/dist/{chunk-5FQGJX7Z-CO1e63h_.js → chunk-5FQGJX7Z-BOg95xG5.js} +2 -2
- package/dist/{chunk-ABZYJK2D-BrBb_0yY.js → chunk-ABZYJK2D-D0cLy8Bb.js} +2 -2
- package/dist/{chunk-ATLVNIR6-D-0XqNah.js → chunk-ATLVNIR6-BXsEjlHF.js} +2 -2
- package/dist/{chunk-B4BG7PRW-8iRKvugR.js → chunk-B4BG7PRW-Q1usn6T3.js} +7 -7
- package/dist/{chunk-CVBHYZKI-B6xhgaBd.js → chunk-CVBHYZKI-B_c5YBcW.js} +1 -1
- package/dist/{chunk-DI55MBZ5-C0_2D4m4.js → chunk-DI55MBZ5-D1qLYNrb.js} +6 -6
- package/dist/{chunk-EXTU4WIE-Jiw9ca1u.js → chunk-EXTU4WIE-BKNXdLmD.js} +2 -2
- package/dist/{chunk-FMBD7UC4-CHdus51S.js → chunk-FMBD7UC4-Ie8M9q0W.js} +1 -1
- package/dist/{chunk-HN2XXSSU-2Vfbq-kU.js → chunk-HN2XXSSU-E3n-Ys7Z.js} +1 -1
- package/dist/{chunk-JA3XYJ7Z-6wbaigKe.js → chunk-JA3XYJ7Z-D6c6cOBG.js} +3 -3
- package/dist/{chunk-JZLCHNYA-_rfptlUP.js → chunk-JZLCHNYA-BvsPHJmL.js} +6 -6
- package/dist/{chunk-MI3HLSF2-Do0-KRc0.js → chunk-MI3HLSF2-CUYEasXO.js} +1 -1
- package/dist/{chunk-N4CR4FBY-DIZG9dVD.js → chunk-N4CR4FBY-8ycT-O9a.js} +7 -7
- package/dist/{chunk-QN33PNHL-Cc64y40m.js → chunk-QN33PNHL-Bb-eUBW3.js} +2 -2
- package/dist/{chunk-QXUST7PY-BDG0-0Or.js → chunk-QXUST7PY-DV8yRwBd.js} +10 -10
- package/dist/{chunk-QZHKN3VN-B_Mdb8GC.js → chunk-QZHKN3VN-DRjXVwuJ.js} +1 -1
- package/dist/{chunk-S3R3BYOJ-DphMP0FA.js → chunk-S3R3BYOJ-mQeCz5CE.js} +4 -4
- package/dist/{chunk-TZMSLE5B-C9LUoYkc.js → chunk-TZMSLE5B-BqW10dHe.js} +3 -3
- package/dist/classDiagram-2ON5EDUG--Yh__LHb.js +30 -0
- package/dist/classDiagram-v2-WZHVMYZB-BC7X7Xtc.js +30 -0
- package/dist/{clike-sBZrGeF8.js → clike-DTxNUn7l.js} +1 -1
- package/dist/clojure-DEttQW5T.js +4 -0
- package/dist/cmake-jNlx_DaM.js +4 -0
- package/dist/cobol-BvvIm5MJ.js +4 -0
- package/dist/{code-block-37QAKDTI-0JNwiPGv.js → code-block-37QAKDTI-BsGy1AOJ.js} +1 -1
- package/dist/{code-visibility-zYxaYsuj.js → code-visibility-Cu7CFm0r.js} +573 -585
- package/dist/coffeescript-DTpBMyFU.js +4 -0
- package/dist/commonlisp-BmmUG8jb.js +4 -0
- package/dist/{copy-TGGAUEWp.js → copy-DLf4aN7I.js} +2 -2
- package/dist/{cose-bilkent-S5V4N54A-DXHZkJKX.js → cose-bilkent-S5V4N54A-kjoZoid4.js} +2 -2
- package/dist/crystal-BxyqmEWC.js +4 -0
- package/dist/css-BGoCtuG3.js +4 -0
- package/dist/cypher-BmCbdl3u.js +4 -0
- package/dist/d-CFrlbjZt.js +4 -0
- package/dist/{dagre-6UL2VRFP-tH87fkPA.js → dagre-6UL2VRFP-DRBWoQUw.js} +10 -10
- package/dist/{data-grid-overlay-editor-CWUN78-s.js → data-grid-overlay-editor-efe5ZagF.js} +2 -2
- package/dist/{diagram-PSM6KHXK-2VjPSCDn.js → diagram-PSM6KHXK-H66ATWP2.js} +18 -18
- package/dist/{diagram-QEK2KX5R-CiLmNyta.js → diagram-QEK2KX5R-DItl5Wns.js} +14 -14
- package/dist/{diagram-S2PKOQOG-Zha_1CLx.js → diagram-S2PKOQOG-CtuW_ZuL.js} +14 -14
- package/dist/diff-D6XwL6P8.js +4 -0
- package/dist/{dist-Brkazupz.js → dist--sWVZwjW.js} +1 -1
- package/dist/{dist-FN0ZA_8F.js → dist-21ButRCu.js} +1 -1
- package/dist/{dist-BetEKbPG.js → dist-B8RaFTRF.js} +1 -1
- package/dist/dist-BoHGySTM.js +5 -0
- package/dist/dist-ByAz19Qc.js +5 -0
- package/dist/dist-C93EysN4.js +5 -0
- package/dist/{dist-BHnX0ia_.js → dist-CY-lVor6.js} +1 -1
- package/dist/{dist-YP-G7W0f.js → dist-CYDuv4bR.js} +1 -1
- package/dist/{dist-CMjD5MQb.js → dist-Cfo5EE2t.js} +1 -1
- package/dist/dist-CjivSDvN.js +5 -0
- package/dist/dist-Cqwx-MH7.js +5 -0
- package/dist/{dist-ESg7xyoD.js → dist-D3ZI9nhS.js} +2 -2
- package/dist/{dist-DkC6YEo0.js → dist-DMZNjfX4.js} +1 -1
- package/dist/{dist-ChC1BhqM.js → dist-DbpcoFAV.js} +1 -1
- package/dist/dist-FUNenbiQ.js +5 -0
- package/dist/{dist-BEOU2g1b.js → dist-zhSud5X3.js} +1 -1
- package/dist/{dockerfile-COvlVLcE.js → dockerfile-twL37N91.js} +1 -1
- package/dist/dtd-Bw0lRN0-.js +4 -0
- package/dist/dylan-B55eBHTt.js +4 -0
- package/dist/ecl-C8G4p0wn.js +4 -0
- package/dist/eiffel-BmH46VJl.js +4 -0
- package/dist/elm-DhzeFqkl.js +4 -0
- package/dist/{erDiagram-Q2GNP2WA-biHZS05w.js → erDiagram-Q2GNP2WA--19X2kU5.js} +14 -14
- package/dist/erlang-CY-wdlsU.js +4 -0
- package/dist/{error-banner-DnBPzEWg.js → error-banner-CVkfBUT3.js} +2 -2
- package/dist/{esm-Dd1z1auZ.js → esm-CWp0KQeK.js} +1 -1
- package/dist/{esm-CYEyrE3Y.js → esm-DjNnlmpf.js} +96 -96
- package/dist/{extends-CzJgxo2J.js → extends-vAi97cpa.js} +4 -4
- package/dist/{factor-C2GT7jfQ.js → factor-CajWS6mS.js} +1 -1
- package/dist/factor-DWkgl0xw.js +4 -0
- package/dist/{flowDiagram-NV44I4VS-CWWlUpBR.js → flowDiagram-NV44I4VS-DQmWlo7f.js} +16 -16
- package/dist/{formats-CgaK7Gmx.js → formats-CpgZM9BM.js} +3 -3
- package/dist/forth-Cij_ie2t.js +4 -0
- package/dist/fortran-Br3X9cfm.js +4 -0
- package/dist/{ganttDiagram-JELNMOA3-D7B2c4Z9.js → ganttDiagram-JELNMOA3-BOGXJ8Lk.js} +7 -7
- package/dist/gas-DYsGcMN2.js +4 -0
- package/dist/gherkin-eVgXQ0fQ.js +4 -0
- package/dist/{gitGraph-G5XIXVHT-BdepdFa_.js → gitGraph-G5XIXVHT-DGlbae5m.js} +1 -1
- package/dist/{gitGraphDiagram-V2S2FVAM-CtLvNR1S.js → gitGraphDiagram-V2S2FVAM-DjzxfF0P.js} +14 -14
- package/dist/{glide-data-editor-CvlvtPWJ.js → glide-data-editor-BK9s_dqy.js} +13 -13
- package/dist/groovy-_NFHIXG7.js +4 -0
- package/dist/haskell-D6xNG_bH.js +4 -0
- package/dist/haxe-sU_rzAwn.js +5 -0
- package/dist/{html-to-image-hMMPiNe_.js → html-to-image-DxWM1HVj.js} +2395 -2301
- package/dist/idl-Ds_VbrUx.js +4 -0
- package/dist/{info-VBDWY6EO--JNA2rNu.js → info-VBDWY6EO-D2lvLLw5.js} +1 -1
- package/dist/{infoDiagram-HS3SLOUP-BbZyOxsP.js → infoDiagram-HS3SLOUP-ChNufFsP.js} +12 -12
- package/dist/{input-BAOe64zx.js → input-Cc1Vvw9A.js} +6 -6
- package/dist/javascript-CztfIl0i.js +4 -0
- package/dist/{journeyDiagram-XKPGCS4Q-BU2mjjzl.js → journeyDiagram-XKPGCS4Q-BO_O4Ij1.js} +6 -6
- package/dist/julia-DDv40QMV.js +4 -0
- package/dist/{kanban-definition-3W4ZIXB7-BlmczUuw.js → kanban-definition-3W4ZIXB7-CPpiiiWk.js} +11 -11
- package/dist/{katex-qPqrBHZ8.js → katex-9-9QRhxz.js} +1 -1
- package/dist/{label-BCWi-Oqu.js → label-BLqV33b1.js} +2 -2
- package/dist/{line-BWRi3U3S.js → line-C5s_12ee.js} +3 -3
- package/dist/{linear-DnHwODZa.js → linear-2NnK4cxi.js} +2 -2
- package/dist/livescript-BEOngLLc.js +4 -0
- package/dist/{loader-BvW0-YWZ.js → loader-Dr8Qem8p.js} +1 -1
- package/dist/lua-9-7BhQ4Y.js +4 -0
- package/dist/main.js +1175 -1142
- package/dist/mathematica-DDa0Pfxm.js +4 -0
- package/dist/mbox-iO03mmoE.js +4 -0
- package/dist/{mermaid-4DMBBIKO-CG1ECj5W.js → mermaid-4DMBBIKO-B7VQMwJx.js} +1 -1
- package/dist/{mermaid-CEbzCxCc.js → mermaid-DO-Daq7u.js} +46 -46
- package/dist/{mermaid-parser.core-CleJseNW.js → mermaid-parser.core-DreccfmS.js} +7 -7
- package/dist/{mhchem-BwoRNwg_.js → mhchem-yiCCuiEF.js} +1 -1
- package/dist/{mindmap-definition-VGOIOE7T-CcSYqYP9.js → mindmap-definition-VGOIOE7T-CC1_Vl0f.js} +13 -13
- package/dist/mirc-C9z5LT4X.js +4 -0
- package/dist/mllike-jGJbdm_C.js +6 -0
- package/dist/modelica-DzF7oIEL.js +4 -0
- package/dist/mscgen-DB-u125o.js +6 -0
- package/dist/mumps-CRTFHhzh.js +4 -0
- package/dist/nsis-C4NPTuox.js +4 -0
- package/dist/{nsis-B5K1qoyo.js → nsis-ClF3r5Tr.js} +1 -1
- package/dist/ntriples-BCOoGph1.js +4 -0
- package/dist/{number-overlay-editor-_GnlYFHC.js → number-overlay-editor-CpKi64Fy.js} +1 -1
- package/dist/octave-DTwNlazz.js +4 -0
- package/dist/{ordinal-2jIulmcR.js → ordinal-B43ZeR68.js} +1 -1
- package/dist/oz-DD38AzSz.js +4 -0
- package/dist/{packet-DYOGHKS2-CBxXGWNr.js → packet-DYOGHKS2-CmWtF3uO.js} +1 -1
- package/dist/pascal-BohSp9jV.js +4 -0
- package/dist/perl-f5OutoPM.js +4 -0
- package/dist/{pie-VRWISCQL-Bmdnqjip.js → pie-VRWISCQL-B6u8vus8.js} +1 -1
- package/dist/{pieDiagram-ADFJNKIX-DNyLF5H2.js → pieDiagram-ADFJNKIX-Di34MOFQ.js} +19 -19
- package/dist/pig-Dv7wSmHb.js +4 -0
- package/dist/powershell-rYgjKB39.js +4 -0
- package/dist/{process-output-Bza_GK7Q.js → process-output-DBYxXdrN.js} +30 -25
- package/dist/properties-BFUNLRDN.js +4 -0
- package/dist/protobuf-B9QJQPPv.js +4 -0
- package/dist/{pug-tjbzJCFk.js → pug-B_rby2yb.js} +1 -1
- package/dist/pug-DzvWpaMC.js +4 -0
- package/dist/puppet-B_K-n_xK.js +4 -0
- package/dist/python-CAiFcaA2.js +4 -0
- package/dist/q-cLeFIBLK.js +4 -0
- package/dist/{quadrantDiagram-AYHSOK5B-rXwjifrj.js → quadrantDiagram-AYHSOK5B-B9kVk1ny.js} +3 -3
- package/dist/r-04Y-Wco3.js +4 -0
- package/dist/{radar-ZZBFDIW7-BmCWDffL.js → radar-ZZBFDIW7-XAmXSa8s.js} +1 -1
- package/dist/{react-vega-B-rkEqtS.js → react-vega-Cavbrg4l.js} +1 -1
- package/dist/{react-vega-k9ODWPlI.js → react-vega-Dh6-UKKe.js} +13 -13
- package/dist/{requirementDiagram-UZGBJVZJ-DBdrMVbs.js → requirementDiagram-UZGBJVZJ-BxGfGYEx.js} +13 -13
- package/dist/{reveal-component-h0_DDpxE.js → reveal-component-CSA3g_zD.js} +10 -10
- package/dist/rpm-FUdrIia9.js +5 -0
- package/dist/ruby-DMjFXuEW.js +4 -0
- package/dist/{sankeyDiagram-TZEHDZUN-CxmzalGv.js → sankeyDiagram-TZEHDZUN-D09PBJ-n.js} +4 -4
- package/dist/sas-DzHZxjXK.js +4 -0
- package/dist/scheme-DxHd_Rb9.js +4 -0
- package/dist/semaphore-CNDGTzkX.js +46 -0
- package/dist/{sequenceDiagram-WL72ISMW-CVCDsJ9h.js → sequenceDiagram-WL72ISMW-t_Dpemj0.js} +7 -7
- package/dist/shell-C8Kwypgf.js +4 -0
- package/dist/sieve-DdyqOKXZ.js +4 -0
- package/dist/smalltalk-pB7X1D9y.js +4 -0
- package/dist/sparql-NhBO6oOa.js +4 -0
- package/dist/{spec-DSIuqd3f.js → spec-hVaaZsY5.js} +4 -4
- package/dist/{src-BY0BGg6V.js → src-Bf2iLOlr.js} +1 -1
- package/dist/{stateDiagram-FKZM4ZOC-D_2djEhW.js → stateDiagram-FKZM4ZOC-B18gTP_j.js} +16 -16
- package/dist/stateDiagram-v2-4FDKWEC3-B6e_t14A.js +29 -0
- package/dist/{step-DGAGWg3y.js → step-CWipAYTY.js} +1 -1
- package/dist/{strings-B_FOH6eV.js → strings-BiIhGaI8.js} +4 -4
- package/dist/style.css +1 -1
- package/dist/stylus-SfWSnzPv.js +4 -0
- package/dist/swift-jRPdq2zR.js +4 -0
- package/dist/{swiper-component-KkEVUDd3.js → swiper-component-DlD2GU2g.js} +2 -2
- package/dist/tcl-_hpTHGX3.js +4 -0
- package/dist/textile-C9h8slqH.js +4 -0
- package/dist/{time-CMdrp3hw.js → time-C1SGcFMH.js} +2 -2
- package/dist/{timeline-definition-IT6M3QCI-E4NzxCs3.js → timeline-definition-IT6M3QCI-DJnh1ks5.js} +3 -3
- package/dist/{toDate-CHtl9vts.js → toDate-CJWlVNGD.js} +3 -3
- package/dist/toml-DWvtinD4.js +4 -0
- package/dist/{tooltip-B0mtKTXm.js → tooltip-DRaMBu06.js} +3 -3
- package/dist/{treemap-GDKQZRPO-CoKHPxa7.js → treemap-GDKQZRPO-Du95DV6u.js} +1 -1
- package/dist/troff-Dwo_A0y7.js +4 -0
- package/dist/ttcn-V--CPFKq.js +4 -0
- package/dist/ttcn-cfg-CPSMchTG.js +4 -0
- package/dist/turtle-B4rPGBWu.js +4 -0
- package/dist/{types-DBtDeUKD.js → types-Dzuoc3LN.js} +1 -1
- package/dist/{useAsyncData-B6hCGywC.js → useAsyncData-C56Khv_R.js} +1 -1
- package/dist/{useDateFormatter-B3mCQMP3.js → useDateFormatter-B_9k85Ex.js} +2 -2
- package/dist/{useDeepCompareMemoize-CmwDuYUH.js → useDeepCompareMemoize-Dt98v2ua.js} +1 -1
- package/dist/{useIframeCapabilities-DbdLoEDm.js → useIframeCapabilities-BkYHTrss.js} +1 -1
- package/dist/{useLifecycle-CjMjllqy.js → useLifecycle-BF6-z62y.js} +3 -3
- package/dist/{useTheme-CByZUW0p.js → useTheme-DykuNHR2.js} +2 -2
- package/dist/vb-DaMBBd4j.js +4 -0
- package/dist/vbscript-BMJQqcE2.js +4 -0
- package/dist/{vega-component-CC8TqWWV.js → vega-component-BtvQ-Kc4.js} +26 -24
- package/dist/velocity-CGq2QRq2.js +4 -0
- package/dist/verilog-CUNo8F5u.js +4 -0
- package/dist/vhdl-CCzA0msW.js +4 -0
- package/dist/webidl-CqIMDIBL.js +4 -0
- package/dist/xquery-XC5Kbr-1.js +4 -0
- package/dist/{xychartDiagram-PRI3JC2R-CuxTvjw5.js → xychartDiagram-PRI3JC2R-Dk2d_bX0.js} +10 -10
- package/dist/yacas-CGOv7Dzy.js +4 -0
- package/dist/z80-CXhVmi-f.js +4 -0
- package/dist/{zod-BxdsqRPd.js → zod-BWkcDORu.js} +1 -1
- package/package.json +3 -3
- package/src/components/chat/chat-components.tsx +47 -0
- package/src/components/chat/chat-display.tsx +41 -7
- package/src/components/chat/chat-panel.tsx +37 -10
- package/src/components/chat/chat-utils.ts +42 -20
- package/src/components/chat/reasoning-accordion.tsx +14 -3
- package/src/components/chat/tool-call/shared.ts +13 -0
- package/src/components/chat/tool-call/tool-approval-card.tsx +62 -0
- package/src/components/chat/tool-call/tool-args.tsx +26 -0
- package/src/components/chat/tool-call/tool-call-view.tsx +99 -0
- package/src/components/chat/tool-call/tool-error-card.tsx +81 -0
- package/src/components/chat/tool-call/tool-history-row.tsx +153 -0
- package/src/components/chat/tool-call/tool-result.tsx +101 -0
- package/src/components/data-table/TableTopBar.tsx +5 -1
- package/src/components/data-table/data-table.tsx +5 -0
- package/src/components/data-table/download-policy/atoms.ts +10 -0
- package/src/components/data-table/export-actions.tsx +31 -4
- package/src/components/editor/actions/pair-with-agent-modal.tsx +140 -49
- package/src/components/editor/actions/useNotebookActions.tsx +3 -1
- package/src/components/editor/app-container.tsx +7 -1
- package/src/components/editor/chrome/panels/context-aware-panel/context-aware-panel.tsx +10 -2
- package/src/components/editor/chrome/wrapper/app-chrome.tsx +1 -0
- package/src/components/editor/chrome/wrapper/footer.tsx +4 -1
- package/src/components/editor/chrome/wrapper/panels.tsx +4 -1
- package/src/components/editor/chrome/wrapper/sidebar.tsx +4 -1
- package/src/components/editor/controls/Controls.tsx +11 -3
- package/src/components/editor/file-tree/requesting-tree.tsx +27 -25
- package/src/components/editor/file-tree/upload.tsx +23 -24
- package/src/components/editor/header/__tests__/status.test.tsx +108 -0
- package/src/components/editor/header/status.tsx +44 -10
- package/src/components/editor/navigation/__tests__/clipboard.test.ts +106 -0
- package/src/components/editor/navigation/__tests__/navigation.test.ts +70 -0
- package/src/components/editor/navigation/clipboard.ts +99 -25
- package/src/components/editor/navigation/navigation.ts +15 -1
- package/src/components/editor/notebook-cell.tsx +3 -0
- package/src/components/home/components.tsx +6 -0
- package/src/components/pages/run-page.tsx +4 -1
- package/src/core/ai/tools/__tests__/registry.test.ts +10 -12
- package/src/core/ai/tools/registry.ts +9 -5
- package/src/core/cells/__tests__/cells.test.ts +187 -0
- package/src/core/cells/__tests__/pending-cut-service.test.tsx +123 -0
- package/src/core/cells/cells.ts +102 -17
- package/src/core/cells/document-changes.ts +6 -1
- package/src/core/cells/pending-cut-service.ts +55 -0
- package/src/core/cells/utils.ts +11 -0
- package/src/core/codemirror/cells/extensions.ts +10 -0
- package/src/core/codemirror/markdown/__tests__/commands.test.ts +3 -3
- package/src/core/codemirror/markdown/commands.ts +1 -2
- package/src/core/edit-app.tsx +2 -1
- package/src/core/hotkeys/hotkeys.ts +5 -0
- package/src/core/islands/worker/worker.tsx +3 -2
- package/src/core/network/requests-network.ts +21 -3
- package/src/core/network/types.ts +12 -1
- package/src/core/run-app.tsx +2 -1
- package/src/core/wasm/__tests__/utils.test.ts +34 -0
- package/src/core/wasm/bridge.ts +14 -1
- package/src/core/wasm/utils.ts +14 -0
- package/src/core/wasm/worker/bootstrap.ts +3 -2
- package/src/core/wasm/worker/worker.ts +3 -2
- package/src/core/websocket/__tests__/useMarimoKernelConnection.hook.test.tsx +155 -0
- package/src/core/websocket/__tests__/useMarimoKernelConnection.test.ts +137 -0
- package/src/core/websocket/transports/basic.ts +2 -0
- package/src/core/websocket/transports/transport.ts +1 -0
- package/src/core/websocket/useMarimoKernelConnection.tsx +130 -55
- package/src/core/websocket/useWebSocket.tsx +5 -2
- package/src/css/app/Cell.css +10 -0
- package/src/plugins/impl/DataTablePlugin.tsx +12 -0
- package/src/plugins/impl/TabsPlugin.tsx +35 -7
- package/src/plugins/impl/__tests__/TabsPlugin.test.tsx +154 -0
- package/src/plugins/impl/data-frames/DataFramePlugin.tsx +6 -0
- package/src/plugins/impl/vega/resolve-data.ts +8 -1
- package/src/utils/__tests__/id-tree.test.ts +71 -0
- package/src/utils/__tests__/semaphore.test.ts +218 -0
- package/src/utils/fileToBase64.ts +8 -7
- package/src/utils/id-tree.tsx +89 -0
- package/src/utils/semaphore.ts +88 -0
- package/dist/apl-BKoVld9y.js +0 -4
- package/dist/asciiarmor-DQrKIjoo.js +0 -4
- package/dist/asn1-BZvnj0dq.js +0 -4
- package/dist/assets/__vite-browser-external-rrUYDKRl.js +0 -1
- package/dist/assets/worker-Bfy15ViQ.js +0 -73
- package/dist/brainfuck-D558nlUv.js +0 -4
- package/dist/classDiagram-2ON5EDUG-CBHMR6ZU.js +0 -30
- package/dist/classDiagram-v2-WZHVMYZB-BsUtUGM_.js +0 -30
- package/dist/clojure-Cq8mTSrE.js +0 -4
- package/dist/cmake-D8HCovWK.js +0 -4
- package/dist/cobol-UolN-9iU.js +0 -4
- package/dist/coffeescript-VdNuWrt5.js +0 -4
- package/dist/commonlisp-ALX7fpDc.js +0 -4
- package/dist/crystal-PbyO9Q_s.js +0 -4
- package/dist/css-DFklJkr_.js +0 -4
- package/dist/cypher-BifNeYlv.js +0 -4
- package/dist/d-BA-JP4PJ.js +0 -4
- package/dist/diff-CtkDpav4.js +0 -4
- package/dist/dist-BuBwsFva.js +0 -5
- package/dist/dist-BzmEQ9u7.js +0 -5
- package/dist/dist-Cih01ssx.js +0 -5
- package/dist/dist-CqfONiY9.js +0 -5
- package/dist/dist-D0iD0Fi9.js +0 -5
- package/dist/dist-DtNLXm8d.js +0 -5
- package/dist/dtd-DW3_UFEG.js +0 -4
- package/dist/dylan-pDhodO2N.js +0 -4
- package/dist/ecl-BJT8-YD7.js +0 -4
- package/dist/eiffel-Dmns-9vS.js +0 -4
- package/dist/elm-Da4sO4Bz.js +0 -4
- package/dist/erlang-C-zBsDi7.js +0 -4
- package/dist/factor-4xPWlWB5.js +0 -4
- package/dist/forth-l-c75zSd.js +0 -4
- package/dist/fortran-DIujSODW.js +0 -4
- package/dist/gas-CXnG5g_b.js +0 -4
- package/dist/gherkin-VPeqd4-X.js +0 -4
- package/dist/groovy-CphhZQgg.js +0 -4
- package/dist/haskell-CCvlS5Iq.js +0 -4
- package/dist/haxe-C_bi66fP.js +0 -5
- package/dist/idl-1DcP4Dm8.js +0 -4
- package/dist/javascript-DUIGhBvO.js +0 -4
- package/dist/julia-Cs2G4PQi.js +0 -4
- package/dist/livescript-DMtVFaAN.js +0 -4
- package/dist/lua-BAoLtdJg.js +0 -4
- package/dist/mathematica-C_NoFtbo.js +0 -4
- package/dist/mbox-DcFJFYrH.js +0 -4
- package/dist/mirc-71dccf_u.js +0 -4
- package/dist/mllike-CWcOFVDq.js +0 -6
- package/dist/modelica-Ape2VXxx.js +0 -4
- package/dist/mscgen-Cc6TwbSN.js +0 -6
- package/dist/mumps-h-ZbdkJ9.js +0 -4
- package/dist/nsis-C0p3m7JW.js +0 -4
- package/dist/ntriples-c9lEeT5w.js +0 -4
- package/dist/octave-DzEgB_74.js +0 -4
- package/dist/oz-CAxvHkyQ.js +0 -4
- package/dist/pascal-BJzu1sgP.js +0 -4
- package/dist/perl--IrOzZ2Z.js +0 -4
- package/dist/pig-CiBKKNhC.js +0 -4
- package/dist/powershell-KY0j6Qop.js +0 -4
- package/dist/properties-BW8q3ziV.js +0 -4
- package/dist/protobuf-BGaeuTGV.js +0 -4
- package/dist/pug-DjOKK-4J.js +0 -4
- package/dist/puppet-DWm2o6zX.js +0 -4
- package/dist/python-Bp2gezZy.js +0 -4
- package/dist/q-DljPshos.js +0 -4
- package/dist/r-BajPMnEu.js +0 -4
- package/dist/rpm-BKx-ZZ62.js +0 -5
- package/dist/ruby-DJq_HNKc.js +0 -4
- package/dist/sas-WANvpcOU.js +0 -4
- package/dist/scheme-CliBbhGF.js +0 -4
- package/dist/shell-BwhrNUvM.js +0 -4
- package/dist/sieve-BIVePvMp.js +0 -4
- package/dist/smalltalk-D6G48JmY.js +0 -4
- package/dist/sparql-jjc3BmEP.js +0 -4
- package/dist/stateDiagram-v2-4FDKWEC3-Cv9Av10H.js +0 -29
- package/dist/stylus-WPBPQ4PE.js +0 -4
- package/dist/swift-O1Qy6iCm.js +0 -4
- package/dist/tcl-BAFdhvsi.js +0 -4
- package/dist/textile-DFuzhNLG.js +0 -4
- package/dist/toml-DRSTeely.js +0 -4
- package/dist/troff-B_ZjwBW0.js +0 -4
- package/dist/ttcn-CAyiB3ic.js +0 -4
- package/dist/ttcn-cfg-BS5_BGBJ.js +0 -4
- package/dist/turtle-CUBEDy3E.js +0 -4
- package/dist/vb-DY9S6-U2.js +0 -4
- package/dist/vbscript-gaHC39Jq.js +0 -4
- package/dist/velocity-TfCOtJZ_.js +0 -4
- package/dist/verilog-c2JOX8mv.js +0 -4
- package/dist/vhdl-dHBirRiO.js +0 -4
- package/dist/webidl-Bauj-i07.js +0 -4
- package/dist/xquery-CtaEAOt8.js +0 -4
- package/dist/yacas-BZ85agQP.js +0 -4
- package/dist/z80-hCgR-L4U.js +0 -4
- package/src/components/chat/tool-call-accordion.tsx +0 -247
- /package/dist/{ImageComparisonComponent-DaocPIse.js → ImageComparisonComponent-CNHIsPDj.js} +0 -0
- /package/dist/{Plot-PIeIvFnD.js → Plot-4wn-lMVn.js} +0 -0
- /package/dist/{apl-Dt8GMXYg.js → apl-BCgCq9iM.js} +0 -0
- /package/dist/{array-B-MVxRIF.js → array-tvvEqPy7.js} +0 -0
- /package/dist/{asciiarmor-CitDQ85h.js → asciiarmor-BtqU-KJQ.js} +0 -0
- /package/dist/{asn1-abrf9SMK.js → asn1-Dmb-dTMx.js} +0 -0
- /package/dist/{asterisk-BUZwqih-.js → asterisk-DaVJJDnV.js} +0 -0
- /package/dist/{brainfuck-BL-Boof0.js → brainfuck-C1HoZKlE.js} +0 -0
- /package/dist/{chunk-4F5CHEZ2-C6tO9vjs.js → chunk-4F5CHEZ2-BZq7Kom7.js} +0 -0
- /package/dist/{chunk-B2363JML-Ds8wZXyP.js → chunk-B2363JML-D9-XOau1.js} +0 -0
- /package/dist/{chunk-DR5Q36YT-CP69aZS_.js → chunk-DR5Q36YT-BflwErH1.js} +0 -0
- /package/dist/{chunk-FRFDVMJY-BgQv1HBE.js → chunk-FRFDVMJY-BSBUAX7r.js} +0 -0
- /package/dist/{chunk-PL6DKKU2-DHfTUHy8.js → chunk-PL6DKKU2-B0MTXvyc.js} +0 -0
- /package/dist/{chunk-SJTYNZTY-Diciw4sx.js → chunk-SJTYNZTY-CEG4F0pB.js} +0 -0
- /package/dist/{chunk-TQ3KTPDO-CQfP9npd.js → chunk-TQ3KTPDO-DiCtqVSi.js} +0 -0
- /package/dist/{chunk-UMXZTB3W-MSKeGL7W.js → chunk-UMXZTB3W-97iS1iEl.js} +0 -0
- /package/dist/{click-outside-container-BZgN7xS_.js → click-outside-container-BDd67_1U.js} +0 -0
- /package/dist/{clike-RWg7anhx.js → clike-CdT0yHjt.js} +0 -0
- /package/dist/{clojure-DaojKHow.js → clojure-CdyrCpUv.js} +0 -0
- /package/dist/{cmake-DN-_v0XE.js → cmake-BFlPxym7.js} +0 -0
- /package/dist/{cobol-C3VpMyux.js → cobol-CcJXewp8.js} +0 -0
- /package/dist/{coffeescript-DIkz3Tbt.js → coffeescript-DnKuIKRo.js} +0 -0
- /package/dist/{colors-Cn2p_FA3.js → colors-CQAOa8cK.js} +0 -0
- /package/dist/{common-keywords-hbLeU7VU.js → common-keywords-FBrXPTcz.js} +0 -0
- /package/dist/{commonlisp-CB1boOiP.js → commonlisp-B-kok83Z.js} +0 -0
- /package/dist/{crystal-DI2oCml6.js → crystal-FYRYjI1I.js} +0 -0
- /package/dist/{css-BdEVwQDV.js → css-B45lc2V3.js} +0 -0
- /package/dist/{cypher-BNHToqxU.js → cypher-DZMLyVY_.js} +0 -0
- /package/dist/{cytoscape.esm-WbbDoCfu.js → cytoscape.esm-ayF70frT.js} +0 -0
- /package/dist/{d-D7we7I1b.js → d-x-VVT4o9.js} +0 -0
- /package/dist/{diff-Cia6fzjN.js → diff-Dxe2mpXk.js} +0 -0
- /package/dist/{dist-BK-3fF4P.js → dist-B4LJpMEg.js} +0 -0
- /package/dist/{dist-CxdUraQr.js → dist-B507mf_I.js} +0 -0
- /package/dist/{dist-C89sHDXk.js → dist-BGdYVvOu.js} +0 -0
- /package/dist/{dist-DquyVv5H.js → dist-BNyrZfqT.js} +0 -0
- /package/dist/{dist-Zn0KNbo9.js → dist-Bc5pmZIw.js} +0 -0
- /package/dist/{dist-C-J0pt5p.js → dist-BvCfQQQE.js} +0 -0
- /package/dist/{dist-D9r7Cmw7.js → dist-C2ej4eOH.js} +0 -0
- /package/dist/{dist-HVuryI1a.js → dist-C34oIrQ9.js} +0 -0
- /package/dist/{dist-CGLzXdrt.js → dist-CDFZi-QD.js} +0 -0
- /package/dist/{dist-C9fmTOin.js → dist-CYEylvZA.js} +0 -0
- /package/dist/{dist-DadjmS-4.js → dist-DJ6zJQZ4.js} +0 -0
- /package/dist/{dist-CtCY55Jf.js → dist-Dh3wkoyH.js} +0 -0
- /package/dist/{dist-C474qFoq.js → dist-Dhk6FMb0.js} +0 -0
- /package/dist/{dist-DZjQ_MBo.js → dist-KnujRhFL.js} +0 -0
- /package/dist/{dist-CinA9Enb.js → dist-WdPUFc56.js} +0 -0
- /package/dist/{dist-DBLeRrPp.js → dist-t_qL7eB8.js} +0 -0
- /package/dist/{dist-CyFFzJTb.js → dist-usPCDYx8.js} +0 -0
- /package/dist/{dtd-H4Hubdwp.js → dtd-C9VM_Wfu.js} +0 -0
- /package/dist/{duckdb-keywords-CZ_ZTscu.js → duckdb-keywords-CvJhR_Yd.js} +0 -0
- /package/dist/{dylan-fVO6rnq3.js → dylan-DTSnEIFO.js} +0 -0
- /package/dist/{ebnf-WEXPLEWb.js → ebnf-2D4Ctp3y.js} +0 -0
- /package/dist/{ecl-B94VPjNR.js → ecl-N04ptnRK.js} +0 -0
- /package/dist/{eiffel-C_R6TusS.js → eiffel-Dd8rpqr_.js} +0 -0
- /package/dist/{elm-DzCHbO2g.js → elm-GT2E866W.js} +0 -0
- /package/dist/{erlang-BGNkx6JU.js → erlang-Cf0Bp5pY.js} +0 -0
- /package/dist/{esm-Bb_hbWan.js → esm-BaaaPNGl.js} +0 -0
- /package/dist/{fcl-B_Gv5Jfx.js → fcl-Ccj8Z5Xd.js} +0 -0
- /package/dist/{forth-Bybw0cJ7.js → forth-wd_XzGTg.js} +0 -0
- /package/dist/{fortran-C6PoCLkI.js → fortran-DcwUTZFe.js} +0 -0
- /package/dist/{gas-BBlhenj4.js → gas-DeALIER3.js} +0 -0
- /package/dist/{gherkin-NXtNG85X.js → gherkin-CKTqaJNX.js} +0 -0
- /package/dist/{groovy-BoFYK9xM.js → groovy-Bwdp_d8D.js} +0 -0
- /package/dist/{haskell-BtBdvQ1n.js → haskell-DCdCcPLK.js} +0 -0
- /package/dist/{haxe-D--o6dr0.js → haxe-DAyktQWJ.js} +0 -0
- /package/dist/{http-Dc2fv19V.js → http-_DVAYWoR.js} +0 -0
- /package/dist/{idl-AqTq5l7e.js → idl-CBuZiRYu.js} +0 -0
- /package/dist/{init-D-g0ONX1.js → init-uv0kkh4g.js} +0 -0
- /package/dist/{javascript-DvwNVye9.js → javascript-DzigE11c.js} +0 -0
- /package/dist/{julia-DoKiagZC.js → julia-PwfB-0Cm.js} +0 -0
- /package/dist/{katex-B7pMJpE0.js → katex-C_XRmjAP.js} +0 -0
- /package/dist/{livescript-DxBZMiWB.js → livescript-BJLz1EbT.js} +0 -0
- /package/dist/{lua-DmS_0NTu.js → lua-ZC-XC2jf.js} +0 -0
- /package/dist/{math-BYK36kWZ.js → math-DFcdCCU8.js} +0 -0
- /package/dist/{mathematica-ChlDFeIC.js → mathematica-DCYMx6qB.js} +0 -0
- /package/dist/{mbox-CguZuODr.js → mbox-OxMK_9XI.js} +0 -0
- /package/dist/{mirc-CFtY8dqz.js → mirc-nJVyhA0H.js} +0 -0
- /package/dist/{mllike-C0EJrEOk.js → mllike-DRO89bsU.js} +0 -0
- /package/dist/{modelica-C1kO1nfS.js → modelica-Don3E6ZD.js} +0 -0
- /package/dist/{mscgen-DEYdr7AY.js → mscgen-DJfqD3bN.js} +0 -0
- /package/dist/{mumps-B3NVJs2V.js → mumps-SjGTvDYL.js} +0 -0
- /package/dist/{nginx-ComVAAGN.js → nginx-DasThI7R.js} +0 -0
- /package/dist/{node-sql-parser-DNGGJ-Rw.js → node-sql-parser-B8nBD36q.js} +0 -0
- /package/dist/{ntriples-DHol9X9H.js → ntriples-CNBKRl3I.js} +0 -0
- /package/dist/{octave-CYGz0bfo.js → octave-DdeVHNlx.js} +0 -0
- /package/dist/{oz-kPxb2ni5.js → oz-CcKSoNvN.js} +0 -0
- /package/dist/{pascal-bZ0yrJKy.js → pascal-6leftwNj.js} +0 -0
- /package/dist/{path-Du6n3sOU.js → path-BGaWgPKg.js} +0 -0
- /package/dist/{perl-z4hvqyqz.js → perl-BhJIwWzN.js} +0 -0
- /package/dist/{pig-DZO8QDF9.js → pig-r-xDHqRf.js} +0 -0
- /package/dist/{powershell-BSuaDQEC.js → powershell-D-BELeNi.js} +0 -0
- /package/dist/{properties-BXhGLlIx.js → properties-CnuDhbll.js} +0 -0
- /package/dist/{protobuf-DM6iybWV.js → protobuf-CO8RBhvX.js} +0 -0
- /package/dist/{puppet-Bn05sQT8.js → puppet-NmXHjLy8.js} +0 -0
- /package/dist/{python-Cvnhm0g7.js → python-DAQXi720.js} +0 -0
- /package/dist/{q-B9V8hzex.js → q-DlikXfV0.js} +0 -0
- /package/dist/{r-Cf0gFqmq.js → r-CuohilwT.js} +0 -0
- /package/dist/{rpm-D-LMkTV1.js → rpm-0Pjwp0Pb.js} +0 -0
- /package/dist/{ruby-DeuPikpK.js → ruby-Dq8NJTDG.js} +0 -0
- /package/dist/{sas-C9tjgAo9.js → sas-CuwonyVP.js} +0 -0
- /package/dist/{scheme-D1_bUF0G.js → scheme-CYU-RRIf.js} +0 -0
- /package/dist/{shell-CJBmnks3.js → shell-COPmX2qE.js} +0 -0
- /package/dist/{sieve-1fSV75CF.js → sieve-B_3zyLne.js} +0 -0
- /package/dist/{simple-mode-B90Wdavj.js → simple-mode-DSBniks8.js} +0 -0
- /package/dist/{smalltalk-sZNPD0HO.js → smalltalk-DRft7iPv.js} +0 -0
- /package/dist/{solr-DTkyqJ-Z.js → solr-RZ9uTl59.js} +0 -0
- /package/dist/{sparql-oHc1nm77.js → sparql-CN6qj55H.js} +0 -0
- /package/dist/{spreadsheet-CER0raqY.js → spreadsheet-BNNUNXA2.js} +0 -0
- /package/dist/{sql-ByOoEONQ.js → sql-B4x8IkwU.js} +0 -0
- /package/dist/{stylus-KzkX6zRB.js → stylus-Bn_ZjOQ3.js} +0 -0
- /package/dist/{swift-DqVxZvKo.js → swift-BLUJhMbz.js} +0 -0
- /package/dist/{tcl-BtWSwXfA.js → tcl-C86fxecl.js} +0 -0
- /package/dist/{textile-CWDbn9Ql.js → textile-DmHh2rsK.js} +0 -0
- /package/dist/{tiddlywiki-Cr9xyOY1.js → tiddlywiki-DI0mF2WJ.js} +0 -0
- /package/dist/{tiki-D5JONyfZ.js → tiki-2HU6XLLn.js} +0 -0
- /package/dist/{timer-D7JVdX9U.js → timer-YZl28NYN.js} +0 -0
- /package/dist/{toml-BfehlgmL.js → toml-GWANRNAD.js} +0 -0
- /package/dist/{treemap-qFGzn7xk.js → treemap-D-ka1hvx.js} +0 -0
- /package/dist/{troff-BZBk6AAu.js → troff-BHTsomIy.js} +0 -0
- /package/dist/{ttcn-DVwvXg0_.js → ttcn-DQuhn5Mn.js} +0 -0
- /package/dist/{ttcn-cfg-gjbVLf1L.js → ttcn-cfg-HjFYtdB-.js} +0 -0
- /package/dist/{turtle-CgxKXorV.js → turtle-nCay33Nv.js} +0 -0
- /package/dist/{vb-B9kSwTdM.js → vb-BG-XlqqJ.js} +0 -0
- /package/dist/{vbscript-DrUKSCdb.js → vbscript-B6vyW0-D.js} +0 -0
- /package/dist/{velocity-AlMYTnMy.js → velocity-CWegueqO.js} +0 -0
- /package/dist/{verilog-DLUaM05j.js → verilog-CzSQm4cG.js} +0 -0
- /package/dist/{vhdl-DUJOtSmO.js → vhdl-DqnNVL7r.js} +0 -0
- /package/dist/{webidl-CQp4aHk_.js → webidl-DXEUpDWH.js} +0 -0
- /package/dist/{xquery-IxkjlwOD.js → xquery-Ba_NB5bD.js} +0 -0
- /package/dist/{yacas-Bnctn5w8.js → yacas-HKQU6hyk.js} +0 -0
- /package/dist/{z80-DrFwhx53.js → z80-CXkHXLdj.js} +0 -0
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
TabsList,
|
|
8
8
|
TabsTrigger,
|
|
9
9
|
} from "../../components/ui/tabs";
|
|
10
|
+
import { cn } from "../../utils/cn";
|
|
10
11
|
import { renderHTML } from "../core/RenderHTML";
|
|
11
12
|
import type { IPlugin, IPluginProps } from "../types";
|
|
12
13
|
import { Labeled } from "./common/labeled";
|
|
@@ -17,6 +18,7 @@ interface Data {
|
|
|
17
18
|
*/
|
|
18
19
|
tabs: string[];
|
|
19
20
|
label: string | null;
|
|
21
|
+
orientation: "horizontal" | "vertical";
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
// Selected tab index
|
|
@@ -28,6 +30,7 @@ export class TabsPlugin implements IPlugin<T, Data> {
|
|
|
28
30
|
validator = z.object({
|
|
29
31
|
tabs: z.array(z.string()),
|
|
30
32
|
label: z.string().nullable(),
|
|
33
|
+
orientation: z.enum(["horizontal", "vertical"]).default("horizontal"),
|
|
31
34
|
});
|
|
32
35
|
|
|
33
36
|
render(props: IPluginProps<T, Data>): JSX.Element {
|
|
@@ -51,6 +54,7 @@ interface TabComponentProps extends Data {
|
|
|
51
54
|
const TabComponent = ({
|
|
52
55
|
tabs,
|
|
53
56
|
label,
|
|
57
|
+
orientation,
|
|
54
58
|
value,
|
|
55
59
|
setValue,
|
|
56
60
|
children,
|
|
@@ -70,19 +74,43 @@ const TabComponent = ({
|
|
|
70
74
|
setInternalValue(value);
|
|
71
75
|
}
|
|
72
76
|
|
|
77
|
+
const isVertical = orientation === "vertical";
|
|
78
|
+
const childArray =
|
|
79
|
+
children == null ? [] : Array.isArray(children) ? children : [children];
|
|
80
|
+
|
|
73
81
|
return (
|
|
74
|
-
<Labeled label={label} align="top">
|
|
75
|
-
<Tabs
|
|
76
|
-
|
|
82
|
+
<Labeled label={label} align="top" fullWidth={true}>
|
|
83
|
+
<Tabs
|
|
84
|
+
value={internalValue}
|
|
85
|
+
onValueChange={handleChange}
|
|
86
|
+
orientation={orientation}
|
|
87
|
+
className={cn(isVertical && "flex flex-row gap-3")}
|
|
88
|
+
>
|
|
89
|
+
<TabsList
|
|
90
|
+
className={cn(
|
|
91
|
+
"scrollbar-thin",
|
|
92
|
+
isVertical
|
|
93
|
+
? "flex flex-col items-stretch justify-start h-auto max-h-none shrink-0 min-w-[10rem] overflow-y-auto"
|
|
94
|
+
: "max-w-full overflow-x-auto justify-start",
|
|
95
|
+
)}
|
|
96
|
+
>
|
|
77
97
|
{tabs.map((tab, index) => (
|
|
78
|
-
<TabsTrigger
|
|
98
|
+
<TabsTrigger
|
|
99
|
+
key={index}
|
|
100
|
+
value={index.toString()}
|
|
101
|
+
className={cn(isVertical && "w-full justify-start")}
|
|
102
|
+
>
|
|
79
103
|
{renderHTML({ html: tab })}
|
|
80
104
|
</TabsTrigger>
|
|
81
105
|
))}
|
|
82
106
|
</TabsList>
|
|
83
|
-
{
|
|
84
|
-
|
|
85
|
-
|
|
107
|
+
<div className={cn(isVertical && "flex-1 min-w-0")}>
|
|
108
|
+
{childArray.map((child, index) => (
|
|
109
|
+
<TabsContent key={index} value={index.toString()}>
|
|
110
|
+
{child}
|
|
111
|
+
</TabsContent>
|
|
112
|
+
))}
|
|
113
|
+
</div>
|
|
86
114
|
</Tabs>
|
|
87
115
|
</Labeled>
|
|
88
116
|
);
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { fireEvent, render, screen } from "@testing-library/react";
|
|
4
|
+
import { beforeAll, describe, expect, it, vi } from "vitest";
|
|
5
|
+
import type { z } from "zod";
|
|
6
|
+
import { initialModeAtom } from "@/core/mode";
|
|
7
|
+
import { store } from "@/core/state/jotai";
|
|
8
|
+
import type { IPluginProps } from "../../types";
|
|
9
|
+
import { TabsPlugin } from "../TabsPlugin";
|
|
10
|
+
|
|
11
|
+
describe("TabsPlugin", () => {
|
|
12
|
+
beforeAll(() => {
|
|
13
|
+
store.set(initialModeAtom, "edit");
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const renderPlugin = (
|
|
17
|
+
data: z.input<TabsPlugin["validator"]>,
|
|
18
|
+
initialValue = "0",
|
|
19
|
+
) => {
|
|
20
|
+
const plugin = new TabsPlugin();
|
|
21
|
+
const host = document.createElement("div");
|
|
22
|
+
const setValue = vi.fn();
|
|
23
|
+
const children = [
|
|
24
|
+
<span key="0">Content 0</span>,
|
|
25
|
+
<span key="1">Content 1</span>,
|
|
26
|
+
<span key="2">Content 2</span>,
|
|
27
|
+
];
|
|
28
|
+
const makeProps = (
|
|
29
|
+
value: string,
|
|
30
|
+
): IPluginProps<string, z.infer<TabsPlugin["validator"]>> => ({
|
|
31
|
+
data: plugin.validator.parse(data),
|
|
32
|
+
value,
|
|
33
|
+
setValue,
|
|
34
|
+
host,
|
|
35
|
+
functions: {},
|
|
36
|
+
children,
|
|
37
|
+
});
|
|
38
|
+
const result = render(plugin.render(makeProps(initialValue)));
|
|
39
|
+
return {
|
|
40
|
+
...result,
|
|
41
|
+
setValue,
|
|
42
|
+
rerenderWithValue: (newValue: string) =>
|
|
43
|
+
result.rerender(plugin.render(makeProps(newValue))),
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
it("renders all tab triggers", () => {
|
|
48
|
+
renderPlugin({
|
|
49
|
+
tabs: ["First", "Second", "Third"],
|
|
50
|
+
label: null,
|
|
51
|
+
});
|
|
52
|
+
expect(screen.getByRole("tab", { name: "First" })).toBeInTheDocument();
|
|
53
|
+
expect(screen.getByRole("tab", { name: "Second" })).toBeInTheDocument();
|
|
54
|
+
expect(screen.getByRole("tab", { name: "Third" })).toBeInTheDocument();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("supports vertical orientation", () => {
|
|
58
|
+
renderPlugin({
|
|
59
|
+
tabs: ["First", "Second"],
|
|
60
|
+
label: null,
|
|
61
|
+
orientation: "vertical",
|
|
62
|
+
});
|
|
63
|
+
const tablist = screen.getByRole("tablist");
|
|
64
|
+
expect(tablist).toHaveAttribute("data-orientation", "vertical");
|
|
65
|
+
expect(tablist.className).toMatch(/flex-col/);
|
|
66
|
+
// Horizontal scroll classes should not be applied in vertical mode.
|
|
67
|
+
expect(tablist.className).not.toMatch(/overflow-x-auto/);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("falls back to horizontal when orientation is omitted (back-compat)", () => {
|
|
71
|
+
// Older Python kernels won't send `orientation` — make sure the validator
|
|
72
|
+
// defaults it so the frontend keeps working.
|
|
73
|
+
const plugin = new TabsPlugin();
|
|
74
|
+
const parsed = plugin.validator.parse({
|
|
75
|
+
tabs: ["First"],
|
|
76
|
+
label: null,
|
|
77
|
+
});
|
|
78
|
+
expect(parsed.orientation).toBe("horizontal");
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("selects the tab matching the initial value", () => {
|
|
82
|
+
renderPlugin({ tabs: ["First", "Second", "Third"], label: null }, "1");
|
|
83
|
+
const tabs = screen.getAllByRole("tab");
|
|
84
|
+
expect(tabs[0]).toHaveAttribute("data-state", "inactive");
|
|
85
|
+
expect(tabs[1]).toHaveAttribute("data-state", "active");
|
|
86
|
+
expect(tabs[2]).toHaveAttribute("data-state", "inactive");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("defaults to the first tab when value is empty", () => {
|
|
90
|
+
renderPlugin({ tabs: ["First", "Second"], label: null }, "");
|
|
91
|
+
const tabs = screen.getAllByRole("tab");
|
|
92
|
+
expect(tabs[0]).toHaveAttribute("data-state", "active");
|
|
93
|
+
expect(tabs[1]).toHaveAttribute("data-state", "inactive");
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("calls setValue with the clicked tab's index", () => {
|
|
97
|
+
const { setValue } = renderPlugin({
|
|
98
|
+
tabs: ["First", "Second", "Third"],
|
|
99
|
+
label: null,
|
|
100
|
+
});
|
|
101
|
+
// Radix Tabs' trigger reacts to mousedown (left button), not click —
|
|
102
|
+
// see https://github.com/radix-ui/primitives/blob/main/packages/react/tabs/src/Tabs.tsx
|
|
103
|
+
fireEvent.mouseDown(screen.getByRole("tab", { name: "Third" }), {
|
|
104
|
+
button: 0,
|
|
105
|
+
});
|
|
106
|
+
expect(setValue).toHaveBeenCalledWith("2");
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("syncs selection when value is updated externally", () => {
|
|
110
|
+
const { rerenderWithValue } = renderPlugin(
|
|
111
|
+
{ tabs: ["First", "Second", "Third"], label: null },
|
|
112
|
+
"0",
|
|
113
|
+
);
|
|
114
|
+
expect(screen.getAllByRole("tab")[0]).toHaveAttribute(
|
|
115
|
+
"data-state",
|
|
116
|
+
"active",
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
rerenderWithValue("2");
|
|
120
|
+
const tabs = screen.getAllByRole("tab");
|
|
121
|
+
expect(tabs[0]).toHaveAttribute("data-state", "inactive");
|
|
122
|
+
expect(tabs[2]).toHaveAttribute("data-state", "active");
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("renders HTML in tab labels via renderHTML", () => {
|
|
126
|
+
renderPlugin({
|
|
127
|
+
tabs: ["<strong>Bold</strong>", "Plain"],
|
|
128
|
+
label: null,
|
|
129
|
+
});
|
|
130
|
+
const boldTab = screen.getByRole("tab", { name: "Bold" });
|
|
131
|
+
// The label markup is preserved (not escaped as text), so the trigger
|
|
132
|
+
// contains a real <strong> element.
|
|
133
|
+
expect(boldTab.querySelector("strong")).not.toBeNull();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("renders no tabpanels when tabs and children are empty", () => {
|
|
137
|
+
// When the Python side passes `tabs={}`, slotted HTML is empty and the
|
|
138
|
+
// resulting React children are null/undefined. We should render zero
|
|
139
|
+
// `TabsContent`s — not a stray one paired to a non-existent trigger.
|
|
140
|
+
const plugin = new TabsPlugin();
|
|
141
|
+
const host = document.createElement("div");
|
|
142
|
+
const props: IPluginProps<string, z.infer<TabsPlugin["validator"]>> = {
|
|
143
|
+
data: plugin.validator.parse({ tabs: [], label: null }),
|
|
144
|
+
value: "",
|
|
145
|
+
setValue: vi.fn(),
|
|
146
|
+
host,
|
|
147
|
+
functions: {},
|
|
148
|
+
children: null,
|
|
149
|
+
};
|
|
150
|
+
render(plugin.render(props));
|
|
151
|
+
expect(screen.queryAllByRole("tab")).toHaveLength(0);
|
|
152
|
+
expect(screen.queryAllByRole("tabpanel")).toHaveLength(0);
|
|
153
|
+
});
|
|
154
|
+
});
|
|
@@ -64,6 +64,7 @@ type PluginFunctions = {
|
|
|
64
64
|
column_types_per_step: FieldTypesWithExternalType[];
|
|
65
65
|
python_code?: string | null;
|
|
66
66
|
sql_code?: string | null;
|
|
67
|
+
size_bytes?: number | null;
|
|
67
68
|
}>;
|
|
68
69
|
get_column_values: (req: { column: string }) => Promise<{
|
|
69
70
|
values: unknown[];
|
|
@@ -81,6 +82,7 @@ type PluginFunctions = {
|
|
|
81
82
|
}) => Promise<{
|
|
82
83
|
data: TableData<T>;
|
|
83
84
|
total_rows: number;
|
|
85
|
+
size_bytes?: number | null;
|
|
84
86
|
}>;
|
|
85
87
|
download_as: DownloadAsArgs;
|
|
86
88
|
};
|
|
@@ -118,6 +120,7 @@ export const DataFramePlugin = createPlugin<S>("marimo-dataframe")
|
|
|
118
120
|
column_types_per_step: z.array(columnToFieldTypesSchema),
|
|
119
121
|
python_code: z.string().nullish(),
|
|
120
122
|
sql_code: z.string().nullish(),
|
|
123
|
+
size_bytes: z.number().nullish(),
|
|
121
124
|
}),
|
|
122
125
|
),
|
|
123
126
|
get_column_values: rpc.input(z.object({ column: z.string() })).output(
|
|
@@ -147,6 +150,7 @@ export const DataFramePlugin = createPlugin<S>("marimo-dataframe")
|
|
|
147
150
|
z.object({
|
|
148
151
|
data: z.union([z.string(), z.array(z.object({}).passthrough())]),
|
|
149
152
|
total_rows: z.number(),
|
|
153
|
+
size_bytes: z.number().nullish(),
|
|
150
154
|
}),
|
|
151
155
|
),
|
|
152
156
|
download_as: DownloadAsSchema,
|
|
@@ -203,6 +207,7 @@ export const DataFrameComponent = memo(
|
|
|
203
207
|
column_types_per_step,
|
|
204
208
|
python_code,
|
|
205
209
|
sql_code,
|
|
210
|
+
size_bytes,
|
|
206
211
|
} = data || {};
|
|
207
212
|
|
|
208
213
|
const totalColumns = field_types?.length;
|
|
@@ -322,6 +327,7 @@ export const DataFrameComponent = memo(
|
|
|
322
327
|
data={url || ""}
|
|
323
328
|
hasStableRowId={false}
|
|
324
329
|
totalRows={total_rows ?? 0}
|
|
330
|
+
sizeBytes={size_bytes ?? null}
|
|
325
331
|
totalColumns={totalColumns ?? 0}
|
|
326
332
|
maxColumns="all"
|
|
327
333
|
pageSize={pageSize}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
3
|
import { asRemoteURL } from "@/core/runtime/config";
|
|
4
|
+
import { Semaphore } from "@/utils/semaphore";
|
|
4
5
|
import { vegaLoadData } from "./loader";
|
|
5
6
|
import type {
|
|
6
7
|
FacetedUnitSpec,
|
|
@@ -17,6 +18,8 @@ type AnySpec =
|
|
|
17
18
|
| UnitSpec<Field>
|
|
18
19
|
| GenericFacetSpec<FacetedUnitSpec<Field>, LayerSpec<Field>, Field>;
|
|
19
20
|
|
|
21
|
+
const VEGA_DATA_FETCH_CONCURRENCY = 5;
|
|
22
|
+
|
|
20
23
|
/**
|
|
21
24
|
* Given a VegaLite spec with URL data, resolve the data and return a new spec with the resolved data.
|
|
22
25
|
*
|
|
@@ -30,6 +33,9 @@ export async function resolveVegaSpecData(
|
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
const datasets = "datasets" in spec ? { ...spec.datasets } : {};
|
|
36
|
+
// Single semaphore shared across the whole spec so total in-flight fetches
|
|
37
|
+
// (across all nested layers/hconcat/vconcat) stay bounded.
|
|
38
|
+
const fetchSem = new Semaphore(VEGA_DATA_FETCH_CONCURRENCY);
|
|
33
39
|
|
|
34
40
|
const traverse = async <T extends AnySpec>(spec: T): Promise<T> => {
|
|
35
41
|
if (!spec) {
|
|
@@ -80,7 +86,8 @@ export async function resolveVegaSpecData(
|
|
|
80
86
|
} catch {
|
|
81
87
|
return spec;
|
|
82
88
|
}
|
|
83
|
-
const
|
|
89
|
+
const format = spec.data.format;
|
|
90
|
+
const data = await fetchSem.run(() => vegaLoadData(url.href, format));
|
|
84
91
|
|
|
85
92
|
datasets[url.pathname] = data;
|
|
86
93
|
|
|
@@ -957,6 +957,77 @@ describe("MultiColumn", () => {
|
|
|
957
957
|
expect(inserted.topLevelIds[1]).toEqual(["B1", "D1", "B2"]);
|
|
958
958
|
});
|
|
959
959
|
|
|
960
|
+
describe("moveCellsRelativeTo", () => {
|
|
961
|
+
it("moves a single cell before target in same column", () => {
|
|
962
|
+
const moved = multiColumn.moveCellsRelativeTo(["A3"], "A1", "before");
|
|
963
|
+
expect(moved.topLevelIds).toEqual([
|
|
964
|
+
["A3", "A1", "A2"],
|
|
965
|
+
["B1", "B2"],
|
|
966
|
+
["C1", "C2", "C3", "C4"],
|
|
967
|
+
]);
|
|
968
|
+
});
|
|
969
|
+
|
|
970
|
+
it("moves a single cell after target in same column", () => {
|
|
971
|
+
const moved = multiColumn.moveCellsRelativeTo(["A1"], "A3", "after");
|
|
972
|
+
expect(moved.topLevelIds).toEqual([
|
|
973
|
+
["A2", "A3", "A1"],
|
|
974
|
+
["B1", "B2"],
|
|
975
|
+
["C1", "C2", "C3", "C4"],
|
|
976
|
+
]);
|
|
977
|
+
});
|
|
978
|
+
|
|
979
|
+
it("moves multiple cells before target", () => {
|
|
980
|
+
const moved = multiColumn.moveCellsRelativeTo(
|
|
981
|
+
["A1", "A2"],
|
|
982
|
+
"B2",
|
|
983
|
+
"before",
|
|
984
|
+
);
|
|
985
|
+
expect(moved.topLevelIds).toEqual([
|
|
986
|
+
["A3"],
|
|
987
|
+
["B1", "A1", "A2", "B2"],
|
|
988
|
+
["C1", "C2", "C3", "C4"],
|
|
989
|
+
]);
|
|
990
|
+
});
|
|
991
|
+
|
|
992
|
+
it("moves multiple cells after target", () => {
|
|
993
|
+
const moved = multiColumn.moveCellsRelativeTo(
|
|
994
|
+
["A2", "A3"],
|
|
995
|
+
"B1",
|
|
996
|
+
"after",
|
|
997
|
+
);
|
|
998
|
+
expect(moved.topLevelIds).toEqual([
|
|
999
|
+
["A1"],
|
|
1000
|
+
["B1", "A2", "A3", "B2"],
|
|
1001
|
+
["C1", "C2", "C3", "C4"],
|
|
1002
|
+
]);
|
|
1003
|
+
});
|
|
1004
|
+
|
|
1005
|
+
it("returns same MultiColumn when cellIds is empty", () => {
|
|
1006
|
+
const moved = multiColumn.moveCellsRelativeTo([], "A1", "before");
|
|
1007
|
+
expect(moved).toBe(multiColumn);
|
|
1008
|
+
});
|
|
1009
|
+
|
|
1010
|
+
it("inserts at end when target is among moved cells", () => {
|
|
1011
|
+
const moved = multiColumn.moveCellsRelativeTo(
|
|
1012
|
+
["A1", "A2"],
|
|
1013
|
+
"A2",
|
|
1014
|
+
"after",
|
|
1015
|
+
);
|
|
1016
|
+
// Target A2 was removed; insert at end of column 0
|
|
1017
|
+
expect(moved.topLevelIds).toEqual([
|
|
1018
|
+
["A3", "A1", "A2"],
|
|
1019
|
+
["B1", "B2"],
|
|
1020
|
+
["C1", "C2", "C3", "C4"],
|
|
1021
|
+
]);
|
|
1022
|
+
});
|
|
1023
|
+
|
|
1024
|
+
it("throws when node not found", () => {
|
|
1025
|
+
expect(() =>
|
|
1026
|
+
multiColumn.moveCellsRelativeTo(["Z1"], "A1", "before"),
|
|
1027
|
+
).toThrow("Cell Z1 not found in any column");
|
|
1028
|
+
});
|
|
1029
|
+
});
|
|
1030
|
+
|
|
960
1031
|
it("deletes by id", () => {
|
|
961
1032
|
const deleted = multiColumn.deleteById("B1");
|
|
962
1033
|
expect(deleted.topLevelIds[1]).toEqual(["B2"]);
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { Deferred } from "../Deferred";
|
|
4
|
+
import { mapWithConcurrency, Semaphore } from "../semaphore";
|
|
5
|
+
|
|
6
|
+
describe("Semaphore", () => {
|
|
7
|
+
it("rejects invalid permit counts", () => {
|
|
8
|
+
expect(() => new Semaphore(0)).toThrow();
|
|
9
|
+
expect(() => new Semaphore(-1)).toThrow();
|
|
10
|
+
expect(() => new Semaphore(1.5)).toThrow();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("resolves run() with the function's value", async () => {
|
|
14
|
+
const sem = new Semaphore(1);
|
|
15
|
+
await expect(sem.run(async () => 42)).resolves.toBe(42);
|
|
16
|
+
expect(sem.available).toBe(1);
|
|
17
|
+
expect(sem.pending).toBe(0);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("releases the permit when run() rejects", async () => {
|
|
21
|
+
const sem = new Semaphore(1);
|
|
22
|
+
await expect(
|
|
23
|
+
sem.run(async () => {
|
|
24
|
+
throw new Error("boom");
|
|
25
|
+
}),
|
|
26
|
+
).rejects.toThrow("boom");
|
|
27
|
+
expect(sem.available).toBe(1);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("caps concurrent in-flight tasks at `permits`", async () => {
|
|
31
|
+
const sem = new Semaphore(3);
|
|
32
|
+
let inFlight = 0;
|
|
33
|
+
let maxInFlight = 0;
|
|
34
|
+
const gates = Array.from({ length: 10 }, () => new Deferred<void>());
|
|
35
|
+
|
|
36
|
+
const tasks = gates.map((gate) =>
|
|
37
|
+
sem.run(async () => {
|
|
38
|
+
inFlight++;
|
|
39
|
+
maxInFlight = Math.max(maxInFlight, inFlight);
|
|
40
|
+
await gate.promise;
|
|
41
|
+
inFlight--;
|
|
42
|
+
}),
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
// Let microtasks run so the first batch is scheduled.
|
|
46
|
+
await Promise.resolve();
|
|
47
|
+
await Promise.resolve();
|
|
48
|
+
|
|
49
|
+
expect(maxInFlight).toBe(3);
|
|
50
|
+
expect(sem.available).toBe(0);
|
|
51
|
+
expect(sem.pending).toBe(7);
|
|
52
|
+
|
|
53
|
+
// Release them all and confirm everyone finishes.
|
|
54
|
+
for (const gate of gates) {
|
|
55
|
+
gate.resolve();
|
|
56
|
+
}
|
|
57
|
+
await Promise.all(tasks);
|
|
58
|
+
expect(maxInFlight).toBe(3);
|
|
59
|
+
expect(sem.available).toBe(3);
|
|
60
|
+
expect(sem.pending).toBe(0);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("releases waiters in FIFO order", async () => {
|
|
64
|
+
const sem = new Semaphore(1);
|
|
65
|
+
const order: number[] = [];
|
|
66
|
+
const hold = new Deferred<void>();
|
|
67
|
+
|
|
68
|
+
// First holder takes the only permit and parks on `hold`.
|
|
69
|
+
const first = sem.run(async () => {
|
|
70
|
+
order.push(0);
|
|
71
|
+
await hold.promise;
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Queue three more in known order.
|
|
75
|
+
const rest = [1, 2, 3].map((i) =>
|
|
76
|
+
sem.run(async () => {
|
|
77
|
+
order.push(i);
|
|
78
|
+
}),
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
await Promise.resolve();
|
|
82
|
+
expect(order).toEqual([0]);
|
|
83
|
+
expect(sem.pending).toBe(3);
|
|
84
|
+
|
|
85
|
+
hold.resolve();
|
|
86
|
+
await Promise.all([first, ...rest]);
|
|
87
|
+
expect(order).toEqual([0, 1, 2, 3]);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("throws on release() beyond initial permit count", () => {
|
|
91
|
+
const sem = new Semaphore(2);
|
|
92
|
+
expect(() => sem.release()).toThrow(/more times than acquire/);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("supports manual acquire/release", async () => {
|
|
96
|
+
const sem = new Semaphore(2);
|
|
97
|
+
await sem.acquire();
|
|
98
|
+
await sem.acquire();
|
|
99
|
+
expect(sem.available).toBe(0);
|
|
100
|
+
|
|
101
|
+
let resolved = false;
|
|
102
|
+
const waiter = sem.acquire().then(() => {
|
|
103
|
+
resolved = true;
|
|
104
|
+
});
|
|
105
|
+
await Promise.resolve();
|
|
106
|
+
expect(resolved).toBe(false);
|
|
107
|
+
|
|
108
|
+
sem.release();
|
|
109
|
+
await waiter;
|
|
110
|
+
expect(resolved).toBe(true);
|
|
111
|
+
|
|
112
|
+
sem.release();
|
|
113
|
+
sem.release();
|
|
114
|
+
expect(sem.available).toBe(2);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
describe("mapWithConcurrency", () => {
|
|
119
|
+
it("returns [] for empty input without invoking fn", async () => {
|
|
120
|
+
let called = 0;
|
|
121
|
+
const result = await mapWithConcurrency([], 5, async () => {
|
|
122
|
+
called++;
|
|
123
|
+
return 1;
|
|
124
|
+
});
|
|
125
|
+
expect(result).toEqual([]);
|
|
126
|
+
expect(called).toBe(0);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("preserves input order in the result", async () => {
|
|
130
|
+
const input = [10, 20, 30, 40, 50];
|
|
131
|
+
const result = await mapWithConcurrency(input, 2, async (n) => {
|
|
132
|
+
// Reverse the natural completion order so we'd notice if order broke.
|
|
133
|
+
await new Promise((r) => setTimeout(r, input.length - input.indexOf(n)));
|
|
134
|
+
return n * 2;
|
|
135
|
+
});
|
|
136
|
+
expect(result).toEqual([20, 40, 60, 80, 100]);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("respects the concurrency cap", async () => {
|
|
140
|
+
let inFlight = 0;
|
|
141
|
+
let maxInFlight = 0;
|
|
142
|
+
const items = Array.from({ length: 20 }, (_, i) => i);
|
|
143
|
+
const gates = items.map(() => new Deferred<void>());
|
|
144
|
+
|
|
145
|
+
const result = mapWithConcurrency(items, 4, async (i) => {
|
|
146
|
+
inFlight++;
|
|
147
|
+
maxInFlight = Math.max(maxInFlight, inFlight);
|
|
148
|
+
await gates[i].promise;
|
|
149
|
+
inFlight--;
|
|
150
|
+
return i;
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// Flush microtasks so the first batch enters fn().
|
|
154
|
+
await Promise.resolve();
|
|
155
|
+
await Promise.resolve();
|
|
156
|
+
expect(maxInFlight).toBe(4);
|
|
157
|
+
|
|
158
|
+
// Releasing one gate should let exactly one queued task enter fn().
|
|
159
|
+
for (const gate of gates) {
|
|
160
|
+
gate.resolve();
|
|
161
|
+
}
|
|
162
|
+
await result;
|
|
163
|
+
expect(maxInFlight).toBe(4);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it("lets in-flight tasks complete after the first rejection", async () => {
|
|
167
|
+
const completed: number[] = [];
|
|
168
|
+
const gates = [0, 1, 2, 3, 4].map(() => new Deferred<void>());
|
|
169
|
+
|
|
170
|
+
const result = mapWithConcurrency([0, 1, 2, 3, 4], 5, async (i) => {
|
|
171
|
+
await gates[i].promise;
|
|
172
|
+
if (i === 0) {
|
|
173
|
+
throw new Error("first failed");
|
|
174
|
+
}
|
|
175
|
+
completed.push(i);
|
|
176
|
+
return i;
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// Reject the first one; the other 4 are still gated.
|
|
180
|
+
gates[0].resolve();
|
|
181
|
+
// Drain the rejection (catch so it doesn't escape the test).
|
|
182
|
+
await expect(result).rejects.toThrow("first failed");
|
|
183
|
+
|
|
184
|
+
// The other tasks were already in-flight when the rejection happened;
|
|
185
|
+
// they should still resolve when their gates open.
|
|
186
|
+
for (let i = 1; i < gates.length; i++) {
|
|
187
|
+
gates[i].resolve();
|
|
188
|
+
}
|
|
189
|
+
// Yield so the remaining sem.run() finallys run.
|
|
190
|
+
await new Promise((r) => setTimeout(r, 0));
|
|
191
|
+
expect(completed.toSorted()).toEqual([1, 2, 3, 4]);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it("rejects on the first error", async () => {
|
|
195
|
+
await expect(
|
|
196
|
+
mapWithConcurrency([1, 2, 3], 2, async (n) => {
|
|
197
|
+
if (n === 2) {
|
|
198
|
+
throw new Error("nope");
|
|
199
|
+
}
|
|
200
|
+
return n;
|
|
201
|
+
}),
|
|
202
|
+
).rejects.toThrow("nope");
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it("throws on invalid concurrency, even for empty input", () => {
|
|
206
|
+
expect(() => mapWithConcurrency([1, 2, 3], 0, async (n) => n)).toThrow();
|
|
207
|
+
expect(() => mapWithConcurrency([], 0, async (n: number) => n)).toThrow();
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it("passes the index to fn", async () => {
|
|
211
|
+
const result = await mapWithConcurrency(
|
|
212
|
+
["a", "b", "c"],
|
|
213
|
+
2,
|
|
214
|
+
async (item, index) => `${index}:${item}`,
|
|
215
|
+
);
|
|
216
|
+
expect(result).toEqual(["0:a", "1:b", "2:c"]);
|
|
217
|
+
});
|
|
218
|
+
});
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
|
+
import { mapWithConcurrency } from "./semaphore";
|
|
4
|
+
|
|
5
|
+
const FILE_READ_CONCURRENCY = 5;
|
|
6
|
+
|
|
3
7
|
/**
|
|
4
8
|
* Converts a Blob or File to either a base64-encoded string or a data URL.
|
|
5
9
|
*
|
|
@@ -35,11 +39,8 @@ export function blobToString(
|
|
|
35
39
|
* Returns a promised array of tuples [file name, file contents].
|
|
36
40
|
*/
|
|
37
41
|
export function filesToBase64(files: File[]): Promise<[string, string][]> {
|
|
38
|
-
return
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
),
|
|
43
|
-
),
|
|
44
|
-
);
|
|
42
|
+
return mapWithConcurrency(files, FILE_READ_CONCURRENCY, async (file) => {
|
|
43
|
+
const contents = await blobToString(file, "base64");
|
|
44
|
+
return [file.name, contents] as [string, string];
|
|
45
|
+
});
|
|
45
46
|
}
|