@marimo-team/frontend 0.15.4 → 0.16.0-dev96986
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/assets/ConnectedDataExplorerComponent-CNLoZkWr.js +19 -0
- package/dist/assets/{ImageComparisonComponent-CEXMKKA4.js → ImageComparisonComponent-SX7fDaTK.js} +1 -1
- package/dist/assets/{VegaLite-Bt14Ds9k.js → VegaLite-MJUW3b7C.js} +6 -6
- package/dist/assets/_baseEach-9_logFrf.js +1 -0
- package/dist/assets/_baseMap-NzEbKt5c.js +1 -0
- package/dist/assets/_baseUniq-C5LFcyNC.js +1 -0
- package/dist/assets/_createAggregator-ZRm2b6Zm.js +1 -0
- package/dist/assets/agent-panel-BBd11wNX.js +287 -0
- package/dist/assets/agent-panel-D92Mfy1i.css +1 -0
- package/dist/assets/{any-language-editor-DiwNT6zp.js → any-language-editor-DwAaEQfS.js} +1 -1
- package/dist/assets/architectureDiagram-W76B3OCA-BJmVXUoW.js +36 -0
- package/dist/assets/{between-horizontal-start-FyewyCGn.js → between-horizontal-start-KiwU-a3C.js} +1 -1
- package/dist/assets/{blockDiagram-QIGZ2CNN-BrOkAf_c.js → blockDiagram-QIGZ2CNN-DzxZjE7B.js} +1 -1
- package/dist/assets/{c4Diagram-FPNF74CW-BHPzDxE2.js → c4Diagram-FPNF74CW-DjmldG_J.js} +5 -5
- package/dist/assets/channel-DHcKBVM4.js +1 -0
- package/dist/assets/chat-panel-DgJZr0eS.js +3 -0
- package/dist/assets/{chunk-4BX2VUAB-DLxaCNYh.js → chunk-4BX2VUAB-EUTQThiZ.js} +1 -1
- package/dist/assets/{chunk-55IACEB6-DdzvO3HR.js → chunk-55IACEB6-DZAiDJxy.js} +1 -1
- package/dist/assets/{chunk-FMBD7UC4-R5o-nSiG.js → chunk-FMBD7UC4-Bd0Czs-J.js} +1 -1
- package/dist/assets/{chunk-K7UQS3LO-DxaMrGgG.js → chunk-K7UQS3LO-DEKMIknX.js} +1 -1
- package/dist/assets/{chunk-QN33PNHL-DqS9-FYm.js → chunk-QN33PNHL-E0jwHU_n.js} +1 -1
- package/dist/assets/{chunk-QZHKN3VN-BZ-TzajS.js → chunk-QZHKN3VN-BzaIHJbq.js} +1 -1
- package/dist/assets/{chunk-TVAH2DTR-BsgP2dyv.js → chunk-TVAH2DTR-CZFYvqnm.js} +1 -1
- package/dist/assets/{chunk-TZMSLE5B-D-h3ahXI.js → chunk-TZMSLE5B-BNqnFjtv.js} +1 -1
- package/dist/assets/{circle-play-CQtRZ-rT.js → circle-play-D3J_mYrF.js} +1 -1
- package/dist/assets/classDiagram-KNZD7YFC-D-xwLnlX.js +1 -0
- package/dist/assets/classDiagram-v2-RKCZMP56-D-xwLnlX.js +1 -0
- package/dist/assets/{clear-button-BY6Z_ViL.js → clear-button-ifzRuAR3.js} +1 -1
- package/dist/assets/clone-CSxIll62.js +1 -0
- package/dist/assets/command-palette-D2fdVSET.js +1 -0
- package/dist/assets/common-Ku-cF_2J.js +1 -0
- package/dist/assets/{compile-Ct_jzdKr.js → compile-BgZlHW1c.js} +1 -1
- package/dist/assets/cose-bilkent-S5V4N54A-CVM83SqK.js +1 -0
- package/dist/assets/dagre-5GWH7T2D-ouQPkxT3.js +4 -0
- package/dist/assets/{data-grid-overlay-editor-BN_wulc3.js → data-grid-overlay-editor-B47j5GJJ.js} +1 -1
- package/dist/assets/datasources-panel-Bt41Zir-.js +1 -0
- package/dist/assets/{dependency-graph-panel-BOmSCZf7.js → dependency-graph-panel-CZC_B7pK.js} +4 -4
- package/dist/assets/diagram-N5W7TBWH-CQ817ZdR.js +24 -0
- package/dist/assets/diagram-QEK2KX5R-DOK_psUO.js +43 -0
- package/dist/assets/diagram-S2PKOQOG-CVljmOW8.js +24 -0
- package/dist/assets/{documentation-panel-BxjJO_Gw.js → documentation-panel-C7yIvGg1.js} +1 -1
- package/dist/assets/edit-page-CyTMQV2u.js +129 -0
- package/dist/assets/{ellipsis-vertical-UHbmjI2n.js → ellipsis-vertical-C7FjlUsY.js} +1 -1
- package/dist/assets/{empty-state-BIBXzY_0.js → empty-state-DIOGM_CU.js} +1 -1
- package/dist/assets/{erDiagram-AWTI2OKA-E84mAle_.js → erDiagram-AWTI2OKA-DYu8cEdc.js} +1 -1
- package/dist/assets/{error-panel-MEvQ6K7h.js → error-panel-Ddb8RkFG.js} +1 -1
- package/dist/assets/file-explorer-panel-Oy9DbyFP.js +1 -0
- package/dist/assets/{flowDiagram-PVAE7QVJ-DfbIRSAW.js → flowDiagram-PVAE7QVJ-CmvW5iTb.js} +1 -1
- package/dist/assets/{ganttDiagram-OWAHRB6G-DR4HZ1z_.js → ganttDiagram-OWAHRB6G-BaKQlCaT.js} +4 -4
- package/dist/assets/gitGraphDiagram-NY62KEGX-CWO24eP6.js +65 -0
- package/dist/assets/{glide-data-editor-nNmo1lPq.js → glide-data-editor-CNDLEJ9a.js} +11 -11
- package/dist/assets/graph-BZKTtxsc.js +1 -0
- package/dist/assets/home-page-Bvwppn9N.js +9 -0
- package/dist/assets/{index-VPWqq2Pg.js → index-0XOUPdwT.js} +1 -1
- package/dist/assets/{index-uacyUula.js → index-BH7f3aiU.js} +1 -1
- package/dist/assets/{index-Dt9UWeWn.js → index-BJVyzkx5.js} +1 -1
- package/dist/assets/{index-BAH034Ue.js → index-B_d_JZGI.js} +1 -1
- package/dist/assets/{index-CB2pnVQG.js → index-BgXbBA39.js} +1 -1
- package/dist/assets/{index-B8llrTSo.js → index-Brf2DwUM.js} +1 -1
- package/dist/assets/{index-BLu5CX6z.js → index-CXrWwFX6.js} +1 -1
- package/dist/assets/{index-DyLSuOH1.js → index-CZaurnA9.js} +1 -1
- package/dist/assets/{index-BFSnz7iM.js → index-CerjupfZ.js} +1 -1
- package/dist/assets/{index-B7yXbrLa.js → index-D-tZfElD.js} +1 -1
- package/dist/assets/{index-c6If577Q.js → index-D3PqGupX.js} +1 -1
- package/dist/assets/{index-CSgxTUzD.js → index-DCkzth56.js} +1 -1
- package/dist/assets/{index-DWOaniGT.js → index-DFrGFNW1.js} +1 -1
- package/dist/assets/{index-CPN7TRA1.js → index-DZhOPkOB.js} +1 -1
- package/dist/assets/index-DadI618h.css +1 -0
- package/dist/assets/{index-DqzMPAC8.js → index-DkntzpX4.js} +2 -2
- package/dist/assets/{index-B1_GXGaP.js → index-DmgwT3sx.js} +1 -1
- package/dist/assets/index-PmY0x4Zd.js +578 -0
- package/dist/assets/{index-Bq516OmX.js → index-WXJFkQHg.js} +1 -1
- package/dist/assets/{index-DSU75csX.js → index-qE8lHQ-N.js} +1 -1
- package/dist/assets/{index-DMomwMcN.js → index-zrSUQXha.js} +1 -1
- package/dist/assets/infoDiagram-STP46IZ2-CAuVVehw.js +2 -0
- package/dist/assets/isEmpty-D1t7Gran.js +1 -0
- package/dist/assets/{journeyDiagram-BIP6EPQ6-BBiFyygf.js → journeyDiagram-BIP6EPQ6-D4Rp6H_h.js} +1 -1
- package/dist/assets/{kanban-definition-6OIFK2YF-DhgA6Nt6.js → kanban-definition-6OIFK2YF-DFt9DftA.js} +4 -4
- package/dist/assets/layout-D8WXi2_g.js +1 -0
- package/dist/assets/linear-BwY8e5hA.js +1 -0
- package/dist/assets/links-4B6ldZ5P.js +7 -0
- package/dist/assets/{logs-panel-B9SmTZAW.js → logs-panel-Dxiyt7dO.js} +1 -1
- package/dist/assets/{agent-panel-DpQ6muj-.css → markdown-renderer-ClyzDMmG.css} +1 -1
- package/dist/assets/markdown-renderer-VDu-NBKB.js +263 -0
- package/dist/assets/mermaid-B-O-Puyi.js +1 -0
- package/dist/assets/{mermaid.core-4nVOEVX3.js → mermaid.core-BFFCqfOn.js} +41 -41
- package/dist/assets/min-DtVSfYKl.js +1 -0
- package/dist/assets/{mindmap-definition-Q6HEUPPD-CVLQNn1q.js → mindmap-definition-Q6HEUPPD-kyvIY8Dg.js} +2 -2
- package/dist/assets/{number-overlay-editor-CzRzXLcd.js → number-overlay-editor-GjLB2UK4.js} +1 -1
- package/dist/assets/outline-panel-CMJjOoN7.js +1 -0
- package/dist/assets/packages-panel-nfXB-bKW.js +1 -0
- package/dist/assets/{pieDiagram-ADFJNKIX-C5IQ5DBZ.js → pieDiagram-ADFJNKIX-D8JFQcWR.js} +3 -3
- package/dist/assets/{quadrantDiagram-LMRXKWRM-CFXFnQxx.js → quadrantDiagram-LMRXKWRM-Nf8GzxXG.js} +1 -1
- package/dist/assets/{react-plotly-mzdv02_Y.js → react-plotly-CnW9p7ZA.js} +1 -1
- package/dist/assets/{requirementDiagram-4UW4RH46-D9bPC89T.js → requirementDiagram-4UW4RH46-CCUxF8BZ.js} +1 -1
- package/dist/assets/run-page-Bl4p3AbZ.js +1 -0
- package/dist/assets/sankeyDiagram-GR3RE2ED-Sr8kDwP1.js +10 -0
- package/dist/assets/scratchpad-panel-Ja1Mu-W3.js +1 -0
- package/dist/assets/secrets-panel-B-3fcSyP.js +1 -0
- package/dist/assets/{sequenceDiagram-C3RYC4MD-6N7_hY4k.js → sequenceDiagram-C3RYC4MD-CBJ152Q3.js} +4 -4
- package/dist/assets/{slides-component-DMjQomc3.css → slides-component-C-LoGC1U.css} +1 -1
- package/dist/assets/{slides-component-EcjC8sDK.js → slides-component-DGtsVP5o.js} +1 -1
- package/dist/assets/snippets-panel-ClNnwKBM.js +1 -0
- package/dist/assets/sortBy-D47H6Vyl.js +1 -0
- package/dist/assets/state-B_RCHTH5.js +1 -0
- package/dist/assets/stateDiagram-KXAO66HF-BlBFSAZr.js +1 -0
- package/dist/assets/stateDiagram-v2-UMBNRL4Z-DbA-iToo.js +1 -0
- package/dist/assets/storage-BNcWOH3-.js +26 -0
- package/dist/assets/terminal-CATzv5Hd.js +10 -0
- package/dist/assets/time-CsYqILfB.js +1 -0
- package/dist/assets/{timeline-definition-XQNQX7LJ-BEaynAiY.js → timeline-definition-XQNQX7LJ-CGrhjuAs.js} +1 -1
- package/dist/assets/tracing-DUbJtOyq.js +2 -0
- package/dist/assets/{tracing-panel-BmuHLPrY.js → tracing-panel-DmzqPUtc.js} +2 -2
- package/dist/assets/{trash-UBqfK4mR.js → trash-rxdjLzkf.js} +1 -1
- package/dist/assets/{tree-XiEycetl.js → tree-C2Ul1h1C.js} +1 -1
- package/dist/assets/{treemap-75Q7IDZK-CnuVFbBG.js → treemap-75Q7IDZK-N9hyUpyj.js} +20 -20
- package/dist/assets/{ts-tags-CloPe9IY.js → ts-tags-DxCDHihD.js} +1 -1
- package/dist/assets/variable-panel-BbgupOdG.js +1 -0
- package/dist/assets/{vega-component-DsTH4tuX.js → vega-component-CR_MHOBT.js} +1 -1
- package/dist/assets/worker-fHbtoWvT.js +1 -0
- package/dist/assets/{xychartDiagram-6GGTOJPD-Dcz3O-A3.js → xychartDiagram-6GGTOJPD-jdLZsMb2.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +10 -5
- package/src/__tests__/mocks.ts +43 -0
- package/src/components/app-config/user-config-form.tsx +78 -1
- package/src/components/chat/acp/__tests__/__snapshots__/prompt.test.ts.snap +116 -65
- package/src/components/chat/acp/__tests__/atoms.test.ts +1 -1
- package/src/components/chat/acp/__tests__/context-utils.test.ts +222 -0
- package/src/components/chat/acp/__tests__/prompt.test.ts +1 -1
- package/src/components/chat/acp/__tests__/state.test.ts +38 -42
- package/src/components/chat/acp/agent-docs.tsx +33 -6
- package/src/components/chat/acp/agent-panel.css +0 -18
- package/src/components/chat/acp/agent-panel.tsx +394 -72
- package/src/components/chat/acp/agent-selector.tsx +7 -1
- package/src/components/chat/acp/blocks.tsx +40 -10
- package/src/components/chat/acp/common.tsx +10 -2
- package/src/components/chat/acp/context-utils.ts +127 -0
- package/src/components/chat/acp/prompt.ts +96 -53
- package/src/components/chat/acp/state.ts +1 -1
- package/src/components/chat/acp/types.ts +8 -0
- package/src/components/chat/chat-panel.tsx +28 -89
- package/src/components/chat/chat-utils.ts +127 -1
- package/src/components/chat/markdown-renderer.css +39 -0
- package/src/components/chat/markdown-renderer.tsx +12 -47
- package/src/components/chat/tool-call-accordion.tsx +148 -26
- package/src/components/data-table/SearchBar.tsx +8 -7
- package/src/components/data-table/__tests__/column_formatting.test.ts +50 -35
- package/src/components/data-table/__tests__/data-table.test.tsx +39 -1
- package/src/components/data-table/cell-hover-template/feature.ts +14 -0
- package/src/components/data-table/cell-hover-template/types.ts +11 -0
- package/src/components/data-table/charts/components/form-fields.tsx +41 -37
- package/src/components/data-table/charts/forms/common-chart.tsx +2 -2
- package/src/components/data-table/column-explorer-panel/column-explorer.tsx +5 -2
- package/src/components/data-table/column-formatting/feature.ts +62 -29
- package/src/components/data-table/column-formatting/types.ts +1 -0
- package/src/components/data-table/column-header.tsx +3 -1
- package/src/components/data-table/column-summary/chart-spec-model.tsx +24 -7
- package/src/components/data-table/column-summary/column-summary.tsx +18 -9
- package/src/components/data-table/columns.tsx +42 -18
- package/src/components/data-table/data-table.tsx +10 -2
- package/src/components/data-table/date-popover.tsx +85 -75
- package/src/components/data-table/filter-pills.tsx +14 -9
- package/src/components/data-table/header-items.tsx +5 -1
- package/src/components/data-table/pagination.tsx +20 -13
- package/src/components/data-table/renderers.tsx +28 -0
- package/src/components/data-table/row-viewer-panel/row-viewer.tsx +10 -8
- package/src/components/datasources/column-preview.tsx +6 -2
- package/src/components/datasources/datasources.tsx +8 -12
- package/src/components/editor/Cell.tsx +6 -0
- package/src/components/editor/actions/name-cell-input.tsx +6 -1
- package/src/components/editor/actions/useCellActionButton.tsx +3 -1
- package/src/components/editor/ai/__tests__/completion-utils.test.ts +178 -1
- package/src/components/editor/ai/add-cell-with-ai.tsx +68 -66
- package/src/components/editor/ai/ai-completion-editor.tsx +29 -26
- package/src/components/editor/ai/completion-handlers.tsx +44 -6
- package/src/components/editor/ai/completion-utils.ts +92 -0
- package/src/components/editor/ai/transport/chat-transport.tsx +39 -0
- package/src/components/editor/cell/CellStatus.tsx +23 -20
- package/src/components/editor/cell/CreateCellButton.tsx +3 -4
- package/src/components/editor/cell/StagedAICell.tsx +51 -0
- package/src/components/editor/cell/cell-actions.tsx +2 -1
- package/src/components/editor/cell/code/language-toggle.tsx +3 -4
- package/src/components/editor/chrome/wrapper/footer-items/machine-stats.tsx +39 -28
- package/src/components/editor/controls/notebook-menu-dropdown.tsx +4 -2
- package/src/components/editor/file-tree/requesting-tree.tsx +14 -8
- package/src/components/editor/renderers/CellArray.tsx +3 -4
- package/src/components/editor/renderers/slides-layout/slides-layout.tsx +3 -3
- package/src/components/editor/renderers/slides-layout/types.ts +1 -0
- package/src/components/pages/home-page.tsx +4 -1
- package/src/components/slides/slides-component.tsx +1 -1
- package/src/components/slides/slides.css +6 -0
- package/src/components/terminal/__tests__/state.test.ts +207 -0
- package/src/components/terminal/hooks.ts +41 -0
- package/src/components/terminal/state.ts +75 -0
- package/src/components/terminal/terminal.tsx +334 -13
- package/src/components/terminal/theme.tsx +57 -0
- package/src/components/tracing/tracing-spec.ts +5 -4
- package/src/components/ui/range-slider.tsx +4 -2
- package/src/components/ui/slider.tsx +3 -1
- package/src/components/variables/variables-table.tsx +3 -0
- package/src/core/MarimoApp.tsx +9 -6
- package/src/core/ai/__tests__/staged-cells.test.ts +356 -0
- package/src/core/ai/context/__tests__/registry.test.ts +6 -4
- package/src/core/ai/context/providers/cell-output.ts +3 -2
- package/src/core/ai/context/providers/error.ts +3 -1
- package/src/core/ai/context/providers/file.ts +7 -2
- package/src/core/ai/context/providers/tables.ts +3 -2
- package/src/core/ai/context/providers/variable.ts +6 -4
- package/src/core/ai/staged-cells.ts +208 -0
- package/src/core/cells/cells.ts +1 -1
- package/src/core/cells/logs.ts +1 -1
- package/src/core/codemirror/find-replace/search-highlight.ts +3 -1
- package/src/core/codemirror/language/LanguageAdapters.ts +9 -3
- package/src/core/codemirror/lsp/federated-lsp.ts +1 -1
- package/src/core/codemirror/lsp/notebook-lsp.ts +8 -2
- package/src/core/codemirror/readonly/__tests__/extension.test.ts +1 -1
- package/src/core/codemirror/rtc/loro/awareness.ts +52 -17
- package/src/core/codemirror/rtc/loro/sync.ts +12 -4
- package/src/core/config/config-schema.ts +1 -0
- package/src/core/config/config.ts +4 -0
- package/src/core/hotkeys/hotkeys.ts +8 -4
- package/src/core/i18n/__tests__/locale-provider.test.tsx +176 -0
- package/src/core/i18n/locale-provider.tsx +35 -0
- package/src/core/i18n/with-locale.tsx +12 -0
- package/src/core/islands/components/web-components.tsx +13 -10
- package/src/core/islands/main.ts +2 -2
- package/src/core/kernel/RuntimeState.ts +4 -1
- package/src/core/kernel/messages.ts +8 -12
- package/src/core/network/DeferredRequestRegistry.ts +16 -4
- package/src/core/runtime/runtime.ts +5 -4
- package/src/core/saving/__tests__/filename.test.ts +37 -0
- package/src/core/static/__tests__/download-html.test.ts +43 -1
- package/src/core/wasm/bridge.ts +5 -1
- package/src/core/wasm/store.ts +4 -1
- package/src/core/wasm/worker/message-buffer.ts +3 -2
- package/src/core/websocket/types.ts +22 -16
- package/src/core/websocket/useMarimoWebSocket.tsx +2 -2
- package/src/css/app/Cell.css +11 -0
- package/src/hooks/useFormatting.ts +97 -0
- package/src/hooks/useTimer.ts +8 -5
- package/src/plugins/core/RenderHTML.tsx +36 -2
- package/src/plugins/core/__test__/RenderHTML.test.ts +72 -0
- package/src/plugins/core/registerReactComponent.tsx +44 -10
- package/src/plugins/impl/DataTablePlugin.tsx +4 -0
- package/src/plugins/impl/FileBrowserPlugin.tsx +8 -2
- package/src/plugins/impl/RangeSliderPlugin.tsx +5 -3
- package/src/plugins/impl/SliderPlugin.tsx +3 -1
- package/src/plugins/impl/anywidget/model.ts +16 -5
- package/src/plugins/impl/data-editor/types.ts +7 -5
- package/src/plugins/impl/data-explorer/components/column-summary.tsx +20 -13
- package/src/plugins/impl/panel/utils.ts +6 -4
- package/src/plugins/layout/OutlinePlugin.tsx +69 -0
- package/src/plugins/layout/StatPlugin.tsx +4 -1
- package/src/plugins/plugins.ts +2 -0
- package/src/stories/cell.stories.tsx +1 -1
- package/src/stories/layout/vertical/one-column.stories.tsx +1 -1
- package/src/utils/__tests__/cell-urls.test.ts +29 -0
- package/src/utils/__tests__/dates.test.ts +45 -24
- package/src/utils/__tests__/filenames.test.ts +18 -0
- package/src/utils/__tests__/numbers.test.ts +42 -30
- package/src/utils/__tests__/once.test.ts +187 -0
- package/src/utils/__tests__/path.test.ts +38 -0
- package/src/utils/__tests__/urls.test.ts +56 -1
- package/src/utils/dates.ts +15 -10
- package/src/utils/edit-distance.ts +8 -6
- package/src/utils/errors.ts +9 -0
- package/src/utils/id-tree.tsx +21 -10
- package/src/utils/localStorage.ts +13 -4
- package/src/utils/numbers.ts +11 -11
- package/src/utils/once.ts +32 -0
- package/src/utils/paths.ts +4 -1
- package/src/utils/pluralize.ts +12 -5
- package/src/utils/python-poet/poet.ts +30 -15
- package/src/utils/time.ts +5 -1
- package/dist/assets/ConnectedDataExplorerComponent-Cn5-l2X1.js +0 -19
- package/dist/assets/_baseEach-C1FLm7WW.js +0 -1
- package/dist/assets/_baseMap-DBVArUYD.js +0 -1
- package/dist/assets/_baseUniq-Dk7ZPJ3N.js +0 -1
- package/dist/assets/_createAggregator-Bn38fDd3.js +0 -1
- package/dist/assets/agent-panel-COUYnuIK.js +0 -475
- package/dist/assets/architectureDiagram-W76B3OCA-DBzWQKKu.js +0 -36
- package/dist/assets/channel-CjhbjOv4.js +0 -1
- package/dist/assets/chat-panel-BPXKoTnZ.js +0 -7
- package/dist/assets/chat-panel-Brrs_eeH.css +0 -1
- package/dist/assets/classDiagram-KNZD7YFC-DHs5cFzy.js +0 -1
- package/dist/assets/classDiagram-v2-RKCZMP56-DHs5cFzy.js +0 -1
- package/dist/assets/clone-DM1YNjEn.js +0 -1
- package/dist/assets/command-palette-S0bzQp7v.js +0 -1
- package/dist/assets/common-B8U9k2Ly.js +0 -1
- package/dist/assets/cose-bilkent-S5V4N54A-wz1Sfx7j.js +0 -1
- package/dist/assets/dagre-5GWH7T2D-BfpcVBgq.js +0 -4
- package/dist/assets/datasources-panel-DfuURLJw.js +0 -1
- package/dist/assets/diagram-N5W7TBWH-Bf0oqqQh.js +0 -24
- package/dist/assets/diagram-QEK2KX5R-ZTc3qikh.js +0 -43
- package/dist/assets/diagram-S2PKOQOG-tLScBy7Z.js +0 -24
- package/dist/assets/edit-page-DJ8kJZ9w.js +0 -129
- package/dist/assets/file-explorer-panel-CzNUJ63G.js +0 -1
- package/dist/assets/gitGraphDiagram-NY62KEGX-C1t6QtVa.js +0 -65
- package/dist/assets/graph-CssCVWIq.js +0 -1
- package/dist/assets/home-page-9eW6qida.js +0 -9
- package/dist/assets/index-CknhX2Vy.css +0 -1
- package/dist/assets/index-DcCIe7np.js +0 -28
- package/dist/assets/index-OC46250R.js +0 -570
- package/dist/assets/infoDiagram-STP46IZ2-CwiAoz9f.js +0 -2
- package/dist/assets/layout-DpQrxGW-.js +0 -1
- package/dist/assets/linear-NsreOeBF.js +0 -1
- package/dist/assets/links-CbvGxbsJ.js +0 -7
- package/dist/assets/mermaid-DSt0r6IQ.js +0 -1
- package/dist/assets/min-D259kI3t.js +0 -1
- package/dist/assets/outline-panel-uvsS-YEQ.js +0 -1
- package/dist/assets/packages-panel-xMz9W2hW.js +0 -1
- package/dist/assets/run-page-Bb68qdhQ.js +0 -1
- package/dist/assets/sankeyDiagram-GR3RE2ED-BSJOau8E.js +0 -10
- package/dist/assets/scratchpad-panel-BF4BO-U4.js +0 -1
- package/dist/assets/secrets-panel-CdIX44dQ.js +0 -1
- package/dist/assets/snippets-panel-Dco9h0rb.js +0 -1
- package/dist/assets/sortBy-aLGA-PGK.js +0 -1
- package/dist/assets/stateDiagram-KXAO66HF-Bd68WT3b.js +0 -1
- package/dist/assets/stateDiagram-v2-UMBNRL4Z-BXz_GSwb.js +0 -1
- package/dist/assets/storage-CGlP4lCF.js +0 -26
- package/dist/assets/terminal-CxkHubcu.js +0 -9
- package/dist/assets/time-D2nr1UgQ.js +0 -1
- package/dist/assets/tracing-kTqHxa7q.js +0 -2
- package/dist/assets/variable-panel-noTnH-AQ.js +0 -1
- package/dist/assets/worker-X5rxzQGQ.js +0 -1
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/* Copyright 2024 Marimo. All rights reserved. */
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { memoizeLastValue, once } from "../once";
|
|
4
|
+
|
|
5
|
+
describe("once", () => {
|
|
6
|
+
it("should call function only once", () => {
|
|
7
|
+
const fn = vi.fn(() => "result");
|
|
8
|
+
const onceFn = once(fn);
|
|
9
|
+
|
|
10
|
+
const result1 = onceFn();
|
|
11
|
+
const result2 = onceFn();
|
|
12
|
+
|
|
13
|
+
expect(result1).toBe("result");
|
|
14
|
+
expect(result2).toBe("result");
|
|
15
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("should return the same result on subsequent calls", () => {
|
|
19
|
+
let counter = 0;
|
|
20
|
+
const fn = () => ++counter;
|
|
21
|
+
const onceFn = once(fn);
|
|
22
|
+
|
|
23
|
+
expect(onceFn()).toBe(1);
|
|
24
|
+
expect(onceFn()).toBe(1);
|
|
25
|
+
expect(onceFn()).toBe(1);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe("memoizeLastValue", () => {
|
|
30
|
+
it("should memoize result for same arguments", () => {
|
|
31
|
+
const fn = vi.fn((a: number, b: string) => `${a}-${b}`);
|
|
32
|
+
const memoizedFn = memoizeLastValue(fn);
|
|
33
|
+
|
|
34
|
+
const result1 = memoizedFn(1, "test");
|
|
35
|
+
const result2 = memoizedFn(1, "test");
|
|
36
|
+
|
|
37
|
+
expect(result1).toBe("1-test");
|
|
38
|
+
expect(result2).toBe("1-test");
|
|
39
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("should recompute for different arguments", () => {
|
|
43
|
+
const fn = vi.fn((a: number) => a * 2);
|
|
44
|
+
const memoizedFn = memoizeLastValue(fn);
|
|
45
|
+
|
|
46
|
+
const result1 = memoizedFn(5);
|
|
47
|
+
const result2 = memoizedFn(10);
|
|
48
|
+
const result3 = memoizedFn(5); // Should recompute since args changed from previous call
|
|
49
|
+
|
|
50
|
+
expect(result1).toBe(10);
|
|
51
|
+
expect(result2).toBe(20);
|
|
52
|
+
expect(result3).toBe(10);
|
|
53
|
+
expect(fn).toHaveBeenCalledTimes(3);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("should handle no arguments", () => {
|
|
57
|
+
const fn = vi.fn(() => "no-args");
|
|
58
|
+
const memoizedFn = memoizeLastValue(fn);
|
|
59
|
+
|
|
60
|
+
const result1 = memoizedFn();
|
|
61
|
+
const result2 = memoizedFn();
|
|
62
|
+
|
|
63
|
+
expect(result1).toBe("no-args");
|
|
64
|
+
expect(result2).toBe("no-args");
|
|
65
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("should handle single argument", () => {
|
|
69
|
+
const fn = vi.fn((x: number) => x + 1);
|
|
70
|
+
const memoizedFn = memoizeLastValue(fn);
|
|
71
|
+
|
|
72
|
+
expect(memoizedFn(5)).toBe(6);
|
|
73
|
+
expect(memoizedFn(5)).toBe(6);
|
|
74
|
+
expect(memoizedFn(3)).toBe(4);
|
|
75
|
+
|
|
76
|
+
expect(fn).toHaveBeenCalledTimes(2);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("should handle multiple arguments", () => {
|
|
80
|
+
const fn = vi.fn((a: number, b: string, c: boolean) => ({ a, b, c }));
|
|
81
|
+
const memoizedFn = memoizeLastValue(fn);
|
|
82
|
+
|
|
83
|
+
const result1 = memoizedFn(1, "hello", true);
|
|
84
|
+
const result2 = memoizedFn(1, "hello", true);
|
|
85
|
+
const result3 = memoizedFn(1, "hello", false);
|
|
86
|
+
|
|
87
|
+
expect(result1).toEqual({ a: 1, b: "hello", c: true });
|
|
88
|
+
expect(result2).toEqual({ a: 1, b: "hello", c: true });
|
|
89
|
+
expect(result3).toEqual({ a: 1, b: "hello", c: false });
|
|
90
|
+
expect(fn).toHaveBeenCalledTimes(2);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("should use shallow comparison for objects", () => {
|
|
94
|
+
const fn = vi.fn((obj: { x: number }) => obj.x * 2);
|
|
95
|
+
const memoizedFn = memoizeLastValue(fn);
|
|
96
|
+
|
|
97
|
+
const obj1 = { x: 5 };
|
|
98
|
+
const obj2 = { x: 5 }; // Different reference but same content
|
|
99
|
+
|
|
100
|
+
const result1 = memoizedFn(obj1);
|
|
101
|
+
const result2 = memoizedFn(obj1); // Same reference
|
|
102
|
+
const result3 = memoizedFn(obj2); // Different reference
|
|
103
|
+
|
|
104
|
+
expect(result1).toBe(10);
|
|
105
|
+
expect(result2).toBe(10);
|
|
106
|
+
expect(result3).toBe(10);
|
|
107
|
+
expect(fn).toHaveBeenCalledTimes(2); // obj1 (twice with same ref) and obj2 (different ref)
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("should handle arrays", () => {
|
|
111
|
+
const fn = vi.fn((arr: number[]) => arr.reduce((sum, x) => sum + x, 0));
|
|
112
|
+
const memoizedFn = memoizeLastValue(fn);
|
|
113
|
+
|
|
114
|
+
const result1 = memoizedFn([1, 2, 3]);
|
|
115
|
+
const result2 = memoizedFn([1, 2, 3]); // Different array reference but same content
|
|
116
|
+
const result3 = memoizedFn([1, 2, 4]); // Different content
|
|
117
|
+
|
|
118
|
+
expect(result1).toBe(6);
|
|
119
|
+
expect(result2).toBe(6);
|
|
120
|
+
expect(result3).toBe(7);
|
|
121
|
+
expect(fn).toHaveBeenCalledTimes(3); // Each call has different array reference
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("should handle mixed argument types", () => {
|
|
125
|
+
const fn = vi.fn(
|
|
126
|
+
(num: number, str: string, arr: number[], obj: { key: string }) =>
|
|
127
|
+
`${num}-${str}-${arr.length}-${obj.key}`,
|
|
128
|
+
);
|
|
129
|
+
const memoizedFn = memoizeLastValue(fn);
|
|
130
|
+
|
|
131
|
+
const arr = [1, 2, 3];
|
|
132
|
+
const obj = { key: "test" };
|
|
133
|
+
|
|
134
|
+
const result1 = memoizedFn(42, "hello", arr, obj);
|
|
135
|
+
const result2 = memoizedFn(42, "hello", arr, obj); // Same references
|
|
136
|
+
const result3 = memoizedFn(42, "hello", [1, 2, 3], { key: "test" }); // Different references
|
|
137
|
+
|
|
138
|
+
expect(result1).toBe("42-hello-3-test");
|
|
139
|
+
expect(result2).toBe("42-hello-3-test");
|
|
140
|
+
expect(result3).toBe("42-hello-3-test");
|
|
141
|
+
expect(fn).toHaveBeenCalledTimes(2);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it("should handle undefined and null arguments", () => {
|
|
145
|
+
const fn = vi.fn((a?: string, b?: null) => `${a}-${b}`);
|
|
146
|
+
const memoizedFn = memoizeLastValue(fn);
|
|
147
|
+
|
|
148
|
+
const result1 = memoizedFn(undefined, null);
|
|
149
|
+
const result2 = memoizedFn(undefined, null);
|
|
150
|
+
const result3 = memoizedFn("test", null);
|
|
151
|
+
|
|
152
|
+
expect(result1).toBe("undefined-null");
|
|
153
|
+
expect(result2).toBe("undefined-null");
|
|
154
|
+
expect(result3).toBe("test-null");
|
|
155
|
+
expect(fn).toHaveBeenCalledTimes(2);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it("should preserve function context", () => {
|
|
159
|
+
const context = {
|
|
160
|
+
value: 10,
|
|
161
|
+
fn: vi.fn(function (this: { value: number }, multiplier: number) {
|
|
162
|
+
return this.value * multiplier;
|
|
163
|
+
}),
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const memoizedFn = memoizeLastValue(context.fn);
|
|
167
|
+
|
|
168
|
+
const result1 = memoizedFn.call(context, 2);
|
|
169
|
+
const result2 = memoizedFn.call(context, 2);
|
|
170
|
+
|
|
171
|
+
expect(result1).toBe(20);
|
|
172
|
+
expect(result2).toBe(20);
|
|
173
|
+
expect(context.fn).toHaveBeenCalledTimes(1);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it("should handle functions that throw errors", () => {
|
|
177
|
+
const error = new Error("test error");
|
|
178
|
+
const fn = vi.fn(() => {
|
|
179
|
+
throw error;
|
|
180
|
+
});
|
|
181
|
+
const memoizedFn = memoizeLastValue(fn);
|
|
182
|
+
|
|
183
|
+
expect(() => memoizedFn()).toThrow("test error");
|
|
184
|
+
expect(() => memoizedFn()).toThrow("test error"); // Should throw cached error
|
|
185
|
+
expect(fn).toHaveBeenCalledTimes(1); // Error should be memoized too
|
|
186
|
+
});
|
|
187
|
+
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* Copyright 2024 Marimo. All rights reserved. */
|
|
2
2
|
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { EDGE_CASE_FILENAMES } from "../../__tests__/mocks";
|
|
3
4
|
import { type FilePath, PathBuilder, Paths } from "../paths";
|
|
4
5
|
|
|
5
6
|
describe("Paths", () => {
|
|
@@ -167,4 +168,41 @@ describe("PathBuilder", () => {
|
|
|
167
168
|
expect(Paths.extension("file.tar.gz")).toBe("gz");
|
|
168
169
|
});
|
|
169
170
|
});
|
|
171
|
+
|
|
172
|
+
describe("edge case filenames", () => {
|
|
173
|
+
it.each(EDGE_CASE_FILENAMES)(
|
|
174
|
+
"should handle unicode and spaces in basename: %s",
|
|
175
|
+
(filename) => {
|
|
176
|
+
const basename = Paths.basename(filename);
|
|
177
|
+
expect(basename).toBe(filename);
|
|
178
|
+
expect(typeof basename).toBe("string");
|
|
179
|
+
expect(basename).not.toBe("");
|
|
180
|
+
},
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
it.each(EDGE_CASE_FILENAMES)(
|
|
184
|
+
"should handle unicode and spaces in dirname: %s",
|
|
185
|
+
(filename) => {
|
|
186
|
+
const fullPath = `/path/to/${filename}`;
|
|
187
|
+
const dirname = Paths.dirname(fullPath);
|
|
188
|
+
expect(dirname).toBe("/path/to");
|
|
189
|
+
},
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
it.each(EDGE_CASE_FILENAMES)(
|
|
193
|
+
"should handle unicode and spaces in path operations: %s",
|
|
194
|
+
(filename) => {
|
|
195
|
+
const baseName = Paths.basename(filename);
|
|
196
|
+
const extension = Paths.extension(filename);
|
|
197
|
+
|
|
198
|
+
// Should preserve unicode characters in basename
|
|
199
|
+
expect(baseName).toContain(filename.split(".")[0]);
|
|
200
|
+
|
|
201
|
+
// Should correctly extract extension
|
|
202
|
+
if (filename.includes(".")) {
|
|
203
|
+
expect(extension).toBe(filename.split(".").pop());
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
);
|
|
207
|
+
});
|
|
170
208
|
});
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
/* Copyright 2024 Marimo. All rights reserved. */
|
|
2
2
|
import { describe, expect, it } from "vitest";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
EDGE_CASE_FILENAMES,
|
|
5
|
+
URL_SPECIAL_CHAR_FILENAMES,
|
|
6
|
+
} from "../../__tests__/mocks";
|
|
7
|
+
import { isUrl, updateQueryParams } from "../urls";
|
|
4
8
|
|
|
5
9
|
describe("isUrl", () => {
|
|
6
10
|
it("should return true for a valid URL", () => {
|
|
@@ -8,3 +12,54 @@ describe("isUrl", () => {
|
|
|
8
12
|
expect(isUrl("curl -X GET http://example.com")).toBe(false);
|
|
9
13
|
});
|
|
10
14
|
});
|
|
15
|
+
|
|
16
|
+
describe("URL parameter handling with edge case filenames", () => {
|
|
17
|
+
it.each(EDGE_CASE_FILENAMES)(
|
|
18
|
+
"should handle unicode filenames in URL parameters: %s",
|
|
19
|
+
(filename) => {
|
|
20
|
+
// Test that updateQueryParams can handle unicode filenames
|
|
21
|
+
updateQueryParams((params) => {
|
|
22
|
+
params.set("file", filename);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Verify URL encoding/decoding works with unicode
|
|
26
|
+
const encoded = encodeURIComponent(filename);
|
|
27
|
+
const decoded = decodeURIComponent(encoded);
|
|
28
|
+
expect(decoded).toBe(filename);
|
|
29
|
+
|
|
30
|
+
// Verify filename can be safely added to URL parameters
|
|
31
|
+
const url = new URL("https://example.com");
|
|
32
|
+
url.searchParams.set("file", filename);
|
|
33
|
+
expect(url.searchParams.get("file")).toBe(filename);
|
|
34
|
+
},
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
it("should preserve unicode in query string round-trip", () => {
|
|
38
|
+
EDGE_CASE_FILENAMES.forEach((filename) => {
|
|
39
|
+
const url = new URL("https://example.com");
|
|
40
|
+
url.searchParams.set("filename", filename);
|
|
41
|
+
|
|
42
|
+
// Convert to string and back
|
|
43
|
+
const urlString = url.toString();
|
|
44
|
+
const reconstructed = new URL(urlString);
|
|
45
|
+
const retrievedFilename = reconstructed.searchParams.get("filename");
|
|
46
|
+
|
|
47
|
+
expect(retrievedFilename).toBe(filename);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("should handle special characters in updateQueryParams", () => {
|
|
52
|
+
URL_SPECIAL_CHAR_FILENAMES.forEach((filename) => {
|
|
53
|
+
let res: string | null = null;
|
|
54
|
+
expect(() => {
|
|
55
|
+
updateQueryParams((params) => {
|
|
56
|
+
// To and from conversion
|
|
57
|
+
params.set("file", filename);
|
|
58
|
+
res = params.get("file");
|
|
59
|
+
});
|
|
60
|
+
}).not.toThrow();
|
|
61
|
+
expect(res).not.toBeNull();
|
|
62
|
+
expect(res).toBe(filename);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
});
|
package/src/utils/dates.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { Logger } from "./Logger";
|
|
|
7
7
|
export function prettyDate(
|
|
8
8
|
value: string | number | null | undefined,
|
|
9
9
|
type: "date" | "datetime",
|
|
10
|
+
locale: string,
|
|
10
11
|
): string {
|
|
11
12
|
if (value == null) {
|
|
12
13
|
return "";
|
|
@@ -16,7 +17,7 @@ export function prettyDate(
|
|
|
16
17
|
// If type is date, drop the timezone by rendering in UTC
|
|
17
18
|
// since dates are absolute
|
|
18
19
|
if (type === "date") {
|
|
19
|
-
return new Date(value).toLocaleDateString(
|
|
20
|
+
return new Date(value).toLocaleDateString(locale, {
|
|
20
21
|
year: "numeric",
|
|
21
22
|
month: "short",
|
|
22
23
|
day: "numeric",
|
|
@@ -25,7 +26,7 @@ export function prettyDate(
|
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
// For datetime, we keep the original timezone
|
|
28
|
-
return new Date(value).toLocaleDateString(
|
|
29
|
+
return new Date(value).toLocaleDateString(locale, {
|
|
29
30
|
year: "numeric",
|
|
30
31
|
month: "short",
|
|
31
32
|
day: "numeric",
|
|
@@ -43,12 +44,13 @@ export function prettyDate(
|
|
|
43
44
|
export function exactDateTime(
|
|
44
45
|
value: Date,
|
|
45
46
|
timezone: string | undefined,
|
|
47
|
+
locale: string,
|
|
46
48
|
): string {
|
|
47
49
|
const hasSubSeconds = value.getUTCMilliseconds() !== 0;
|
|
48
50
|
try {
|
|
49
51
|
if (timezone) {
|
|
50
52
|
const valueWithTimezone = new TZDate(value, timezone);
|
|
51
|
-
const shortTimeZone = getShortTimeZone(timezone);
|
|
53
|
+
const shortTimeZone = getShortTimeZone(timezone, locale);
|
|
52
54
|
if (hasSubSeconds) {
|
|
53
55
|
return `${formatDate(valueWithTimezone, "yyyy-MM-dd HH:mm:ss.SSS")} ${shortTimeZone}`;
|
|
54
56
|
}
|
|
@@ -66,9 +68,9 @@ export function exactDateTime(
|
|
|
66
68
|
}
|
|
67
69
|
}
|
|
68
70
|
|
|
69
|
-
export function getShortTimeZone(timezone: string): string {
|
|
71
|
+
export function getShortTimeZone(timezone: string, locale: string): string {
|
|
70
72
|
try {
|
|
71
|
-
const abbrev = new Intl.DateTimeFormat(
|
|
73
|
+
const abbrev = new Intl.DateTimeFormat(locale, {
|
|
72
74
|
timeZone: timezone,
|
|
73
75
|
timeZoneName: "short",
|
|
74
76
|
})
|
|
@@ -88,7 +90,10 @@ export function getShortTimeZone(timezone: string): string {
|
|
|
88
90
|
*
|
|
89
91
|
* If a date in the past, it should say "<date> at 8:00 AM".
|
|
90
92
|
*/
|
|
91
|
-
export function timeAgo(
|
|
93
|
+
export function timeAgo(
|
|
94
|
+
value: string | number | null | undefined,
|
|
95
|
+
locale: string,
|
|
96
|
+
): string {
|
|
92
97
|
if (value == null) {
|
|
93
98
|
return "";
|
|
94
99
|
}
|
|
@@ -103,22 +108,22 @@ export function timeAgo(value: string | number | null | undefined): string {
|
|
|
103
108
|
yesterday.setDate(yesterday.getDate() - 1);
|
|
104
109
|
|
|
105
110
|
if (date.toDateString() === today.toDateString()) {
|
|
106
|
-
return `Today at ${date.toLocaleTimeString(
|
|
111
|
+
return `Today at ${date.toLocaleTimeString(locale, {
|
|
107
112
|
hour: "numeric",
|
|
108
113
|
minute: "numeric",
|
|
109
114
|
})}`;
|
|
110
115
|
}
|
|
111
116
|
if (date.toDateString() === yesterday.toDateString()) {
|
|
112
|
-
return `Yesterday at ${date.toLocaleTimeString(
|
|
117
|
+
return `Yesterday at ${date.toLocaleTimeString(locale, {
|
|
113
118
|
hour: "numeric",
|
|
114
119
|
minute: "numeric",
|
|
115
120
|
})}`;
|
|
116
121
|
}
|
|
117
|
-
return `${date.toLocaleDateString(
|
|
122
|
+
return `${date.toLocaleDateString(locale, {
|
|
118
123
|
year: "numeric",
|
|
119
124
|
month: "short",
|
|
120
125
|
day: "numeric",
|
|
121
|
-
})} at ${date.toLocaleTimeString(
|
|
126
|
+
})} at ${date.toLocaleTimeString(locale, {
|
|
122
127
|
hour: "numeric",
|
|
123
128
|
minute: "numeric",
|
|
124
129
|
})}`;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/* Copyright 2024 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
|
-
export
|
|
4
|
-
INSERT
|
|
5
|
-
DELETE
|
|
6
|
-
SUBSTITUTE
|
|
7
|
-
MATCH
|
|
8
|
-
}
|
|
3
|
+
export const OperationType = {
|
|
4
|
+
INSERT: "insert",
|
|
5
|
+
DELETE: "delete",
|
|
6
|
+
SUBSTITUTE: "substitute",
|
|
7
|
+
MATCH: "match",
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
type OperationType = (typeof OperationType)[keyof typeof OperationType];
|
|
9
11
|
|
|
10
12
|
export interface EditOperation {
|
|
11
13
|
type: OperationType;
|
package/src/utils/errors.ts
CHANGED
|
@@ -61,3 +61,12 @@ function safeJSONParse(message: string): unknown {
|
|
|
61
61
|
return message;
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
|
+
|
|
65
|
+
export class CellNotInitializedError extends Error {
|
|
66
|
+
constructor(
|
|
67
|
+
message = "The cell containing this UI element has not been run yet. Please run the cell first.",
|
|
68
|
+
) {
|
|
69
|
+
super(message);
|
|
70
|
+
this.name = "CellNotInitializedError";
|
|
71
|
+
}
|
|
72
|
+
}
|
package/src/utils/id-tree.tsx
CHANGED
|
@@ -23,11 +23,15 @@ export type CellIndex = number & { __brand?: "CellIndex" };
|
|
|
23
23
|
* Tree data structure for handling ids with nested children
|
|
24
24
|
*/
|
|
25
25
|
export class TreeNode<T> {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
) {
|
|
26
|
+
public value: T;
|
|
27
|
+
public isCollapsed: boolean;
|
|
28
|
+
public children: Array<TreeNode<T>>;
|
|
29
|
+
|
|
30
|
+
constructor(value: T, isCollapsed: boolean, children: Array<TreeNode<T>>) {
|
|
31
|
+
this.value = value;
|
|
32
|
+
this.isCollapsed = isCollapsed;
|
|
33
|
+
this.children = children;
|
|
34
|
+
}
|
|
31
35
|
|
|
32
36
|
/**
|
|
33
37
|
* Recursively count the number of nodes in the tree
|
|
@@ -102,10 +106,13 @@ export class TreeNode<T> {
|
|
|
102
106
|
let uniqueId = 0;
|
|
103
107
|
|
|
104
108
|
export class CollapsibleTree<T> {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
) {
|
|
109
|
+
public readonly nodes: Array<TreeNode<T>>;
|
|
110
|
+
public readonly id: CellColumnId;
|
|
111
|
+
|
|
112
|
+
private constructor(nodes: Array<TreeNode<T>>, id: CellColumnId) {
|
|
113
|
+
this.nodes = nodes;
|
|
114
|
+
this.id = id;
|
|
115
|
+
}
|
|
109
116
|
|
|
110
117
|
static from<T>(ids: T[]): CollapsibleTree<T> {
|
|
111
118
|
const id = `tree_${uniqueId++}` as CellColumnId;
|
|
@@ -592,7 +599,11 @@ export class CollapsibleTree<T> {
|
|
|
592
599
|
}
|
|
593
600
|
|
|
594
601
|
export class MultiColumn<T> {
|
|
595
|
-
|
|
602
|
+
private readonly columns: ReadonlyArray<CollapsibleTree<T>>;
|
|
603
|
+
|
|
604
|
+
constructor(columns: ReadonlyArray<CollapsibleTree<T>>) {
|
|
605
|
+
this.columns = columns;
|
|
606
|
+
|
|
596
607
|
// Ensure there is always at least one column
|
|
597
608
|
if (columns.length === 0) {
|
|
598
609
|
this.columns = [CollapsibleTree.from([])];
|
|
@@ -12,7 +12,10 @@ interface Storage<T> {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export class TypedLocalStorage<T> implements Storage<T> {
|
|
15
|
-
|
|
15
|
+
private defaultValue: T;
|
|
16
|
+
constructor(defaultValue: T) {
|
|
17
|
+
this.defaultValue = defaultValue;
|
|
18
|
+
}
|
|
16
19
|
|
|
17
20
|
get(key: string): T {
|
|
18
21
|
try {
|
|
@@ -33,10 +36,16 @@ export class TypedLocalStorage<T> implements Storage<T> {
|
|
|
33
36
|
}
|
|
34
37
|
|
|
35
38
|
export class ZodLocalStorage<T> implements Storage<T> {
|
|
39
|
+
private schema: ZodType<T, ZodTypeDef, unknown>;
|
|
40
|
+
private getDefaultValue: () => T;
|
|
41
|
+
|
|
36
42
|
constructor(
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
) {
|
|
43
|
+
schema: ZodType<T, ZodTypeDef, unknown>,
|
|
44
|
+
getDefaultValue: () => T,
|
|
45
|
+
) {
|
|
46
|
+
this.schema = schema;
|
|
47
|
+
this.getDefaultValue = getDefaultValue;
|
|
48
|
+
}
|
|
40
49
|
|
|
41
50
|
get(key: string): T {
|
|
42
51
|
try {
|
package/src/utils/numbers.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* Copyright 2024 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
|
-
export function prettyNumber(value: unknown): string {
|
|
3
|
+
export function prettyNumber(value: unknown, locale: string): string {
|
|
4
4
|
if (value === undefined || value === null) {
|
|
5
5
|
return "";
|
|
6
6
|
}
|
|
@@ -18,7 +18,7 @@ export function prettyNumber(value: unknown): string {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
if (typeof value === "number" || typeof value === "bigint") {
|
|
21
|
-
return value.toLocaleString(
|
|
21
|
+
return value.toLocaleString(locale, {
|
|
22
22
|
minimumFractionDigits: 0,
|
|
23
23
|
maximumFractionDigits: 2,
|
|
24
24
|
});
|
|
@@ -45,9 +45,9 @@ function scientificSpecialCase(value: number): string | null {
|
|
|
45
45
|
export function prettyScientificNumber(
|
|
46
46
|
value: number,
|
|
47
47
|
opts: {
|
|
48
|
-
// Default to false
|
|
49
|
-
|
|
50
|
-
}
|
|
48
|
+
shouldRound?: boolean; // Default to false
|
|
49
|
+
locale: string;
|
|
50
|
+
},
|
|
51
51
|
): string {
|
|
52
52
|
// Handle special cases first
|
|
53
53
|
const specialCase = scientificSpecialCase(value);
|
|
@@ -58,7 +58,7 @@ export function prettyScientificNumber(
|
|
|
58
58
|
// Determine if the number should be in scientific notation
|
|
59
59
|
const absValue = Math.abs(value);
|
|
60
60
|
if (absValue < 1e-2 || absValue >= 1e6) {
|
|
61
|
-
return new Intl.NumberFormat(
|
|
61
|
+
return new Intl.NumberFormat(opts.locale, {
|
|
62
62
|
minimumFractionDigits: 1,
|
|
63
63
|
maximumFractionDigits: 1,
|
|
64
64
|
notation: "scientific",
|
|
@@ -67,18 +67,18 @@ export function prettyScientificNumber(
|
|
|
67
67
|
.toLowerCase();
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
const { shouldRound } = opts;
|
|
70
|
+
const { shouldRound, locale } = opts;
|
|
71
71
|
|
|
72
72
|
if (shouldRound) {
|
|
73
73
|
// Number has an integer part, format with 2 decimal places
|
|
74
|
-
return new Intl.NumberFormat(
|
|
74
|
+
return new Intl.NumberFormat(locale, {
|
|
75
75
|
minimumFractionDigits: 0,
|
|
76
76
|
maximumFractionDigits: 2,
|
|
77
77
|
}).format(value);
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
// Don't round
|
|
81
|
-
return value.toLocaleString(
|
|
81
|
+
return value.toLocaleString(locale, {
|
|
82
82
|
minimumFractionDigits: 0,
|
|
83
83
|
maximumFractionDigits: 100,
|
|
84
84
|
});
|
|
@@ -104,14 +104,14 @@ const prefixes = {
|
|
|
104
104
|
"-24": "y",
|
|
105
105
|
};
|
|
106
106
|
|
|
107
|
-
export function prettyEngineeringNumber(value: number): string {
|
|
107
|
+
export function prettyEngineeringNumber(value: number, locale: string): string {
|
|
108
108
|
// Handle special cases first
|
|
109
109
|
const specialCase = scientificSpecialCase(value);
|
|
110
110
|
if (specialCase !== null) {
|
|
111
111
|
return specialCase;
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
const [mant, exp] = new Intl.NumberFormat(
|
|
114
|
+
const [mant, exp] = new Intl.NumberFormat(locale, {
|
|
115
115
|
notation: "engineering",
|
|
116
116
|
maximumSignificantDigits: 3,
|
|
117
117
|
})
|
package/src/utils/once.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/* Copyright 2024 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
|
+
import { arrayShallowEquals } from "./arrays";
|
|
4
|
+
|
|
3
5
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4
6
|
export function once<T extends (...args: any[]) => any>(fn: T): T {
|
|
5
7
|
let result: ReturnType<T>;
|
|
@@ -15,3 +17,33 @@ export function once<T extends (...args: any[]) => any>(fn: T): T {
|
|
|
15
17
|
return result;
|
|
16
18
|
} as T;
|
|
17
19
|
}
|
|
20
|
+
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
22
|
+
export function memoizeLastValue<T extends (...args: any[]) => any>(fn: T): T {
|
|
23
|
+
let result: ReturnType<T>;
|
|
24
|
+
let lastArgs: Parameters<T> | undefined;
|
|
25
|
+
let lastError: unknown;
|
|
26
|
+
let hasError = false;
|
|
27
|
+
|
|
28
|
+
return function (
|
|
29
|
+
this: ThisParameterType<T>,
|
|
30
|
+
...args: Parameters<T>
|
|
31
|
+
): ReturnType<T> {
|
|
32
|
+
if (lastArgs === undefined || !arrayShallowEquals(args, lastArgs)) {
|
|
33
|
+
try {
|
|
34
|
+
result = fn.apply(this, args) as ReturnType<T>;
|
|
35
|
+
hasError = false;
|
|
36
|
+
lastError = undefined;
|
|
37
|
+
} catch (error) {
|
|
38
|
+
hasError = true;
|
|
39
|
+
lastError = error;
|
|
40
|
+
}
|
|
41
|
+
lastArgs = args;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (hasError) {
|
|
45
|
+
throw lastError;
|
|
46
|
+
}
|
|
47
|
+
return result;
|
|
48
|
+
} as T;
|
|
49
|
+
}
|
package/src/utils/paths.ts
CHANGED
|
@@ -32,7 +32,10 @@ export const Paths = {
|
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
export class PathBuilder {
|
|
35
|
-
|
|
35
|
+
public readonly deliminator: string;
|
|
36
|
+
constructor(deliminator: "/" | "\\") {
|
|
37
|
+
this.deliminator = deliminator;
|
|
38
|
+
}
|
|
36
39
|
|
|
37
40
|
static guessDeliminator(path: string): PathBuilder {
|
|
38
41
|
return path.includes("/") ? new PathBuilder("/") : new PathBuilder("\\");
|
package/src/utils/pluralize.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
/* Copyright 2024 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
3
|
export class PluralWord {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
) {
|
|
4
|
+
public singular: string;
|
|
5
|
+
public _plural?: string;
|
|
6
|
+
|
|
7
|
+
constructor(singular: string, _plural?: string) {
|
|
8
|
+
this.singular = singular;
|
|
9
|
+
this._plural = _plural;
|
|
10
|
+
}
|
|
8
11
|
|
|
9
12
|
public pluralize(count: number) {
|
|
10
13
|
return count === 1 ? this.singular : this.plural;
|
|
@@ -16,7 +19,11 @@ export class PluralWord {
|
|
|
16
19
|
}
|
|
17
20
|
|
|
18
21
|
export class PluralWords {
|
|
19
|
-
|
|
22
|
+
private words: PluralWord[];
|
|
23
|
+
|
|
24
|
+
constructor(words: PluralWord[]) {
|
|
25
|
+
this.words = words;
|
|
26
|
+
}
|
|
20
27
|
|
|
21
28
|
static of(...words: Array<PluralWord | string>) {
|
|
22
29
|
return new PluralWords(
|