@marimo-team/islands 0.15.5 → 0.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{ConnectedDataExplorerComponent-CBeIYi8p.js → ConnectedDataExplorerComponent-DyqLQGPc.js} +1567 -1544
- package/dist/{ImageComparisonComponent-Bk0a0xBq.js → ImageComparisonComponent-CQDGJfUA.js} +1 -1
- package/dist/{_baseUniq-utU5_Vu-.js → _baseUniq-B2Nna6Kt.js} +1 -1
- package/dist/{any-language-editor-PrUUh2lr.js → any-language-editor-D-wq0tOG.js} +1 -1
- package/dist/{architectureDiagram-W76B3OCA-D-vOp0UU.js → architectureDiagram-W76B3OCA-C6tdnMBf.js} +4 -4
- package/dist/assets/{worker-BcG8m3h5.js → worker-B0C57BK8.js} +40 -38
- package/dist/{blockDiagram-QIGZ2CNN-IG-z8q8A.js → blockDiagram-QIGZ2CNN-IagL8LCN.js} +5 -5
- package/dist/{c4Diagram-FPNF74CW-5AEXIX3t.js → c4Diagram-FPNF74CW-D3_lIWUP.js} +2 -2
- package/dist/{channel-ECVsTGGL.js → channel-DCJI_DKk.js} +1 -1
- package/dist/{chunk-4BX2VUAB-DfJcd9e-.js → chunk-4BX2VUAB-B2DrODwN.js} +1 -1
- package/dist/{chunk-55IACEB6-BwT8MejR.js → chunk-55IACEB6-BUWDsQ-t.js} +1 -1
- package/dist/{chunk-FMBD7UC4-DW7uxNR6.js → chunk-FMBD7UC4-BExPNFv1.js} +1 -1
- package/dist/{chunk-K7UQS3LO-BGn2ZPDQ.js → chunk-K7UQS3LO-Cixi-Yko.js} +4 -4
- package/dist/{chunk-QN33PNHL-BcIbOumv.js → chunk-QN33PNHL-B83MtvER.js} +1 -1
- package/dist/{chunk-QZHKN3VN-CMSnhk6x.js → chunk-QZHKN3VN-CXvbu85X.js} +1 -1
- package/dist/{chunk-TVAH2DTR-CZF2JRya.js → chunk-TVAH2DTR-CpiumCHg.js} +3 -3
- package/dist/{chunk-TZMSLE5B-BHzN_BY6.js → chunk-TZMSLE5B-DIzaZjcI.js} +1 -1
- package/dist/{classDiagram-v2-RKCZMP56-2H7MseyB.js → classDiagram-KNZD7YFC-DyN5HPdk.js} +2 -2
- package/dist/{classDiagram-KNZD7YFC-2H7MseyB.js → classDiagram-v2-RKCZMP56-DyN5HPdk.js} +2 -2
- package/dist/{clone-DKQcSK7N.js → clone-DrJYap2i.js} +1 -1
- package/dist/{cose-bilkent-S5V4N54A-CgvKFxTr.js → cose-bilkent-S5V4N54A-D39b4WrQ.js} +2 -2
- package/dist/{dagre-5GWH7T2D-VNFIipzt.js → dagre-5GWH7T2D-BLjRxDpS.js} +6 -6
- package/dist/{data-grid-overlay-editor-XdqkKCVx.js → data-grid-overlay-editor-DTALqerV.js} +2 -2
- package/dist/{diagram-N5W7TBWH-D1s8h-eH.js → diagram-N5W7TBWH-MM8AIKGR.js} +5 -5
- package/dist/{diagram-QEK2KX5R-DOa-AstT.js → diagram-QEK2KX5R-BZGarWuJ.js} +3 -3
- package/dist/{diagram-S2PKOQOG-CFZ-Y2zi.js → diagram-S2PKOQOG-CnPinN9Q.js} +3 -3
- package/dist/{dockerfile-zE-2DWBS.js → dockerfile-U8DnCJ4X.js} +1 -1
- package/dist/{erDiagram-AWTI2OKA-WxUYJfbS.js → erDiagram-AWTI2OKA-CvDVbxOO.js} +4 -4
- package/dist/{flowDiagram-PVAE7QVJ-dDZH2O1W.js → flowDiagram-PVAE7QVJ-C2uuBTZS.js} +5 -5
- package/dist/{ganttDiagram-OWAHRB6G-D3CCqPQq.js → ganttDiagram-OWAHRB6G-BEff10RF.js} +4 -4
- package/dist/{gitGraphDiagram-NY62KEGX-BHFylEwc.js → gitGraphDiagram-NY62KEGX-wggu0kb2.js} +4 -4
- package/dist/{glide-data-editor-D0aJSGV_.js → glide-data-editor-Bqh5_dzJ.js} +3 -3
- package/dist/{graph-BPGEu6c8.js → graph-DKpp_wzf.js} +3 -3
- package/dist/{index-HtOEKQ3O.js → index-4XruEJkp.js} +1 -1
- package/dist/{index-eDB61tLS.js → index-DW0BCGJE.js} +1 -1
- package/dist/{index-DotQhzoN.js → index-DdfF_cLK.js} +1 -1
- package/dist/{index-Bx2b23rX.js → index-DzJ_YPCG.js} +3 -3
- package/dist/{infoDiagram-STP46IZ2-DWhhqGPi.js → infoDiagram-STP46IZ2-DF7KW-Op.js} +2 -2
- package/dist/{journeyDiagram-BIP6EPQ6-CU8FpryL.js → journeyDiagram-BIP6EPQ6-B_jmhmqd.js} +3 -3
- package/dist/{kanban-definition-6OIFK2YF-CWhF_a4g.js → kanban-definition-6OIFK2YF-B-M9FTyw.js} +2 -2
- package/dist/{layout-DGonEvAZ.js → layout-C4oVYZZD.js} +4 -4
- package/dist/{linear-Cww2a6nQ.js → linear-C-HCGr0T.js} +1 -1
- package/dist/{main-Bc0LY9fB.js → main-B9x2-9f2.js} +93798 -93495
- package/dist/main.js +1 -1
- package/dist/{mermaid-DpJuOhRr.js → mermaid-BE4cM3Qs.js} +30 -30
- package/dist/{min-CFQjsG4L.js → min-DTpHJ698.js} +2 -2
- package/dist/{mindmap-definition-Q6HEUPPD-K513Ef1t.js → mindmap-definition-Q6HEUPPD-Cpd-hO1E.js} +3 -3
- package/dist/{number-overlay-editor-DuSchUfE.js → number-overlay-editor-CvURA2Ud.js} +2 -2
- package/dist/{pieDiagram-ADFJNKIX-DAIIUJJO.js → pieDiagram-ADFJNKIX-D9f_f6fn.js} +3 -3
- package/dist/{quadrantDiagram-LMRXKWRM-yuf-j7Os.js → quadrantDiagram-LMRXKWRM-DgllE7xw.js} +2 -2
- package/dist/{react-plotly-B378DZ9U.js → react-plotly-BU-JRJSi.js} +1 -1
- package/dist/{requirementDiagram-4UW4RH46-BBWvEl6q.js → requirementDiagram-4UW4RH46-Dk_G8eUb.js} +3 -3
- package/dist/{sankeyDiagram-GR3RE2ED-B_TwV-dS.js → sankeyDiagram-GR3RE2ED-BhLIhDc1.js} +1 -1
- package/dist/{sequenceDiagram-C3RYC4MD-BVC6lltp.js → sequenceDiagram-C3RYC4MD-DHoZdMFJ.js} +3 -3
- package/dist/{slides-component-CPX3S0Y9.js → slides-component-DXAgdf7K.js} +2 -2
- package/dist/{stateDiagram-KXAO66HF-BCU1tYTD.js → stateDiagram-KXAO66HF-C1Ie-7Xf.js} +4 -4
- package/dist/{stateDiagram-v2-UMBNRL4Z-BdvN6wTu.js → stateDiagram-v2-UMBNRL4Z--CRuIHtM.js} +2 -2
- package/dist/style.css +1 -1
- package/dist/{time-CSIip6fV.js → time-yQjlGPwa.js} +2 -2
- package/dist/{timeline-definition-XQNQX7LJ-CCxCPNQI.js → timeline-definition-XQNQX7LJ-D_PjxB1B.js} +1 -1
- package/dist/{treemap-75Q7IDZK-Du6v0BzD.js → treemap-75Q7IDZK--NYqQjUZ.js} +134 -134
- package/dist/{vega-component-Da93sTnp.js → vega-component-CCUOMM5K.js} +2 -2
- package/dist/{xychartDiagram-6GGTOJPD-Oq6xaZKR.js → xychartDiagram-6GGTOJPD-WLKsEnzs.js} +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 +241 -0
- 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 +27 -28
- 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
|
@@ -1,35 +1,249 @@
|
|
|
1
1
|
/* Copyright 2024 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
3
|
import { AttachAddon } from "@xterm/addon-attach";
|
|
4
|
+
import { CanvasAddon } from "@xterm/addon-canvas";
|
|
4
5
|
import { FitAddon } from "@xterm/addon-fit";
|
|
6
|
+
import { SearchAddon } from "@xterm/addon-search";
|
|
7
|
+
import { Unicode11Addon } from "@xterm/addon-unicode11";
|
|
8
|
+
import { WebLinksAddon } from "@xterm/addon-web-links";
|
|
5
9
|
import { Terminal } from "@xterm/xterm";
|
|
6
|
-
import React, { useEffect, useRef, useState } from "react";
|
|
10
|
+
import React, { useEffect, useMemo, useRef, useState } from "react";
|
|
7
11
|
import "@xterm/xterm/css/xterm.css";
|
|
8
12
|
import "./xterm.css";
|
|
13
|
+
import {
|
|
14
|
+
ClipboardPasteIcon,
|
|
15
|
+
CopyIcon,
|
|
16
|
+
TextSelectionIcon,
|
|
17
|
+
Trash2Icon,
|
|
18
|
+
} from "lucide-react";
|
|
19
|
+
import useEvent from "react-use-event-hook";
|
|
9
20
|
import { waitForConnectionOpen } from "@/core/network/connection";
|
|
10
21
|
import { useRuntimeManager } from "@/core/runtime/config";
|
|
22
|
+
import { useDebouncedCallback } from "@/hooks/useDebounce";
|
|
23
|
+
import { cn } from "@/utils/cn";
|
|
24
|
+
import { copyToClipboard } from "@/utils/copy";
|
|
11
25
|
import { Logger } from "@/utils/Logger";
|
|
26
|
+
import { MinimalHotkeys } from "../shortcuts/renderShortcut";
|
|
27
|
+
import { useTerminalActions, useTerminalState } from "./state";
|
|
28
|
+
import { createTerminalTheme } from "./theme";
|
|
12
29
|
|
|
13
|
-
|
|
30
|
+
interface TerminalButtonProps {
|
|
31
|
+
onClick: () => void;
|
|
32
|
+
disabled?: boolean;
|
|
33
|
+
icon: React.ComponentType<{ className?: string }>;
|
|
34
|
+
children: React.ReactNode;
|
|
35
|
+
keyboardShortcut?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const TerminalButton: React.FC<TerminalButtonProps> = ({
|
|
39
|
+
onClick,
|
|
40
|
+
disabled = false,
|
|
41
|
+
icon: Icon,
|
|
42
|
+
children,
|
|
43
|
+
keyboardShortcut,
|
|
44
|
+
}) => (
|
|
45
|
+
<button
|
|
46
|
+
className={cn(
|
|
47
|
+
"w-full text-left px-3 py-2 text-sm flex items-center gap-3 disabled:opacity-50 disabled:cursor-not-allowed hover:bg-muted",
|
|
48
|
+
)}
|
|
49
|
+
type="button"
|
|
50
|
+
onClick={onClick}
|
|
51
|
+
disabled={disabled}
|
|
52
|
+
>
|
|
53
|
+
<Icon className="w-4 h-4" />
|
|
54
|
+
{children}
|
|
55
|
+
{keyboardShortcut && (
|
|
56
|
+
<MinimalHotkeys className="ml-auto" shortcut={keyboardShortcut} />
|
|
57
|
+
)}
|
|
58
|
+
</button>
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
interface TerminalComponentProps {
|
|
14
62
|
visible: boolean;
|
|
15
63
|
onClose: () => void;
|
|
16
|
-
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
interface Position {
|
|
67
|
+
x: number;
|
|
68
|
+
y: number;
|
|
69
|
+
placement: "bottom" | "top"; // Whether to place the menu above or below the cursor
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Keyboard shortcut handlers
|
|
73
|
+
function createKeyboardHandler(terminal: Terminal, _searchAddon: SearchAddon) {
|
|
74
|
+
return (event: KeyboardEvent) => {
|
|
75
|
+
const { ctrlKey, metaKey, key } = event;
|
|
76
|
+
const modifier = ctrlKey || metaKey;
|
|
77
|
+
|
|
78
|
+
if (modifier) {
|
|
79
|
+
switch (key) {
|
|
80
|
+
case "c":
|
|
81
|
+
if (terminal.hasSelection()) {
|
|
82
|
+
event.preventDefault();
|
|
83
|
+
void copyToClipboard(terminal.getSelection());
|
|
84
|
+
}
|
|
85
|
+
break;
|
|
86
|
+
case "v":
|
|
87
|
+
event.preventDefault();
|
|
88
|
+
void navigator.clipboard.readText().then((text) => {
|
|
89
|
+
terminal.paste(text);
|
|
90
|
+
});
|
|
91
|
+
break;
|
|
92
|
+
case "a":
|
|
93
|
+
event.preventDefault();
|
|
94
|
+
terminal.selectAll();
|
|
95
|
+
break;
|
|
96
|
+
case "l":
|
|
97
|
+
event.preventDefault();
|
|
98
|
+
terminal.clear();
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Context menu actions
|
|
106
|
+
function createContextMenuActions(
|
|
107
|
+
terminal: Terminal,
|
|
108
|
+
setContextMenu: (menu: Position | null) => void,
|
|
109
|
+
) {
|
|
110
|
+
const closeMenu = () => setContextMenu(null);
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
handleCopy: () => {
|
|
114
|
+
if (terminal.hasSelection()) {
|
|
115
|
+
navigator.clipboard.writeText(terminal.getSelection());
|
|
116
|
+
}
|
|
117
|
+
closeMenu();
|
|
118
|
+
},
|
|
119
|
+
handlePaste: () => {
|
|
120
|
+
navigator.clipboard.readText().then((text) => {
|
|
121
|
+
terminal.paste(text);
|
|
122
|
+
});
|
|
123
|
+
closeMenu();
|
|
124
|
+
},
|
|
125
|
+
handleSelectAll: () => {
|
|
126
|
+
terminal.selectAll();
|
|
127
|
+
closeMenu();
|
|
128
|
+
},
|
|
129
|
+
handleClear: () => {
|
|
130
|
+
terminal.clear();
|
|
131
|
+
closeMenu();
|
|
132
|
+
},
|
|
133
|
+
closeMenu,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const RESIZE_DEBOUNCE_TIME = 100;
|
|
138
|
+
|
|
139
|
+
const TerminalComponent: React.FC<TerminalComponentProps> = ({
|
|
140
|
+
visible,
|
|
141
|
+
onClose,
|
|
142
|
+
}) => {
|
|
17
143
|
const terminalRef = useRef<HTMLDivElement>(null);
|
|
144
|
+
const wsRef = useRef<WebSocket | null>(null);
|
|
145
|
+
|
|
18
146
|
// eslint-disable-next-line react/hook-use-state
|
|
19
|
-
const [{ terminal, fitAddon }] = useState(() => {
|
|
147
|
+
const [{ terminal, fitAddon, searchAddon }] = useState(() => {
|
|
20
148
|
// Create a new terminal instance
|
|
21
149
|
const term = new Terminal({
|
|
22
150
|
fontFamily:
|
|
23
151
|
"Menlo, DejaVu Sans Mono, Consolas, Lucida Console, monospace",
|
|
24
152
|
fontSize: 14,
|
|
153
|
+
scrollback: 10_000,
|
|
154
|
+
cursorBlink: true,
|
|
155
|
+
cursorStyle: "block",
|
|
156
|
+
allowTransparency: false,
|
|
157
|
+
theme: createTerminalTheme("dark"),
|
|
158
|
+
rightClickSelectsWord: true,
|
|
159
|
+
wordSeparator: " \t\r\n\"'`(){}[]<>|&;",
|
|
160
|
+
allowProposedApi: true,
|
|
25
161
|
});
|
|
162
|
+
|
|
163
|
+
// Load essential addons
|
|
26
164
|
const fitAddon = new FitAddon();
|
|
165
|
+
const searchAddon = new SearchAddon();
|
|
166
|
+
const canvasAddon = new CanvasAddon();
|
|
167
|
+
const unicode11Addon = new Unicode11Addon();
|
|
168
|
+
const webLinksAddon = new WebLinksAddon();
|
|
169
|
+
|
|
27
170
|
term.loadAddon(fitAddon);
|
|
28
|
-
|
|
171
|
+
term.loadAddon(searchAddon);
|
|
172
|
+
term.loadAddon(canvasAddon);
|
|
173
|
+
term.loadAddon(unicode11Addon);
|
|
174
|
+
term.loadAddon(webLinksAddon);
|
|
175
|
+
|
|
176
|
+
// Set Unicode version
|
|
177
|
+
term.unicode.activeVersion = "11";
|
|
178
|
+
|
|
179
|
+
return { terminal: term, fitAddon, searchAddon };
|
|
29
180
|
});
|
|
181
|
+
|
|
30
182
|
const [initialized, setInitialized] = React.useState(false);
|
|
183
|
+
const [contextMenu, setContextMenu] = useState<Position | null>(null);
|
|
31
184
|
const runtimeManager = useRuntimeManager();
|
|
32
185
|
|
|
186
|
+
// Terminal command state management
|
|
187
|
+
const terminalState = useTerminalState();
|
|
188
|
+
const { removeCommand, setReady } = useTerminalActions();
|
|
189
|
+
|
|
190
|
+
// Keyboard shortcuts handler
|
|
191
|
+
const handleKeyDown = useEvent(createKeyboardHandler(terminal, searchAddon));
|
|
192
|
+
|
|
193
|
+
// Context menu handler
|
|
194
|
+
const handleContextMenu = useEvent((event: MouseEvent) => {
|
|
195
|
+
event.preventDefault();
|
|
196
|
+
|
|
197
|
+
const menuHeight = 200; // Approximate height of the context menu
|
|
198
|
+
const viewportHeight = window.innerHeight;
|
|
199
|
+
const cursorY = event.clientY;
|
|
200
|
+
|
|
201
|
+
// Check if there's enough space below the cursor
|
|
202
|
+
const spaceBelow = viewportHeight - cursorY;
|
|
203
|
+
const shouldPlaceAbove = spaceBelow < menuHeight;
|
|
204
|
+
|
|
205
|
+
setContextMenu({
|
|
206
|
+
x: event.clientX,
|
|
207
|
+
y: event.clientY,
|
|
208
|
+
placement: shouldPlaceAbove ? "top" : "bottom",
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// Close context menu on click outside
|
|
213
|
+
const handleClickOutside = useEvent((event: MouseEvent) => {
|
|
214
|
+
const target = event.target;
|
|
215
|
+
const isInsideContextMenu =
|
|
216
|
+
target &&
|
|
217
|
+
target instanceof HTMLElement &&
|
|
218
|
+
target.closest(".xterm-context-menu");
|
|
219
|
+
if (contextMenu && !isInsideContextMenu) {
|
|
220
|
+
setContextMenu(null);
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
const handleBackendResizeDebounced = useDebouncedCallback(
|
|
225
|
+
({ cols, rows }: { cols: number; rows: number }) => {
|
|
226
|
+
if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) {
|
|
227
|
+
Logger.debug("Sending resize to backend terminal", { cols, rows });
|
|
228
|
+
wsRef.current.send(JSON.stringify({ type: "resize", cols, rows }));
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
RESIZE_DEBOUNCE_TIME,
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
const handleResize = useEvent(() => {
|
|
235
|
+
if (!terminal || !fitAddon) {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
fitAddon.fit();
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// Context menu actions
|
|
242
|
+
const { handleCopy, handlePaste, handleSelectAll, handleClear } = useMemo(
|
|
243
|
+
() => createContextMenuActions(terminal, setContextMenu),
|
|
244
|
+
[terminal],
|
|
245
|
+
);
|
|
246
|
+
|
|
33
247
|
// Websocket Connection
|
|
34
248
|
useEffect(() => {
|
|
35
249
|
if (initialized) {
|
|
@@ -43,16 +257,37 @@ const TerminalComponent: React.FC<{
|
|
|
43
257
|
const socket = new WebSocket(runtimeManager.getTerminalWsURL());
|
|
44
258
|
const attachAddon = new AttachAddon(socket);
|
|
45
259
|
terminal.loadAddon(attachAddon);
|
|
260
|
+
wsRef.current = socket;
|
|
261
|
+
|
|
262
|
+
// Terminal is ready when the websocket is open
|
|
263
|
+
const updateReadyState = () => {
|
|
264
|
+
setReady(socket.readyState === WebSocket.OPEN);
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
const handleError = () => {
|
|
268
|
+
updateReadyState();
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
const handleOpen = () => {
|
|
272
|
+
updateReadyState();
|
|
273
|
+
};
|
|
46
274
|
|
|
47
275
|
const handleDisconnect = () => {
|
|
48
276
|
onClose();
|
|
49
277
|
// Reset
|
|
50
278
|
attachAddon.dispose();
|
|
279
|
+
wsRef.current = null;
|
|
51
280
|
terminal.clear();
|
|
52
281
|
setInitialized(false);
|
|
282
|
+
setReady(false);
|
|
53
283
|
};
|
|
54
284
|
|
|
285
|
+
socket.addEventListener("open", handleOpen);
|
|
55
286
|
socket.addEventListener("close", handleDisconnect);
|
|
287
|
+
socket.addEventListener("error", handleError);
|
|
288
|
+
|
|
289
|
+
// Set initial ready state
|
|
290
|
+
updateReadyState();
|
|
56
291
|
setInitialized(true);
|
|
57
292
|
} catch (error) {
|
|
58
293
|
Logger.error("Runtime health check failed for terminal", error);
|
|
@@ -68,6 +303,30 @@ const TerminalComponent: React.FC<{
|
|
|
68
303
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
69
304
|
}, [initialized]);
|
|
70
305
|
|
|
306
|
+
// Process pending commands when terminal is ready
|
|
307
|
+
useEffect(() => {
|
|
308
|
+
if (!terminalState.isReady || terminalState.pendingCommands.length === 0) {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Process all pending commands
|
|
313
|
+
for (const command of terminalState.pendingCommands) {
|
|
314
|
+
if (terminal && wsRef.current?.readyState === WebSocket.OPEN) {
|
|
315
|
+
Logger.debug("Sending programmatic command to terminal", {
|
|
316
|
+
command: command.text,
|
|
317
|
+
});
|
|
318
|
+
terminal.input(command.text);
|
|
319
|
+
terminal.focus();
|
|
320
|
+
removeCommand(command.id);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}, [
|
|
324
|
+
terminal,
|
|
325
|
+
terminalState.isReady,
|
|
326
|
+
terminalState.pendingCommands,
|
|
327
|
+
removeCommand,
|
|
328
|
+
]);
|
|
329
|
+
|
|
71
330
|
// When visible
|
|
72
331
|
useEffect(() => {
|
|
73
332
|
if (visible) {
|
|
@@ -86,23 +345,85 @@ const TerminalComponent: React.FC<{
|
|
|
86
345
|
}
|
|
87
346
|
|
|
88
347
|
terminal.open(terminalRef.current);
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const handleResize = () => {
|
|
348
|
+
|
|
349
|
+
// Initial fit with delay to ensure DOM is ready
|
|
350
|
+
setTimeout(() => {
|
|
93
351
|
fitAddon.fit();
|
|
94
|
-
};
|
|
95
|
-
|
|
352
|
+
}, RESIZE_DEBOUNCE_TIME);
|
|
353
|
+
|
|
354
|
+
terminal.focus();
|
|
355
|
+
|
|
356
|
+
const abortController = new AbortController();
|
|
357
|
+
|
|
358
|
+
// Add event listeners
|
|
359
|
+
window.addEventListener("resize", handleResize, {
|
|
360
|
+
signal: abortController.signal,
|
|
361
|
+
});
|
|
362
|
+
terminalRef.current.addEventListener("keydown", handleKeyDown, {
|
|
363
|
+
signal: abortController.signal,
|
|
364
|
+
});
|
|
365
|
+
terminalRef.current.addEventListener("contextmenu", handleContextMenu, {
|
|
366
|
+
signal: abortController.signal,
|
|
367
|
+
});
|
|
368
|
+
terminal.onResize(handleBackendResizeDebounced);
|
|
369
|
+
document.addEventListener("click", handleClickOutside, {
|
|
370
|
+
signal: abortController.signal,
|
|
371
|
+
});
|
|
96
372
|
|
|
97
373
|
return () => {
|
|
98
|
-
|
|
374
|
+
abortController.abort();
|
|
99
375
|
};
|
|
100
376
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
101
377
|
}, []);
|
|
102
378
|
|
|
103
379
|
return (
|
|
104
|
-
<div className="relative w-full h-[calc(100%-4px)]
|
|
380
|
+
<div className={"relative w-full h-[calc(100%-4px)] bg-popover"}>
|
|
105
381
|
<div className="w-full h-full" ref={terminalRef} />
|
|
382
|
+
{contextMenu && (
|
|
383
|
+
<div
|
|
384
|
+
className={
|
|
385
|
+
"xterm-context-menu fixed z-50 rounded-md shadow-lg py-1 min-w-[160px] border bg-popover"
|
|
386
|
+
}
|
|
387
|
+
style={{
|
|
388
|
+
left: contextMenu.x,
|
|
389
|
+
[contextMenu.placement === "top" ? "bottom" : "top"]:
|
|
390
|
+
contextMenu.placement === "top"
|
|
391
|
+
? window.innerHeight - contextMenu.y
|
|
392
|
+
: contextMenu.y,
|
|
393
|
+
}}
|
|
394
|
+
>
|
|
395
|
+
<TerminalButton
|
|
396
|
+
onClick={handleCopy}
|
|
397
|
+
disabled={!terminal.hasSelection()}
|
|
398
|
+
icon={CopyIcon}
|
|
399
|
+
keyboardShortcut="mod-c"
|
|
400
|
+
>
|
|
401
|
+
Copy
|
|
402
|
+
</TerminalButton>
|
|
403
|
+
<TerminalButton
|
|
404
|
+
onClick={handlePaste}
|
|
405
|
+
icon={ClipboardPasteIcon}
|
|
406
|
+
keyboardShortcut="mod-v"
|
|
407
|
+
>
|
|
408
|
+
Paste
|
|
409
|
+
</TerminalButton>
|
|
410
|
+
<hr className={cn("my-1 border-border")} />
|
|
411
|
+
<TerminalButton
|
|
412
|
+
onClick={handleSelectAll}
|
|
413
|
+
icon={TextSelectionIcon}
|
|
414
|
+
keyboardShortcut="mod-a"
|
|
415
|
+
>
|
|
416
|
+
Select all
|
|
417
|
+
</TerminalButton>
|
|
418
|
+
<TerminalButton
|
|
419
|
+
onClick={handleClear}
|
|
420
|
+
icon={Trash2Icon}
|
|
421
|
+
keyboardShortcut="mod-l"
|
|
422
|
+
>
|
|
423
|
+
Clear terminal
|
|
424
|
+
</TerminalButton>
|
|
425
|
+
</div>
|
|
426
|
+
)}
|
|
106
427
|
</div>
|
|
107
428
|
);
|
|
108
429
|
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/* Copyright 2024 Marimo. All rights reserved. */
|
|
2
|
+
import type { ResolvedTheme } from "@/theme/useTheme";
|
|
3
|
+
|
|
4
|
+
// Terminal theme configuration
|
|
5
|
+
export function createTerminalTheme(theme: ResolvedTheme) {
|
|
6
|
+
const baseTheme = {
|
|
7
|
+
cursor: "#ffffff",
|
|
8
|
+
cursorAccent: "#000000",
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
return theme === "dark"
|
|
12
|
+
? {
|
|
13
|
+
...baseTheme,
|
|
14
|
+
background: "#0f172a", // slate-900
|
|
15
|
+
foreground: "#f8fafc", // slate-50
|
|
16
|
+
black: "#0f172a",
|
|
17
|
+
red: "#ef4444",
|
|
18
|
+
green: "#22c55e",
|
|
19
|
+
yellow: "#eab308",
|
|
20
|
+
blue: "#3b82f6",
|
|
21
|
+
magenta: "#a855f7",
|
|
22
|
+
cyan: "#06b6d4",
|
|
23
|
+
white: "#f1f5f9",
|
|
24
|
+
brightBlack: "#475569",
|
|
25
|
+
brightRed: "#f87171",
|
|
26
|
+
brightGreen: "#4ade80",
|
|
27
|
+
brightYellow: "#facc15",
|
|
28
|
+
brightBlue: "#60a5fa",
|
|
29
|
+
brightMagenta: "#c084fc",
|
|
30
|
+
brightCyan: "#22d3ee",
|
|
31
|
+
brightWhite: "#ffffff",
|
|
32
|
+
selection: "rgba(148, 163, 184, 0.3)", // slate-400 with opacity
|
|
33
|
+
}
|
|
34
|
+
: {
|
|
35
|
+
...baseTheme,
|
|
36
|
+
background: "#ffffff", // white
|
|
37
|
+
foreground: "#0f172a", // slate-900
|
|
38
|
+
cursor: "#0f172a",
|
|
39
|
+
black: "#0f172a",
|
|
40
|
+
red: "#dc2626",
|
|
41
|
+
green: "#16a34a",
|
|
42
|
+
yellow: "#ca8a04",
|
|
43
|
+
blue: "#2563eb",
|
|
44
|
+
magenta: "#9333ea",
|
|
45
|
+
cyan: "#0891b2",
|
|
46
|
+
white: "#e2e8f0",
|
|
47
|
+
brightBlack: "#64748b",
|
|
48
|
+
brightRed: "#ef4444",
|
|
49
|
+
brightGreen: "#22c55e",
|
|
50
|
+
brightYellow: "#eab308",
|
|
51
|
+
brightBlue: "#3b82f6",
|
|
52
|
+
brightMagenta: "#a855f7",
|
|
53
|
+
brightCyan: "#06b6d4",
|
|
54
|
+
brightWhite: "#ffffff",
|
|
55
|
+
selection: "rgba(71, 85, 105, 0.2)", // slate-600 with opacity
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* Copyright 2024 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
3
|
import type { TopLevelSpec } from "vega-lite";
|
|
4
|
+
import type { TimeUnit } from "vega-lite/build/src/timeunit";
|
|
4
5
|
import type { CellId } from "@/core/cells/ids";
|
|
5
6
|
import type { CellRun } from "@/core/cells/runs";
|
|
6
7
|
import type { ResolvedTheme } from "@/theme/useTheme";
|
|
@@ -30,6 +31,8 @@ export function createGanttBaseSpec(
|
|
|
30
31
|
chartPosition: ChartPosition,
|
|
31
32
|
theme: ResolvedTheme,
|
|
32
33
|
): Readonly<TopLevelSpec> {
|
|
34
|
+
// @ts-expect-error - Supported by vega/vega-lite but invalid "TimeUnit" option from exported type
|
|
35
|
+
const timeUnit: TimeUnit = "hoursminutessecondsmilliseconds";
|
|
33
36
|
return {
|
|
34
37
|
$schema: "https://vega.github.io/schema/vega-lite/v5.json",
|
|
35
38
|
background: theme === "dark" ? "black" : undefined,
|
|
@@ -76,15 +79,13 @@ export function createGanttBaseSpec(
|
|
|
76
79
|
{
|
|
77
80
|
field: startTimestampField,
|
|
78
81
|
type: "temporal",
|
|
79
|
-
|
|
80
|
-
timeUnit: "hoursminutessecondsmilliseconds",
|
|
82
|
+
timeUnit: timeUnit,
|
|
81
83
|
title: "Start",
|
|
82
84
|
},
|
|
83
85
|
{
|
|
84
86
|
field: endTimestampField,
|
|
85
87
|
type: "temporal",
|
|
86
|
-
|
|
87
|
-
timeUnit: "hoursminutessecondsmilliseconds",
|
|
88
|
+
timeUnit: timeUnit,
|
|
88
89
|
title: "End",
|
|
89
90
|
},
|
|
90
91
|
],
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import * as SliderPrimitive from "@radix-ui/react-slider";
|
|
4
4
|
import { TooltipPortal } from "@radix-ui/react-tooltip";
|
|
5
5
|
import * as React from "react";
|
|
6
|
+
import { useLocale } from "react-aria";
|
|
6
7
|
import { cn } from "@/utils/cn";
|
|
7
8
|
import { prettyScientificNumber } from "@/utils/numbers";
|
|
8
9
|
import { useBoolean } from "../../hooks/useBoolean";
|
|
@@ -20,6 +21,7 @@ const RangeSlider = React.forwardRef<
|
|
|
20
21
|
}
|
|
21
22
|
>(({ className, valueMap, ...props }, ref) => {
|
|
22
23
|
const [open, openActions] = useBoolean(false);
|
|
24
|
+
const { locale } = useLocale();
|
|
23
25
|
|
|
24
26
|
return (
|
|
25
27
|
<SliderPrimitive.Root
|
|
@@ -66,7 +68,7 @@ const RangeSlider = React.forwardRef<
|
|
|
66
68
|
<TooltipPortal>
|
|
67
69
|
{props.value != null && props.value.length === 2 && (
|
|
68
70
|
<TooltipContent key={props.value[0]}>
|
|
69
|
-
{prettyScientificNumber(valueMap(props.value[0]))}
|
|
71
|
+
{prettyScientificNumber(valueMap(props.value[0]), { locale })}
|
|
70
72
|
</TooltipContent>
|
|
71
73
|
)}
|
|
72
74
|
</TooltipPortal>
|
|
@@ -87,7 +89,7 @@ const RangeSlider = React.forwardRef<
|
|
|
87
89
|
<TooltipPortal>
|
|
88
90
|
{props.value != null && props.value.length === 2 && (
|
|
89
91
|
<TooltipContent key={props.value[1]}>
|
|
90
|
-
{prettyScientificNumber(valueMap(props.value[1]))}
|
|
92
|
+
{prettyScientificNumber(valueMap(props.value[1]), { locale })}
|
|
91
93
|
</TooltipContent>
|
|
92
94
|
)}
|
|
93
95
|
</TooltipPortal>
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import * as SliderPrimitive from "@radix-ui/react-slider";
|
|
4
4
|
import { TooltipPortal } from "@radix-ui/react-tooltip";
|
|
5
5
|
import * as React from "react";
|
|
6
|
+
import { useLocale } from "react-aria";
|
|
6
7
|
import { cn } from "@/utils/cn";
|
|
7
8
|
import { prettyScientificNumber } from "@/utils/numbers";
|
|
8
9
|
import { useBoolean } from "../../hooks/useBoolean";
|
|
@@ -20,6 +21,7 @@ const Slider = React.forwardRef<
|
|
|
20
21
|
}
|
|
21
22
|
>(({ className, valueMap, ...props }, ref) => {
|
|
22
23
|
const [open, openActions] = useBoolean(false);
|
|
24
|
+
const { locale } = useLocale();
|
|
23
25
|
|
|
24
26
|
return (
|
|
25
27
|
<SliderPrimitive.Root
|
|
@@ -66,7 +68,7 @@ const Slider = React.forwardRef<
|
|
|
66
68
|
<TooltipPortal>
|
|
67
69
|
{props.value != null && props.value.length === 1 && (
|
|
68
70
|
<TooltipContent key={props.value[0]}>
|
|
69
|
-
{prettyScientificNumber(valueMap(props.value[0]))}
|
|
71
|
+
{prettyScientificNumber(valueMap(props.value[0]), { locale })}
|
|
70
72
|
</TooltipContent>
|
|
71
73
|
)}
|
|
72
74
|
</TooltipPortal>
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
import { sortBy } from "lodash-es";
|
|
15
15
|
import { SquareEqualIcon, WorkflowIcon } from "lucide-react";
|
|
16
16
|
import React, { memo, useMemo } from "react";
|
|
17
|
+
import { useLocale } from "react-aria";
|
|
17
18
|
import { CellLink } from "@/components/editor/links/cell-link";
|
|
18
19
|
import { getCellEditorView, useCellNames } from "@/core/cells/cells";
|
|
19
20
|
import type { CellId } from "@/core/cells/ids";
|
|
@@ -233,6 +234,7 @@ export const VariableTable: React.FC<Props> = memo(
|
|
|
233
234
|
const [sorting, setSorting] = React.useState<SortingState>([]);
|
|
234
235
|
const [globalFilter, setGlobalFilter] = React.useState("");
|
|
235
236
|
const cellNames = useCellNames();
|
|
237
|
+
const { locale } = useLocale();
|
|
236
238
|
|
|
237
239
|
const resolvedVariables: ResolvedVariable[] = useMemo(() => {
|
|
238
240
|
const getName = (id: CellId) => {
|
|
@@ -279,6 +281,7 @@ export const VariableTable: React.FC<Props> = memo(
|
|
|
279
281
|
globalFilterFn: "auto",
|
|
280
282
|
// sorting
|
|
281
283
|
manualSorting: true,
|
|
284
|
+
locale: locale,
|
|
282
285
|
onSortingChange: setSorting,
|
|
283
286
|
getSortedRowModel: getSortedRowModel(),
|
|
284
287
|
state: {
|
package/src/core/MarimoApp.tsx
CHANGED
|
@@ -15,6 +15,7 @@ import { ErrorBoundary } from "../components/editor/boundary/ErrorBoundary";
|
|
|
15
15
|
import { ModalProvider } from "../components/modal/ImperativeModal";
|
|
16
16
|
import { Toaster } from "../components/ui/toaster";
|
|
17
17
|
import { TooltipProvider } from "../components/ui/tooltip";
|
|
18
|
+
import { LocaleProvider } from "./i18n/locale-provider";
|
|
18
19
|
import { slotsController } from "./slots/slots";
|
|
19
20
|
|
|
20
21
|
// Force tailwind classnames
|
|
@@ -69,7 +70,7 @@ export const MarimoApp: React.FC = memo(() => {
|
|
|
69
70
|
<CssVariables
|
|
70
71
|
variables={{ "--marimo-code-editor-font-size": editorFontSize }}
|
|
71
72
|
>
|
|
72
|
-
{renderBody()}
|
|
73
|
+
<LocaleProvider>{renderBody()}</LocaleProvider>
|
|
73
74
|
</CssVariables>
|
|
74
75
|
</Providers>
|
|
75
76
|
);
|
|
@@ -85,11 +86,13 @@ const Providers = memo(({ children }: PropsWithChildren) => {
|
|
|
85
86
|
<Suspense>
|
|
86
87
|
<TooltipProvider>
|
|
87
88
|
<SlotzProvider controller={slotsController}>
|
|
88
|
-
<
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
89
|
+
<LocaleProvider>
|
|
90
|
+
<ModalProvider>
|
|
91
|
+
{children}
|
|
92
|
+
<Toaster />
|
|
93
|
+
<TailwindIndicator />
|
|
94
|
+
</ModalProvider>
|
|
95
|
+
</LocaleProvider>
|
|
93
96
|
</SlotzProvider>
|
|
94
97
|
</TooltipProvider>
|
|
95
98
|
</Suspense>
|