@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.
- package/dist/{ConnectedDataExplorerComponent-D5KcOOzu.js → ConnectedDataExplorerComponent-DjQ_E5BA.js} +3 -3
- package/dist/assets/__vite-browser-external-DRa9CT_O.js +1 -0
- package/dist/assets/{worker-BR7KVExK.js → worker-SqntmiwV.js} +2 -2
- package/dist/{glide-data-editor-DsVDCmV2.js → glide-data-editor-zEomQJ3U.js} +2 -2
- package/dist/main.js +290 -244
- package/dist/{mermaid-DZjjc-kI.js → mermaid-D7wtYc6C.js} +2 -2
- package/dist/{spec-B1PGDiGh.js → spec-Cif4tBMJ.js} +1 -1
- package/dist/style.css +1 -1
- package/dist/{types-CbQF8CBX.js → types-BQOP2pRy.js} +1 -1
- package/dist/{useAsyncData-TLXJC7yx.js → useAsyncData-kqbhbSuf.js} +1 -1
- package/dist/{useDeepCompareMemoize-DVnEG7jx.js → useDeepCompareMemoize-B2QEm3jo.js} +1 -1
- package/dist/{useTheme-BllQjRdW.js → useTheme-CVr6Gb_R.js} +4 -1
- package/dist/{vega-component-B2QrGnW8.js → vega-component-DAeU1_cV.js} +3 -3
- package/package.json +1 -1
- package/src/__mocks__/requests.ts +1 -0
- package/src/components/ai/__tests__/ai-utils.test.ts +276 -0
- package/src/components/ai/ai-utils.ts +101 -0
- package/src/components/app-config/ai-config.tsx +56 -16
- package/src/components/app-config/user-config-form.tsx +63 -1
- package/src/components/chat/chat-panel.tsx +4 -4
- package/src/components/data-table/cell-utils.ts +10 -0
- package/src/components/editor/Output.tsx +21 -14
- package/src/components/editor/actions/useCellActionButton.tsx +2 -2
- package/src/components/editor/actions/useNotebookActions.tsx +61 -21
- package/src/components/editor/cell/cell-actions.tsx +6 -1
- package/src/components/editor/controls/Controls.tsx +1 -8
- package/src/components/editor/file-tree/file-explorer.tsx +4 -2
- package/src/components/editor/file-tree/file-viewer.tsx +9 -6
- package/src/components/editor/navigation/navigation.ts +39 -1
- package/src/components/editor/renderMimeIcon.tsx +2 -0
- package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +1 -1
- package/src/core/ai/model-registry.ts +21 -3
- package/src/core/codemirror/language/panel/panel.tsx +3 -0
- package/src/core/codemirror/language/panel/sql.tsx +6 -2
- package/src/core/config/config-schema.ts +5 -1
- package/src/core/config/config.ts +4 -0
- package/src/core/config/feature-flag.tsx +2 -0
- package/src/core/export/__tests__/hooks.test.ts +120 -1
- package/src/core/export/hooks.ts +48 -18
- package/src/core/islands/bridge.ts +1 -0
- package/src/core/lsp/__tests__/transport.test.ts +149 -0
- package/src/core/lsp/transport.ts +48 -0
- package/src/core/network/requests-lazy.ts +1 -0
- package/src/core/network/requests-network.ts +9 -0
- package/src/core/network/requests-static.ts +1 -0
- package/src/core/network/requests-toasting.tsx +1 -0
- package/src/core/network/types.ts +2 -0
- package/src/core/wasm/bridge.ts +1 -0
- package/src/css/app/Cell.css +0 -2
- package/src/plugins/layout/TexPlugin.tsx +7 -5
- package/src/utils/__tests__/download.test.tsx +492 -0
- package/src/utils/download.ts +161 -6
- package/src/utils/filenames.ts +3 -0
- package/dist/assets/__vite-browser-external-CgHmDpAZ.js +0 -1
- package/src/components/export/export-output-button.tsx +0 -14
package/src/utils/download.ts
CHANGED
|
@@ -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
|
-
|
|
7
|
-
|
|
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
|
-
|
|
14
|
-
|
|
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
|
-
|
|
27
|
-
document.body.classList.
|
|
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
|
+
}
|
package/src/utils/filenames.ts
CHANGED
|
@@ -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
|
-
}
|