@marimo-team/islands 0.23.9-dev7 → 0.23.9-dev8

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.
@@ -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-CiSinpSR.js";
10
10
  import "./chunk-5FQGJX7Z-BNjes6Yx.js";
11
11
  import { u as createLucideIcon } from "./dist-C1BYNeCR.js";
12
- import { G as PanelGroup, Gt as Code, Ht as Expand, K as PanelResizeHandle, Vt as EyeOff, W as Panel, a as DEFAULT_SLIDE_TYPE, c as Slide, i as DEFAULT_DECK_TRANSITION, s as SlideSidebar, t as useNotebookCodeAvailable } from "./code-visibility-ydMmCkzY.js";
12
+ import { G as Panel, Ht as EyeOff, K as PanelGroup, Kt as Code, Ut as Expand, a as DEFAULT_SLIDE_TYPE, c as Slide, i as DEFAULT_DECK_TRANSITION, q as PanelResizeHandle, s as SlideSidebar, t as useNotebookCodeAvailable } from "./code-visibility-SqsoLwxQ.js";
13
13
  import { q as useDebouncedCallback } from "./input-CZD2z6X2.js";
14
14
  import "./toDate-ZVVIBmdk.js";
15
15
  import "./react-dom-BTJzcVJ9.js";
@@ -7223,7 +7223,7 @@ var NotesAside = (e5) => {
7223
7223
  t[18] === s ? p = t[19] : (p = s && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(NotesAside, { text: s }), t[18] = s, t[19] = p);
7224
7224
  let io;
7225
7225
  return t[20] !== o || t[21] !== ro || t[22] !== p ? (io = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(o, { children: [ro, p] }), t[20] = o, t[21] = ro, t[22] = p, t[23] = io) : io = t[23], io;
7226
- }, reveal_component_default = ({ cellsWithOutput: e5, layout: o, setLayout: s, activeIndex: l, onSlideChange: ao, mode: so, configWidth: uo = 300, isEditable: fo = false }) => {
7226
+ }, reveal_component_default = ({ cellsWithOutput: e5, layout: o, setLayout: s, activeIndex: l, onSlideChange: io, mode: oo, configWidth: uo = 300, isEditable: fo = false }) => {
7227
7227
  var _a2, _b2;
7228
7228
  let _o = (0, import_react.useRef)(null), vo = (0, import_react.useRef)(null), { width: bo, height: xo } = useSlideDimensions(_o), So = useAtomValue(kioskModeAtom), Co = (0, import_react.useMemo)(() => So ? [] : [ce], [So]), [wo, To] = (0, import_react.useState)(false), Eo = useNotebookCodeAvailable(e5), Do = !isIslands() && Eo, Oo = Do && wo, ko = l == null ? void 0 : e5[l], Ao = ko ?? e5.at(0), jo = (0, import_react.useMemo)(() => composeSlides({
7229
7229
  cells: e5,
@@ -7294,14 +7294,14 @@ var NotesAside = (e5) => {
7294
7294
  let e6 = vo.current;
7295
7295
  if (!e6) return;
7296
7296
  let t = resolveActiveCellIndex(Po, e6.getIndices());
7297
- t != null && (ao == null ? void 0 : ao(t));
7297
+ t != null && (io == null ? void 0 : io(t));
7298
7298
  }), Uo = useEvent_default((t) => {
7299
7299
  if (!Mo || l == null || Events.fromInput(t)) return;
7300
7300
  let n = classifyNavKey(t);
7301
7301
  if (n === 0) return;
7302
7302
  t.preventDefault(), t.stopPropagation();
7303
7303
  let i = l + n;
7304
- i < 0 || i >= e5.length || (ao == null ? void 0 : ao(i));
7304
+ i < 0 || i >= e5.length || (io == null ? void 0 : io(i));
7305
7305
  }), Wo = useEvent_default(() => {
7306
7306
  Ho(), triggerResize(vo.current);
7307
7307
  });
@@ -7400,7 +7400,7 @@ var NotesAside = (e5) => {
7400
7400
  ]
7401
7401
  })
7402
7402
  });
7403
- return so === "read" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
7403
+ return oo === "read" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
7404
7404
  className: "flex-1 min-w-0 flex flex-row gap-3",
7405
7405
  children: Go
7406
7406
  }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marimo-team/islands",
3
- "version": "0.23.9-dev7",
3
+ "version": "0.23.9-dev8",
4
4
  "main": "dist/main.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -35,6 +35,7 @@ interface TableTopBarProps extends Partial<ExportActionProps> {
35
35
  togglePanel?: (panelType: PanelType) => void;
