@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,79 @@
|
|
|
1
|
+
/* Copyright 2024 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import type { SupportedDialects } from "@marimo-team/codemirror-sql";
|
|
4
|
+
import { atom, useAtomValue } from "jotai";
|
|
5
|
+
import type { CellId } from "@/core/cells/ids";
|
|
6
|
+
import { store } from "@/core/state/jotai";
|
|
7
|
+
|
|
8
|
+
export interface SQLValidationError {
|
|
9
|
+
errorType: string;
|
|
10
|
+
errorMessage: string;
|
|
11
|
+
codeblock?: string; // Code block that caused the error
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
type CellToSQLErrors = Map<CellId, SQLValidationError>;
|
|
15
|
+
|
|
16
|
+
export const sqlValidationErrorsAtom = atom<CellToSQLErrors>(
|
|
17
|
+
new Map<CellId, SQLValidationError>(),
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
export const useSqlValidationErrorsForCell = (cellId: CellId) => {
|
|
21
|
+
const sqlValidationErrors = useAtomValue(sqlValidationErrorsAtom);
|
|
22
|
+
return sqlValidationErrors.get(cellId);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export function clearSqlValidationError(cellId: CellId) {
|
|
26
|
+
const sqlValidationErrors = store.get(sqlValidationErrorsAtom);
|
|
27
|
+
const newErrors = new Map(sqlValidationErrors);
|
|
28
|
+
newErrors.delete(cellId);
|
|
29
|
+
store.set(sqlValidationErrorsAtom, newErrors);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function setSqlValidationError({
|
|
33
|
+
cellId,
|
|
34
|
+
error,
|
|
35
|
+
dialect,
|
|
36
|
+
}: {
|
|
37
|
+
cellId: CellId;
|
|
38
|
+
error: string;
|
|
39
|
+
dialect: SupportedDialects | null;
|
|
40
|
+
}) {
|
|
41
|
+
const sqlValidationErrors = store.get(sqlValidationErrorsAtom);
|
|
42
|
+
const newErrors = new Map(sqlValidationErrors);
|
|
43
|
+
|
|
44
|
+
const errorResult: SQLValidationError =
|
|
45
|
+
dialect === "DuckDB" ? handleDuckdbError(error) : splitErrorMessage(error);
|
|
46
|
+
|
|
47
|
+
newErrors.set(cellId, errorResult);
|
|
48
|
+
store.set(sqlValidationErrorsAtom, newErrors);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function handleDuckdbError(error: string): SQLValidationError {
|
|
52
|
+
const { errorType, errorMessage } = splitErrorMessage(error);
|
|
53
|
+
let newErrorMessage = errorMessage;
|
|
54
|
+
|
|
55
|
+
// Extract the LINE and the rest of the message as codeblock, keep errorMessage as whatever is before
|
|
56
|
+
let codeblock: string | undefined;
|
|
57
|
+
const lineIndex = errorMessage.indexOf("LINE ");
|
|
58
|
+
if (lineIndex !== -1) {
|
|
59
|
+
codeblock = errorMessage.slice(Math.max(0, lineIndex)).trim();
|
|
60
|
+
newErrorMessage = errorMessage.slice(0, Math.max(0, lineIndex)).trim();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
errorType,
|
|
65
|
+
errorMessage: newErrorMessage,
|
|
66
|
+
codeblock,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function splitErrorMessage(error: string) {
|
|
71
|
+
const errorType = error.split(":")[0].trim();
|
|
72
|
+
const errorMessage = error.split(":").slice(1).join(":").trim();
|
|
73
|
+
return { errorType, errorMessage };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export const exportedForTesting = {
|
|
77
|
+
splitErrorMessage,
|
|
78
|
+
handleDuckdbError,
|
|
79
|
+
};
|
|
@@ -5,7 +5,8 @@ import { Button } from "@/components/ui/button";
|
|
|
5
5
|
import { Checkbox } from "@/components/ui/checkbox";
|
|
6
6
|
import { Tooltip, TooltipProvider } from "@/components/ui/tooltip";
|
|
7
7
|
import { normalizeName } from "@/core/cells/names";
|
|
8
|
-
import
|
|
8
|
+
import { getFeatureFlag } from "@/core/config/feature-flag";
|
|
9
|
+
import { type ConnectionName, DUCKDB_ENGINE } from "@/core/datasets/engines";
|
|
9
10
|
import { useAutoGrowInputProps } from "@/hooks/useAutoGrowInputProps";
|
|
10
11
|
import { formatSQL } from "../../format";
|
|
11
12
|
import { languageAdapterState } from "../extension";
|
|
@@ -22,7 +23,7 @@ import {
|
|
|
22
23
|
import type { LanguageMetadataOf } from "../types";
|
|
23
24
|
import type { QuotePrefixKind } from "../utils/quotes";
|
|
24
25
|
import { getQuotePrefix, MarkdownQuotePrefixTooltip } from "./markdown";
|
|
25
|
-
import { SQLEngineSelect } from "./sql";
|
|
26
|
+
import { SQLEngineSelect, SQLModeSelect } from "./sql";
|
|
26
27
|
|
|
27
28
|
const Divider = () => <div className="h-4 border-r border-border" />;
|
|
28
29
|
|
|
@@ -70,6 +71,8 @@ export const LanguagePanelComponent: React.FC<{
|
|
|
70
71
|
updateSQLDialectFromConnection(view, engine);
|
|
71
72
|
};
|
|
72
73
|
|
|
74
|
+
const sqlModeEnabled = getFeatureFlag("sql_mode");
|
|
75
|
+
|
|
73
76
|
actions = (
|
|
74
77
|
<div className="flex flex-1 gap-2 items-center">
|
|
75
78
|
<label className="flex gap-2 items-center">
|
|
@@ -95,6 +98,9 @@ export const LanguagePanelComponent: React.FC<{
|
|
|
95
98
|
onChange={switchEngine}
|
|
96
99
|
/>
|
|
97
100
|
<div className="flex items-center gap-2 ml-auto">
|
|
101
|
+
{sqlModeEnabled && metadata.engine === DUCKDB_ENGINE && (
|
|
102
|
+
<SQLModeSelect />
|
|
103
|
+
)}
|
|
98
104
|
<Tooltip content="Format SQL">
|
|
99
105
|
<Button
|
|
100
106
|
variant="text"
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
/* Copyright 2024 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
|
+
import type { SelectTriggerProps } from "@radix-ui/react-select";
|
|
3
4
|
import { useAtomValue } from "jotai";
|
|
4
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
AlertCircle,
|
|
7
|
+
CircleHelpIcon,
|
|
8
|
+
DatabaseBackup,
|
|
9
|
+
SearchCheck,
|
|
10
|
+
} from "lucide-react";
|
|
5
11
|
import { transformDisplayName } from "@/components/databases/display";
|
|
6
12
|
import { DatabaseLogo } from "@/components/databases/icon";
|
|
13
|
+
import { Button } from "@/components/ui/button";
|
|
7
14
|
import {
|
|
8
15
|
Select,
|
|
9
16
|
SelectContent,
|
|
@@ -14,6 +21,7 @@ import {
|
|
|
14
21
|
SelectTrigger,
|
|
15
22
|
SelectValue,
|
|
16
23
|
} from "@/components/ui/select";
|
|
24
|
+
import { Tooltip } from "@/components/ui/tooltip";
|
|
17
25
|
import {
|
|
18
26
|
dataConnectionsMapAtom,
|
|
19
27
|
setLatestEngineSelected,
|
|
@@ -24,6 +32,7 @@ import {
|
|
|
24
32
|
} from "@/core/datasets/engines";
|
|
25
33
|
import type { DataSourceConnection } from "@/core/kernel/messages";
|
|
26
34
|
import { useNonce } from "@/hooks/useNonce";
|
|
35
|
+
import { type SQLMode, useSQLMode } from "../languages/sql/sql-mode";
|
|
27
36
|
|
|
28
37
|
interface SelectProps {
|
|
29
38
|
selectedEngine: ConnectionName;
|
|
@@ -77,7 +86,7 @@ export const SQLEngineSelect: React.FC<SelectProps> = ({
|
|
|
77
86
|
<SelectItem key={connection.name} value={connection.name}>
|
|
78
87
|
<div className="flex items-center gap-1">
|
|
79
88
|
<DatabaseLogo className="h-3 w-3" name={connection.dialect} />
|
|
80
|
-
<span className="truncate">
|
|
89
|
+
<span className="truncate ml-0.5">
|
|
81
90
|
{transformDisplayName(connection.display_name)}
|
|
82
91
|
</span>
|
|
83
92
|
</div>
|
|
@@ -88,9 +97,9 @@ export const SQLEngineSelect: React.FC<SelectProps> = ({
|
|
|
88
97
|
return (
|
|
89
98
|
<div className="flex flex-row gap-1 items-center">
|
|
90
99
|
<Select value={selectedEngine} onValueChange={handleSelectEngine}>
|
|
91
|
-
<
|
|
100
|
+
<SQLSelectTrigger>
|
|
92
101
|
<SelectValue placeholder="Select an engine" />
|
|
93
|
-
</
|
|
102
|
+
</SQLSelectTrigger>
|
|
94
103
|
<SelectContent>
|
|
95
104
|
<SelectGroup>
|
|
96
105
|
<SelectLabel>Database connections</SelectLabel>
|
|
@@ -130,3 +139,71 @@ export const SQLEngineSelect: React.FC<SelectProps> = ({
|
|
|
130
139
|
const HELP_KEY = "__help__";
|
|
131
140
|
const HELP_URL =
|
|
132
141
|
"http://docs.marimo.io/guides/working_with_data/sql/#connecting-to-a-custom-database";
|
|
142
|
+
|
|
143
|
+
export const SQLModeSelect: React.FC = () => {
|
|
144
|
+
const { sqlMode, setSQLMode } = useSQLMode();
|
|
145
|
+
|
|
146
|
+
const handleToggleMode = () => {
|
|
147
|
+
setSQLMode(sqlMode === "validate" ? "default" : "validate");
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const getModeIcon = (mode: SQLMode) => {
|
|
151
|
+
return mode === "validate" ? (
|
|
152
|
+
<SearchCheck className="h-3 w-3" />
|
|
153
|
+
) : (
|
|
154
|
+
<DatabaseBackup className="h-3 w-3" />
|
|
155
|
+
);
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const getTooltipContent = (mode: SQLMode) => {
|
|
159
|
+
return mode === "validate" ? (
|
|
160
|
+
<div className="text-xs">
|
|
161
|
+
<div className="font-semibold mb-1 flex flex-row items-center gap-1">
|
|
162
|
+
<SearchCheck className="h-3 w-3" />
|
|
163
|
+
Validate Mode
|
|
164
|
+
</div>
|
|
165
|
+
<p>Queries are validated as you write them</p>
|
|
166
|
+
</div>
|
|
167
|
+
) : (
|
|
168
|
+
<div className="text-xs">
|
|
169
|
+
<div className="font-semibold mb-1 flex flex-row items-center gap-1">
|
|
170
|
+
<DatabaseBackup className="h-3 w-3" />
|
|
171
|
+
Default Mode
|
|
172
|
+
</div>
|
|
173
|
+
<p>Standard editing</p>
|
|
174
|
+
</div>
|
|
175
|
+
);
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
return (
|
|
179
|
+
<div className="flex flex-row gap-1 items-center">
|
|
180
|
+
<Tooltip delayDuration={300} content={getTooltipContent(sqlMode)}>
|
|
181
|
+
<Button
|
|
182
|
+
variant="ghost"
|
|
183
|
+
size="sm"
|
|
184
|
+
onClick={handleToggleMode}
|
|
185
|
+
className="h-5 px-1.5 text-xs border-border shadow-none hover:bg-accent"
|
|
186
|
+
>
|
|
187
|
+
{getModeIcon(sqlMode)}
|
|
188
|
+
<span className="ml-1">
|
|
189
|
+
{sqlMode === "validate" ? "Validate" : "Default"}
|
|
190
|
+
</span>
|
|
191
|
+
</Button>
|
|
192
|
+
</Tooltip>
|
|
193
|
+
</div>
|
|
194
|
+
);
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
const SQLSelectTrigger: React.FC<SelectTriggerProps> = ({
|
|
198
|
+
children,
|
|
199
|
+
...props
|
|
200
|
+
}) => {
|
|
201
|
+
return (
|
|
202
|
+
<SelectTrigger
|
|
203
|
+
className="text-xs border-border shadow-none! ring-0! h-5 px-1.5 hover:bg-accent transition-colors"
|
|
204
|
+
{...props}
|
|
205
|
+
>
|
|
206
|
+
{children}
|
|
207
|
+
</SelectTrigger>
|
|
208
|
+
);
|
|
209
|
+
};
|
|
@@ -18,13 +18,19 @@ import { getLSPDocument } from "./utils";
|
|
|
18
18
|
|
|
19
19
|
class Snapshotter {
|
|
20
20
|
private documentVersion = 0;
|
|
21
|
+
private readonly getNotebookCode: () => {
|
|
22
|
+
cellIds: CellId[];
|
|
23
|
+
codes: Record<CellId, string>;
|
|
24
|
+
};
|
|
21
25
|
|
|
22
26
|
constructor(
|
|
23
|
-
|
|
27
|
+
getNotebookCode: () => {
|
|
24
28
|
cellIds: CellId[];
|
|
25
29
|
codes: Record<CellId, string>;
|
|
26
30
|
},
|
|
27
|
-
) {
|
|
31
|
+
) {
|
|
32
|
+
this.getNotebookCode = getNotebookCode;
|
|
33
|
+
}
|
|
28
34
|
|
|
29
35
|
/**
|
|
30
36
|
* Map from the global document version to the cell id and version.
|
|
@@ -9,7 +9,7 @@ import { connectionAtom } from "../../../network/connection";
|
|
|
9
9
|
import { dynamicReadonly, isEditorReadonly } from "../extension";
|
|
10
10
|
|
|
11
11
|
function makeStoreWithConnection(
|
|
12
|
-
state: WebSocketState.CONNECTING | WebSocketState.OPEN,
|
|
12
|
+
state: typeof WebSocketState.CONNECTING | typeof WebSocketState.OPEN,
|
|
13
13
|
) {
|
|
14
14
|
const store = createStore();
|
|
15
15
|
store.set(connectionAtom, { state });
|
|
@@ -202,13 +202,25 @@ export const createSelectionLayer = (): Extension =>
|
|
|
202
202
|
* Renders a blinking cursor to indicate the cursor of another user.
|
|
203
203
|
*/
|
|
204
204
|
export class RemoteCursorMarker implements LayerMarker {
|
|
205
|
+
private left: number;
|
|
206
|
+
private top: number;
|
|
207
|
+
private height: number;
|
|
208
|
+
private name: string;
|
|
209
|
+
private colorClassName: string;
|
|
210
|
+
|
|
205
211
|
constructor(
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
) {
|
|
212
|
+
left: number,
|
|
213
|
+
top: number,
|
|
214
|
+
height: number,
|
|
215
|
+
name: string,
|
|
216
|
+
colorClassName: string,
|
|
217
|
+
) {
|
|
218
|
+
this.left = left;
|
|
219
|
+
this.top = top;
|
|
220
|
+
this.height = height;
|
|
221
|
+
this.name = name;
|
|
222
|
+
this.colorClassName = colorClassName;
|
|
223
|
+
}
|
|
212
224
|
|
|
213
225
|
draw(): HTMLElement {
|
|
214
226
|
const elt = document.createElement("div");
|
|
@@ -353,16 +365,30 @@ export interface CursorPosition {
|
|
|
353
365
|
|
|
354
366
|
export class AwarenessPlugin implements PluginValue {
|
|
355
367
|
sub: Subscription;
|
|
368
|
+
public view: EditorView;
|
|
369
|
+
public doc: LoroDoc;
|
|
370
|
+
public user: UserState;
|
|
371
|
+
public awareness: Awareness<AwarenessState>;
|
|
372
|
+
private getTextFromDoc: (doc: LoroDoc) => LoroText;
|
|
373
|
+
private scopeId: ScopeId;
|
|
374
|
+
private getUserId?: () => Uid;
|
|
356
375
|
|
|
357
376
|
constructor(
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
377
|
+
view: EditorView,
|
|
378
|
+
doc: LoroDoc,
|
|
379
|
+
user: UserState,
|
|
380
|
+
awareness: Awareness<AwarenessState>,
|
|
381
|
+
getTextFromDoc: (doc: LoroDoc) => LoroText,
|
|
382
|
+
scopeId: ScopeId,
|
|
383
|
+
getUserId?: () => Uid,
|
|
365
384
|
) {
|
|
385
|
+
this.view = view;
|
|
386
|
+
this.doc = doc;
|
|
387
|
+
this.user = user;
|
|
388
|
+
this.awareness = awareness;
|
|
389
|
+
this.getTextFromDoc = getTextFromDoc;
|
|
390
|
+
this.scopeId = scopeId;
|
|
391
|
+
this.getUserId = getUserId;
|
|
366
392
|
this.sub = this.doc.subscribe((e) => {
|
|
367
393
|
if (e.by === "local") {
|
|
368
394
|
// update remote cursor position
|
|
@@ -435,12 +461,21 @@ export class AwarenessPlugin implements PluginValue {
|
|
|
435
461
|
}
|
|
436
462
|
export class RemoteAwarenessPlugin implements PluginValue {
|
|
437
463
|
_awarenessListener?: AwarenessListener;
|
|
464
|
+
public view: EditorView;
|
|
465
|
+
public doc: LoroDoc;
|
|
466
|
+
public awareness: Awareness<AwarenessState>;
|
|
467
|
+
private scopeId: ScopeId;
|
|
468
|
+
|
|
438
469
|
constructor(
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
470
|
+
view: EditorView,
|
|
471
|
+
doc: LoroDoc,
|
|
472
|
+
awareness: Awareness<AwarenessState>,
|
|
473
|
+
scopeId: ScopeId,
|
|
443
474
|
) {
|
|
475
|
+
this.view = view;
|
|
476
|
+
this.doc = doc;
|
|
477
|
+
this.awareness = awareness;
|
|
478
|
+
this.scopeId = scopeId;
|
|
444
479
|
const listener: AwarenessListener = async (arg, origin) => {
|
|
445
480
|
if (origin === "local") {
|
|
446
481
|
return;
|
|
@@ -40,13 +40,21 @@ export const loroSyncAnnotation = Annotation.define();
|
|
|
40
40
|
export class LoroSyncPluginValue implements PluginValue {
|
|
41
41
|
sub?: Subscription;
|
|
42
42
|
private isInitDispatch = false;
|
|
43
|
+
private view: EditorView;
|
|
44
|
+
private doc: LoroDoc;
|
|
45
|
+
private docPath: string[];
|
|
46
|
+
private getTextFromDoc: (doc: LoroDoc) => LoroText;
|
|
43
47
|
|
|
44
48
|
constructor(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
+
view: EditorView,
|
|
50
|
+
doc: LoroDoc,
|
|
51
|
+
docPath: string[],
|
|
52
|
+
getTextFromDoc: (doc: LoroDoc) => LoroText,
|
|
49
53
|
) {
|
|
54
|
+
this.view = view;
|
|
55
|
+
this.doc = doc;
|
|
56
|
+
this.docPath = docPath;
|
|
57
|
+
this.getTextFromDoc = getTextFromDoc;
|
|
50
58
|
this.sub = doc.subscribe(this.onRemoteUpdate);
|
|
51
59
|
Promise.resolve().then(() => {
|
|
52
60
|
this.isInitDispatch = true;
|
|
@@ -82,6 +82,10 @@ export const editorFontSizeAtom = atom<number>((get) => {
|
|
|
82
82
|
return get(resolvedMarimoConfigAtom).display.code_editor_font_size;
|
|
83
83
|
});
|
|
84
84
|
|
|
85
|
+
export const localeAtom = atom<string | null | undefined>((get) => {
|
|
86
|
+
return get(resolvedMarimoConfigAtom).display.locale;
|
|
87
|
+
});
|
|
88
|
+
|
|
85
89
|
export function isAiEnabled(config: UserConfig) {
|
|
86
90
|
return (
|
|
87
91
|
Boolean(config.ai?.models?.chat_model) ||
|
|
@@ -14,6 +14,7 @@ export interface ExperimentalFeatures {
|
|
|
14
14
|
mcp_docs: boolean;
|
|
15
15
|
sql_linter: boolean;
|
|
16
16
|
external_agents: boolean;
|
|
17
|
+
sql_mode: boolean;
|
|
17
18
|
// Add new feature flags here
|
|
18
19
|
}
|
|
19
20
|
|
|
@@ -26,13 +27,14 @@ const defaultValues: ExperimentalFeatures = {
|
|
|
26
27
|
mcp_docs: false,
|
|
27
28
|
sql_linter: false,
|
|
28
29
|
external_agents: import.meta.env.DEV,
|
|
30
|
+
sql_mode: false,
|
|
29
31
|
};
|
|
30
32
|
|
|
31
33
|
export function getFeatureFlag<T extends keyof ExperimentalFeatures>(
|
|
32
34
|
feature: T,
|
|
33
35
|
): ExperimentalFeatures[T] {
|
|
34
36
|
return (
|
|
35
|
-
(getResolvedMarimoConfig()
|
|
37
|
+
(getResolvedMarimoConfig()?.experimental?.[
|
|
36
38
|
feature
|
|
37
39
|
] as ExperimentalFeatures[T]) ?? defaultValues[feature]
|
|
38
40
|
);
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
/* Copyright 2024 Marimo. All rights reserved. */
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
SQLTableListPreview,
|
|
4
|
+
SQLTablePreview,
|
|
5
|
+
ValidateSQLResult,
|
|
6
|
+
} from "../kernel/messages";
|
|
3
7
|
import { DeferredRequestRegistry } from "../network/DeferredRequestRegistry";
|
|
4
8
|
import { getRequestClient } from "../network/requests";
|
|
5
9
|
import type {
|
|
6
10
|
PreviewSQLTableListRequest,
|
|
7
11
|
PreviewSQLTableRequest,
|
|
12
|
+
ValidateSQLRequest,
|
|
8
13
|
} from "../network/types";
|
|
9
14
|
|
|
10
15
|
// We make a request to the backend to preview the table, passing in Engine, DB, Schema, and Table
|
|
@@ -32,3 +37,14 @@ export const PreviewSQLTableList = new DeferredRequestRegistry<
|
|
|
32
37
|
...req,
|
|
33
38
|
});
|
|
34
39
|
});
|
|
40
|
+
|
|
41
|
+
export const ValidateSQL = new DeferredRequestRegistry<
|
|
42
|
+
Omit<ValidateSQLRequest, "requestId">,
|
|
43
|
+
ValidateSQLResult
|
|
44
|
+
>("validate-sql", async (requestId, req) => {
|
|
45
|
+
const client = getRequestClient();
|
|
46
|
+
await client.validateSQL({
|
|
47
|
+
requestId: requestId,
|
|
48
|
+
...req,
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -452,10 +452,13 @@ export class HotkeyProvider implements IHotkeyProvider {
|
|
|
452
452
|
return new HotkeyProvider(DEFAULT_HOT_KEY, { platform });
|
|
453
453
|
}
|
|
454
454
|
|
|
455
|
+
private hotkeys: Record<HotkeyAction, Hotkey>;
|
|
456
|
+
|
|
455
457
|
constructor(
|
|
456
|
-
|
|
458
|
+
hotkeys: Record<HotkeyAction, Hotkey>,
|
|
457
459
|
options: HotkeyProviderOptions = {},
|
|
458
460
|
) {
|
|
461
|
+
this.hotkeys = hotkeys;
|
|
459
462
|
this.platform = options.platform ?? resolvePlatform();
|
|
460
463
|
this.mod = this.platform === "mac" ? "Cmd" : "Ctrl";
|
|
461
464
|
}
|
|
@@ -503,13 +506,14 @@ export class HotkeyProvider implements IHotkeyProvider {
|
|
|
503
506
|
}
|
|
504
507
|
|
|
505
508
|
export class OverridingHotkeyProvider extends HotkeyProvider {
|
|
509
|
+
private readonly overrides: Partial<Record<HotkeyAction, string | undefined>>;
|
|
510
|
+
|
|
506
511
|
constructor(
|
|
507
|
-
|
|
508
|
-
Record<HotkeyAction, string | undefined>
|
|
509
|
-
>,
|
|
512
|
+
overrides: Partial<Record<HotkeyAction, string | undefined>>,
|
|
510
513
|
options: HotkeyProviderOptions = {},
|
|
511
514
|
) {
|
|
512
515
|
super(DEFAULT_HOT_KEY, options);
|
|
516
|
+
this.overrides = overrides;
|
|
513
517
|
}
|
|
514
518
|
|
|
515
519
|
override getHotkey(action: HotkeyAction): ResolvedHotkey {
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/* Copyright 2024 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { cleanup, render } from "@testing-library/react";
|
|
4
|
+
import { createStore, Provider } from "jotai";
|
|
5
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
6
|
+
import { userConfigAtom } from "@/core/config/config";
|
|
7
|
+
import { parseUserConfig } from "@/core/config/config-schema";
|
|
8
|
+
import { LocaleProvider } from "../locale-provider";
|
|
9
|
+
|
|
10
|
+
// Mock navigator.language with a getter
|
|
11
|
+
let mockNavigatorLanguage: string | undefined;
|
|
12
|
+
|
|
13
|
+
Object.defineProperty(window, "navigator", {
|
|
14
|
+
value: {
|
|
15
|
+
get language() {
|
|
16
|
+
return mockNavigatorLanguage;
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
writable: true,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// Mock react-aria-components I18nProvider
|
|
23
|
+
vi.mock("react-aria-components", () => ({
|
|
24
|
+
I18nProvider: ({
|
|
25
|
+
children,
|
|
26
|
+
locale,
|
|
27
|
+
}: {
|
|
28
|
+
children: React.ReactNode;
|
|
29
|
+
locale: string;
|
|
30
|
+
}) => (
|
|
31
|
+
<div data-testid="i18n-provider" data-locale={locale}>
|
|
32
|
+
{children}
|
|
33
|
+
</div>
|
|
34
|
+
),
|
|
35
|
+
}));
|
|
36
|
+
|
|
37
|
+
describe("LocaleProvider", () => {
|
|
38
|
+
beforeEach(() => {
|
|
39
|
+
// Reset the mock before each test
|
|
40
|
+
mockNavigatorLanguage = undefined;
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
afterEach(() => {
|
|
44
|
+
cleanup();
|
|
45
|
+
// Clear all mocks after each test
|
|
46
|
+
mockNavigatorLanguage = undefined;
|
|
47
|
+
vi.clearAllMocks();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("should render I18nProvider without locale when locale is null", () => {
|
|
51
|
+
const store = createStore();
|
|
52
|
+
const config = parseUserConfig({ display: { locale: null } });
|
|
53
|
+
store.set(userConfigAtom, config);
|
|
54
|
+
|
|
55
|
+
const { getByTestId } = render(
|
|
56
|
+
<Provider store={store}>
|
|
57
|
+
<LocaleProvider>
|
|
58
|
+
<div>Test content</div>
|
|
59
|
+
</LocaleProvider>
|
|
60
|
+
</Provider>,
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const i18nProvider = getByTestId("i18n-provider");
|
|
64
|
+
expect(i18nProvider).toBeInTheDocument();
|
|
65
|
+
expect(i18nProvider.dataset.locale).toBe(undefined);
|
|
66
|
+
expect(i18nProvider).toHaveTextContent("Test content");
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("should render I18nProvider without locale when locale is undefined", () => {
|
|
70
|
+
const store = createStore();
|
|
71
|
+
const config = parseUserConfig({ display: { locale: undefined } });
|
|
72
|
+
store.set(userConfigAtom, config);
|
|
73
|
+
|
|
74
|
+
const { getByTestId } = render(
|
|
75
|
+
<Provider store={store}>
|
|
76
|
+
<LocaleProvider>
|
|
77
|
+
<div>Test content</div>
|
|
78
|
+
</LocaleProvider>
|
|
79
|
+
</Provider>,
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const i18nProvider = getByTestId("i18n-provider");
|
|
83
|
+
expect(i18nProvider).toBeInTheDocument();
|
|
84
|
+
expect(i18nProvider.dataset.locale).toBe(undefined);
|
|
85
|
+
expect(i18nProvider).toHaveTextContent("Test content");
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("should render I18nProvider with locale when locale is provided", () => {
|
|
89
|
+
const store = createStore();
|
|
90
|
+
const testLocale = "es-ES";
|
|
91
|
+
const config = parseUserConfig({ display: { locale: testLocale } });
|
|
92
|
+
store.set(userConfigAtom, config);
|
|
93
|
+
|
|
94
|
+
const { getByTestId } = render(
|
|
95
|
+
<Provider store={store}>
|
|
96
|
+
<LocaleProvider>
|
|
97
|
+
<div>Test content</div>
|
|
98
|
+
</LocaleProvider>
|
|
99
|
+
</Provider>,
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
const i18nProvider = getByTestId("i18n-provider");
|
|
103
|
+
expect(i18nProvider).toBeInTheDocument();
|
|
104
|
+
expect(i18nProvider.dataset.locale).toBe(testLocale);
|
|
105
|
+
expect(i18nProvider).toHaveTextContent("Test content");
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("should render I18nProvider with different locale values", () => {
|
|
109
|
+
const testCases = ["en-US", "fr-FR", "de-DE", "ja-JP"];
|
|
110
|
+
|
|
111
|
+
testCases.forEach((locale) => {
|
|
112
|
+
const store = createStore();
|
|
113
|
+
const config = parseUserConfig({ display: { locale } });
|
|
114
|
+
store.set(userConfigAtom, config);
|
|
115
|
+
|
|
116
|
+
const { getByTestId } = render(
|
|
117
|
+
<Provider store={store}>
|
|
118
|
+
<LocaleProvider>
|
|
119
|
+
<div>Test content for {locale}</div>
|
|
120
|
+
</LocaleProvider>
|
|
121
|
+
</Provider>,
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
const i18nProvider = getByTestId("i18n-provider");
|
|
125
|
+
expect(i18nProvider.dataset.locale).toBe(locale);
|
|
126
|
+
expect(i18nProvider).toHaveTextContent(`Test content for ${locale}`);
|
|
127
|
+
|
|
128
|
+
// Clean up after each iteration
|
|
129
|
+
cleanup();
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("should render children correctly", () => {
|
|
134
|
+
const store = createStore();
|
|
135
|
+
const config = parseUserConfig({ display: { locale: "en-US" } });
|
|
136
|
+
store.set(userConfigAtom, config);
|
|
137
|
+
|
|
138
|
+
const { getByText, getByRole } = render(
|
|
139
|
+
<Provider store={store}>
|
|
140
|
+
<LocaleProvider>
|
|
141
|
+
<div>
|
|
142
|
+
<h1>Test Heading</h1>
|
|
143
|
+
<p>Test paragraph</p>
|
|
144
|
+
<button type="button">Test Button</button>
|
|
145
|
+
</div>
|
|
146
|
+
</LocaleProvider>
|
|
147
|
+
</Provider>,
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
expect(getByText("Test Heading")).toBeInTheDocument();
|
|
151
|
+
expect(getByText("Test paragraph")).toBeInTheDocument();
|
|
152
|
+
expect(getByRole("button", { name: "Test Button" })).toBeInTheDocument();
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("should auto-detect locale when no locale is set in config", () => {
|
|
156
|
+
mockNavigatorLanguage = "de-DE";
|
|
157
|
+
|
|
158
|
+
const store = createStore();
|
|
159
|
+
const config = parseUserConfig({});
|
|
160
|
+
store.set(userConfigAtom, config);
|
|
161
|
+
|
|
162
|
+
const { getByTestId } = render(
|
|
163
|
+
<Provider store={store}>
|
|
164
|
+
<LocaleProvider>
|
|
165
|
+
<div>Test content</div>
|
|
166
|
+
</LocaleProvider>
|
|
167
|
+
</Provider>,
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
const i18nProvider = getByTestId("i18n-provider");
|
|
171
|
+
expect(i18nProvider).toBeInTheDocument();
|
|
172
|
+
// When no locale is specified in config, it should use navigator.language
|
|
173
|
+
expect(i18nProvider.dataset.locale).toBe("de-DE");
|
|
174
|
+
expect(i18nProvider).toHaveTextContent("Test content");
|
|
175
|
+
});
|
|
176
|
+
});
|