@marimo-team/islands 0.19.10-dev9 → 0.19.11-dev0

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 (94) hide show
  1. package/dist/Plot-C9vQQj4X.js +172249 -0
  2. package/dist/{any-language-editor-CKEbZakX.js → any-language-editor-t_VsTNa-.js} +16 -16
  3. package/dist/dist-4YNZxwMI.js +8 -0
  4. package/dist/dist-7nR3r2kG.js +5 -0
  5. package/dist/{dist-CBA36Nuy.js → dist-B2-r9y-0.js} +109 -109
  6. package/dist/dist-B2gkyT3r.js +5 -0
  7. package/dist/{dist-DRtGOCCq.js → dist-B4tYJP_i.js} +2 -2
  8. package/dist/{dist-C0e1aNzV.js → dist-B5ATpkxy.js} +2 -2
  9. package/dist/dist-B8G3I6vJ.js +8 -0
  10. package/dist/{dist-DKnxaCRl.js → dist-B94MxrQS.js} +2 -2
  11. package/dist/dist-BJ96Ykfp.js +8 -0
  12. package/dist/dist-BKLIWGw4.js +5 -0
  13. package/dist/{dist-l0KayR2-.js → dist-BLwfpZD-.js} +2 -2
  14. package/dist/{dist-CzKXtzDE.js → dist-BYmtF1W6.js} +2 -2
  15. package/dist/{dist-BJUs1DAG.js → dist-BbBnU4tG.js} +1 -1
  16. package/dist/dist-Bf3ou00A.js +6 -0
  17. package/dist/{dist-CITQGRtG.js → dist-BfactX3G.js} +4 -4
  18. package/dist/{dist-DiCjkKC2.js → dist-BoAHOW2l.js} +2 -2
  19. package/dist/{dist-BsBHh4jO.js → dist-Bsv_ARko.js} +4 -4
  20. package/dist/dist-BvkKXuPm.js +5 -0
  21. package/dist/{dist-DsqQCNKw.js → dist-C2-m5aEk.js} +119 -119
  22. package/dist/dist-C6NJ3n6r.js +5 -0
  23. package/dist/{dist-yI-ah_iK.js → dist-CC9VUnXd.js} +1 -1
  24. package/dist/{dist-tdABwZK5.js → dist-CE43BRmt.js} +1 -1
  25. package/dist/{dist-COp5dkis.js → dist-CJrHMxlI.js} +31 -31
  26. package/dist/{dist-Ct5hkOvC.js → dist-CPTE45iS.js} +1 -1
  27. package/dist/{dist-C-at-5cM.js → dist-CcOGT46m.js} +27 -27
  28. package/dist/dist-CecLPYY5.js +5 -0
  29. package/dist/{dist-BSMZYwqW.js → dist-Cgf353Ki.js} +1 -1
  30. package/dist/dist-Ch0SwRzK.js +5 -0
  31. package/dist/{dist-BqYNqP5W.js → dist-CkEUrAus.js} +2 -2
  32. package/dist/{dist-D4ObdSdT.js → dist-CmZYrgd_.js} +1 -1
  33. package/dist/{dist-D1q38GZb.js → dist-Crk9ejOy.js} +4 -4
  34. package/dist/dist-D6eWHiFh.js +6 -0
  35. package/dist/dist-DCQ710Bv.js +5 -0
  36. package/dist/{dist-r6N_0WG-.js → dist-DOil6y-3.js} +4 -4
  37. package/dist/{dist-CPd_adhw.js → dist-Dc1SFk5I.js} +2 -2
  38. package/dist/dist-Dit9tk8a.js +1242 -0
  39. package/dist/{dist-B7NoEgR4.js → dist-DqJdzAYM.js} +2 -2
  40. package/dist/dist-P_pkS5f-.js +8 -0
  41. package/dist/{dist-CsjsvW0K.js → dist-T4g7Sr6e.js} +3 -3
  42. package/dist/{dist-WETuLs_C.js → dist-glA_fIK_.js} +2 -2
  43. package/dist/{dist-bRBEzJF8.js → dist-iiugPhCC.js} +1 -1
  44. package/dist/{dist-D7jHtwN8.js → dist-r8ecBV-v.js} +135 -65
  45. package/dist/{dist-BlRm4v0e.js → dist-yVJ4xE5n.js} +5 -5
  46. package/dist/{esm-QY6C-Sev.js → esm-BAS2d2Ad.js} +1421 -1454
  47. package/dist/main.js +421 -383
  48. package/package.json +11 -11
  49. package/src/components/data-table/TableActions.tsx +8 -1
  50. package/src/components/data-table/data-table.tsx +2 -0
  51. package/src/components/data-table/download-actions.tsx +6 -1
  52. package/src/components/dependency-graph/dependency-graph-tree.tsx +10 -1
  53. package/src/components/dependency-graph/dependency-graph.tsx +1 -0
  54. package/src/components/dependency-graph/elements.ts +20 -9
  55. package/src/components/dependency-graph/panels.tsx +27 -11
  56. package/src/components/dependency-graph/types.ts +1 -0
  57. package/src/components/editor/chrome/wrapper/app-chrome.tsx +3 -0
  58. package/src/components/editor/package-alert.tsx +4 -4
  59. package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +3 -5
  60. package/src/core/codemirror/misc/__tests__/paste.test.ts +18 -0
  61. package/src/core/codemirror/misc/paste.ts +14 -10
  62. package/src/core/kernel/messages.ts +1 -0
  63. package/src/core/static/static-state.ts +5 -0
  64. package/src/core/static/types.ts +2 -0
  65. package/src/core/wasm/__tests__/store.test.ts +33 -0
  66. package/src/core/wasm/bridge.ts +2 -1
  67. package/src/core/wasm/store.ts +13 -1
  68. package/src/mount.tsx +23 -1
  69. package/src/plugins/impl/DataTablePlugin.tsx +4 -0
  70. package/src/plugins/impl/anywidget/AnyWidgetPlugin.tsx +7 -5
  71. package/src/plugins/impl/anywidget/__tests__/model.test.ts +53 -0
  72. package/src/plugins/impl/anywidget/model.ts +13 -10
  73. package/src/plugins/impl/chat/ChatPlugin.tsx +2 -0
  74. package/src/plugins/impl/chat/chat-ui.tsx +10 -1
  75. package/src/plugins/impl/data-frames/DataFramePlugin.tsx +4 -0
  76. package/src/plugins/impl/plotly/Plot.tsx +2 -0
  77. package/src/plugins/impl/plotly/PlotlyPlugin.tsx +36 -0
  78. package/src/theme/ThemeProvider.tsx +2 -0
  79. package/dist/Plot-CmsrWWji.js +0 -169233
  80. package/dist/dist-BKTAAusE.js +0 -5
  81. package/dist/dist-BNXv9Wjt.js +0 -6
  82. package/dist/dist-C-tlm9eD.js +0 -6
  83. package/dist/dist-CFi_P6cs.js +0 -5
  84. package/dist/dist-CgQuqOGS.js +0 -5
  85. package/dist/dist-CgkWmw0c.js +0 -5
  86. package/dist/dist-DGkeEIsV.js +0 -8
  87. package/dist/dist-DOwZz8aI.js +0 -5
  88. package/dist/dist-DRiJGkDN.js +0 -8
  89. package/dist/dist-GR6ABeNk.js +0 -8
  90. package/dist/dist-NcujbSeH.js +0 -8
  91. package/dist/dist-g1p2PEVs.js +0 -1242
  92. package/dist/dist-pNvwCQqJ.js +0 -5
  93. package/dist/dist-wudNDAiO.js +0 -5
  94. package/dist/dist-yK8yJfz2.js +0 -5