36
36
  isAnyPanelOpen?: boolean;
37
37
  sizeBytes?: number | null;
38
+ sizeBytesIsLoading?: boolean;
38
39
  }
39
40
 
40
41
  export const TableTopBar: React.FC<TableTopBarProps> = ({
@@ -50,6 +51,7 @@ export const TableTopBar: React.FC<TableTopBarProps> = ({
50
51
  isAnyPanelOpen,
51
52
  downloadAs,
52
53
  sizeBytes,
54
+ sizeBytesIsLoading,
53
55
  }) => {
54
56
  const [internalValue, setInternalValue] = useState(searchQuery || "");
55
57
  const debouncedSearch = useDebounce(internalValue, 500);
@@ -133,7 +135,11 @@ export const TableTopBar: React.FC<TableTopBarProps> = ({
133
135
  </Button>
134
136
  )}
135
137
  {downloadAs && (
136
- <ExportMenu downloadAs={downloadAs} sizeBytes={sizeBytes} />
138
+ <ExportMenu
139
+ downloadAs={downloadAs}
140
+ sizeBytes={sizeBytes}
141
+ sizeBytesIsLoading={sizeBytesIsLoading}
142
+ />
137
143
  )}
138
144
  </div>
139
145
  </div>
@@ -86,6 +86,7 @@ interface DataTableProps<TData> extends Partial<ExportActionProps> {
86
86
  // JSON-serialized size of the currently-rendered data. Forwarded to
87
87
  // ExportMenu so hosts can size-gate the Export button via downloadSizeLimitAtom.
88
88
  sizeBytes?: number | null;
89
+ sizeBytesIsLoading?: boolean;
89
90
  totalColumns: number;
90
91
  pagination?: boolean;
91
92
  manualPagination?: boolean; // server-side pagination
@@ -139,6 +140,7 @@ const DataTableInternal = <TData,>({
139
140
  totalColumns,
140
141
  totalRows,
141
142
  sizeBytes,
143
+ sizeBytesIsLoading,
142
144
  manualSorting = false,
143
145
  sorting,
144
146
  setSorting,
@@ -362,6 +364,7 @@ const DataTableInternal = <TData,>({
362
364
  isAnyPanelOpen={isAnyPanelOpen}
363
365
  downloadAs={downloadAs}
364
366
  sizeBytes={sizeBytes}
367
+ sizeBytesIsLoading={sizeBytesIsLoading}
365
368
  />
366
369
  {allUserColumnsHidden && (
367
370
  <Banner className="mb-1 mx-2 rounded flex items-center justify-between">
@@ -91,6 +91,7 @@ export interface ExportActionProps {
91
91
  // marimo-lsp inside VS Code) declares a download size cap. Null/undefined
92
92
  // means "no info" and the gate stays disabled (fail-open).
93
93
  sizeBytes?: number | null;
94
+ sizeBytesIsLoading?: boolean;
94
95
  }
95
96
 
96
97
  const labelForDownloadFormat = (format: DownloadFormat): string =>
@@ -100,13 +101,19 @@ const labelForCopyFormat = (format: CopyFormat): string =>
100
101
 
101
102
  export const ExportMenu: React.FC<ExportActionProps> = (props) => {
102
103
  const { locale } = useLocale();
103
- const [open, setOpen] = React.useState(false);
104
+ const [downloadMenuOpen, setDownloadMenuOpen] = React.useState(false);
104
105
  const policy = useAtomValue(downloadSizeLimitAtom);
105
- const disabled = !!(
106
+ const overLimit = !!(
106
107
  policy &&
107
108
  props.sizeBytes != null &&
108
109
  props.sizeBytes > policy.limitBytes
109
110
  );
111
+ const disabled = !!(policy && (props.sizeBytesIsLoading || overLimit));
112
+ const tooltipContent = !disabled
113
+ ? "Export"
114
+ : props.sizeBytesIsLoading
115
+ ? "Checking download size…"
116
+ : policy?.unavailableMessage;
110
117
 
111
118
  const button = (
112
119
  <Button
@@ -116,7 +123,7 @@ export const ExportMenu: React.FC<ExportActionProps> = (props) => {
116
123
  disabled={disabled}
117
124
  className={cn(
118
125
  "print:hidden text-xs gap-1",
119
- open ? "text-primary" : "text-muted-foreground",
126
+ downloadMenuOpen ? "text-primary" : "text-muted-foreground",
120
127
  )}
121
128
  >
122
129
  <DownloadIcon className="w-3.5 h-3.5" />
@@ -248,10 +255,14 @@ export const ExportMenu: React.FC<ExportActionProps> = (props) => {
248
255
  };
249
256
 
250
257
  return (
251
- <DropdownMenu modal={false} open={open} onOpenChange={setOpen}>
258
+ <DropdownMenu
259
+ modal={false}
260
+ open={downloadMenuOpen}
261
+ onOpenChange={setDownloadMenuOpen}
262
+ >
252
263
  <Tooltip
253
- content={disabled ? policy?.unavailableMessage : "Export"}
254
- open={open ? false : undefined}
264
+ content={tooltipContent}
265
+ open={downloadMenuOpen ? false : undefined}
255
266
  >
256
267
  <DropdownMenuTrigger asChild={true} disabled={disabled}>
257
268
  <span tabIndex={disabled ? 0 : -1} className="inline-flex">
@@ -9,7 +9,7 @@ import type {
9
9
  RowSelectionState,
10
10
  SortingState,
11
11
  } from "@tanstack/react-table";
12
- import { Provider } from "jotai";
12
+ import { Provider, useAtomValue } from "jotai";
13
13
  import { Table2Icon } from "lucide-react";
14
14
  import type { JSX } from "react";
15
15
  import React, {
@@ -28,6 +28,7 @@ import { TablePanel } from "@/components/data-table/charts/charts";
28
28
  import { hasChart } from "@/components/data-table/charts/storage";
29
29
  import { ColumnChartSpecModel } from "@/components/data-table/column-summary/chart-spec-model";
30
30
  import { ColumnChartContext } from "@/components/data-table/column-summary/column-summary";
31
+ import { downloadSizeLimitAtom } from "@/components/data-table/download-policy/atoms";
31
32
  import { filtersToFilterGroup } from "@/components/data-table/filters";
32
33
  import { usePanelOwnership } from "@/components/data-table/hooks/use-panel-ownership";
33
34
  import { LoadingTable } from "@/components/data-table/loading-table";
@@ -195,7 +196,6 @@ interface Data<T> {
195
196
  wrappedColumns?: string[];
196
197
  headerTooltip?: Record<string, string>;
197
198
  totalColumns: number;
198
- sizeBytes?: number | null;
199
199
  maxColumns: number | "all";
200
200
  hasStableRowId: boolean;
201
201
  lazy: boolean;
@@ -222,12 +222,14 @@ type DataTableFunctions = {
222
222
  cell_styles?: CellStyleState | null;
223
223
  cell_hover_texts?: Record<string, Record<string, string | null>> | null;
224
224
  raw_data?: TableData<T> | null;
225
- size_bytes?: number | null;
226
225
  }>;
227
226
  get_data_url?: GetDataUrl;
228
227
  get_row_ids?: GetRowIds;
229
228
  calculate_top_k_rows?: CalculateTopKRows;
230
229
  preview_column?: PreviewColumn;
230
+ get_size_bytes: (opts: Record<string, never>) => Promise<{
231
+ size_bytes?: number | null;
232
+ }>;
231
233
  };
232
234
 
233
235
  type S = (number | string | { rowId: string; columnName?: string })[];
@@ -274,7 +276,6 @@ export const DataTablePlugin = createPlugin<S>("marimo-table")
274
276
  headerTooltip: z.record(z.string(), z.string()).optional(),
275
277
  fieldTypes: columnToFieldTypesSchema.nullish(),
276
278
  totalColumns: z.number(),
277
- sizeBytes: z.number().nullish(),
278
279
  maxColumns: z.union([z.number(), z.literal("all")]).default("all"),
279
280
  hasStableRowId: z.boolean().default(false),
280
281
  maxHeight: z.number().optional(),
@@ -332,7 +333,6 @@ export const DataTablePlugin = createPlugin<S>("marimo-table")
332
333
  .nullable(),
333
334
  cell_hover_texts: cellHoverTextSchema.nullable(),
334
335
  raw_data: z.union([z.string(), z.array(z.looseObject({}))]).nullish(),
335
- size_bytes: z.number().nullish(),
336
336
  }),
337
337
  ),
338
338
  get_row_ids: rpc.input(z.object({}).passthrough()).output(
@@ -364,6 +364,9 @@ export const DataTablePlugin = createPlugin<S>("marimo-table")
364
364
  stats: columnStats.nullable(),
365
365
  }),
366
366
  ),
367
+ get_size_bytes: rpc
368
+ .input(z.object({}))
369
+ .output(z.object({ size_bytes: z.number().nullish() })),
367
370
  })
368
371
  .renderer((props) => {
369
372
  return (
@@ -538,7 +541,6 @@ export const LoadingDataTableComponent = memo(
538
541
  rows: T[];
539
542
  rawRows?: T[];
540
543
  totalRows: number | TooManyRows;
541
- sizeBytes?: number | null;
542
544
  cellStyles: CellStyleState | undefined | null;
543
545
  cellHoverTexts?: Record<string, Record<string, string | null>> | null;
544
546
  }>(async () => {
@@ -555,7 +557,6 @@ export const LoadingDataTableComponent = memo(
555
557
  let tableData = props.data;
556
558
  let rawTableData: TableData<T> | undefined | null = props.rawData;
557
559
  let totalRows = props.totalRows;
558
- let sizeBytes = props.sizeBytes ?? null;
559
560
  let cellStyles = props.cellStyles;
560
561
  let cellHoverTexts = props.cellHoverTexts;
561
562
 
@@ -599,7 +600,6 @@ export const LoadingDataTableComponent = memo(
599
600
  tableData = searchResults.data;
600
601
  rawTableData = searchResults.raw_data;
601
602
  totalRows = searchResults.total_rows;
602
- sizeBytes = searchResults.size_bytes ?? null;
603
603
  cellStyles = searchResults.cell_styles || {};
604
604
  cellHoverTexts = searchResults.cell_hover_texts || {};
605
605
  }
@@ -612,7 +612,6 @@ export const LoadingDataTableComponent = memo(
612
612
  rows: tableData,
613
613
  rawRows: rawData,
614
614
  totalRows: totalRows,
615
- sizeBytes,
616
615
  cellStyles,
617
616
  cellHoverTexts,
618
617
  };
@@ -624,7 +623,6 @@ export const LoadingDataTableComponent = memo(
624
623
  useDeepCompareMemoize(props.fieldTypes),
625
624
  props.data,
626
625
  props.totalRows,
627
- props.sizeBytes,
628
626
  props.lazy,
629
627
  props.cellHoverTexts,
630
628
  props.cellStyles,
@@ -632,6 +630,34 @@ export const LoadingDataTableComponent = memo(
632
630
  paginationState.pageIndex,
633
631
  ]);
634
632
 
633
+ const policy = useAtomValue(downloadSizeLimitAtom);
634
+ const { data: sizeBytesData, isPending: sizeBytesPending } = useAsyncData<
635
+ number | null
636
+ >(async () => {
637
+ if (
638
+ !policy ||
639
+ !props.showDownload ||
640
+ props.lazy ||
641
+ props.totalRows === 0
642
+ ) {
643
+ return null;
644
+ }
645
+ const result = await props.get_size_bytes({});
646
+ return result.size_bytes ?? null;
647
+ }, [
648
+ policy,
649
+ props.showDownload,
650
+ props.get_size_bytes,
651
+ props.lazy,
652
+ props.totalRows,
653
+ searchQuery,
654
+ useDeepCompareMemoize(filters),
655
+ useDeepCompareMemoize(sorting),
656
+ ]);
657
+ const sizeBytes = sizeBytesData ?? null;
658
+ const sizeBytesIsLoading =
659
+ !!policy && props.showDownload && sizeBytesPending;
660
+
635
661
  const getRow = useCallback(
636
662
  async (rowId: number) => {
637
663
  const sortArgs =
@@ -739,7 +765,8 @@ export const LoadingDataTableComponent = memo(
739
765
  setFilters={setFilters}
740
766
  reloading={isFetching && !isPending}
741
767
  totalRows={data?.totalRows ?? props.totalRows}
742
- sizeBytes={data?.sizeBytes ?? props.sizeBytes ?? null}
768
+ sizeBytes={sizeBytes}
769
+ sizeBytesIsLoading={sizeBytesIsLoading}
743
770
  paginationState={paginationState}
744
771
  setPaginationState={setPaginationState}
745
772
  cellStyles={data?.cellStyles ?? props.cellStyles}
@@ -787,6 +814,7 @@ const DataTableComponent = ({
787
814
  rawData,
788
815
  totalRows,
789
816
  sizeBytes,
817
+ sizeBytesIsLoading,
790
818
  maxColumns,
791
819
  pagination,
792
820
  selection,
@@ -838,6 +866,8 @@ const DataTableComponent = ({
838
866
  rawData?: unknown[];
839
867
  columnSummaries?: ColumnSummaries;
840
868
  getRow: (rowIdx: number) => Promise<GetRowResult>;
869
+ sizeBytes?: number | null;
870
+ sizeBytesIsLoading?: boolean;
841
871
  }): JSX.Element => {
842
872
  const id = useId();
843
873
  const [viewedRowIdx, setViewedRowIdx] = useState(0);
@@ -1068,6 +1098,7 @@ const DataTableComponent = ({
1068
1098
  sorting={sorting}
1069
1099
  totalRows={totalRows}
1070
1100
  sizeBytes={sizeBytes}
1101
+ sizeBytesIsLoading={sizeBytesIsLoading}
1071
1102
  totalColumns={totalColumns}
1072
1103
  manualSorting={true}
1073
1104
  setSorting={setSorting}
@@ -107,6 +107,7 @@ describe("LoadingDataTableComponent", () => {
107
107
  }),
108
108
  get_data_url: vi.fn() as GetDataUrl,
109
109
  get_row_ids: vi.fn() as GetRowIds,
110
+ get_size_bytes: vi.fn().mockResolvedValue({ size_bytes: null }),
110
111
  };
111
112
 
112
113
  const Wrapper = ({ children }: { children: React.ReactNode }) => (
@@ -64,7 +64,6 @@ type PluginFunctions = {
64
64
  column_types_per_step: FieldTypesWithExternalType[];
65
65
  python_code?: string | null;
66
66
  sql_code?: string | null;
67
- size_bytes?: number | null;
68
67
  }>;
69
68
  get_column_values: (req: { column: string }) => Promise<{
70
69
  values: unknown[];
@@ -82,9 +81,11 @@ type PluginFunctions = {
82
81
  }) => Promise<{
83
82
  data: TableData<T>;
84
83
  total_rows: number;
85
- size_bytes?: number | null;
86
84
  }>;
87
85
  download_as: DownloadAsArgs;
86
+ get_size_bytes: (opts: Record<string, never>) => Promise<{
87
+ size_bytes?: number | null;
88
+ }>;
88
89
  };
89
90
 
90
91
  // Value is selection, but it is not currently exposed to the user
@@ -120,7 +121,6 @@ export const DataFramePlugin = createPlugin<S>("marimo-dataframe")
120
121
  column_types_per_step: z.array(columnToFieldTypesSchema),
121
122
  python_code: z.string().nullish(),
122
123
  sql_code: z.string().nullish(),
123
- size_bytes: z.number().nullish(),
124
124
  }),
125
125
  ),
126
126
  get_column_values: rpc.input(z.object({ column: z.string() })).output(
@@ -150,10 +150,12 @@ export const DataFramePlugin = createPlugin<S>("marimo-dataframe")
150
150
  z.object({
151
151
  data: z.union([z.string(), z.array(z.object({}).passthrough())]),
152
152
  total_rows: z.number(),
153
- size_bytes: z.number().nullish(),
154
153
  }),
155
154
  ),
156
155
  download_as: DownloadAsSchema,
156
+ get_size_bytes: rpc
157
+ .input(z.object({}))
158
+ .output(z.object({ size_bytes: z.number().nullish() })),
157
159
  })
158
160
  .renderer((props) => (
159
161
  <TableProviders>
@@ -192,6 +194,7 @@ export const DataFrameComponent = memo(
192
194
  get_column_values,
193
195
  search,
194
196
  download_as,
197
+ get_size_bytes,
195
198
  host,
196
199
  }: DataTableProps): JSX.Element => {
197
200
  const { data, error, isPending } = useAsyncData(
@@ -207,7 +210,6 @@ export const DataFrameComponent = memo(
207
210
  column_types_per_step,
208
211
  python_code,
209
212
  sql_code,
210
- size_bytes,
211
213
  } = data || {};
212
214
 
213
215
  const totalColumns = field_types?.length;
@@ -327,7 +329,6 @@ export const DataFrameComponent = memo(
327
329
  data={url || ""}
328
330
  hasStableRowId={false}
329
331
  totalRows={total_rows ?? 0}
330
- sizeBytes={size_bytes ?? null}
331
332
  totalColumns={totalColumns ?? 0}
332
333
  maxColumns="all"
333
334
  pageSize={pageSize}
@@ -336,6 +337,7 @@ export const DataFrameComponent = memo(
336
337
  rowHeaders={row_headers || Arrays.EMPTY}
337
338
  showDownload={showDownload}
338
339
  download_as={download_as}
340
+ get_size_bytes={get_size_bytes}
339
341
  enableSearch={false}
340
342
  showFilters={false}
341
343
  search={search}
@@ -42,6 +42,7 @@ export const DataFrame: StoryObj = {
42
42
  host={document.body}
43
43
  showDownload={false}
44
44
  download_as={async () => ({ url: "", filename: "" })}
45
+ get_size_bytes={async () => ({ size_bytes: null })}
45
46
  lazy={false}
46
47
  />
47
48
  );