@marimo-team/islands 0.24.1-dev41 → 0.24.1-dev43

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.
Files changed (33) hide show
  1. package/dist/{ConnectedDataExplorerComponent-4UvlnITc.js → ConnectedDataExplorerComponent-CxLgYj_n.js} +1038 -1038
  2. package/dist/{any-language-editor-ClDhQoll.js → any-language-editor-BrPYtXHK.js} +1 -1
  3. package/dist/{chat-ui-BzZL5_XE.js → chat-ui-Mjq3lBLn.js} +1335 -1335
  4. package/dist/{common-DVtMyyCC.js → common-IoLWZR0C.js} +20955 -20975
  5. package/dist/{input-CIaA2N0m.js → dropdown-menu-DEu759Oe.js} +930 -927
  6. package/dist/{error-banner-BbxpEiD5.js → error-banner-BT4uIIQ2.js} +190 -192
  7. package/dist/{glide-data-editor-CgO5z9xu.js → glide-data-editor-C2RTkAgQ.js} +51 -51
  8. package/dist/{html-to-image-DPAZGZSi.js → html-to-image-hKcfSWj_.js} +5795 -5590
  9. package/dist/main.js +8696 -8703
  10. package/dist/{mermaid-BBTUHwCi.js → mermaid-OsTxhZOI.js} +12 -12
  11. package/dist/{process-output-oZcxC_tq.js → process-output-BkHK-KDc.js} +6 -6
  12. package/dist/{reveal-component-BHpu6-qW.js → reveal-component-DUBiSLUI.js} +11 -11
  13. package/dist/{strings-CoQ5sA4r.js → select-Bakm9EUs.js} +50 -53
  14. package/dist/{spec-6c0_fLYz.js → spec-DqUrBQpS.js} +2 -2
  15. package/dist/style.css +1 -1
  16. package/dist/{useDateFormatter-6ImHK1ln.js → useDateFormatter-DjRKOAkR.js} +1 -1
  17. package/dist/{vega-component-DlnGFDeY.js → vega-component-A2xO4BUa.js} +17 -17
  18. package/package.json +1 -1
  19. package/src/components/chat/acp/agent-panel.tsx +4 -6
  20. package/src/components/data-table/columns.tsx +130 -132
  21. package/src/components/datasources/datasources.tsx +16 -17
  22. package/src/components/editor/actions/useNotebookActions.tsx +50 -0
  23. package/src/components/editor/chrome/panels/components.tsx +1 -1
  24. package/src/components/editor/connections/__tests__/add-connection-button.test.tsx +129 -0
  25. package/src/components/editor/connections/add-connection-button.tsx +132 -0
  26. package/src/components/editor/connections/components.tsx +11 -0
  27. package/src/components/editor/connections/quick-add-data-sources.tsx +11 -11
  28. package/src/components/editor/controls/__tests__/notebook-menu-dropdown.test.tsx +109 -1
  29. package/src/components/storage/storage-inspector.tsx +15 -17
  30. package/src/core/codemirror/language/panel/sql.test.tsx +69 -0
  31. package/src/core/codemirror/language/panel/sql.tsx +81 -4
  32. package/src/core/codemirror/markdown/commands.ts +1 -1
  33. package/src/core/codemirror/markdown/completions.ts +15 -17
@@ -1,7 +1,7 @@
1
1
  import { a as e } from "./rolldown-runtime-Cb76FRpK.js";
2
2
  import { t } from "./react-DMWcHin-.js";
3
3
  import { u as n } from "./dist-DTSuOdDc.js";
