@marimo-team/islands 0.19.5-dev4 → 0.19.5-dev43

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 (55) hide show
  1. package/dist/{ConnectedDataExplorerComponent-D5KcOOzu.js → ConnectedDataExplorerComponent-DjQ_E5BA.js} +3 -3
  2. package/dist/assets/__vite-browser-external-DRa9CT_O.js +1 -0
  3. package/dist/assets/{worker-BR7KVExK.js → worker-SqntmiwV.js} +2 -2
  4. package/dist/{glide-data-editor-DsVDCmV2.js → glide-data-editor-zEomQJ3U.js} +2 -2
  5. package/dist/main.js +290 -244
  6. package/dist/{mermaid-DZjjc-kI.js → mermaid-D7wtYc6C.js} +2 -2
  7. package/dist/{spec-B1PGDiGh.js → spec-Cif4tBMJ.js} +1 -1
  8. package/dist/style.css +1 -1
  9. package/dist/{types-CbQF8CBX.js → types-BQOP2pRy.js} +1 -1
  10. package/dist/{useAsyncData-TLXJC7yx.js → useAsyncData-kqbhbSuf.js} +1 -1
  11. package/dist/{useDeepCompareMemoize-DVnEG7jx.js → useDeepCompareMemoize-B2QEm3jo.js} +1 -1
  12. package/dist/{useTheme-BllQjRdW.js → useTheme-CVr6Gb_R.js} +4 -1
  13. package/dist/{vega-component-B2QrGnW8.js → vega-component-DAeU1_cV.js} +3 -3
  14. package/package.json +1 -1
  15. package/src/__mocks__/requests.ts +1 -0
  16. package/src/components/ai/__tests__/ai-utils.test.ts +276 -0
  17. package/src/components/ai/ai-utils.ts +101 -0
  18. package/src/components/app-config/ai-config.tsx +56 -16
  19. package/src/components/app-config/user-config-form.tsx +63 -1
  20. package/src/components/chat/chat-panel.tsx +4 -4
  21. package/src/components/data-table/cell-utils.ts +10 -0
  22. package/src/components/editor/Output.tsx +21 -14
  23. package/src/components/editor/actions/useCellActionButton.tsx +2 -2
  24. package/src/components/editor/actions/useNotebookActions.tsx +61 -21
  25. package/src/components/editor/cell/cell-actions.tsx +6 -1
  26. package/src/components/editor/controls/Controls.tsx +1 -8
  27. package/src/components/editor/file-tree/file-explorer.tsx +4 -2
  28. package/src/components/editor/file-tree/file-viewer.tsx +9 -6
  29. package/src/components/editor/navigation/navigation.ts +39 -1
  30. package/src/components/editor/renderMimeIcon.tsx +2 -0
  31. package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +1 -1
  32. package/src/core/ai/model-registry.ts +21 -3
  33. package/src/core/codemirror/language/panel/panel.tsx +3 -0
  34. package/src/core/codemirror/language/panel/sql.tsx +6 -2
  35. package/src/core/config/config-schema.ts +5 -1
  36. package/src/core/config/config.ts +4 -0
  37. package/src/core/config/feature-flag.tsx +2 -0
  38. package/src/core/export/__tests__/hooks.test.ts +120 -1
  39. package/src/core/export/hooks.ts +48 -18
  40. package/src/core/islands/bridge.ts +1 -0
  41. package/src/core/lsp/__tests__/transport.test.ts +149 -0
  42. package/src/core/lsp/transport.ts +48 -0
  43. package/src/core/network/requests-lazy.ts +1 -0
  44. package/src/core/network/requests-network.ts +9 -0
  45. package/src/core/network/requests-static.ts +1 -0
  46. package/src/core/network/requests-toasting.tsx +1 -0
  47. package/src/core/network/types.ts +2 -0
  48. package/src/core/wasm/bridge.ts +1 -0
  49. package/src/css/app/Cell.css +0 -2
  50. package/src/plugins/layout/TexPlugin.tsx +7 -5
  51. package/src/utils/__tests__/download.test.tsx +492 -0
  52. package/src/utils/download.ts +161 -6
  53. package/src/utils/filenames.ts +3 -0
  54. package/dist/assets/__vite-browser-external-CgHmDpAZ.js +0 -1
  55. package/src/components/export/export-output-button.tsx +0 -14
