@marimo-team/islands 0.23.16-dev9 → 0.23.17-dev0
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/{chat-ui-JeekqoMH.js → chat-ui-DsZJj75A.js} +3104 -3107
- package/dist/{code-visibility-DveLKNtj.js → common-CotPLtrU.js} +1068 -1036
- package/dist/{formats-CO8cn5_K.js → formats-Dwq1THhr.js} +1 -1
- package/dist/{html-to-image-Bif4utQZ.js → html-to-image-CZ1kLKkq.js} +7336 -6342
- package/dist/main.js +211 -147
- package/dist/{node-sql-parser-DLl_zNw0.js → node-sql-parser-v1Vi-Fft.js} +16867 -16665
- package/dist/{process-output-TAzy-GUk.js → process-output-bVfbcx5_.js} +30 -26
- package/dist/{reveal-component-DxV4Ppa2.js → reveal-component-IoHipy82.js} +634 -626
- package/dist/style.css +1 -1
- package/dist/toDate-CqhBWjvO.js +713 -0
- package/dist/{vega-component-DphdxaJ_.js → vega-component-C7nO5zcf.js} +2 -2
- package/package.json +3 -4
- package/src/__mocks__/requests.ts +56 -4
- package/src/components/data-table/__tests__/__snapshots__/chart-spec-model.test.ts.snap +174 -21
- package/src/components/data-table/__tests__/chart-spec-model.test.ts +102 -1
- package/src/components/data-table/__tests__/columns.test.tsx +9 -1
- package/src/components/data-table/column-summary/chart-spec-model.tsx +163 -103
- package/src/components/data-table/column-summary/legacy-chart-spec.ts +15 -7
- package/src/components/data-table/column-summary/utils.ts +26 -7
- package/src/components/data-table/columns.tsx +11 -1
- package/src/components/data-table/types.ts +1 -1
- package/src/components/editor/__tests__/viewer-banner.test.tsx +14 -0
- package/src/components/editor/actions/__tests__/pair-with-agent-commands.test.ts +99 -18
- package/src/components/editor/actions/__tests__/pdf-export.test.ts +32 -13
- package/src/components/editor/actions/pair-with-agent-commands.ts +21 -10
- package/src/components/editor/actions/pair-with-agent-modal.tsx +2 -2
- package/src/components/editor/actions/pdf-export.ts +11 -14
- package/src/components/editor/actions/useNotebookActions.tsx +28 -19
- package/src/components/editor/cell/code/__tests__/cell-editor-activity-lifecycle.test.ts +250 -0
- package/src/components/editor/cell/code/cell-editor.tsx +13 -7
- package/src/components/editor/chrome/__tests__/state.test.ts +7 -1
- package/src/components/editor/chrome/panels/__tests__/logs-panel.test.tsx +25 -0
- package/src/components/editor/chrome/panels/__tests__/panel-accordion-state.test.ts +42 -0
- package/src/components/editor/chrome/panels/file-explorer-panel.tsx +6 -17
- package/src/components/editor/chrome/panels/logs-panel.tsx +12 -2
- package/src/components/editor/chrome/panels/panel-accordion-state.ts +51 -0
- package/src/components/editor/chrome/panels/session-panel.tsx +5 -16
- package/src/components/editor/chrome/panels/write-secret-modal.tsx +40 -12
- package/src/components/editor/chrome/state.ts +10 -0
- package/src/components/editor/connections/__tests__/json-credentials.test.ts +114 -0
- package/src/components/editor/connections/__tests__/paths.test.ts +39 -0
- package/src/components/editor/connections/__tests__/quick-add-data-sources.test.tsx +113 -0
- package/src/components/editor/connections/add-connection-dialog.tsx +2 -0
- package/src/components/editor/connections/components.tsx +6 -2
- package/src/components/editor/connections/database/__tests__/__snapshots__/as-code.test.ts.snap +27 -5
- package/src/components/editor/connections/database/__tests__/as-code.test.ts +33 -0
- package/src/components/editor/connections/database/as-code.ts +37 -13
- package/src/components/editor/connections/database/schemas.ts +6 -5
- package/src/components/editor/connections/form-renderers.tsx +99 -1
- package/src/components/editor/connections/json-credentials.ts +57 -0
- package/src/components/editor/connections/paths.ts +21 -0
- package/src/components/editor/connections/quick-add-data-sources.tsx +106 -0
- package/src/components/editor/connections/secret-combobox.tsx +41 -20
- package/src/components/editor/connections/storage/__tests__/__snapshots__/as-code.test.ts.snap +88 -2
- package/src/components/editor/connections/storage/__tests__/as-code.test.ts +128 -8
- package/src/components/editor/connections/storage/as-code.ts +79 -36
- package/src/components/editor/connections/storage/schemas.ts +85 -44
- package/src/components/editor/file-tree/__tests__/renderers.test.ts +31 -0
- package/src/components/editor/file-tree/file-viewer.tsx +11 -7
- package/src/components/editor/file-tree/renderers.tsx +21 -0
- package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +2 -2
- package/src/components/editor/viewer-banner.tsx +6 -1
- package/src/components/forms/form.tsx +59 -24
- package/src/components/forms/options.ts +2 -1
- package/src/components/slides/__tests__/cell-output-block.test.tsx +40 -0
- package/src/components/slides/__tests__/slide-cell-view.test.tsx +33 -0
- package/src/components/slides/reveal-component.tsx +22 -17
- package/src/components/slides/slide-cell-view.tsx +1 -1
- package/src/components/slides/slides.css +4 -2
- package/src/components/static-html/share-modal.tsx +1 -1
- package/src/core/ai/context/providers/file.ts +8 -29
- package/src/core/cells/__tests__/cells.test.ts +12 -2
- package/src/core/cells/__tests__/logs.test.ts +5 -5
- package/src/core/cells/cells.ts +17 -4
- package/src/core/cells/document-changes.ts +1 -0
- package/src/core/cells/logs.ts +14 -4
- package/src/core/codemirror/cm.ts +3 -0
- package/src/core/codemirror/code-lens/__tests__/actions.test.ts +68 -0
- package/src/core/codemirror/code-lens/__tests__/analyzer.test.ts +224 -0
- package/src/core/codemirror/code-lens/__tests__/entities.test.ts +175 -0
- package/src/core/codemirror/code-lens/__tests__/extension.test.ts +325 -0
- package/src/core/codemirror/code-lens/__tests__/perf.test.ts +121 -0
- package/src/core/codemirror/code-lens/__tests__/popover.test.ts +93 -0
- package/src/core/codemirror/code-lens/actions.ts +33 -0
- package/src/core/codemirror/code-lens/analyzer.ts +284 -0
- package/src/core/codemirror/code-lens/entities.ts +123 -0
- package/src/core/codemirror/code-lens/extension.ts +298 -0
- package/src/core/codemirror/code-lens/icons.ts +36 -0
- package/src/core/codemirror/code-lens/popover.tsx +240 -0
- package/src/core/codemirror/copilot/__tests__/language-server.test.ts +396 -100
- package/src/core/codemirror/copilot/__tests__/transport.test.ts +128 -540
- package/src/core/codemirror/copilot/language-server.ts +182 -70
- package/src/core/codemirror/copilot/transport.ts +15 -207
- package/src/core/codemirror/copilot/types.ts +9 -2
- package/src/core/codemirror/find-replace/__tests__/navigate.test.ts +42 -30
- package/src/core/codemirror/find-replace/navigate.ts +2 -0
- package/src/core/codemirror/go-to-definition/__tests__/commands.test.ts +24 -3
- package/src/core/codemirror/go-to-definition/commands.ts +93 -90
- package/src/core/codemirror/language/__tests__/sql.test.ts +627 -61
- package/src/core/codemirror/language/languages/python.ts +23 -11
- package/src/core/codemirror/language/languages/sql/completion-store.ts +5 -1
- package/src/core/codemirror/language/languages/sql/renderers.tsx +3 -3
- package/src/core/codemirror/language/languages/sql/sql.ts +251 -16
- package/src/core/codemirror/lsp/__tests__/federated-lsp.test.ts +161 -0
- package/src/core/codemirror/lsp/__tests__/log-messages.test.ts +53 -0
- package/src/core/codemirror/lsp/__tests__/normalize-markdown-math.test.ts +25 -0
- package/src/core/codemirror/lsp/__tests__/notebook-lsp.test.ts +598 -156
- package/src/core/codemirror/lsp/__tests__/transports.test.ts +112 -0
- package/src/core/codemirror/lsp/federated-lsp.ts +76 -23
- package/src/core/codemirror/lsp/log-messages.ts +37 -0
- package/src/core/codemirror/lsp/normalize-markdown-math.ts +8 -4
- package/src/core/codemirror/lsp/notebook-lsp.ts +220 -56
- package/src/core/codemirror/lsp/transports.ts +8 -1
- package/src/core/codemirror/lsp/types.ts +68 -8
- package/src/core/codemirror/lsp/utils.ts +14 -0
- package/src/core/codemirror/python-node-names.ts +90 -0
- package/src/core/codemirror/reactive-references/analyzer.ts +52 -57
- package/src/core/codemirror/utils.ts +28 -0
- package/src/core/config/feature-flag.tsx +3 -1
- package/src/core/datasets/data-source-discovery.ts +5 -0
- package/src/core/datasets/request-registry.ts +11 -0
- package/src/core/edit-app.tsx +1 -2
- package/src/core/export/__tests__/hooks.test.ts +2 -4
- package/src/core/export/hooks.ts +4 -3
- package/src/core/islands/__tests__/bridge.test.ts +107 -0
- package/src/core/islands/bootstrap.ts +1 -0
- package/src/core/islands/bridge.ts +18 -0
- package/src/core/kernel/messages.ts +2 -0
- package/src/core/lsp/__tests__/transport.test.ts +509 -337
- package/src/core/lsp/transport.ts +304 -132
- package/src/core/mode.ts +10 -0
- package/src/core/network/__tests__/api.test.ts +42 -0
- package/src/core/network/__tests__/connection.test.ts +45 -0
- package/src/core/network/__tests__/requests-lazy.test.ts +47 -1
- package/src/core/network/__tests__/requests-network.test.ts +48 -0
- package/src/core/network/api.ts +27 -0
- package/src/core/network/connection.ts +15 -0
- package/src/core/network/requests-lazy.ts +3 -0
- package/src/core/network/requests-network.ts +31 -8
- package/src/core/network/requests-static.ts +3 -0
- package/src/core/network/requests-toasting.tsx +3 -0
- package/src/core/network/types.ts +27 -6
- package/src/core/rtc/state.ts +9 -0
- package/src/core/static/download-html.ts +5 -14
- package/src/core/wasm/__tests__/bridge.test.ts +99 -5
- package/src/core/wasm/bridge.ts +42 -9
- package/src/core/wasm/worker/types.ts +10 -3
- package/src/core/wasm/worker/worker.ts +7 -8
- package/src/core/websocket/useMarimoKernelConnection.tsx +4 -0
- package/src/css/app/codemirror.css +16 -2
- package/src/hooks/__tests__/useDataSourceDiscovery.test.ts +80 -0
- package/src/hooks/useDataSourceDiscovery.ts +18 -0
- package/src/plugins/impl/DataTablePlugin.tsx +4 -1
- package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +158 -0
- package/src/plugins/impl/anywidget/__tests__/model.test.ts +55 -0
- package/src/plugins/impl/anywidget/model.ts +20 -5
- package/src/plugins/impl/data-frames/forms/__tests__/__snapshots__/form.test.tsx.snap +33 -21
- package/src/plugins/impl/data-frames/forms/renderers.tsx +11 -8
- package/src/stories/log-viewer.stories.tsx +12 -6
- package/src/utils/__tests__/blob.test.ts +27 -1
- package/src/utils/__tests__/download.test.tsx +44 -4
- package/src/utils/blob.ts +24 -1
- package/src/utils/download.ts +14 -15
- package/dist/toDate-CmR_xR3P.js +0 -692
|
@@ -22,6 +22,7 @@ vi.mock("../api", async () => {
|
|
|
22
22
|
return {
|
|
23
23
|
...actual,
|
|
24
24
|
API: {
|
|
25
|
+
handleExportResponse: vi.fn((response) => response.data),
|
|
25
26
|
handleResponse: vi.fn((response) => response.data),
|
|
26
27
|
handleResponseReturnNull: vi.fn(() => null),
|
|
27
28
|
},
|
|
@@ -32,6 +33,7 @@ vi.mock("../api", async () => {
|
|
|
32
33
|
vi.mock("../connection", () => ({
|
|
33
34
|
isConnectedAtom: { read: vi.fn(() => true) },
|
|
34
35
|
waitForConnectionOpen: vi.fn().mockResolvedValue(undefined),
|
|
36
|
+
waitForConnectionOpenIfNotebook: vi.fn().mockResolvedValue(undefined),
|
|
35
37
|
}));
|
|
36
38
|
|
|
37
39
|
describe("createNetworkRequests", () => {
|
|
@@ -114,6 +116,19 @@ describe("createNetworkRequests", () => {
|
|
|
114
116
|
);
|
|
115
117
|
});
|
|
116
118
|
|
|
119
|
+
it("exportAsScript should request a text export", async () => {
|
|
120
|
+
const requests = createNetworkRequests();
|
|
121
|
+
await requests.exportAsScript({ download: false });
|
|
122
|
+
|
|
123
|
+
expect(mockClient.POST).toHaveBeenCalledWith(
|
|
124
|
+
"/api/export/script",
|
|
125
|
+
expect.objectContaining({
|
|
126
|
+
body: { download: false },
|
|
127
|
+
parseAs: "text",
|
|
128
|
+
}),
|
|
129
|
+
);
|
|
130
|
+
});
|
|
131
|
+
|
|
117
132
|
it("getEnvironmentInfo should GET /api/environment", async () => {
|
|
118
133
|
const requests = createNetworkRequests();
|
|
119
134
|
await requests.getEnvironmentInfo();
|
|
@@ -121,6 +136,39 @@ describe("createNetworkRequests", () => {
|
|
|
121
136
|
expect(mockClient.GET).toHaveBeenCalledWith("/api/environment");
|
|
122
137
|
});
|
|
123
138
|
|
|
139
|
+
it("getExportAvailability should GET /api/export/availability", async () => {
|
|
140
|
+
const requests = createNetworkRequests();
|
|
141
|
+
await requests.getExportAvailability();
|
|
142
|
+
|
|
143
|
+
expect(mockClient.GET).toHaveBeenCalledWith("/api/export/availability");
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("discoverDataSources should POST to the discovery endpoint", async () => {
|
|
147
|
+
const requests = createNetworkRequests();
|
|
148
|
+
const request = { requestId: "discovery-request" } as any;
|
|
149
|
+
await requests.discoverDataSources(request);
|
|
150
|
+
|
|
151
|
+
expect(mockClient.POST).toHaveBeenCalledWith(
|
|
152
|
+
"/api/datasources/discover",
|
|
153
|
+
expect.objectContaining({
|
|
154
|
+
body: request,
|
|
155
|
+
params: expect.anything(),
|
|
156
|
+
}),
|
|
157
|
+
);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("getPackageList should not require a kernel connection", async () => {
|
|
161
|
+
const { waitForConnectionOpen, waitForConnectionOpenIfNotebook } =
|
|
162
|
+
await import("../connection");
|
|
163
|
+
|
|
164
|
+
const requests = createNetworkRequests();
|
|
165
|
+
await requests.getPackageList();
|
|
166
|
+
|
|
167
|
+
expect(mockClient.GET).toHaveBeenCalledWith("/api/packages/list");
|
|
168
|
+
expect(waitForConnectionOpenIfNotebook).toHaveBeenCalled();
|
|
169
|
+
expect(waitForConnectionOpen).not.toHaveBeenCalled();
|
|
170
|
+
});
|
|
171
|
+
|
|
124
172
|
it("exportAsIPYNB should call the new endpoint as text", async () => {
|
|
125
173
|
const requests = createNetworkRequests();
|
|
126
174
|
await requests.exportAsIPYNB({
|
package/src/core/network/api.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { Logger } from "../../utils/Logger";
|
|
|
6
6
|
import { Strings } from "../../utils/strings";
|
|
7
7
|
import { getRuntimeManager } from "../runtime/config";
|
|
8
8
|
import type { RuntimeManager } from "../runtime/runtime";
|
|
9
|
+
import type { ExportedFile } from "./types";
|
|
9
10
|
|
|
10
11
|
function getBaseUriWithoutQueryParams(): string {
|
|
11
12
|
// Remove query params and hash
|
|
@@ -15,6 +16,15 @@ function getBaseUriWithoutQueryParams(): string {
|
|
|
15
16
|
return url.toString();
|
|
16
17
|
}
|
|
17
18
|
|
|
19
|
+
function getExportFilename(response: Response): string {
|
|
20
|
+
const contentDisposition = response.headers.get("Content-Disposition");
|
|
21
|
+
const match = contentDisposition?.match(/filename\*=UTF-8''([^;]+)/i);
|
|
22
|
+
if (!match) {
|
|
23
|
+
throw new Error("Export response is missing a filename");
|
|
24
|
+
}
|
|
25
|
+
return decodeURIComponent(match[1]);
|
|
26
|
+
}
|
|
27
|
+
|
|
18
28
|
/**
|
|
19
29
|
* Wrapper around fetch that adds XSRF token and session ID to the request and
|
|
20
30
|
* strong types.
|
|
@@ -119,6 +129,23 @@ export const API = {
|
|
|
119
129
|
}
|
|
120
130
|
return Promise.resolve(response.data as T);
|
|
121
131
|
},
|
|
132
|
+
handleExportResponse: async <T extends BlobPart>(response: {
|
|
133
|
+
data?: T | undefined;
|
|
134
|
+
error?: Record<string, unknown>;
|
|
135
|
+
response: Response;
|
|
136
|
+
}): Promise<ExportedFile<T>> => {
|
|
137
|
+
const contents = await API.handleResponse<T>(response);
|
|
138
|
+
const mediaType = response.response.headers.get("Content-Type");
|
|
139
|
+
if (!mediaType) {
|
|
140
|
+
throw new Error("Export response is missing a media type");
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
contents,
|
|
145
|
+
filename: getExportFilename(response.response),
|
|
146
|
+
mediaType,
|
|
147
|
+
};
|
|
148
|
+
},
|
|
122
149
|
handleResponseReturnNull: (response: {
|
|
123
150
|
error?: Record<string, unknown>;
|
|
124
151
|
response: Response;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
import { atom } from "jotai";
|
|
3
|
+
import { isNotebookPage } from "../mode";
|
|
3
4
|
import { waitFor } from "../state/jotai";
|
|
4
5
|
import { type ConnectionStatus, WebSocketState } from "../websocket/types";
|
|
5
6
|
|
|
@@ -17,6 +18,20 @@ export function waitForConnectionOpen() {
|
|
|
17
18
|
});
|
|
18
19
|
}
|
|
19
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Waits for the kernel connection, but only on pages that open one. Non-notebook
|
|
23
|
+
* pages (home, gallery) never connect, so waiting there would hang forever.
|
|
24
|
+
*
|
|
25
|
+
* Use for requests that the marimo server can serve without a session, but that
|
|
26
|
+
* should still wait for the kernel when a notebook is open.
|
|
27
|
+
*/
|
|
28
|
+
export function waitForConnectionOpenIfNotebook() {
|
|
29
|
+
if (!isNotebookPage()) {
|
|
30
|
+
return Promise.resolve();
|
|
31
|
+
}
|
|
32
|
+
return waitForConnectionOpen();
|
|
33
|
+
}
|
|
34
|
+
|
|
20
35
|
export const isConnectingAtom = atom((get) => {
|
|
21
36
|
const connection = get(connectionAtom);
|
|
22
37
|
return connection.state === WebSocketState.CONNECTING;
|
|
@@ -51,11 +51,13 @@ const ACTIONS: Record<keyof AllRequests, Action> = {
|
|
|
51
51
|
sendRunScratchpad: "startConnection",
|
|
52
52
|
saveAppConfig: "startConnection",
|
|
53
53
|
saveCellConfig: "startConnection",
|
|
54
|
+
discoverDataSources: "startConnection",
|
|
54
55
|
|
|
55
56
|
// Export operations start a connection
|
|
56
57
|
exportAsHTML: "startConnection",
|
|
57
58
|
exportAsIPYNB: "startConnection",
|
|
58
59
|
exportAsMarkdown: "startConnection",
|
|
60
|
+
exportAsScript: "startConnection",
|
|
59
61
|
exportAsPDF: "startConnection",
|
|
60
62
|
readCode: "startConnection",
|
|
61
63
|
sendCopy: "throwError",
|
|
@@ -103,6 +105,7 @@ const ACTIONS: Record<keyof AllRequests, Action> = {
|
|
|
103
105
|
|
|
104
106
|
// Served by the marimo server without a kernel session
|
|
105
107
|
getEnvironmentInfo: "serverOnly",
|
|
108
|
+
getExportAvailability: "serverOnly",
|
|
106
109
|
|
|
107
110
|
// Home operations throw errors
|
|
108
111
|
getRecentFiles: "startConnection",
|
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
import { once } from "@/utils/once";
|
|
4
4
|
import { getRuntimeManager } from "../runtime/config";
|
|
5
5
|
import { API, createClientWithRuntimeManager } from "./api";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
waitForConnectionOpen,
|
|
8
|
+
waitForConnectionOpenIfNotebook,
|
|
9
|
+
} from "./connection";
|
|
7
10
|
import type { EditRequests, RunRequests } from "./types";
|
|
8
11
|
|
|
9
12
|
/**
|
|
@@ -19,7 +22,7 @@ function multipartInit(formData: FormData) {
|
|
|
19
22
|
};
|
|
20
23
|
}
|
|
21
24
|
|
|
22
|
-
const { handleResponse, handleResponseReturnNull } = API;
|
|
25
|
+
const { handleExportResponse, handleResponse, handleResponseReturnNull } = API;
|
|
23
26
|
|
|
24
27
|
export function createNetworkRequests(): EditRequests & RunRequests {
|
|
25
28
|
const getClient = once(() => {
|
|
@@ -264,6 +267,14 @@ export function createNetworkRequests(): EditRequests & RunRequests {
|
|
|
264
267
|
})
|
|
265
268
|
.then(handleResponseReturnNull);
|
|
266
269
|
},
|
|
270
|
+
discoverDataSources: (request) => {
|
|
271
|
+
return getClient()
|
|
272
|
+
.POST("/api/datasources/discover", {
|
|
273
|
+
body: request,
|
|
274
|
+
params: getParams(),
|
|
275
|
+
})
|
|
276
|
+
.then(handleResponseReturnNull);
|
|
277
|
+
},
|
|
267
278
|
validateSQL: (request) => {
|
|
268
279
|
return getClient()
|
|
269
280
|
.POST("/api/sql/validate", {
|
|
@@ -399,6 +410,9 @@ export function createNetworkRequests(): EditRequests & RunRequests {
|
|
|
399
410
|
})
|
|
400
411
|
.then(handleResponse);
|
|
401
412
|
},
|
|
413
|
+
getExportAvailability: () => {
|
|
414
|
+
return getClient().GET("/api/export/availability").then(handleResponse);
|
|
415
|
+
},
|
|
402
416
|
exportAsHTML: async (request) => {
|
|
403
417
|
if (
|
|
404
418
|
process.env.NODE_ENV === "development" ||
|
|
@@ -412,7 +426,7 @@ export function createNetworkRequests(): EditRequests & RunRequests {
|
|
|
412
426
|
parseAs: "text",
|
|
413
427
|
params: getParams(),
|
|
414
428
|
})
|
|
415
|
-
.then(
|
|
429
|
+
.then(handleExportResponse);
|
|
416
430
|
},
|
|
417
431
|
exportAsMarkdown: async (request) => {
|
|
418
432
|
return getClient()
|
|
@@ -421,7 +435,16 @@ export function createNetworkRequests(): EditRequests & RunRequests {
|
|
|
421
435
|
parseAs: "text",
|
|
422
436
|
params: getParams(),
|
|
423
437
|
})
|
|
424
|
-
.then(
|
|
438
|
+
.then(handleExportResponse);
|
|
439
|
+
},
|
|
440
|
+
exportAsScript: async (request) => {
|
|
441
|
+
return getClient()
|
|
442
|
+
.POST("/api/export/script", {
|
|
443
|
+
body: request,
|
|
444
|
+
parseAs: "text",
|
|
445
|
+
params: getParams(),
|
|
446
|
+
})
|
|
447
|
+
.then(handleExportResponse);
|
|
425
448
|
},
|
|
426
449
|
exportAsIPYNB: async (request) => {
|
|
427
450
|
return getClient()
|
|
@@ -430,7 +453,7 @@ export function createNetworkRequests(): EditRequests & RunRequests {
|
|
|
430
453
|
parseAs: "text",
|
|
431
454
|
params: getParams(),
|
|
432
455
|
})
|
|
433
|
-
.then(
|
|
456
|
+
.then(handleExportResponse);
|
|
434
457
|
},
|
|
435
458
|
exportAsPDF: async (request) => {
|
|
436
459
|
return getClient()
|
|
@@ -439,7 +462,7 @@ export function createNetworkRequests(): EditRequests & RunRequests {
|
|
|
439
462
|
parseAs: "blob",
|
|
440
463
|
params: getParams(),
|
|
441
464
|
})
|
|
442
|
-
.then(
|
|
465
|
+
.then(handleExportResponse);
|
|
443
466
|
},
|
|
444
467
|
autoExportAsHTML: async (request) => {
|
|
445
468
|
return getClient()
|
|
@@ -489,12 +512,12 @@ export function createNetworkRequests(): EditRequests & RunRequests {
|
|
|
489
512
|
},
|
|
490
513
|
getPackageList: async () => {
|
|
491
514
|
// If the sidebar is already open, it may try to load before the session has been initialized
|
|
492
|
-
await
|
|
515
|
+
await waitForConnectionOpenIfNotebook();
|
|
493
516
|
return getClient().GET("/api/packages/list").then(handleResponse);
|
|
494
517
|
},
|
|
495
518
|
getDependencyTree: async () => {
|
|
496
519
|
// If the sidebar is already open, it may try to load before the session has been initialized
|
|
497
|
-
await
|
|
520
|
+
await waitForConnectionOpenIfNotebook();
|
|
498
521
|
return getClient().GET("/api/packages/tree").then(handleResponse);
|
|
499
522
|
},
|
|
500
523
|
listSecretKeys: async (request) => {
|
|
@@ -62,6 +62,7 @@ export function createStaticRequests(): EditRequests & RunRequests {
|
|
|
62
62
|
previewSQLTableList: throwNotInEditMode,
|
|
63
63
|
previewSQLSchemaList: throwNotInEditMode,
|
|
64
64
|
previewDataSourceConnection: throwNotInEditMode,
|
|
65
|
+
discoverDataSources: throwNotInEditMode,
|
|
65
66
|
validateSQL: throwNotInEditMode,
|
|
66
67
|
openFile: throwNotInEditMode,
|
|
67
68
|
getUsageStats: throwNotInEditMode,
|
|
@@ -82,9 +83,11 @@ export function createStaticRequests(): EditRequests & RunRequests {
|
|
|
82
83
|
getWorkspaceFiles: throwNotInEditMode,
|
|
83
84
|
getRunningNotebooks: throwNotInEditMode,
|
|
84
85
|
shutdownSession: throwNotInEditMode,
|
|
86
|
+
getExportAvailability: throwNotInEditMode,
|
|
85
87
|
exportAsHTML: throwNotInEditMode,
|
|
86
88
|
exportAsIPYNB: throwNotInEditMode,
|
|
87
89
|
exportAsMarkdown: throwNotInEditMode,
|
|
90
|
+
exportAsScript: throwNotInEditMode,
|
|
88
91
|
exportAsPDF: throwNotInEditMode,
|
|
89
92
|
autoExportAsHTML: throwNotInEditMode,
|
|
90
93
|
autoExportAsMarkdown: throwNotInEditMode,
|
|
@@ -43,6 +43,7 @@ export function createErrorToastingRequests(
|
|
|
43
43
|
previewSQLTableList: "Failed to fetch SQL table list",
|
|
44
44
|
previewSQLSchemaList: "Failed to fetch SQL schema list",
|
|
45
45
|
previewDataSourceConnection: "Failed to preview data source connection",
|
|
46
|
+
discoverDataSources: "Failed to discover data sources",
|
|
46
47
|
validateSQL: "Failed to validate SQL",
|
|
47
48
|
openFile: "Failed to open file",
|
|
48
49
|
getUsageStats: "", // No toast
|
|
@@ -63,9 +64,11 @@ export function createErrorToastingRequests(
|
|
|
63
64
|
getWorkspaceFiles: "Failed to get workspace files",
|
|
64
65
|
getRunningNotebooks: "Failed to get running notebooks",
|
|
65
66
|
shutdownSession: "Failed to shutdown session",
|
|
67
|
+
getExportAvailability: "", // No toast
|
|
66
68
|
exportAsHTML: "Failed to export HTML",
|
|
67
69
|
exportAsIPYNB: "Failed to export ipynb",
|
|
68
70
|
exportAsMarkdown: "Failed to export Markdown",
|
|
71
|
+
exportAsScript: "Failed to export Script",
|
|
69
72
|
exportAsPDF: "Failed to export PDF",
|
|
70
73
|
autoExportAsHTML: "", // No toast
|
|
71
74
|
autoExportAsMarkdown: "", // No toast
|
|
@@ -17,12 +17,23 @@ export type CellConfig = schemas["CellConfig"];
|
|
|
17
17
|
export type RuntimeState = schemas["CellNotification"]["status"];
|
|
18
18
|
export type CodeCompletionRequest = schemas["CodeCompletionRequest"];
|
|
19
19
|
export type DeleteCellRequest = schemas["DeleteCellRequest"];
|
|
20
|
+
export type AutoExportAsIPYNBRequest = schemas["AutoExportAsIPYNBRequest"];
|
|
21
|
+
export type AutoExportAsMarkdownRequest =
|
|
22
|
+
schemas["AutoExportAsMarkdownRequest"];
|
|
20
23
|
export type ExportAsHTMLRequest = schemas["ExportAsHTMLRequest"];
|
|
21
24
|
export type ExportAsMarkdownRequest = schemas["ExportAsMarkdownRequest"];
|
|
22
25
|
export type ExportAsIPYNBRequest = schemas["ExportAsIPYNBRequest"];
|
|
23
26
|
export type ExportAsScriptRequest = schemas["ExportAsScriptRequest"];
|
|
24
27
|
export type ExportAsPDFRequest = schemas["ExportAsPDFRequest"];
|
|
28
|
+
export type ExportAvailabilityResponse = schemas["ExportAvailabilityResponse"];
|
|
25
29
|
export type UpdateCellOutputsRequest = schemas["UpdateCellOutputsRequest"];
|
|
30
|
+
|
|
31
|
+
export interface ExportedFile<T extends BlobPart = BlobPart> {
|
|
32
|
+
contents: T;
|
|
33
|
+
filename: string;
|
|
34
|
+
mediaType: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
26
37
|
export type FileCopyRequest = schemas["FileCopyRequest"];
|
|
27
38
|
export type FileCopyResponse = schemas["FileCopyResponse"];
|
|
28
39
|
export type FileCreateRequest = schemas["FileCreateRequest"];
|
|
@@ -64,6 +75,7 @@ export type ListSQLTablesRequest = schemas["ListSQLTablesRequest"];
|
|
|
64
75
|
export type ListSQLSchemasRequest = schemas["ListSQLSchemasRequest"];
|
|
65
76
|
export type ListDataSourceConnectionRequest =
|
|
66
77
|
schemas["ListDataSourceConnectionRequest"];
|
|
78
|
+
export type DiscoverDataSourcesRequest = schemas["DiscoverDataSourcesRequest"];
|
|
67
79
|
export type ValidateSQLRequest = schemas["ValidateSQLRequest"];
|
|
68
80
|
export type DebugCellRequest = schemas["DebugCellRequest"];
|
|
69
81
|
export type SetBreakpointsRequest = schemas["SetBreakpointsRequest"];
|
|
@@ -170,6 +182,7 @@ export interface EditRequests {
|
|
|
170
182
|
previewDataSourceConnection: (
|
|
171
183
|
request: ListDataSourceConnectionRequest,
|
|
172
184
|
) => Promise<null>;
|
|
185
|
+
discoverDataSources: (request: DiscoverDataSourcesRequest) => Promise<null>;
|
|
173
186
|
validateSQL: (request: ValidateSQLRequest) => Promise<null>;
|
|
174
187
|
openFile: (request: { path: string; lineNumber?: number }) => Promise<null>;
|
|
175
188
|
getUsageStats: () => Promise<UsageResponse>;
|
|
@@ -205,13 +218,21 @@ export interface EditRequests {
|
|
|
205
218
|
request: ShutdownSessionRequest,
|
|
206
219
|
) => Promise<RunningNotebooksResponse>;
|
|
207
220
|
// Export requests
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
221
|
+
getExportAvailability: () => Promise<ExportAvailabilityResponse>;
|
|
222
|
+
exportAsHTML: (request: ExportAsHTMLRequest) => Promise<ExportedFile<string>>;
|
|
223
|
+
exportAsIPYNB: (
|
|
224
|
+
request: ExportAsIPYNBRequest,
|
|
225
|
+
) => Promise<ExportedFile<string>>;
|
|
226
|
+
exportAsMarkdown: (
|
|
227
|
+
request: ExportAsMarkdownRequest,
|
|
228
|
+
) => Promise<ExportedFile<string>>;
|
|
229
|
+
exportAsScript: (
|
|
230
|
+
request: ExportAsScriptRequest,
|
|
231
|
+
) => Promise<ExportedFile<string>>;
|
|
232
|
+
exportAsPDF: (request: ExportAsPDFRequest) => Promise<ExportedFile<Blob>>;
|
|
212
233
|
autoExportAsHTML: (request: ExportAsHTMLRequest) => Promise<null>;
|
|
213
|
-
autoExportAsMarkdown: (request:
|
|
214
|
-
autoExportAsIPYNB: (request:
|
|
234
|
+
autoExportAsMarkdown: (request: AutoExportAsMarkdownRequest) => Promise<null>;
|
|
235
|
+
autoExportAsIPYNB: (request: AutoExportAsIPYNBRequest) => Promise<null>;
|
|
215
236
|
updateCellOutputs: (request: UpdateCellOutputsRequest) => Promise<null>;
|
|
216
237
|
// Package requests
|
|
217
238
|
getPackageList: () => Promise<ListPackagesResponse>;
|
package/src/core/rtc/state.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
import { atomWithStorage } from "jotai/utils";
|
|
3
|
+
import { SCRATCH_CELL_ID, type CellId } from "@/core/cells/ids";
|
|
3
4
|
import { once } from "@/utils/once";
|
|
4
5
|
import { jotaiJsonStorage } from "@/utils/storage/jotai";
|
|
5
6
|
import { getFeatureFlag } from "../config/feature-flag";
|
|
@@ -23,3 +24,11 @@ export const usernameAtom = atomWithStorage<string>(
|
|
|
23
24
|
export const isRtcEnabled = once(() => {
|
|
24
25
|
return getFeatureFlag("rtc_v2");
|
|
25
26
|
});
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Whether this cell type can participate in real-time collaboration.
|
|
30
|
+
* Scratchpad cells are local-only.
|
|
31
|
+
*/
|
|
32
|
+
export function canUseRtc(cellId: CellId): boolean {
|
|
33
|
+
return cellId !== SCRATCH_CELL_ID;
|
|
34
|
+
}
|
|
@@ -1,31 +1,22 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import { Filenames } from "@/utils/filenames";
|
|
5
|
-
import { Paths } from "@/utils/paths";
|
|
3
|
+
import { downloadExportedFile } from "@/utils/download";
|
|
6
4
|
import { getRequestClient } from "../network/requests";
|
|
7
5
|
import { VirtualFileTracker } from "./virtual-file-tracker";
|
|
8
6
|
|
|
9
7
|
/**
|
|
10
8
|
* Downloads the current notebook as an HTML file.
|
|
11
9
|
*/
|
|
12
|
-
export async function downloadAsHTML(opts: {
|
|
13
|
-
filename: string;
|
|
14
|
-
includeCode: boolean;
|
|
15
|
-
}) {
|
|
10
|
+
export async function downloadAsHTML(opts: { includeCode: boolean }) {
|
|
16
11
|
const client = getRequestClient();
|
|
17
|
-
const {
|
|
18
|
-
const
|
|
12
|
+
const { includeCode } = opts;
|
|
13
|
+
const exportedFile = await client.exportAsHTML({
|
|
19
14
|
download: true,
|
|
20
15
|
includeCode: includeCode,
|
|
21
16
|
files: VirtualFileTracker.INSTANCE.filenames(),
|
|
22
17
|
});
|
|
23
|
-
const filenameWithoutPath = Paths.basename(filename) ?? "notebook.py";
|
|
24
18
|
|
|
25
|
-
|
|
26
|
-
new Blob([html], { type: "text/html" }),
|
|
27
|
-
Filenames.toHTML(filenameWithoutPath),
|
|
28
|
-
);
|
|
19
|
+
downloadExportedFile(exportedFile);
|
|
29
20
|
}
|
|
30
21
|
|
|
31
22
|
function updateAssetUrl(existingUrl: string, assetBaseUrl: string) {
|
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
3
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
import { cellId } from "@/__tests__/branded";
|
|
5
|
+
import { Deferred } from "@/utils/Deferred";
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
mockBridge,
|
|
9
|
+
mockNotebookReadFile,
|
|
10
|
+
mockReadNotebook,
|
|
11
|
+
mockSaveNotebook,
|
|
12
|
+
rpcListeners,
|
|
13
|
+
} = vi.hoisted(() => ({
|
|
14
|
+
mockBridge: vi.fn(),
|
|
6
15
|
mockNotebookReadFile: vi.fn(),
|
|
16
|
+
mockReadNotebook: vi.fn(),
|
|
17
|
+
mockSaveNotebook: vi.fn(),
|
|
7
18
|
rpcListeners: {} as Record<string, () => void>,
|
|
8
19
|
}));
|
|
9
20
|
|
|
@@ -28,11 +39,11 @@ vi.mock("@/core/wasm/rpc", () => ({
|
|
|
28
39
|
getWorkerRPC: () => ({
|
|
29
40
|
proxy: {
|
|
30
41
|
request: {
|
|
31
|
-
bridge:
|
|
42
|
+
bridge: mockBridge,
|
|
32
43
|
startSession: vi.fn(),
|
|
33
44
|
readFile: vi.fn(),
|
|
34
|
-
readNotebook:
|
|
35
|
-
saveNotebook:
|
|
45
|
+
readNotebook: mockReadNotebook,
|
|
46
|
+
saveNotebook: mockSaveNotebook,
|
|
36
47
|
},
|
|
37
48
|
send: { consumerReady: vi.fn() },
|
|
38
49
|
},
|
|
@@ -112,6 +123,89 @@ describe("PyodideBridge.readCode", () => {
|
|
|
112
123
|
});
|
|
113
124
|
});
|
|
114
125
|
|
|
126
|
+
describe("PyodideBridge.exportAsScript", () => {
|
|
127
|
+
beforeEach(() => {
|
|
128
|
+
vi.clearAllMocks();
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("returns the script export from the Python bridge", async () => {
|
|
132
|
+
const exportedFile = {
|
|
133
|
+
contents: '# %%\nprint("hello")',
|
|
134
|
+
filename: "notebook.script.py",
|
|
135
|
+
mediaType: "text/plain; charset=utf-8",
|
|
136
|
+
};
|
|
137
|
+
mockBridge.mockResolvedValue(exportedFile);
|
|
138
|
+
|
|
139
|
+
const result = await PyodideBridge.INSTANCE.exportAsScript({
|
|
140
|
+
download: false,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
expect(mockBridge).toHaveBeenCalledWith({
|
|
144
|
+
functionName: "export_script",
|
|
145
|
+
payload: { download: false },
|
|
146
|
+
});
|
|
147
|
+
expect(result).toEqual(exportedFile);
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
describe("PyodideBridge.sendSave", () => {
|
|
152
|
+
const request = {
|
|
153
|
+
cellIds: [cellId("cell-1")],
|
|
154
|
+
codes: ['value = "NEW"'],
|
|
155
|
+
names: ["_"],
|
|
156
|
+
filename: "notebook.py",
|
|
157
|
+
configs: [{}],
|
|
158
|
+
layout: null,
|
|
159
|
+
persist: true,
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
beforeEach(() => {
|
|
163
|
+
vi.clearAllMocks();
|
|
164
|
+
store.set(initialModeAtom, "edit");
|
|
165
|
+
rpcListeners.initialized();
|
|
166
|
+
mockSaveNotebook.mockResolvedValue(null);
|
|
167
|
+
mockReadNotebook.mockResolvedValue(
|
|
168
|
+
"import marimo\napp = marimo.App()\n# NEW",
|
|
169
|
+
);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
afterEach(() => {
|
|
173
|
+
store.set(initialModeAtom, undefined);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it("waits for the session save before generating exports", async () => {
|
|
177
|
+
const mainSave = new Deferred<void>();
|
|
178
|
+
mockSaveNotebook
|
|
179
|
+
.mockResolvedValueOnce(null)
|
|
180
|
+
.mockReturnValueOnce(mainSave.promise);
|
|
181
|
+
|
|
182
|
+
await PyodideBridge.INSTANCE.sendSave(request);
|
|
183
|
+
expect(mockSaveNotebook).toHaveBeenCalledTimes(2);
|
|
184
|
+
expect(mockSaveNotebook).toHaveBeenNthCalledWith(1, request);
|
|
185
|
+
expect(mockSaveNotebook).toHaveBeenNthCalledWith(2, request);
|
|
186
|
+
|
|
187
|
+
const markdown = PyodideBridge.INSTANCE.exportAsMarkdown({
|
|
188
|
+
download: false,
|
|
189
|
+
});
|
|
190
|
+
const script = PyodideBridge.INSTANCE.exportAsScript({
|
|
191
|
+
download: false,
|
|
192
|
+
});
|
|
193
|
+
await Promise.resolve();
|
|
194
|
+
expect(mockBridge).not.toHaveBeenCalled();
|
|
195
|
+
|
|
196
|
+
mainSave.resolve();
|
|
197
|
+
await Promise.all([markdown, script]);
|
|
198
|
+
expect(mockBridge).toHaveBeenCalledWith({
|
|
199
|
+
functionName: "export_markdown",
|
|
200
|
+
payload: { download: false },
|
|
201
|
+
});
|
|
202
|
+
expect(mockBridge).toHaveBeenCalledWith({
|
|
203
|
+
functionName: "export_script",
|
|
204
|
+
payload: { download: false },
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
|
|
115
209
|
describe("getWasmWorkerName", () => {
|
|
116
210
|
afterEach(() => {
|
|
117
211
|
delete (window as unknown as { __MARIMO_HAS_WASM_CONTROLLER__?: boolean })
|
package/src/core/wasm/bridge.ts
CHANGED
|
@@ -19,6 +19,8 @@ import type {
|
|
|
19
19
|
EnvironmentInfo,
|
|
20
20
|
ExportAsHTMLRequest,
|
|
21
21
|
ExportAsMarkdownRequest,
|
|
22
|
+
ExportAsScriptRequest,
|
|
23
|
+
ExportedFile,
|
|
22
24
|
FileCopyResponse,
|
|
23
25
|
FileCreateResponse,
|
|
24
26
|
FileDeleteResponse,
|
|
@@ -60,6 +62,7 @@ export class PyodideBridge implements RunRequests, EditRequests {
|
|
|
60
62
|
|
|
61
63
|
private rpc!: ReturnType<typeof getWorkerRPC<WorkerSchema>>;
|
|
62
64
|
private saveRpc: SaveWorker | undefined;
|
|
65
|
+
private pendingSessionSave: Promise<unknown> = Promise.resolve();
|
|
63
66
|
private interruptBuffer?: Uint8Array;
|
|
64
67
|
private messageConsumer:
|
|
65
68
|
| ((message: MessageEvent<string>) => void)
|
|
@@ -227,18 +230,24 @@ export class PyodideBridge implements RunRequests, EditRequests {
|
|
|
227
230
|
return null;
|
|
228
231
|
}
|
|
229
232
|
|
|
230
|
-
|
|
233
|
+
const durableSave = this.saveRpc.saveNotebook(request);
|
|
234
|
+
// Generated exports read the session-owned app in the main worker.
|
|
235
|
+
this.pendingSessionSave = this.pendingSessionSave
|
|
236
|
+
.catch(() => undefined)
|
|
237
|
+
.then(async () => {
|
|
238
|
+
await durableSave;
|
|
239
|
+
await this.rpc.proxy.request.saveNotebook(request);
|
|
240
|
+
});
|
|
241
|
+
void this.pendingSessionSave.catch((error) => {
|
|
242
|
+
Logger.error(error);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
await durableSave;
|
|
231
246
|
const code = await this.readCode();
|
|
232
247
|
if (code.contents) {
|
|
233
248
|
notebookFileStore.saveFile(code.contents);
|
|
234
249
|
fallbackFileStore.saveFile(code.contents);
|
|
235
250
|
}
|
|
236
|
-
// Also save to the other worker, since this is needed for
|
|
237
|
-
// exporting to HTML
|
|
238
|
-
// Fire-and-forget
|
|
239
|
-
void this.rpc.proxy.request.saveNotebook(request).catch((error) => {
|
|
240
|
-
Logger.error(error);
|
|
241
|
-
});
|
|
242
251
|
return null;
|
|
243
252
|
};
|
|
244
253
|
|
|
@@ -510,6 +519,7 @@ export class PyodideBridge implements RunRequests, EditRequests {
|
|
|
510
519
|
exportAsHTML: EditRequests["exportAsHTML"] = async (
|
|
511
520
|
request: ExportAsHTMLRequest,
|
|
512
521
|
) => {
|
|
522
|
+
await this.pendingSessionSave;
|
|
513
523
|
if (
|
|
514
524
|
process.env.NODE_ENV === "development" ||
|
|
515
525
|
process.env.NODE_ENV === "test"
|
|
@@ -520,17 +530,29 @@ export class PyodideBridge implements RunRequests, EditRequests {
|
|
|
520
530
|
functionName: "export_html",
|
|
521
531
|
payload: request,
|
|
522
532
|
});
|
|
523
|
-
return response as string
|
|
533
|
+
return response as ExportedFile<string>;
|
|
524
534
|
};
|
|
525
535
|
|
|
526
536
|
exportAsMarkdown: EditRequests["exportAsMarkdown"] = async (
|
|
527
537
|
request: ExportAsMarkdownRequest,
|
|
528
538
|
) => {
|
|
539
|
+
await this.pendingSessionSave;
|
|
529
540
|
const response = await this.rpc.proxy.request.bridge({
|
|
530
541
|
functionName: "export_markdown",
|
|
531
542
|
payload: request,
|
|
532
543
|
});
|
|
533
|
-
return response as string
|
|
544
|
+
return response as ExportedFile<string>;
|
|
545
|
+
};
|
|
546
|
+
|
|
547
|
+
exportAsScript: EditRequests["exportAsScript"] = async (
|
|
548
|
+
request: ExportAsScriptRequest,
|
|
549
|
+
) => {
|
|
550
|
+
await this.pendingSessionSave;
|
|
551
|
+
const response = await this.rpc.proxy.request.bridge({
|
|
552
|
+
functionName: "export_script",
|
|
553
|
+
payload: request,
|
|
554
|
+
});
|
|
555
|
+
return response as ExportedFile<string>;
|
|
534
556
|
};
|
|
535
557
|
|
|
536
558
|
previewDatasetColumn: EditRequests["previewDatasetColumn"] = async (
|
|
@@ -629,6 +651,16 @@ export class PyodideBridge implements RunRequests, EditRequests {
|
|
|
629
651
|
return null;
|
|
630
652
|
};
|
|
631
653
|
|
|
654
|
+
discoverDataSources: EditRequests["discoverDataSources"] = async (
|
|
655
|
+
request,
|
|
656
|
+
) => {
|
|
657
|
+
await this.putControlRequest({
|
|
658
|
+
type: "discover-data-sources",
|
|
659
|
+
...request,
|
|
660
|
+
});
|
|
661
|
+
return null;
|
|
662
|
+
};
|
|
663
|
+
|
|
632
664
|
getUsageStats = throwNotImplemented;
|
|
633
665
|
getEnvironmentInfo: EditRequests["getEnvironmentInfo"] = async () => {
|
|
634
666
|
const response = await this.rpc.proxy.request.bridge({
|
|
@@ -642,6 +674,7 @@ export class PyodideBridge implements RunRequests, EditRequests {
|
|
|
642
674
|
getWorkspaceFiles = throwNotImplemented;
|
|
643
675
|
getRunningNotebooks = throwNotImplemented;
|
|
644
676
|
shutdownSession = throwNotImplemented;
|
|
677
|
+
getExportAvailability = throwNotImplemented;
|
|
645
678
|
exportAsIPYNB = throwNotImplemented;
|
|
646
679
|
exportAsPDF = throwNotImplemented;
|
|
647
680
|
autoExportAsHTML = throwNotImplemented;
|