@marimo-team/islands 0.23.10-dev12 → 0.23.10-dev14
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/{chat-ui-K84W5ecY.js → chat-ui-C7ZYD6Uw.js} +2 -2
- package/dist/{code-visibility-DK0V-P30.js → code-visibility-BdMybs1o.js} +2 -2
- package/dist/{html-to-image-B93KtAVY.js → html-to-image-BqJ4c852.js} +2129 -2112
- package/dist/main.js +5 -5
- package/dist/{process-output-BOXi9fnS.js → process-output-DzDBuKrN.js} +1 -1
- package/dist/{reveal-component-DLdvi2qq.js → reveal-component-C_rTOIWy.js} +2 -2
- package/package.json +2 -2
- package/src/components/datasources/__tests__/filter-empty.test.ts +183 -0
- package/src/components/datasources/datasources.tsx +107 -3
- package/src/core/codemirror/language/languages/sql/utils.ts +3 -1
- package/src/core/datasets/data-source-connections.ts +2 -0
package/dist/main.js
CHANGED
|
@@ -22,17 +22,17 @@ import { _ as Logger, c as Objects, g as cn, h as Events, i as NOT_SET, l as use
|
|
|
22
22
|
import { t as require_react } from "./react-DA-nE2FX.js";
|
|
23
23
|
import { t as require_compiler_runtime } from "./compiler-runtime-CEbnTgxf.js";
|
|
24
24
|
import { n as Copy, r as toast, t as copyToClipboard } from "./copy-5jQ_kGE1.js";
|
|
25
|
-
import { $ as useCellActions, An as LoaderCircle, At as DeferredRequestRegistry, B as safeExtractSetUIElementMessageBuffers, Bn as Braces, Bt as getDataTypeColor, C as AccordionContent, Cn as Trigger2, Ct as customPythonLanguageSupport, Dn as PaintRoller, Dt as Paths, E as BorderAllIcon, En as Table2, Et as PathBuilder, F as base64ToDataView, Ft as jotaiJsonStorage, Gt as convertStatsName, H as getMarimoExportContext, In as Database, J as getCellNames, Jt as useRequestClient, K as createActions, Kt as getRequestClient, L as dataViewToBase64, Ln as Columns2, Mn as Info, Nn as FileText, Nt as repl, Pn as Eye, Q as reducer, Rt as PluralWords, S as Accordion, Sn as Root2$1, St as Checkbox, T as AccordionTrigger, Tn as Trash2, U as hasTrustedExportContext, V as renderHTML, Vt as require_client, W as hasRunAnyCellAtom, X as notebookOutline, Y as notebookAtom, Yt as isUninstantiated, Z as numColumnsAtom, _n as selectAtom, a as useCellFocusActions, an as parseInitialValue, bn as Content2, bt as isInternalCellName, ct as kioskModeAtom, dn as OBJECT_ID_ATTR, dt as outputIsLoading, en as NotebookScopedLocalStorage, et as useCellIds, f as isOutputEmpty, fn as RANDOM_ID_ATTR, ft as outputIsStale, gn as atomWithStorage, hn as atomWithReducer, i as LazyAnyLanguageCodeMirror, in as parseDataset, jn as Layers, jt as generateUUID, k as ChevronDownIcon, ln as UIElementId, mt as headingToIdentifier, n as Spinner, nt as createCell, o as useLastFocusedCellId, ot as getInitialAppMode, p as useExpandedConsoleOutput, pn as jsonParseWithSpecialChar, pt as isErrorMime, qt as requestClientAtom, rn as parseAttrValue, s as maybeAddAltairImport, sn as HTMLCellId, st as initialModeAtom, un as findCellId, w as AccordionItem, wt as MarkdownLanguageAdapter, xn as Item$1, xt as normalizeName, yt as getValidName, zn as CircleAlert, zt as DATA_TYPE_ICON, __tla as __tla_0 } from "./html-to-image-
|
|
25
|
+
import { $ as useCellActions, An as LoaderCircle, At as DeferredRequestRegistry, B as safeExtractSetUIElementMessageBuffers, Bn as Braces, Bt as getDataTypeColor, C as AccordionContent, Cn as Trigger2, Ct as customPythonLanguageSupport, Dn as PaintRoller, Dt as Paths, E as BorderAllIcon, En as Table2, Et as PathBuilder, F as base64ToDataView, Ft as jotaiJsonStorage, Gt as convertStatsName, H as getMarimoExportContext, In as Database, J as getCellNames, Jt as useRequestClient, K as createActions, Kt as getRequestClient, L as dataViewToBase64, Ln as Columns2, Mn as Info, Nn as FileText, Nt as repl, Pn as Eye, Q as reducer, Rt as PluralWords, S as Accordion, Sn as Root2$1, St as Checkbox, T as AccordionTrigger, Tn as Trash2, U as hasTrustedExportContext, V as renderHTML, Vt as require_client, W as hasRunAnyCellAtom, X as notebookOutline, Y as notebookAtom, Yt as isUninstantiated, Z as numColumnsAtom, _n as selectAtom, a as useCellFocusActions, an as parseInitialValue, bn as Content2, bt as isInternalCellName, ct as kioskModeAtom, dn as OBJECT_ID_ATTR, dt as outputIsLoading, en as NotebookScopedLocalStorage, et as useCellIds, f as isOutputEmpty, fn as RANDOM_ID_ATTR, ft as outputIsStale, gn as atomWithStorage, hn as atomWithReducer, i as LazyAnyLanguageCodeMirror, in as parseDataset, jn as Layers, jt as generateUUID, k as ChevronDownIcon, ln as UIElementId, mt as headingToIdentifier, n as Spinner, nt as createCell, o as useLastFocusedCellId, ot as getInitialAppMode, p as useExpandedConsoleOutput, pn as jsonParseWithSpecialChar, pt as isErrorMime, qt as requestClientAtom, rn as parseAttrValue, s as maybeAddAltairImport, sn as HTMLCellId, st as initialModeAtom, un as findCellId, w as AccordionItem, wt as MarkdownLanguageAdapter, xn as Item$1, xt as normalizeName, yt as getValidName, zn as CircleAlert, zt as DATA_TYPE_ICON, __tla as __tla_0 } from "./html-to-image-BqJ4c852.js";
|
|
26
26
|
import { __tla as __tla_1 } from "./chunk-5FQGJX7Z-BNjes6Yx.js";
|
|
27
27
|
import { o as useSize, s as Root$2, u as createLucideIcon } from "./dist-C1BYNeCR.js";
|
|
28
28
|
import { A as SquareFunction, C as DEFAULT_COLOR_SCHEME, D as SCALE_TYPE_DESCRIPTIONS, E as EMPTY_VALUE$1, O as TIME_UNIT_DESCRIPTIONS, S as DEFAULT_AGGREGATION, T as DEFAULT_TIME_UNIT, _ as AGGREGATION_TYPE_DESCRIPTIONS, a as AGGREGATION_FNS$1, b as COLOR_SCHEMES, c as COLOR_BY_FIELDS, d as NONE_VALUE, f as SELECTABLE_DATA_TYPES, g as TIME_UNITS, h as STRING_AGGREGATION_FNS, i as convertDataTypeToSelectable, j as ChartColumn, k as escapeFieldName, l as COMBINED_TIME_UNITS, m as SORT_TYPES, n as createSpecWithoutData, o as BIN_AGGREGATION, p as SINGLE_TIME_UNITS, r as isFieldSet, s as CHART_TYPES, t as augmentSpecWithData, u as ChartType, v as AGGREGATION_TYPE_ICON, w as DEFAULT_MAX_BINS_FACET, x as COUNT_FIELD, y as CHART_TYPE_ICON } from "./spec-B96zNUEA.js";
|
|
29
|
-
import { $ as TableBody, $t as ChevronLeft, A as ComboboxItem, At as ChartErrorState, B as contextAwarePanelOpen, Bt as $fae977aafc393c5c$export$6b862160d295c8e, C as prettifyRowColumnCount, Ct as dateToLocalISODate, D as DatePicker, Dt as TabsContent, E as useInternalStateWithSync, Et as Tabs, F as CommandList, Ft as RenderTextWithLinks, G as slotsController, H as contextAwarePanelType, Ht as GripHorizontal, I as CommandSeparator, It as Kbd, Jt as Code, K as Toggle, Kt as Ellipsis, L as smartMatch, Lt as HtmlOutput, M as CommandEmpty, Mt as ChartLoadingState, N as CommandInput, Nt as LazyVegaEmbed, O as DateRangePicker, Ot as TabsList, P as CommandItem, Pt as useOverflowDetection, Q as Table, Qt as ChevronsDownUp, R as ContextAwarePanelItem, Rt as EmotionCacheProvider, S as downloadSizeLimitAtom, St as Maps, T as getColumnCountForDisplay, Tt as dateToLocalISOTime, U as isCellAwareAtom, Ut as Funnel, V as contextAwarePanelOwner, Vt as TextWrap, W as SlotNames, Wt as EyeOff, X as Fill, Xt as ChevronsRight, Yt as ChevronsUpDown, Z as Provider$1, Zt as ChevronsLeft, _ as downloadBlob, _t as SELECT_COLUMN_ID, at as generateColumns, b as Progress, bt as getMimeValues, c as Slide, ct as ColumnChartContext, d as JsonOutput, dt as useIntersectionObserver, en as ArrowDownWideNarrow, et as TableCell, f as OutputArea, ft as usePrevious$1, g as ADD_PRINTING_CLASS, gt as INDEX_COLUMN_NAME, h as InstallPackageButton, ht as loadTableData, it as NAMELESS_COLUMN_PREFIX, j as Command, jt as ChartInfoState, k as Combobox, kt as TabsTrigger, l as RadioGroup, lt as ColumnChartSpecModel, m as DataTable, mt as loadTableAndRawData, n as marimoVersionAtom, nt as TableHeader, o as SLIDE_TYPE_OPTIONS_BY_VALUE, ot as inferFieldTypes, p as OutputRenderer, pt as getPageIndexForRow, qt as Download, r as showCodeInRunModeAtom, rt as TableRow, st as renderCellValue, t as useNotebookCodeAvailable, tt as TableHead, u as RadioGroupItem, ut as DelayMount, v as downloadByURL, vt as TOO_MANY_ROWS, w as prettifyRowCount, wt as dateToLocalISODateTime, x as Filenames, xt as isNullishFilter, y as downloadHTMLAsImage, yt as toFieldTypes, z as PANEL_TYPES, zt as $fae977aafc393c5c$export$588937bcd60ade55, __tla as __tla_2 } from "./code-visibility-
|
|
29
|
+
import { $ as TableBody, $t as ChevronLeft, A as ComboboxItem, At as ChartErrorState, B as contextAwarePanelOpen, Bt as $fae977aafc393c5c$export$6b862160d295c8e, C as prettifyRowColumnCount, Ct as dateToLocalISODate, D as DatePicker, Dt as TabsContent, E as useInternalStateWithSync, Et as Tabs, F as CommandList, Ft as RenderTextWithLinks, G as slotsController, H as contextAwarePanelType, Ht as GripHorizontal, I as CommandSeparator, It as Kbd, Jt as Code, K as Toggle, Kt as Ellipsis, L as smartMatch, Lt as HtmlOutput, M as CommandEmpty, Mt as ChartLoadingState, N as CommandInput, Nt as LazyVegaEmbed, O as DateRangePicker, Ot as TabsList, P as CommandItem, Pt as useOverflowDetection, Q as Table, Qt as ChevronsDownUp, R as ContextAwarePanelItem, Rt as EmotionCacheProvider, S as downloadSizeLimitAtom, St as Maps, T as getColumnCountForDisplay, Tt as dateToLocalISOTime, U as isCellAwareAtom, Ut as Funnel, V as contextAwarePanelOwner, Vt as TextWrap, W as SlotNames, Wt as EyeOff, X as Fill, Xt as ChevronsRight, Yt as ChevronsUpDown, Z as Provider$1, Zt as ChevronsLeft, _ as downloadBlob, _t as SELECT_COLUMN_ID, at as generateColumns, b as Progress, bt as getMimeValues, c as Slide, ct as ColumnChartContext, d as JsonOutput, dt as useIntersectionObserver, en as ArrowDownWideNarrow, et as TableCell, f as OutputArea, ft as usePrevious$1, g as ADD_PRINTING_CLASS, gt as INDEX_COLUMN_NAME, h as InstallPackageButton, ht as loadTableData, it as NAMELESS_COLUMN_PREFIX, j as Command, jt as ChartInfoState, k as Combobox, kt as TabsTrigger, l as RadioGroup, lt as ColumnChartSpecModel, m as DataTable, mt as loadTableAndRawData, n as marimoVersionAtom, nt as TableHeader, o as SLIDE_TYPE_OPTIONS_BY_VALUE, ot as inferFieldTypes, p as OutputRenderer, pt as getPageIndexForRow, qt as Download, r as showCodeInRunModeAtom, rt as TableRow, st as renderCellValue, t as useNotebookCodeAvailable, tt as TableHead, u as RadioGroupItem, ut as DelayMount, v as downloadByURL, vt as TOO_MANY_ROWS, w as prettifyRowCount, wt as dateToLocalISODateTime, x as Filenames, xt as isNullishFilter, y as downloadHTMLAsImage, yt as toFieldTypes, z as PANEL_TYPES, zt as $fae977aafc393c5c$export$588937bcd60ade55, __tla as __tla_2 } from "./code-visibility-BdMybs1o.js";
|
|
30
30
|
import { c as Calendar, i as createReducerAndAtoms, n as useOnUnmount, o as ToggleLeft, t as useOnMount } from "./useLifecycle-BBO9PIph.js";
|
|
31
31
|
import { t as Check } from "./check-DTbrK0zt.js";
|
|
32
32
|
import { A as Trigger$1, C as $a916eb452884faea$export$b7a616150fdb9f44, E as $18f2051aff69b9bf$export$a54013f0d02a8f82, F as X, L as ChevronDown, M as usePrevious$2, N as useDirection, P as createCollection, S as logNever, T as $18f2051aff69b9bf$export$43bb16f9c6d9e3f7, a as SelectGroup, c as SelectSeparator, d as NativeSelect, i as SelectContent, j as clamp$2, k as Icon, l as SelectTrigger, n as capitalize, o as SelectItem, r as Select, s as SelectLabel, t as Strings, u as SelectValue, x as assertNever } from "./strings-Bu3vlb6W.js";
|
|
33
33
|
import { I as $64fa3d84918910a7$export$29f1550f4b0d4415, K as useDebounceControlledState, L as $64fa3d84918910a7$export$4d86445c2cf5e3, Mt as $65484d02dcb7eb3e$export$457c3d6518dd4c6f, Nt as $3ef42575df84b30b$export$9d1611c77c2fe928, V as $64fa3d84918910a7$export$df3a06d6289f983e, Vt as $ff5963eb1fccf552$export$e08e3b67e392101e, a as NumberField, b as DropdownMenuTrigger, c as prettyNumber, d as DropdownMenuContent, f as DropdownMenuGroup, fn as Circle, g as DropdownMenuSeparator, i as OnBlurredInput, it as $701a24aa0da5b062$export$ea18c227d4417cc3, l as prettyScientificNumber, m as DropdownMenuLabel, n as DebouncedNumberInput, p as DropdownMenuItem, pn as ChevronRight, q as useDebouncedCallback, r as Input, rt as $f7dceffc5ad7768b$export$4e328f61c538687f, t as DebouncedInput, u as DropdownMenu, ut as $6179b936705e76d3$export$ae780daf29e6d456, vt as $458b0a5536c1a7cf$export$40bfa8c7b0832715 } from "./input-_2sjvfne.js";
|
|
34
34
|
import { _ as isWasm, c as asRemoteURL, d as isStaticNotebook, f as appendQueryParams, g as Deferred, m as require_cuid2, u as getStaticVirtualFiles, v as CircleQuestionMark } from "./toDate-x-WRDCH7.js";
|
|
35
|
-
import { a as MarimoIncomingMessageEvent, c as MarimoValueUpdateEvent, d as Square, f as File, i as PythonIcon, l as createInputEvent, n as blobToString, o as MarimoValueInputEvent, r as filesToBase64, s as MarimoValueReadyEvent, t as processOutput, u as deserializeBlob } from "./process-output-
|
|
35
|
+
import { a as MarimoIncomingMessageEvent, c as MarimoValueUpdateEvent, d as Square, f as File, i as PythonIcon, l as createInputEvent, n as blobToString, o as MarimoValueInputEvent, r as filesToBase64, s as MarimoValueReadyEvent, t as processOutput, u as deserializeBlob } from "./process-output-DzDBuKrN.js";
|
|
36
36
|
import { n as Trash, r as Pencil, t as BulkEdit } from "./types-CVvp1fKr.js";
|
|
37
37
|
import { n as require_prop_types, r as Plus, t as ErrorBoundary } from "./ErrorBoundary-rULOrC_p.js";
|
|
38
38
|
import { t as require_react_dom } from "./react-dom-BTJzcVJ9.js";
|
|
@@ -5590,7 +5590,7 @@ let __tla = Promise.all([
|
|
|
5590
5590
|
};
|
|
5591
5591
|
}
|
|
5592
5592
|
};
|
|
5593
|
-
var LazyChatbot = import_react.lazy(() => import("./chat-ui-
|
|
5593
|
+
var LazyChatbot = import_react.lazy(() => import("./chat-ui-C7ZYD6Uw.js").then((e) => ({
|
|
5594
5594
|
default: e.Chatbot
|
|
5595
5595
|
}))), messageSchema = array(object({
|
|
5596
5596
|
id: string(),
|
|
@@ -36116,7 +36116,7 @@ ${c}
|
|
|
36116
36116
|
if (l && l !== "slide") return l;
|
|
36117
36117
|
if (c == null ? void 0 : c.has(e)) return "skip";
|
|
36118
36118
|
}
|
|
36119
|
-
var LazySlidesComponent = import_react.lazy(() => import("./reveal-component-
|
|
36119
|
+
var LazySlidesComponent = import_react.lazy(() => import("./reveal-component-C_rTOIWy.js"));
|
|
36120
36120
|
const SlidesLayoutRenderer = ({ layout: e, setLayout: r, cells: c, mode: l }) => {
|
|
36121
36121
|
var _a3;
|
|
36122
36122
|
let u = useAtomValue(kioskModeAtom), d = l === "read" || u, f = useAtomValue(numColumnsAtom) > 1, [p, m] = (0, import_react.useState)(null), { slideCells: h, skippedIds: g, noOutputIds: _, slideTypes: v, startCellIndex: y } = (0, import_react.useMemo)(() => computeSlideCellsInfo(c, e), [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { s as __toESM } from "./chunk-BNovOVIE.js";
|
|
2
2
|
import { t as require_compiler_runtime } from "./compiler-runtime-CEbnTgxf.js";
|
|
3
|
-
import { it as parseHtmlContent, rt as ansiToPlainText } from "./html-to-image-
|
|
3
|
+
import { it as parseHtmlContent, rt as ansiToPlainText } from "./html-to-image-BqJ4c852.js";
|
|
4
4
|
import { u as createLucideIcon } from "./dist-C1BYNeCR.js";
|
|
5
5
|
import { t as Strings } from "./strings-Bu3vlb6W.js";
|
|
6
6
|
import { t as require_jsx_runtime } from "./jsx-runtime-DebpN0FN.js";
|
|
@@ -6,10 +6,10 @@ import { s as __toESM } from "./chunk-BNovOVIE.js";
|
|
|
6
6
|
import { _ as Logger, g as cn, h as Events, l as useEventListener, t as Button } from "./button-C5K9fIPF.js";
|
|
7
7
|
import { t as require_react } from "./react-DA-nE2FX.js";
|
|
8
8
|
import { t as require_compiler_runtime } from "./compiler-runtime-CEbnTgxf.js";
|
|
9
|
-
import { ct as kioskModeAtom } from "./html-to-image-
|
|
9
|
+
import { ct as kioskModeAtom } from "./html-to-image-BqJ4c852.js";
|
|
10
10
|
import "./chunk-5FQGJX7Z-BNjes6Yx.js";
|
|
11
11
|
import { u as createLucideIcon } from "./dist-C1BYNeCR.js";
|
|
12
|
-
import { Gt as Expand, J as PanelGroup, Jt as Code, Wt as EyeOff, Y as PanelResizeHandle, a as DEFAULT_SLIDE_TYPE, c as Slide, i as DEFAULT_DECK_TRANSITION, q as Panel, s as SlideSidebar, t as useNotebookCodeAvailable } from "./code-visibility-
|
|
12
|
+
import { Gt as Expand, J as PanelGroup, Jt as Code, Wt as EyeOff, Y as PanelResizeHandle, a as DEFAULT_SLIDE_TYPE, c as Slide, i as DEFAULT_DECK_TRANSITION, q as Panel, s as SlideSidebar, t as useNotebookCodeAvailable } from "./code-visibility-BdMybs1o.js";
|
|
13
13
|
import { q as useDebouncedCallback } from "./input-_2sjvfne.js";
|
|
14
14
|
import "./toDate-x-WRDCH7.js";
|
|
15
15
|
import "./react-dom-BTJzcVJ9.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marimo-team/islands",
|
|
3
|
-
"version": "0.23.10-
|
|
3
|
+
"version": "0.23.10-dev14",
|
|
4
4
|
"main": "dist/main.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"@marimo-team/codemirror-ai": "^0.3.7",
|
|
58
58
|
"@marimo-team/codemirror-languageserver": "^1.16.12",
|
|
59
59
|
"@marimo-team/codemirror-mcp": "^0.1.5",
|
|
60
|
-
"@marimo-team/codemirror-sql": "^0.2.
|
|
60
|
+
"@marimo-team/codemirror-sql": "^0.2.8",
|
|
61
61
|
"@marimo-team/llm-info": "workspace:*",
|
|
62
62
|
"@marimo-team/marimo-api": "workspace:*",
|
|
63
63
|
"@marimo-team/react-slotz": "^0.2.0",
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
import type {
|
|
5
|
+
Database,
|
|
6
|
+
DatabaseSchema,
|
|
7
|
+
DataTable,
|
|
8
|
+
} from "@/core/kernel/messages";
|
|
9
|
+
import { filterEmptyDatabases } from "../datasources";
|
|
10
|
+
|
|
11
|
+
function makeTable(name: string): DataTable {
|
|
12
|
+
return {
|
|
13
|
+
name,
|
|
14
|
+
columns: [],
|
|
15
|
+
source: "memory",
|
|
16
|
+
source_type: "local",
|
|
17
|
+
type: "table",
|
|
18
|
+
engine: null,
|
|
19
|
+
indexes: null,
|
|
20
|
+
num_columns: null,
|
|
21
|
+
num_rows: null,
|
|
22
|
+
variable_name: null,
|
|
23
|
+
primary_keys: null,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function makeSchema(opts: {
|
|
28
|
+
name: string;
|
|
29
|
+
tables: DataTable[];
|
|
30
|
+
tables_resolved?: boolean;
|
|
31
|
+
}): DatabaseSchema {
|
|
32
|
+
return {
|
|
33
|
+
name: opts.name,
|
|
34
|
+
tables: opts.tables,
|
|
35
|
+
tables_resolved: opts.tables_resolved ?? true,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function makeDatabase(
|
|
40
|
+
name: string,
|
|
41
|
+
schemas: DatabaseSchema[],
|
|
42
|
+
schemas_resolved = true,
|
|
43
|
+
): Database {
|
|
44
|
+
return {
|
|
45
|
+
name,
|
|
46
|
+
dialect: "duckdb",
|
|
47
|
+
schemas,
|
|
48
|
+
schemas_resolved,
|
|
49
|
+
engine: null,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
describe("filterEmptyDatabases", () => {
|
|
54
|
+
it("hides schemas whose tables are resolved and empty", () => {
|
|
55
|
+
const databases = [
|
|
56
|
+
makeDatabase("memory", [
|
|
57
|
+
makeSchema({ name: "main", tables: [makeTable("t1")] }),
|
|
58
|
+
makeSchema({ name: "empty_schema", tables: [] }),
|
|
59
|
+
]),
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
expect(filterEmptyDatabases(databases)).toEqual([
|
|
63
|
+
makeDatabase("memory", [
|
|
64
|
+
makeSchema({ name: "main", tables: [makeTable("t1")] }),
|
|
65
|
+
]),
|
|
66
|
+
]);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("preserves databases whose schemas have not been resolved yet (lazy state)", () => {
|
|
70
|
+
const databases = [
|
|
71
|
+
makeDatabase("not_loaded_yet", [], /* schemas_resolved */ false),
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
expect(filterEmptyDatabases(databases)).toEqual([
|
|
75
|
+
makeDatabase("not_loaded_yet", [], false),
|
|
76
|
+
]);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("hides databases that have been resolved as empty", () => {
|
|
80
|
+
const databases = [
|
|
81
|
+
makeDatabase("really_empty", [], /* schemas_resolved */ true),
|
|
82
|
+
makeDatabase("has_tables", [
|
|
83
|
+
makeSchema({ name: "main", tables: [makeTable("t1")] }),
|
|
84
|
+
]),
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
expect(filterEmptyDatabases(databases)).toEqual([
|
|
88
|
+
makeDatabase("has_tables", [
|
|
89
|
+
makeSchema({ name: "main", tables: [makeTable("t1")] }),
|
|
90
|
+
]),
|
|
91
|
+
]);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("hides databases whose schemas all filtered to empty", () => {
|
|
95
|
+
const databases = [
|
|
96
|
+
makeDatabase("only_empty", [
|
|
97
|
+
makeSchema({ name: "a", tables: [] }),
|
|
98
|
+
makeSchema({ name: "b", tables: [] }),
|
|
99
|
+
]),
|
|
100
|
+
makeDatabase("has_tables", [
|
|
101
|
+
makeSchema({ name: "main", tables: [makeTable("t1")] }),
|
|
102
|
+
]),
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
expect(filterEmptyDatabases(databases)).toEqual([
|
|
106
|
+
makeDatabase("has_tables", [
|
|
107
|
+
makeSchema({ name: "main", tables: [makeTable("t1")] }),
|
|
108
|
+
]),
|
|
109
|
+
]);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("treats missing schemas_resolved as resolved (backward compatible)", () => {
|
|
113
|
+
const databases = [
|
|
114
|
+
{ name: "memory", dialect: "duckdb", schemas: [], engine: null },
|
|
115
|
+
] as Database[];
|
|
116
|
+
|
|
117
|
+
expect(filterEmptyDatabases(databases)).toEqual([]);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("preserves schemas whose tables have not been resolved yet", () => {
|
|
121
|
+
const databases = [
|
|
122
|
+
makeDatabase("snowflake_db", [
|
|
123
|
+
// include_tables=False was used; the schema is not actually empty,
|
|
124
|
+
// tables will be fetched lazily on expand.
|
|
125
|
+
makeSchema({ name: "public", tables: [], tables_resolved: false }),
|
|
126
|
+
makeSchema({ name: "audit", tables: [], tables_resolved: false }),
|
|
127
|
+
makeSchema({
|
|
128
|
+
name: "really_empty",
|
|
129
|
+
tables: [],
|
|
130
|
+
tables_resolved: true,
|
|
131
|
+
}),
|
|
132
|
+
]),
|
|
133
|
+
];
|
|
134
|
+
|
|
135
|
+
expect(filterEmptyDatabases(databases)).toEqual([
|
|
136
|
+
makeDatabase("snowflake_db", [
|
|
137
|
+
makeSchema({ name: "public", tables: [], tables_resolved: false }),
|
|
138
|
+
makeSchema({ name: "audit", tables: [], tables_resolved: false }),
|
|
139
|
+
]),
|
|
140
|
+
]);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it("treats missing tables_resolved as resolved (backward compatible)", () => {
|
|
144
|
+
// Older payloads predating the new flag may omit it; default semantics
|
|
145
|
+
// treat the schema as resolved/authoritative.
|
|
146
|
+
const databases = [
|
|
147
|
+
makeDatabase("memory", [
|
|
148
|
+
{ name: "main", tables: [makeTable("t1")] },
|
|
149
|
+
{ name: "empty_schema", tables: [] },
|
|
150
|
+
] as DatabaseSchema[]),
|
|
151
|
+
];
|
|
152
|
+
|
|
153
|
+
expect(filterEmptyDatabases(databases)).toEqual([
|
|
154
|
+
makeDatabase("memory", [
|
|
155
|
+
{ name: "main", tables: [makeTable("t1")] },
|
|
156
|
+
] as DatabaseSchema[]),
|
|
157
|
+
]);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("returns the same reference when nothing was filtered", () => {
|
|
161
|
+
const databases = [
|
|
162
|
+
makeDatabase("memory", [
|
|
163
|
+
makeSchema({ name: "main", tables: [makeTable("t1")] }),
|
|
164
|
+
]),
|
|
165
|
+
];
|
|
166
|
+
|
|
167
|
+
expect(filterEmptyDatabases(databases)).toBe(databases);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("does not mutate the input", () => {
|
|
171
|
+
const databases = [
|
|
172
|
+
makeDatabase("memory", [
|
|
173
|
+
makeSchema({ name: "main", tables: [makeTable("t1")] }),
|
|
174
|
+
makeSchema({ name: "empty_schema", tables: [] }),
|
|
175
|
+
]),
|
|
176
|
+
];
|
|
177
|
+
const snapshot = JSON.parse(JSON.stringify(databases));
|
|
178
|
+
|
|
179
|
+
filterEmptyDatabases(databases);
|
|
180
|
+
|
|
181
|
+
expect(databases).toEqual(snapshot);
|
|
182
|
+
});
|
|
183
|
+
});
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
3
|
import { CommandList } from "cmdk";
|
|
4
|
-
import { atom, useAtomValue, useSetAtom } from "jotai";
|
|
5
|
-
import {
|
|
4
|
+
import { atom, useAtom, useAtomValue, useSetAtom } from "jotai";
|
|
5
|
+
import { atomWithStorage } from "jotai/utils";
|
|
6
|
+
import {
|
|
7
|
+
EyeIcon,
|
|
8
|
+
EyeOffIcon,
|
|
9
|
+
PlusIcon,
|
|
10
|
+
PlusSquareIcon,
|
|
11
|
+
XIcon,
|
|
12
|
+
} from "lucide-react";
|
|
6
13
|
import React from "react";
|
|
7
14
|
import { dbDisplayName } from "@/components/databases/display";
|
|
8
15
|
import { EngineVariable } from "@/components/databases/engine-variable";
|
|
@@ -52,6 +59,7 @@ import { sortBy } from "@/utils/arrays";
|
|
|
52
59
|
import { logNever } from "@/utils/assertNever";
|
|
53
60
|
import { cn } from "@/utils/cn";
|
|
54
61
|
import { Events } from "@/utils/events";
|
|
62
|
+
import { jotaiJsonStorage } from "@/utils/storage/jotai";
|
|
55
63
|
import {
|
|
56
64
|
DatabaseIcon,
|
|
57
65
|
SchemaIcon,
|
|
@@ -116,6 +124,63 @@ const sortedTablesAtom = atom((get) => {
|
|
|
116
124
|
});
|
|
117
125
|
});
|
|
118
126
|
|
|
127
|
+
/**
|
|
128
|
+
* Whether to hide empty schemas and databases (those with no tables) in the
|
|
129
|
+
* datasources panel.
|
|
130
|
+
*/
|
|
131
|
+
export const hideEmptyDatasourcesAtom = atomWithStorage<boolean>(
|
|
132
|
+
"marimo:datasources:hideEmpty",
|
|
133
|
+
false,
|
|
134
|
+
jotaiJsonStorage,
|
|
135
|
+
{ getOnInit: true },
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
function isKnownEmptySchema(schema: DatabaseSchema): boolean {
|
|
139
|
+
return schema.tables_resolved !== false && schema.tables.length === 0;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Apply the "hide empty" filter to a connection's databases.
|
|
144
|
+
*
|
|
145
|
+
* - Schemas with confirmed-empty table lists are hidden.
|
|
146
|
+
* - Databases are hidden when either (a) their schemas have been enumerated
|
|
147
|
+
* and the list is empty, or (b) every schema in them was hidden by the
|
|
148
|
+
* schema-level filter.
|
|
149
|
+
* - Databases / schemas whose contents haven't been resolved yet (deferred
|
|
150
|
+
* discovery — `schemas_resolved === false` or `tables_resolved === false`)
|
|
151
|
+
* are preserved so the user can expand them to trigger a fetch.
|
|
152
|
+
*/
|
|
153
|
+
export function filterEmptyDatabases(databases: Database[]): Database[] {
|
|
154
|
+
let changed = false;
|
|
155
|
+
const result: Database[] = [];
|
|
156
|
+
for (const database of databases) {
|
|
157
|
+
// Known-empty database: schema list was enumerated and is empty.
|
|
158
|
+
if (database.schemas_resolved !== false && database.schemas.length === 0) {
|
|
159
|
+
changed = true;
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
// Deferred schema discovery — keep so the user can expand and load.
|
|
163
|
+
if (database.schemas.length === 0) {
|
|
164
|
+
result.push(database);
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
const visibleSchemas = database.schemas.filter(
|
|
168
|
+
(schema) => !isKnownEmptySchema(schema),
|
|
169
|
+
);
|
|
170
|
+
if (visibleSchemas.length === 0) {
|
|
171
|
+
changed = true;
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
if (visibleSchemas.length === database.schemas.length) {
|
|
175
|
+
result.push(database);
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
changed = true;
|
|
179
|
+
result.push({ ...database, schemas: visibleSchemas });
|
|
180
|
+
}
|
|
181
|
+
return changed ? result : databases;
|
|
182
|
+
}
|
|
183
|
+
|
|
119
184
|
/**
|
|
120
185
|
* This atom is used to get the data connections that are available to the user.
|
|
121
186
|
* It filters out the internal engines if it has no databases or if it has only the in-memory database and no schemas.
|
|
@@ -152,10 +217,27 @@ export const connectionsAtom = atom((get) => {
|
|
|
152
217
|
|
|
153
218
|
export const DataSources: React.FC = () => {
|
|
154
219
|
const [searchValue, setSearchValue] = React.useState<string>("");
|
|
220
|
+
const [hideEmpty, setHideEmpty] = useAtom(hideEmptyDatasourcesAtom);
|
|
155
221
|
|
|
156
222
|
const closeAllColumns = useSetAtom(closeAllColumnsAtom);
|
|
157
223
|
const tables = useAtomValue(sortedTablesAtom);
|
|
158
|
-
const
|
|
224
|
+
const rawConnections = useAtomValue(connectionsAtom);
|
|
225
|
+
|
|
226
|
+
const dataConnections = React.useMemo(() => {
|
|
227
|
+
if (!hideEmpty) {
|
|
228
|
+
return rawConnections;
|
|
229
|
+
}
|
|
230
|
+
let changed = false;
|
|
231
|
+
const filtered = rawConnections.map((connection) => {
|
|
232
|
+
const databases = filterEmptyDatabases(connection.databases);
|
|
233
|
+
if (databases === connection.databases) {
|
|
234
|
+
return connection;
|
|
235
|
+
}
|
|
236
|
+
changed = true;
|
|
237
|
+
return { ...connection, databases };
|
|
238
|
+
});
|
|
239
|
+
return changed ? filtered : rawConnections;
|
|
240
|
+
}, [rawConnections, hideEmpty]);
|
|
159
241
|
|
|
160
242
|
if (tables.length === 0 && dataConnections.length === 0) {
|
|
161
243
|
return (
|
|
@@ -204,6 +286,28 @@ export const DataSources: React.FC = () => {
|
|
|
204
286
|
</button>
|
|
205
287
|
)}
|
|
206
288
|
|
|
289
|
+
<Tooltip
|
|
290
|
+
content={
|
|
291
|
+
hideEmpty
|
|
292
|
+
? "Show empty schemas and databases"
|
|
293
|
+
: "Hide empty schemas and databases"
|
|
294
|
+
}
|
|
295
|
+
>
|
|
296
|
+
<Button
|
|
297
|
+
data-testid="datasources-hide-empty-button"
|
|
298
|
+
variant="ghost"
|
|
299
|
+
size="sm"
|
|
300
|
+
className="px-2 rounded-none focus-visible:outline-hidden"
|
|
301
|
+
onClick={() => setHideEmpty(!hideEmpty)}
|
|
302
|
+
>
|
|
303
|
+
{hideEmpty ? (
|
|
304
|
+
<EyeOffIcon className="h-4 w-4" />
|
|
305
|
+
) : (
|
|
306
|
+
<EyeIcon className="h-4 w-4" />
|
|
307
|
+
)}
|
|
308
|
+
</Button>
|
|
309
|
+
</Tooltip>
|
|
310
|
+
|
|
207
311
|
<AddConnectionDialog>
|
|
208
312
|
<Button
|
|
209
313
|
variant="ghost"
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
} from "@codemirror/lang-sql";
|
|
15
15
|
import {
|
|
16
16
|
BigQueryDialect,
|
|
17
|
+
DremioDialect,
|
|
17
18
|
DuckDBDialect,
|
|
18
19
|
} from "@marimo-team/codemirror-sql/dialects";
|
|
19
20
|
import type { DataSourceConnection } from "@/core/kernel/messages";
|
|
@@ -97,6 +98,8 @@ export function guessDialect(
|
|
|
97
98
|
return PLSQL;
|
|
98
99
|
case "bigquery":
|
|
99
100
|
return BigQueryDialect;
|
|
101
|
+
case "dremio":
|
|
102
|
+
return DremioDialect;
|
|
100
103
|
case "timescaledb":
|
|
101
104
|
return PostgreSQL; // TimescaleDB is a PostgreSQL dialect
|
|
102
105
|
case "awsathena":
|
|
@@ -116,7 +119,6 @@ export function guessDialect(
|
|
|
116
119
|
case "spark":
|
|
117
120
|
case "databricks":
|
|
118
121
|
case "datafusion":
|
|
119
|
-
case "dremio":
|
|
120
122
|
Logger.debug("Unsupported dialect", { dialect });
|
|
121
123
|
return ModifiedStandardSQL;
|
|
122
124
|
default:
|
|
@@ -169,6 +169,7 @@ const {
|
|
|
169
169
|
return {
|
|
170
170
|
...db,
|
|
171
171
|
schemas: schemas,
|
|
172
|
+
schemas_resolved: true,
|
|
172
173
|
};
|
|
173
174
|
}),
|
|
174
175
|
};
|
|
@@ -213,6 +214,7 @@ const {
|
|
|
213
214
|
return {
|
|
214
215
|
...schema,
|
|
215
216
|
tables: tables,
|
|
217
|
+
tables_resolved: true,
|
|
216
218
|
};
|
|
217
219
|
}),
|
|
218
220
|
};
|