@marimo-team/islands 0.23.10-dev16 → 0.23.10-dev18

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.
@@ -35274,7 +35274,7 @@ ${d}`,
35274
35274
  return Logger.warn("Failed to get version from mount config"), null;
35275
35275
  }
35276
35276
  }
35277
- marimoVersionAtom = atom(getVersionFromMountConfig() || "0.23.10-dev16");
35277
+ marimoVersionAtom = atom(getVersionFromMountConfig() || "0.23.10-dev18");
35278
35278
  showCodeInRunModeAtom = atom(true);
35279
35279
  atom(null);
35280
35280
  var import_compiler_runtime = require_compiler_runtime();
package/dist/main.js CHANGED
@@ -26,7 +26,7 @@ import { $ as useCellActions, An as LoaderCircle, At as DeferredRequestRegistry,
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-hyWa1mOg.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-CsR8644L.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";
@@ -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-DMJJplJ7.js"));
36119
+ var LazySlidesComponent = import_react.lazy(() => import("./reveal-component-IUUJ4Dzx.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), [
@@ -9,7 +9,7 @@ import { t as require_compiler_runtime } from "./compiler-runtime-CEbnTgxf.js";
9
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-hyWa1mOg.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-CsR8644L.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-dev16",
3
+ "version": "0.23.10-dev18",
4
4
  "main": "dist/main.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -84,9 +84,7 @@ export const VisibilityToggleButton: React.FC<{
84
84
  /** Whether the optional items are currently visible. */
85
85
  isVisible: boolean;
86
86
  onToggle: () => void;
87
- /** Tooltip shown while items are hidden (clicking will show them). */
88
87
  showTooltip: string;
89
- /** Tooltip shown while items are visible (clicking will hide them). */
90
88
  hideTooltip: string;
91
89
  size?: ButtonProps["size"];
92
90
  className?: string;
@@ -0,0 +1,53 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+ import { describe, expect, it } from "vitest";
3
+ import type { StorageEntry } from "@/core/storage/types";
4
+ import { storageEntryKey } from "../storage-inspector";
5
+
6
+ function makeEntry(
7
+ overrides: Partial<StorageEntry> & { path: string },
8
+ ): StorageEntry {
9
+ return {
10
+ kind: overrides.kind ?? "file",
11
+ lastModified: overrides.lastModified ?? null,
12
+ metadata: overrides.metadata ?? {},
13
+ path: overrides.path,
14
+ size: overrides.size ?? 0,
15
+ };
16
+ }
17
+
18
+ describe("storageEntryKey", () => {
19
+ it("prefers the backend id when present (e.g. Google Drive)", () => {
20
+ const entry = makeEntry({
21
+ path: "Data Resume.pdf",
22
+ metadata: { id: "drive-file-id-123" },
23
+ });
24
+ // Two files can share a path on Drive; the id keeps keys unique.
25
+ expect(storageEntryKey(entry, 0)).toBe("drive-file-id-123");
26
+ expect(storageEntryKey(entry, 4)).toBe("drive-file-id-123");
27
+ });
28
+
29
+ it("falls back to path + index when there is no id", () => {
30
+ const entry = makeEntry({ path: "Data Resume.pdf" });
31
+ expect(storageEntryKey(entry, 0)).toBe("Data Resume.pdf::0");
32
+ expect(storageEntryKey(entry, 4)).toBe("Data Resume.pdf::4");
33
+ });
34
+
35
+ it("keeps duplicate-path entries unique via the index fallback", () => {
36
+ const entries = [
37
+ makeEntry({ path: "Data Resume.pdf" }),
38
+ makeEntry({ path: "Data Resume.pdf" }),
39
+ makeEntry({ path: "Data Resume.pdf" }),
40
+ ];
41
+ const keys = entries.map((entry, index) => storageEntryKey(entry, index));
42
+ expect(new Set(keys).size).toBe(entries.length);
43
+ });
44
+
45
+ it("ignores a non-string or empty id", () => {
46
+ expect(
47
+ storageEntryKey(makeEntry({ path: "a.pdf", metadata: { id: "" } }), 2),
48
+ ).toBe("a.pdf::2");
49
+ expect(
50
+ storageEntryKey(makeEntry({ path: "a.pdf", metadata: { id: 5 } }), 2),
51
+ ).toBe("a.pdf::2");
52
+ });
53
+ });
@@ -94,15 +94,31 @@ function displayName(path: string): string {
94
94
  return parts[parts.length - 1] || trimmed;
95
95
  }
96
96
 
97
+ /**
98
+ * Stable, unique identity for an entry row. Prefer the
99
+ * backend's stable id when present and fall back to the list index
100
+ */
101
+ export function storageEntryKey(entry: StorageEntry, index: number): string {
102
+ const id = entry.metadata?.id;
103
+ if (typeof id === "string" && id.length > 0) {
104
+ return id;
105
+ }
106
+ return `${entry.path}::${index}`;
107
+ }
108
+
109
+ interface SearchContext {
110
+ namespace: string;
111
+ searchValue: string;
112
+ entriesByPath: ReadonlyMap<StoragePathKey, StorageEntry[]>;
113
+ }
114
+
97
115
  /**
98
116
  * Recursively check whether an entry (or any of its loaded descendants)
99
117
  * matches the search query.
100
118
  */
101
119
  function entryMatchesSearch(
102
120
  entry: StorageEntry,
103
- namespace: string,
104
- searchValue: string,
105
- entriesByPath: ReadonlyMap<StoragePathKey, StorageEntry[]>,
121
+ { namespace, searchValue, entriesByPath }: SearchContext,
106
122
  ): boolean {
107
123
  const query = searchValue.toLowerCase();
108
124
 
@@ -115,7 +131,7 @@ function entryMatchesSearch(
115
131
  const children = entriesByPath.get(storagePathKey(namespace, entry.path));
116
132
  if (children) {
117
133
  return children.some((child) =>
118
- entryMatchesSearch(child, namespace, searchValue, entriesByPath),
134
+ entryMatchesSearch(child, { namespace, searchValue, entriesByPath }),
119
135
  );
120
136
  }
121
137
  }
@@ -129,16 +145,12 @@ function entryMatchesSearch(
129
145
  */
130
146
  function filterEntries(
131
147
  entries: StorageEntry[],
132
- namespace: string,
133
- searchValue: string,
134
- entriesByPath: ReadonlyMap<StoragePathKey, StorageEntry[]>,
148
+ context: SearchContext,
135
149
  ): StorageEntry[] {
136
- if (!searchValue.trim()) {
150
+ if (!context.searchValue.trim()) {
137
151
  return entries;
138
152
  }
139
- return entries.filter((entry) =>
140
- entryMatchesSearch(entry, namespace, searchValue, entriesByPath),
141
- );
153
+ return entries.filter((entry) => entryMatchesSearch(entry, context));
142
154
  }
143
155
 
144
156
  /**
@@ -204,35 +216,39 @@ const StorageEntryChildren: React.FC<{
204
216
  );
205
217
  }
206
218
 
207
- const filtered = filterEntries(
208
- children,
219
+ const filtered = filterEntries(children, {
209
220
  namespace,
210
221
  searchValue,
211
222
  entriesByPath,
212
- );
223
+ });
213
224
 
214
225
  return (
215
226
  <>
216
- {filtered.map((child) => (
217
- <StorageEntryRow
218
- key={child.path}
219
- entry={child}
220
- namespace={namespace}
221
- protocol={protocol}
222
- rootPath={rootPath}
223
- backendType={backendType}
224
- depth={depth}
225
- locale={locale}
226
- searchValue={searchValue}
227
- onOpenFile={onOpenFile}
228
- />
229
- ))}
227
+ {filtered.map((child) => {
228
+ const rowKey = storageEntryKey(child, children.indexOf(child));
229
+ return (
230
+ <StorageEntryRow
231
+ key={rowKey}
232
+ rowKey={rowKey}
233
+ entry={child}
234
+ namespace={namespace}
235
+ protocol={protocol}
236
+ rootPath={rootPath}
237
+ backendType={backendType}
238
+ depth={depth}
239
+ locale={locale}
240
+ searchValue={searchValue}
241
+ onOpenFile={onOpenFile}
242
+ />
243
+ );
244
+ })}
230
245
  </>
231
246
  );
232
247
  };
233
248
 
234
249
  const StorageEntryRow: React.FC<{
235
250
  entry: StorageEntry;
251
+ rowKey: string;
236
252
  namespace: string;
237
253
  protocol: string;
238
254
  rootPath: string;
@@ -243,6 +259,7 @@ const StorageEntryRow: React.FC<{
243
259
  onOpenFile: (info: OpenFileInfo) => void;
244
260
  }> = ({
245
261
  entry,
262
+ rowKey,
246
263
  namespace,
247
264
  protocol,
248
265
  rootPath,
@@ -271,7 +288,7 @@ const StorageEntryRow: React.FC<{
271
288
  !!entriesByPath
272
289
  .get(storagePathKey(namespace, entry.path))
273
290
  ?.some((child) =>
274
- entryMatchesSearch(child, namespace, searchValue, entriesByPath),
291
+ entryMatchesSearch(child, { namespace, searchValue, entriesByPath }),
275
292
  );
276
293
 
277
294
  // Folder is shown expanded by manual toggle OR by search auto-expand
@@ -312,7 +329,7 @@ const StorageEntryRow: React.FC<{
312
329
  isDir && "font-medium",
313
330
  )}
314
331
  style={indentStyle(depth)}
315
- value={`${namespace}:${entry.path}`}
332
+ value={`${namespace}:${rowKey}`}
316
333
  onSelect={() => {
317
334
  if (isDir) {
318
335
  setIsExpanded(!effectiveExpanded);
@@ -458,12 +475,11 @@ const StorageNamespaceSection: React.FC<{
458
475
 
459
476
  // While loading, fall back to initial entries from the namespace notification
460
477
  const entries = isPending ? namespace.storageEntries : fetchedEntries;
461
- const filtered = filterEntries(
462
- entries,
463
- namespaceName,
478
+ const filtered = filterEntries(entries, {
479
+ namespace: namespaceName,
464
480
  searchValue,
465
481
  entriesByPath,
466
- );
482
+ });
467
483
 
468
484
  return (
469
485
  <>
@@ -525,20 +541,24 @@ const StorageNamespaceSection: React.FC<{
525
541
  No matches
526
542
  </div>
527
543
  )}
528
- {filtered.map((entry) => (
529
- <StorageEntryRow
530
- key={entry.path}
531
- entry={entry}
532
- namespace={namespaceName}
533
- protocol={namespace.protocol}
534
- rootPath={namespace.rootPath}
535
- backendType={namespace.backendType}
536
- depth={1}
537
- locale={locale}
538
- searchValue={searchValue}
539
- onOpenFile={onOpenFile}
540
- />
541
- ))}
544
+ {filtered.map((entry) => {
545
+ const rowKey = storageEntryKey(entry, entries.indexOf(entry));
546
+ return (
547
+ <StorageEntryRow
548
+ key={rowKey}
549
+ rowKey={rowKey}
550
+ entry={entry}
551
+ namespace={namespaceName}
552
+ protocol={namespace.protocol}
553
+ rootPath={namespace.rootPath}
554
+ backendType={namespace.backendType}
555
+ depth={1}
556
+ locale={locale}
557
+ searchValue={searchValue}
558
+ onOpenFile={onOpenFile}
559
+ />
560
+ );
561
+ })}
542
562
  </>
543
563
  )}
544
564
  </>