@@ -1,17 +1,140 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
  import { toPng } from "html-to-image";
3
3
  import { toast } from "@/components/ui/use-toast";
4
+ import { type CellId, CellOutputId } from "@/core/cells/ids";
5
+ import { getRequestClient } from "@/core/network/requests";
4
6
  import { Filenames } from "@/utils/filenames";
7
+ import { Paths } from "@/utils/paths";
8
+ import { prettyError } from "./errors";
9
+ import { Logger } from "./Logger";
5
10
 
6
- export async function downloadHTMLAsImage(
7
- element: HTMLElement,
11
+ /**
12
+ * Show a loading toast while an async operation is in progress.
13
+ * Automatically dismisses the toast when the operation completes or fails.
14
+ */
15
+ export async function withLoadingToast<T>(
16
+ title: string,
17
+ fn: () => Promise<T>,
18
+ ): Promise<T> {
19
+ const loadingToast = toast({
20
+ title,
21
+ duration: Infinity,
22
+ });
23
+ try {
24
+ const result = await fn();
25
+ loadingToast.dismiss();
26
+ return result;
27
+ } catch (error) {
28
+ loadingToast.dismiss();
29
+ throw error;
30
+ }
31
+ }
32
+
33
+ function findElementForCell(cellId: CellId): HTMLElement | undefined {
34
+ const element = document.getElementById(CellOutputId.create(cellId));
35
+ if (!element) {
36
+ Logger.error(`Output element not found for cell ${cellId}`);
37
+ return;
38
+ }
39
+ return element;
40
+ }
41
+
42
+ /**
43
+ * Reference counter for body.printing class to handle concurrent screenshot captures.
44
+ * Only adds the class when count goes 0→1, only removes when count goes 1→0.
45
+ */
46
+ let bodyPrintingRefCount = 0;
47
+
48
+ function acquireBodyPrinting() {
49
+ bodyPrintingRefCount++;
50
+ if (bodyPrintingRefCount === 1) {
51
+ document.body.classList.add("printing");
52
+ }
53
+ }
54
+
55
+ function releaseBodyPrinting() {
56
+ bodyPrintingRefCount--;
57
+ if (bodyPrintingRefCount === 0) {
58
+ document.body.classList.remove("printing");
59
+ }
60
+ }
61
+
62
+ /*
63
+ * Prepare a cell element for screenshot capture.
64
+ * Returns a cleanup function that should be called when the screenshot is complete.
65
+ */
66
+ function prepareCellElementForScreenshot(element: HTMLElement) {
67
+ element.classList.add("printing-output");
68
+ acquireBodyPrinting();
69
+ const originalOverflow = element.style.overflow;
70
+ element.style.overflow = "auto";
71
+
72
+ return () => {
73
+ element.classList.remove("printing-output");
74
+ releaseBodyPrinting();
75
+ element.style.overflow = originalOverflow;
76
+ };
77
+ }
78
+
79
+ /**
80
+ * Capture a cell output as a PNG data URL.
81
+ */
82
+ export async function getImageDataUrlForCell(
83
+ cellId: CellId,
84
+ ): Promise<string | undefined> {
85
+ const element = findElementForCell(cellId);
86
+ if (!element) {
87
+ return;
88
+ }
89
+ const cleanup = prepareCellElementForScreenshot(element);
90
+
91
+ try {
92
+ return await toPng(element);
93
+ } finally {
94
+ cleanup();
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Download a cell output as a PNG image file.
100
+ */
101
+ export async function downloadCellOutputAsImage(
102
+ cellId: CellId,
8
103
  filename: string,
9
104
  ) {
105
+ const element = findElementForCell(cellId);
106
+ if (!element) {
107
+ return;
108
+ }
109
+
110
+ await downloadHTMLAsImage({
111
+ element,
112
+ filename,
113
+ prepare: prepareCellElementForScreenshot,
114
+ });
115
+ }
116
+
117
+ export async function downloadHTMLAsImage(opts: {
118
+ element: HTMLElement;
119
+ filename: string;
120
+ prepare?: (element: HTMLElement) => () => void;
121
+ }) {
122
+ const { element, filename, prepare } = opts;
123
+
10
124
  // Capture current scroll position
11
125
  const appEl = document.getElementById("App");
12
126
  const currentScrollY = appEl?.scrollTop ?? 0;
13
- // Add classnames for printing
14
- document.body.classList.add("printing");
127
+
128
+ let cleanup: (() => void) | undefined;
129
+ if (prepare) {
130
+ // Let the prepare function handle adding classes (e.g., body.printing)
131
+ cleanup = prepare(element);
132
+ } else {
133
+ // When no prepare function is provided (e.g., downloading full notebook),
134
+ // add body.printing ourselves
135
+ document.body.classList.add("printing");
136
+ }
137
+
15
138
  try {
16
139
  // Get screenshot
17
140
  const dataUrl = await toPng(element);
@@ -23,8 +146,10 @@ export async function downloadHTMLAsImage(
23
146
  variant: "danger",
24
147
  });
25
148
  } finally {
26
- // Remove classnames for printing
27
- document.body.classList.remove("printing");
149
+ cleanup?.();
150
+ if (document.body.classList.contains("printing")) {
151
+ document.body.classList.remove("printing");
152
+ }
28
153
  // Restore scroll position
29
154
  requestAnimationFrame(() => {
30
155
  appEl?.scrollTo(0, currentScrollY);
@@ -45,3 +170,33 @@ export function downloadBlob(blob: Blob, filename: string) {
45
170
  downloadByURL(url, filename);
46
171
  URL.revokeObjectURL(url);
47
172
  }
173
+
174
+ /**
175
+ * Download the current notebook as a PDF file.
176
+ *
177
+ * WebPDF only requires Chromium to be installed.
178
+ * Standard PDF requires Pandoc & TeX (~few GBs) but is of higher quality.
179
+ */
180
+ export async function downloadAsPDF(opts: {
181
+ filename: string;
182
+ webpdf: boolean;
183
+ }) {
184
+ const client = getRequestClient();
185
+ const { filename, webpdf } = opts;
186
+
187
+ try {
188
+ const pdfBlob = await client.exportAsPDF({
189
+ webpdf,
190
+ });
191
+
192
+ const filenameWithoutPath = Paths.basename(filename);
193
+ downloadBlob(pdfBlob, Filenames.toPDF(filenameWithoutPath));
194
+ } catch (error) {
195
+ toast({
196
+ title: "Failed to download",
197
+ description: prettyError(error),
198
+ variant: "danger",
199
+ });
200
+ throw error;
201
+ }
202
+ }
@@ -9,6 +9,9 @@ export const Filenames = {
9
9
  toPNG: (filename: string): string => {
10
10
  return Filenames.replace(filename, "png");
11
11
  },
12
+ toPDF: (filename: string): string => {
13
+ return Filenames.replace(filename, "pdf");
14
+ },
12
15
  toPY: (filename: string): string => {
13
16
  return Filenames.replace(filename, "py");
14
17
  },
@@ -1 +0,0 @@
1
- import{t as e}from"./worker-BR7KVExK.js";var t=e(((e,t)=>{t.exports={}}));export default t();
@@ -1,14 +0,0 @@
1
- /* Copyright 2026 Marimo. All rights reserved. */
2
-
3
- import { type CellId, CellOutputId } from "@/core/cells/ids";
4
- import { downloadHTMLAsImage } from "@/utils/download";
5
-
6
- export function downloadCellOutput(cellId: CellId) {
7
- const output = document.getElementById(CellOutputId.create(cellId));
8
- if (output) {
9
- output.classList.add("printing-output");
10
- downloadHTMLAsImage(output, "result.png").finally(() => {
11
- output.classList.remove("printing-output");
12
- });
13
- }
14
- }