@marimo-team/islands 0.23.17-dev5 → 0.23.17-dev50
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-uMQbwoo-.js → common-CEVN8ZqA.js} +787 -794
- 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-CtEDksOP.js → reveal-component-BCp1vgBy.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 +7 -7
- package/src/__mocks__/requests.ts +5 -0
- package/src/components/ai/ai-model-dropdown.tsx +2 -2
- package/src/components/app-config/user-config-form.tsx +26 -0
- package/src/components/chat/__tests__/capabilities-popover.test.tsx +38 -0
- package/src/components/chat/__tests__/chat-abort.test.ts +51 -0
- package/src/components/chat/__tests__/use-chat-controller-id.test.tsx +39 -0
- package/src/components/chat/capabilities-popover.tsx +68 -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 +107 -50
- package/src/components/chat/chat-utils.ts +1 -1
- package/src/components/chat/use-chat-controller-id.ts +24 -0
- 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/chrome/wrapper/useOpenAiAssistant.ts +2 -2
- 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/__tests__/config.test.tsx +96 -0
- package/src/core/ai/config.ts +16 -22
- package/src/core/ai/constants.ts +7 -0
- package/src/core/ai/staged-cells.ts +3 -0
- package/src/core/ai/state.ts +10 -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 +4 -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
|
@@ -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
|
+
});
|
|
@@ -4,7 +4,7 @@ import { useSetAtom, useStore } from "jotai";
|
|
|
4
4
|
import useEvent from "react-use-event-hook";
|
|
5
5
|
import { agentSessionStateAtom } from "@/components/chat/acp/state";
|
|
6
6
|
import { toast } from "@/components/ui/use-toast";
|
|
7
|
-
import {
|
|
7
|
+
import { useAIConfigActions } from "@/core/ai/config";
|
|
8
8
|
import { pendingAiPromptAtom } from "@/core/ai/state";
|
|
9
9
|
import type { CopilotMode } from "@/core/ai/tools/registry";
|
|
10
10
|
import { aiModelConfiguredAtom } from "@/core/config/config";
|
|
@@ -43,7 +43,7 @@ export function resolveAiPanelTab(
|
|
|
43
43
|
export function useOpenAiAssistant() {
|
|
44
44
|
const { openApplication } = useChromeActions();
|
|
45
45
|
const { setAiPanelTab } = useAiPanelTab();
|
|
46
|
-
const { saveModeChange } =
|
|
46
|
+
const { saveModeChange } = useAIConfigActions();
|
|
47
47
|
const setPendingPrompt = useSetAtom(pendingAiPromptAtom);
|
|
48
48
|
const store = useStore();
|
|
49
49
|
|
|
@@ -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
|
|
|
@@ -309,6 +309,26 @@ describe("generateStorageCode", () => {
|
|
|
309
309
|
});
|
|
310
310
|
});
|
|
311
311
|
|
|
312
|
+
describe("Hugging Face", () => {
|
|
313
|
+
it("default connection", () => {
|
|
314
|
+
expect(
|
|
315
|
+
generateStorageCode({ type: "huggingface" }, { library: "fsspec" }),
|
|
316
|
+
).toMatchSnapshot();
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it("with token from secrets", () => {
|
|
320
|
+
expect(
|
|
321
|
+
generateStorageCode(
|
|
322
|
+
{
|
|
323
|
+
type: "huggingface",
|
|
324
|
+
token: prefixSecret("HF_TOKEN"),
|
|
325
|
+
},
|
|
326
|
+
{ library: "fsspec" },
|
|
327
|
+
),
|
|
328
|
+
).toMatchSnapshot();
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
|
|
312
332
|
describe("invalid cases", () => {
|
|
313
333
|
it("throws for empty S3 bucket", () => {
|
|
314
334
|
expect(() =>
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
CoreWeaveStorageSchema,
|
|
18
18
|
GCSStorageSchema,
|
|
19
19
|
GoogleDriveStorageSchema,
|
|
20
|
+
HuggingfaceStorageSchema,
|
|
20
21
|
S3StorageSchema,
|
|
21
22
|
type StorageConnection,
|
|
22
23
|
} from "./schemas";
|
|
@@ -79,6 +80,15 @@ const STORAGE_PROVIDERS = [
|
|
|
79
80
|
preferred: "fsspec",
|
|
80
81
|
},
|
|
81
82
|
},
|
|
83
|
+
{
|
|
84
|
+
name: "Hugging Face Hub",
|
|
85
|
+
schema: HuggingfaceStorageSchema,
|
|
86
|
+
protocol: "hf",
|
|
87
|
+
storageLibraries: {
|
|
88
|
+
libraries: ["huggingface_hub"],
|
|
89
|
+
preferred: "huggingface_hub",
|
|
90
|
+
},
|
|
91
|
+
},
|
|
82
92
|
] satisfies StorageProviderSchema[];
|
|
83
93
|
|
|
84
94
|
const StorageProviderSelector: React.FC<{
|