@marimo-team/islands 0.23.16-dev6 → 0.23.16-dev62
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-CJaUI-S-.js → ConnectedDataExplorerComponent-f9vLjQiF.js} +13 -13
- package/dist/{ErrorBoundary-DXlxsrTF.js → ErrorBoundary-DhLqwSWA.js} +3 -3
- package/dist/{any-language-editor-P9tjkj4n.js → any-language-editor-DifE-fl7.js} +5 -5
- package/dist/{button-BcSml68y.js → button-BSm8IXRU.js} +2 -4
- package/dist/{capabilities-BYLFYTYv.js → capabilities-C6OQ5YBO.js} +1 -1
- package/dist/{chat-ui-BdSDiVjk.js → chat-ui-DsZJj75A.js} +3117 -3120
- package/dist/{check-Cs-KV5XY.js → check-DP7U0H0G.js} +1 -1
- package/dist/{code-visibility-CfBftiVM.js → common-Bpnmu1Yz.js} +1085 -1053
- package/dist/{copy-Cs3yt6Vt.js → copy-DNq8q3zw.js} +2 -2
- package/dist/{dist-B96tMAst.js → dist-DIlxLFXG.js} +2 -2
- package/dist/{error-banner-DR7o3CbX.js → error-banner-B_GQfxxD.js} +2 -2
- package/dist/{esm-k8PIvuZW.js → esm-DzZVaNH4.js} +1 -1
- package/dist/{extends-Cq2JYHKy.js → extends-w2_S44bV.js} +4 -4
- package/dist/{formats-BFhkZJwd.js → formats-Dwq1THhr.js} +3 -3
- package/dist/{glide-data-editor--CaN-f27.js → glide-data-editor-B9K6-MJe.js} +9 -9
- package/dist/{html-to-image-DDENewl2.js → html-to-image-CZ1kLKkq.js} +7350 -6356
- package/dist/{input-DStQIuqD.js → input-C5K4VBvB.js} +7 -7
- package/dist/{label-DIk0QoFe.js → label-DjoYwqmj.js} +2 -2
- package/dist/{loader-CTHCI-qe.js → loader-DX-IDS7n.js} +1 -1
- package/dist/main.js +259 -194
- package/dist/{mermaid-CdSvixW2.js → mermaid-hv6IckVw.js} +5 -5
- package/dist/{node-sql-parser-DLl_zNw0.js → node-sql-parser-v1Vi-Fft.js} +16867 -16665
- package/dist/{process-output-2QPcgBQg.js → process-output-bVfbcx5_.js} +32 -28
- package/dist/{reveal-component-BclLkYnV.js → reveal-component-JIt3oSeU.js} +642 -634
- package/dist/{spec-CnaxwDqy.js → spec-CQKL_D3i.js} +4 -4
- package/dist/{strings-BYAhZnQi.js → strings-DIy9NAmg.js} +4 -4
- package/dist/style.css +1 -1
- package/dist/{swiper-component-DE94711u.js → swiper-component-CEdAYjb7.js} +2 -2
- package/dist/toDate-CqhBWjvO.js +713 -0
- package/dist/{tooltip-B-qh7UPB.js → tooltip-f-qR0iUx.js} +3 -3
- package/dist/{types-DQl7jVYK.js → types-DicH9Oi0.js} +1 -1
- package/dist/{useAsyncData-CvO8WUy3.js → useAsyncData-CYOQb1EW.js} +1 -1
- package/dist/{useDateFormatter-BSoGim-G.js → useDateFormatter-Bt2qr_XS.js} +2 -2
- package/dist/{useDeepCompareMemoize-J3MKRdA4.js → useDeepCompareMemoize-CSHQeQEI.js} +1 -1
- package/dist/{useIframeCapabilities-POcFXHbw.js → useIframeCapabilities-kA09ykNq.js} +1 -1
- package/dist/{useLifecycle-DqPxHgAN.js → useLifecycle-CMJGqBaY.js} +3 -3
- package/dist/{useTheme-XMq9mNf_.js → useTheme-DYfby65G.js} +2 -2
- package/dist/{vega-component-CrFoZT1S.js → vega-component-C7nO5zcf.js} +10 -10
- package/dist/{zod-D0yOqoGe.js → zod-BUcyXSdR.js} +1 -1
- 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/components/ui/input.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/keymaps/__tests__/keymaps.test.ts +31 -2
- package/src/core/codemirror/keymaps/keymaps.ts +12 -0
- 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/hotkeys/hotkeys.ts +0 -2
- 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/FormPlugin.tsx +1 -1
- package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +158 -0
- package/src/plugins/impl/__tests__/FormPlugin.test.tsx +162 -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-B3IRfHhw.js +0 -692
|
@@ -749,14 +749,8 @@ exports[`renderZodSchema > should render a form group_by 1`] = `
|
|
|
749
749
|
class="flex flex-col gap-1"
|
|
750
750
|
>
|
|
751
751
|
<div
|
|
752
|
-
class="flex flex-row
|
|
752
|
+
class="flex flex-row gap-2 items-center"
|
|
753
753
|
>
|
|
754
|
-
<label
|
|
755
|
-
class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
756
|
-
for="_r_1u_-form-item"
|
|
757
|
-
>
|
|
758
|
-
Drop N/A
|
|
759
|
-
</label>
|
|
760
754
|
<button
|
|
761
755
|
aria-checked="false"
|
|
762
756
|
aria-describedby="_r_1u_-form-item-description"
|
|
@@ -769,6 +763,16 @@ exports[`renderZodSchema > should render a form group_by 1`] = `
|
|
|
769
763
|
type="button"
|
|
770
764
|
value="on"
|
|
771
765
|
/>
|
|
766
|
+
<div
|
|
767
|
+
class="flex flex-col gap-1"
|
|
768
|
+
>
|
|
769
|
+
<label
|
|
770
|
+
class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
771
|
+
for="_r_1u_-form-item"
|
|
772
|
+
>
|
|
773
|
+
Drop N/A
|
|
774
|
+
</label>
|
|
775
|
+
</div>
|
|
772
776
|
</div>
|
|
773
777
|
</div>
|
|
774
778
|
</div>
|
|
@@ -1252,14 +1256,8 @@ exports[`renderZodSchema > should render a form sample_rows 1`] = `
|
|
|
1252
1256
|
class="flex flex-col gap-1"
|
|
1253
1257
|
>
|
|
1254
1258
|
<div
|
|
1255
|
-
class="flex flex-row
|
|
1259
|
+
class="flex flex-row gap-2 items-center"
|
|
1256
1260
|
>
|
|
1257
|
-
<label
|
|
1258
|
-
class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
1259
|
-
for="_r_2n_-form-item"
|
|
1260
|
-
>
|
|
1261
|
-
Sample with replacement
|
|
1262
|
-
</label>
|
|
1263
1261
|
<button
|
|
1264
1262
|
aria-checked="false"
|
|
1265
1263
|
aria-describedby="_r_2n_-form-item-description"
|
|
@@ -1272,6 +1270,16 @@ exports[`renderZodSchema > should render a form sample_rows 1`] = `
|
|
|
1272
1270
|
type="button"
|
|
1273
1271
|
value="on"
|
|
1274
1272
|
/>
|
|
1273
|
+
<div
|
|
1274
|
+
class="flex flex-col gap-1"
|
|
1275
|
+
>
|
|
1276
|
+
<label
|
|
1277
|
+
class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
1278
|
+
for="_r_2n_-form-item"
|
|
1279
|
+
>
|
|
1280
|
+
Sample with replacement
|
|
1281
|
+
</label>
|
|
1282
|
+
</div>
|
|
1275
1283
|
</div>
|
|
1276
1284
|
</div>
|
|
1277
1285
|
</div>
|
|
@@ -1468,14 +1476,8 @@ exports[`renderZodSchema > should render a form sort_column 1`] = `
|
|
|
1468
1476
|
class="flex flex-col gap-1"
|
|
1469
1477
|
>
|
|
1470
1478
|
<div
|
|
1471
|
-
class="flex flex-row
|
|
1479
|
+
class="flex flex-row gap-2 items-center"
|
|
1472
1480
|
>
|
|
1473
|
-
<label
|
|
1474
|
-
class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
1475
|
-
for="_r_1f_-form-item"
|
|
1476
|
-
>
|
|
1477
|
-
Ascending
|
|
1478
|
-
</label>
|
|
1479
1481
|
<button
|
|
1480
1482
|
aria-checked="false"
|
|
1481
1483
|
aria-describedby="_r_1f_-form-item-description"
|
|
@@ -1488,6 +1490,16 @@ exports[`renderZodSchema > should render a form sort_column 1`] = `
|
|
|
1488
1490
|
type="button"
|
|
1489
1491
|
value="on"
|
|
1490
1492
|
/>
|
|
1493
|
+
<div
|
|
1494
|
+
class="flex flex-col gap-1"
|
|
1495
|
+
>
|
|
1496
|
+
<label
|
|
1497
|
+
class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
1498
|
+
for="_r_1f_-form-item"
|
|
1499
|
+
>
|
|
1500
|
+
Ascending
|
|
1501
|
+
</label>
|
|
1502
|
+
</div>
|
|
1491
1503
|
</div>
|
|
1492
1504
|
</div>
|
|
1493
1505
|
</div>
|
|
@@ -411,9 +411,12 @@ const ColumnFilterForm = <T extends FieldValues>({
|
|
|
411
411
|
|
|
412
412
|
const columnRenderer = columnIdRenderer<T>();
|
|
413
413
|
const children = [
|
|
414
|
-
renderZodSchema(
|
|
415
|
-
|
|
416
|
-
|
|
414
|
+
renderZodSchema({
|
|
415
|
+
schema: columnIdSchema,
|
|
416
|
+
form,
|
|
417
|
+
path: `${path}.column_id` as Path<T>,
|
|
418
|
+
renderers: [columnRenderer],
|
|
419
|
+
}),
|
|
417
420
|
];
|
|
418
421
|
|
|
419
422
|
// When column ID changes, get the new dtype and reset the operator
|
|
@@ -486,12 +489,12 @@ const ColumnFilterForm = <T extends FieldValues>({
|
|
|
486
489
|
if (operandSchemas.length === 1) {
|
|
487
490
|
children.push(
|
|
488
491
|
<React.Fragment key="value">
|
|
489
|
-
{renderZodSchema(
|
|
490
|
-
operandSchemas[0],
|
|
492
|
+
{renderZodSchema({
|
|
493
|
+
schema: operandSchemas[0],
|
|
491
494
|
form,
|
|
492
|
-
`${path}.value` as Path<T>,
|
|
493
|
-
[],
|
|
494
|
-
)}
|
|
495
|
+
path: `${path}.value` as Path<T>,
|
|
496
|
+
renderers: [],
|
|
497
|
+
})}
|
|
495
498
|
</React.Fragment>,
|
|
496
499
|
);
|
|
497
500
|
}
|
|
@@ -23,37 +23,43 @@ export const Primary: Story = {
|
|
|
23
23
|
{
|
|
24
24
|
timestamp: Date.now(),
|
|
25
25
|
level: "stdout",
|
|
26
|
-
cellId: cellId("cell1"),
|
|
26
|
+
source: { type: "cell", cellId: cellId("cell1") },
|
|
27
27
|
message: "Hello world!",
|
|
28
28
|
},
|
|
29
29
|
{
|
|
30
30
|
timestamp: Date.now(),
|
|
31
31
|
level: "stdout",
|
|
32
|
-
cellId: cellId("cell1"),
|
|
32
|
+
source: { type: "cell", cellId: cellId("cell1") },
|
|
33
33
|
message: "Running cell...",
|
|
34
34
|
},
|
|
35
35
|
{
|
|
36
36
|
timestamp: Date.now(),
|
|
37
37
|
level: "stdout",
|
|
38
|
-
cellId: cellId("cell1"),
|
|
38
|
+
source: { type: "cell", cellId: cellId("cell1") },
|
|
39
39
|
message: "Done!",
|
|
40
40
|
},
|
|
41
41
|
{
|
|
42
42
|
timestamp: Date.now(),
|
|
43
43
|
level: "stderr",
|
|
44
|
-
cellId: cellId("cell2"),
|
|
44
|
+
source: { type: "cell", cellId: cellId("cell2") },
|
|
45
45
|
message: "Output is too large!",
|
|
46
46
|
},
|
|
47
47
|
{
|
|
48
48
|
timestamp: Date.now(),
|
|
49
49
|
level: "stderr",
|
|
50
|
-
cellId: cellId("cell2"),
|
|
50
|
+
source: { type: "cell", cellId: cellId("cell2") },
|
|
51
51
|
message: "String length is too short.".repeat(100),
|
|
52
52
|
},
|
|
53
|
+
{
|
|
54
|
+
timestamp: Date.now(),
|
|
55
|
+
level: "stderr",
|
|
56
|
+
source: { type: "lsp", name: "pylsp" },
|
|
57
|
+
message: "Failed to load plugin 'ruff': not installed",
|
|
58
|
+
},
|
|
53
59
|
...Array.from({ length: 100 }).map(() => ({
|
|
54
60
|
timestamp: Date.now(),
|
|
55
61
|
level: "stdout" as const,
|
|
56
|
-
cellId: cellId("cell1"),
|
|
62
|
+
source: { type: "cell", cellId: cellId("cell1") } as const,
|
|
57
63
|
message: "Running cell...",
|
|
58
64
|
})),
|
|
59
65
|
]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
import { beforeEach, describe, expect, test } from "vitest";
|
|
3
|
-
import { deserializeBlob, serializeBlob } from "../blob";
|
|
3
|
+
import { deserializeBlob, fileDetailsToBlob, serializeBlob } from "../blob";
|
|
4
4
|
import { blobToString } from "../fileToBase64";
|
|
5
5
|
|
|
6
6
|
describe("Blob serialization and deserialization", () => {
|
|
@@ -52,6 +52,32 @@ describe("Blob serialization and deserialization", () => {
|
|
|
52
52
|
});
|
|
53
53
|
});
|
|
54
54
|
|
|
55
|
+
describe("fileDetailsToBlob", () => {
|
|
56
|
+
test("decodes base64 contents into a binary blob", async () => {
|
|
57
|
+
const bytes = new Uint8Array([0, 1, 2, 3, 255]);
|
|
58
|
+
const base64 = btoa(String.fromCharCode(...bytes));
|
|
59
|
+
const blob = fileDetailsToBlob({
|
|
60
|
+
contents: base64,
|
|
61
|
+
mimeType: "image/png",
|
|
62
|
+
isBase64: true,
|
|
63
|
+
});
|
|
64
|
+
expect(blob.type).toBe("image/png");
|
|
65
|
+
const roundTripped = new Uint8Array(await blob.arrayBuffer());
|
|
66
|
+
expect(roundTripped).toEqual(bytes);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("treats non-base64 contents as UTF-8 text", async () => {
|
|
70
|
+
const svg = '<svg xmlns="http://www.w3.org/2000/svg"></svg>';
|
|
71
|
+
const blob = fileDetailsToBlob({
|
|
72
|
+
contents: svg,
|
|
73
|
+
mimeType: "image/svg+xml",
|
|
74
|
+
isBase64: false,
|
|
75
|
+
});
|
|
76
|
+
expect(blob.type).toBe("image/svg+xml");
|
|
77
|
+
expect(await blob.text()).toBe(svg);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
55
81
|
describe("blobToString", () => {
|
|
56
82
|
const testString = "Hello, world!";
|
|
57
83
|
const mimeType = "text/plain";
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
downloadAsPDF,
|
|
9
9
|
downloadByURL,
|
|
10
10
|
downloadCellOutputAsImage,
|
|
11
|
+
downloadExportedFile,
|
|
11
12
|
downloadHTMLAsImage,
|
|
12
13
|
getImageDataUrlForCell,
|
|
13
14
|
withLoadingToast,
|
|
@@ -195,7 +196,6 @@ describe("downloadAsPDF", () => {
|
|
|
195
196
|
|
|
196
197
|
await expect(
|
|
197
198
|
downloadAsPDF({
|
|
198
|
-
filename: "path/to/notebook.py",
|
|
199
199
|
webpdf: false,
|
|
200
200
|
preset: "slides",
|
|
201
201
|
}),
|
|
@@ -205,9 +205,26 @@ describe("downloadAsPDF", () => {
|
|
|
205
205
|
webpdf: false,
|
|
206
206
|
preset: "slides",
|
|
207
207
|
includeInputs: true,
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
208
|
+
includeOutputs: true,
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it("should send input and output selections", async () => {
|
|
213
|
+
mockExportAsPDF.mockRejectedValue(new Error("network"));
|
|
214
|
+
|
|
215
|
+
await expect(
|
|
216
|
+
downloadAsPDF({
|
|
217
|
+
webpdf: true,
|
|
218
|
+
includeInputs: false,
|
|
219
|
+
includeOutputs: false,
|
|
220
|
+
}),
|
|
221
|
+
).rejects.toThrow("network");
|
|
222
|
+
|
|
223
|
+
expect(mockExportAsPDF).toHaveBeenCalledWith({
|
|
224
|
+
webpdf: true,
|
|
225
|
+
preset: "document",
|
|
226
|
+
includeInputs: false,
|
|
227
|
+
includeOutputs: false,
|
|
211
228
|
});
|
|
212
229
|
});
|
|
213
230
|
});
|
|
@@ -572,6 +589,7 @@ describe("downloadByURL", () => {
|
|
|
572
589
|
|
|
573
590
|
afterEach(() => {
|
|
574
591
|
vi.restoreAllMocks();
|
|
592
|
+
vi.unstubAllGlobals();
|
|
575
593
|
});
|
|
576
594
|
|
|
577
595
|
it("should create anchor, set attributes, click, and remove", () => {
|
|
@@ -583,4 +601,26 @@ describe("downloadByURL", () => {
|
|
|
583
601
|
expect(mockAnchor.click).toHaveBeenCalled();
|
|
584
602
|
expect(mockAnchor.remove).toHaveBeenCalled();
|
|
585
603
|
});
|
|
604
|
+
|
|
605
|
+
it("downloads an exported file with its returned metadata", async () => {
|
|
606
|
+
let downloadedBlob: Blob | undefined;
|
|
607
|
+
vi.stubGlobal("URL", {
|
|
608
|
+
createObjectURL: vi.fn((blob: Blob) => {
|
|
609
|
+
downloadedBlob = blob;
|
|
610
|
+
return "blob:export";
|
|
611
|
+
}),
|
|
612
|
+
revokeObjectURL: vi.fn(),
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
downloadExportedFile({
|
|
616
|
+
contents: "notebook",
|
|
617
|
+
filename: "résumé.qmd",
|
|
618
|
+
mediaType: "text/plain; charset=utf-8",
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
expect(downloadedBlob?.type).toBe("text/plain; charset=utf-8");
|
|
622
|
+
expect(await downloadedBlob?.text()).toBe("notebook");
|
|
623
|
+
expect(mockAnchor.download).toBe("résumé.qmd");
|
|
624
|
+
expect(URL.revokeObjectURL).toHaveBeenCalledWith("blob:export");
|
|
625
|
+
});
|
|
586
626
|
});
|
package/src/utils/blob.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
-
import
|
|
2
|
+
import {
|
|
3
|
+
type Base64String,
|
|
4
|
+
base64ToDataURL,
|
|
5
|
+
type DataURLString,
|
|
6
|
+
} from "./json/base64";
|
|
3
7
|
|
|
4
8
|
export function serializeBlob<T>(blob: Blob): Promise<DataURLString> {
|
|
5
9
|
return new Promise<DataURLString>((resolve, reject) => {
|
|
@@ -30,3 +34,22 @@ export function deserializeBlob(serializedBlob: DataURLString): Blob {
|
|
|
30
34
|
const blob = new Blob([bytes], { type: mimeType });
|
|
31
35
|
return blob;
|
|
32
36
|
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Convert file details into a `Blob`, using the authoritative `isBase64` flag
|
|
40
|
+
* to decide between decoding base64 bytes and treating contents as UTF-8 text.
|
|
41
|
+
*/
|
|
42
|
+
export function fileDetailsToBlob({
|
|
43
|
+
contents,
|
|
44
|
+
mimeType,
|
|
45
|
+
isBase64,
|
|
46
|
+
}: {
|
|
47
|
+
contents: string;
|
|
48
|
+
mimeType: string;
|
|
49
|
+
isBase64: boolean;
|
|
50
|
+
}): Blob {
|
|
51
|
+
if (isBase64) {
|
|
52
|
+
return deserializeBlob(base64ToDataURL(contents as Base64String, mimeType));
|
|
53
|
+
}
|
|
54
|
+
return new Blob([contents], { type: mimeType });
|
|
55
|
+
}
|
package/src/utils/download.ts
CHANGED
|
@@ -4,8 +4,8 @@ import React from "react";
|
|
|
4
4
|
import { toast } from "@/components/ui/use-toast";
|
|
5
5
|
import { type CellId, CellOutputId } from "@/core/cells/ids";
|
|
6
6
|
import { getRequestClient } from "@/core/network/requests";
|
|
7
|
+
import type { ExportedFile } from "@/core/network/types";
|
|
7
8
|
import { Filenames } from "@/utils/filenames";
|
|
8
|
-
import { Paths } from "@/utils/paths";
|
|
9
9
|
import { prettyError } from "./errors";
|
|
10
10
|
import { toPng } from "./html-to-image";
|
|
11
11
|
import { captureExternalIframes } from "./iframe";
|
|
@@ -191,6 +191,14 @@ export function downloadBlob(blob: Blob, filename: string) {
|
|
|
191
191
|
URL.revokeObjectURL(url);
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
+
export function downloadExportedFile({
|
|
195
|
+
contents,
|
|
196
|
+
filename,
|
|
197
|
+
mediaType,
|
|
198
|
+
}: ExportedFile) {
|
|
199
|
+
downloadBlob(new Blob([contents], { type: mediaType }), filename);
|
|
200
|
+
}
|
|
201
|
+
|
|
194
202
|
export type PDFExportPreset = "document" | "slides";
|
|
195
203
|
|
|
196
204
|
/**
|
|
@@ -200,37 +208,28 @@ export type PDFExportPreset = "document" | "slides";
|
|
|
200
208
|
* Standard PDF requires Pandoc & TeX (~few GBs) but is of higher quality.
|
|
201
209
|
*/
|
|
202
210
|
export async function downloadAsPDF(opts: {
|
|
203
|
-
filename: string;
|
|
204
211
|
webpdf: boolean;
|
|
205
212
|
preset?: PDFExportPreset;
|
|
206
213
|
includeInputs?: boolean;
|
|
207
|
-
|
|
208
|
-
rasterScale?: number;
|
|
209
|
-
rasterServer?: "static" | "live";
|
|
214
|
+
includeOutputs?: boolean;
|
|
210
215
|
}) {
|
|
211
216
|
const client = getRequestClient();
|
|
212
217
|
const {
|
|
213
|
-
filename,
|
|
214
218
|
webpdf,
|
|
215
219
|
preset = "document",
|
|
216
220
|
includeInputs = true,
|
|
217
|
-
|
|
218
|
-
rasterScale = 4,
|
|
219
|
-
rasterServer = "static",
|
|
221
|
+
includeOutputs = true,
|
|
220
222
|
} = opts;
|
|
221
223
|
|
|
222
224
|
try {
|
|
223
|
-
const
|
|
225
|
+
const exportedFile = await client.exportAsPDF({
|
|
224
226
|
webpdf,
|
|
225
227
|
preset,
|
|
226
228
|
includeInputs,
|
|
227
|
-
|
|
228
|
-
rasterScale,
|
|
229
|
-
rasterServer,
|
|
229
|
+
includeOutputs,
|
|
230
230
|
});
|
|
231
231
|
|
|
232
|
-
|
|
233
|
-
downloadBlob(pdfBlob, Filenames.toPDF(filenameWithoutPath));
|
|
232
|
+
downloadExportedFile(exportedFile);
|
|
234
233
|
} catch (error) {
|
|
235
234
|
toast({
|
|
236
235
|
title: "Failed to download",
|