@marimo-team/islands 0.23.9-dev4 → 0.23.9-dev41
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-OzrfMM5L.js → ConnectedDataExplorerComponent-CyV83R2m.js} +4 -4
- package/dist/assets/__vite-browser-external-Ci2ZQfXU.js +1 -0
- package/dist/assets/{worker-CpBbwbQo.js → worker-ip3AI_sN.js} +2 -2
- package/dist/{chat-ui-BDI3FMI8.js → chat-ui-ChD4VvCo.js} +3060 -3033
- package/dist/{code-visibility-VZebNmSs.js → code-visibility-s3OWNKLN.js} +1634 -1314
- package/dist/{formats-DQ5qjo_Q.js → formats-DHxc-FdY.js} +1 -1
- package/dist/{glide-data-editor-DqRY9naW.js → glide-data-editor-BOmK9ETQ.js} +2 -2
- package/dist/{html-to-image-CiSinpSR.js → html-to-image-BHv7CEU_.js} +2145 -2153
- package/dist/{input-CZD2z6X2.js → input-_2sjvfne.js} +1 -1
- package/dist/main.js +1522 -1556
- package/dist/{mermaid-IU93XzmY.js → mermaid-lXOw5Py9.js} +2 -2
- package/dist/{process-output-5qJjMRKh.js → process-output-BvySRgli.js} +33 -25
- package/dist/{reveal-component-DZtPMEoM.js → reveal-component-DCO6zm_5.js} +17 -17
- package/dist/{spec-a6DaqW__.js → spec-B96zNUEA.js} +1 -1
- package/dist/style.css +1 -1
- package/dist/{toDate-ZVVIBmdk.js → toDate-x-WRDCH7.js} +1 -1
- package/dist/{useAsyncData-C008zUPi.js → useAsyncData-iRgKDT5s.js} +1 -1
- package/dist/{useDeepCompareMemoize-BrA3_n61.js → useDeepCompareMemoize-CkQ57VS2.js} +1 -1
- package/dist/{useLifecycle-BNaoJ5a4.js → useLifecycle-BBO9PIph.js} +1 -1
- package/dist/{useTheme-7O0YWlE5.js → useTheme-DHIrRQOe.js} +34 -21
- package/dist/{vega-component-DJNmOdUj.js → vega-component-Dq-SH463.js} +5 -5
- package/package.json +1 -1
- package/src/components/ai/__tests__/ai-utils.test.ts +43 -38
- package/src/components/ai/ai-model-dropdown.tsx +2 -2
- package/src/components/app-config/ai-config.tsx +147 -16
- package/src/components/app-config/user-config-form.tsx +37 -1
- package/src/components/chat/__tests__/chat-utils.test.ts +269 -0
- package/src/components/chat/chat-panel.tsx +38 -5
- package/src/components/chat/chat-utils.ts +14 -58
- package/src/components/data-table/TableBottomBar.tsx +27 -6
- package/src/components/data-table/TableTopBar.tsx +7 -1
- package/src/components/data-table/__tests__/TableBottomBar.test.tsx +73 -0
- package/src/components/data-table/__tests__/column-explorer.test.tsx +128 -0
- package/src/components/data-table/__tests__/data-table.test.tsx +52 -1
- package/src/components/data-table/__tests__/header-items.test.tsx +257 -1
- package/src/components/data-table/__tests__/useColumnVisibility.test.ts +42 -0
- package/src/components/data-table/column-explorer-panel/column-explorer.tsx +98 -26
- package/src/components/data-table/column-header.tsx +19 -12
- package/src/components/data-table/columns.tsx +3 -4
- package/src/components/data-table/data-table.tsx +37 -0
- package/src/components/data-table/export-actions.tsx +36 -18
- package/src/components/data-table/header-items.tsx +58 -17
- package/src/components/data-table/hooks/use-column-visibility.ts +56 -0
- package/src/components/data-table/pagination.tsx +16 -3
- package/src/components/data-table/schemas.ts +2 -2
- package/src/components/data-table/table-explorer-panel/table-explorer-panel.tsx +16 -6
- package/src/components/databases/display.tsx +2 -0
- package/src/components/datasources/__tests__/utils.test.ts +82 -0
- package/src/components/datasources/utils.ts +16 -15
- package/src/components/editor/actions/pair-with-agent-modal.tsx +1 -0
- package/src/components/editor/actions/useCellActionButton.tsx +3 -3
- package/src/components/editor/actions/useNotebookActions.tsx +5 -2
- package/src/components/editor/cell/code/cell-editor.tsx +7 -4
- package/src/components/editor/chrome/types.ts +13 -6
- package/src/components/editor/chrome/wrapper/app-chrome.tsx +6 -4
- package/src/components/editor/chrome/wrapper/footer-items/ai-status.tsx +10 -1
- package/src/components/editor/chrome/wrapper/sidebar.tsx +7 -5
- package/src/components/editor/errors/auto-fix.tsx +3 -3
- package/src/components/editor/errors/mangled-local-chip.tsx +50 -0
- package/src/components/editor/navigation/__tests__/navigation.test.ts +15 -0
- package/src/components/editor/navigation/navigation.ts +5 -0
- package/src/components/editor/output/MarimoErrorOutput.tsx +110 -27
- package/src/components/editor/output/MarimoTracebackOutput.tsx +55 -37
- package/src/components/editor/renderers/cell-array.tsx +27 -24
- package/src/components/editor/renderers/slides-layout/slides-layout.tsx +1 -1
- package/src/components/slides/reveal-component.tsx +3 -3
- package/src/components/slides/slide-form.tsx +11 -3
- package/src/components/static-html/static-banner.tsx +28 -22
- package/src/core/ai/__tests__/model-registry.test.ts +72 -60
- package/src/core/ai/model-registry.ts +33 -28
- package/src/core/cells/__tests__/actions.test.ts +48 -0
- package/src/core/cells/actions.ts +5 -6
- package/src/core/codemirror/__tests__/setup.test.ts +29 -0
- package/src/core/codemirror/cells/traceback-decorations.ts +1 -1
- package/src/core/codemirror/cm.ts +3 -2
- package/src/core/codemirror/completion/hints.ts +4 -1
- package/src/core/codemirror/format.ts +1 -0
- package/src/core/codemirror/keymaps/vim.ts +63 -0
- package/src/core/codemirror/language/languages/sql/sql.ts +1 -0
- package/src/core/codemirror/language/languages/sql/utils.ts +2 -0
- package/src/core/config/__tests__/config-schema.test.ts +4 -0
- package/src/core/config/config-schema.ts +4 -0
- package/src/core/config/config.ts +16 -0
- package/src/css/app/Cell.css +0 -1
- package/src/plugins/impl/DataTablePlugin.tsx +94 -33
- package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +1 -0
- package/src/plugins/impl/chat/ChatPlugin.tsx +7 -1
- package/src/plugins/impl/chat/__tests__/chat-ui.test.ts +278 -0
- package/src/plugins/impl/chat/chat-ui.tsx +106 -59
- package/src/plugins/impl/chat/types.ts +5 -0
- package/src/plugins/impl/data-frames/DataFramePlugin.tsx +8 -6
- package/src/stories/dataframe.stories.tsx +1 -0
- package/src/utils/__tests__/json-parser.test.ts +1 -69
- package/src/utils/__tests__/local-variables.test.ts +132 -0
- package/src/utils/json/json-parser.ts +0 -30
- package/src/utils/local-variables.ts +67 -0
- package/dist/assets/__vite-browser-external-CAdMKBac.js +0 -1
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
ArrowUpNarrowWideIcon,
|
|
9
9
|
ChevronsUpDown,
|
|
10
10
|
CopyIcon,
|
|
11
|
+
EyeOffIcon,
|
|
11
12
|
FilterX,
|
|
12
13
|
PinOffIcon,
|
|
13
14
|
WrapTextIcon,
|
|
@@ -28,10 +29,13 @@ import { formattingExample } from "./column-formatting/feature";
|
|
|
28
29
|
import { formatOptions } from "./column-formatting/types";
|
|
29
30
|
import { NAMELESS_COLUMN_PREFIX } from "./columns";
|
|
30
31
|
|
|
31
|
-
export function
|
|
32
|
-
column
|
|
33
|
-
locale
|
|
34
|
-
|
|
32
|
+
export function FormatOptions<TData, TValue>({
|
|
33
|
+
column,
|
|
34
|
+
locale,
|
|
35
|
+
}: {
|
|
36
|
+
column: Column<TData, TValue>;
|
|
37
|
+
locale: string;
|
|
38
|
+
}) {
|
|
35
39
|
const dataType: DataType | undefined = column.columnDef.meta?.dataType;
|
|
36
40
|
const columnFormatOptions = dataType ? formatOptions[dataType] : [];
|
|
37
41
|
|
|
@@ -82,9 +86,11 @@ export function renderFormatOptions<TData, TValue>(
|
|
|
82
86
|
);
|
|
83
87
|
}
|
|
84
88
|
|
|
85
|
-
export function
|
|
86
|
-
column
|
|
87
|
-
|
|
89
|
+
export function ColumnWrapping<TData, TValue>({
|
|
90
|
+
column,
|
|
91
|
+
}: {
|
|
92
|
+
column: Column<TData, TValue>;
|
|
93
|
+
}) {
|
|
88
94
|
if (!column.getCanWrap?.() || !column.getColumnWrapping) {
|
|
89
95
|
return null;
|
|
90
96
|
}
|
|
@@ -107,9 +113,11 @@ export function renderColumnWrapping<TData, TValue>(
|
|
|
107
113
|
);
|
|
108
114
|
}
|
|
109
115
|
|
|
110
|
-
export function
|
|
111
|
-
column
|
|
112
|
-
|
|
116
|
+
export function ColumnPinning<TData, TValue>({
|
|
117
|
+
column,
|
|
118
|
+
}: {
|
|
119
|
+
column: Column<TData, TValue>;
|
|
120
|
+
}) {
|
|
113
121
|
if (!column.getCanPin?.() || !column.getIsPinned) {
|
|
114
122
|
return null;
|
|
115
123
|
}
|
|
@@ -139,7 +147,28 @@ export function renderColumnPinning<TData, TValue>(
|
|
|
139
147
|
);
|
|
140
148
|
}
|
|
141
149
|
|
|
142
|
-
export function
|
|
150
|
+
export function HideColumn<TData, TValue>({
|
|
151
|
+
column,
|
|
152
|
+
}: {
|
|
153
|
+
column: Column<TData, TValue>;
|
|
154
|
+
}) {
|
|
155
|
+
if (!column.getCanHide()) {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return (
|
|
160
|
+
<DropdownMenuItem onClick={() => column.toggleVisibility(false)}>
|
|
161
|
+
<EyeOffIcon className="mo-dropdown-icon" />
|
|
162
|
+
Hide column
|
|
163
|
+
</DropdownMenuItem>
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function CopyColumn<TData, TValue>({
|
|
168
|
+
column,
|
|
169
|
+
}: {
|
|
170
|
+
column: Column<TData, TValue>;
|
|
171
|
+
}) {
|
|
143
172
|
if (!column.getCanCopy?.()) {
|
|
144
173
|
return null;
|
|
145
174
|
}
|
|
@@ -159,10 +188,19 @@ export function renderCopyColumn<TData, TValue>(column: Column<TData, TValue>) {
|
|
|
159
188
|
const AscIcon = ArrowUpNarrowWideIcon;
|
|
160
189
|
const DescIcon = ArrowDownWideNarrowIcon;
|
|
161
190
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
)
|
|
191
|
+
/**
|
|
192
|
+
* `table` is optional: it is only needed to detect multi-column sorting and
|
|
193
|
+
* offer "Clear all sorts". Call sites that build their header inside column
|
|
194
|
+
* definitions (where the table instance isn't yet in scope) omit it and fall
|
|
195
|
+
* back to single-column "Clear sort".
|
|
196
|
+
*/
|
|
197
|
+
export function Sorts<TData, TValue>({
|
|
198
|
+
column,
|
|
199
|
+
table,
|
|
200
|
+
}: {
|
|
201
|
+
column: Column<TData, TValue>;
|
|
202
|
+
table?: Table<TData>;
|
|
203
|
+
}) {
|
|
166
204
|
if (!column.getCanSort()) {
|
|
167
205
|
return null;
|
|
168
206
|
}
|
|
@@ -233,7 +271,6 @@ export function renderSorts<TData, TValue>(
|
|
|
233
271
|
{sortDirection === "desc" && renderSortIndex()}
|
|
234
272
|
</DropdownMenuItem>
|
|
235
273
|
{renderClearSort()}
|
|
236
|
-
<DropdownMenuSeparator />
|
|
237
274
|
</>
|
|
238
275
|
);
|
|
239
276
|
}
|
|
@@ -254,7 +291,11 @@ export function renderSortIcon<TData, TValue>(column: Column<TData, TValue>) {
|
|
|
254
291
|
return <Icon className="h-3 w-3" />;
|
|
255
292
|
}
|
|
256
293
|
|
|
257
|
-
export function
|
|
294
|
+
export function DataType<TData, TValue>({
|
|
295
|
+
column,
|
|
296
|
+
}: {
|
|
297
|
+
column: Column<TData, TValue>;
|
|
298
|
+
}) {
|
|
258
299
|
const dtype: string | undefined = column.columnDef.meta?.dtype;
|
|
259
300
|
if (!dtype) {
|
|
260
301
|
return null;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
"use no memo";
|
|
3
|
+
|
|
4
|
+
import { useInternalStateWithSync } from "@/hooks/useInternalStateWithSync";
|
|
5
|
+
import type { Table, VisibilityState } from "@tanstack/react-table";
|
|
6
|
+
import { dequal as isDeepEqual } from "dequal";
|
|
7
|
+
import type React from "react";
|
|
8
|
+
|
|
9
|
+
interface UseColumnVisibilityResult {
|
|
10
|
+
columnVisibility: VisibilityState;
|
|
11
|
+
setColumnVisibility: React.Dispatch<React.SetStateAction<VisibilityState>>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function useColumnVisibility(
|
|
15
|
+
hiddenColumns?: string[],
|
|
16
|
+
): UseColumnVisibilityResult {
|
|
17
|
+
const [columnVisibility, setColumnVisibility] =
|
|
18
|
+
useInternalStateWithSync<VisibilityState>(
|
|
19
|
+
Object.fromEntries((hiddenColumns ?? []).map((c) => [c, false])),
|
|
20
|
+
isDeepEqual,
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
return { columnVisibility, setColumnVisibility };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface ColumnVisibilityCounts {
|
|
27
|
+
total: number;
|
|
28
|
+
visible: number;
|
|
29
|
+
hidden: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function getUserColumnVisibilityCounts<TData>(
|
|
33
|
+
table: Table<TData>,
|
|
34
|
+
): ColumnVisibilityCounts {
|
|
35
|
+
const userColumns = table.getAllLeafColumns().filter((c) => c.getCanHide());
|
|
36
|
+
const visible = userColumns.filter((c) => c.getIsVisible()).length;
|
|
37
|
+
return {
|
|
38
|
+
total: userColumns.length,
|
|
39
|
+
visible,
|
|
40
|
+
hidden: userColumns.length - visible,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// When columns are clipped server-side, the TanStack instance only holds the
|
|
45
|
+
// rendered subset, so visible/hidden math must use that subset's total. The
|
|
46
|
+
// dataset-wide value is still correct for the no-hidden "N columns" label.
|
|
47
|
+
export function getColumnCountForDisplay<TData>(
|
|
48
|
+
table: Table<TData>,
|
|
49
|
+
datasetTotalColumns: number,
|
|
50
|
+
): { totalColumns: number; hiddenColumns: number } {
|
|
51
|
+
const counts = getUserColumnVisibilityCounts(table);
|
|
52
|
+
return {
|
|
53
|
+
totalColumns: counts.hidden > 0 ? counts.total : datasetTotalColumns,
|
|
54
|
+
hiddenColumns: counts.hidden,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
@@ -443,15 +443,28 @@ export function prettifyRowCount(rowCount: number, locale: string): string {
|
|
|
443
443
|
export const prettifyRowColumnCount = ({
|
|
444
444
|
numRows,
|
|
445
445
|
totalColumns,
|
|
446
|
+
hiddenColumns,
|
|
446
447
|
locale,
|
|
447
448
|
}: {
|
|
448
449
|
numRows: number | "too_many";
|
|
449
450
|
totalColumns: number;
|
|
451
|
+
hiddenColumns?: number;
|
|
450
452
|
locale: string;
|
|
451
|
-
}): string => {
|
|
453
|
+
}): { rowsAndColumns: string; hiddenSuffix: string | null } => {
|
|
452
454
|
const rowsLabel =
|
|
453
455
|
numRows === "too_many" ? "Unknown" : prettifyRowCount(numRows, locale);
|
|
454
|
-
const columnsLabel = `${prettyNumber(totalColumns, locale)} ${new PluralWord("column").pluralize(totalColumns)}`;
|
|
455
456
|
|
|
456
|
-
|
|
457
|
+
const hidden = hiddenColumns ?? 0;
|
|
458
|
+
const visibleColumns = totalColumns - hidden;
|
|
459
|
+
|
|
460
|
+
const columnsLabel =
|
|
461
|
+
hidden > 0
|
|
462
|
+
? `${prettyNumber(visibleColumns, locale)} visible`
|
|
463
|
+
: `${prettyNumber(totalColumns, locale)} ${new PluralWord("column").pluralize(totalColumns)}`;
|
|
464
|
+
|
|
465
|
+
return {
|
|
466
|
+
rowsAndColumns: [rowsLabel, columnsLabel].join(", "),
|
|
467
|
+
hiddenSuffix:
|
|
468
|
+
hidden > 0 ? `(${prettyNumber(hidden, locale)} hidden)` : null,
|
|
469
|
+
};
|
|
457
470
|
};
|
|
@@ -4,7 +4,7 @@ import z from "zod";
|
|
|
4
4
|
import { rpc } from "@/plugins/core/rpc";
|
|
5
5
|
|
|
6
6
|
export type DownloadAsArgs = (req: {
|
|
7
|
-
format: "csv" | "json" | "parquet";
|
|
7
|
+
format: "csv" | "json" | "parquet" | "tsv";
|
|
8
8
|
}) => Promise<{
|
|
9
9
|
url: string;
|
|
10
10
|
filename: string;
|
|
@@ -15,7 +15,7 @@ export type DownloadAsArgs = (req: {
|
|
|
15
15
|
export const DownloadAsSchema = rpc
|
|
16
16
|
.input(
|
|
17
17
|
z.object({
|
|
18
|
-
format: z.enum(["csv", "json", "parquet"]),
|
|
18
|
+
format: z.enum(["csv", "json", "parquet", "tsv"]),
|
|
19
19
|
}),
|
|
20
20
|
)
|
|
21
21
|
.output(
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
"use no memo";
|
|
3
|
+
|
|
4
|
+
// tanstack/table is not compatible with React compiler
|
|
5
|
+
// https://github.com/TanStack/table/issues/5567
|
|
2
6
|
|
|
3
7
|
import { Fill } from "@marimo-team/react-slotz";
|
|
4
|
-
import type {
|
|
5
|
-
|
|
8
|
+
import type {
|
|
9
|
+
OnChangeFn,
|
|
10
|
+
RowSelectionState,
|
|
11
|
+
Table,
|
|
12
|
+
} from "@tanstack/react-table";
|
|
6
13
|
import { Button } from "@/components/ui/button";
|
|
7
14
|
import { Tabs, TabsContent } from "@/components/ui/tabs";
|
|
8
15
|
import { SlotNames } from "@/core/slots/slots";
|
|
@@ -19,7 +26,7 @@ import { ColumnExplorerPanel } from "../column-explorer-panel/column-explorer";
|
|
|
19
26
|
import { RowViewerPanel } from "../row-viewer-panel/row-viewer";
|
|
20
27
|
import type { FieldTypesWithExternalType, TooManyRows } from "../types";
|
|
21
28
|
|
|
22
|
-
export interface TableExplorerPanelProps {
|
|
29
|
+
export interface TableExplorerPanelProps<TData> {
|
|
23
30
|
// Row viewer props
|
|
24
31
|
rowIdx: number;
|
|
25
32
|
setRowIdx: (rowIdx: number) => void;
|
|
@@ -33,6 +40,7 @@ export interface TableExplorerPanelProps {
|
|
|
33
40
|
previewColumn?: PreviewColumn;
|
|
34
41
|
totalColumns: number;
|
|
35
42
|
tableId: string;
|
|
43
|
+
table: Table<TData>;
|
|
36
44
|
// Visibility flags
|
|
37
45
|
showRowExplorer: boolean;
|
|
38
46
|
showColumnExplorer: boolean;
|
|
@@ -46,7 +54,7 @@ const tabTriggerClassName =
|
|
|
46
54
|
const activeClassName = "text-primary";
|
|
47
55
|
const inactiveClassName = "hover:text-foreground";
|
|
48
56
|
|
|
49
|
-
export
|
|
57
|
+
export function TableExplorerPanel<TData>({
|
|
50
58
|
// Row viewer
|
|
51
59
|
rowIdx,
|
|
52
60
|
setRowIdx,
|
|
@@ -60,13 +68,14 @@ export const TableExplorerPanel: React.FC<TableExplorerPanelProps> = ({
|
|
|
60
68
|
previewColumn,
|
|
61
69
|
totalColumns,
|
|
62
70
|
tableId,
|
|
71
|
+
table,
|
|
63
72
|
// Visibility
|
|
64
73
|
showRowExplorer,
|
|
65
74
|
showColumnExplorer,
|
|
66
75
|
// Tab state
|
|
67
76
|
activeTab,
|
|
68
77
|
onTabChange,
|
|
69
|
-
})
|
|
78
|
+
}: TableExplorerPanelProps<TData>) {
|
|
70
79
|
const showTabs = showRowExplorer && showColumnExplorer;
|
|
71
80
|
|
|
72
81
|
const rowViewer = (
|
|
@@ -89,6 +98,7 @@ export const TableExplorerPanel: React.FC<TableExplorerPanelProps> = ({
|
|
|
89
98
|
totalRows={totalRows}
|
|
90
99
|
totalColumns={totalColumns}
|
|
91
100
|
tableId={tableId}
|
|
101
|
+
table={table}
|
|
92
102
|
/>
|
|
93
103
|
);
|
|
94
104
|
|
|
@@ -158,4 +168,4 @@ export const TableExplorerPanel: React.FC<TableExplorerPanelProps> = ({
|
|
|
158
168
|
</TabsContent>
|
|
159
169
|
</Tabs>
|
|
160
170
|
);
|
|
161
|
-
}
|
|
171
|
+
}
|
|
@@ -313,6 +313,26 @@ describe("sqlCode", () => {
|
|
|
313
313
|
);
|
|
314
314
|
});
|
|
315
315
|
|
|
316
|
+
it("should preserve dots inside quoted schema names", () => {
|
|
317
|
+
const sqlTableContext: SQLTableContext = {
|
|
318
|
+
engine: "postgres",
|
|
319
|
+
schema: "analytics.events",
|
|
320
|
+
defaultSchema: "public",
|
|
321
|
+
defaultDatabase: "mydb",
|
|
322
|
+
database: "remote",
|
|
323
|
+
dialect: "postgres",
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
const result = sqlCode({
|
|
327
|
+
table: mockTable,
|
|
328
|
+
columnName: mockColumn.name,
|
|
329
|
+
sqlTableContext,
|
|
330
|
+
});
|
|
331
|
+
expect(result).toBe(
|
|
332
|
+
'_df = mo.sql(f"""\nSELECT "email" FROM "remote"."analytics.events"."users" LIMIT 100\n""", engine=postgres)',
|
|
333
|
+
);
|
|
334
|
+
});
|
|
335
|
+
|
|
316
336
|
it("should not quote * column name", () => {
|
|
317
337
|
const sqlTableContext: SQLTableContext = {
|
|
318
338
|
engine: "postgres",
|
|
@@ -334,6 +354,68 @@ describe("sqlCode", () => {
|
|
|
334
354
|
});
|
|
335
355
|
});
|
|
336
356
|
|
|
357
|
+
describe("Dremio dialect", () => {
|
|
358
|
+
it("should quote reserved column names and table path parts", () => {
|
|
359
|
+
const sqlTableContext: SQLTableContext = {
|
|
360
|
+
engine: "dremio_conn",
|
|
361
|
+
schema: "operations",
|
|
362
|
+
defaultSchema: "",
|
|
363
|
+
defaultDatabase: "",
|
|
364
|
+
database: "lakehouse",
|
|
365
|
+
dialect: "dremio",
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
const result = sqlCode({
|
|
369
|
+
table: { ...mockTable, name: "shipments" as const },
|
|
370
|
+
columnName: "order",
|
|
371
|
+
sqlTableContext,
|
|
372
|
+
});
|
|
373
|
+
expect(result).toBe(
|
|
374
|
+
'_df = mo.sql(f"""\nSELECT "order" FROM "lakehouse"."operations"."shipments" LIMIT 100\n""", engine=dremio_conn)',
|
|
375
|
+
);
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
it("should not quote * column name", () => {
|
|
379
|
+
const sqlTableContext: SQLTableContext = {
|
|
380
|
+
engine: "dremio_conn",
|
|
381
|
+
schema: "operations",
|
|
382
|
+
defaultSchema: "",
|
|
383
|
+
defaultDatabase: "",
|
|
384
|
+
database: "lakehouse",
|
|
385
|
+
dialect: "dremio",
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
const result = sqlCode({
|
|
389
|
+
table: { ...mockTable, name: "customers" as const },
|
|
390
|
+
columnName: "*",
|
|
391
|
+
sqlTableContext,
|
|
392
|
+
});
|
|
393
|
+
expect(result).toBe(
|
|
394
|
+
'_df = mo.sql(f"""\nSELECT * FROM "lakehouse"."operations"."customers" LIMIT 100\n""", engine=dremio_conn)',
|
|
395
|
+
);
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
it("should preserve dots inside quoted schema names", () => {
|
|
399
|
+
const sqlTableContext: SQLTableContext = {
|
|
400
|
+
engine: "dremio_conn",
|
|
401
|
+
schema: "samples.dremio.com",
|
|
402
|
+
defaultSchema: "",
|
|
403
|
+
defaultDatabase: "",
|
|
404
|
+
database: "Samples",
|
|
405
|
+
dialect: "dremio",
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
const result = sqlCode({
|
|
409
|
+
table: { ...mockTable, name: "airlines" as const },
|
|
410
|
+
columnName: "*",
|
|
411
|
+
sqlTableContext,
|
|
412
|
+
});
|
|
413
|
+
expect(result).toBe(
|
|
414
|
+
'_df = mo.sql(f"""\nSELECT * FROM "Samples"."samples.dremio.com"."airlines" LIMIT 100\n""", engine=dremio_conn)',
|
|
415
|
+
);
|
|
416
|
+
});
|
|
417
|
+
});
|
|
418
|
+
|
|
337
419
|
describe("fallback behavior", () => {
|
|
338
420
|
it("should use default formatter for unknown dialect", () => {
|
|
339
421
|
const sqlTableContext: SQLTableContext = {
|
|
@@ -15,9 +15,9 @@ export function isSchemaless(schemaName: string) {
|
|
|
15
15
|
|
|
16
16
|
interface SqlCodeFormatter {
|
|
17
17
|
/**
|
|
18
|
-
* Format the table
|
|
18
|
+
* Format the table path based on dialect-specific rules
|
|
19
19
|
*/
|
|
20
|
-
|
|
20
|
+
formatTablePath: (tablePath: string[]) => string;
|
|
21
21
|
/**
|
|
22
22
|
* Format the SELECT clause
|
|
23
23
|
*/
|
|
@@ -25,7 +25,7 @@ interface SqlCodeFormatter {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
const defaultFormatter: SqlCodeFormatter = {
|
|
28
|
-
|
|
28
|
+
formatTablePath: (tablePath: string[]) => tablePath.join("."),
|
|
29
29
|
formatSelectClause: (columnName: string, tableName: string) =>
|
|
30
30
|
`SELECT ${columnName} FROM ${tableName} LIMIT 100`,
|
|
31
31
|
};
|
|
@@ -41,7 +41,8 @@ function getFormatter(dialect: string): SqlCodeFormatter {
|
|
|
41
41
|
const quote = BigQueryDialect.spec.identifierQuotes;
|
|
42
42
|
return {
|
|
43
43
|
// BigQuery uses backticks for identifiers
|
|
44
|
-
|
|
44
|
+
formatTablePath: (tablePath: string[]) =>
|
|
45
|
+
`${quote}${tablePath.join(".")}${quote}`,
|
|
45
46
|
formatSelectClause: defaultFormatter.formatSelectClause,
|
|
46
47
|
};
|
|
47
48
|
}
|
|
@@ -49,7 +50,7 @@ function getFormatter(dialect: string): SqlCodeFormatter {
|
|
|
49
50
|
case "sqlserver":
|
|
50
51
|
case "microsoft sql server":
|
|
51
52
|
return {
|
|
52
|
-
|
|
53
|
+
formatTablePath: defaultFormatter.formatTablePath,
|
|
53
54
|
formatSelectClause: (columnName: string, tableName: string) =>
|
|
54
55
|
`SELECT TOP 100 ${columnName} FROM ${tableName}`,
|
|
55
56
|
};
|
|
@@ -57,12 +58,11 @@ function getFormatter(dialect: string): SqlCodeFormatter {
|
|
|
57
58
|
case "postgres":
|
|
58
59
|
case "postgresql":
|
|
59
60
|
case "duckdb":
|
|
61
|
+
case "dremio":
|
|
60
62
|
// Quote column and table names to avoid raising errors on weird characters
|
|
61
63
|
return {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
return parts.map((part) => `"${part}"`).join(".");
|
|
65
|
-
},
|
|
64
|
+
formatTablePath: (tablePath: string[]) =>
|
|
65
|
+
tablePath.map((part) => `"${part}"`).join("."),
|
|
66
66
|
formatSelectClause: (columnName: string, tableName: string) =>
|
|
67
67
|
`SELECT ${columnName === "*" ? "*" : `"${columnName}"`} FROM ${tableName} LIMIT 100`,
|
|
68
68
|
};
|
|
@@ -114,26 +114,27 @@ export function sqlCode({
|
|
|
114
114
|
database,
|
|
115
115
|
dialect,
|
|
116
116
|
} = sqlTableContext;
|
|
117
|
-
|
|
117
|
+
const tablePath = [table.name];
|
|
118
118
|
|
|
119
119
|
// Set the fully qualified table name based on schema and database
|
|
120
120
|
if (isSchemaless(schema)) {
|
|
121
|
-
|
|
122
|
-
database
|
|
121
|
+
if (database !== defaultDatabase) {
|
|
122
|
+
tablePath.unshift(database);
|
|
123
|
+
}
|
|
123
124
|
} else {
|
|
124
125
|
// Include schema if it's not the default schema
|
|
125
126
|
if (schema !== defaultSchema) {
|
|
126
|
-
|
|
127
|
+
tablePath.unshift(schema);
|
|
127
128
|
}
|
|
128
129
|
|
|
129
130
|
// Include database if it's not the default database
|
|
130
131
|
if (database !== defaultDatabase) {
|
|
131
|
-
|
|
132
|
+
tablePath.unshift(database);
|
|
132
133
|
}
|
|
133
134
|
}
|
|
134
135
|
|
|
135
136
|
const formatter = getFormatter(dialect);
|
|
136
|
-
const formattedTableName = formatter.
|
|
137
|
+
const formattedTableName = formatter.formatTablePath(tablePath);
|
|
137
138
|
const selectClause = formatter.formatSelectClause(
|
|
138
139
|
columnName,
|
|
139
140
|
formattedTableName,
|
|
@@ -48,7 +48,7 @@ import {
|
|
|
48
48
|
import { switchLanguage } from "@/core/codemirror/language/extension";
|
|
49
49
|
import { MARKDOWN_INITIAL_HIDE_CODE } from "@/core/codemirror/language/languages/markdown";
|
|
50
50
|
import {
|
|
51
|
-
|
|
51
|
+
aiFeaturesEnabledAtom,
|
|
52
52
|
appWidthAtom,
|
|
53
53
|
autoInstantiateAtom,
|
|
54
54
|
} from "@/core/config/config";
|
|
@@ -100,7 +100,7 @@ export function useCellActionButtons({ cell, closePopover }: Props) {
|
|
|
100
100
|
const deleteCell = useDeleteCellCallback();
|
|
101
101
|
const { openModal } = useImperativeModal();
|
|
102
102
|
const setAiCompletionCell = useSetAtom(aiCompletionCellAtom);
|
|
103
|
-
const
|
|
103
|
+
const aiFeaturesEnabled = useAtomValue(aiFeaturesEnabledAtom);
|
|
104
104
|
const autoInstantiate = useAtomValue(autoInstantiateAtom);
|
|
105
105
|
const kioskMode = useAtomValue(kioskModeAtom);
|
|
106
106
|
const appWidth = useAtomValue(appWidthAtom);
|
|
@@ -162,7 +162,7 @@ export function useCellActionButtons({ cell, closePopover }: Props) {
|
|
|
162
162
|
{
|
|
163
163
|
icon: <SparklesIcon size={13} strokeWidth={1.5} />,
|
|
164
164
|
label: "Refactor with AI",
|
|
165
|
-
hidden: !
|
|
165
|
+
hidden: !aiFeaturesEnabled,
|
|
166
166
|
handle: () => {
|
|
167
167
|
setAiCompletionCell((current) =>
|
|
168
168
|
current?.cellId === cellId ? null : { cellId },
|
|
@@ -142,9 +142,10 @@ export function useNotebookActions() {
|
|
|
142
142
|
const { selectedLayout } = useLayoutState();
|
|
143
143
|
const { setLayoutView } = useLayoutActions();
|
|
144
144
|
const togglePresenting = useTogglePresenting();
|
|
145
|
-
// Fallback: if sharing is undefined,
|
|
145
|
+
// Fallback: if sharing is undefined, all options are enabled by default
|
|
146
146
|
const sharingHtmlEnabled = resolvedConfig.sharing?.html ?? true;
|
|
147
147
|
const sharingWasmEnabled = resolvedConfig.sharing?.wasm ?? true;
|
|
148
|
+
const sharingMolabEnabled = resolvedConfig.sharing?.molab ?? true;
|
|
148
149
|
|
|
149
150
|
// Server-side PDF export is always available outside WASM.
|
|
150
151
|
// Browser print fallback is used in WASM.
|
|
@@ -360,7 +361,8 @@ export function useNotebookActions() {
|
|
|
360
361
|
icon: <Share2Icon size={14} strokeWidth={1.5} />,
|
|
361
362
|
label: "Share",
|
|
362
363
|
handle: NOOP_HANDLER,
|
|
363
|
-
hidden:
|
|
364
|
+
hidden:
|
|
365
|
+
!sharingHtmlEnabled && !sharingWasmEnabled && !sharingMolabEnabled,
|
|
364
366
|
dropdown: [
|
|
365
367
|
{
|
|
366
368
|
icon: <GlobeIcon size={14} strokeWidth={1.5} />,
|
|
@@ -387,6 +389,7 @@ export function useNotebookActions() {
|
|
|
387
389
|
{
|
|
388
390
|
icon: <MarimoPlusIcon size={14} strokeWidth={1.5} />,
|
|
389
391
|
label: "Create molab notebook",
|
|
392
|
+
hidden: !sharingMolabEnabled,
|
|
390
393
|
handle: async () => {
|
|
391
394
|
const code = await readCode();
|
|
392
395
|
const url = createShareableLink({
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
connectedDocAtom,
|
|
27
27
|
realTimeCollaboration,
|
|
28
28
|
} from "@/core/codemirror/rtc/extension";
|
|
29
|
-
import { autoInstantiateAtom,
|
|
29
|
+
import { autoInstantiateAtom, isAiFeatureEnabled } from "@/core/config/config";
|
|
30
30
|
import type { UserConfig } from "@/core/config/config-schema";
|
|
31
31
|
import { OverridingHotkeyProvider } from "@/core/hotkeys/hotkeys";
|
|
32
32
|
import { connectionAtom } from "@/core/network/connection";
|
|
@@ -173,13 +173,13 @@ const CellEditorInternal = ({
|
|
|
173
173
|
});
|
|
174
174
|
});
|
|
175
175
|
|
|
176
|
-
const
|
|
176
|
+
const aiFeaturesEnabled = isAiFeatureEnabled(userConfig);
|
|
177
177
|
|
|
178
178
|
const extensions = useMemo(() => {
|
|
179
179
|
const extensions = setupCodeMirror({
|
|
180
180
|
cellId,
|
|
181
181
|
showPlaceholder,
|
|
182
|
-
enableAI:
|
|
182
|
+
enableAI: aiFeaturesEnabled,
|
|
183
183
|
cellActions: {
|
|
184
184
|
...cellActions,
|
|
185
185
|
afterToggleMarkdown,
|
|
@@ -201,6 +201,9 @@ const CellEditorInternal = ({
|
|
|
201
201
|
splitCell,
|
|
202
202
|
toggleHideCode,
|
|
203
203
|
aiCellCompletion: () => {
|
|
204
|
+
if (!aiFeaturesEnabled) {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
204
207
|
let closed = false;
|
|
205
208
|
setAiCompletionCell((v) => {
|
|
206
209
|
// Toggle close
|
|
@@ -271,7 +274,7 @@ const CellEditorInternal = ({
|
|
|
271
274
|
userConfig.display,
|
|
272
275
|
userConfig.diagnostics,
|
|
273
276
|
userConfig.ai?.inline_tooltip,
|
|
274
|
-
|
|
277
|
+
aiFeaturesEnabled,
|
|
275
278
|
theme,
|
|
276
279
|
showPlaceholder,
|
|
277
280
|
cellActions,
|
|
@@ -201,16 +201,23 @@ export const PANEL_MAP = new Map<PanelType, PanelDescriptor>(
|
|
|
201
201
|
);
|
|
202
202
|
|
|
203
203
|
/**
|
|
204
|
-
* Check if a panel should be hidden based on its
|
|
205
|
-
* and `requiredCapability`.
|
|
204
|
+
* Check if a panel should be hidden based on its descriptor and runtime state.
|
|
206
205
|
*/
|
|
207
|
-
export function isPanelHidden(
|
|
208
|
-
panel
|
|
209
|
-
capabilities
|
|
210
|
-
|
|
206
|
+
export function isPanelHidden({
|
|
207
|
+
panel,
|
|
208
|
+
capabilities,
|
|
209
|
+
aiEnabled,
|
|
210
|
+
}: {
|
|
211
|
+
panel: PanelDescriptor;
|
|
212
|
+
capabilities: Capabilities;
|
|
213
|
+
aiEnabled: boolean;
|
|
214
|
+
}): boolean {
|
|
211
215
|
if (panel.hidden) {
|
|
212
216
|
return true;
|
|
213
217
|
}
|
|
218
|
+
if (panel.type === "ai" && !aiEnabled) {
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
214
221
|
if (panel.requiredCapability && !capabilities[panel.requiredCapability]) {
|
|
215
222
|
return true;
|
|
216
223
|
}
|