@marimo-team/islands 0.23.17-dev4 → 0.23.17-dev40
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-f9vLjQiF.js → ConnectedDataExplorerComponent-BBZRR1-C.js} +16 -16
- package/dist/{ErrorBoundary-DhLqwSWA.js → ErrorBoundary-CptUP1_t.js} +3 -3
- package/dist/{ImageComparisonComponent-C3y7L3eJ.js → ImageComparisonComponent-fdY0LUuf.js} +38 -27
- package/dist/{any-language-editor-DifE-fl7.js → any-language-editor-BnIS9Mvg.js} +21 -21
- package/dist/assets/__vite-browser-external-DUz7vvG0.js +1 -0
- package/dist/assets/{worker-CDEKsBgX.js → worker-Cl1KJG2_.js} +2 -2
- package/dist/{button-BSm8IXRU.js → button-8aS1QcHr.js} +4 -2
- package/dist/{capabilities-C6OQ5YBO.js → capabilities-D23lgbH_.js} +1 -1
- package/dist/chat-ui-DH9M4L-6.js +8620 -0
- package/dist/{check-DP7U0H0G.js → check-D-BaEjEh.js} +1 -1
- package/dist/{common-BcQ4kHDP.js → common-DSVkRiDF.js} +652 -652
- package/dist/{copy-DNq8q3zw.js → copy-BkA3t7rJ.js} +2 -2
- package/dist/{defaultLocale-BpsHxBd7.js → defaultLocale-BsfI21d_.js} +26 -26
- package/dist/{diagram-PSM6KHXK-D4ftnaua.js → diagram-PSM6KHXK-D3GsWNcQ.js} +1 -1
- package/dist/{dist-BpeiYpG2.js → dist-3F1XDSYc.js} +2 -2
- package/dist/{dist-BP39XM8u.js → dist-B-upo54Y.js} +1 -1
- package/dist/{dist-BdqCKQ1O.js → dist-B1W12VzQ.js} +2 -2
- package/dist/{dist-3NCqaWjt.js → dist-B6au1PTB.js} +2 -2
- package/dist/{dist-BCX980mU.js → dist-BDwn6WIk.js} +2 -2
- package/dist/dist-BDzVdDo2.js +8 -0
- package/dist/dist-BH2Tq1S1.js +5 -0
- package/dist/dist-BIhBrzqg.js +5 -0
- package/dist/dist-BLCsvXTG.js +6 -0
- package/dist/dist-BNVtuFdd.js +5 -0
- package/dist/{dist-BOMbzyb3.js → dist-BNeJ8P-4.js} +3 -3
- package/dist/{dist-LpLXa9Lb.js → dist-BT96P4oR.js} +2 -2
- package/dist/{dist-CFSb_Frm.js → dist-BUzxp0Xf.js} +1 -1
- package/dist/{dist-JGehap7K.js → dist-Ba1WFESi.js} +5 -5
- package/dist/{dist-CRmSjkLP.js → dist-BrKNRKC8.js} +2 -2
- package/dist/{dist-BIEt-mlF.js → dist-BvvQUFUK.js} +1 -1
- package/dist/{dist-pqIwD8VD.js → dist-BxR-g1ig.js} +2 -2
- package/dist/{dist-DviUBLUp.js → dist-CBraAJlT.js} +4 -4
- package/dist/{dist-BwhXBwMd.js → dist-CDBV8n0I.js} +1 -1
- package/dist/dist-CEJ6Lowi.js +5 -0
- package/dist/{dist-CSlJrrAw.js → dist-CWYsUX_O.js} +1 -1
- package/dist/dist-CYoa69kj.js +5 -0
- package/dist/{dist-DwqBJ014.js → dist-CdKRSqjo.js} +2 -2
- package/dist/{dist-De-GlSAs.js → dist-Cds3J-EE.js} +15 -9
- package/dist/dist-CnZyXFq3.js +8 -0
- package/dist/dist-D-4HaJKc.js +8 -0
- package/dist/{dist-C18MUWTj.js → dist-D8SX5Qdg.js} +2 -2
- package/dist/{dist-D3bdsa1d.js → dist-DFQ91Mx1.js} +4 -4
- package/dist/{dist-BPF0gTRZ.js → dist-DGQmYLkh.js} +1 -1
- package/dist/dist-DIGyvbg3.js +5 -0
- package/dist/dist-DIhaoCL4.js +5 -0
- package/dist/{dist-C2_4pjuS.js → dist-DK0pFEmc.js} +1 -1
- package/dist/{dist-BTdrwBWP.js → dist-DQj6A7SE.js} +3 -3
- package/dist/{dist-DIlxLFXG.js → dist-DarwE0ZO.js} +11 -11
- package/dist/{dist-DaXJPX8F.js → dist-Ddb0VFS3.js} +2 -2
- package/dist/dist-DgQleHmb.js +8 -0
- package/dist/{dist-DZwp8lro.js → dist-DgkJegw4.js} +3 -3
- package/dist/{dist-BO5HRiTf.js → dist-DjQRRttD.js} +2 -2
- package/dist/dist-JaoLzZmt.js +5 -0
- package/dist/{dist-B2jmQKKx.js → dist-PQDZaPEo.js} +2 -2
- package/dist/{dist-4lVIWUT3.js → dist-Slyx4vXr.js} +1 -1
- package/dist/{dist-DgjbnqTg.js → dist-dJeh9ef4.js} +2 -2
- package/dist/dist-pEX0t8ZV.js +6 -0
- package/dist/{dist-w_gWE5Wf.js → dist-v-kAJfMq.js} +1 -1
- package/dist/{error-banner-B_GQfxxD.js → error-banner-dmkbm21G.js} +2 -2
- package/dist/{esm-DzZVaNH4.js → esm-D8vjxCyn.js} +3 -3
- package/dist/{esm-DKOv6P2M.js → esm-DEQU-LUO.js} +22 -22
- package/dist/{extends-w2_S44bV.js → extends-DDOVg_b8.js} +5 -5
- package/dist/{formats-Dwq1THhr.js → formats-xc-Msa0v.js} +3 -3
- package/dist/{ganttDiagram-JELNMOA3-CXXCN7LN.js → ganttDiagram-JELNMOA3-D9ywbC5g.js} +3 -3
- package/dist/{glide-data-editor-B9K6-MJe.js → glide-data-editor-CyTNN635.js} +9 -9
- package/dist/{html-to-image-CZ1kLKkq.js → html-to-image-DrEMf75G.js} +2219 -2209
- package/dist/{input-C5K4VBvB.js → input-B4ddy8Jy.js} +6 -6
- package/dist/{label-DjoYwqmj.js → label-CzHncDvS.js} +2 -2
- package/dist/{linear-H_vsH9ao.js → linear-BOGUO8Cd.js} +2 -2
- package/dist/{loader-DX-IDS7n.js → loader-C7SfUdOD.js} +10 -10
- package/dist/main.js +729 -733
- package/dist/{mermaid-hv6IckVw.js → mermaid-CpL8y5pw.js} +9 -9
- package/dist/{precisionRound-CnHPY_5v.js → precisionRound-Bq50n-bG.js} +1 -1
- package/dist/{process-output-bVfbcx5_.js → process-output-B9IHQuus.js} +3 -3
- package/dist/{quadrantDiagram-AYHSOK5B-BqDB9tUC.js → quadrantDiagram-AYHSOK5B-DD4QBdSv.js} +2 -2
- package/dist/{react-vega-BazDALhI.js → react-vega-Cg17A6rO.js} +8017 -4777
- package/dist/{reveal-component-9QGZq7uF.js → reveal-component-CUzZw4ZH.js} +19 -19
- package/dist/{spec-CQKL_D3i.js → spec-uMKNvMRZ.js} +4 -4
- package/dist/{strings-DIy9NAmg.js → strings-BUDhKc0s.js} +299 -299
- package/dist/style.css +1 -1
- package/dist/{swiper-component-CEdAYjb7.js → swiper-component-dl1H9WIW.js} +2 -2
- package/dist/{time-Bb4A7I6a.js → time-CQnohwNM.js} +2 -2
- package/dist/{toDate-CqhBWjvO.js → toDate-BiyuFdnT.js} +3 -3
- package/dist/{tooltip-f-qR0iUx.js → tooltip-CkCWlUOf.js} +3 -3
- package/dist/{types-DicH9Oi0.js → types-uENjXsiC.js} +1 -1
- package/dist/{useAsyncData-CYOQb1EW.js → useAsyncData-DqJLysjC.js} +1 -1
- package/dist/{useDateFormatter-Bt2qr_XS.js → useDateFormatter-BeN3fHMH.js} +2 -2
- package/dist/{useDeepCompareMemoize-CSHQeQEI.js → useDeepCompareMemoize-CvlmHAmy.js} +1 -1
- package/dist/{useIframeCapabilities-kA09ykNq.js → useIframeCapabilities-pLC-ceDP.js} +1 -1
- package/dist/{useLifecycle-CMJGqBaY.js → useLifecycle-D4N02q82.js} +3 -3
- package/dist/{useTheme-DYfby65G.js → useTheme-Dur2p3iC.js} +4 -3
- package/dist/{vega-component-C7nO5zcf.js → vega-component-Cft458Sr.js} +13 -13
- package/dist/{vega-embed-container-DAQAMjiO.js → vega-embed-container-S9gRu4TZ.js} +3 -3
- package/dist/{vega-loader.browser-DmpxCJqx.js → vega-loader.browser-CmLRI3wk.js} +104 -111
- package/dist/{xychartDiagram-PRI3JC2R-BSkNxYNw.js → xychartDiagram-PRI3JC2R-D9DgEtKn.js} +2 -2
- package/dist/{zod-BUcyXSdR.js → zod-yTHHzJiv.js} +37 -38
- package/package.json +6 -6
- package/src/__mocks__/requests.ts +5 -0
- package/src/components/app-config/user-config-form.tsx +26 -0
- package/src/components/chat/__tests__/chat-abort.test.ts +51 -0
- package/src/components/chat/chat-abort.ts +44 -0
- package/src/components/chat/chat-display.tsx +17 -0
- package/src/components/chat/chat-panel.tsx +85 -37
- package/src/components/chat/chat-utils.ts +1 -1
- package/src/components/data-table/charts/__tests__/schemas.test.ts +35 -0
- package/src/components/data-table/charts/schemas.ts +5 -2
- package/src/components/databases/icons/huggingface.svg +8 -0
- package/src/components/datasets/__tests__/icons.test.ts +21 -0
- package/src/components/datasets/icons.tsx +10 -0
- package/src/components/datasources/__tests__/components.test.tsx +29 -0
- package/src/components/datasources/components.tsx +8 -3
- package/src/components/editor/actions/export-dialog/__tests__/export-dialog.test.tsx +35 -0
- package/src/components/editor/actions/export-dialog/__tests__/state.test.ts +61 -0
- package/src/components/editor/actions/export-dialog/export-command.ts +4 -9
- package/src/components/editor/actions/export-dialog/export-dialog.tsx +27 -23
- package/src/components/editor/actions/export-dialog/format-notice.tsx +28 -0
- package/src/components/editor/actions/export-dialog/state.ts +23 -5
- package/src/components/editor/actions/export-dialog/use-export-dialog.ts +7 -15
- package/src/components/editor/actions/useConfigActions.tsx +24 -0
- package/src/components/editor/ai/add-cell-with-ai.tsx +2 -2
- package/src/components/editor/ai/ai-completion-editor.tsx +2 -2
- package/src/components/editor/chrome/panels/outline/__tests__/useActiveOutline.test.ts +26 -0
- package/src/components/editor/code/__tests__/readonly-python-code.test.tsx +114 -2
- package/src/components/editor/connections/storage/__tests__/__snapshots__/as-code.test.ts.snap +14 -0
- package/src/components/editor/connections/storage/__tests__/as-code.test.ts +20 -0
- package/src/components/editor/connections/storage/add-storage-form.tsx +10 -0
- package/src/components/editor/connections/storage/as-code.ts +19 -1
- package/src/components/editor/connections/storage/schemas.ts +19 -0
- package/src/components/storage/__tests__/storage-snippets.test.ts +88 -0
- package/src/components/storage/components.tsx +2 -0
- package/src/components/storage/storage-snippets.ts +58 -0
- package/src/core/__tests__/mode.test.ts +85 -0
- package/src/core/ai/constants.ts +7 -0
- package/src/core/ai/staged-cells.ts +3 -0
- package/src/core/codemirror/__tests__/setup.test.ts +1 -1
- package/src/core/codemirror/cm.ts +2 -2
- package/src/core/codemirror/code-lens/__tests__/extension.test.ts +4 -5
- package/src/core/codemirror/code-lens/extension.ts +3 -3
- package/src/core/codemirror/find-replace/search-highlight.ts +8 -3
- package/src/core/codemirror/language/languages/sql/__tests__/renderers.test.tsx +44 -0
- package/src/core/codemirror/language/languages/sql/renderers.tsx +3 -3
- package/src/core/codemirror/theme/__tests__/light.test.ts +59 -123
- package/src/core/codemirror/theme/dark.ts +6 -5
- package/src/core/codemirror/theme/light.ts +6 -4
- package/src/core/config/__tests__/config-schema.test.ts +2 -0
- package/src/core/config/config-schema.ts +1 -0
- package/src/core/config/feature-flag.tsx +0 -2
- package/src/core/dom/outline.ts +25 -2
- package/src/core/hotkeys/hotkeys.ts +2 -0
- package/src/core/mode.ts +11 -9
- package/src/core/network/__tests__/api.test.ts +132 -17
- package/src/core/network/__tests__/export-filename.test.ts +43 -0
- package/src/core/network/__tests__/requests-lazy.test.ts +1 -0
- package/src/core/network/api.ts +47 -12
- package/src/core/network/export-filename.ts +37 -0
- package/src/core/network/markdown-export.ts +19 -0
- package/src/core/network/requests-network.ts +29 -5
- package/src/core/network/types.ts +3 -0
- package/src/core/storage/types.ts +1 -0
- package/src/css/app/codemirror.css +11 -7
- package/src/css/globals.css +8 -0
- package/src/plugins/impl/DataEditorPlugin.tsx +2 -9
- package/src/plugins/impl/__tests__/DataEditorPlugin.test.ts +18 -0
- package/src/plugins/impl/chat/chat-ui.tsx +51 -3
- package/src/plugins/impl/data-frames/__tests__/schema.test.ts +30 -0
- package/src/plugins/impl/data-frames/schema.ts +4 -1
- package/src/plugins/impl/image-comparison/ImageComparisonComponent.tsx +20 -1
- package/src/plugins/impl/image-comparison/__tests__/ImageComparisonComponent.test.tsx +58 -0
- package/src/stories/theme.stories.tsx +71 -1
- package/dist/assets/__vite-browser-external-DltVXDv7.js +0 -1
- package/dist/chat-ui-DsZJj75A.js +0 -8369
- package/dist/dist-5o9uz19x.js +0 -5
- package/dist/dist-BpDH0DEP.js +0 -5
- package/dist/dist-BpXhbQXO.js +0 -8
- package/dist/dist-ByadAk_L.js +0 -5
- package/dist/dist-CK0g7JvP.js +0 -8
- package/dist/dist-CfzCtiq2.js +0 -5
- package/dist/dist-D8JtVFrW.js +0 -8
- package/dist/dist-DK2Oocpu.js +0 -6
- package/dist/dist-DnSu977r.js +0 -5
- package/dist/dist-UmxJm6sW.js +0 -8
- package/dist/dist-aYSBQSyM.js +0 -5
- package/dist/dist-kkZ1Ztlh.js +0 -6
- package/dist/dist-ngPczwNi.js +0 -5
- package/dist/dist-wQRyxyTV.js +0 -5
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { render } from "@testing-library/react";
|
|
4
|
+
import { describe, expect, it, vi } from "vitest";
|
|
5
|
+
import type { DataType } from "@/core/kernel/messages";
|
|
6
|
+
import { Logger } from "@/utils/Logger";
|
|
7
|
+
import { ColumnName } from "../components";
|
|
8
|
+
|
|
9
|
+
describe("ColumnName", () => {
|
|
10
|
+
it("renders the unknown icon for unrecognized data types", () => {
|
|
11
|
+
const warn = vi.spyOn(Logger, "warn").mockImplementation(() => {});
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
const unrecognized = render(
|
|
15
|
+
<ColumnName columnName="geom" dataType={"geometry" as DataType} />,
|
|
16
|
+
);
|
|
17
|
+
const unknown = render(
|
|
18
|
+
<ColumnName columnName="geom" dataType="unknown" />,
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
expect(unrecognized.container.innerHTML).toEqual(
|
|
22
|
+
unknown.container.innerHTML,
|
|
23
|
+
);
|
|
24
|
+
expect(warn).not.toHaveBeenCalled();
|
|
25
|
+
} finally {
|
|
26
|
+
warn.mockRestore();
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -5,7 +5,11 @@ import type { CSSProperties } from "react";
|
|
|
5
5
|
import { TreeChevron } from "@/components/editor/file-tree/tree-actions";
|
|
6
6
|
import type { DataType } from "@/core/kernel/messages";
|
|
7
7
|
import { cn } from "@/utils/cn";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
DATA_TYPE_ICON,
|
|
10
|
+
getDataTypeColor,
|
|
11
|
+
resolveDataType,
|
|
12
|
+
} from "../datasets/icons";
|
|
9
13
|
|
|
10
14
|
export const RotatingChevron: React.FC<{ isExpanded: boolean }> = ({
|
|
11
15
|
isExpanded,
|
|
@@ -101,8 +105,9 @@ export const ColumnName = ({
|
|
|
101
105
|
columnName: React.ReactNode;
|
|
102
106
|
dataType: DataType;
|
|
103
107
|
}) => {
|
|
104
|
-
const
|
|
105
|
-
const
|
|
108
|
+
const resolvedDataType = resolveDataType(dataType);
|
|
109
|
+
const Icon = DATA_TYPE_ICON[resolvedDataType];
|
|
110
|
+
const color = getDataTypeColor(resolvedDataType);
|
|
106
111
|
|
|
107
112
|
return (
|
|
108
113
|
<div className="flex flex-row items-center gap-1.5">
|
|
@@ -259,6 +259,7 @@ describe("ExportDialog", () => {
|
|
|
259
259
|
format: "pdf",
|
|
260
260
|
dependenciesAvailable: false,
|
|
261
261
|
missingPackages: ["nbconvert[webpdf]"],
|
|
262
|
+
missingSetup: [],
|
|
262
263
|
},
|
|
263
264
|
],
|
|
264
265
|
}),
|
|
@@ -275,6 +276,40 @@ describe("ExportDialog", () => {
|
|
|
275
276
|
expect(screen.getByTestId("export-format-pdf")).toBeVisible();
|
|
276
277
|
});
|
|
277
278
|
|
|
279
|
+
it("explains missing Playwright Chromium setup", async () => {
|
|
280
|
+
store.set(
|
|
281
|
+
requestClientAtom,
|
|
282
|
+
MockRequestClient.create({
|
|
283
|
+
getExportAvailability: vi.fn().mockResolvedValue({
|
|
284
|
+
source: "server",
|
|
285
|
+
formats: [
|
|
286
|
+
{
|
|
287
|
+
format: "pdf",
|
|
288
|
+
dependenciesAvailable: false,
|
|
289
|
+
missingPackages: [],
|
|
290
|
+
missingSetup: [
|
|
291
|
+
{
|
|
292
|
+
name: "playwright-chromium",
|
|
293
|
+
command: "uv run playwright install chromium",
|
|
294
|
+
},
|
|
295
|
+
],
|
|
296
|
+
},
|
|
297
|
+
],
|
|
298
|
+
}),
|
|
299
|
+
}),
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
renderDialog("pdf");
|
|
303
|
+
|
|
304
|
+
expect(
|
|
305
|
+
await screen.findByText("PDF export requires Playwright Chromium."),
|
|
306
|
+
).toBeVisible();
|
|
307
|
+
expect(
|
|
308
|
+
screen.getByText("uv run playwright install chromium"),
|
|
309
|
+
).toBeVisible();
|
|
310
|
+
expect(screen.getByTestId("export-submit")).toBeDisabled();
|
|
311
|
+
});
|
|
312
|
+
|
|
278
313
|
it("announces requirement checks as status updates", () => {
|
|
279
314
|
store.set(
|
|
280
315
|
requestClientAtom,
|
|
@@ -19,11 +19,13 @@ const AVAILABLE: ExportAvailabilityResponse = {
|
|
|
19
19
|
format: "ipynb",
|
|
20
20
|
dependenciesAvailable: true,
|
|
21
21
|
missingPackages: [],
|
|
22
|
+
missingSetup: [],
|
|
22
23
|
},
|
|
23
24
|
{
|
|
24
25
|
format: "pdf",
|
|
25
26
|
dependenciesAvailable: true,
|
|
26
27
|
missingPackages: [],
|
|
28
|
+
missingSetup: [],
|
|
27
29
|
},
|
|
28
30
|
],
|
|
29
31
|
};
|
|
@@ -188,6 +190,7 @@ describe("getExportFormatStatus", () => {
|
|
|
188
190
|
format: "pdf",
|
|
189
191
|
dependenciesAvailable: false,
|
|
190
192
|
missingPackages: ["nbconvert[webpdf]"],
|
|
193
|
+
missingSetup: [],
|
|
191
194
|
},
|
|
192
195
|
],
|
|
193
196
|
},
|
|
@@ -202,6 +205,64 @@ describe("getExportFormatStatus", () => {
|
|
|
202
205
|
});
|
|
203
206
|
});
|
|
204
207
|
|
|
208
|
+
it("reports missing server setup", () => {
|
|
209
|
+
expect(
|
|
210
|
+
status("pdf", {
|
|
211
|
+
availability: {
|
|
212
|
+
status: "success",
|
|
213
|
+
data: {
|
|
214
|
+
source: "server",
|
|
215
|
+
formats: [
|
|
216
|
+
{
|
|
217
|
+
format: "pdf",
|
|
218
|
+
dependenciesAvailable: false,
|
|
219
|
+
missingPackages: [],
|
|
220
|
+
missingSetup: [
|
|
221
|
+
{
|
|
222
|
+
name: "playwright-chromium",
|
|
223
|
+
command: "uv run playwright install chromium",
|
|
224
|
+
},
|
|
225
|
+
],
|
|
226
|
+
},
|
|
227
|
+
],
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
}),
|
|
231
|
+
).toEqual({
|
|
232
|
+
available: false,
|
|
233
|
+
reason: {
|
|
234
|
+
type: "missing-setup",
|
|
235
|
+
requirements: [
|
|
236
|
+
{
|
|
237
|
+
name: "playwright-chromium",
|
|
238
|
+
command: "uv run playwright install chromium",
|
|
239
|
+
},
|
|
240
|
+
],
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it("fails open when an unavailable format has no requirements", () => {
|
|
246
|
+
expect(
|
|
247
|
+
status("pdf", {
|
|
248
|
+
availability: {
|
|
249
|
+
status: "success",
|
|
250
|
+
data: {
|
|
251
|
+
source: "server",
|
|
252
|
+
formats: [
|
|
253
|
+
{
|
|
254
|
+
format: "pdf",
|
|
255
|
+
dependenciesAvailable: false,
|
|
256
|
+
missingPackages: [],
|
|
257
|
+
missingSetup: [],
|
|
258
|
+
},
|
|
259
|
+
],
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
}),
|
|
263
|
+
).toEqual({ available: true, availabilityCheckFailed: true });
|
|
264
|
+
});
|
|
265
|
+
|
|
205
266
|
it.each([
|
|
206
267
|
["html", true, undefined],
|
|
207
268
|
["markdown", true, undefined],
|
|
@@ -1,18 +1,13 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
|
+
import {
|
|
4
|
+
MARKDOWN_EXTENSIONS,
|
|
5
|
+
MARKDOWN_SUFFIXES,
|
|
6
|
+
} from "@/core/network/markdown-export";
|
|
3
7
|
import { Filenames } from "@/utils/filenames";
|
|
4
8
|
import { shellQuote } from "@/utils/shell";
|
|
5
9
|
import type { ExportFormat, ExportOptions, MarkdownFlavor } from "./state";
|
|
6
10
|
|
|
7
|
-
const MARKDOWN_EXTENSIONS: Record<MarkdownFlavor, string> = {
|
|
8
|
-
pymdown: "md",
|
|
9
|
-
qmd: "qmd",
|
|
10
|
-
mystmd: "myst.md",
|
|
11
|
-
mdx: "mdx",
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
const MARKDOWN_SUFFIXES = [".myst.md", ".markdown", ".qmd", ".mdx", ".md"];
|
|
15
|
-
|
|
16
11
|
function inferMarkdownFlavor(filename: string): MarkdownFlavor {
|
|
17
12
|
if (filename.endsWith(".myst.md")) {
|
|
18
13
|
return "mystmd";
|
|
@@ -101,31 +101,35 @@ export const ExportDialog: React.FC<{
|
|
|
101
101
|
})}
|
|
102
102
|
</TabsList>
|
|
103
103
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
formatLabel={definition.label}
|
|
118
|
-
status={status}
|
|
119
|
-
/>
|
|
104
|
+
{/* One panel per tab so each trigger's aria-controls resolves. */}
|
|
105
|
+
{formats.map(({ format: candidate }) => (
|
|
106
|
+
<TabsContent
|
|
107
|
+
key={candidate}
|
|
108
|
+
value={candidate}
|
|
109
|
+
className="mt-0 min-h-0 min-w-0 overflow-y-auto px-4 py-3"
|
|
110
|
+
>
|
|
111
|
+
<div className="mb-3">
|
|
112
|
+
<h3 className="text-base font-semibold">{definition.label}</h3>
|
|
113
|
+
<p className="mt-0.5 text-sm text-muted-foreground">
|
|
114
|
+
{definition.description}
|
|
115
|
+
</p>
|
|
116
|
+
</div>
|
|
120
117
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
disabled={isExporting}
|
|
118
|
+
<FormatNotice
|
|
119
|
+
format={format}
|
|
120
|
+
formatLabel={definition.label}
|
|
121
|
+
status={status}
|
|
126
122
|
/>
|
|
127
|
-
|
|
128
|
-
|
|
123
|
+
|
|
124
|
+
{usesBrowserPrint ? null : (
|
|
125
|
+
<FormatOptions
|
|
126
|
+
options={options}
|
|
127
|
+
updateOptions={updateOptions}
|
|
128
|
+
disabled={isExporting}
|
|
129
|
+
/>
|
|
130
|
+
)}
|
|
131
|
+
</TabsContent>
|
|
132
|
+
))}
|
|
129
133
|
</Tabs>
|
|
130
134
|
|
|
131
135
|
<footer className="min-w-0 border-t bg-muted/20 px-4 py-3">
|
|
@@ -10,8 +10,18 @@ import type {
|
|
|
10
10
|
ExportBlockReason,
|
|
11
11
|
ExportFormat,
|
|
12
12
|
ExportFormatStatus,
|
|
13
|
+
ExportSetupRequirement,
|
|
13
14
|
} from "./state";
|
|
14
15
|
|
|
16
|
+
const SETUP_REQUIREMENTS: Record<
|
|
17
|
+
ExportSetupRequirement["name"],
|
|
18
|
+
{ description: string }
|
|
19
|
+
> = {
|
|
20
|
+
"playwright-chromium": {
|
|
21
|
+
description: "PDF export requires Playwright Chromium.",
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
15
25
|
export function FormatStatusIcon({ status }: { status: ExportFormatStatus }) {
|
|
16
26
|
if (status.available && !status.availabilityCheckFailed) {
|
|
17
27
|
return null;
|
|
@@ -124,6 +134,24 @@ function ReasonNotice({
|
|
|
124
134
|
where marimo is running to use this export.
|
|
125
135
|
</Notice>
|
|
126
136
|
);
|
|
137
|
+
case "missing-setup":
|
|
138
|
+
return (
|
|
139
|
+
<Notice>
|
|
140
|
+
<span className="block min-w-0">
|
|
141
|
+
{reason.requirements.map((requirement) => {
|
|
142
|
+
const details = SETUP_REQUIREMENTS[requirement.name];
|
|
143
|
+
return (
|
|
144
|
+
<span className="block" key={requirement.name}>
|
|
145
|
+
{details.description}
|
|
146
|
+
<code className="mt-1 block break-all font-mono">
|
|
147
|
+
{requirement.command}
|
|
148
|
+
</code>
|
|
149
|
+
</span>
|
|
150
|
+
);
|
|
151
|
+
})}
|
|
152
|
+
</span>
|
|
153
|
+
</Notice>
|
|
154
|
+
);
|
|
127
155
|
case "wasm-runtime":
|
|
128
156
|
return (
|
|
129
157
|
<Notice>
|
|
@@ -15,6 +15,8 @@ export const EXPORT_FORMATS = [
|
|
|
15
15
|
] as const;
|
|
16
16
|
|
|
17
17
|
export type ExportFormat = (typeof EXPORT_FORMATS)[number];
|
|
18
|
+
export type ExportSetupRequirement =
|
|
19
|
+
ExportAvailabilityResponse["formats"][number]["missingSetup"][number];
|
|
18
20
|
|
|
19
21
|
export const MARKDOWN_FLAVORS = ["pymdown", "qmd", "mystmd", "mdx"] as const;
|
|
20
22
|
export const IPYNB_SORT_MODES = ["topological", "top-down"] as const;
|
|
@@ -185,6 +187,7 @@ export type ExportBlockReason =
|
|
|
185
187
|
| { type: "checking-requirements" }
|
|
186
188
|
| { type: "notebook-must-be-named" }
|
|
187
189
|
| { type: "missing-packages"; packages: string[] }
|
|
190
|
+
| { type: "missing-setup"; requirements: ExportSetupRequirement[] }
|
|
188
191
|
| { type: "wasm-runtime" };
|
|
189
192
|
|
|
190
193
|
export interface ExportFormatStatus {
|
|
@@ -308,12 +311,27 @@ export function getExportFormatStatus({
|
|
|
308
311
|
return { available: true, availabilityCheckFailed: true };
|
|
309
312
|
}
|
|
310
313
|
if (!serverStatus.dependenciesAvailable) {
|
|
314
|
+
if (serverStatus.missingPackages.length > 0) {
|
|
315
|
+
return {
|
|
316
|
+
available: false,
|
|
317
|
+
reason: {
|
|
318
|
+
type: "missing-packages",
|
|
319
|
+
packages: serverStatus.missingPackages,
|
|
320
|
+
},
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
if (serverStatus.missingSetup.length > 0) {
|
|
324
|
+
return {
|
|
325
|
+
available: false,
|
|
326
|
+
reason: {
|
|
327
|
+
type: "missing-setup",
|
|
328
|
+
requirements: serverStatus.missingSetup,
|
|
329
|
+
},
|
|
330
|
+
};
|
|
331
|
+
}
|
|
311
332
|
return {
|
|
312
|
-
available:
|
|
313
|
-
|
|
314
|
-
type: "missing-packages",
|
|
315
|
-
packages: serverStatus.missingPackages,
|
|
316
|
-
},
|
|
333
|
+
available: true,
|
|
334
|
+
availabilityCheckFailed: true,
|
|
317
335
|
};
|
|
318
336
|
}
|
|
319
337
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
|
-
import { useAtom
|
|
3
|
+
import { useAtom } from "jotai";
|
|
4
4
|
import { useEffect, useRef, useState } from "react";
|
|
5
5
|
import { toast } from "@/components/ui/use-toast";
|
|
6
6
|
import {
|
|
7
7
|
updateCellOutputsWithScreenshots,
|
|
8
8
|
useEnrichCellOutputs,
|
|
9
9
|
} from "@/core/export/hooks";
|
|
10
|
-
import { runDuringPresentMode
|
|
10
|
+
import { runDuringPresentMode } from "@/core/mode";
|
|
11
11
|
import { useRequestClient } from "@/core/network/requests";
|
|
12
12
|
import type { ExportAvailabilityResponse } from "@/core/network/types";
|
|
13
13
|
import { useFilename } from "@/core/saving/filename";
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
withLoadingToast,
|
|
22
22
|
} from "@/utils/download";
|
|
23
23
|
import { Filenames } from "@/utils/filenames";
|
|
24
|
+
import { Logger } from "@/utils/Logger";
|
|
24
25
|
import { Paths } from "@/utils/paths";
|
|
25
26
|
import { getExportCommand } from "./export-command";
|
|
26
27
|
import { exportNotebook } from "./export-notebook";
|
|
@@ -277,8 +278,6 @@ function useExportDialogAction({
|
|
|
277
278
|
}) {
|
|
278
279
|
const requests = useRequestClient();
|
|
279
280
|
const takeScreenshots = useEnrichCellOutputs();
|
|
280
|
-
const viewState = useAtomValue(viewStateAtom);
|
|
281
|
-
const setViewState = useSetAtom(viewStateAtom);
|
|
282
281
|
const [isExporting, setIsExporting] = useState(false);
|
|
283
282
|
const dialogRef = useRef<HTMLDivElement>(null);
|
|
284
283
|
const mountedRef = useRef(true);
|
|
@@ -302,15 +301,7 @@ function useExportDialogAction({
|
|
|
302
301
|
const capturePNG = async () => {
|
|
303
302
|
const capture = () =>
|
|
304
303
|
captureCurrentAppView(dialogRef.current?.parentElement ?? null);
|
|
305
|
-
|
|
306
|
-
await capture();
|
|
307
|
-
return;
|
|
308
|
-
}
|
|
309
|
-
try {
|
|
310
|
-
await runDuringPresentMode(capture);
|
|
311
|
-
} finally {
|
|
312
|
-
setViewState(viewState);
|
|
313
|
-
}
|
|
304
|
+
await runDuringPresentMode(capture);
|
|
314
305
|
};
|
|
315
306
|
|
|
316
307
|
const submit = async () => {
|
|
@@ -342,8 +333,9 @@ function useExportDialogAction({
|
|
|
342
333
|
if (mountedRef.current) {
|
|
343
334
|
onClose();
|
|
344
335
|
}
|
|
345
|
-
} catch {
|
|
346
|
-
//
|
|
336
|
+
} catch (error) {
|
|
337
|
+
// Most helpers toast actionable errors, but not all (e.g. updateCellOutputs).
|
|
338
|
+
Logger.error("Export failed", error);
|
|
347
339
|
} finally {
|
|
348
340
|
if (mountedRef.current) {
|
|
349
341
|
setIsExporting(false);
|
|
@@ -141,6 +141,30 @@ export function useConfigActions() {
|
|
|
141
141
|
});
|
|
142
142
|
},
|
|
143
143
|
},
|
|
144
|
+
{
|
|
145
|
+
label: "Config > Disable code lens",
|
|
146
|
+
hidden: !(config.display.code_lens ?? true),
|
|
147
|
+
handle: () => {
|
|
148
|
+
handleUserConfig({
|
|
149
|
+
display: {
|
|
150
|
+
...config.display,
|
|
151
|
+
code_lens: false,
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
label: "Config > Enable code lens",
|
|
158
|
+
hidden: config.display.code_lens ?? true,
|
|
159
|
+
handle: () => {
|
|
160
|
+
handleUserConfig({
|
|
161
|
+
display: {
|
|
162
|
+
...config.display,
|
|
163
|
+
code_lens: true,
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
},
|
|
167
|
+
},
|
|
144
168
|
{
|
|
145
169
|
label: "Config > Set cell output area: above",
|
|
146
170
|
hidden: config.display.cell_output === "above",
|
|
@@ -51,6 +51,7 @@ import {
|
|
|
51
51
|
} from "@/components/ui/dropdown-menu";
|
|
52
52
|
import { toast } from "@/components/ui/use-toast";
|
|
53
53
|
import { AiModelId } from "@/core/ai/ids/ids";
|
|
54
|
+
import { AI_SDK_UI_THROTTLE_MS } from "@/core/ai/constants";
|
|
54
55
|
import { stagedAICellsAtom, useStagedCells } from "@/core/ai/staged-cells";
|
|
55
56
|
import type { ToolNotebookContext } from "@/core/ai/tools/base";
|
|
56
57
|
import { useCellActions } from "@/core/cells/cells";
|
|
@@ -117,8 +118,7 @@ export const AddCellWithAI: React.FC<{
|
|
|
117
118
|
};
|
|
118
119
|
|
|
119
120
|
const { sendMessage, stop, status, addToolOutput } = useChat({
|
|
120
|
-
|
|
121
|
-
experimental_throttle: 100,
|
|
121
|
+
throttle: AI_SDK_UI_THROTTLE_MS,
|
|
122
122
|
transport: new StreamingChunkTransport(
|
|
123
123
|
{
|
|
124
124
|
api: runtimeManager.getAiURL("completion").toString(),
|
|
@@ -27,6 +27,7 @@ import { Label } from "@/components/ui/label";
|
|
|
27
27
|
import { Switch } from "@/components/ui/switch";
|
|
28
28
|
import { Tooltip } from "@/components/ui/tooltip";
|
|
29
29
|
import { toast } from "@/components/ui/use-toast";
|
|
30
|
+
import { AI_SDK_UI_THROTTLE_MS } from "@/core/ai/constants";
|
|
30
31
|
import { stripWrappingBackticks } from "@/core/ai/strip-wrapping-backticks";
|
|
31
32
|
import { type AiCompletionCell, includeOtherCellsAtom } from "@/core/ai/state";
|
|
32
33
|
import type { CellId } from "@/core/cells/ids";
|
|
@@ -124,8 +125,7 @@ export const AiCompletionEditor: React.FC<Props> = ({
|
|
|
124
125
|
api: runtimeManager.getAiURL("completion").toString(),
|
|
125
126
|
headers: runtimeManager.headers(),
|
|
126
127
|
initialInput: initialPrompt,
|
|
127
|
-
|
|
128
|
-
experimental_throttle: 100,
|
|
128
|
+
throttle: AI_SDK_UI_THROTTLE_MS,
|
|
129
129
|
body: {
|
|
130
130
|
...(Object.keys(completionBody).length > 0
|
|
131
131
|
? completionBody
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { parseOutline } from "@/core/dom/outline";
|
|
4
|
+
import { findOutlineElements } from "../useActiveOutline";
|
|
5
|
+
|
|
6
|
+
describe("findOutlineElements", () => {
|
|
7
|
+
it.each([
|
|
8
|
+
"Design from the portfolio",
|
|
9
|
+
'Decorative Art of "Spanish California"',
|
|
10
|
+
`The painter's "Study"`,
|
|
11
|
+
])("finds an id-less heading named %s", (name) => {
|
|
12
|
+
const html = `<h3>${name}</h3>`;
|
|
13
|
+
document.body.innerHTML = html;
|
|
14
|
+
|
|
15
|
+
const outline = parseOutline({
|
|
16
|
+
mimetype: "text/html",
|
|
17
|
+
timestamp: 0,
|
|
18
|
+
channel: "output",
|
|
19
|
+
data: html,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
expect(
|
|
23
|
+
findOutlineElements(outline?.items ?? []).map(([element]) => element),
|
|
24
|
+
).toEqual([document.querySelector("h3")]);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -1,13 +1,61 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { EditorView } from "@codemirror/view";
|
|
4
|
+
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
|
5
|
+
import {
|
|
6
|
+
afterAll,
|
|
7
|
+
beforeAll,
|
|
8
|
+
beforeEach,
|
|
9
|
+
describe,
|
|
10
|
+
expect,
|
|
11
|
+
it,
|
|
12
|
+
vi,
|
|
13
|
+
} from "vitest";
|
|
5
14
|
import { SetupMocks } from "@/__mocks__/common";
|
|
6
15
|
import { TooltipProvider } from "@/components/ui/tooltip";
|
|
7
16
|
import { ReadonlyCode } from "../readonly-python-code";
|
|
8
17
|
|
|
18
|
+
const themeState = vi.hoisted(() => ({
|
|
19
|
+
value: "light" as "light" | "dark",
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
let originalRangeGetClientRects: PropertyDescriptor | undefined;
|
|
23
|
+
|
|
24
|
+
vi.mock("@/theme/useTheme", async (importOriginal) => {
|
|
25
|
+
const actual = await importOriginal<typeof import("@/theme/useTheme")>();
|
|
26
|
+
return {
|
|
27
|
+
...actual,
|
|
28
|
+
useTheme: () => ({ theme: themeState.value }),
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
|
|
9
32
|
beforeAll(() => {
|
|
10
33
|
SetupMocks.resizeObserver();
|
|
34
|
+
originalRangeGetClientRects = Object.getOwnPropertyDescriptor(
|
|
35
|
+
Range.prototype,
|
|
36
|
+
"getClientRects",
|
|
37
|
+
);
|
|
38
|
+
Object.defineProperty(Range.prototype, "getClientRects", {
|
|
39
|
+
configurable: true,
|
|
40
|
+
value: () => [],
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
afterAll(() => {
|
|
45
|
+
// Keep the shared JSDOM prototype unchanged for other test files.
|
|
46
|
+
if (originalRangeGetClientRects) {
|
|
47
|
+
Object.defineProperty(
|
|
48
|
+
Range.prototype,
|
|
49
|
+
"getClientRects",
|
|
50
|
+
originalRangeGetClientRects,
|
|
51
|
+
);
|
|
52
|
+
} else {
|
|
53
|
+
Reflect.deleteProperty(Range.prototype, "getClientRects");
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
beforeEach(() => {
|
|
58
|
+
themeState.value = "light";
|
|
11
59
|
});
|
|
12
60
|
|
|
13
61
|
/**
|
|
@@ -26,7 +74,71 @@ function isCollapsed(root: ParentNode) {
|
|
|
26
74
|
return root.querySelector(".cm")?.classList.contains("opacity-20") ?? false;
|
|
27
75
|
}
|
|
28
76
|
|
|
77
|
+
function getEditorIdentity(view: EditorView) {
|
|
78
|
+
return {
|
|
79
|
+
doc: view.state.doc,
|
|
80
|
+
dom: view.dom,
|
|
81
|
+
contentDOM: view.contentDOM,
|
|
82
|
+
scrollDOM: view.scrollDOM,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function expectEditorIdentity(
|
|
87
|
+
view: EditorView,
|
|
88
|
+
identity: ReturnType<typeof getEditorIdentity>,
|
|
89
|
+
) {
|
|
90
|
+
expect(view.state.doc).toBe(identity.doc);
|
|
91
|
+
expect(view.dom).toBe(identity.dom);
|
|
92
|
+
expect(view.contentDOM).toBe(identity.contentDOM);
|
|
93
|
+
expect(view.scrollDOM).toBe(identity.scrollDOM);
|
|
94
|
+
}
|
|
95
|
+
|
|
29
96
|
describe("ReadonlyCode", () => {
|
|
97
|
+
it("reconfigures the theme without replacing the editor", async () => {
|
|
98
|
+
let createdView: EditorView | undefined;
|
|
99
|
+
const onCreateEditor = (view: EditorView) => {
|
|
100
|
+
createdView = view;
|
|
101
|
+
};
|
|
102
|
+
const renderCode = (id: string) => (
|
|
103
|
+
<TooltipProvider>
|
|
104
|
+
<ReadonlyCode
|
|
105
|
+
id={id}
|
|
106
|
+
code={"line_1 = 1\nline_2 = 2\nline_3 = 3"}
|
|
107
|
+
showCopyCode={false}
|
|
108
|
+
onCreateEditor={onCreateEditor}
|
|
109
|
+
/>
|
|
110
|
+
</TooltipProvider>
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
const { rerender } = render(renderCode("light"));
|
|
114
|
+
await waitFor(() => {
|
|
115
|
+
expect(createdView).toBeDefined();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const view = createdView;
|
|
119
|
+
if (!view) {
|
|
120
|
+
throw new Error("ReadonlyCode did not create an editor view");
|
|
121
|
+
}
|
|
122
|
+
const identity = getEditorIdentity(view);
|
|
123
|
+
expect(view.state.facet(EditorView.darkTheme)).toBe(false);
|
|
124
|
+
|
|
125
|
+
themeState.value = "dark";
|
|
126
|
+
rerender(renderCode("dark"));
|
|
127
|
+
await waitFor(() => {
|
|
128
|
+
expect(view.state.facet(EditorView.darkTheme)).toBe(true);
|
|
129
|
+
});
|
|
130
|
+
expect(createdView).toBe(view);
|
|
131
|
+
expectEditorIdentity(view, identity);
|
|
132
|
+
|
|
133
|
+
themeState.value = "light";
|
|
134
|
+
rerender(renderCode("light-again"));
|
|
135
|
+
await waitFor(() => {
|
|
136
|
+
expect(view.state.facet(EditorView.darkTheme)).toBe(false);
|
|
137
|
+
});
|
|
138
|
+
expect(createdView).toBe(view);
|
|
139
|
+
expectEditorIdentity(view, identity);
|
|
140
|
+
});
|
|
141
|
+
|
|
30
142
|
it("starts collapsed when initiallyHideCode is true", () => {
|
|
31
143
|
const { container } = renderReadonly({ initiallyHideCode: true });
|
|
32
144
|
expect(isCollapsed(container)).toBe(true);
|
package/src/components/editor/connections/storage/__tests__/__snapshots__/as-code.test.ts.snap
CHANGED
|
@@ -154,6 +154,20 @@ _creds = json.loads(_credentials_json)
|
|
|
154
154
|
fs = GoogleDriveFileSystem(creds=_creds, token="service_account", use_listings_cache=False, skip_instance_cache=True)"
|
|
155
155
|
`;
|
|
156
156
|
|
|
157
|
+
exports[`generateStorageCode > Hugging Face > default connection 1`] = `
|
|
158
|
+
"from huggingface_hub import HfApi
|
|
159
|
+
|
|
160
|
+
hf = HfApi()"
|
|
161
|
+
`;
|
|
162
|
+
|
|
163
|
+
exports[`generateStorageCode > Hugging Face > with token from secrets 1`] = `
|
|
164
|
+
"from huggingface_hub import HfApi
|
|
165
|
+
import os
|
|
166
|
+
|
|
167
|
+
_token = os.environ.get("HF_TOKEN")
|
|
168
|
+
hf = HfApi(token=_token)"
|
|
169
|
+
`;
|
|
170
|
+
|
|
157
171
|
exports[`generateStorageCode > S3 > basic connection with all fields 1`] = `
|
|
158
172
|
"from obstore.store import S3Store
|
|
159
173
|
|