@marimo-team/islands 0.19.7-dev41 → 0.19.7-dev42

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
@@ -73168,7 +73168,7 @@ Image URL: ${r.imageUrl}`)), contextToXml({
73168
73168
  return Logger.warn("Failed to get version from mount config"), null;
73169
73169
  }
73170
73170
  }
73171
- const marimoVersionAtom = atom(getVersionFromMountConfig() || "0.19.7-dev41"), showCodeInRunModeAtom = atom(true);
73171
+ const marimoVersionAtom = atom(getVersionFromMountConfig() || "0.19.7-dev42"), showCodeInRunModeAtom = atom(true);
73172
73172
  atom(null);
73173
73173
  var import_compiler_runtime$88 = require_compiler_runtime();
73174
73174
  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-dev41",
3
+ "version": "0.19.7-dev42",
4
4
  "main": "dist/main.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -166,13 +166,63 @@ describe("getImageDataUrlForCell", () => {
166
166
  );
167
167
  });
168
168
 
169
- it("should restore original overflow style after capture", async () => {
169
+ it("should pass style options to prevent clipping", async () => {
170
+ vi.mocked(toPng).mockResolvedValue(mockDataUrl);
171
+
172
+ await getImageDataUrlForCell("cell-1" as CellId);
173
+
174
+ expect(toPng).toHaveBeenCalledWith(
175
+ mockElement,
176
+ expect.objectContaining({
177
+ style: {
178
+ maxHeight: "none",
179
+ overflow: "visible",
180
+ },
181
+ }),
182
+ );
183
+ });
184
+
185
+ it("should pass scrollHeight as height option", async () => {
186
+ // Set up element with scrollHeight
187
+ Object.defineProperty(mockElement, "scrollHeight", {
188
+ value: 500,
189
+ configurable: true,
190
+ });
191
+ vi.mocked(toPng).mockResolvedValue(mockDataUrl);
192
+
193
+ await getImageDataUrlForCell("cell-1" as CellId);
194
+
195
+ expect(toPng).toHaveBeenCalledWith(
196
+ mockElement,
197
+ expect.objectContaining({
198
+ height: 500,
199
+ }),
200
+ );
201
+ });
202
+
203
+ it("should pass scrollbar hiding styles via extraStyleContent", async () => {
204
+ vi.mocked(toPng).mockResolvedValue(mockDataUrl);
205
+
206
+ await getImageDataUrlForCell("cell-1" as CellId);
207
+
208
+ expect(toPng).toHaveBeenCalledWith(
209
+ mockElement,
210
+ expect.objectContaining({
211
+ extraStyleContent: expect.stringContaining("scrollbar-width: none"),
212
+ }),
213
+ );
214
+ });
215
+
216
+ it("should not modify the live DOM element", async () => {
170
217
  mockElement.style.overflow = "hidden";
218
+ mockElement.style.maxHeight = "100px";
171
219
  vi.mocked(toPng).mockResolvedValue(mockDataUrl);
172
220
 
173
221
  await getImageDataUrlForCell("cell-1" as CellId);
174
222
 
223
+ // DOM should remain unchanged
175
224
  expect(mockElement.style.overflow).toBe("hidden");
225
+ expect(mockElement.style.maxHeight).toBe("100px");
176
226
  });
177
227
 
178
228
  it("should throw error on failure", async () => {
@@ -183,34 +233,20 @@ describe("getImageDataUrlForCell", () => {
183
233
  );
184
234
  });
185
235
 
186
- it("should cleanup even on failure", async () => {
187
- mockElement.style.overflow = "scroll";
188
- vi.mocked(toPng).mockRejectedValue(new Error("Capture failed"));
189
-
190
- await expect(getImageDataUrlForCell("cell-1" as CellId)).rejects.toThrow();
191
-
192
- expect(document.body.classList.contains("printing")).toBe(false);
193
- expect(mockElement.style.overflow).toBe("scroll");
194
- });
195
-
196
236
  it("should handle concurrent captures correctly", async () => {
197
237
  // Create a second element
198
238
  const mockElement2 = document.createElement("div");
199
239
  mockElement2.id = CellOutputId.create("cell-2" as CellId);
200
240
  document.body.append(mockElement2);
201
241
 
202
- vi.mocked(toPng).mockImplementation(async () => {
203
- // body.printing should not be added during cell captures
204
- expect(document.body.classList.contains("printing")).toBe(false);
205
- return mockDataUrl;
206
- });
242
+ vi.mocked(toPng).mockResolvedValue(mockDataUrl);
207
243
 
208
244
  const capture1 = getImageDataUrlForCell("cell-1" as CellId);
209
245
  const capture2 = getImageDataUrlForCell("cell-2" as CellId);
210
246
 
211
247
  await Promise.all([capture1, capture2]);
212
248
 
213
- expect(document.body.classList.contains("printing")).toBe(false);
249
+ expect(toPng).toHaveBeenCalledTimes(2);
214
250
 
215
251
  mockElement2.remove();
216
252
  });
@@ -266,23 +302,6 @@ describe("downloadHTMLAsImage", () => {
266
302
  expect(mockAnchor.click).toHaveBeenCalled();
267
303
  });
268
304
 
269
- it("should add body.printing class without prepare function", async () => {
270
- vi.mocked(toPng).mockImplementation(async () => {
271
- expect(document.body.classList.contains("printing")).toBe(true);
272
- return mockDataUrl;
273
- });
274
-
275
- await downloadHTMLAsImage({ element: mockElement, filename: "test" });
276
- });
277
-
278
- it("should remove body.printing class after download without prepare", async () => {
279
- vi.mocked(toPng).mockResolvedValue(mockDataUrl);
280
-
281
- await downloadHTMLAsImage({ element: mockElement, filename: "test" });
282
-
283
- expect(document.body.classList.contains("printing")).toBe(false);
284
- });
285
-
286
305
  it("should use prepare function when provided", async () => {
287
306
  vi.mocked(toPng).mockResolvedValue(mockDataUrl);
288
307
  const cleanup = vi.fn();
@@ -406,24 +425,32 @@ describe("downloadCellOutputAsImage", () => {
406
425
  expect(mockAnchor.download).toBe("result.png");
407
426
  });
408
427
 
409
- it("should apply cell-specific preparation", async () => {
410
- vi.mocked(toPng).mockImplementation(async () => {
411
- // Check that cell-specific classes are applied
412
- expect(mockElement.style.overflow).toBe("visible");
413
- return mockDataUrl;
414
- });
428
+ it("should pass style options to toPng for full content capture", async () => {
429
+ vi.mocked(toPng).mockResolvedValue(mockDataUrl);
415
430
 
416
431
  await downloadCellOutputAsImage("cell-1" as CellId, "result");
432
+
433
+ expect(toPng).toHaveBeenCalledWith(
434
+ mockElement,
435
+ expect.objectContaining({
436
+ style: {
437
+ maxHeight: "none",
438
+ overflow: "visible",
439
+ },
440
+ }),
441
+ );
417
442
  });
418
443
 
419
- it("should cleanup after download", async () => {
420
- mockElement.style.overflow = "visible";
444
+ it("should not modify the live DOM element", async () => {
445
+ mockElement.style.overflow = "hidden";
446
+ mockElement.style.maxHeight = "100px";
421
447
  vi.mocked(toPng).mockResolvedValue(mockDataUrl);
422
448
 
423
449
  await downloadCellOutputAsImage("cell-1" as CellId, "result");
424
450
 
425
- expect(document.body.classList.contains("printing")).toBe(false);
426
- expect(mockElement.style.overflow).toBe("visible");
451
+ // DOM should remain unchanged
452
+ expect(mockElement.style.overflow).toBe("hidden");
453
+ expect(mockElement.style.maxHeight).toBe("100px");
427
454
  });
428
455
  });
429
456
 
@@ -46,24 +46,6 @@ function findElementForCell(cellId: CellId): HTMLElement | undefined {
46
46
  return element;
47
47
  }
48
48
 
49
- /**
50
- * Prepare a cell element for screenshot capture.
51
- *
52
- * @param element - The cell output element to prepare
53
- * @returns A cleanup function to restore the element's original state
54
- */
55
- function prepareCellElementForScreenshot(element: HTMLElement) {
56
- const originalOverflow = element.style.overflow;
57
- const maxHeight = element.style.maxHeight;
58
- element.style.overflow = "visible";
59
- element.style.maxHeight = "none";
60
-
61
- return () => {
62
- element.style.overflow = originalOverflow;
63
- element.style.maxHeight = maxHeight;
64
- };
65
- }
66
-
67
49
  const THRESHOLD_TIME_MS = 500;
68
50
  const HIDE_SCROLLBAR_STYLES = `