4
- import { E as r } from "./strings-CoQ5sA4r.js";
4
+ import { E as r } from "./select-Bakm9EUs.js";
5
5
  var i = n("chart-pie", [["path", {
6
6
  d: "M21 12c.552 0 1.005-.449.95-.998a10 10 0 0 0-8.953-8.951c-.55-.055-.998.398-.998.95v8a1 1 0 0 0 1 1z",
7
7
  key: "pzmjnu"
@@ -4,17 +4,17 @@ import { t as i } from "./react-DMWcHin-.js";
4
4
  import { t as a } from "./compiler-runtime-CAH9RLw2.js";
5
5
  import { _ as o, c as s } from "./toDate-C4ZHOUTi.js";
6
6
  import { t as c } from "./jsx-runtime-CpoKNRgu.js";
7
- import { n as l } from "./error-banner-BbxpEiD5.js";
7
+ import { n as l } from "./error-banner-BT4uIIQ2.js";
8
8
  import { t as u } from "./tooltip-rwx3Qbw6.js";
9
9
  import { t as d } from "./debounce-C3jyATpc.js";
10
10
  import { A as f, n as p } from "./useTheme-CLjV5H1i.js";
11
11
  import { c as m } from "./arrays-CN1djC4k.js";
12
12
  import { t as ee } from "./objects-CzUgoMIF.js";
13
- import { a as h, i as te, n as ne, t as re } from "./formats-l5hZdDGj.js";
14
- import { A as ie, n as g } from "./vega-loader.browser-DEggZzni.js";
15
- import { t as ae } from "./react-vega-DSD5aT7_.js";
16
- import { a as oe, n as se } from "./loader-CgRtoLc5.js";
17
- import { t as _ } from "./useAsyncData-BPIoRmNG.js";
13
+ import { t as h } from "./useAsyncData-BPIoRmNG.js";
14
+ import { a as te, i as ne, n as re, t as g } from "./formats-l5hZdDGj.js";
15
+ import { A as ie, n as ae } from "./vega-loader.browser-DEggZzni.js";
16
+ import { t as oe } from "./react-vega-DSD5aT7_.js";
17
+ import { a as se, n as _ } from "./loader-CgRtoLc5.js";
18
18
  import { t as ce } from "./useDeepCompareMemoize-rqhnGZ6D.js";
19
19
  import { t as le } from "./semaphore-89jsoHtS.js";
20
20
  import { t as ue } from "./use-vega-container-remeasure-DUW4tRxq.js";
@@ -490,7 +490,7 @@ async function U(e) {
490
490
  } catch {
491
491
  return e;
492
492
  }
493
- let a = e.data.format, o = await n.run(() => se(i.href, a));
493
+ let a = e.data.format, o = await n.run(() => _(i.href, a));
494
494
  return t[i.pathname] = o, {
495
495
  ...e,
496
496
  data: { name: i.pathname }
@@ -504,11 +504,11 @@ async function U(e) {
504
504
  //#endregion
505
505
  //#region src/plugins/impl/vega/vega-component.tsx
506
506
  var W = c();
507
- g("arrow", re);
507
+ ae("arrow", g);
508
508
  var G = (e) => {
509
509
  let t = (0, de.c)(12), { value: n, setValue: r, chartSelection: i, fieldSelection: a, spec: o, embedOptions: s } = e, c, u;
510
510
  t[0] === o ? (c = t[1], u = t[2]) : (c = async () => U(o), u = [o], t[0] = o, t[1] = c, t[2] = u);
511
- let { data: d, error: f } = _(c, u);
511
+ let { data: d, error: f } = h(c, u);
512
512
  if (f) {
513
513
  let e;
514
514
  return t[3] === f ? e = t[4] : (e = /* @__PURE__ */ (0, W.jsx)(l, { error: f }), t[3] = f, t[4] = e), e;
@@ -524,7 +524,7 @@ var G = (e) => {
524
524
  embedOptions: s
525
525
  }), t[5] = i, t[6] = s, t[7] = a, t[8] = d, t[9] = r, t[10] = n, t[11] = p) : p = t[11], p;
526
526
  }, K = (e) => {
527
- let i = (0, de.c)(50), { value: a, setValue: s, chartSelection: c, fieldSelection: l, spec: m, embedOptions: h } = e, { theme: re } = p(), g = (0, v.useRef)(null), se = (0, v.useRef)(void 0), [_, le] = (0, v.useState)(), y;
527
+ let i = (0, de.c)(50), { value: a, setValue: s, chartSelection: c, fieldSelection: l, spec: m, embedOptions: h } = e, { theme: te } = p(), g = (0, v.useRef)(null), ae = (0, v.useRef)(void 0), [_, le] = (0, v.useState)(), y;
528
528
  bb0: {
529
529
  if (h && "actions" in h) {
530
530
  y = h.actions;
@@ -570,7 +570,7 @@ var G = (e) => {
570
570
  }, i[17] = w, i[18] = F);
571
571
  let I = f(F), L;
572
572
  i[19] === Symbol.for("react.memo_cache_sentinel") ? (L = (e) => {
573
- t.debug("[Vega view] created", e), se.current = e, le(void 0);
573
+ t.debug("[Vega view] created", e), ae.current = e, le(void 0);
574
574
  }, i[19] = L) : L = i[19];
575
575
  let R = f(L), z;
576
576
  i[20] === D ? z = i[21] : (z = () => {
@@ -585,12 +585,12 @@ var G = (e) => {
585
585
  children: /* @__PURE__ */ (0, W.jsx)(o, { className: "absolute bottom-1 right-0 m-2 h-4 w-4 cursor-help text-muted-foreground hover:text-foreground" })
586
586
  });
587
587
  }, i[20] = D, i[21] = z);
588
- let B = z, V = re === "dark" ? "dark" : void 0, H;
588
+ let B = z, V = te === "dark" ? "dark" : void 0, H;
589
589
  i[22] !== b || i[23] !== V ? (H = {
590
590
  theme: V,
591
591
  actions: b,
592
592
  mode: "vega-lite",
593
- tooltip: oe.call,
593
+ tooltip: se.call,
594
594
  renderer: "canvas"
595
595
  }, i[22] = b, i[23] = V, i[24] = H) : H = i[24];
596
596
  let U;
@@ -601,7 +601,7 @@ var G = (e) => {
601
601
  onError: I,
602
602
  onEmbed: R
603
603
  }, i[25] = I, i[26] = R, i[27] = w, i[28] = H, i[29] = U) : U = i[29];
604
- let G = ae(U), K;
604
+ let G = oe(U), K;
605
605
  i[30] === w ? K = i[31] : (K = ie(w), i[30] = w, i[31] = K);
606
606
  let q = K;
607
607
  ue(g, q === "container");
@@ -626,9 +626,9 @@ var G = (e) => {
626
626
  });
627
627
  }), Y = [G, P], i[32] = G, i[33] = P, i[34] = J, i[35] = Y) : (J = i[34], Y = i[35]), (0, v.useEffect)(J, Y);
628
628
  let X;
629
- i[36] === _ ? X = i[37] : (X = _ && /* @__PURE__ */ (0, W.jsxs)(ne, {
629
+ i[36] === _ ? X = i[37] : (X = _ && /* @__PURE__ */ (0, W.jsxs)(re, {
630
630
  variant: "destructive",
631
- children: [/* @__PURE__ */ (0, W.jsx)(te, { children: _.message }), /* @__PURE__ */ (0, W.jsx)("div", {
631
+ children: [/* @__PURE__ */ (0, W.jsx)(ne, { children: _.message }), /* @__PURE__ */ (0, W.jsx)("div", {
632
632
  className: "text-md",
633
633
  children: _.stack
634
634
  })]
@@ -655,7 +655,7 @@ function me(e) {
655
655
  return e instanceof Set ? [...e] : e;
656
656
  }
657
657
  function he(e) {
658
- return Array.isArray(e) ? e.map((e) => e instanceof Date && h(e) ? new Date(e).getTime() : e) : e;
658
+ return Array.isArray(e) ? e.map((e) => e instanceof Date && te(e) ? new Date(e).getTime() : e) : e;
659
659
  }
660
660
  function ge(e, t) {
661
661
  return /* @__PURE__ */ (0, W.jsxs)("div", { children: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marimo-team/islands",
3
- "version": "0.24.1-dev41",
3
+ "version": "0.24.1-dev43",
4
4
  "main": "dist/main.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -334,12 +334,10 @@ const PromptArea = memo<PromptAreaProps>(
334
334
  // sentence has to begin with '/' to trigger autocomplete
335
335
  return {
336
336
  triggerCompletionRegex: /^\/(\w+)?/,
337
- completions: commands.map(
338
- (prompt): Completion => ({
339
- label: `/${prompt.name}`,
340
- info: prompt.description,
341
- }),
342
- ),
337
+ completions: commands.map((prompt): Completion => ({
338
+ label: `/${prompt.name}`,
339
+ info: prompt.description,
340
+ })),
343
341
  };
344
342
  }, [commands]);
345
343
 
@@ -196,150 +196,148 @@ export function generateColumns<T>({
196
196
  columnKeys.splice(indexColumnIdx, 1);
197
197
  }
198
198
 
199
- const columns = columnKeys.map(
200
- (key, idx): ColumnDef<T> => ({
201
- id: key || `${NAMELESS_COLUMN_PREFIX}${idx}`,
202
- // Use an accessorFn instead of an accessorKey because column names
203
- // may have periods in them ...
204
- // https://github.com/TanStack/table/issues/1671
205
- accessorFn: (row) => {
206
- return row[key as keyof T];
207
- },
208
- enableHiding: !rowHeadersSet.has(key) && key !== "",
209
- header: ({ column, table }) => {
210
- const stats = chartSpecModel?.getColumnStats(key);
211
- const dtype = column.columnDef.meta?.dtype;
212
- const headerTitle = headerTooltip?.[key];
213
- const headerJustify = textJustifyColumns?.[key];
214
-
215
- const dtypeHeader =
216
- showDataTypes && dtype ? (
217
- <div
218
- className={cn(
219
- "flex flex-row gap-1",
220
- headerJustify === "center" && "justify-center",
221
- headerJustify === "right" && "justify-end",
222
- )}
223
- >
224
- <span className="text-xs text-muted-foreground">{dtype}</span>
225
- {stats && typeof stats.nulls === "number" && stats.nulls > 0 && (
226
- <span className="text-xs text-muted-foreground">
227
- (nulls: {stats.nulls})
228
- </span>
229
- )}
230
- </div>
231
- ) : null;
232
-
233
- const headerName = (
234
- <span
199
+ const columns = columnKeys.map((key, idx): ColumnDef<T> => ({
200
+ id: key || `${NAMELESS_COLUMN_PREFIX}${idx}`,
201
+ // Use an accessorFn instead of an accessorKey because column names
202
+ // may have periods in them ...
203
+ // https://github.com/TanStack/table/issues/1671
204
+ accessorFn: (row) => {
205
+ return row[key as keyof T];
206
+ },
207
+ enableHiding: !rowHeadersSet.has(key) && key !== "",
208
+ header: ({ column, table }) => {
209
+ const stats = chartSpecModel?.getColumnStats(key);
210
+ const dtype = column.columnDef.meta?.dtype;
211
+ const headerTitle = headerTooltip?.[key];
212
+ const headerJustify = textJustifyColumns?.[key];
213
+
214
+ const dtypeHeader =
215
+ showDataTypes && dtype ? (
216
+ <div
235
217
  className={cn(
236
- "font-bold",
237
- headerTitle && "underline decoration-dotted",
218
+ "flex flex-row gap-1",
219
+ headerJustify === "center" && "justify-center",
220
+ headerJustify === "right" && "justify-end",
238
221
  )}
239
222
  >
240
- {key === "" ? " " : key}
241
- </span>
242
- );
223
+ <span className="text-xs text-muted-foreground">{dtype}</span>
224
+ {stats && typeof stats.nulls === "number" && stats.nulls > 0 && (
225
+ <span className="text-xs text-muted-foreground">
226
+ (nulls: {stats.nulls})
227
+ </span>
228
+ )}
229
+ </div>
230
+ ) : null;
243
231
 
244
- const headerWithTooltip = headerTitle ? (
245
- <Tooltip content={headerTitle} delayDuration={300}>
246
- {headerName}
247
- </Tooltip>
248
- ) : (
249
- headerName
250
- );
232
+ const headerName = (
233
+ <span
234
+ className={cn(
235
+ "font-bold",
236
+ headerTitle && "underline decoration-dotted",
237
+ )}
238
+ >
239
+ {key === "" ? " " : key}
240
+ </span>
241
+ );
251
242
 
252
- const dataTableColumnHeader = (
253
- <DataTableColumnHeader
254
- header={headerWithTooltip}
255
- subheader={dtypeHeader}
256
- column={column}
257
- justify={headerJustify}
258
- calculateTopKRows={calculateTopKRows}
259
- table={table}
260
- />
261
- );
243
+ const headerWithTooltip = headerTitle ? (
244
+ <Tooltip content={headerTitle} delayDuration={300}>
245
+ {headerName}
246
+ </Tooltip>
247
+ ) : (
248
+ headerName
249
+ );
262
250
 
263
- // Row headers have no summaries
264
- if (rowHeadersSet.has(key)) {
265
- return dataTableColumnHeader;
266
- }
251
+ const dataTableColumnHeader = (
252
+ <DataTableColumnHeader
253
+ header={headerWithTooltip}
254
+ subheader={dtypeHeader}
255
+ column={column}
256
+ justify={headerJustify}
257
+ calculateTopKRows={calculateTopKRows}
258
+ table={table}
259
+ />
260
+ );
267
261
 
268
- return (
269
- <div
270
- className={cn(
271
- "flex flex-col h-full pt-0.5 pb-3 justify-between items-start",
272
- headerJustify === "center" && "items-center",
273
- headerJustify === "right" && "items-end",
274
- )}
275
- >
276
- {dataTableColumnHeader}
277
- <TableColumnSummary columnId={key} />
278
- </div>
279
- );
280
- },
262
+ // Row headers have no summaries
263
+ if (rowHeadersSet.has(key)) {
264
+ return dataTableColumnHeader;
265
+ }
281
266
 
282
- cell: ({ column, renderValue, getValue, cell }) => {
283
- function selectCell() {
284
- if (selection !== "single-cell" && selection !== "multi-cell") {
285
- return;
286
- }
267
+ return (
268
+ <div
269
+ className={cn(
270
+ "flex flex-col h-full pt-0.5 pb-3 justify-between items-start",
271
+ headerJustify === "center" && "items-center",
272
+ headerJustify === "right" && "items-end",
273
+ )}
274
+ >
275
+ {dataTableColumnHeader}
276
+ <TableColumnSummary columnId={key} />
277
+ </div>
278
+ );
279
+ },
287
280
 
288
- cell.toggleSelected?.();
281
+ cell: ({ column, renderValue, getValue, cell }) => {
282
+ function selectCell() {
283
+ if (selection !== "single-cell" && selection !== "multi-cell") {
284
+ return;
289
285
  }
290
286
 
291
- const justify = getJustify(key);
292
- const wrapped = wrappedColumns?.includes(key);
293
- const isCellSelected = cell?.getIsSelected?.() || false;
294
- const canSelectCell =
295
- (selection === "single-cell" || selection === "multi-cell") &&
296
- !isCellSelected;
297
-
298
- const dataType = column.columnDef.meta?.dataType;
299
- const isNumeric = isNumericType(dataType);
300
- const cellStyles = getCellStyleClass({
301
- justify,
302
- wrapped,
303
- canSelectCell,
304
- isSelected: isCellSelected,
305
- isNumeric,
306
- });
307
-
308
- const renderedCell = renderCellValue({
309
- column,
310
- renderValue,
311
- getValue,
312
- selectCell,
313
- cellStyles,
314
- });
315
-
316
- // Row headers are bold
317
- if (rowHeadersSet.has(key)) {
318
- return <b>{renderedCell}</b>;
319
- }
287
+ cell.toggleSelected?.();
288
+ }
320
289
 
321
- return renderedCell;
322
- },
323
- // Remove any default filtering
324
- filterFn: undefined,
325
- // Unnamed index columns and geometry columns are not sortable
326
- enableSorting: !!key && getMeta(key).dataType !== "geometry",
327
- meta: {
328
- ...getMeta(key),
329
- width: columnWidths?.[key],
330
- },
331
- // size seeds the width before measurement; minSize/maxSize pin
332
- // getSize() to the fixed width so the per-paint measurement writeback
333
- // cannot drift the header width or sticky-pin offsets.
334
- ...(columnWidths?.[key] !== undefined
335
- ? {
336
- size: columnWidths[key],
337
- minSize: columnWidths[key],
338
- maxSize: columnWidths[key],
339
- }
340
- : {}),
341
- }),
342
- );
290
+ const justify = getJustify(key);
291
+ const wrapped = wrappedColumns?.includes(key);
292
+ const isCellSelected = cell?.getIsSelected?.() || false;
293
+ const canSelectCell =
294
+ (selection === "single-cell" || selection === "multi-cell") &&
295
+ !isCellSelected;
296
+
297
+ const dataType = column.columnDef.meta?.dataType;
298
+ const isNumeric = isNumericType(dataType);
299
+ const cellStyles = getCellStyleClass({
300
+ justify,
301
+ wrapped,
302
+ canSelectCell,
303
+ isSelected: isCellSelected,
304
+ isNumeric,
305
+ });
306
+
307
+ const renderedCell = renderCellValue({
308
+ column,
309
+ renderValue,
310
+ getValue,
311
+ selectCell,
312
+ cellStyles,
313
+ });
314
+
315
+ // Row headers are bold
316
+ if (rowHeadersSet.has(key)) {
317
+ return <b>{renderedCell}</b>;
318
+ }
319
+
320
+ return renderedCell;
321
+ },
322
+ // Remove any default filtering
323
+ filterFn: undefined,
324
+ // Unnamed index columns and geometry columns are not sortable
325
+ enableSorting: !!key && getMeta(key).dataType !== "geometry",
326
+ meta: {
327
+ ...getMeta(key),
328
+ width: columnWidths?.[key],
329
+ },
330
+ // size seeds the width before measurement; minSize/maxSize pin
331
+ // getSize() to the fixed width so the per-paint measurement writeback
332
+ // cannot drift the header width or sticky-pin offsets.
333
+ ...(columnWidths?.[key] !== undefined
334
+ ? {
335
+ size: columnWidths[key],
336
+ minSize: columnWidths[key],
337
+ maxSize: columnWidths[key],
338
+ }
339
+ : {}),
340
+ }));
343
341
 
344
342
  if (selection === "single" || selection === "multi") {
345
343
  columns.unshift({
@@ -3,7 +3,7 @@
3
3
  import { CommandList } from "cmdk";
4
4
  import { atom, useAtom, useAtomValue, useSetAtom } from "jotai";
5
5
  import { atomWithStorage } from "jotai/utils";
6
- import { PlusIcon, PlusSquareIcon, XIcon } from "lucide-react";
6
+ import { PlusSquareIcon, XIcon } from "lucide-react";
7
7
  import React from "react";
8
8
  import { dbDisplayName } from "@/components/databases/display";
9
9
  import { EngineVariable } from "@/components/databases/engine-variable";
@@ -64,7 +64,7 @@ import { ErrorBoundary } from "../editor/boundary/ErrorBoundary";
64
64
  import { PythonIcon } from "../editor/cell/code/icons";
65
65
  import { useAddCodeToNewCell } from "../editor/cell/useAddCell";
66
66
  import { PanelEmptyState } from "../editor/chrome/panels/empty-state";
67
- import { AddConnectionDialog } from "../editor/connections/add-connection-dialog";
67
+ import { AddConnectionButton } from "../editor/connections/add-connection-button";
68
68
  import { DatasetColumnPreview } from "./column-preview";
69
69
  import {
70
70
  ColumnName,
@@ -250,12 +250,12 @@ export const DataSources: React.FC = () => {
250
250
  title="No tables found"
251
251
  description="Any datasets/dataframes in the global scope will be shown here."
252
252
  action={
253
- <AddConnectionDialog>
254
- <Button variant="outline" size="sm">
255
- Add database or catalog
256
- <PlusIcon className="h-4 w-4 ml-2" />
257
- </Button>
258
- </AddConnectionDialog>
253
+ <AddConnectionButton
254
+ group="database"
255
+ label="Add database or catalog"
256
+ variant="outline"
257
+ size="sm"
258
+ />
259
259
  }
260
260
  icon={<DatabaseIcon />}
261
261
  />
@@ -301,15 +301,14 @@ export const DataSources: React.FC = () => {
301
301
  className="px-2 rounded-none focus-visible:outline-hidden"
302
302
  />
303
303
 
304
- <AddConnectionDialog>
305
- <Button
306
- variant="ghost"
307
- size="sm"
308
- className="px-2 rounded-none focus-visible:outline-hidden"
309
- >
310
- <PlusIcon className="h-4 w-4" />
311
- </Button>
312
- </AddConnectionDialog>
304
+ <AddConnectionButton
305
+ group="database"
306
+ label="Add database or catalog"
307
+ compact={true}
308
+ variant="ghost"
309
+ size="sm"
310
+ className="px-2 rounded-none focus-visible:outline-hidden"
311
+ />
313
312
  </div>
314
313
 
315
314
  <CommandList className="flex flex-col">
@@ -73,6 +73,7 @@ import { useRequestClient } from "@/core/network/requests";
73
73
  import { useFilename } from "@/core/saving/filename";
74
74
  import { createShareableLink } from "@/core/wasm/share";
75
75
  import { isWasm } from "@/core/wasm/utils";
76
+ import { useDetectedDataSources } from "@/hooks/useDataSourceDiscovery";
76
77
  import { copyToClipboard } from "@/utils/copy";
77
78
  import { Objects } from "@/utils/objects";
78
79
  import { Strings } from "@/utils/strings";
@@ -81,6 +82,7 @@ import { useRunAllCells } from "../cell/useRunCells";
81
82
  import { useChromeActions, useChromeState } from "../chrome/state";
82
83
  import { isPanelHidden, PANELS } from "../chrome/types";
83
84
  import { AddConnectionDialogContent } from "../connections/add-connection-dialog";
85
+ import { useAddDetectedDataSource } from "../connections/components";
84
86
  import { keyboardShortcutsAtom } from "../controls/keyboard-shortcuts";
85
87
  import { commandPaletteAtom } from "../controls/state";
86
88
  import { displayLayoutName, getLayoutIcon } from "../renderers/layout-select";
@@ -135,6 +137,9 @@ export function useNotebookActions({
135
137
  const setKeyboardShortcutsOpen = useSetAtom(keyboardShortcutsAtom);
136
138
  const setExportOptions = useSetAtom(exportOptionsAtom);
137
139
  const { readCode, saveCellConfig } = useRequestClient();
140
+ const detectedDatabaseSources = useDetectedDataSources("database");
141
+ const detectedStorageSources = useDetectedDataSources("storage");
142
+ const addDetectedDataSource = useAddDetectedDataSource();
138
143
 
139
144
  const hasDisabledCells = useAtomValue(hasDisabledCellsAtom);
140
145
  const canUndoDeletes = useAtomValue(canUndoDeletesAtom);
@@ -455,6 +460,27 @@ export function useNotebookActions({
455
460
  handle: () => {
456
461
  openModal(<AddConnectionDialogContent onClose={closeModal} />);
457
462
  },
463
+ dropdown:
464
+ detectedDatabaseSources.length === 0
465
+ ? undefined
466
+ : [
467
+ ...detectedDatabaseSources.map((source) => ({
468
+ icon: <SparklesIcon size={14} strokeWidth={1.5} />,
469
+ label: `Add ${source.displayName}`,
470
+ description: "Detected in your environment",
471
+ handle: () => addDetectedDataSource(source),
472
+ })),
473
+ {
474
+ divider: true,
475
+ icon: <DatabaseIcon size={14} strokeWidth={1.5} />,
476
+ label: "Browse all connections",
477
+ handle: () => {
478
+ openModal(
479
+ <AddConnectionDialogContent onClose={closeModal} />,
480
+ );
481
+ },
482
+ },
483
+ ],
458
484
  },
459
485
  {
460
486
  icon: <HardDrive size={14} strokeWidth={1.5} />,
@@ -467,6 +493,30 @@ export function useNotebookActions({
467
493
  />,
468
494
  );
469
495
  },
496
+ dropdown:
497
+ detectedStorageSources.length === 0
498
+ ? undefined
499
+ : [
500
+ ...detectedStorageSources.map((source) => ({
501
+ icon: <SparklesIcon size={14} strokeWidth={1.5} />,
502
+ label: `Add ${source.displayName}`,
503
+ description: "Detected in your environment",
504
+ handle: () => addDetectedDataSource(source),
505
+ })),
506
+ {
507
+ divider: true,
508
+ icon: <HardDrive size={14} strokeWidth={1.5} />,
509
+ label: "Browse all connections",
510
+ handle: () => {
511
+ openModal(
512
+ <AddConnectionDialogContent
513
+ defaultTab="storage"
514
+ onClose={closeModal}
515
+ />,
516
+ );
517
+ },
518
+ },
519
+ ],
470
520
  },
471
521
  {
472
522
  icon: <Undo2Icon size={14} strokeWidth={1.5} />,
@@ -105,7 +105,7 @@ const DiscoveredSourcesBadge = ({
105
105
  return (
106
106
  <Tooltip content={content}>
107
107
  <span>
108
- <PanelBadge className="gap-0.5 border-(--blue-6) bg-(--blue-3) text-(--blue-11)">
108
+ <PanelBadge className="gap-0.5 border-(--blue-6) bg-(--blue-3) text-(--blue-11) hover:bg-(--blue-4) dark:border-(--blue-8) dark:bg-(--blue-3) dark:hover:bg-(--blue-4)">
109
109
  <SparklesIcon className="h-2.5 w-2.5" />
110
110
  {count}
111
111
  </PanelBadge>