@marimo-team/islands 0.19.7-dev19 → 0.19.7-dev20

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 CHANGED
@@ -60930,6 +60930,17 @@ ${r}
60930
60930
  VY
60931
60931
  ]
60932
60932
  }), r[18] = BY, r[19] = VY, r[20] = IY, r[21] = HY) : HY = r[21], HY;
60933
+ }, Filenames = {
60934
+ toMarkdown: (e) => Filenames.replace(e, "md"),
60935
+ toHTML: (e) => Filenames.replace(e, "html"),
60936
+ toPNG: (e) => Filenames.replace(e, "png"),
60937
+ toPDF: (e) => Filenames.replace(e, "pdf"),
60938
+ toPY: (e) => Filenames.replace(e, "py"),
60939
+ withoutExtension: (e) => {
60940
+ let r = e.split(".");
60941
+ return r.length === 1 ? e : r.slice(0, -1).join(".");
60942
+ },
60943
+ replace: (e, r) => e.endsWith(`.${r}`) ? e : `${Filenames.withoutExtension(e)}.${r}`
60933
60944
  };
60934
60945
  function resolveUrl(e, r) {
60935
60946
  if (e.match(/^[a-z]+:\/\//i)) return e;
@@ -61370,21 +61381,27 @@ ${r}
61370
61381
  let { width: c, height: d } = getImageSize(e, r), f = await createImage(await toSvg(e, r)), _ = document.createElement("canvas"), v = _.getContext("2d"), y = r.pixelRatio || getPixelRatio(), S = r.canvasWidth || c, w = r.canvasHeight || d;
61371
61382
  return _.width = S * y, _.height = w * y, r.skipAutoScale || checkCanvasDimensions(_), _.style.width = `${S}`, _.style.height = `${w}`, r.backgroundColor && (v.fillStyle = r.backgroundColor, v.fillRect(0, 0, _.width, _.height)), v.drawImage(f, 0, 0, _.width, _.height), _;
61372
61383
  }
61373
- async function toPng(e, r = {}) {
61384
+ async function toPng$1(e, r = {}) {
61374
61385
  return (await toCanvas(e, r)).toDataURL();
61375
61386
  }
61376
- const Filenames = {
61377
- toMarkdown: (e) => Filenames.replace(e, "md"),
61378
- toHTML: (e) => Filenames.replace(e, "html"),
61379
- toPNG: (e) => Filenames.replace(e, "png"),
61380
- toPDF: (e) => Filenames.replace(e, "pdf"),
61381
- toPY: (e) => Filenames.replace(e, "py"),
61382
- withoutExtension: (e) => {
61383
- let r = e.split(".");
61384
- return r.length === 1 ? e : r.slice(0, -1).join(".");
61387
+ const defaultHtmlToImageOptions = {
61388
+ filter: (e) => {
61389
+ try {
61390
+ return "classList" in e ? !e.classList.contains("mpl-toolbar") : true;
61391
+ } catch (e2) {
61392
+ return Logger.error("Error filtering node:", e2), true;
61393
+ }
61385
61394
  },
61386
- replace: (e, r) => e.endsWith(`.${r}`) ? e : `${Filenames.withoutExtension(e)}.${r}`
61395
+ onImageErrorHandler: (e) => {
61396
+ Logger.error("Error loading image:", e);
61397
+ }
61387
61398
  };
61399
+ function toPng(e, r) {
61400
+ return toPng$1(e, {
61401
+ ...defaultHtmlToImageOptions,
61402
+ ...r
61403
+ });
61404
+ }
61388
61405
  async function downloadHTMLAsImage(e) {
61389
61406
  let { element: r, filename: c, prepare: d } = e, f = document.getElementById("App"), _ = (f == null ? void 0 : f.scrollTop) ?? 0, v;
61390
61407
  d ? v = d(r) : document.body.classList.add("printing");
@@ -72896,7 +72913,7 @@ Image URL: ${r.imageUrl}`)), contextToXml({
72896
72913
  return Logger.warn("Failed to get version from mount config"), null;
72897
72914
  }
72898
72915
  }
72899
- const marimoVersionAtom = atom(getVersionFromMountConfig() || "0.19.7-dev19"), showCodeInRunModeAtom = atom(true);
72916
+ const marimoVersionAtom = atom(getVersionFromMountConfig() || "0.19.7-dev20"), showCodeInRunModeAtom = atom(true);
72900
72917
  atom(null);
72901
72918
  var import_compiler_runtime$89 = require_compiler_runtime();
72902
72919
  function useKeydownOnElement(e, r) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marimo-team/islands",
3
- "version": "0.19.7-dev19",
3
+ "version": "0.19.7-dev20",
4
4
  "main": "dist/main.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -128,7 +128,7 @@
128
128
  "eslint-plugin-header": "^3.1.1",
129
129
  "htm": "^3.1.1",
130
130
  "html-react-parser": "^5.2.11",
131
- "html-to-image": "^1.11.13",
131
+ "html-to-image": "1.11.13",
132
132
  "humanize-duration": "^3.33.2",
133
133
  "iconify-icon": "^2.3.0",
134
134
  "jotai": "^2.16.1",
@@ -2,7 +2,6 @@
2
2
 
3
3
  import type { Completion } from "@codemirror/autocomplete";
4
4
  import type { FileUIPart } from "ai";
5
- import { toPng } from "html-to-image";
6
5
  import { processOutput } from "@/components/editor/output/console/process-output";
7
6
  import { type NotebookState, notebookAtom } from "@/core/cells/cells";
8
7
  import { type CellId, CellOutputId } from "@/core/cells/ids";
@@ -10,6 +9,7 @@ import { displayCellName } from "@/core/cells/names";
10
9
  import { isOutputEmpty } from "@/core/cells/outputs";
11
10
  import type { OutputMessage } from "@/core/kernel/messages";
12
11
  import type { JotaiStore } from "@/core/state/jotai";
12
+ import { toPng } from "@/utils/html-to-image";
13
13
  import { Logger } from "@/utils/Logger";
14
14
  import { type AIContextItem, AIContextProvider } from "../registry";
15
15
  import { contextToXml } from "../utils";
@@ -137,7 +137,13 @@ describe("useEnrichCellOutputs", () => {
137
137
  expect(document.getElementById).toHaveBeenCalledWith(
138
138
  CellOutputId.create(cellId),
139
139
  );
140
- expect(toPng).toHaveBeenCalledWith(mockElement);
140
+ expect(toPng).toHaveBeenCalledWith(
141
+ mockElement,
142
+ expect.objectContaining({
143
+ filter: expect.any(Function),
144
+ onImageErrorHandler: expect.any(Function),
145
+ }),
146
+ );
141
147
  expect(output).toEqual({
142
148
  [cellId]: ["image/png", mockDataUrl],
143
149
  });
@@ -155,7 +155,13 @@ describe("getImageDataUrlForCell", () => {
155
155
  const result = await getImageDataUrlForCell("cell-1" as CellId);
156
156
 
157
157
  expect(result).toBe(mockDataUrl);
158
- expect(toPng).toHaveBeenCalledWith(mockElement);
158
+ expect(toPng).toHaveBeenCalledWith(
159
+ mockElement,
160
+ expect.objectContaining({
161
+ filter: expect.any(Function),
162
+ onImageErrorHandler: expect.any(Function),
163
+ }),
164
+ );
159
165
  });
160
166
 
161
167
  it("should add printing classes before capture when enablePrintMode is true", async () => {
@@ -352,7 +358,13 @@ describe("downloadHTMLAsImage", () => {
352
358
 
353
359
  await downloadHTMLAsImage({ element: mockElement, filename: "test" });
354
360
 
355
- expect(toPng).toHaveBeenCalledWith(mockElement);
361
+ expect(toPng).toHaveBeenCalledWith(
362
+ mockElement,
363
+ expect.objectContaining({
364
+ filter: expect.any(Function),
365
+ onImageErrorHandler: expect.any(Function),
366
+ }),
367
+ );
356
368
  expect(mockAnchor.href).toBe(mockDataUrl);
357
369
  expect(mockAnchor.download).toBe("test.png");
358
370
  expect(mockAnchor.click).toHaveBeenCalled();
@@ -488,7 +500,13 @@ describe("downloadCellOutputAsImage", () => {
488
500
 
489
501
  await downloadCellOutputAsImage("cell-1" as CellId, "result");
490
502
 
491
- expect(toPng).toHaveBeenCalledWith(mockElement);
503
+ expect(toPng).toHaveBeenCalledWith(
504
+ mockElement,
505
+ expect.objectContaining({
506
+ filter: expect.any(Function),
507
+ onImageErrorHandler: expect.any(Function),
508
+ }),
509
+ );
492
510
  expect(mockAnchor.download).toBe("result.png");
493
511
  });
494
512
 
@@ -1,11 +1,12 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
- import { toPng } from "html-to-image";
2
+
3
3
  import { toast } from "@/components/ui/use-toast";
4
4
  import { type CellId, CellOutputId } from "@/core/cells/ids";
5
5
  import { getRequestClient } from "@/core/network/requests";
6
6
  import { Filenames } from "@/utils/filenames";
7
7
  import { Paths } from "@/utils/paths";
8
8
  import { prettyError } from "./errors";
9
+ import { toPng } from "./html-to-image";
9
10
  import { Logger } from "./Logger";
10
11
 
11
12
  /**
@@ -0,0 +1,41 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+ import { toPng as htmlToImageToPng } from "html-to-image";
3
+ import { Logger } from "./Logger";
4
+
5
+ export type HtmlToImageOptions = Parameters<typeof htmlToImageToPng>[1];
6
+
7
+ /**
8
+ * Default options for html-to-image conversions.
9
+ * These handle common edge cases like filtering out toolbars and logging errors.
10
+ */
11
+ export const defaultHtmlToImageOptions: HtmlToImageOptions = {
12
+ filter: (node) => {
13
+ try {
14
+ if ("classList" in node) {
15
+ // Filter out matplotlib toolbars
16
+ return !node.classList.contains("mpl-toolbar");
17
+ }
18
+ return true;
19
+ } catch (error) {
20
+ Logger.error("Error filtering node:", error);
21
+ return true;
22
+ }
23
+ },
24
+ onImageErrorHandler: (event) => {
25
+ Logger.error("Error loading image:", event);
26
+ },
27
+ };
28
+
29
+ /**
30
+ * Convert an HTML element to a PNG data URL.
31
+ * This is a wrapper around html-to-image's toPng with default options applied.
32
+ */
33
+ export function toPng(
34
+ element: HTMLElement,
35
+ options?: HtmlToImageOptions,
36
+ ): Promise<string> {
37
+ return htmlToImageToPng(element, {
38
+ ...defaultHtmlToImageOptions,
39
+ ...options,
40
+ });
41
+ }