69
51
  * { scrollbar-width: none; -ms-overflow-style: none; }
@@ -89,25 +71,26 @@ export async function getImageDataUrlForCell(
89
71
  return iframeDataUrl;
90
72
  }
91
73
 
92
- const cleanup = prepareCellElementForScreenshot(element);
93
-
94
- try {
95
- const startTime = Date.now();
96
- const dataUrl = await toPng(element, {
97
- extraStyleContent: HIDE_SCROLLBAR_STYLES,
98
- });
99
- const timeTaken = Date.now() - startTime;
100
- if (timeTaken > THRESHOLD_TIME_MS) {
101
- Logger.debug(
102
- "toPng operation for element",
103
- element,
104
- `took ${timeTaken} ms (exceeds threshold)`,
105
- );
106
- }
107
- return dataUrl;
108
- } finally {
109
- cleanup();
74
+ const startTime = Date.now();
75
+ const dataUrl = await toPng(element, {
76
+ extraStyleContent: HIDE_SCROLLBAR_STYLES,
77
+ // Add these styles so the element output is not clipped
78
+ // Width can be clipped since pdf has limited width
79
+ style: {
80
+ maxHeight: "none",
81
+ overflow: "visible",
82
+ },
83
+ height: element.scrollHeight,
84
+ });
85
+ const timeTaken = Date.now() - startTime;
86
+ if (timeTaken > THRESHOLD_TIME_MS) {
87
+ Logger.debug(
88
+ "toPng operation for element",
89
+ element,
90
+ `took ${timeTaken} ms (exceeds threshold)`,
91
+ );
110
92
  }
93
+ return dataUrl;
111
94
  }
112
95
 
113
96
  /**