@@ -21,7 +21,6 @@ import { BINDING_MANAGER, WIDGET_DEF_REGISTRY } from "./widget-binding";
21
21
  interface Data {
22
22
  jsUrl: string;
23
23
  jsHash: string;
24
- css?: string | null;
25
24
  }
26
25
 
27
26
  type AnyWidgetState = ModelState;
@@ -118,21 +117,18 @@ export const AnyWidgetPlugin = createPlugin<ModelIdRef>("marimo-anywidget")
118
117
  z.object({
119
118
  jsUrl: z.string(),
120
119
  jsHash: z.string(),
121
- css: z.string().nullish(),
122
120
  }),
123
121
  )
124
122
  .withFunctions({})
125
123
  .renderer((props) => <AnyWidgetSlot {...props} />);
126
124
 
127
125
  const AnyWidgetSlot = (props: IPluginProps<ModelIdRef, Data>) => {
128
- const { css, jsUrl, jsHash } = props.data;
126
+ const { jsUrl, jsHash } = props.data;
129
127
  const { model_id: modelId } = props.value;
130
128
  const host = props.host as HTMLElementNotDerivedFromRef;
131
129
 
132
130
  const { jsModule, error } = useAnyWidgetModule({ jsUrl, jsHash });
133
131
 
134
- useMountCss(css, host);
135
-
136
132
  if (error) {
137
133
  return <ErrorBanner error={error} />;
138
134
  }
@@ -160,6 +156,7 @@ const AnyWidgetSlot = (props: IPluginProps<ModelIdRef, Data>) => {
160
156
  key={key}
161
157
  widget={jsModule.default}
162
158
  modelId={modelId}
159
+ host={host}
163
160
  />
164
161
  );
165
162
  };
