@marimo-team/islands 0.20.5-dev74 → 0.20.5-dev76
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/main.js +35 -14
- package/package.json +1 -1
- package/src/core/cells/ids.ts +2 -1
- package/src/core/dom/ui-element-constants.ts +15 -0
- package/src/core/dom/ui-element.ts +3 -2
- package/src/core/islands/components/web-components.tsx +2 -1
- package/src/plugins/impl/DataTablePlugin.tsx +53 -3
- package/src/plugins/impl/FileBrowserPlugin.tsx +4 -1
- package/src/plugins/impl/anywidget/AnyWidgetPlugin.tsx +4 -1
package/dist/main.js
CHANGED
|
@@ -11701,6 +11701,7 @@ ${d.join("\n")}`;
|
|
|
11701
11701
|
function sanitizeBigInt(e) {
|
|
11702
11702
|
return typeof e == "object" && e && "$bigint" in e && typeof e.$bigint == "string" ? BigInt(e.$bigint) : e;
|
|
11703
11703
|
}
|
|
11704
|
+
const OBJECT_ID_ATTR = "object-id", RANDOM_ID_ATTR = "random-id";
|
|
11704
11705
|
var lowercase = "abcdefghijklmnopqrstuvwxyz", alphabet = lowercase + lowercase.toUpperCase(), seen = /* @__PURE__ */ new Set();
|
|
11705
11706
|
const SCRATCH_CELL_ID = "__scratch__", SETUP_CELL_ID = "setup", CellId = {
|
|
11706
11707
|
create() {
|
|
@@ -11740,7 +11741,7 @@ ${d.join("\n")}`;
|
|
|
11740
11741
|
}
|
|
11741
11742
|
const UIElementId = {
|
|
11742
11743
|
parse(e) {
|
|
11743
|
-
return e.getAttribute(
|
|
11744
|
+
return e.getAttribute(OBJECT_ID_ATTR);
|
|
11744
11745
|
},
|
|
11745
11746
|
parseOrThrow(e) {
|
|
11746
11747
|
let r = UIElementId.parse(e);
|
|
@@ -61588,7 +61589,7 @@ ${O}`,
|
|
|
61588
61589
|
}
|
|
61589
61590
|
static get observedAttributes() {
|
|
61590
61591
|
return [
|
|
61591
|
-
|
|
61592
|
+
RANDOM_ID_ATTR
|
|
61592
61593
|
];
|
|
61593
61594
|
}
|
|
61594
61595
|
attributeChangedCallback(e2, r, c) {
|
|
@@ -61969,7 +61970,7 @@ ${O}`,
|
|
|
61969
61970
|
}), r[7] = f2, r[8] = _2), _2;
|
|
61970
61971
|
}
|
|
61971
61972
|
let w;
|
|
61972
|
-
r[9] === e.host ? w = r[10] : (w = (_a3 = e.host.closest(
|
|
61973
|
+
r[9] === e.host ? w = r[10] : (w = (_a3 = e.host.closest(`[${RANDOM_ID_ATTR}]`)) == null ? void 0 : _a3.getAttribute(RANDOM_ID_ATTR), r[9] = e.host, r[10] = w);
|
|
61973
61974
|
let E = w ?? c, O;
|
|
61974
61975
|
return r[11] !== _ || r[12] !== y.default || r[13] !== E || r[14] !== f ? (O = (0, import_jsx_runtime.jsx)(LoadedSlot, {
|
|
61975
61976
|
widget: y.default,
|
|
@@ -70719,7 +70720,7 @@ Image URL: ${r.imageUrl}`)), contextToXml({
|
|
|
70719
70720
|
return Logger.warn("Failed to get version from mount config"), null;
|
|
70720
70721
|
}
|
|
70721
70722
|
}
|
|
70722
|
-
const marimoVersionAtom = atom(getVersionFromMountConfig() || "0.20.5-
|
|
70723
|
+
const marimoVersionAtom = atom(getVersionFromMountConfig() || "0.20.5-dev76"), showCodeInRunModeAtom = atom(true);
|
|
70723
70724
|
atom(null);
|
|
70724
70725
|
var import_compiler_runtime$89 = require_compiler_runtime();
|
|
70725
70726
|
function useKeydownOnElement(e, r) {
|
|
@@ -81049,6 +81050,7 @@ ${c}
|
|
|
81049
81050
|
children: (0, import_jsx_runtime.jsx)(LazyDataTableComponent, {
|
|
81050
81051
|
isLazy: e.data.lazy,
|
|
81051
81052
|
preload: e.data.preload,
|
|
81053
|
+
host: e.host,
|
|
81052
81054
|
children: (0, import_jsx_runtime.jsx)(LoadingDataTableComponent, {
|
|
81053
81055
|
...e.data,
|
|
81054
81056
|
...e.functions,
|
|
@@ -81061,24 +81063,43 @@ ${c}
|
|
|
81061
81063
|
})
|
|
81062
81064
|
})
|
|
81063
81065
|
}));
|
|
81066
|
+
var previewedTables = /* @__PURE__ */ new Map();
|
|
81067
|
+
function wasTablePreviewed(e, r) {
|
|
81068
|
+
return e != null && r != null && previewedTables.get(e) === r;
|
|
81069
|
+
}
|
|
81070
|
+
function markTablePreviewed(e, r) {
|
|
81071
|
+
e != null && r != null && previewedTables.set(e, r);
|
|
81072
|
+
}
|
|
81064
81073
|
var LazyDataTableComponent = (e) => {
|
|
81065
|
-
|
|
81066
|
-
|
|
81074
|
+
var _a3;
|
|
81075
|
+
let r = (0, import_compiler_runtime$58.c)(13), { isLazy: c, children: d, preload: f, host: _ } = e, v, y, S, w;
|
|
81076
|
+
if (r[0] !== _ || r[1] !== c || r[2] !== f) {
|
|
81077
|
+
let e2 = _.closest(`[${OBJECT_ID_ATTR}]`);
|
|
81078
|
+
w = e2 ? UIElementId.parse(e2) : null, v = (_a3 = _.closest(`[${RANDOM_ID_ATTR}]`)) == null ? void 0 : _a3.getAttribute(RANDOM_ID_ATTR), y = import_react.useState, S = c && !f && !wasTablePreviewed(w, v), r[0] = _, r[1] = c, r[2] = f, r[3] = v, r[4] = y, r[5] = S, r[6] = w;
|
|
81079
|
+
} else v = r[3], y = r[4], S = r[5], w = r[6];
|
|
81080
|
+
let [E, O] = y(S);
|
|
81081
|
+
if (E) {
|
|
81067
81082
|
let e2;
|
|
81068
|
-
|
|
81083
|
+
r[7] !== v || r[8] !== w ? (e2 = () => {
|
|
81084
|
+
markTablePreviewed(w, v), O(false);
|
|
81085
|
+
}, r[7] = v, r[8] = w, r[9] = e2) : e2 = r[9];
|
|
81086
|
+
let c2;
|
|
81087
|
+
r[10] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel") ? (c2 = (0, import_jsx_runtime.jsx)(Table2, {
|
|
81088
|
+
className: "mr-2 h-4 w-4"
|
|
81089
|
+
}), r[10] = c2) : c2 = r[10];
|
|
81090
|
+
let d2;
|
|
81091
|
+
return r[11] === e2 ? d2 = r[12] : (d2 = (0, import_jsx_runtime.jsx)("div", {
|
|
81069
81092
|
className: "flex h-20 items-center justify-center",
|
|
81070
81093
|
children: (0, import_jsx_runtime.jsxs)(Button, {
|
|
81071
81094
|
variant: "outline",
|
|
81072
81095
|
size: "xs",
|
|
81073
|
-
onClick:
|
|
81096
|
+
onClick: e2,
|
|
81074
81097
|
children: [
|
|
81075
|
-
|
|
81076
|
-
className: "mr-2 h-4 w-4"
|
|
81077
|
-
}),
|
|
81098
|
+
c2,
|
|
81078
81099
|
"Preview data"
|
|
81079
81100
|
]
|
|
81080
81101
|
})
|
|
81081
|
-
}), r[
|
|
81102
|
+
}), r[11] = e2, r[12] = d2), d2;
|
|
81082
81103
|
}
|
|
81083
81104
|
return d;
|
|
81084
81105
|
};
|
|
@@ -84467,7 +84488,7 @@ ${c}
|
|
|
84467
84488
|
var PARENT_DIRECTORY = "..";
|
|
84468
84489
|
const FileBrowser = ({ value: e, setValue: r, initialPath: c, selectionMode: d, multiple: f, label: _, restrictNavigation: v, list_directory: y, host: S }) => {
|
|
84469
84490
|
var _a3;
|
|
84470
|
-
let [E, O] = useInternalStateWithSync(c), [M, I] = (0, import_react.useState)("Select all"), [z, G] = (0, import_react.useState)(false), [q, IY] = (0, import_react.useState)(false), LY = (_a3 = S.closest(
|
|
84491
|
+
let [E, O] = useInternalStateWithSync(c), [M, I] = (0, import_react.useState)("Select all"), [z, G] = (0, import_react.useState)(false), [q, IY] = (0, import_react.useState)(false), LY = (_a3 = S.closest(`[${RANDOM_ID_ATTR}]`)) == null ? void 0 : _a3.getAttribute(RANDOM_ID_ATTR), { data: RY, error: zY, isPending: BY } = useAsyncData(() => y({
|
|
84471
84492
|
path: E
|
|
84472
84493
|
}), [
|
|
84473
84494
|
E,
|
|
@@ -101391,7 +101412,7 @@ ${c}
|
|
|
101391
101412
|
return extractIslandCodeFromEmbed(this);
|
|
101392
101413
|
}
|
|
101393
101414
|
connectedCallback() {
|
|
101394
|
-
let r = this.querySelectorOrThrow(_l2.outputTagName).innerHTML, c = this.getOptionalEditor(), d = this.code, f = c ? () => `${UI_ELEMENT_REGISTRY.lookupValue(c.props[
|
|
101415
|
+
let r = this.querySelectorOrThrow(_l2.outputTagName).innerHTML, c = this.getOptionalEditor(), d = this.code, f = c ? () => `${UI_ELEMENT_REGISTRY.lookupValue(c.props[OBJECT_ID_ATTR])}` : () => d;
|
|
101395
101416
|
this.root = import_client.createRoot(this), this.render(r, f, c);
|
|
101396
101417
|
}
|
|
101397
101418
|
render(e, r, c) {
|
package/package.json
CHANGED
package/src/core/cells/ids.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-redeclare */
|
|
3
3
|
|
|
4
|
+
import { OBJECT_ID_ATTR } from "@/core/dom/ui-element-constants";
|
|
4
5
|
import { invariant } from "@/utils/invariant";
|
|
5
6
|
import type { TypedString } from "../../utils/typed";
|
|
6
7
|
|
|
@@ -110,7 +111,7 @@ export function findCellId(element: HTMLElement): CellId | null {
|
|
|
110
111
|
export type UIElementId = `${CellId}-${string}`;
|
|
111
112
|
export const UIElementId = {
|
|
112
113
|
parse(element: Element): UIElementId | null {
|
|
113
|
-
return element.getAttribute(
|
|
114
|
+
return element.getAttribute(OBJECT_ID_ATTR) as UIElementId | null;
|
|
114
115
|
},
|
|
115
116
|
parseOrThrow(element: Element): UIElementId {
|
|
116
117
|
const id = UIElementId.parse(element);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Stable identifier for a UI element, deterministic across re-executions
|
|
5
|
+
* of the same cell (based on cell ID + creation order).
|
|
6
|
+
* Used to synchronize multiple instances and kernel state.
|
|
7
|
+
*/
|
|
8
|
+
export const OBJECT_ID_ATTR = "object-id";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Random token that changes every time a UI element is constructed
|
|
12
|
+
* (i.e., every cell execution). Used to detect stale elements and
|
|
13
|
+
* force re-renders when a cell re-runs.
|
|
14
|
+
*/
|
|
15
|
+
export const RANDOM_ID_ATTR = "random-id";
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
MarimoValueInputEvent,
|
|
9
9
|
type MarimoValueInputEventType,
|
|
10
10
|
} from "./events";
|
|
11
|
+
import { RANDOM_ID_ATTR } from "./ui-element-constants";
|
|
11
12
|
import { UI_ELEMENT_REGISTRY } from "./uiregistry";
|
|
12
13
|
|
|
13
14
|
import "./ui-element.css";
|
|
@@ -189,7 +190,7 @@ export function initializeUIElement() {
|
|
|
189
190
|
// used like a React key. If the random-id changes, we need to unmount and
|
|
190
191
|
// remount its child.
|
|
191
192
|
static get observedAttributes() {
|
|
192
|
-
return [
|
|
193
|
+
return [RANDOM_ID_ATTR];
|
|
193
194
|
}
|
|
194
195
|
|
|
195
196
|
attributeChangedCallback(
|
|
@@ -199,7 +200,7 @@ export function initializeUIElement() {
|
|
|
199
200
|
) {
|
|
200
201
|
if (this.initialized) {
|
|
201
202
|
const hasChanged = oldValue !== newValue;
|
|
202
|
-
if (name ===
|
|
203
|
+
if (name === RANDOM_ID_ATTR && hasChanged) {
|
|
203
204
|
// deregister/clean-up this instance
|
|
204
205
|
this.disconnectedCallback();
|
|
205
206
|
// remove and re-add its child to force it to re-render; note that
|
|
@@ -6,6 +6,7 @@ import ReactDOM, { type Root } from "react-dom/client";
|
|
|
6
6
|
import { ErrorBoundary } from "@/components/editor/boundary/ErrorBoundary";
|
|
7
7
|
import { TooltipProvider } from "@/components/ui/tooltip";
|
|
8
8
|
import { notebookAtom } from "@/core/cells/cells";
|
|
9
|
+
import { OBJECT_ID_ATTR } from "@/core/dom/ui-element-constants";
|
|
9
10
|
import { UI_ELEMENT_REGISTRY } from "@/core/dom/uiregistry";
|
|
10
11
|
import { LocaleProvider } from "@/core/i18n/locale-provider";
|
|
11
12
|
import { renderHTML } from "@/plugins/core/RenderHTML";
|
|
@@ -60,7 +61,7 @@ export class MarimoIslandElement extends HTMLElement {
|
|
|
60
61
|
const codeCallback: () => string = optionalEditor
|
|
61
62
|
? () =>
|
|
62
63
|
`${UI_ELEMENT_REGISTRY.lookupValue(
|
|
63
|
-
optionalEditor.props[
|
|
64
|
+
optionalEditor.props[OBJECT_ID_ATTR],
|
|
64
65
|
)}`
|
|
65
66
|
: () => code;
|
|
66
67
|
|
|
@@ -60,7 +60,11 @@ import { ContextAwarePanelItem } from "@/components/editor/chrome/panels/context
|
|
|
60
60
|
import { Alert, AlertTitle } from "@/components/ui/alert";
|
|
61
61
|
import { Button } from "@/components/ui/button";
|
|
62
62
|
import { DelayMount } from "@/components/utils/delay-mount";
|
|
63
|
-
import { type CellId, findCellId } from "@/core/cells/ids";
|
|
63
|
+
import { type CellId, findCellId, UIElementId } from "@/core/cells/ids";
|
|
64
|
+
import {
|
|
65
|
+
OBJECT_ID_ATTR,
|
|
66
|
+
RANDOM_ID_ATTR,
|
|
67
|
+
} from "@/core/dom/ui-element-constants";
|
|
64
68
|
import { slotsController } from "@/core/slots/slots";
|
|
65
69
|
import { store } from "@/core/state/jotai";
|
|
66
70
|
import { isStaticNotebook } from "@/core/static/static-state";
|
|
@@ -361,6 +365,7 @@ export const DataTablePlugin = createPlugin<S>("marimo-table")
|
|
|
361
365
|
<LazyDataTableComponent
|
|
362
366
|
isLazy={props.data.lazy}
|
|
363
367
|
preload={props.data.preload}
|
|
368
|
+
host={props.host}
|
|
364
369
|
>
|
|
365
370
|
<LoadingDataTableComponent
|
|
366
371
|
{...props.data}
|
|
@@ -377,21 +382,66 @@ export const DataTablePlugin = createPlugin<S>("marimo-table")
|
|
|
377
382
|
);
|
|
378
383
|
});
|
|
379
384
|
|
|
385
|
+
/**
|
|
386
|
+
* Tracks which lazy tables have been previewed across remounts (e.g. tab switches).
|
|
387
|
+
* Keyed by uiElementId (stable across remounts) with randomId as value
|
|
388
|
+
* (changes on cell re-execution, so stale entries are naturally invalidated).
|
|
389
|
+
*/
|
|
390
|
+
const previewedTables = new Map<UIElementId, string>();
|
|
391
|
+
|
|
392
|
+
function wasTablePreviewed(
|
|
393
|
+
uiElementId: UIElementId | null | undefined,
|
|
394
|
+
randomId: string | null | undefined,
|
|
395
|
+
): boolean {
|
|
396
|
+
return (
|
|
397
|
+
uiElementId != null &&
|
|
398
|
+
randomId != null &&
|
|
399
|
+
previewedTables.get(uiElementId) === randomId
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function markTablePreviewed(
|
|
404
|
+
uiElementId: UIElementId | null | undefined,
|
|
405
|
+
randomId: string | null | undefined,
|
|
406
|
+
): void {
|
|
407
|
+
if (uiElementId != null && randomId != null) {
|
|
408
|
+
previewedTables.set(uiElementId, randomId);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
380
412
|
const LazyDataTableComponent = ({
|
|
381
413
|
isLazy: initialIsLazy,
|
|
382
414
|
children,
|
|
383
415
|
preload,
|
|
416
|
+
host,
|
|
384
417
|
}: {
|
|
385
418
|
isLazy: boolean;
|
|
386
419
|
children: React.ReactNode;
|
|
387
420
|
preload: boolean;
|
|
421
|
+
host: HTMLElement;
|
|
388
422
|
}) => {
|
|
389
|
-
const
|
|
423
|
+
const parentElement = host.closest(`[${OBJECT_ID_ATTR}]`);
|
|
424
|
+
const uiElementId = parentElement ? UIElementId.parse(parentElement) : null;
|
|
425
|
+
|
|
426
|
+
const randomId = host
|
|
427
|
+
.closest(`[${RANDOM_ID_ATTR}]`)
|
|
428
|
+
?.getAttribute(RANDOM_ID_ATTR);
|
|
429
|
+
|
|
430
|
+
const [isLazy, setIsLazy] = useState(
|
|
431
|
+
initialIsLazy && !preload && !wasTablePreviewed(uiElementId, randomId),
|
|
432
|
+
);
|
|
390
433
|
|
|
391
434
|
if (isLazy) {
|
|
392
435
|
return (
|
|
393
436
|
<div className="flex h-20 items-center justify-center">
|
|
394
|
-
<Button
|
|
437
|
+
<Button
|
|
438
|
+
variant="outline"
|
|
439
|
+
size="xs"
|
|
440
|
+
onClick={() => {
|
|
441
|
+
markTablePreviewed(uiElementId, randomId);
|
|
442
|
+
setIsLazy(false);
|
|
443
|
+
}}
|
|
444
|
+
>
|
|
395
445
|
<Table2Icon className="mr-2 h-4 w-4" />
|
|
396
446
|
Preview data
|
|
397
447
|
</Button>
|
|
@@ -15,6 +15,7 @@ import { Label } from "@/components/ui/label";
|
|
|
15
15
|
import { NativeSelect } from "@/components/ui/native-select";
|
|
16
16
|
import { Table, TableBody, TableCell, TableRow } from "@/components/ui/table";
|
|
17
17
|
import { toast } from "@/components/ui/use-toast";
|
|
18
|
+
import { RANDOM_ID_ATTR } from "@/core/dom/ui-element-constants";
|
|
18
19
|
import { useAsyncData } from "@/hooks/useAsyncData";
|
|
19
20
|
import { useInternalStateWithSync } from "@/hooks/useInternalStateWithSync";
|
|
20
21
|
import { cn } from "@/utils/cn";
|
|
@@ -150,7 +151,9 @@ export const FileBrowser = ({
|
|
|
150
151
|
|
|
151
152
|
// HACK: use the random-id of the host element to force a re-render
|
|
152
153
|
// when the random-id changes, this means the cell was re-rendered
|
|
153
|
-
const randomId = host
|
|
154
|
+
const randomId = host
|
|
155
|
+
.closest(`[${RANDOM_ID_ATTR}]`)
|
|
156
|
+
?.getAttribute(RANDOM_ID_ATTR);
|
|
154
157
|
|
|
155
158
|
const { data, error, isPending } = useAsyncData(() => {
|
|
156
159
|
return list_directory({ path: path });
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import type { AnyWidget } from "@anywidget/types";
|
|
5
5
|
import { useEffect, useRef } from "react";
|
|
6
6
|
import { z } from "zod";
|
|
7
|
+
import { RANDOM_ID_ATTR } from "@/core/dom/ui-element-constants";
|
|
7
8
|
import { useAsyncData } from "@/hooks/useAsyncData";
|
|
8
9
|
import type { HTMLElementNotDerivedFromRef } from "@/hooks/useEventListener";
|
|
9
10
|
import { createPlugin } from "@/plugins/core/builder";
|
|
@@ -145,7 +146,9 @@ const AnyWidgetSlot = (props: IPluginProps<ModelIdRef, Data>) => {
|
|
|
145
146
|
}
|
|
146
147
|
|
|
147
148
|
// Find the closest parent element with an attribute of `random-id`
|
|
148
|
-
const randomId = props.host
|
|
149
|
+
const randomId = props.host
|
|
150
|
+
.closest(`[${RANDOM_ID_ATTR}]`)
|
|
151
|
+
?.getAttribute(RANDOM_ID_ATTR);
|
|
149
152
|
const key = randomId ?? jsUrl;
|
|
150
153
|
|
|
151
154
|
return (
|