@marimo-team/islands 0.20.5-dev15 → 0.20.5-dev16
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 +3 -1
- package/package.json +1 -1
- package/src/__mocks__/requests.ts +1 -0
- package/src/components/editor/actions/useNotebookActions.tsx +34 -2
- package/src/core/islands/bridge.ts +1 -0
- package/src/core/network/__tests__/requests-network.test.ts +17 -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 +1 -0
- package/src/core/wasm/bridge.ts +1 -0
- package/src/utils/__tests__/filenames.test.ts +7 -0
- package/src/utils/filenames.ts +3 -0
package/dist/main.js
CHANGED
|
@@ -58409,6 +58409,7 @@ ${r}
|
|
|
58409
58409
|
toPNG: (e) => Filenames.replace(e, "png"),
|
|
58410
58410
|
toPDF: (e) => Filenames.replace(e, "pdf"),
|
|
58411
58411
|
toPY: (e) => Filenames.replace(e, "py"),
|
|
58412
|
+
toIPYNB: (e) => Filenames.replace(e, "ipynb"),
|
|
58412
58413
|
withoutExtension: (e) => {
|
|
58413
58414
|
let r = e.split(".");
|
|
58414
58415
|
return r.length === 1 ? e : r.slice(0, -1).join(".");
|
|
@@ -70403,7 +70404,7 @@ Image URL: ${r.imageUrl}`)), contextToXml({
|
|
|
70403
70404
|
return Logger.warn("Failed to get version from mount config"), null;
|
|
70404
70405
|
}
|
|
70405
70406
|
}
|
|
70406
|
-
const marimoVersionAtom = atom(getVersionFromMountConfig() || "0.20.5-
|
|
70407
|
+
const marimoVersionAtom = atom(getVersionFromMountConfig() || "0.20.5-dev16"), showCodeInRunModeAtom = atom(true);
|
|
70407
70408
|
atom(null);
|
|
70408
70409
|
var import_compiler_runtime$88 = require_compiler_runtime();
|
|
70409
70410
|
function useKeydownOnElement(e, r) {
|
|
@@ -101072,6 +101073,7 @@ ${r}
|
|
|
101072
101073
|
__publicField(this, "sendFileDetails", throwNotImplemented);
|
|
101073
101074
|
__publicField(this, "openTutorial", throwNotImplemented);
|
|
101074
101075
|
__publicField(this, "exportAsHTML", throwNotImplemented);
|
|
101076
|
+
__publicField(this, "exportAsIPYNB", throwNotImplemented);
|
|
101075
101077
|
__publicField(this, "exportAsMarkdown", throwNotImplemented);
|
|
101076
101078
|
__publicField(this, "exportAsPDF", throwNotImplemented);
|
|
101077
101079
|
__publicField(this, "autoExportAsHTML", throwNotImplemented);
|
package/package.json
CHANGED
|
@@ -59,6 +59,7 @@ export const MockRequestClient = {
|
|
|
59
59
|
getRunningNotebooks: vi.fn().mockResolvedValue({ files: [] }),
|
|
60
60
|
shutdownSession: vi.fn().mockResolvedValue({}),
|
|
61
61
|
exportAsHTML: vi.fn().mockResolvedValue({ html: "" }),
|
|
62
|
+
exportAsIPYNB: vi.fn().mockResolvedValue(""),
|
|
62
63
|
exportAsMarkdown: vi.fn().mockResolvedValue({ markdown: "" }),
|
|
63
64
|
exportAsPDF: vi.fn().mockResolvedValue(new Blob()),
|
|
64
65
|
autoExportAsHTML: vi.fn().mockResolvedValue({}),
|
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
LayoutTemplateIcon,
|
|
32
32
|
LinkIcon,
|
|
33
33
|
MessagesSquareIcon,
|
|
34
|
+
NotebookIcon,
|
|
34
35
|
PanelLeftIcon,
|
|
35
36
|
PowerSquareIcon,
|
|
36
37
|
PresentationIcon,
|
|
@@ -123,8 +124,13 @@ export function useNotebookActions() {
|
|
|
123
124
|
const setCommandPaletteOpen = useSetAtom(commandPaletteAtom);
|
|
124
125
|
const setSettingsDialogOpen = useSetAtom(settingDialogAtom);
|
|
125
126
|
const setKeyboardShortcutsOpen = useSetAtom(keyboardShortcutsAtom);
|
|
126
|
-
const {
|
|
127
|
-
|
|
127
|
+
const {
|
|
128
|
+
exportAsIPYNB,
|
|
129
|
+
exportAsMarkdown,
|
|
130
|
+
readCode,
|
|
131
|
+
saveCellConfig,
|
|
132
|
+
updateCellOutputs,
|
|
133
|
+
} = useRequestClient();
|
|
128
134
|
const takeScreenshots = useEnrichCellOutputs();
|
|
129
135
|
|
|
130
136
|
const hasDisabledCells = useAtomValue(hasDisabledCellsAtom);
|
|
@@ -199,6 +205,27 @@ export function useNotebookActions() {
|
|
|
199
205
|
setTimeout(() => window.dispatchEvent(afterprint), 0);
|
|
200
206
|
};
|
|
201
207
|
|
|
208
|
+
const handleDownloadAsIPYNB = async () => {
|
|
209
|
+
if (!filename) {
|
|
210
|
+
toastNotebookMustBeNamed();
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const runDownload = async (progress: ProgressState) => {
|
|
215
|
+
await updateCellOutputsWithScreenshots({
|
|
216
|
+
takeScreenshots: () => takeScreenshots({ progress }),
|
|
217
|
+
updateCellOutputs,
|
|
218
|
+
});
|
|
219
|
+
const ipynb = await exportAsIPYNB({ download: false });
|
|
220
|
+
downloadBlob(
|
|
221
|
+
new Blob([ipynb], { type: "application/x-ipynb+json" }),
|
|
222
|
+
Filenames.toIPYNB(document.title),
|
|
223
|
+
);
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
await withLoadingToast("Downloading IPYNB...", runDownload);
|
|
227
|
+
};
|
|
228
|
+
|
|
202
229
|
const actions: ActionButton[] = [
|
|
203
230
|
{
|
|
204
231
|
icon: <DownloadIcon size={14} strokeWidth={1.5} />,
|
|
@@ -240,6 +267,11 @@ export function useNotebookActions() {
|
|
|
240
267
|
);
|
|
241
268
|
},
|
|
242
269
|
},
|
|
270
|
+
{
|
|
271
|
+
icon: <NotebookIcon size={14} strokeWidth={1.5} />,
|
|
272
|
+
label: "Download as ipynb",
|
|
273
|
+
handle: handleDownloadAsIPYNB,
|
|
274
|
+
},
|
|
243
275
|
{
|
|
244
276
|
icon: <CodeIcon size={14} strokeWidth={1.5} />,
|
|
245
277
|
label: "Download Python code",
|
|
@@ -181,6 +181,7 @@ export class IslandsPyodideBridge implements RunRequests, EditRequests {
|
|
|
181
181
|
sendFileDetails = throwNotImplemented;
|
|
182
182
|
openTutorial = throwNotImplemented;
|
|
183
183
|
exportAsHTML = throwNotImplemented;
|
|
184
|
+
exportAsIPYNB = throwNotImplemented;
|
|
184
185
|
exportAsMarkdown = throwNotImplemented;
|
|
185
186
|
exportAsPDF = throwNotImplemented;
|
|
186
187
|
autoExportAsHTML = throwNotImplemented;
|
|
@@ -113,5 +113,22 @@ describe("createNetworkRequests", () => {
|
|
|
113
113
|
}),
|
|
114
114
|
);
|
|
115
115
|
});
|
|
116
|
+
|
|
117
|
+
it("exportAsIPYNB should call the new endpoint as text", async () => {
|
|
118
|
+
const requests = createNetworkRequests();
|
|
119
|
+
await requests.exportAsIPYNB({
|
|
120
|
+
download: false,
|
|
121
|
+
} as any);
|
|
122
|
+
|
|
123
|
+
expect(mockClient.POST).toHaveBeenCalledWith(
|
|
124
|
+
"/api/export/ipynb",
|
|
125
|
+
expect.objectContaining({
|
|
126
|
+
body: expect.objectContaining({
|
|
127
|
+
download: false,
|
|
128
|
+
}),
|
|
129
|
+
parseAs: "text",
|
|
130
|
+
}),
|
|
131
|
+
);
|
|
132
|
+
});
|
|
116
133
|
});
|
|
117
134
|
});
|
|
@@ -49,6 +49,7 @@ const ACTIONS: Record<keyof AllRequests, Action> = {
|
|
|
49
49
|
|
|
50
50
|
// Export operations start a connection
|
|
51
51
|
exportAsHTML: "startConnection",
|
|
52
|
+
exportAsIPYNB: "startConnection",
|
|
52
53
|
exportAsMarkdown: "startConnection",
|
|
53
54
|
exportAsPDF: "startConnection",
|
|
54
55
|
readCode: "startConnection",
|
|
@@ -381,6 +381,15 @@ export function createNetworkRequests(): EditRequests & RunRequests {
|
|
|
381
381
|
})
|
|
382
382
|
.then(handleResponse);
|
|
383
383
|
},
|
|
384
|
+
exportAsIPYNB: async (request) => {
|
|
385
|
+
return getClient()
|
|
386
|
+
.POST("/api/export/ipynb", {
|
|
387
|
+
body: request,
|
|
388
|
+
parseAs: "text",
|
|
389
|
+
params: getParams(),
|
|
390
|
+
})
|
|
391
|
+
.then(handleResponse);
|
|
392
|
+
},
|
|
384
393
|
exportAsPDF: async (request) => {
|
|
385
394
|
return getClient()
|
|
386
395
|
.POST("/api/export/pdf", {
|
|
@@ -75,6 +75,7 @@ export function createStaticRequests(): EditRequests & RunRequests {
|
|
|
75
75
|
getRunningNotebooks: throwNotInEditMode,
|
|
76
76
|
shutdownSession: throwNotInEditMode,
|
|
77
77
|
exportAsHTML: throwNotInEditMode,
|
|
78
|
+
exportAsIPYNB: throwNotInEditMode,
|
|
78
79
|
exportAsMarkdown: throwNotInEditMode,
|
|
79
80
|
exportAsPDF: throwNotInEditMode,
|
|
80
81
|
autoExportAsHTML: throwNotInEditMode,
|
|
@@ -60,6 +60,7 @@ export function createErrorToastingRequests(
|
|
|
60
60
|
getRunningNotebooks: "Failed to get running notebooks",
|
|
61
61
|
shutdownSession: "Failed to shutdown session",
|
|
62
62
|
exportAsHTML: "Failed to export HTML",
|
|
63
|
+
exportAsIPYNB: "Failed to export ipynb",
|
|
63
64
|
exportAsMarkdown: "Failed to export Markdown",
|
|
64
65
|
exportAsPDF: "Failed to export PDF",
|
|
65
66
|
autoExportAsHTML: "", // No toast
|
|
@@ -180,6 +180,7 @@ export interface EditRequests {
|
|
|
180
180
|
) => Promise<RunningNotebooksResponse>;
|
|
181
181
|
// Export requests
|
|
182
182
|
exportAsHTML: (request: ExportAsHTMLRequest) => Promise<string>;
|
|
183
|
+
exportAsIPYNB: (request: ExportAsIPYNBRequest) => Promise<string>;
|
|
183
184
|
exportAsMarkdown: (request: ExportAsMarkdownRequest) => Promise<string>;
|
|
184
185
|
exportAsPDF: (request: ExportAsPDFRequest) => Promise<Blob>;
|
|
185
186
|
autoExportAsHTML: (request: ExportAsHTMLRequest) => Promise<null>;
|
package/src/core/wasm/bridge.ts
CHANGED
|
@@ -587,6 +587,7 @@ export class PyodideBridge implements RunRequests, EditRequests {
|
|
|
587
587
|
getWorkspaceFiles = throwNotImplemented;
|
|
588
588
|
getRunningNotebooks = throwNotImplemented;
|
|
589
589
|
shutdownSession = throwNotImplemented;
|
|
590
|
+
exportAsIPYNB = throwNotImplemented;
|
|
590
591
|
exportAsPDF = throwNotImplemented;
|
|
591
592
|
autoExportAsHTML = throwNotImplemented;
|
|
592
593
|
autoExportAsMarkdown = throwNotImplemented;
|
|
@@ -23,6 +23,12 @@ describe("Filenames", () => {
|
|
|
23
23
|
expect(Filenames.toPNG("test.foo.py")).toEqual("test.foo.png");
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
+
it("should convert filename to ipynb", () => {
|
|
27
|
+
expect(Filenames.toIPYNB("test")).toEqual("test.ipynb");
|
|
28
|
+
expect(Filenames.toIPYNB("test.txt")).toEqual("test.ipynb");
|
|
29
|
+
expect(Filenames.toIPYNB("test.foo.py")).toEqual("test.foo.ipynb");
|
|
30
|
+
});
|
|
31
|
+
|
|
26
32
|
it("should remove extension from filename", () => {
|
|
27
33
|
expect(Filenames.withoutExtension("test")).toEqual("test");
|
|
28
34
|
expect(Filenames.withoutExtension("test.txt")).toEqual("test");
|
|
@@ -39,6 +45,7 @@ describe("Filenames", () => {
|
|
|
39
45
|
expect(Filenames.toHTML(filename)).toEqual(`${withoutExt}.html`);
|
|
40
46
|
expect(Filenames.toPNG(filename)).toEqual(`${withoutExt}.png`);
|
|
41
47
|
expect(Filenames.toPY(filename)).toEqual(`${withoutExt}.py`);
|
|
48
|
+
expect(Filenames.toIPYNB(filename)).toEqual(`${withoutExt}.ipynb`);
|
|
42
49
|
|
|
43
50
|
// Ensure operations preserve unicode and special characters in base name
|
|
44
51
|
expect(withoutExt).not.toEqual("");
|
package/src/utils/filenames.ts
CHANGED
|
@@ -15,6 +15,9 @@ export const Filenames = {
|
|
|
15
15
|
toPY: (filename: string): string => {
|
|
16
16
|
return Filenames.replace(filename, "py");
|
|
17
17
|
},
|
|
18
|
+
toIPYNB: (filename: string): string => {
|
|
19
|
+
return Filenames.replace(filename, "ipynb");
|
|
20
|
+
},
|
|
18
21
|
withoutExtension: (filename: string): string => {
|
|
19
22
|
// Just remove the last extension
|
|
20
23
|
const parts = filename.split(".");
|