@marimo-team/frontend 0.16.0 → 0.16.2
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-B5cPvWoQ.js +19 -0
- package/dist/assets/{ImageComparisonComponent-fTHv1Ih0.js → ImageComparisonComponent-CqR26LSv.js} +1 -1
- package/dist/assets/{VegaLite-Bdi-TyfY.js → VegaLite-DvQDATwI.js} +1 -1
- package/dist/assets/_baseEach--KDTwKbG.js +1 -0
- package/dist/assets/_baseMap-Cu3o-eyO.js +1 -0
- package/dist/assets/{_baseUniq-CCgDNtZb.js → _baseUniq-y7ZXnMo1.js} +1 -1
- package/dist/assets/{_createAggregator-DcD0kTA5.js → _createAggregator-ZcHkHPNJ.js} +1 -1
- package/dist/assets/{agent-panel-Crv430aI.js → agent-panel-B91RoLct.js} +76 -57
- package/dist/assets/{any-language-editor-CQh552Wu.js → any-language-editor-CxfHcm5h.js} +1 -1
- package/dist/assets/{architectureDiagram-W76B3OCA-BAJeBxzt.js → architectureDiagram-W76B3OCA-BQsvK8uR.js} +1 -1
- package/dist/assets/{between-horizontal-start-Boxgxbt_.js → between-horizontal-start-BmYToIaM.js} +1 -1
- package/dist/assets/{blockDiagram-QIGZ2CNN-CL-1svEK.js → blockDiagram-QIGZ2CNN-r3HgCj4w.js} +1 -1
- package/dist/assets/{c4Diagram-FPNF74CW-BbEqbCTl.js → c4Diagram-FPNF74CW-BJbPNt41.js} +1 -1
- package/dist/assets/channel-DFaEx1fu.js +1 -0
- package/dist/assets/chat-panel-IoPMv8e2.js +3 -0
- package/dist/assets/{chunk-4BX2VUAB-C--8TXeE.js → chunk-4BX2VUAB-Dv4MZ9Hj.js} +1 -1
- package/dist/assets/{chunk-55IACEB6-Bj00HDqq.js → chunk-55IACEB6-CM4AHquB.js} +1 -1
- package/dist/assets/{chunk-FMBD7UC4-C-lhB6hN.js → chunk-FMBD7UC4-C_Zz0ENB.js} +1 -1
- package/dist/assets/{chunk-K7UQS3LO-B-pGTXPt.js → chunk-K7UQS3LO-DYSmiXYq.js} +1 -1
- package/dist/assets/{chunk-QN33PNHL-DqUzGhvm.js → chunk-QN33PNHL-QM4OPuQP.js} +1 -1
- package/dist/assets/{chunk-QZHKN3VN-TntJHfSk.js → chunk-QZHKN3VN-CfAsGyeB.js} +1 -1
- package/dist/assets/{chunk-TVAH2DTR-HUJb1psV.js → chunk-TVAH2DTR-6j_Cpjsi.js} +1 -1
- package/dist/assets/{chunk-TZMSLE5B-BK3C__t3.js → chunk-TZMSLE5B-BHslFJQE.js} +1 -1
- package/dist/assets/{circle-play-DBLOv1Yu.js → circle-play-CK3UZRYQ.js} +1 -1
- package/dist/assets/classDiagram-KNZD7YFC-BsZtvV5O.js +1 -0
- package/dist/assets/classDiagram-v2-RKCZMP56-BsZtvV5O.js +1 -0
- package/dist/assets/{clear-button-BeoFbEKH.js → clear-button-C4fDVSv8.js} +1 -1
- package/dist/assets/clone-YBEvPE-s.js +1 -0
- package/dist/assets/command-palette-D7hOfvf6.js +1 -0
- package/dist/assets/{common-C7oJcmCT.js → common-D-lbuUwz.js} +1 -1
- package/dist/assets/{compile-7L0MwhyI.js → compile-DVQe1Mzk.js} +1 -1
- package/dist/assets/{cose-bilkent-S5V4N54A-BMkGLcVC.js → cose-bilkent-S5V4N54A-D-IS7WC8.js} +1 -1
- package/dist/assets/{dagre-5GWH7T2D-BJtRienS.js → dagre-5GWH7T2D-lYu-tEWT.js} +1 -1
- package/dist/assets/{data-grid-overlay-editor-DBkmGtNs.js → data-grid-overlay-editor-C5peOCit.js} +1 -1
- package/dist/assets/datasources-panel-D3NA20uZ.js +1 -0
- package/dist/assets/{dependency-graph-panel-DEdOxp2X.js → dependency-graph-panel-BGVYOfkV.js} +1 -1
- package/dist/assets/{diagram-N5W7TBWH-CmECY3nb.js → diagram-N5W7TBWH-BnvIuYUp.js} +1 -1
- package/dist/assets/{diagram-QEK2KX5R-DMOVSNKD.js → diagram-QEK2KX5R-DemedRK3.js} +1 -1
- package/dist/assets/{diagram-S2PKOQOG-BiJ96PNQ.js → diagram-S2PKOQOG-iiY7AuyH.js} +1 -1
- package/dist/assets/{documentation-panel-xULhaEv3.js → documentation-panel-C3dSwOSQ.js} +1 -1
- package/dist/assets/edit-page-C5TsEeSo.js +129 -0
- package/dist/assets/{ellipsis-vertical-BBqXIlc2.js → ellipsis-vertical-CazJl8M7.js} +1 -1
- package/dist/assets/{empty-state-B3dA3G5P.js → empty-state-DW308mFO.js} +1 -1
- package/dist/assets/{erDiagram-AWTI2OKA-MP1DiFRo.js → erDiagram-AWTI2OKA-6wQ8Ugg0.js} +1 -1
- package/dist/assets/{error-panel-Cc1sv-Ag.js → error-panel-D1VnJ1yP.js} +1 -1
- package/dist/assets/file-explorer-panel-0oVd4t-D.js +1 -0
- package/dist/assets/{flowDiagram-PVAE7QVJ-BX7caPp7.js → flowDiagram-PVAE7QVJ-C55IUWjm.js} +1 -1
- package/dist/assets/{ganttDiagram-OWAHRB6G-B462g4Yf.js → ganttDiagram-OWAHRB6G-DmqCM6ME.js} +4 -4
- package/dist/assets/{gitGraphDiagram-NY62KEGX-CGgvZ9-9.js → gitGraphDiagram-NY62KEGX-DBvhAeM_.js} +1 -1
- package/dist/assets/{glide-data-editor-C0gUFZON.js → glide-data-editor-CHNuHidQ.js} +4 -4
- package/dist/assets/{graph-CHRVBzY5.js → graph-CG6BgUWQ.js} +1 -1
- package/dist/assets/home-page-dgivXuSR.js +9 -0
- package/dist/assets/{index-Clbi_Yaq.js → index-BTGpssVX.js} +1 -1
- package/dist/assets/{index-CQDrxQ0j.js → index-BYVZlBF8.js} +1 -1
- package/dist/assets/{index-lYa_leQE.js → index-BelfnXwL.js} +1 -1
- package/dist/assets/{index-DRMm6SNo.js → index-BneyUujp.js} +1 -1
- package/dist/assets/{index-BY93Ejhl.js → index-C02SqeRj.js} +1 -1
- package/dist/assets/{index-C-8WADat.js → index-C7dtgr9A.js} +1 -1
- package/dist/assets/{index-D9UKkrr2.js → index-CAQvMTzM.js} +1 -1
- package/dist/assets/index-CGDMlQfO.css +1 -0
- package/dist/assets/index-CelXfcd8.js +580 -0
- package/dist/assets/{index-vmICa5KN.js → index-Csd6QrCV.js} +1 -1
- package/dist/assets/{index-DoRmcrKM.js → index-CtPksxf0.js} +1 -1
- package/dist/assets/{index-C1v_Z9et.js → index-Cxyk7pt-.js} +1 -1
- package/dist/assets/{index-CpTPJo4k.js → index-DAZ-9ri2.js} +1 -1
- package/dist/assets/{index-DEQvTChO.js → index-DONRrmA2.js} +1 -1
- package/dist/assets/{index-C4Tn5NvJ.js → index-Db36XTG_.js} +1 -1
- package/dist/assets/{index-z9bohSQJ.js → index-DdIhdEVw.js} +1 -1
- package/dist/assets/{index-C-GhZ7ti.js → index-M_pBKDSe.js} +1 -1
- package/dist/assets/{index-C77h_TXN.js → index-_luCZMLM.js} +1 -1
- package/dist/assets/{index-D1vmG6DS.js → index-mkubqy9-.js} +1 -1
- package/dist/assets/{index-BVgAenPd.js → index-sbO9UaUU.js} +1 -1
- package/dist/assets/{index-CWMgowgL.js → index-z4krxQ4j.js} +1 -1
- package/dist/assets/infoDiagram-STP46IZ2-wTALjfPc.js +2 -0
- package/dist/assets/{isEmpty-DU_ogP_D.js → isEmpty-CqX_YTIf.js} +1 -1
- package/dist/assets/{journeyDiagram-BIP6EPQ6-C6EgLP_Q.js → journeyDiagram-BIP6EPQ6-Y5w_Tqe_.js} +1 -1
- package/dist/assets/{kanban-definition-6OIFK2YF-BXzYO1yj.js → kanban-definition-6OIFK2YF-DbXs5Rxi.js} +1 -1
- package/dist/assets/{layout-jihVw5-i.js → layout-BCNPDACj.js} +1 -1
- package/dist/assets/{linear-C4blANlC.js → linear-uO6UVhXt.js} +1 -1
- package/dist/assets/links-Drv7cJgN.js +7 -0
- package/dist/assets/{logs-panel-D401qzZh.js → logs-panel-BEQ1eRUp.js} +1 -1
- package/dist/assets/{markdown-renderer-Cd9eYyaL.js → markdown-renderer-Dmzbb00W.js} +20 -20
- package/dist/assets/{mermaid-BEVuRz_O.js → mermaid-qRc4MXIj.js} +1 -1
- package/dist/assets/{mermaid.core-CaSnaLH0.js → mermaid.core-CvvJtCRj.js} +4 -4
- package/dist/assets/min-DYUOb1RR.js +1 -0
- package/dist/assets/{mindmap-definition-Q6HEUPPD-BXUM5MT2.js → mindmap-definition-Q6HEUPPD-G5NognM-.js} +1 -1
- package/dist/assets/{number-overlay-editor-4uWXGlPG.js → number-overlay-editor-DPr5sHFu.js} +1 -1
- package/dist/assets/outline-panel-gxQXvVi4.js +1 -0
- package/dist/assets/{packages-panel-CJL0MVlj.js → packages-panel-B1T0VPlg.js} +1 -1
- package/dist/assets/{pieDiagram-ADFJNKIX-Dxt5PVNo.js → pieDiagram-ADFJNKIX-DK9SHkfc.js} +1 -1
- package/dist/assets/{quadrantDiagram-LMRXKWRM-D4pUaA31.js → quadrantDiagram-LMRXKWRM-D1DdWF8C.js} +1 -1
- package/dist/assets/{react-plotly-cJZ0VWBq.js → react-plotly-CTwajqCb.js} +1 -1
- package/dist/assets/{requirementDiagram-4UW4RH46-DVRTjgas.js → requirementDiagram-4UW4RH46-DnjDAypr.js} +1 -1
- package/dist/assets/{run-page-BUEnMC9w.js → run-page-CQY9im22.js} +1 -1
- package/dist/assets/{sankeyDiagram-GR3RE2ED-CVFnD9C-.js → sankeyDiagram-GR3RE2ED-B67Va-ER.js} +1 -1
- package/dist/assets/{scratchpad-panel-BIgRENkI.js → scratchpad-panel-DlDfcDtW.js} +1 -1
- package/dist/assets/{secrets-panel-xY5-V_BD.js → secrets-panel-BDGyuGZA.js} +1 -1
- package/dist/assets/{sequenceDiagram-C3RYC4MD-_lY4ZN_S.js → sequenceDiagram-C3RYC4MD-DiWgZPtN.js} +1 -1
- package/dist/assets/{slides-component-DMjQomc3.css → slides-component-C-LoGC1U.css} +1 -1
- package/dist/assets/{slides-component-Xjymwj7X.js → slides-component-DhpPRtQp.js} +1 -1
- package/dist/assets/snippets-panel-CLkBXhJ2.js +1 -0
- package/dist/assets/sortBy-D4OG7w4O.js +1 -0
- package/dist/assets/{state-C4NiC9tO.js → state-Dz_3JyED.js} +1 -1
- package/dist/assets/{stateDiagram-KXAO66HF-Da0JQWCn.js → stateDiagram-KXAO66HF-ByF2AULw.js} +1 -1
- package/dist/assets/stateDiagram-v2-UMBNRL4Z-CtBJqosP.js +1 -0
- package/dist/assets/storage-Dr0CC44z.js +26 -0
- package/dist/assets/{terminal-BPwTkXae.js → terminal-BtdissBf.js} +1 -1
- package/dist/assets/{time-Dv5_Ouz_.js → time-DKdOTnQg.js} +1 -1
- package/dist/assets/{timeline-definition-XQNQX7LJ-Dxh5Zu2e.js → timeline-definition-XQNQX7LJ-DzER9bf6.js} +1 -1
- package/dist/assets/tracing-Dpx5M-u3.js +2 -0
- package/dist/assets/{tracing-panel-DAzrzNmm.js → tracing-panel-hCjBkSER.js} +2 -2
- package/dist/assets/{trash-Dc6DSjz_.js → trash-C6Ko-g5q.js} +1 -1
- package/dist/assets/{tree-jheoerAX.js → tree-BHN2gcCF.js} +6 -6
- package/dist/assets/{treemap-75Q7IDZK-IgpxeGaf.js → treemap-75Q7IDZK-DR79Mhzt.js} +27 -27
- package/dist/assets/variable-panel-PFBCFz36.js +1 -0
- package/dist/assets/{vega-component-BpfpiPKI.js → vega-component-Db6-uY4C.js} +1 -1
- package/dist/assets/worker-fHbtoWvT.js +1 -0
- package/dist/assets/{xychartDiagram-6GGTOJPD-CmNigJ31.js → xychartDiagram-6GGTOJPD-DWzBP3tZ.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +5 -4
- package/src/__mocks__/requests.ts +1 -0
- package/src/components/app-config/user-config-form.tsx +46 -1
- package/src/components/chat/acp/__tests__/__snapshots__/prompt.test.ts.snap +62 -43
- package/src/components/chat/acp/__tests__/atoms.test.ts +1 -1
- package/src/components/chat/acp/__tests__/state.test.ts +36 -36
- package/src/components/chat/acp/agent-panel.tsx +24 -27
- package/src/components/chat/acp/blocks.tsx +6 -6
- package/src/components/chat/acp/prompt.ts +62 -43
- package/src/components/chat/chat-panel.tsx +5 -1
- package/src/components/chat/markdown-renderer.tsx +6 -10
- package/src/components/chat/tool-call-accordion.tsx +52 -20
- 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__/columns.test.tsx +38 -0
- 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 +63 -20
- 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 +36 -0
- package/src/components/data-table/row-viewer-panel/row-viewer.tsx +10 -8
- package/src/components/data-table/schemas.ts +16 -0
- package/src/components/datasources/column-preview.tsx +6 -2
- package/src/components/datasources/datasources.tsx +8 -12
- package/src/components/editor/Cell.tsx +2 -0
- package/src/components/editor/ai/transport/chat-transport.tsx +4 -1
- package/src/components/editor/cell/CellStatus.tsx +23 -20
- package/src/components/editor/cell/CreateCellButton.tsx +3 -4
- 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/errors/sql-validation-errors.tsx +34 -0
- package/src/components/editor/file-tree/requesting-tree.tsx +14 -8
- package/src/components/editor/output/ConsoleOutput.tsx +13 -1
- package/src/components/editor/output/MarimoErrorOutput.tsx +60 -1
- 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/theme.tsx +1 -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/context/__tests__/registry.test.ts +6 -4
- package/src/core/ai/context/providers/cell-output.ts +4 -20
- 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 +34 -1
- package/src/core/cells/__tests__/add-missing-import.test.ts +67 -22
- package/src/core/cells/add-missing-import.ts +24 -7
- package/src/core/cells/cells.ts +26 -27
- 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/language/__tests__/extension.test.ts +24 -0
- package/src/core/codemirror/language/__tests__/sql-validation.test.ts +133 -0
- package/src/core/codemirror/language/languages/sql/sql-mode.ts +20 -0
- package/src/core/codemirror/language/languages/sql/sql.ts +90 -3
- package/src/core/codemirror/language/languages/sql/validation-errors.ts +79 -0
- package/src/core/codemirror/language/panel/panel.tsx +8 -2
- package/src/core/codemirror/language/panel/sql.tsx +81 -4
- 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/config/feature-flag.tsx +3 -1
- package/src/core/datasets/request-registry.ts +17 -1
- 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/bridge.ts +1 -0
- package/src/core/islands/components/web-components.tsx +13 -10
- package/src/core/islands/main.ts +1 -0
- package/src/core/kernel/RuntimeState.ts +4 -1
- package/src/core/kernel/messages.ts +3 -2
- package/src/core/network/DeferredRequestRegistry.ts +16 -4
- package/src/core/network/requests-network.ts +7 -0
- package/src/core/network/requests-static.ts +1 -0
- package/src/core/network/requests-toasting.ts +1 -0
- package/src/core/network/types.ts +2 -0
- package/src/core/runtime/runtime.ts +5 -4
- package/src/core/wasm/bridge.ts +10 -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 +4 -0
- package/src/hooks/useFormatting.ts +97 -0
- package/src/hooks/useTimer.ts +8 -5
- package/src/plugins/core/registerReactComponent.tsx +39 -29
- package/src/plugins/impl/DataTablePlugin.tsx +15 -4
- 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/data-frames/DataFramePlugin.tsx +17 -5
- 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/dataframe.stories.tsx +2 -0
- package/src/utils/__tests__/dates.test.ts +45 -24
- package/src/utils/__tests__/dom.test.ts +167 -0
- package/src/utils/__tests__/numbers.test.ts +42 -30
- package/src/utils/__tests__/once.test.ts +187 -0
- package/src/utils/dates.ts +15 -10
- package/src/utils/dom.ts +55 -0
- package/src/utils/edit-distance.ts +8 -6
- package/src/utils/errors.ts +1 -1
- 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-BErMbWvG.js +0 -19
- package/dist/assets/_baseEach-CNBxBxvS.js +0 -1
- package/dist/assets/_baseMap-D1WHjKrd.js +0 -1
- package/dist/assets/channel-_2eNSz0n.js +0 -1
- package/dist/assets/chat-panel-CXh5Wl6C.js +0 -3
- package/dist/assets/classDiagram-KNZD7YFC-BGmh9POF.js +0 -1
- package/dist/assets/classDiagram-v2-RKCZMP56-BGmh9POF.js +0 -1
- package/dist/assets/clone-BFDSPAj3.js +0 -1
- package/dist/assets/command-palette-CXZiSv0I.js +0 -1
- package/dist/assets/datasources-panel-B7FbYLiy.js +0 -1
- package/dist/assets/edit-page-BrYda9VE.js +0 -129
- package/dist/assets/file-explorer-panel-Bw59Kva1.js +0 -1
- package/dist/assets/home-page-Fb2osjys.js +0 -9
- package/dist/assets/index-Cx0bsY1w.css +0 -1
- package/dist/assets/index-DKEudB02.js +0 -578
- package/dist/assets/infoDiagram-STP46IZ2-CVyrdLc8.js +0 -2
- package/dist/assets/links-D59GIweI.js +0 -7
- package/dist/assets/min-DUMu_zeK.js +0 -1
- package/dist/assets/outline-panel-DIzkvm2I.js +0 -1
- package/dist/assets/snippets-panel-CTPYW41n.js +0 -1
- package/dist/assets/sortBy-BNZKwiq_.js +0 -1
- package/dist/assets/stateDiagram-v2-UMBNRL4Z-D5lYZOOt.js +0 -1
- package/dist/assets/storage-CMdLzB_c.js +0 -26
- package/dist/assets/tracing-BCIurUfa.js +0 -2
- package/dist/assets/variable-panel-DYAiLBmF.js +0 -1
- package/dist/assets/worker-X5rxzQGQ.js +0 -1
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/* Copyright 2024 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { describe, expect, test } from "vitest";
|
|
4
|
+
import { ansiToPlainText, parseHtmlContent } from "../dom";
|
|
5
|
+
|
|
6
|
+
describe("parseHtmlContent", () => {
|
|
7
|
+
test("strips HTML tags and returns plain text", () => {
|
|
8
|
+
const htmlString =
|
|
9
|
+
'<span style="color: red;">Error: Something went wrong</span>';
|
|
10
|
+
const result = parseHtmlContent(htmlString);
|
|
11
|
+
expect(result).toMatchInlineSnapshot(`"Error: Something went wrong"`);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test("handles ANSI color span tags", () => {
|
|
15
|
+
const htmlString =
|
|
16
|
+
'<span style="color:#d03050;">ERROR</span>: <span style="color:#8ad03a;">File not found</span>';
|
|
17
|
+
const result = parseHtmlContent(htmlString);
|
|
18
|
+
expect(result).toMatchInlineSnapshot(`"ERROR: File not found"`);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("normalizes whitespace", () => {
|
|
22
|
+
const htmlString = "<span> Multiple \n\n spaces and \t tabs </span>";
|
|
23
|
+
const result = parseHtmlContent(htmlString);
|
|
24
|
+
expect(result).toMatchInlineSnapshot(`
|
|
25
|
+
" Multiple
|
|
26
|
+
|
|
27
|
+
spaces and tabs"
|
|
28
|
+
`);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test("handles empty HTML", () => {
|
|
32
|
+
const htmlString = "";
|
|
33
|
+
const result = parseHtmlContent(htmlString);
|
|
34
|
+
expect(result).toMatchInlineSnapshot(`""`);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("handles plain text without HTML", () => {
|
|
38
|
+
const htmlString = "Simple error message";
|
|
39
|
+
const result = parseHtmlContent(htmlString);
|
|
40
|
+
expect(result).toMatchInlineSnapshot(`"Simple error message"`);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("handles nested HTML elements", () => {
|
|
44
|
+
const htmlString =
|
|
45
|
+
'<div><span>Traceback:</span><pre><code> File "test.py", line 1\n print("hello"</code></pre></div>';
|
|
46
|
+
const result = parseHtmlContent(htmlString);
|
|
47
|
+
expect(result).toMatchInlineSnapshot(`
|
|
48
|
+
"Traceback: File "test.py", line 1
|
|
49
|
+
print("hello""
|
|
50
|
+
`);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("handles complex ANSI-converted HTML with styles", () => {
|
|
54
|
+
const htmlString =
|
|
55
|
+
'<span style="background:#fff;color:#000"> File "</span><span style="background:#fff;color:#0000ff">test.py</span><span style="background:#fff;color:#000">", line </span><span style="background:#fff;color:#008000">1</span>';
|
|
56
|
+
const result = parseHtmlContent(htmlString);
|
|
57
|
+
expect(result).toMatchInlineSnapshot(`" File "test.py", line 1"`);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe("ansiToPlainText", () => {
|
|
62
|
+
test("converts ANSI color codes to plain text", () => {
|
|
63
|
+
const ansiString = "\x1b[31mError:\x1b[0m Something went wrong";
|
|
64
|
+
const result = ansiToPlainText(ansiString);
|
|
65
|
+
expect(result).toMatchInlineSnapshot(`"Error: Something went wrong"`);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("handles multiple ANSI color codes", () => {
|
|
69
|
+
const ansiString =
|
|
70
|
+
"\x1b[32mSUCCESS:\x1b[0m \x1b[34mOperation completed\x1b[0m successfully";
|
|
71
|
+
const result = ansiToPlainText(ansiString);
|
|
72
|
+
expect(result).toMatchInlineSnapshot(
|
|
73
|
+
`"SUCCESS: Operation completed successfully"`,
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("handles ANSI bold and color combinations", () => {
|
|
78
|
+
const ansiString =
|
|
79
|
+
"\x1b[1;31mBOLD RED ERROR:\x1b[0m \x1b[33mWarning message\x1b[0m";
|
|
80
|
+
const result = ansiToPlainText(ansiString);
|
|
81
|
+
expect(result).toMatchInlineSnapshot(`"BOLD RED ERROR: Warning message"`);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("handles Python traceback with ANSI codes", () => {
|
|
85
|
+
const ansiString =
|
|
86
|
+
"\x1b[0;36m File \"\x1b[0m\x1b[0;32mtest.py\x1b[0m\x1b[0;36m\", line \x1b[0m\x1b[0;32m1\x1b[0m\x1b[0;36m, in \x1b[0m\x1b[0;35m<module>\x1b[0m\n\x1b[0;31mNameError\x1b[0m: name 'undefined_var' is not defined";
|
|
87
|
+
const result = ansiToPlainText(ansiString);
|
|
88
|
+
expect(result).toMatchInlineSnapshot(`
|
|
89
|
+
" File "test.py", line 1, in <module>
|
|
90
|
+
NameError: name 'undefined_var' is not defined"
|
|
91
|
+
`);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("handles error messages with background colors", () => {
|
|
95
|
+
const ansiString =
|
|
96
|
+
"\x1b[41;37m CRITICAL ERROR \x1b[0m \x1b[31mSystem failure detected\x1b[0m";
|
|
97
|
+
const result = ansiToPlainText(ansiString);
|
|
98
|
+
expect(result).toMatchInlineSnapshot(
|
|
99
|
+
`" CRITICAL ERROR System failure detected"`,
|
|
100
|
+
);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("handles complex stack trace with mixed formatting", () => {
|
|
104
|
+
const ansiString =
|
|
105
|
+
'Traceback (most recent call last):\n \x1b[36mFile "\x1b[32m/path/to/file.py\x1b[36m", line \x1b[32m42\x1b[36m, in \x1b[35mfunction_name\x1b[0m\n \x1b[31mraise ValueError("Something went wrong")\x1b[0m\n\x1b[31mValueError\x1b[0m: Something went wrong';
|
|
106
|
+
const result = ansiToPlainText(ansiString);
|
|
107
|
+
expect(result).toMatchInlineSnapshot(`
|
|
108
|
+
"Traceback (most recent call last):
|
|
109
|
+
File "/path/to/file.py", line 42, in function_name
|
|
110
|
+
raise ValueError("Something went wrong")
|
|
111
|
+
ValueError: Something went wrong"
|
|
112
|
+
`);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test("handles empty string", () => {
|
|
116
|
+
const ansiString = "";
|
|
117
|
+
const result = ansiToPlainText(ansiString);
|
|
118
|
+
expect(result).toMatchInlineSnapshot(`""`);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("handles plain text without ANSI codes", () => {
|
|
122
|
+
const ansiString = "Plain error message without colors";
|
|
123
|
+
const result = ansiToPlainText(ansiString);
|
|
124
|
+
expect(result).toMatchInlineSnapshot(
|
|
125
|
+
`"Plain error message without colors"`,
|
|
126
|
+
);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test("handles whitespace and newlines correctly", () => {
|
|
130
|
+
const ansiString =
|
|
131
|
+
"\x1b[31m Error: \x1b[0m\n\n \x1b[33m Warning \x1b[0m ";
|
|
132
|
+
const result = ansiToPlainText(ansiString);
|
|
133
|
+
expect(result).toMatchInlineSnapshot(`
|
|
134
|
+
" Error:
|
|
135
|
+
|
|
136
|
+
Warning"
|
|
137
|
+
`);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test("handles JavaScript error stack trace", () => {
|
|
141
|
+
const ansiString =
|
|
142
|
+
"\x1b[31mReferenceError\x1b[0m: \x1b[33mvariable\x1b[0m is not defined\n at \x1b[36mObject.<anonymous>\x1b[0m (\x1b[32m/path/to/script.js\x1b[0m:\x1b[33m5\x1b[0m:\x1b[33m1\x1b[0m)";
|
|
143
|
+
const result = ansiToPlainText(ansiString);
|
|
144
|
+
expect(result).toMatchInlineSnapshot(`
|
|
145
|
+
"ReferenceError: variable is not defined
|
|
146
|
+
at Object.<anonymous> (/path/to/script.js:5:1)"
|
|
147
|
+
`);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test("handles Rust panic with ANSI formatting", () => {
|
|
151
|
+
const ansiString =
|
|
152
|
+
"thread '\x1b[32mmain\x1b[0m' panicked at '\x1b[31massertion failed: `(left == right)`\x1b[0m'\n \x1b[36mleft\x1b[0m: `\x1b[33m5\x1b[0m`\n \x1b[36mright\x1b[0m: `\x1b[33m10\x1b[0m`";
|
|
153
|
+
const result = ansiToPlainText(ansiString);
|
|
154
|
+
expect(result).toMatchInlineSnapshot(`
|
|
155
|
+
"thread 'main' panicked at 'assertion failed: \`(left == right)\`'
|
|
156
|
+
left: \`5\`
|
|
157
|
+
right: \`10\`"
|
|
158
|
+
`);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test("handles mix of 8-bit and 256-color ANSI codes", () => {
|
|
162
|
+
const ansiString =
|
|
163
|
+
"\x1b[38;5;196mBright Red\x1b[0m and \x1b[38;5;46mBright Green\x1b[0m text";
|
|
164
|
+
const result = ansiToPlainText(ansiString);
|
|
165
|
+
expect(result).toMatchInlineSnapshot(`"Bright Red and Bright Green text"`);
|
|
166
|
+
});
|
|
167
|
+
});
|
|
@@ -6,24 +6,32 @@ import {
|
|
|
6
6
|
prettyScientificNumber,
|
|
7
7
|
} from "../numbers";
|
|
8
8
|
|
|
9
|
+
const locale = "en-US";
|
|
10
|
+
|
|
9
11
|
describe("prettyNumber", () => {
|
|
10
12
|
it("should format numbers", () => {
|
|
11
|
-
expect(prettyNumber(123_456_789)).toBe("123,456,789");
|
|
12
|
-
expect(prettyNumber(1234.567_89)).toBe("1,234.57");
|
|
13
|
-
expect(prettyNumber(0)).toBe("0");
|
|
13
|
+
expect(prettyNumber(123_456_789, locale)).toBe("123,456,789");
|
|
14
|
+
expect(prettyNumber(1234.567_89, locale)).toBe("1,234.57");
|
|
15
|
+
expect(prettyNumber(0, locale)).toBe("0");
|
|
14
16
|
});
|
|
15
17
|
});
|
|
16
18
|
|
|
19
|
+
const options = { locale };
|
|
20
|
+
|
|
17
21
|
describe("prettyScientificNumber", () => {
|
|
18
22
|
it("should handle special cases", () => {
|
|
19
|
-
expect(prettyScientificNumber(0)).toBe("0");
|
|
20
|
-
expect(prettyScientificNumber(Number.NaN)).toBe("NaN");
|
|
21
|
-
expect(prettyScientificNumber(Number.POSITIVE_INFINITY)).toBe(
|
|
22
|
-
|
|
23
|
+
expect(prettyScientificNumber(0, options)).toBe("0");
|
|
24
|
+
expect(prettyScientificNumber(Number.NaN, options)).toBe("NaN");
|
|
25
|
+
expect(prettyScientificNumber(Number.POSITIVE_INFINITY, options)).toBe(
|
|
26
|
+
"Infinity",
|
|
27
|
+
);
|
|
28
|
+
expect(prettyScientificNumber(Number.NEGATIVE_INFINITY, options)).toBe(
|
|
29
|
+
"-Infinity",
|
|
30
|
+
);
|
|
23
31
|
});
|
|
24
32
|
|
|
25
33
|
it("should format decimals with scientific notation, ignoring integer part rounding", () => {
|
|
26
|
-
const opts = { shouldRound: true };
|
|
34
|
+
const opts = { shouldRound: true, locale };
|
|
27
35
|
expect(prettyScientificNumber(123_456, opts)).toBe("123,456");
|
|
28
36
|
expect(prettyScientificNumber(123_456.7, opts)).toBe("123,456.7");
|
|
29
37
|
expect(prettyScientificNumber(12_345.6789, opts)).toBe("12,345.68");
|
|
@@ -40,10 +48,10 @@ describe("prettyScientificNumber", () => {
|
|
|
40
48
|
});
|
|
41
49
|
|
|
42
50
|
it("should not round numbers when shouldRound is false", () => {
|
|
43
|
-
expect(prettyScientificNumber(123_456)).toBe("123,456");
|
|
44
|
-
expect(prettyScientificNumber(123_456.7)).toBe("123,456.7");
|
|
45
|
-
expect(prettyScientificNumber(12_345.6789)).toBe("12,345.6789");
|
|
46
|
-
expect(prettyScientificNumber(1.234_567_891_011_12)).toBe(
|
|
51
|
+
expect(prettyScientificNumber(123_456, options)).toBe("123,456");
|
|
52
|
+
expect(prettyScientificNumber(123_456.7, options)).toBe("123,456.7");
|
|
53
|
+
expect(prettyScientificNumber(12_345.6789, options)).toBe("12,345.6789");
|
|
54
|
+
expect(prettyScientificNumber(1.234_567_891_011_12, options)).toBe(
|
|
47
55
|
"1.23456789101112",
|
|
48
56
|
);
|
|
49
57
|
});
|
|
@@ -51,26 +59,30 @@ describe("prettyScientificNumber", () => {
|
|
|
51
59
|
|
|
52
60
|
describe("prettyEngineeringNumber", () => {
|
|
53
61
|
it("should handle special cases", () => {
|
|
54
|
-
expect(prettyEngineeringNumber(0)).toBe("0");
|
|
55
|
-
expect(prettyEngineeringNumber(-0)).toBe("0"); // Test with negative zero
|
|
56
|
-
expect(prettyEngineeringNumber(Number.NaN)).toBe("NaN");
|
|
57
|
-
expect(prettyEngineeringNumber(Number.POSITIVE_INFINITY)).toBe(
|
|
58
|
-
|
|
62
|
+
expect(prettyEngineeringNumber(0, locale)).toBe("0");
|
|
63
|
+
expect(prettyEngineeringNumber(-0, locale)).toBe("0"); // Test with negative zero
|
|
64
|
+
expect(prettyEngineeringNumber(Number.NaN, locale)).toBe("NaN");
|
|
65
|
+
expect(prettyEngineeringNumber(Number.POSITIVE_INFINITY, locale)).toBe(
|
|
66
|
+
"Infinity",
|
|
67
|
+
);
|
|
68
|
+
expect(prettyEngineeringNumber(Number.NEGATIVE_INFINITY, locale)).toBe(
|
|
69
|
+
"-Infinity",
|
|
70
|
+
);
|
|
59
71
|
});
|
|
60
72
|
|
|
61
73
|
it("should format decimals with engineering notation, ignoring integer part", () => {
|
|
62
|
-
expect(prettyEngineeringNumber(123_456)).toBe("123k");
|
|
63
|
-
expect(prettyEngineeringNumber(123_456.7)).toBe("123k");
|
|
64
|
-
expect(prettyEngineeringNumber(12_345.6789)).toBe("12.3k");
|
|
65
|
-
expect(prettyEngineeringNumber(1.2345)).toBe("1.23");
|
|
66
|
-
expect(prettyEngineeringNumber(1.000_001_234)).toBe("1");
|
|
67
|
-
expect(prettyEngineeringNumber(0.12)).toBe("120m");
|
|
68
|
-
expect(prettyEngineeringNumber(0.1234)).toBe("123m");
|
|
69
|
-
expect(prettyEngineeringNumber(0.000_123_4)).toBe("123µ");
|
|
70
|
-
expect(prettyEngineeringNumber(-1.2345)).toBe("-1.23"); // Test with negative numbers
|
|
71
|
-
expect(prettyEngineeringNumber(-1.000_001_234)).toBe("-1");
|
|
72
|
-
expect(prettyEngineeringNumber(-0.12)).toBe("-120m");
|
|
73
|
-
expect(prettyEngineeringNumber(-0.1234)).toBe("-123m");
|
|
74
|
-
expect(prettyEngineeringNumber(-0.000_123_4)).toBe("-123µ");
|
|
74
|
+
expect(prettyEngineeringNumber(123_456, locale)).toBe("123k");
|
|
75
|
+
expect(prettyEngineeringNumber(123_456.7, locale)).toBe("123k");
|
|
76
|
+
expect(prettyEngineeringNumber(12_345.6789, locale)).toBe("12.3k");
|
|
77
|
+
expect(prettyEngineeringNumber(1.2345, locale)).toBe("1.23");
|
|
78
|
+
expect(prettyEngineeringNumber(1.000_001_234, locale)).toBe("1");
|
|
79
|
+
expect(prettyEngineeringNumber(0.12, locale)).toBe("120m");
|
|
80
|
+
expect(prettyEngineeringNumber(0.1234, locale)).toBe("123m");
|
|
81
|
+
expect(prettyEngineeringNumber(0.000_123_4, locale)).toBe("123µ");
|
|
82
|
+
expect(prettyEngineeringNumber(-1.2345, locale)).toBe("-1.23"); // Test with negative numbers
|
|
83
|
+
expect(prettyEngineeringNumber(-1.000_001_234, locale)).toBe("-1");
|
|
84
|
+
expect(prettyEngineeringNumber(-0.12, locale)).toBe("-120m");
|
|
85
|
+
expect(prettyEngineeringNumber(-0.1234, locale)).toBe("-123m");
|
|
86
|
+
expect(prettyEngineeringNumber(-0.000_123_4, locale)).toBe("-123µ");
|
|
75
87
|
});
|
|
76
88
|
});
|
|
@@ -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
|
+
});
|
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
|
})}`;
|
package/src/utils/dom.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/* Copyright 2024 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { AnsiUp } from "ansi_up";
|
|
4
|
+
import { Logger } from "./Logger";
|
|
5
|
+
|
|
6
|
+
// Create a shared AnsiUp instance
|
|
7
|
+
const ansiUp = new AnsiUp();
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Extracts plain text content from HTML by removing all HTML tags and normalizing whitespace.
|
|
11
|
+
*
|
|
12
|
+
* @param htmlString The HTML string to parse
|
|
13
|
+
* @returns Plain text content with HTML tags removed and whitespace normalized
|
|
14
|
+
*/
|
|
15
|
+
export function parseHtmlContent(htmlString: string): string {
|
|
16
|
+
try {
|
|
17
|
+
// Create a temporary DOM element to parse HTML
|
|
18
|
+
const tempDiv = document.createElement("div");
|
|
19
|
+
tempDiv.innerHTML = htmlString;
|
|
20
|
+
|
|
21
|
+
// Extract text content, removing HTML tags
|
|
22
|
+
const textContent = tempDiv.textContent || tempDiv.innerText || "";
|
|
23
|
+
const lines = textContent.split("\n");
|
|
24
|
+
return lines.map((line) => line.trimEnd()).join("\n");
|
|
25
|
+
} catch (error) {
|
|
26
|
+
Logger.error("Error parsing HTML content:", error);
|
|
27
|
+
// If parsing fails, return the original string
|
|
28
|
+
return htmlString;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Converts ANSI escape sequences to plain text by first converting to HTML and then stripping HTML tags.
|
|
34
|
+
* This is useful for console output that may contain ANSI color codes or formatting.
|
|
35
|
+
*
|
|
36
|
+
* @param ansiString String that may contain ANSI escape sequences
|
|
37
|
+
* @returns Plain text with ANSI codes removed and HTML stripped
|
|
38
|
+
*/
|
|
39
|
+
export function ansiToPlainText(ansiString: string): string {
|
|
40
|
+
if (!ansiString) {
|
|
41
|
+
return "";
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
// Convert ANSI escape sequences to HTML
|
|
46
|
+
const htmlString = ansiUp.ansi_to_html(ansiString);
|
|
47
|
+
|
|
48
|
+
// Strip HTML tags and return clean text
|
|
49
|
+
return parseHtmlContent(htmlString);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
Logger.error("Error converting ANSI to plain text:", error);
|
|
52
|
+
// If conversion fails, return the original string
|
|
53
|
+
return ansiString;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -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
|
@@ -64,7 +64,7 @@ function safeJSONParse(message: string): unknown {
|
|
|
64
64
|
|
|
65
65
|
export class CellNotInitializedError extends Error {
|
|
66
66
|
constructor(
|
|
67
|
-
message
|
|
67
|
+
message = "The cell containing this UI element has not been run yet. Please run the cell first.",
|
|
68
68
|
) {
|
|
69
69
|
super(message);
|
|
70
70
|
this.name = "CellNotInitializedError";
|