@marimo-team/islands 0.23.9-dev2 → 0.23.9-dev20
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-2lBNiUv6.js → ConnectedDataExplorerComponent-DSyAzzpW.js} +20 -20
- package/dist/ErrorBoundary-rULOrC_p.js +175 -0
- package/dist/{ImageComparisonComponent-CNHIsPDj.js → ImageComparisonComponent-CHrI72em.js} +1 -1
- package/dist/{Plot-4wn-lMVn.js → Plot-CAYS29h9.js} +1 -1
- package/dist/{_baseUniq-CxZRxRRo.js → _baseUniq-B_2Hw7zG.js} +3 -3
- package/dist/{any-language-editor-VWs_7v27.js → any-language-editor-DfdpyDv_.js} +23 -23
- package/dist/architecture-7HQA4BMR-Kyc44TmC.js +6 -0
- package/dist/{architectureDiagram-VXUJARFQ-CXVJxFhH.js → architectureDiagram-VXUJARFQ-CT2SuxNw.js} +15 -15
- package/dist/{arrays-CldYf7p7.js → arrays-sEtDRoG4.js} +1 -1
- package/dist/assets/__vite-browser-external-Ddfz4Fpd.js +1 -0
- package/dist/assets/{worker-CpBbwbQo.js → worker-CgL6N0XX.js} +2 -2
- package/dist/{blockDiagram-VD42YOAC-DGDaxR8I.js → blockDiagram-VD42YOAC-Dy7hlFla.js} +7 -7
- package/dist/{button-Dj4BTre0.js → button-C5K9fIPF.js} +2 -2
- package/dist/{c4Diagram-YG6GDRKO-C2hc6ne8.js → c4Diagram-YG6GDRKO-BXlAmZ8Z.js} +4 -4
- package/dist/{capabilities-C9rrYCzf.js → capabilities-BceAxrAW.js} +2 -2
- package/dist/{channel-BBoIVUrJ.js → channel-D_PHgcig.js} +1 -1
- package/dist/{chat-ui-D3XBept8.js → chat-ui-s5fKbcIr.js} +3084 -3057
- package/dist/{check-BcUIXnUT.js → check-DTbrK0zt.js} +1 -1
- package/dist/{chunk-4F5CHEZ2-BZq7Kom7.js → chunk-4F5CHEZ2-D9nGEHV8.js} +1 -1
- package/dist/{chunk-5FQGJX7Z-BOg95xG5.js → chunk-5FQGJX7Z-BNjes6Yx.js} +5 -5
- package/dist/{chunk-ABZYJK2D-D0cLy8Bb.js → chunk-ABZYJK2D-Dz0-H2B5.js} +1 -1
- package/dist/{chunk-ATLVNIR6-BXsEjlHF.js → chunk-ATLVNIR6-o0Z5MZLd.js} +1 -1
- package/dist/{chunk-B2363JML-D9-XOau1.js → chunk-B2363JML-KEJpLGGP.js} +1 -1
- package/dist/{chunk-B4BG7PRW-Q1usn6T3.js → chunk-B4BG7PRW-BL98U9B4.js} +4 -4
- package/dist/{chunk-DI55MBZ5-D1qLYNrb.js → chunk-DI55MBZ5-Dwkn0LWm.js} +4 -4
- package/dist/{chunk-EXTU4WIE-BKNXdLmD.js → chunk-EXTU4WIE-9sNjmQrB.js} +1 -1
- package/dist/{chunk-FRFDVMJY-BSBUAX7r.js → chunk-FRFDVMJY-DzQqMWrl.js} +1 -1
- package/dist/{chunk-JA3XYJ7Z-D6c6cOBG.js → chunk-JA3XYJ7Z-C32Y7Epf.js} +2 -2
- package/dist/{chunk-JZLCHNYA-BvsPHJmL.js → chunk-JZLCHNYA-C6ftyVMN.js} +4 -4
- package/dist/{chunk-N4CR4FBY-8ycT-O9a.js → chunk-N4CR4FBY-DUhGZhZs.js} +5 -5
- package/dist/{chunk-PL6DKKU2-B0MTXvyc.js → chunk-PL6DKKU2-D7km-08O.js} +1 -1
- package/dist/{chunk-QN33PNHL-Bb-eUBW3.js → chunk-QN33PNHL-0K6SDYn3.js} +1 -1
- package/dist/{chunk-QXUST7PY-DV8yRwBd.js → chunk-QXUST7PY-DMhsRpYK.js} +5 -5
- package/dist/{chunk-S3R3BYOJ-mQeCz5CE.js → chunk-S3R3BYOJ-oAe3dEbO.js} +3 -3
- package/dist/{chunk-SJTYNZTY-CEG4F0pB.js → chunk-SJTYNZTY-BkJrPRFC.js} +1 -1
- package/dist/{chunk-TCCFYFTB-d3HOqL2I.js → chunk-TCCFYFTB-D58KeXnC.js} +6 -6
- package/dist/{chunk-TQ3KTPDO-DiCtqVSi.js → chunk-TQ3KTPDO-D_yA_wAb.js} +1 -1
- package/dist/{chunk-TZMSLE5B-BqW10dHe.js → chunk-TZMSLE5B-yBKS_DQU.js} +1 -1
- package/dist/{chunk-UMXZTB3W-97iS1iEl.js → chunk-UMXZTB3W-D7uwvNjd.js} +1 -1
- package/dist/{classDiagram-2ON5EDUG--Yh__LHb.js → classDiagram-2ON5EDUG-QjoAcuFE.js} +10 -10
- package/dist/{classDiagram-v2-WZHVMYZB-BC7X7Xtc.js → classDiagram-v2-WZHVMYZB-bUCv4gu2.js} +10 -10
- package/dist/{clone-BuIIsfA8.js → clone-Q4Fqwn6q.js} +1 -1
- package/dist/{code-block-37QAKDTI-BsGy1AOJ.js → code-block-37QAKDTI-m92Yc8pv.js} +2 -2
- package/dist/{code-visibility-Dt0Aq6-t.js → code-visibility-DvMXzzJz.js} +8425 -8595
- package/dist/{constants-D0gkYoE2.js → constants-T20xxyNf.js} +2 -2
- package/dist/{copy-DLf4aN7I.js → copy-BuQpJEzp.js} +2 -2
- package/dist/{dagre-6UL2VRFP-DRBWoQUw.js → dagre-6UL2VRFP-J0JKgwOt.js} +11 -11
- package/dist/{dagre-VYEPqXIV.js → dagre-By_QsQgc.js} +11 -11
- package/dist/{data-grid-overlay-editor-efe5ZagF.js → data-grid-overlay-editor-mfEJ5475.js} +2 -2
- package/dist/{diagram-PSM6KHXK-H66ATWP2.js → diagram-PSM6KHXK-DYgJuNk9.js} +18 -18
- package/dist/{diagram-QEK2KX5R-DItl5Wns.js → diagram-QEK2KX5R-CKdBR2sb.js} +14 -14
- package/dist/{diagram-S2PKOQOG-CtuW_ZuL.js → diagram-S2PKOQOG-Dpi7mo5W.js} +14 -14
- package/dist/dist-0Fif7jnk.js +5 -0
- package/dist/{dist-Dh3wkoyH.js → dist-4j4c7bjm.js} +2 -2
- package/dist/{dist-CDFZi-QD.js → dist-B3P2fFpz.js} +1 -1
- package/dist/{dist-BNyrZfqT.js → dist-B3pZ0Ab6.js} +2 -2
- package/dist/dist-B5h_9sHB.js +6 -0
- package/dist/dist-B9M6R5ye.js +5 -0
- package/dist/dist-BCt3tnck.js +8 -0
- package/dist/{dist-BrBucRXs.js → dist-BTfv03uy.js} +2 -2
- package/dist/dist-BUIJwMwn.js +8 -0
- package/dist/{dist-CYEylvZA.js → dist-BbbIBDiQ.js} +1 -1
- package/dist/{dist-KnujRhFL.js → dist-BcuoonNH.js} +4 -4
- package/dist/{dist-DJ6zJQZ4.js → dist-Bde4a2kU.js} +2 -2
- package/dist/{dist-t_qL7eB8.js → dist-Bfwsv11D.js} +2 -2
- package/dist/{dist-CNtV21T_.js → dist-BhM8gdSO.js} +4 -4
- package/dist/{dist-nuW5EDYT.js → dist-BotSqB48.js} +2 -2
- package/dist/dist-BpquMd3k.js +5 -0
- package/dist/dist-BzJsqYfz.js +5 -0
- package/dist/{dist-D029TiHd.js → dist-Bz_sYWbr.js} +2 -2
- package/dist/{dist-D3ZI9nhS.js → dist-C1BYNeCR.js} +4 -4
- package/dist/{dist-Bc5pmZIw.js → dist-C5VC_yzu.js} +1 -1
- package/dist/dist-CA5ELXAf.js +6 -0
- package/dist/dist-CLBRs6Uv.js +5 -0
- package/dist/{dist-Dhk6FMb0.js → dist-CLJWPTX2.js} +3 -3
- package/dist/{dist-C34oIrQ9.js → dist-CLUtPrdy.js} +1 -1
- package/dist/dist-CStVCMbq.js +5 -0
- package/dist/{dist-B8RaFTRF.js → dist-CUCNs1ja.js} +2 -2
- package/dist/dist-CZRIEY3Y.js +8 -0
- package/dist/{dist-UcOPnRMa.js → dist-CcXxepx6.js} +3 -3
- package/dist/dist-CuUHbFD0.js +5 -0
- package/dist/{dist-B8BjrFUE.js → dist-Cy1WxgBD.js} +5 -5
- package/dist/{dist-WdPUFc56.js → dist-D4CewLk6.js} +1 -1
- package/dist/{dist-DMZNjfX4.js → dist-DRfcqpxJ.js} +2 -2
- package/dist/dist-DV7Iabxb.js +8 -0
- package/dist/{dist-usPCDYx8.js → dist-D_bzzWBm.js} +1 -1
- package/dist/{dist-BvCfQQQE.js → dist-DgnE8F-r.js} +1 -1
- package/dist/{dist-JEhxD_cn.js → dist-DhHh0jLg.js} +1 -1
- package/dist/{dist-DGAfI2rB.js → dist-DqAWR3CS.js} +2 -2
- package/dist/{dist--sWVZwjW.js → dist-Du8WkPuU.js} +1 -1
- package/dist/dist-DuEeHMvL.js +5 -0
- package/dist/{dist-BTyJtnNg.js → dist-DxvORzUR.js} +1 -1
- package/dist/{dist-B507mf_I.js → dist-RqXTaiir.js} +2 -2
- package/dist/{dist-Yrfc6L0I.js → dist-fQ0ViXGs.js} +3 -3
- package/dist/{dist-B4LJpMEg.js → dist-h2c8sZvT.js} +1 -1
- package/dist/{dist-C2ej4eOH.js → dist-luvabDEB.js} +2 -2
- package/dist/{dist-B52GXZbd.js → dist-p2qyWijU.js} +2 -2
- package/dist/{erDiagram-Q2GNP2WA--19X2kU5.js → erDiagram-Q2GNP2WA-BU-m41EQ.js} +10 -10
- package/dist/{error-banner-CVkfBUT3.js → error-banner-5bz0L9hS.js} +3 -3
- package/dist/{esm-CWp0KQeK.js → esm-BfhQmZjp.js} +4 -4
- package/dist/{esm-DjNnlmpf.js → esm-Duie8iU-.js} +23 -23
- package/dist/{extends-vAi97cpa.js → extends-BgdxCfYu.js} +6 -6
- package/dist/{flatten-CzBvFdvC.js → flatten-Bbw7g6-K.js} +1 -1
- package/dist/{flowDiagram-NV44I4VS-DQmWlo7f.js → flowDiagram-NV44I4VS-CRoXKjGq.js} +10 -10
- package/dist/{formats-Dsy9kkZu.js → formats-BiH6HX1V.js} +4 -4
- package/dist/{ganttDiagram-JELNMOA3-BOGXJ8Lk.js → ganttDiagram-JELNMOA3-7mq5f9cO.js} +7 -7
- package/dist/{gitGraph-G5XIXVHT-DGlbae5m.js → gitGraph-G5XIXVHT-DiniR35k.js} +3 -3
- package/dist/{gitGraphDiagram-V2S2FVAM-DjzxfF0P.js → gitGraphDiagram-V2S2FVAM-Dfuokq6w.js} +13 -13
- package/dist/{glide-data-editor-DucgdjRo.js → glide-data-editor-Ck-MRdns.js} +557 -557
- package/dist/{graphlib-CVPKjKCS.js → graphlib-Ns7y5crs.js} +5 -5
- package/dist/{hasIn-COs6vImh.js → hasIn-Deg7jl_j.js} +3 -3
- package/dist/{html-to-image-CpggM7u1.js → html-to-image-CTU_-PnW.js} +109 -109
- package/dist/{info-VBDWY6EO-D2lvLLw5.js → info-VBDWY6EO-DVZvGhkQ.js} +3 -3
- package/dist/{infoDiagram-HS3SLOUP-ChNufFsP.js → infoDiagram-HS3SLOUP-CEnzWruK.js} +13 -13
- package/dist/{input-D4kjoQUB.js → input-BwcGY_X1.js} +70 -67
- package/dist/{isEmpty-Dd8mx_WL.js → isEmpty-CJJMn-QP.js} +1 -1
- package/dist/{isSymbol-BvIfMnn6.js → isSymbol-CoUCgMCM.js} +1 -1
- package/dist/{journeyDiagram-XKPGCS4Q-BO_O4Ij1.js → journeyDiagram-XKPGCS4Q-8XYSU1GI.js} +3 -3
- package/dist/{kanban-definition-3W4ZIXB7-CPpiiiWk.js → kanban-definition-3W4ZIXB7--9pT9z1R.js} +7 -7
- package/dist/{label-BLqV33b1.js → label-LWtdw5i8.js} +3 -3
- package/dist/{linear-2NnK4cxi.js → linear-B5-AFRiR.js} +2 -2
- package/dist/{loader-Dr8Qem8p.js → loader-BWLPpjKK.js} +2 -2
- package/dist/main.js +1688 -1569
- package/dist/{memoize-C9ltv0Cw.js → memoize-BOtf2yFf.js} +1 -1
- package/dist/{merge-CHn7Yx0N.js → merge-Be1CqGnU.js} +1 -1
- package/dist/mermaid-4DMBBIKO-DIdL224_.js +6 -0
- package/dist/{mermaid-DO-Daq7u.js → mermaid-YK4c8MNC.js} +44 -44
- package/dist/{mermaid-parser.core-DreccfmS.js → mermaid-parser.core-C3XRsazI.js} +8 -8
- package/dist/{min-BNz2lZfk.js → min-Dtgc8txR.js} +4 -4
- package/dist/{mindmap-definition-VGOIOE7T-CC1_Vl0f.js → mindmap-definition-VGOIOE7T-B-4mnfFG.js} +9 -9
- package/dist/{now-Sgq5m3D-.js → now-Ch98bJO_.js} +2 -2
- package/dist/{number-overlay-editor-CpKi64Fy.js → number-overlay-editor-D-a0qCT8.js} +1 -1
- package/dist/{once-rJImu7SE.js → once-DPuqGUeo.js} +1 -1
- package/dist/{packet-DYOGHKS2-CmWtF3uO.js → packet-DYOGHKS2-34raHOiB.js} +3 -3
- package/dist/{pick-CRAXxDYn.js → pick-D1Qo8s2C.js} +4 -4
- package/dist/{pie-VRWISCQL-B6u8vus8.js → pie-VRWISCQL-BaLlzZa3.js} +3 -3
- package/dist/{pieDiagram-ADFJNKIX-Di34MOFQ.js → pieDiagram-ADFJNKIX-Cr3cNpZY.js} +15 -15
- package/dist/{precisionRound-CnHPY_5v.js → precisionRound-Tqb4mg-H.js} +1 -1
- package/dist/{process-output-X8TR20AK.js → process-output-CVDHJqo6.js} +36 -28
- package/dist/{quadrantDiagram-AYHSOK5B-B9kVk1ny.js → quadrantDiagram-AYHSOK5B-BuNL8Q93.js} +4 -4
- package/dist/{radar-ZZBFDIW7-XAmXSa8s.js → radar-ZZBFDIW7-Ci7bfoZa.js} +3 -3
- package/dist/{react-vega-Dh6-UKKe.js → react-vega-B0sAlDTL.js} +9 -9
- package/dist/react-vega-B6ncY2Tp.js +9 -0
- package/dist/{requirementDiagram-UZGBJVZJ-BxGfGYEx.js → requirementDiagram-UZGBJVZJ-BG2lLUN1.js} +9 -9
- package/dist/{reveal-component-Cj2fUG1Q.js → reveal-component-Dskn4JVk.js} +31 -31
- package/dist/{sankeyDiagram-TZEHDZUN-D09PBJ-n.js → sankeyDiagram-TZEHDZUN-DMal8sps.js} +3 -3
- package/dist/{sequenceDiagram-WL72ISMW-t_Dpemj0.js → sequenceDiagram-WL72ISMW-DT6Tk-Eo.js} +4 -4
- package/dist/{spec-hVaaZsY5.js → spec-CyLiCjSf.js} +4 -4
- package/dist/{stateDiagram-FKZM4ZOC-B18gTP_j.js → stateDiagram-FKZM4ZOC-CB_lodq3.js} +12 -12
- package/dist/{stateDiagram-v2-4FDKWEC3-B6e_t14A.js → stateDiagram-v2-4FDKWEC3-E0RGjKsm.js} +10 -10
- package/dist/stex-KfRnSHzF.js +4 -0
- package/dist/{strings-BiIhGaI8.js → strings-Bu3vlb6W.js} +7 -7
- package/dist/style.css +1 -1
- package/dist/{swiper-component-DlD2GU2g.js → swiper-component-B2t5sN1q.js} +3 -3
- package/dist/{time-C1SGcFMH.js → time-CsmIF9YZ.js} +3 -3
- package/dist/{timeline-definition-IT6M3QCI-DJnh1ks5.js → timeline-definition-IT6M3QCI-NfSKRvH0.js} +2 -2
- package/dist/{toDate-CIpC_34u.js → toDate-DNWCUEQp.js} +5 -5
- package/dist/{tooltip-DRaMBu06.js → tooltip-C5FYOpQc.js} +4 -4
- package/dist/{treemap-GDKQZRPO-Du95DV6u.js → treemap-GDKQZRPO-Cl6OQh8D.js} +3 -3
- package/dist/{types-Dzuoc3LN.js → types-CVvp1fKr.js} +2 -9
- package/dist/{useAsyncData-C56Khv_R.js → useAsyncData-xWFWzCee.js} +2 -2
- package/dist/{useDateFormatter-B_9k85Ex.js → useDateFormatter-BA4FCquG.js} +2 -2
- package/dist/{useDeepCompareMemoize-Dt98v2ua.js → useDeepCompareMemoize-DSChED4g.js} +1 -1
- package/dist/{useIframeCapabilities-BkYHTrss.js → useIframeCapabilities-C4JTXTIh.js} +1 -1
- package/dist/{useLifecycle-BF6-z62y.js → useLifecycle-B81PFEja.js} +4 -4
- package/dist/{useTheme-DykuNHR2.js → useTheme-EmVyK9N9.js} +23 -21
- package/dist/{vega-component-cSdqoAxe.js → vega-component-BCunE3-9.js} +18 -18
- package/dist/{vega-loader.browser-3_z8GoFC.js → vega-loader.browser-CZ-J8Py3.js} +3 -3
- package/dist/{xychartDiagram-PRI3JC2R-Dk2d_bX0.js → xychartDiagram-PRI3JC2R-BvwftqMA.js} +9 -9
- package/dist/{zod-BWkcDORu.js → zod-CoBiJ5v4.js} +3 -3
- package/package.json +1 -1
- package/src/components/app-config/user-config-form.tsx +36 -0
- package/src/components/chat/__tests__/chat-utils.test.ts +269 -0
- package/src/components/chat/chat-utils.ts +14 -58
- package/src/components/data-table/TableBottomBar.tsx +27 -6
- package/src/components/data-table/TableTopBar.tsx +7 -1
- package/src/components/data-table/__tests__/TableBottomBar.test.tsx +73 -0
- package/src/components/data-table/__tests__/column-explorer.test.tsx +128 -0
- package/src/components/data-table/__tests__/column-header.test.tsx +110 -277
- package/src/components/data-table/__tests__/data-table.test.tsx +52 -1
- package/src/components/data-table/__tests__/date-filter-inputs.test.tsx +33 -0
- package/src/components/data-table/__tests__/filter-pill-editor.test.tsx +75 -38
- package/src/components/data-table/__tests__/filter-pills.test.tsx +287 -0
- package/src/components/data-table/__tests__/filter-test-utils.ts +47 -0
- package/src/components/data-table/__tests__/filters.test.ts +5 -5
- package/src/components/data-table/__tests__/header-items.test.tsx +47 -1
- package/src/components/data-table/__tests__/useColumnVisibility.test.ts +42 -0
- package/src/components/data-table/add-filter-button.tsx +85 -0
- package/src/components/data-table/column-explorer-panel/column-explorer.tsx +98 -26
- package/src/components/data-table/column-header.tsx +94 -691
- package/src/components/data-table/columns.tsx +3 -4
- package/src/components/data-table/context-menu.tsx +26 -12
- package/src/components/data-table/data-table.tsx +125 -56
- package/src/components/data-table/date-filter-inputs.tsx +13 -10
- package/src/components/data-table/export-actions.tsx +17 -6
- package/src/components/data-table/filter-by-values-picker.tsx +13 -19
- package/src/components/data-table/filter-editor-context.tsx +34 -0
- package/src/components/data-table/filter-pill-editor.tsx +152 -175
- package/src/components/data-table/filter-pills.tsx +190 -153
- package/src/components/data-table/filters/builders.ts +102 -0
- package/src/components/data-table/filters/defaults.ts +31 -0
- package/src/components/data-table/filters/format.ts +131 -0
- package/src/components/data-table/filters/guards.ts +51 -0
- package/src/components/data-table/filters/index.ts +7 -0
- package/src/components/data-table/filters/operators.ts +76 -0
- package/src/components/data-table/filters/serialize.ts +186 -0
- package/src/components/data-table/filters/types.ts +33 -0
- package/src/components/data-table/header-items.tsx +25 -85
- package/src/components/data-table/hooks/use-column-visibility.ts +56 -0
- package/src/components/data-table/pagination.tsx +16 -3
- package/src/components/data-table/table-explorer-panel/table-explorer-panel.tsx +16 -6
- package/src/components/data-table/value-chips.tsx +52 -0
- package/src/components/dependency-graph/minimap-content.tsx +2 -2
- package/src/components/editor/errors/mangled-local-chip.tsx +50 -0
- package/src/components/editor/output/MarimoErrorOutput.tsx +110 -27
- package/src/components/editor/output/MarimoTracebackOutput.tsx +51 -34
- package/src/components/ui/number-field.tsx +13 -1
- package/src/core/cells/__tests__/actions.test.ts +48 -0
- package/src/core/cells/actions.ts +5 -6
- package/src/core/codemirror/__tests__/setup.test.ts +29 -0
- package/src/core/codemirror/cells/traceback-decorations.ts +1 -1
- package/src/core/codemirror/cm.ts +3 -2
- package/src/core/config/__tests__/config-schema.test.ts +2 -0
- package/src/core/config/config-schema.ts +1 -0
- package/src/css/app/Cell.css +0 -1
- package/src/plugins/impl/DataTablePlugin.tsx +94 -33
- package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +1 -0
- package/src/plugins/impl/chat/ChatPlugin.tsx +7 -1
- package/src/plugins/impl/chat/__tests__/chat-ui.test.ts +278 -0
- package/src/plugins/impl/chat/chat-ui.tsx +106 -59
- package/src/plugins/impl/chat/types.ts +5 -0
- package/src/plugins/impl/data-frames/DataFramePlugin.tsx +8 -6
- package/src/stories/dataframe.stories.tsx +1 -0
- package/src/utils/__tests__/local-variables.test.ts +132 -0
- package/src/utils/dates.ts +39 -0
- package/src/utils/local-variables.ts +67 -0
- package/dist/ErrorBoundary-D3wrPNma.js +0 -167
- package/dist/architecture-7HQA4BMR-CS9jOrqM.js +0 -6
- package/dist/assets/__vite-browser-external-CAdMKBac.js +0 -1
- package/dist/dist-21ButRCu.js +0 -8
- package/dist/dist-B--tLnAP.js +0 -5
- package/dist/dist-BoHGySTM.js +0 -5
- package/dist/dist-ByAz19Qc.js +0 -5
- package/dist/dist-C1Ap5CYU.js +0 -5
- package/dist/dist-C93EysN4.js +0 -5
- package/dist/dist-CY-lVor6.js +0 -8
- package/dist/dist-CYDuv4bR.js +0 -8
- package/dist/dist-Cfo5EE2t.js +0 -6
- package/dist/dist-CjivSDvN.js +0 -5
- package/dist/dist-Cqwx-MH7.js +0 -5
- package/dist/dist-DbpcoFAV.js +0 -6
- package/dist/dist-FUNenbiQ.js +0 -5
- package/dist/dist-zhSud5X3.js +0 -8
- package/dist/mermaid-4DMBBIKO-B7VQMwJx.js +0 -6
- package/dist/react-vega-Cavbrg4l.js +0 -9
- package/dist/stex-ChDHQs3R.js +0 -4
- package/src/components/data-table/__tests__/column-header.test.ts +0 -65
- package/src/components/data-table/filters.ts +0 -386
- /package/dist/{_baseFor-BGiY-cm1.js → _baseFor-4jw-lnCC.js} +0 -0
- /package/dist/{clsx-CyyyQ8Ue.js → clsx-CIWA5tNO.js} +0 -0
- /package/dist/{defaultLocale-DoeErsX2.js → defaultLocale-BoHTsDG6.js} +0 -0
- /package/dist/{defaultLocale-BpsHxBd7.js → defaultLocale-u-3osm0P.js} +0 -0
- /package/dist/{dist-CCADb07R.js → dist-DNdhYsgW.js} +0 -0
- /package/dist/{emotion-is-prop-valid.esm-DtW2o230.js → emotion-is-prop-valid.esm-DzSb5hsH.js} +0 -0
- /package/dist/{invariant-UcGKQEhF.js → invariant-wRzNXIsJ.js} +0 -0
- /package/dist/{jsx-runtime-COBk7ree.js → jsx-runtime-DebpN0FN.js} +0 -0
- /package/dist/{main-CThhXnXU.js → main-Tj_-QTyF.js} +0 -0
- /package/dist/{micromark-factory-space-CwHmg6iz.js → micromark-factory-space-DF2w36zS.js} +0 -0
- /package/dist/{ordinal-B43ZeR68.js → ordinal-ArJavP1Q.js} +0 -0
- /package/dist/{purify.es-DT70lfR0.js → purify.es-H92eMd9-.js} +0 -0
- /package/dist/{range-BOiA8qqU.js → range-C-rmrM1O.js} +0 -0
- /package/dist/{react-dom-BWRJ_g_k.js → react-dom-BTJzcVJ9.js} +0 -0
- /package/dist/{stex-DrxP7bb3.js → stex-BIsgBmK4.js} +0 -0
|
@@ -20,13 +20,17 @@ import {
|
|
|
20
20
|
RotateCwIcon,
|
|
21
21
|
SendHorizontalIcon,
|
|
22
22
|
SettingsIcon,
|
|
23
|
+
SquareIcon,
|
|
23
24
|
Trash2Icon,
|
|
24
25
|
X,
|
|
25
26
|
} from "lucide-react";
|
|
26
27
|
import React, { useEffect, useRef, useState } from "react";
|
|
27
28
|
import { z } from "zod";
|
|
28
29
|
import { renderUIMessage } from "@/components/chat/chat-display";
|
|
29
|
-
import {
|
|
30
|
+
import {
|
|
31
|
+
convertToFileUIPart,
|
|
32
|
+
hasPendingToolCalls,
|
|
33
|
+
} from "@/components/chat/chat-utils";
|
|
30
34
|
import {
|
|
31
35
|
type AdditionalCompletions,
|
|
32
36
|
PromptInput,
|
|
@@ -60,6 +64,7 @@ import { cn } from "@/utils/cn";
|
|
|
60
64
|
import { Logger } from "@/utils/Logger";
|
|
61
65
|
import { Objects } from "@/utils/objects";
|
|
62
66
|
import { Strings } from "@/utils/strings";
|
|
67
|
+
import { generateUUID } from "@/utils/uuid";
|
|
63
68
|
import { ErrorBanner } from "../common/error-banner";
|
|
64
69
|
import type { PluginFunctions } from "./ChatPlugin";
|
|
65
70
|
import type { ChatConfig } from "./types";
|
|
@@ -86,6 +91,48 @@ const ChatMessageIncomingSchema = z.object({
|
|
|
86
91
|
is_final: z.boolean().optional(),
|
|
87
92
|
});
|
|
88
93
|
|
|
94
|
+
type ChatMessageIncoming = z.infer<typeof ChatMessageIncomingSchema>;
|
|
95
|
+
|
|
96
|
+
export interface IncomingChatChunkRefs {
|
|
97
|
+
controllerRef: {
|
|
98
|
+
current: ReadableStreamDefaultController<UIMessageChunk> | null;
|
|
99
|
+
};
|
|
100
|
+
activeRequestIdRef: { current: string | null };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Route a single incoming chunk to the active stream controller, dropping it
|
|
105
|
+
* if it belongs to a stale (aborted-but-not-yet-cancelled) backend run.
|
|
106
|
+
*/
|
|
107
|
+
export function routeIncomingChatChunk(
|
|
108
|
+
message: ChatMessageIncoming,
|
|
109
|
+
refs: IncomingChatChunkRefs,
|
|
110
|
+
): "enqueued" | "closed" | "dropped-no-controller" | "dropped-stale" {
|
|
111
|
+
const { controllerRef, activeRequestIdRef } = refs;
|
|
112
|
+
const controller = controllerRef.current;
|
|
113
|
+
if (controller === null) {
|
|
114
|
+
return "dropped-no-controller";
|
|
115
|
+
}
|
|
116
|
+
const activeRequestId = activeRequestIdRef.current;
|
|
117
|
+
if (activeRequestId !== null && message.message_id !== activeRequestId) {
|
|
118
|
+
Logger.debug("Dropping stale chat chunk", {
|
|
119
|
+
chunkRequestId: message.message_id,
|
|
120
|
+
activeRequestId,
|
|
121
|
+
});
|
|
122
|
+
return "dropped-stale";
|
|
123
|
+
}
|
|
124
|
+
if (message.content) {
|
|
125
|
+
controller.enqueue(message.content);
|
|
126
|
+
}
|
|
127
|
+
if (message.is_final) {
|
|
128
|
+
controller.close();
|
|
129
|
+
controllerRef.current = null;
|
|
130
|
+
activeRequestIdRef.current = null;
|
|
131
|
+
return "closed";
|
|
132
|
+
}
|
|
133
|
+
return "enqueued";
|
|
134
|
+
}
|
|
135
|
+
|
|
89
136
|
export const Chatbot: React.FC<Props> = (props) => {
|
|
90
137
|
const [input, setInput] = useState("");
|
|
91
138
|
const [config, setConfig] = useState<ChatConfig>(props.config);
|
|
@@ -113,16 +160,15 @@ export const Chatbot: React.FC<Props> = (props) => {
|
|
|
113
160
|
const configRef = useRef<ChatConfig>(config);
|
|
114
161
|
configRef.current = config;
|
|
115
162
|
|
|
116
|
-
// Track streaming state - maps backend message_id to frontend message index
|
|
117
|
-
const streamingStateRef = useRef<{
|
|
118
|
-
backendMessageId: string | null;
|
|
119
|
-
frontendMessageIndex: number | null;
|
|
120
|
-
}>({ backendMessageId: null, frontendMessageIndex: null });
|
|
121
|
-
|
|
122
163
|
// For frontend-managed streaming, create a controller to enqueue chunks to.
|
|
123
164
|
const frontendStreamControllerRef =
|
|
124
165
|
useRef<ReadableStreamDefaultController<UIMessageChunk> | null>(null);
|
|
125
166
|
|
|
167
|
+
// The request_id of the currently-active prompt run. Chunks arriving with a
|
|
168
|
+
// different message_id are stale (from an aborted-but-not-yet-cancelled run
|
|
169
|
+
// on the kernel) and must be dropped
|
|
170
|
+
const activeRequestIdRef = useRef<string | null>(null);
|
|
171
|
+
|
|
126
172
|
const { data: backendMessages } = useAsyncData(async () => {
|
|
127
173
|
const response = await props.get_chat_history({});
|
|
128
174
|
return response.messages;
|
|
@@ -143,7 +189,9 @@ export const Chatbot: React.FC<Props> = (props) => {
|
|
|
143
189
|
error,
|
|
144
190
|
regenerate,
|
|
145
191
|
clearError,
|
|
192
|
+
addToolApprovalResponse,
|
|
146
193
|
} = useChat({
|
|
194
|
+
sendAutomaticallyWhen: ({ messages }) => hasPendingToolCalls(messages),
|
|
147
195
|
transport: new DefaultChatTransport({
|
|
148
196
|
fetch: async (
|
|
149
197
|
request: RequestInfo | URL,
|
|
@@ -180,17 +228,33 @@ export const Chatbot: React.FC<Props> = (props) => {
|
|
|
180
228
|
};
|
|
181
229
|
});
|
|
182
230
|
|
|
231
|
+
// Client-generated id used to (a) route chunks back to this stream
|
|
232
|
+
// and (b) ask the kernel to cancel just this run on Stop.
|
|
233
|
+
const requestId = generateUUID();
|
|
234
|
+
|
|
183
235
|
const stream = new ReadableStream<UIMessageChunk>({
|
|
184
236
|
start(controller) {
|
|
185
237
|
frontendStreamControllerRef.current = controller;
|
|
238
|
+
activeRequestIdRef.current = requestId;
|
|
186
239
|
|
|
187
240
|
const abortHandler = () => {
|
|
241
|
+
// Close the local controller first so the chat status flips to
|
|
242
|
+
// "ready" immediately and any racing chunks are dropped; then
|
|
243
|
+
// fire-and-forget the backend cancel so the kernel stops the
|
|
244
|
+
// model and we don't waste tokens / leak chunks to the next
|
|
245
|
+
// run.
|
|
188
246
|
try {
|
|
189
247
|
controller.close();
|
|
190
248
|
} catch (error) {
|
|
191
249
|
Logger.debug("Controller may already be closed", { error });
|
|
192
250
|
}
|
|
193
251
|
frontendStreamControllerRef.current = null;
|
|
252
|
+
activeRequestIdRef.current = null;
|
|
253
|
+
void props
|
|
254
|
+
.cancel_prompt({ request_id: requestId })
|
|
255
|
+
.catch((error: Error) => {
|
|
256
|
+
Logger.debug("cancel_prompt failed", { error });
|
|
257
|
+
});
|
|
194
258
|
};
|
|
195
259
|
signal?.addEventListener("abort", abortHandler);
|
|
196
260
|
|
|
@@ -200,28 +264,25 @@ export const Chatbot: React.FC<Props> = (props) => {
|
|
|
200
264
|
},
|
|
201
265
|
cancel() {
|
|
202
266
|
frontendStreamControllerRef.current = null;
|
|
267
|
+
activeRequestIdRef.current = null;
|
|
203
268
|
},
|
|
204
269
|
});
|
|
205
270
|
|
|
206
271
|
// Start the prompt, chunks will be sent via events
|
|
207
272
|
void props
|
|
208
273
|
.send_prompt({
|
|
274
|
+
request_id: requestId,
|
|
209
275
|
messages: messages,
|
|
210
276
|
config: chatConfig,
|
|
211
277
|
})
|
|
212
278
|
.catch((error: Error) => {
|
|
213
279
|
frontendStreamControllerRef.current?.error(error);
|
|
214
280
|
frontendStreamControllerRef.current = null;
|
|
281
|
+
activeRequestIdRef.current = null;
|
|
215
282
|
});
|
|
216
283
|
|
|
217
284
|
return createUIMessageStreamResponse({ stream });
|
|
218
285
|
} catch (error: unknown) {
|
|
219
|
-
// Clear streaming state on error
|
|
220
|
-
streamingStateRef.current = {
|
|
221
|
-
backendMessageId: null,
|
|
222
|
-
frontendMessageIndex: null,
|
|
223
|
-
};
|
|
224
|
-
|
|
225
286
|
// Handle abort gracefully without showing an error
|
|
226
287
|
if (error instanceof Error && error.name === "AbortError") {
|
|
227
288
|
return new Response("Aborted", { status: 499 });
|
|
@@ -244,21 +305,10 @@ export const Chatbot: React.FC<Props> = (props) => {
|
|
|
244
305
|
}
|
|
245
306
|
Logger.debug("Finished streaming message:", message);
|
|
246
307
|
|
|
247
|
-
// Clear streaming state
|
|
248
|
-
streamingStateRef.current = {
|
|
249
|
-
backendMessageId: null,
|
|
250
|
-
frontendMessageIndex: null,
|
|
251
|
-
};
|
|
252
|
-
|
|
253
308
|
props.setValue(message.messages);
|
|
254
309
|
},
|
|
255
310
|
onError: (error) => {
|
|
256
311
|
Logger.error("An error occurred:", error);
|
|
257
|
-
// Clear streaming state on error
|
|
258
|
-
streamingStateRef.current = {
|
|
259
|
-
backendMessageId: null,
|
|
260
|
-
frontendMessageIndex: null,
|
|
261
|
-
};
|
|
262
312
|
},
|
|
263
313
|
});
|
|
264
314
|
|
|
@@ -273,23 +323,10 @@ export const Chatbot: React.FC<Props> = (props) => {
|
|
|
273
323
|
if (!parsedMessage.success) {
|
|
274
324
|
return;
|
|
275
325
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
if (!controller) {
|
|
281
|
-
return;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
if (message.content) {
|
|
285
|
-
controller.enqueue(message.content);
|
|
286
|
-
}
|
|
287
|
-
if (message.is_final) {
|
|
288
|
-
controller.close();
|
|
289
|
-
frontendStreamControllerRef.current = null;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
return;
|
|
326
|
+
routeIncomingChatChunk(parsedMessage.data, {
|
|
327
|
+
controllerRef: frontendStreamControllerRef,
|
|
328
|
+
activeRequestIdRef,
|
|
329
|
+
});
|
|
293
330
|
},
|
|
294
331
|
);
|
|
295
332
|
|
|
@@ -408,6 +445,9 @@ export const Chatbot: React.FC<Props> = (props) => {
|
|
|
408
445
|
message,
|
|
409
446
|
isStreamingReasoning: status === "streaming",
|
|
410
447
|
isLast,
|
|
448
|
+
addToolApprovalResponse: isLast
|
|
449
|
+
? addToolApprovalResponse
|
|
450
|
+
: undefined,
|
|
411
451
|
})}
|
|
412
452
|
</div>
|
|
413
453
|
<div className="flex justify-end text-xs gap-2 invisible group-hover:visible">
|
|
@@ -429,16 +469,8 @@ export const Chatbot: React.FC<Props> = (props) => {
|
|
|
429
469
|
})}
|
|
430
470
|
|
|
431
471
|
{isLoading && (
|
|
432
|
-
<div className="flex items-center justify-center
|
|
472
|
+
<div className="flex items-center justify-center mb-4">
|
|
433
473
|
<Spinner size="small" />
|
|
434
|
-
<Button
|
|
435
|
-
variant="link"
|
|
436
|
-
size="sm"
|
|
437
|
-
onClick={() => stop()}
|
|
438
|
-
className="text-(--red-9) hover:text-(--red-11)"
|
|
439
|
-
>
|
|
440
|
-
Stop
|
|
441
|
-
</Button>
|
|
442
474
|
</div>
|
|
443
475
|
)}
|
|
444
476
|
|
|
@@ -569,15 +601,30 @@ export const Chatbot: React.FC<Props> = (props) => {
|
|
|
569
601
|
/>
|
|
570
602
|
</>
|
|
571
603
|
)}
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
604
|
+
{isLoading ? (
|
|
605
|
+
<Tooltip content="Stop generating">
|
|
606
|
+
<Button
|
|
607
|
+
type="button"
|
|
608
|
+
variant="link"
|
|
609
|
+
size="xs"
|
|
610
|
+
onClick={() => stop()}
|
|
611
|
+
className="text-(--red-9) hover:text-(--red-11)"
|
|
612
|
+
>
|
|
613
|
+
<SquareIcon className="h-4 w-4 fill-current" />
|
|
614
|
+
</Button>
|
|
615
|
+
</Tooltip>
|
|
616
|
+
) : (
|
|
617
|
+
<Button
|
|
618
|
+
type="submit"
|
|
619
|
+
disabled={!input}
|
|
620
|
+
variant="outline"
|
|
621
|
+
size="xs"
|
|
622
|
+
className="text-(--slate-11)"
|
|
623
|
+
aria-label="Send message"
|
|
624
|
+
>
|
|
625
|
+
<SendHorizontalIcon className="h-4 w-4" />
|
|
626
|
+
</Button>
|
|
627
|
+
)}
|
|
581
628
|
</form>
|
|
582
629
|
</div>
|
|
583
630
|
);
|
|
@@ -64,7 +64,6 @@ 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;
|
|
68
67
|
}>;
|
|
69
68
|
get_column_values: (req: { column: string }) => Promise<{
|
|
70
69
|
values: unknown[];
|
|
@@ -82,9 +81,11 @@ type PluginFunctions = {
|
|
|
82
81
|
}) => Promise<{
|
|
83
82
|
data: TableData<T>;
|
|
84
83
|
total_rows: number;
|
|
85
|
-
size_bytes?: number | null;
|
|
86
84
|
}>;
|
|
87
85
|
download_as: DownloadAsArgs;
|
|
86
|
+
get_size_bytes: (opts: Record<string, never>) => Promise<{
|
|
87
|
+
size_bytes?: number | null;
|
|
88
|
+
}>;
|
|
88
89
|
};
|
|
89
90
|
|
|
90
91
|
// Value is selection, but it is not currently exposed to the user
|
|
@@ -120,7 +121,6 @@ export const DataFramePlugin = createPlugin<S>("marimo-dataframe")
|
|
|
120
121
|
column_types_per_step: z.array(columnToFieldTypesSchema),
|
|
121
122
|
python_code: z.string().nullish(),
|
|
122
123
|
sql_code: z.string().nullish(),
|
|
123
|
-
size_bytes: z.number().nullish(),
|
|
124
124
|
}),
|
|
125
125
|
),
|
|
126
126
|
get_column_values: rpc.input(z.object({ column: z.string() })).output(
|
|
@@ -150,10 +150,12 @@ export const DataFramePlugin = createPlugin<S>("marimo-dataframe")
|
|
|
150
150
|
z.object({
|
|
151
151
|
data: z.union([z.string(), z.array(z.object({}).passthrough())]),
|
|
152
152
|
total_rows: z.number(),
|
|
153
|
-
size_bytes: z.number().nullish(),
|
|
154
153
|
}),
|
|
155
154
|
),
|
|
156
155
|
download_as: DownloadAsSchema,
|
|
156
|
+
get_size_bytes: rpc
|
|
157
|
+
.input(z.object({}))
|
|
158
|
+
.output(z.object({ size_bytes: z.number().nullish() })),
|
|
157
159
|
})
|
|
158
160
|
.renderer((props) => (
|
|
159
161
|
<TableProviders>
|
|
@@ -192,6 +194,7 @@ export const DataFrameComponent = memo(
|
|
|
192
194
|
get_column_values,
|
|
193
195
|
search,
|
|
194
196
|
download_as,
|
|
197
|
+
get_size_bytes,
|
|
195
198
|
host,
|
|
196
199
|
}: DataTableProps): JSX.Element => {
|
|
197
200
|
const { data, error, isPending } = useAsyncData(
|
|
@@ -207,7 +210,6 @@ export const DataFrameComponent = memo(
|
|
|
207
210
|
column_types_per_step,
|
|
208
211
|
python_code,
|
|
209
212
|
sql_code,
|
|
210
|
-
size_bytes,
|
|
211
213
|
} = data || {};
|
|
212
214
|
|
|
213
215
|
const totalColumns = field_types?.length;
|
|
@@ -327,7 +329,6 @@ export const DataFrameComponent = memo(
|
|
|
327
329
|
data={url || ""}
|
|
328
330
|
hasStableRowId={false}
|
|
329
331
|
totalRows={total_rows ?? 0}
|
|
330
|
-
sizeBytes={size_bytes ?? null}
|
|
331
332
|
totalColumns={totalColumns ?? 0}
|
|
332
333
|
maxColumns="all"
|
|
333
334
|
pageSize={pageSize}
|
|
@@ -336,6 +337,7 @@ export const DataFrameComponent = memo(
|
|
|
336
337
|
rowHeaders={row_headers || Arrays.EMPTY}
|
|
337
338
|
showDownload={showDownload}
|
|
338
339
|
download_as={download_as}
|
|
340
|
+
get_size_bytes={get_size_bytes}
|
|
339
341
|
enableSearch={false}
|
|
340
342
|
showFilters={false}
|
|
341
343
|
search={search}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import {
|
|
4
|
+
containsMangledLocal,
|
|
5
|
+
splitMangledLocals,
|
|
6
|
+
unmangleLocal,
|
|
7
|
+
} from "../local-variables";
|
|
8
|
+
|
|
9
|
+
describe("unmangleLocal", () => {
|
|
10
|
+
it("extracts the cell id and original name", () => {
|
|
11
|
+
expect(unmangleLocal("_cell_Hbol_a")).toEqual({
|
|
12
|
+
cellId: "Hbol",
|
|
13
|
+
name: "_a",
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("handles names with multiple underscore segments", () => {
|
|
18
|
+
expect(unmangleLocal("_cell_Hbol_a_b")).toEqual({
|
|
19
|
+
cellId: "Hbol",
|
|
20
|
+
name: "_a_b",
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("returns null for non-mangled strings", () => {
|
|
25
|
+
expect(unmangleLocal("just_a_variable")).toBeNull();
|
|
26
|
+
expect(unmangleLocal("_private")).toBeNull();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("handles the single-underscore local", () => {
|
|
30
|
+
// `_` is a valid local name (variables.py:62-63), mangling to
|
|
31
|
+
// `_cell_<id>_` with no trailing suffix.
|
|
32
|
+
expect(unmangleLocal("_cell_Hbol_")).toEqual({
|
|
33
|
+
cellId: "Hbol",
|
|
34
|
+
name: "_",
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("handles UUID-style cell ids", () => {
|
|
39
|
+
// External / VSCode notebooks use `external_prefix()` which is a
|
|
40
|
+
// `uuid4()` (hyphenated).
|
|
41
|
+
expect(
|
|
42
|
+
unmangleLocal("_cell_c9bf9e57-1685-4c89-bafb-ff5af830be8a_a"),
|
|
43
|
+
).toEqual({
|
|
44
|
+
cellId: "c9bf9e57-1685-4c89-bafb-ff5af830be8a",
|
|
45
|
+
name: "_a",
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("does not match marimo cell file paths", () => {
|
|
50
|
+
// The compiled cell file is `__marimo__cell_<id>_.py` (two leading
|
|
51
|
+
// underscores, trailing `_` with no name); it must not be confused with a
|
|
52
|
+
// mangled local.
|
|
53
|
+
expect(unmangleLocal("__marimo__cell_Hbol_.py")).toBeNull();
|
|
54
|
+
expect(unmangleLocal("/tmp/marimo_42/__marimo__cell_Hbol_.py")).toBeNull();
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe("splitMangledLocals", () => {
|
|
59
|
+
it("returns a single text segment when nothing matches", () => {
|
|
60
|
+
expect(splitMangledLocals("plain text")).toEqual(["plain text"]);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("splits a NameError message", () => {
|
|
64
|
+
expect(splitMangledLocals("name '_cell_Hbol_a' is not defined")).toEqual([
|
|
65
|
+
"name '",
|
|
66
|
+
{ cellId: "Hbol", name: "_a" },
|
|
67
|
+
"' is not defined",
|
|
68
|
+
]);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("handles multiple mangled names in one string", () => {
|
|
72
|
+
expect(splitMangledLocals("_cell_AAAA_x and _cell_BBBB_y")).toEqual([
|
|
73
|
+
{ cellId: "AAAA", name: "_x" },
|
|
74
|
+
" and ",
|
|
75
|
+
{ cellId: "BBBB", name: "_y" },
|
|
76
|
+
]);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("leaves the cell file path alone", () => {
|
|
80
|
+
const path = "/tmp/marimo_42/__marimo__cell_Hbol_.py";
|
|
81
|
+
expect(splitMangledLocals(path)).toEqual([path]);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("ignores `_cell_...` substrings preceded by `_`", () => {
|
|
85
|
+
// Mirrors `(?<!_)` in `variables.py`: a leading `_` (e.g. inside
|
|
86
|
+
// `__marimo__cell_<id>_<...>`) means this is not a mangle the compiler
|
|
87
|
+
// produced, so we must not demangle it.
|
|
88
|
+
const text = "see __marimo__cell_Hbol_a for details";
|
|
89
|
+
expect(splitMangledLocals(text)).toEqual([text]);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("splits a UUID-style cell id", () => {
|
|
93
|
+
expect(
|
|
94
|
+
splitMangledLocals(
|
|
95
|
+
"name '_cell_c9bf9e57-1685-4c89-bafb-ff5af830be8a_a' is not defined",
|
|
96
|
+
),
|
|
97
|
+
).toEqual([
|
|
98
|
+
"name '",
|
|
99
|
+
{
|
|
100
|
+
cellId: "c9bf9e57-1685-4c89-bafb-ff5af830be8a",
|
|
101
|
+
name: "_a",
|
|
102
|
+
},
|
|
103
|
+
"' is not defined",
|
|
104
|
+
]);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("splits the single-underscore local", () => {
|
|
108
|
+
expect(splitMangledLocals("name '_cell_Hbol_' is not defined")).toEqual([
|
|
109
|
+
"name '",
|
|
110
|
+
{ cellId: "Hbol", name: "_" },
|
|
111
|
+
"' is not defined",
|
|
112
|
+
]);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
describe("containsMangledLocal", () => {
|
|
117
|
+
it("detects mangled names", () => {
|
|
118
|
+
expect(containsMangledLocal("name '_cell_Hbol_a' is not defined")).toBe(
|
|
119
|
+
true,
|
|
120
|
+
);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("ignores the cell file path", () => {
|
|
124
|
+
expect(containsMangledLocal("/tmp/marimo_42/__marimo__cell_Hbol_.py")).toBe(
|
|
125
|
+
false,
|
|
126
|
+
);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("ignores `_cell_...` substrings preceded by `_`", () => {
|
|
130
|
+
expect(containsMangledLocal("see __marimo__cell_Hbol_a")).toBe(false);
|
|
131
|
+
});
|
|
132
|
+
});
|
package/src/utils/dates.ts
CHANGED
|
@@ -134,6 +134,45 @@ export function timeAgo(
|
|
|
134
134
|
return value.toString();
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
+
function pad2(n: number): string {
|
|
138
|
+
return n.toString().padStart(2, "0");
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function pad4(n: number): string {
|
|
142
|
+
return n.toString().padStart(4, "0");
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Format a Date as `YYYY-MM-DD` using the date's local-time fields.
|
|
147
|
+
*
|
|
148
|
+
* The output reflects what the user sees in their own timezone (the calendar
|
|
149
|
+
* day on their clock), not the UTC day. Use this when round-tripping values
|
|
150
|
+
* that originated from local-time inputs — date pickers, "filter on this
|
|
151
|
+
* day", calendar UI — so the displayed and serialized days agree.
|
|
152
|
+
*
|
|
153
|
+
* Not suitable for cross-timezone storage; use `Date.toISOString()` for that.
|
|
154
|
+
*/
|
|
155
|
+
export function dateToLocalISODate(d: Date): string {
|
|
156
|
+
return `${pad4(d.getFullYear())}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Format a Date as `HH:MM:SS` using the date's local-time fields.
|
|
161
|
+
*
|
|
162
|
+
* See `dateToLocalISODate` for the rationale on local vs UTC.
|
|
163
|
+
*/
|
|
164
|
+
export function dateToLocalISOTime(d: Date): string {
|
|
165
|
+
return `${pad2(d.getHours())}:${pad2(d.getMinutes())}:${pad2(d.getSeconds())}`;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Format a Date as `YYYY-MM-DDTHH:MM:SS` (no timezone suffix) using local
|
|
170
|
+
* fields. See `dateToLocalISODate` for the rationale on local vs UTC.
|
|
171
|
+
*/
|
|
172
|
+
export function dateToLocalISODateTime(d: Date): string {
|
|
173
|
+
return `${dateToLocalISODate(d)}T${dateToLocalISOTime(d)}`;
|
|
174
|
+
}
|
|
175
|
+
|
|
137
176
|
export const supportedDateFormats = ["yyyy", "yyyy-MM", "yyyy-MM-dd"] as const;
|
|
138
177
|
export type DateFormat = (typeof supportedDateFormats)[number];
|
|
139
178
|
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
import type { CellId } from "@/core/cells/ids";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The marimo compiler rewrites underscore-prefixed references inside a cell
|
|
6
|
+
* (which Python would treat as module-private) into `_cell_<cell_id>_<name>`
|
|
7
|
+
* so each cell gets its own private namespace. When such a reference fails at
|
|
8
|
+
* runtime (`NameError: name '_cell_Hbol_a' is not defined`), the mangled name
|
|
9
|
+
* leaks into the error UI. These helpers undo that mangling for display.
|
|
10
|
+
*
|
|
11
|
+
* Mirrors `marimo/_ast/variables.py`.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
// Matches `_cell_<cell_id><name>` for normal ids and UUIDs. The `[\w-]`
|
|
15
|
+
// id class admits hyphens; the `_\w*` name group admits the bare `_`
|
|
16
|
+
// local; the `(?<!_)` lookbehind skips `__marimo__cell_...` paths.
|
|
17
|
+
// Mirrors `_MANGLED_LOCAL_IN_TEXT_RE` in `variables.py`.
|
|
18
|
+
const MANGLED_LOCAL_BODY = String.raw`_cell_([^\W_][\w-]*?)(_\w*)`;
|
|
19
|
+
const MANGLED_LOCAL_PATTERN = String.raw`(?<!_)${MANGLED_LOCAL_BODY}`;
|
|
20
|
+
// Strict (whole-string) form for `unmangleLocal`; the leading `^` makes the
|
|
21
|
+
// lookbehind trivially satisfied, so use the bare body.
|
|
22
|
+
const ANCHORED_RE = new RegExp(`^${MANGLED_LOCAL_BODY}$`);
|
|
23
|
+
const UNANCHORED_RE = new RegExp(MANGLED_LOCAL_PATTERN);
|
|
24
|
+
const GLOBAL_RE = new RegExp(MANGLED_LOCAL_PATTERN, "g");
|
|
25
|
+
|
|
26
|
+
export interface UnmangledLocal {
|
|
27
|
+
cellId: CellId;
|
|
28
|
+
/** The original underscore-prefixed name, e.g. "_a". */
|
|
29
|
+
name: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function unmangleLocal(mangled: string): UnmangledLocal | null {
|
|
33
|
+
const match = ANCHORED_RE.exec(mangled);
|
|
34
|
+
if (!match) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
return { cellId: match[1] as CellId, name: match[2] };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type MangledSegment = string | UnmangledLocal;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Split a plain text string into alternating literal text and unmangled-local
|
|
44
|
+
* segments, so callers can render mixed React content.
|
|
45
|
+
*/
|
|
46
|
+
export function splitMangledLocals(text: string): MangledSegment[] {
|
|
47
|
+
const segments: MangledSegment[] = [];
|
|
48
|
+
GLOBAL_RE.lastIndex = 0;
|
|
49
|
+
let lastIndex = 0;
|
|
50
|
+
let match: RegExpExecArray | null = GLOBAL_RE.exec(text);
|
|
51
|
+
while (match !== null) {
|
|
52
|
+
if (match.index > lastIndex) {
|
|
53
|
+
segments.push(text.slice(lastIndex, match.index));
|
|
54
|
+
}
|
|
55
|
+
segments.push({ cellId: match[1] as CellId, name: match[2] });
|
|
56
|
+
lastIndex = match.index + match[0].length;
|
|
57
|
+
match = GLOBAL_RE.exec(text);
|
|
58
|
+
}
|
|
59
|
+
if (lastIndex < text.length) {
|
|
60
|
+
segments.push(text.slice(lastIndex));
|
|
61
|
+
}
|
|
62
|
+
return segments;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function containsMangledLocal(text: string): boolean {
|
|
66
|
+
return UNANCHORED_RE.test(text);
|
|
67
|
+
}
|