@@ -207,11 +204,13 @@ function isAnyWidgetModule(mod: any): mod is { default: AnyWidget } {
207
204
  interface Props<T extends AnyWidgetState> {
208
205
  widget: AnyWidget<T>;
209
206
  modelId: WidgetModelId;
207
+ host: HTMLElementNotDerivedFromRef;
210
208
  }
211
209
 
212
210
  const LoadedSlot = <T extends AnyWidgetState>({
213
211
  widget,
214
212
  modelId,
213
+ host,
215
214
  }: Props<T> & { widget: AnyWidget<T> }) => {
216
215
  const htmlRef = useRef<HTMLDivElement>(null);
217
216
 
@@ -222,6 +221,9 @@ const LoadedSlot = <T extends AnyWidgetState>({
222
221
  Logger.error("Model not found for modelId", modelId);
223
222
  }
224
223
 
224
+ const css = model?.get("_css");
225
+ useMountCss(css, host);
226
+
225
227
  useEffect(() => {
226
228
  if (!htmlRef.current || !model) {
227
229
  return;
@@ -31,6 +31,12 @@ vi.mock("@/core/network/requests", () => ({
31
31
  }),
32
32
  }));
33
33
 
34
+ // Mock isStaticNotebook — default to false (normal mode)
35
+ const mockIsStatic = vi.fn().mockReturnValue(false);
36
+ vi.mock("@/core/static/static-state", () => ({
37
+ isStaticNotebook: () => mockIsStatic(),
38
+ }));
39
+
34
40
  // Helper to create a mock MarimoComm
35
41
  function createMockComm<T>() {
36
42
  return {
@@ -393,4 +399,51 @@ describe("ModelManager", () => {
393
399
 
394
400
  expect(BINDING_MANAGER.has(testId)).toBe(false);
395
401
  });
402
+
403
+ describe("static mode", () => {
404
+ beforeEach(() => {
405
+ mockIsStatic.mockReturnValue(true);
406
+ });
407
+
408
+ afterAll(() => {
409
+ mockIsStatic.mockReturnValue(false);
410
+ });
411
+
412
+ it("should create model with no-op comm in static mode", async () => {
413
+ await handleWidgetMessage(modelManager, {
414
+ model_id: testId,
415
+ message: {
416
+ method: "open",
417
+ state: { count: 42 },
418
+ buffer_paths: [],
419
+ buffers: [],
420
+ },
421
+ });
422
+
423
+ const model = await modelManager.get(testId);
424
+ expect(model.get("count")).toBe(42);
425
+
426
+ // save_changes should not call the real request client
427
+ model.set("count", 100);
428
+ model.save_changes();
429
+ expect(mockSendModelValue).not.toHaveBeenCalled();
430
+ });
431
+
432
+ it("should not throw on send in static mode", async () => {
433
+ await handleWidgetMessage(modelManager, {
434
+ model_id: testId,
435
+ message: {
436
+ method: "open",
437
+ state: { count: 0 },
438
+ buffer_paths: [],
439
+ buffers: [],
440
+ },
441
+ });
442
+
443
+ const model = await modelManager.get(testId);
444
+ // send() should silently no-op
445
+ await expect(model.send({ test: true })).resolves.toBeUndefined();
446
+ expect(mockSendModelValue).not.toHaveBeenCalled();
447
+ });
448
+ });
396
449
  });
@@ -5,6 +5,7 @@ import type { AnyModel } from "@anywidget/types";
5
5
  import { debounce } from "lodash-es";
6
6
  import type { NotificationMessageData } from "@/core/kernel/messages";
7
7
  import { getRequestClient } from "@/core/network/requests";
8
+ import { isStaticNotebook } from "@/core/static/static-state";
8
9
  import {
9
10
  decodeFromWire,
10
11
  serializeBuffersToBase64,
@@ -343,12 +344,14 @@ export async function handleWidgetMessage(
343
344
  return;
344
345
  }
345
346
 
346
- modelManager.create(
347
- modelId,
348
- (signal) =>
349
- new Model(
350
- stateWithBuffers,
351
- {
347
+ modelManager.create(modelId, (signal) => {
348
+ // In static exports there is no kernel, so comm calls are no-ops.
349
+ const comm: MarimoComm<ModelState> = isStaticNotebook()
350
+ ? {
351
+ sendUpdate: async () => undefined,
352
+ sendCustomMessage: async () => undefined,
353
+ }
354
+ : {
352
355
  async sendUpdate(changeData) {
353
356
  if (signal.aborted) {
354
357
  Logger.debug(
@@ -377,10 +380,10 @@ export async function handleWidgetMessage(
377
380
  buffers: buffers.map(dataViewToBase64),
378
381
  });
379
382
  },
380
- },
381
- signal,
382
- ),
383
- );
383
+ };
384
+
385
+ return new Model(stateWithBuffers, comm, signal);
386
+ });
384
387
  return;
385
388
  }
386
389
 
@@ -47,6 +47,7 @@ export const ChatPlugin = createPlugin<{ messages: UIMessage[] }>(
47
47
  maxHeight: z.number().optional(),
48
48
  config: configSchema,
49
49
  allowAttachments: z.union([z.boolean(), z.string().array()]),
50
+ disabled: z.boolean().default(false),
50
51
  }),
51
52
  )
52
53
  .withFunctions<PluginFunctions>({
@@ -76,6 +77,7 @@ export const ChatPlugin = createPlugin<{ messages: UIMessage[] }>(
76
77
  showConfigurationControls={props.data.showConfigurationControls}
77
78
  maxHeight={props.data.maxHeight}
78
79
  allowAttachments={props.data.allowAttachments}
80
+ disabled={props.data.disabled}
79
81
  config={props.data.config}
80
82
  get_chat_history={props.functions.get_chat_history}
81
83
  delete_chat_history={props.functions.delete_chat_history}
@@ -67,6 +67,7 @@ interface Props extends PluginFunctions {
67
67
  showConfigurationControls: boolean;
68
68
  maxHeight: number | undefined;
69
69
  allowAttachments: boolean | string[];
70
+ disabled: boolean;
70
71
  value: UIMessage[];
71
72
  setValue: (messages: UIMessage[]) => void;
72
73
  host: HTMLElement;
@@ -450,6 +451,9 @@ export const Chatbot: React.FC<Props> = (props) => {
450
451
  <form
451
452
  onSubmit={async (evt) => {
452
453
  evt.preventDefault();
454
+ if (props.disabled) {
455
+ return;
456
+ }
453
457
 
454
458
  const fileParts = files
455
459
  ? await convertToFileUIPart(files)
@@ -462,7 +466,12 @@ export const Chatbot: React.FC<Props> = (props) => {
462
466
  resetInput();
463
467
  }}
464
468
  ref={formRef}
465
- className="flex w-full border-t border-(--slate-6) px-2 py-1 items-center"
469
+ // biome-ignore lint/a11y/useSemanticElements: inert is used to disable the entire form
470
+ inert={props.disabled || undefined}
471
+ className={cn(
472
+ "flex w-full border-t border-(--slate-6) px-2 py-1 items-center",
473
+ props.disabled && "opacity-50 cursor-not-allowed",
474
+ )}
466
475
  >
467
476
  {props.showConfigurationControls && (
468
477
  <ConfigPopup config={config} onChange={setConfig} />
@@ -48,6 +48,7 @@ type TableData<T> = T[] | CsvURL;
48
48
  interface Data {
49
49
  label?: string | null;
50
50
  columns: ColumnDataTypes;
51
+ dataframeName?: string;
51
52
  pageSize: number;
52
53
  showDownload: boolean;
53
54
  lazy: boolean;
@@ -93,6 +94,7 @@ export const DataFramePlugin = createPlugin<S>("marimo-dataframe")
93
94
  label: z.string().nullish(),
94
95
  pageSize: z.number().default(5),
95
96
  showDownload: z.boolean().default(true),
97
+ dataframeName: z.string().optional(),
96
98
  columns: z
97
99
  .array(z.tuple([z.string().or(z.number()), z.string(), z.string()]))
98
100
  .transform((value) => {
@@ -176,6 +178,7 @@ const EMPTY: Transformations = {
176
178
  export const DataFrameComponent = memo(
177
179
  ({
178
180
  columns,
181
+ dataframeName,
179
182
  pageSize,
180
183
  showDownload,
181
184
  lazy,
@@ -326,6 +329,7 @@ export const DataFrameComponent = memo(
326
329
  fieldTypes={field_types}
327
330
  rowHeaders={row_headers || Arrays.EMPTY}
328
331
  showDownload={showDownload}
332
+ downloadFileName={dataframeName}
329
333
  download_as={download_as}
330
334
  enableSearch={false}
331
335
  showFilters={false}
@@ -35,6 +35,7 @@ export interface PlotProps {
35
35
  onRelayouting?: (event: PlotlyTypes.PlotRelayoutEvent) => void;
36
36
  onSelected?: (event: PlotlyTypes.PlotSelectionEvent) => void;
37
37
  onDeselect?: () => void;
38
+ onClick?: (event: PlotlyTypes.PlotMouseEvent) => void;
38
39
  onSunburstClick?: (event: PlotlyTypes.PlotMouseEvent) => void;
39
40
  onTreemapClick?: (event: PlotlyTypes.PlotMouseEvent) => void;
40
41
  onError?: (err: Error) => void;
@@ -48,6 +49,7 @@ const EVENT_NAMES = [
48
49
  "Relayouting",
49
50
  "Selected",
50
51
  "Deselect",
52
+ "Click",
51
53
  "SunburstClick",
52
54
  "TreemapClick",
53
55
  ] as const;
@@ -193,6 +193,24 @@ export const PlotlyComponent = memo(
193
193
  }));
194
194
  })}
195
195
  config={plotlyConfig}
196
+ onClick={useEvent((evt: Readonly<Plotly.PlotMouseEvent>) => {
197
+ if (!evt) {
198
+ return;
199
+ }
200
+ // Only handle clicks for chart types where box/lasso selection
201
+ // (onSelected) doesn't work, such as heatmaps.
202
+ const isHeatmap = evt.points.some(
203
+ (point) => point.data?.type === "heatmap",
204
+ );
205
+ if (!isHeatmap) {
206
+ return;
207
+ }
208
+ setValue((prev) => ({
209
+ ...prev,
210
+ points: extractPoints(evt.points),
211
+ indices: evt.points.map((point) => point.pointIndex),
212
+ }));
213
+ })}
196
214
  onSelected={useEvent((evt: Readonly<Plotly.PlotSelectionEvent>) => {
197
215
  if (!evt) {
198
216
  return;
@@ -224,6 +242,17 @@ PlotlyComponent.displayName = "PlotlyComponent";
224
242
  * instead of the ones that Plotly uses internally,
225
243
  * by using the hovertemplate.
226
244
  */
245
+ const STANDARD_POINT_KEYS: string[] = [
246
+ "x",
247
+ "y",
248
+ "z",
249
+ "lat",
250
+ "lon",
251
+ "curveNumber",
252
+ "pointNumber",
253
+ "pointIndex",
254
+ ];
255
+
227
256
  function extractPoints(
228
257
  points: Plotly.PlotDatum[],
229
258
  ): Record<AxisName, AxisDatum>[] {
@@ -238,6 +267,13 @@ function extractPoints(
238
267
  const hovertemplate = Array.isArray(point.data.hovertemplate)
239
268
  ? point.data.hovertemplate[0]
240
269
  : point.data.hovertemplate;
270
+
271
+ // For chart types with standard point keys (e.g. heatmaps),
272
+ // or when there's no hovertemplate, pick keys directly from the point.
273
+ if (!hovertemplate || point.data?.type === "heatmap") {
274
+ return pick(point, STANDARD_POINT_KEYS);
275
+ }
276
+
241
277
  // Update or create a parser
242
278
  parser = parser
243
279
  ? parser.update(hovertemplate)
@@ -10,8 +10,10 @@ export const ThemeProvider: React.FC<PropsWithChildren> = memo(
10
10
  const { theme } = useTheme();
11
11
  useLayoutEffect(() => {
12
12
  document.body.classList.add(theme, `${theme}-theme`);
13
+ document.body.dataset.theme = theme;
13
14
  return () => {
14
15
  document.body.classList.remove(theme, `${theme}-theme`);
16
+ delete document.body.dataset.theme;
15
17
  };
16
18
  }, [theme]);
17
19