@marimo-team/islands 0.23.14-dev56 → 0.23.14-dev59
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/{code-visibility-D7wWXyyp.js → code-visibility-ChrxMIRR.js} +28 -28
- package/dist/main.js +1920 -1619
- package/dist/{reveal-component-bWXDKgVc.js → reveal-component-BLijd8xt.js} +1 -1
- package/package.json +2 -2
- package/src/core/islands/bootstrap.ts +3 -3
- package/src/core/websocket/useMarimoKernelConnection.tsx +3 -3
- package/src/mount.tsx +3 -3
- package/src/plugins/impl/anywidget/AnyWidgetPlugin.tsx +33 -305
- package/src/plugins/impl/anywidget/__tests__/AnyWidgetPlugin.test.tsx +121 -229
- package/src/plugins/impl/anywidget/__tests__/host.test.ts +313 -0
- package/src/plugins/impl/anywidget/__tests__/model-proxy.test.ts +158 -0
- package/src/plugins/impl/anywidget/__tests__/model.test.ts +8 -177
- package/src/plugins/impl/anywidget/__tests__/registry.test.ts +708 -0
- package/src/plugins/impl/anywidget/__tests__/resolve-widget.test.ts +132 -0
- package/src/plugins/impl/anywidget/__tests__/widget-binding.test.ts +305 -133
- package/src/plugins/impl/anywidget/__tests__/widget-ref.test.ts +28 -0
- package/src/plugins/impl/anywidget/host.ts +54 -0
- package/src/plugins/impl/anywidget/model-proxy.ts +70 -0
- package/src/plugins/impl/anywidget/model.ts +59 -239
- package/src/plugins/impl/anywidget/registry.ts +268 -0
- package/src/plugins/impl/anywidget/resolve-widget.ts +138 -0
- package/src/plugins/impl/anywidget/runtime.ts +408 -0
- package/src/plugins/impl/anywidget/types.ts +14 -0
- package/src/plugins/impl/anywidget/widget-binding.ts +280 -119
- package/src/plugins/impl/anywidget/widget-ref.ts +29 -0
- package/src/plugins/impl/mpl-interactive/MplInteractivePlugin.tsx +3 -2
- package/src/plugins/impl/mpl-interactive/__tests__/MplInteractivePlugin.test.tsx +16 -13
- package/src/plugins/impl/mpl-interactive/mpl-websocket-shim.ts +1 -1
- package/src/plugins/impl/anywidget/schemas.ts +0 -32
|
@@ -9,7 +9,7 @@ import { t as require_compiler_runtime } from "./compiler-runtime-CEbnTgxf.js";
|
|
|
9
9
|
import { lt as kioskModeAtom, pt as outputIsStale } from "./html-to-image-hevd6UWC.js";
|
|
10
10
|
import "./chunk-5FQGJX7Z-BbqSm5gU.js";
|
|
11
11
|
import { u as createLucideIcon } from "./dist--2Bqjvs0.js";
|
|
12
|
-
import { a as DEFAULT_SLIDE_TYPE, c as Slide, ct as PanelGroup, i as DEFAULT_DECK_TRANSITION, lt as PanelResizeHandle, on as EyeOff, s as SlideSidebar, sn as Expand, st as Panel, t as useNotebookCodeAvailable, un as Code } from "./code-visibility-
|
|
12
|
+
import { a as DEFAULT_SLIDE_TYPE, c as Slide, ct as PanelGroup, i as DEFAULT_DECK_TRANSITION, lt as PanelResizeHandle, on as EyeOff, s as SlideSidebar, sn as Expand, st as Panel, t as useNotebookCodeAvailable, un as Code } from "./code-visibility-ChrxMIRR.js";
|
|
13
13
|
import { X as useDebouncedCallback } from "./input-DtsN7xm-.js";
|
|
14
14
|
import "./toDate-CWNNlFEX.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.14-
|
|
3
|
+
"version": "0.23.14-dev59",
|
|
4
4
|
"main": "dist/main.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@ai-sdk/react": "^3.0.134",
|
|
25
|
-
"@anywidget/types": "^0.
|
|
25
|
+
"@anywidget/types": "^0.4.0",
|
|
26
26
|
"@codemirror/autocomplete": "^6.20.1",
|
|
27
27
|
"@codemirror/commands": "^6.10.2",
|
|
28
28
|
"@codemirror/lang-markdown": "^6.5.0",
|
|
@@ -5,8 +5,8 @@ import { ISLAND_CSS_CLASSES, ISLAND_TAG_NAMES } from "@/core/islands/constants";
|
|
|
5
5
|
import { renderHTML } from "@/plugins/core/RenderHTML";
|
|
6
6
|
import {
|
|
7
7
|
handleWidgetMessage,
|
|
8
|
-
|
|
9
|
-
} from "@/plugins/impl/anywidget/
|
|
8
|
+
WIDGET_REGISTRY,
|
|
9
|
+
} from "@/plugins/impl/anywidget/registry";
|
|
10
10
|
import { initializePlugins } from "@/plugins/plugins";
|
|
11
11
|
import { Functions } from "@/utils/functions";
|
|
12
12
|
import {
|
|
@@ -253,7 +253,7 @@ function handleMessage(
|
|
|
253
253
|
return;
|
|
254
254
|
|
|
255
255
|
case "model-lifecycle":
|
|
256
|
-
handleWidgetMessage(
|
|
256
|
+
handleWidgetMessage(WIDGET_REGISTRY, msg.data);
|
|
257
257
|
return;
|
|
258
258
|
|
|
259
259
|
case "consumer-capabilities":
|
|
@@ -16,8 +16,8 @@ import { useConnectionTransport } from "@/core/websocket/useWebSocket";
|
|
|
16
16
|
import { renderHTML } from "@/plugins/core/RenderHTML";
|
|
17
17
|
import {
|
|
18
18
|
handleWidgetMessage,
|
|
19
|
-
|
|
20
|
-
} from "@/plugins/impl/anywidget/
|
|
19
|
+
WIDGET_REGISTRY,
|
|
20
|
+
} from "@/plugins/impl/anywidget/registry";
|
|
21
21
|
import { logNever } from "@/utils/assertNever";
|
|
22
22
|
import { prettyError } from "@/utils/errors";
|
|
23
23
|
import {
|
|
@@ -284,7 +284,7 @@ export function useMarimoKernelConnection(opts: {
|
|
|
284
284
|
}
|
|
285
285
|
|
|
286
286
|
case "model-lifecycle":
|
|
287
|
-
handleWidgetMessage(
|
|
287
|
+
handleWidgetMessage(WIDGET_REGISTRY, msg.data);
|
|
288
288
|
return;
|
|
289
289
|
|
|
290
290
|
case "remove-ui-elements":
|
package/src/mount.tsx
CHANGED
|
@@ -54,8 +54,8 @@ import { notebookFileStore } from "./core/wasm/store";
|
|
|
54
54
|
import { WebSocketState } from "./core/websocket/types";
|
|
55
55
|
import {
|
|
56
56
|
handleWidgetMessage,
|
|
57
|
-
|
|
58
|
-
} from "./plugins/impl/anywidget/
|
|
57
|
+
WIDGET_REGISTRY,
|
|
58
|
+
} from "./plugins/impl/anywidget/registry";
|
|
59
59
|
import { vegaLoader } from "./plugins/impl/vega/loader";
|
|
60
60
|
import { initializePlugins } from "./plugins/plugins";
|
|
61
61
|
import { ThemeProvider } from "./theme/ThemeProvider";
|
|
@@ -369,7 +369,7 @@ function hydrateStaticModels(): void {
|
|
|
369
369
|
return;
|
|
370
370
|
}
|
|
371
371
|
for (const notification of notifications) {
|
|
372
|
-
handleWidgetMessage(
|
|
372
|
+
handleWidgetMessage(WIDGET_REGISTRY, notification);
|
|
373
373
|
}
|
|
374
374
|
}
|
|
375
375
|
|
|
@@ -1,32 +1,21 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
-
/* oxlint-disable typescript/no-explicit-any */
|
|
3
2
|
|
|
4
|
-
import
|
|
5
|
-
import { useEffect, useRef } from "react";
|
|
3
|
+
import { useEffect, useRef, useState } from "react";
|
|
6
4
|
import { z } from "zod";
|
|
7
|
-
import { useAsyncData } from "@/hooks/useAsyncData";
|
|
8
|
-
import type { HTMLElementNotDerivedFromRef } from "@/hooks/useEventListener";
|
|
9
5
|
import { createPlugin } from "@/plugins/core/builder";
|
|
10
6
|
import type { IPluginProps } from "@/plugins/types";
|
|
11
|
-
import { prettyError } from "@/utils/errors";
|
|
12
|
-
import { Logger } from "@/utils/Logger";
|
|
13
|
-
import { hasFunctionProperty, isRecord } from "@/utils/records";
|
|
14
7
|
import { ErrorBanner } from "../common/error-banner";
|
|
15
|
-
import {
|
|
16
|
-
import
|
|
17
|
-
import { BINDING_MANAGER, WIDGET_DEF_REGISTRY } from "./widget-binding";
|
|
8
|
+
import { WIDGET_REGISTRY } from "./registry";
|
|
9
|
+
import { isWidgetModelId, type WidgetModelId } from "./types";
|
|
18
10
|
|
|
19
11
|
/**
|
|
20
|
-
*
|
|
12
|
+
* The component's only data attribute; everything else arrives through
|
|
13
|
+
* the model's comm messages, keyed by this id.
|
|
21
14
|
*/
|
|
22
15
|
interface Data {
|
|
23
|
-
jsUrl: string;
|
|
24
|
-
jsHash: string;
|
|
25
16
|
modelId: WidgetModelId;
|
|
26
17
|
}
|
|
27
18
|
|
|
28
|
-
type AnyWidgetState = ModelState;
|
|
29
|
-
|
|
30
19
|
/**
|
|
31
20
|
* Value payload sent by the frontend on state updates.
|
|
32
21
|
*
|
|
@@ -37,315 +26,54 @@ interface ModelIdRef {
|
|
|
37
26
|
model_id?: WidgetModelId;
|
|
38
27
|
}
|
|
39
28
|
|
|
40
|
-
export function useAnyWidgetModule(opts: { jsUrl: string; jsHash: string }) {
|
|
41
|
-
const { jsUrl, jsHash } = opts;
|
|
42
|
-
|
|
43
|
-
// JS is an ESM file with a render function on it
|
|
44
|
-
// export function render({ model, el }) {
|
|
45
|
-
// ...
|
|
46
|
-
const {
|
|
47
|
-
data: jsModule,
|
|
48
|
-
error,
|
|
49
|
-
refetch,
|
|
50
|
-
} = useAsyncData(async () => {
|
|
51
|
-
return await WIDGET_DEF_REGISTRY.getModule(jsUrl, jsHash);
|
|
52
|
-
// Re-render on jsHash change (which is a hash of the contents of the file)
|
|
53
|
-
// instead of a jsUrl change because URLs may change without the contents
|
|
54
|
-
// actually changing (and we don't want to re-render on every change).
|
|
55
|
-
// If there is an error loading the URL (e.g. maybe an invalid or old URL),
|
|
56
|
-
// we also want to re-render.
|
|
57
|
-
}, [jsHash]);
|
|
58
|
-
|
|
59
|
-
// If there is an error and the jsUrl has changed, we want to re-render
|
|
60
|
-
// because the URL may have changed to a valid URL.
|
|
61
|
-
const hasError = Boolean(error);
|
|
62
|
-
useEffect(() => {
|
|
63
|
-
if (hasError && jsUrl) {
|
|
64
|
-
WIDGET_DEF_REGISTRY.invalidate(jsHash);
|
|
65
|
-
refetch();
|
|
66
|
-
}
|
|
67
|
-
}, [hasError, jsUrl]);
|
|
68
|
-
|
|
69
|
-
return {
|
|
70
|
-
jsModule,
|
|
71
|
-
error,
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export function useMountCss(css: string | null | undefined, host: HTMLElement) {
|
|
76
|
-
// Mount the CSS
|
|
77
|
-
useEffect(() => {
|
|
78
|
-
const shadowRoot = host.shadowRoot;
|
|
79
|
-
if (!css || !shadowRoot) {
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Try constructed stylesheets first
|
|
84
|
-
if (
|
|
85
|
-
"adoptedStyleSheets" in Document.prototype &&
|
|
86
|
-
"replace" in CSSStyleSheet.prototype
|
|
87
|
-
) {
|
|
88
|
-
const sheet = new CSSStyleSheet();
|
|
89
|
-
try {
|
|
90
|
-
sheet.replaceSync(css);
|
|
91
|
-
if (shadowRoot) {
|
|
92
|
-
shadowRoot.adoptedStyleSheets = [
|
|
93
|
-
...shadowRoot.adoptedStyleSheets,
|
|
94
|
-
sheet,
|
|
95
|
-
];
|
|
96
|
-
}
|
|
97
|
-
return () => {
|
|
98
|
-
if (shadowRoot) {
|
|
99
|
-
shadowRoot.adoptedStyleSheets =
|
|
100
|
-
shadowRoot.adoptedStyleSheets.filter((s) => s !== sheet);
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
} catch {
|
|
104
|
-
// Fall through to inline styles if constructed sheets fail
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Fallback to inline styles
|
|
109
|
-
const style = document.createElement("style");
|
|
110
|
-
style.innerHTML = css;
|
|
111
|
-
shadowRoot.append(style);
|
|
112
|
-
return () => {
|
|
113
|
-
style.remove();
|
|
114
|
-
};
|
|
115
|
-
}, [css, host]);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
29
|
export const AnyWidgetPlugin = createPlugin<ModelIdRef>("marimo-anywidget")
|
|
119
30
|
.withData(
|
|
120
31
|
z.object({
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
32
|
+
modelId: z.custom<WidgetModelId>(isWidgetModelId, {
|
|
33
|
+
message: "Expected a non-empty widget model id",
|
|
34
|
+
}),
|
|
124
35
|
}),
|
|
125
36
|
)
|
|
126
37
|
.withFunctions({})
|
|
127
38
|
.renderer((props) => <AnyWidgetSlot {...props} />);
|
|
128
39
|
|
|
129
|
-
const AnyWidgetSlot = (props: IPluginProps<ModelIdRef, Data>) => {
|
|
130
|
-
const { jsUrl, jsHash, modelId } = props.data;
|
|
131
|
-
const host = props.host as HTMLElementNotDerivedFromRef;
|
|
132
|
-
|
|
133
|
-
const { jsModule, error } = useAnyWidgetModule({ jsUrl, jsHash });
|
|
134
|
-
|
|
135
|
-
if (error) {
|
|
136
|
-
return <ErrorBanner error={error} />;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
if (!jsModule) {
|
|
140
|
-
return null;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const widget = resolveAnyWidget(jsModule, jsUrl);
|
|
144
|
-
if (!widget) {
|
|
145
|
-
return (
|
|
146
|
-
<ErrorBanner error={getInvalidAnyWidgetModuleError(jsModule, jsUrl)} />
|
|
147
|
-
);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
return (
|
|
151
|
-
<LoadedSlot
|
|
152
|
-
// Force remount when the widget module or model changes (cell re-run).
|
|
153
|
-
key={`${jsHash}:${modelId}`}
|
|
154
|
-
widget={widget}
|
|
155
|
-
modelId={modelId}
|
|
156
|
-
host={host}
|
|
157
|
-
/>
|
|
158
|
-
);
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Run the anywidget module
|
|
163
|
-
*
|
|
164
|
-
* Per AFM spec (anywidget.dev/en/afm):
|
|
165
|
-
* - initialize() is called once per model lifetime
|
|
166
|
-
* - render() is called once per view (can be multiple per model)
|
|
167
|
-
*/
|
|
168
|
-
async function runAnyWidgetModule<T extends AnyWidgetState>(
|
|
169
|
-
widgetDef: AnyWidget<T>,
|
|
170
|
-
model: Model<T>,
|
|
171
|
-
modelId: WidgetModelId,
|
|
172
|
-
el: HTMLElement,
|
|
173
|
-
signal: AbortSignal,
|
|
174
|
-
): Promise<void> {
|
|
175
|
-
// Clear the element, in case the widget is re-rendering
|
|
176
|
-
el.innerHTML = "";
|
|
177
|
-
|
|
178
|
-
try {
|
|
179
|
-
const binding = BINDING_MANAGER.getOrCreate(modelId);
|
|
180
|
-
const render = await binding.bind(widgetDef, model);
|
|
181
|
-
await render(el, signal);
|
|
182
|
-
// Replay current model values so render listeners observe hydrated state
|
|
183
|
-
// even if backend updates arrived before listeners were attached.
|
|
184
|
-
getMarimoInternal(model).reemitState();
|
|
185
|
-
} catch (error) {
|
|
186
|
-
Logger.error("Error rendering anywidget", error);
|
|
187
|
-
el.classList.add("text-error");
|
|
188
|
-
el.innerHTML = `Error rendering anywidget: ${prettyError(error)}`;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
function isAnyWidgetModule(mod: any): mod is { default: AnyWidget } {
|
|
193
|
-
if (!mod.default) {
|
|
194
|
-
return false;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
return (
|
|
198
|
-
typeof mod.default === "function" ||
|
|
199
|
-
typeof mod.default?.render === "function" ||
|
|
200
|
-
typeof mod.default?.initialize === "function"
|
|
201
|
-
);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
const warnedLegacyNamedExportUrls = new Set<string>();
|
|
205
|
-
// Cache the synthesized widget per module namespace so its identity stays
|
|
206
|
-
// stable across re-renders (like a default export), avoiding needless
|
|
207
|
-
// WidgetBinding re-initialization.
|
|
208
|
-
const legacyWidgetCache = new WeakMap<object, AnyWidget>();
|
|
209
|
-
|
|
210
40
|
/**
|
|
211
|
-
*
|
|
212
|
-
*
|
|
213
|
-
*
|
|
41
|
+
* React adapter for a runtime-owned view. React supplies only the render
|
|
42
|
+
* target and its lifetime; the runtime owns models, generations, CSS,
|
|
43
|
+
* composition, and hot reload.
|
|
214
44
|
*/
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
return mod.default;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// Only fall back to legacy (pre-AFM) named exports when there is no default
|
|
221
|
-
// export at all; a present-but-invalid default should surface an error
|
|
222
|
-
// rather than be masked.
|
|
223
|
-
if (mod?.default != null) {
|
|
224
|
-
return null;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
const hasNamedRender = typeof mod?.render === "function";
|
|
228
|
-
const hasNamedInitialize = typeof mod?.initialize === "function";
|
|
229
|
-
if (!hasNamedRender && !hasNamedInitialize) {
|
|
230
|
-
return null;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
const cached = legacyWidgetCache.get(mod);
|
|
234
|
-
if (cached) {
|
|
235
|
-
return cached;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
if (!warnedLegacyNamedExportUrls.has(jsUrl)) {
|
|
239
|
-
warnedLegacyNamedExportUrls.add(jsUrl);
|
|
240
|
-
Logger.warn(
|
|
241
|
-
`Anywidget module at ${jsUrl} uses deprecated top-level named ` +
|
|
242
|
-
"exports (`render`/`initialize`). Per the AFM spec, use a default " +
|
|
243
|
-
"export instead: `export default { render }`. " +
|
|
244
|
-
"See https://anywidget.dev/en/afm/",
|
|
245
|
-
);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
const widget: AnyWidget = { render: mod.render, initialize: mod.initialize };
|
|
249
|
-
legacyWidgetCache.set(mod, widget);
|
|
250
|
-
return widget;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
function getInvalidAnyWidgetModuleError(mod: unknown, jsUrl: string): Error {
|
|
254
|
-
const afmDocs = "https://anywidget.dev/en/afm/";
|
|
255
|
-
const hasNamedRender = isRecord(mod) && hasFunctionProperty(mod, "render");
|
|
256
|
-
const hasNamedInitialize =
|
|
257
|
-
isRecord(mod) && hasFunctionProperty(mod, "initialize");
|
|
258
|
-
|
|
259
|
-
if (hasNamedRender || hasNamedInitialize) {
|
|
260
|
-
const namedExports = [
|
|
261
|
-
hasNamedRender ? "`render`" : null,
|
|
262
|
-
hasNamedInitialize ? "`initialize`" : null,
|
|
263
|
-
]
|
|
264
|
-
.filter(Boolean)
|
|
265
|
-
.join(" and ");
|
|
266
|
-
const lifecycleHooks = [
|
|
267
|
-
hasNamedRender ? "render" : null,
|
|
268
|
-
hasNamedInitialize ? "initialize" : null,
|
|
269
|
-
].filter((hook): hook is string => hook !== null);
|
|
270
|
-
const defaultExportExample = `export default { ${lifecycleHooks.join(", ")} }`;
|
|
271
|
-
const namedExportExample =
|
|
272
|
-
lifecycleHooks.length === 1
|
|
273
|
-
? `export function ${lifecycleHooks[0]}`
|
|
274
|
-
: "named export function ...";
|
|
275
|
-
return new Error(
|
|
276
|
-
`Anywidget module at ${jsUrl} uses named exports (${namedExports}). ` +
|
|
277
|
-
"Per the AFM spec, use a default export instead: " +
|
|
278
|
-
`\`${defaultExportExample}\` (not \`${namedExportExample}\`). ` +
|
|
279
|
-
`See ${afmDocs}`,
|
|
280
|
-
);
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
if (!isRecord(mod) || mod.default === undefined) {
|
|
284
|
-
return new Error(
|
|
285
|
-
`Anywidget module at ${jsUrl} is missing a default export. ` +
|
|
286
|
-
"Per the AFM spec, use `export default { render }` or " +
|
|
287
|
-
"`export default async () => ({ render })`. " +
|
|
288
|
-
`See ${afmDocs}`,
|
|
289
|
-
);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
return new Error(
|
|
293
|
-
`Anywidget module at ${jsUrl} has an invalid default export. ` +
|
|
294
|
-
"Expected a factory function or an object with `render` or `initialize`. " +
|
|
295
|
-
`See ${afmDocs}`,
|
|
296
|
-
);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
interface Props<T extends AnyWidgetState> {
|
|
300
|
-
widget: AnyWidget<T>;
|
|
301
|
-
modelId: WidgetModelId;
|
|
302
|
-
host: HTMLElementNotDerivedFromRef;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
const LoadedSlot = <T extends AnyWidgetState>({
|
|
306
|
-
widget,
|
|
307
|
-
modelId,
|
|
308
|
-
host,
|
|
309
|
-
}: Props<T> & { widget: AnyWidget<T> }) => {
|
|
45
|
+
const AnyWidgetSlot = (props: IPluginProps<ModelIdRef, Data>) => {
|
|
46
|
+
const { modelId } = props.data;
|
|
310
47
|
const htmlRef = useRef<HTMLDivElement>(null);
|
|
311
|
-
|
|
312
|
-
// value is already decoded from wire format, may be null if waiting for open message
|
|
313
|
-
const model = MODEL_MANAGER.getSync(modelId);
|
|
314
|
-
|
|
315
|
-
if (!model) {
|
|
316
|
-
Logger.error("Model not found for modelId", modelId);
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
const css = model?.get("_css");
|
|
320
|
-
useMountCss(css, host);
|
|
48
|
+
const [error, setError] = useState<Error>();
|
|
321
49
|
|
|
322
50
|
useEffect(() => {
|
|
323
|
-
|
|
51
|
+
const el = htmlRef.current;
|
|
52
|
+
if (!el) {
|
|
324
53
|
return;
|
|
325
54
|
}
|
|
326
55
|
const controller = new AbortController();
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
model,
|
|
56
|
+
setError(undefined);
|
|
57
|
+
WIDGET_REGISTRY.createView({
|
|
330
58
|
modelId,
|
|
331
|
-
|
|
332
|
-
controller.signal,
|
|
333
|
-
)
|
|
59
|
+
el,
|
|
60
|
+
signal: controller.signal,
|
|
61
|
+
}).catch((error) => {
|
|
62
|
+
if (!controller.signal.aborted) {
|
|
63
|
+
setError(error instanceof Error ? error : new Error(String(error)));
|
|
64
|
+
}
|
|
65
|
+
});
|
|
334
66
|
return () => controller.abort();
|
|
335
|
-
|
|
336
|
-
// that created the Widget has been re-run.
|
|
337
|
-
// We need to re-run the widget because it may contain initialization code
|
|
338
|
-
// that could be reset by the new widget.
|
|
339
|
-
// See example: https://github.com/marimo-team/marimo/issues/3962#issuecomment-2703184123
|
|
340
|
-
}, [widget, modelId, model]);
|
|
67
|
+
}, [modelId]);
|
|
341
68
|
|
|
342
|
-
return
|
|
69
|
+
return (
|
|
70
|
+
<>
|
|
71
|
+
{error ? <ErrorBanner error={error} /> : null}
|
|
72
|
+
<div ref={htmlRef} />
|
|
73
|
+
</>
|
|
74
|
+
);
|
|
343
75
|
};
|
|
344
76
|
|
|
345
77
|
export const visibleForTesting = {
|
|
346
|
-
|
|
347
|
-
runAnyWidgetModule,
|
|
348
|
-
isAnyWidgetModule,
|
|
349
|
-
resolveAnyWidget,
|
|
350
|
-
getInvalidAnyWidgetModuleError,
|
|
78
|
+
AnyWidgetSlot,
|
|
351
79
|
};
|