@marimo-team/frontend 0.19.7-dev24 → 0.19.7-dev26
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/assets/{CellStatus-DFhwmbo5.js → CellStatus-oBL2iale.js} +1 -1
- package/dist/assets/{ConnectedDataExplorerComponent-Cosas-Z0.js → ConnectedDataExplorerComponent-ec_bSRbX.js} +1 -1
- package/dist/assets/{ErrorBoundary-BU1OKJ3L.js → ErrorBoundary-ChCiwl15.js} +1 -1
- package/dist/assets/{ImperativeModal-DstvzsTs.js → ImperativeModal-CUbWEBci.js} +1 -1
- package/dist/assets/{JsonOutput-Dmfgex9T.js → JsonOutput-BKP4rBIw.js} +3 -3
- package/dist/assets/{LazyAnyLanguageCodeMirror-ygeIsKeo.js → LazyAnyLanguageCodeMirror-yzHjsVJt.js} +2 -2
- package/dist/assets/{MarimoErrorOutput-eoDwRuKU.js → MarimoErrorOutput-cw9gEb4T.js} +1 -1
- package/dist/assets/{RenderHTML-DuJkj1GV.js → RenderHTML-Bgz4e362.js} +1 -1
- package/dist/assets/{add-cell-with-ai-COxBrLrH.js → add-cell-with-ai-DNbX7Ctg.js} +1 -1
- package/dist/assets/{add-database-form-BtP14Anj.js → add-database-form-CP39qGit.js} +1 -1
- package/dist/assets/{agent-panel-Baphp2Rh.js → agent-panel-Bak-DtIc.js} +1 -1
- package/dist/assets/{ai-model-dropdown-GvB-5iLY.js → ai-model-dropdown-Dpr0DUJN.js} +1 -1
- package/dist/assets/{alert-dialog-Bv1sALHm.js → alert-dialog-DwQffb13.js} +1 -1
- package/dist/assets/{any-language-editor-WGtuXPVf.js → any-language-editor-BMlwpEZ4.js} +1 -1
- package/dist/assets/{app-config-button-BK2508Jf.js → app-config-button-CAaYaq0L.js} +1 -1
- package/dist/assets/{button-B3uq-Cpf.js → button-YC1gW_kJ.js} +1 -1
- package/dist/assets/{cache-panel-DFXoPp-r.js → cache-panel-BHX9f5Bx.js} +1 -1
- package/dist/assets/{capabilities-CUpom6r4.js → capabilities-MM7JYRxj.js} +1 -1
- package/dist/assets/{capitalize-CXGTjPyK.js → capitalize-CkclHYWI.js} +1 -1
- package/dist/assets/{cell-editor-p88PqmUA.js → cell-editor-B6jD1bv8.js} +1 -1
- package/dist/assets/{cell-link-BITMERKQ.js → cell-link-CyIWDVXR.js} +1 -1
- package/dist/assets/{cells-Cvo5AIrZ.js → cells-DRiwSVs0.js} +1 -1
- package/dist/assets/{chat-components-BV83l1rZ.js → chat-components-DDIZD9FU.js} +1 -1
- package/dist/assets/{chat-display-C4qhRyWq.js → chat-display-D-Wa-1Hv.js} +1 -1
- package/dist/assets/{chat-panel-BxxPae_R.js → chat-panel-RtFQiyUV.js} +1 -1
- package/dist/assets/{column-preview-BvwEAGt6.js → column-preview-C72fVZoP.js} +1 -1
- package/dist/assets/{command-BjWSp3sa.js → command-Bq3e85NA.js} +1 -1
- package/dist/assets/{command-palette-aT5upvTH.js → command-palette-D4NcN7PV.js} +1 -1
- package/dist/assets/{common-BLddv5HY.js → common-CeF2TOUZ.js} +1 -1
- package/dist/assets/{config-BgpK7vqH.js → config-CIrPQIbt.js} +1 -1
- package/dist/assets/{copy-r7i0SKI4.js → copy-Bv2DBpIS.js} +1 -1
- package/dist/assets/{copy-icon-wZr2McVB.js → copy-icon-BhONVREY.js} +1 -1
- package/dist/assets/{createReducer-Cki97cx5.js → createReducer-Dnna-AUO.js} +1 -1
- package/dist/assets/{datasource-D2hNaG_n.js → datasource-AZ3l2P48.js} +1 -1
- package/dist/assets/{dates-CxJmszXT.js → dates-Dhn1r-h6.js} +1 -1
- package/dist/assets/{dependency-graph-panel-CBiKFUBG.js → dependency-graph-panel-i3yTswTN.js} +1 -1
- package/dist/assets/{dialog-C5Pa_iIq.js → dialog-CxGKN4C_.js} +1 -1
- package/dist/assets/{dist-R0oOvu5B.js → dist-CdxIjAOP.js} +1 -1
- package/dist/assets/{documentation-panel-ChkvmAB1.js → documentation-panel-J2fwLjEP.js} +1 -1
- package/dist/assets/download-Y3BpaOoI.js +6 -0
- package/dist/assets/{edit-page-BiUBRcLx.js → edit-page-Bpl6BN5G.js} +3 -3
- package/dist/assets/{error-banner-BWJsOpnc.js → error-banner-DUzsIXtq.js} +1 -1
- package/dist/assets/{error-panel-Bnhv5zxn.js → error-panel-Cpfr8TZw.js} +1 -1
- package/dist/assets/{field-BN2j4cag.js → field-BEg1eC0P.js} +1 -1
- package/dist/assets/{file-explorer-panel-CkOMaMbq.js → file-explorer-panel-DsMwvQX7.js} +1 -1
- package/dist/assets/{floating-outline-CxfziveS.js → floating-outline-yvPiOGQ2.js} +1 -1
- package/dist/assets/{focus-BeWVOW9Q.js → focus-B7xu7kpl.js} +1 -1
- package/dist/assets/{form-DazVYGCT.js → form-BpwI8bBX.js} +1 -1
- package/dist/assets/{formats-BBDL2N4i.js → formats-W1SWxSE3.js} +1 -1
- package/dist/assets/{glide-data-editor-DEwgx2xp.js → glide-data-editor-DVw6MlCk.js} +1 -1
- package/dist/assets/{globals-uEPg-4pq.js → globals-BW23ny3Q.js} +1 -1
- package/dist/assets/{home-page-Dtcd7Trh.js → home-page-C-JIKt-2.js} +1 -1
- package/dist/assets/hooks-2c2seyHG.js +1 -0
- package/dist/assets/hotkeys-BHHWjLlp.js +1 -0
- package/dist/assets/{html-to-image-CJgqxZci.js → html-to-image-Cy_LYuWW.js} +1 -1
- package/dist/assets/{index-CSj0b_Pr.js → index-C4si8YAb.js} +21 -26
- package/dist/assets/index-DHsXOI_M.css +2 -0
- package/dist/assets/{input-BeGfEf2S.js → input-pAun1m1X.js} +1 -1
- package/dist/assets/{kiosk-mode-Djm3JPwk.js → kiosk-mode-D9jdUD5P.js} +1 -1
- package/dist/assets/{label-C_OuzPjQ.js → label-Be1daUcS.js} +1 -1
- package/dist/assets/{layout-BkGjQBnQ.js → layout-BjIcxFO0.js} +4 -4
- package/dist/assets/links-C-GGaW8R.js +1 -0
- package/dist/assets/{logs-panel-BsnCP3we.js → logs-panel-XVSOzGFv.js} +1 -1
- package/dist/assets/{maps-vcWR7nnr.js → maps-t9yNKYA8.js} +1 -1
- package/dist/assets/{markdown-renderer-CoQm4UxN.js → markdown-renderer-BM4a9QeZ.js} +1 -1
- package/dist/assets/{mermaid-R2vBM-JH.js → mermaid-BG5ill_a.js} +1 -1
- package/dist/assets/{mode-DjraKyN2.js → mode-BD6zDBBd.js} +1 -1
- package/dist/assets/{multi-map-Cwq--tzY.js → multi-map-C8GlnP-4.js} +1 -1
- package/dist/assets/{name-cell-input-CHmzPoeN.js → name-cell-input-CGevX71g.js} +1 -1
- package/dist/assets/{numbers-CSY3JIgn.js → numbers-iQunIAXf.js} +1 -1
- package/dist/assets/{outline-panel-DMECjI9i.js → outline-panel-BgZXYbEO.js} +1 -1
- package/dist/assets/{packages-panel-CbXV6Rc8.js → packages-panel-8NDUaEZw.js} +1 -1
- package/dist/assets/{panels-B55q0DEo.js → panels-05G4QYfq.js} +1 -1
- package/dist/assets/{process-output-CwcoTocd.js → process-output-nNOt7QtH.js} +1 -1
- package/dist/assets/{readonly-python-code-BlVsu50E.js → readonly-python-code-CfLdThzF.js} +1 -1
- package/dist/assets/{renderShortcut-Dyrbz79Y.js → renderShortcut-DHc-p-_c.js} +1 -1
- package/dist/assets/{run-page-6DKviU71.js → run-page-DvWw2rQu.js} +1 -1
- package/dist/assets/{runs-CAvk6jVz.js → runs-bjsj1D88.js} +1 -1
- package/dist/assets/{scratchpad-panel-c7i4YZ8j.js → scratchpad-panel-Bzreyj8q.js} +1 -1
- package/dist/assets/{secrets-panel-XDvrD2PC.js → secrets-panel-Br6CcsOE.js} +1 -1
- package/dist/assets/{select-UFziUNxL.js → select-V5IdpNiR.js} +1 -1
- package/dist/assets/{session-panel-CRStSDDj.js → session-panel-CxCUFR_x.js} +1 -1
- package/dist/assets/{share-CKfNi8fD.js → share-CbPtIlnM.js} +1 -1
- package/dist/assets/{slides-component-0GonPC6Y.js → slides-component-DP2pxhDh.js} +1 -1
- package/dist/assets/{snippets-panel-Db-biIgP.js → snippets-panel-BeqbqiKB.js} +1 -1
- package/dist/assets/{spec-DJ3YTCel.js → spec-BSxN05D8.js} +1 -1
- package/dist/assets/{state-D-CqcbQE.js → state-D7LZMIOW.js} +1 -1
- package/dist/assets/{state-CS48Wh7M.js → state-DnUQ1uxR.js} +1 -1
- package/dist/assets/{switch-Di5kBaS8.js → switch-Cx8dJhf6.js} +1 -1
- package/dist/assets/{terminal-nu6YfkVm.js → terminal-DNwT6UrR.js} +1 -1
- package/dist/assets/{textarea-hNyWE2r_.js → textarea-DellDgP4.js} +1 -1
- package/dist/assets/{tooltip-CE4l3v3B.js → tooltip-BGrCWNss.js} +1 -1
- package/dist/assets/{tracing-CDNRUjb9.js → tracing-7U4WTsN0.js} +1 -1
- package/dist/assets/{tracing-panel-CHVKa-9o.js → tracing-panel-BPW1q7K3.js} +2 -2
- package/dist/assets/{type-D8l_U05h.js → type-DUK-1jKc.js} +1 -1
- package/dist/assets/{types-DWpF5HiT.js → types-D5X7ikSD.js} +1 -1
- package/dist/assets/{useAddCell-CklpKCq2.js → useAddCell-bMoxoWAg.js} +1 -1
- package/dist/assets/{useBoolean-BE72e3yb.js → useBoolean-B1Xeh6vA.js} +1 -1
- package/dist/assets/{useCellActionButton-CQQ8dBy_.js → useCellActionButton-0MDUWMcl.js} +1 -1
- package/dist/assets/{useDeleteCell-BRWEDSc9.js → useDeleteCell-BUAQb9OH.js} +1 -1
- package/dist/assets/{useDependencyPanelTab-_ibASMD1.js → useDependencyPanelTab-odtlfdG2.js} +1 -1
- package/dist/assets/{useIframeCapabilities-BVYqWHmA.js → useIframeCapabilities-DuIDx9mD.js} +1 -1
- package/dist/assets/{useInstallPackage-Dxl_p6oW.js → useInstallPackage-Bdnnp5fe.js} +1 -1
- package/dist/assets/{useLifecycle-DvpL8DUJ.js → useLifecycle-ChNbzbYY.js} +1 -1
- package/dist/assets/useNotebookActions-CNWP5yqL.js +1 -0
- package/dist/assets/{useRunCells-jbEa8WGV.js → useRunCells-DyBzMbHe.js} +1 -1
- package/dist/assets/{useSplitCell-CdjW9REr.js → useSplitCell-CyFavQ2l.js} +1 -1
- package/dist/assets/{useTheme-CuyH5VNX.js → useTheme-DUdVAZI8.js} +1 -1
- package/dist/assets/{utilities.esm-eBoXu7lR.js → utilities.esm-j_F9mYkM.js} +1 -1
- package/dist/assets/{utils-CSDCHxwI.js → utils-DXvhzCGS.js} +1 -1
- package/dist/assets/{vega-component-B3_9VVhH.js → vega-component-Dd3MCYZO.js} +1 -1
- package/dist/assets/{write-secret-modal-DfRIeQB5.js → write-secret-modal-CpmU5gbF.js} +1 -1
- package/dist/index.html +67 -67
- package/package.json +1 -1
- package/src/components/editor/actions/useNotebookActions.tsx +5 -3
- package/src/components/ui/progress.tsx +22 -5
- package/src/core/export/__tests__/hooks.test.ts +42 -19
- package/src/core/export/hooks.ts +33 -32
- package/src/utils/__tests__/download.test.tsx +6 -4
- package/src/utils/__tests__/objects.test.ts +263 -0
- package/src/utils/__tests__/progress.test.ts +156 -0
- package/src/utils/download.ts +7 -2
- package/src/utils/objects.ts +3 -0
- package/src/utils/progress.ts +61 -0
- package/src/utils/toast-progress.tsx +41 -0
- package/dist/assets/download-kUMZIq8-.js +0 -1
- package/dist/assets/hooks-DxXRX-38.js +0 -1
- package/dist/assets/hotkeys-DghjL7BQ.js +0 -1
- package/dist/assets/index-CodxHczV.css +0 -2
- package/dist/assets/links-CUKo4afc.js +0 -1
- package/dist/assets/useNotebookActions-REoVp8xc.js +0 -1
|
@@ -57,10 +57,12 @@ describe("withLoadingToast", () => {
|
|
|
57
57
|
});
|
|
58
58
|
|
|
59
59
|
expect(toast).toHaveBeenCalledTimes(1);
|
|
60
|
-
expect(toast).toHaveBeenCalledWith(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
expect(toast).toHaveBeenCalledWith(
|
|
61
|
+
expect.objectContaining({
|
|
62
|
+
title: "Loading...",
|
|
63
|
+
duration: Infinity,
|
|
64
|
+
}),
|
|
65
|
+
);
|
|
64
66
|
expect(mockDismiss).toHaveBeenCalledTimes(1);
|
|
65
67
|
expect(result).toBe("success");
|
|
66
68
|
});
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { Objects } from "../objects";
|
|
4
|
+
|
|
5
|
+
describe("Objects", () => {
|
|
6
|
+
describe("EMPTY", () => {
|
|
7
|
+
it("should be an empty frozen object", () => {
|
|
8
|
+
expect(Objects.EMPTY).toEqual({});
|
|
9
|
+
expect(Object.isFrozen(Objects.EMPTY)).toBe(true);
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
describe("mapValues", () => {
|
|
14
|
+
it("should map values of an object", () => {
|
|
15
|
+
const obj = { a: 1, b: 2, c: 3 };
|
|
16
|
+
const result = Objects.mapValues(obj, (v) => v * 2);
|
|
17
|
+
expect(result).toEqual({ a: 2, b: 4, c: 6 });
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("should pass key as second argument", () => {
|
|
21
|
+
const obj = { a: 1, b: 2 };
|
|
22
|
+
const result = Objects.mapValues(obj, (v, k) => `${k}:${v}`);
|
|
23
|
+
expect(result).toEqual({ a: "a:1", b: "b:2" });
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("should handle empty objects", () => {
|
|
27
|
+
const result = Objects.mapValues({}, (v) => v);
|
|
28
|
+
expect(result).toEqual({});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("should return falsy input unchanged", () => {
|
|
32
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
33
|
+
expect(Objects.mapValues(null as any, (v) => v)).toBe(null);
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
35
|
+
expect(Objects.mapValues(undefined as any, (v) => v)).toBe(undefined);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe("fromEntries", () => {
|
|
40
|
+
it("should create object from entries", () => {
|
|
41
|
+
const entries: [string, number][] = [
|
|
42
|
+
["a", 1],
|
|
43
|
+
["b", 2],
|
|
44
|
+
];
|
|
45
|
+
expect(Objects.fromEntries(entries)).toEqual({ a: 1, b: 2 });
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("should handle empty entries", () => {
|
|
49
|
+
expect(Objects.fromEntries([])).toEqual({});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("should handle numeric keys", () => {
|
|
53
|
+
const entries: [number, string][] = [
|
|
54
|
+
[1, "a"],
|
|
55
|
+
[2, "b"],
|
|
56
|
+
];
|
|
57
|
+
expect(Objects.fromEntries(entries)).toEqual({ 1: "a", 2: "b" });
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe("entries", () => {
|
|
62
|
+
it("should return entries of an object", () => {
|
|
63
|
+
const obj = { a: 1, b: 2 };
|
|
64
|
+
const entries = Objects.entries(obj);
|
|
65
|
+
expect(entries).toContainEqual(["a", 1]);
|
|
66
|
+
expect(entries).toContainEqual(["b", 2]);
|
|
67
|
+
expect(entries).toHaveLength(2);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("should handle empty objects", () => {
|
|
71
|
+
expect(Objects.entries({})).toEqual([]);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe("keys", () => {
|
|
76
|
+
it("should return keys of an object", () => {
|
|
77
|
+
const obj = { a: 1, b: 2, c: 3 };
|
|
78
|
+
const keys = Objects.keys(obj);
|
|
79
|
+
expect(keys).toContain("a");
|
|
80
|
+
expect(keys).toContain("b");
|
|
81
|
+
expect(keys).toContain("c");
|
|
82
|
+
expect(keys).toHaveLength(3);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("should handle empty objects", () => {
|
|
86
|
+
expect(Objects.keys({})).toEqual([]);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe("size", () => {
|
|
91
|
+
it("should return the number of keys", () => {
|
|
92
|
+
expect(Objects.size({ a: 1, b: 2, c: 3 })).toBe(3);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("should return 0 for empty objects", () => {
|
|
96
|
+
expect(Objects.size({})).toBe(0);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe("keyBy", () => {
|
|
101
|
+
it("should key items by specified key function", () => {
|
|
102
|
+
const items = [
|
|
103
|
+
{ id: "a", value: 1 },
|
|
104
|
+
{ id: "b", value: 2 },
|
|
105
|
+
];
|
|
106
|
+
const result = Objects.keyBy(items, (item) => item.id);
|
|
107
|
+
expect(result).toEqual({
|
|
108
|
+
a: { id: "a", value: 1 },
|
|
109
|
+
b: { id: "b", value: 2 },
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("should skip items with undefined keys", () => {
|
|
114
|
+
const items = [
|
|
115
|
+
{ id: "a", value: 1 },
|
|
116
|
+
{ id: undefined as unknown as string, value: 2 },
|
|
117
|
+
{ id: "c", value: 3 },
|
|
118
|
+
];
|
|
119
|
+
const result = Objects.keyBy(items, (item) => item.id);
|
|
120
|
+
expect(result).toEqual({
|
|
121
|
+
a: { id: "a", value: 1 },
|
|
122
|
+
c: { id: "c", value: 3 },
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("should handle empty arrays", () => {
|
|
127
|
+
expect(Objects.keyBy([], (item) => item)).toEqual({});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it("should use last item when keys collide", () => {
|
|
131
|
+
const items = [
|
|
132
|
+
{ id: "a", value: 1 },
|
|
133
|
+
{ id: "a", value: 2 },
|
|
134
|
+
];
|
|
135
|
+
const result = Objects.keyBy(items, (item) => item.id);
|
|
136
|
+
expect(result).toEqual({ a: { id: "a", value: 2 } });
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
describe("collect", () => {
|
|
141
|
+
it("should collect and transform items", () => {
|
|
142
|
+
const items = [
|
|
143
|
+
{ id: "a", value: 1 },
|
|
144
|
+
{ id: "b", value: 2 },
|
|
145
|
+
];
|
|
146
|
+
const result = Objects.collect(
|
|
147
|
+
items,
|
|
148
|
+
(item) => item.id,
|
|
149
|
+
(item) => item.value * 2,
|
|
150
|
+
);
|
|
151
|
+
expect(result).toEqual({ a: 2, b: 4 });
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it("should handle empty arrays", () => {
|
|
155
|
+
const result = Objects.collect(
|
|
156
|
+
[],
|
|
157
|
+
(item) => item,
|
|
158
|
+
(item) => item,
|
|
159
|
+
);
|
|
160
|
+
expect(result).toEqual({});
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
describe("groupBy", () => {
|
|
165
|
+
it("should group items by key", () => {
|
|
166
|
+
const items = [
|
|
167
|
+
{ category: "a", value: 1 },
|
|
168
|
+
{ category: "b", value: 2 },
|
|
169
|
+
{ category: "a", value: 3 },
|
|
170
|
+
];
|
|
171
|
+
const result = Objects.groupBy(
|
|
172
|
+
items,
|
|
173
|
+
(item) => item.category,
|
|
174
|
+
(item) => item.value,
|
|
175
|
+
);
|
|
176
|
+
expect(result).toEqual({
|
|
177
|
+
a: [1, 3],
|
|
178
|
+
b: [2],
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("should skip items with undefined keys", () => {
|
|
183
|
+
const items = [
|
|
184
|
+
{ category: "a", value: 1 },
|
|
185
|
+
{ category: undefined as unknown as string, value: 2 },
|
|
186
|
+
{ category: "a", value: 3 },
|
|
187
|
+
];
|
|
188
|
+
const result = Objects.groupBy(
|
|
189
|
+
items,
|
|
190
|
+
(item) => item.category,
|
|
191
|
+
(item) => item.value,
|
|
192
|
+
);
|
|
193
|
+
expect(result).toEqual({ a: [1, 3] });
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it("should handle empty arrays", () => {
|
|
197
|
+
const result = Objects.groupBy(
|
|
198
|
+
[],
|
|
199
|
+
(item) => item,
|
|
200
|
+
(item) => item,
|
|
201
|
+
);
|
|
202
|
+
expect(result).toEqual({});
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
describe("filter", () => {
|
|
207
|
+
it("should filter object entries by predicate", () => {
|
|
208
|
+
const obj = { a: 1, b: 2, c: 3, d: 4 };
|
|
209
|
+
const result = Objects.filter(obj, (v) => v % 2 === 0);
|
|
210
|
+
expect(result).toEqual({ b: 2, d: 4 });
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it("should pass key as second argument", () => {
|
|
214
|
+
const obj = { a: 1, b: 2, c: 3 };
|
|
215
|
+
const result = Objects.filter(obj, (_, k) => k !== "b");
|
|
216
|
+
expect(result).toEqual({ a: 1, c: 3 });
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it("should handle empty objects", () => {
|
|
220
|
+
const result = Objects.filter({}, () => true);
|
|
221
|
+
expect(result).toEqual({});
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it("should return empty object when nothing matches", () => {
|
|
225
|
+
const obj = { a: 1, b: 2 };
|
|
226
|
+
const result = Objects.filter(obj, () => false);
|
|
227
|
+
expect(result).toEqual({});
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
describe("omit", () => {
|
|
232
|
+
it("should omit specified keys from object", () => {
|
|
233
|
+
const obj = { a: 1, b: 2, c: 3 };
|
|
234
|
+
const result = Objects.omit(obj, ["b"]);
|
|
235
|
+
expect(result).toEqual({ a: 1, c: 3 });
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it("should omit multiple keys", () => {
|
|
239
|
+
const obj = { a: 1, b: 2, c: 3, d: 4 };
|
|
240
|
+
const result = Objects.omit(obj, ["a", "c"]);
|
|
241
|
+
expect(result).toEqual({ b: 2, d: 4 });
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it("should handle keys provided as Set", () => {
|
|
245
|
+
const obj = { a: 1, b: 2, c: 3 };
|
|
246
|
+
const result = Objects.omit(obj, new Set(["a", "c"] as const));
|
|
247
|
+
expect(result).toEqual({ b: 2 });
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it("should handle omitting non-existent keys", () => {
|
|
251
|
+
const obj = { a: 1, b: 2 };
|
|
252
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
253
|
+
const result = Objects.omit(obj, ["c" as any]);
|
|
254
|
+
expect(result).toEqual({ a: 1, b: 2 });
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it("should return all properties when omitting empty array", () => {
|
|
258
|
+
const obj = { a: 1, b: 2 };
|
|
259
|
+
const result = Objects.omit(obj, []);
|
|
260
|
+
expect(result).toEqual({ a: 1, b: 2 });
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
});
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { ProgressState } from "../progress";
|
|
4
|
+
|
|
5
|
+
describe("ProgressState", () => {
|
|
6
|
+
describe("constructor", () => {
|
|
7
|
+
it("should initialize with a numeric total", () => {
|
|
8
|
+
const progress = new ProgressState(100);
|
|
9
|
+
expect(progress.getProgress()).toBe(0);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it("should initialize with indeterminate total", () => {
|
|
13
|
+
const progress = new ProgressState("indeterminate");
|
|
14
|
+
expect(progress.getProgress()).toBe("indeterminate");
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe("static indeterminate", () => {
|
|
19
|
+
it("should create an indeterminate progress state", () => {
|
|
20
|
+
const progress = ProgressState.indeterminate();
|
|
21
|
+
expect(progress.getProgress()).toBe("indeterminate");
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe("addTotal", () => {
|
|
26
|
+
it("should add to the total when numeric", () => {
|
|
27
|
+
const progress = new ProgressState(100);
|
|
28
|
+
progress.addTotal(50);
|
|
29
|
+
// Progress is 0, total is now 150
|
|
30
|
+
expect(progress.getProgress()).toBe(0);
|
|
31
|
+
progress.increment(75);
|
|
32
|
+
expect(progress.getProgress()).toBe(50); // 75/150 * 100 = 50
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("should convert indeterminate to numeric when adding total", () => {
|
|
36
|
+
const progress = ProgressState.indeterminate();
|
|
37
|
+
expect(progress.getProgress()).toBe("indeterminate");
|
|
38
|
+
progress.addTotal(100);
|
|
39
|
+
expect(progress.getProgress()).toBe(0);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe("increment", () => {
|
|
44
|
+
it("should increment the progress", () => {
|
|
45
|
+
const progress = new ProgressState(100);
|
|
46
|
+
progress.increment(25);
|
|
47
|
+
expect(progress.getProgress()).toBe(25);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("should accumulate multiple increments", () => {
|
|
51
|
+
const progress = new ProgressState(100);
|
|
52
|
+
progress.increment(25);
|
|
53
|
+
progress.increment(25);
|
|
54
|
+
progress.increment(25);
|
|
55
|
+
expect(progress.getProgress()).toBe(75);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("should allow progress beyond 100%", () => {
|
|
59
|
+
const progress = new ProgressState(100);
|
|
60
|
+
progress.increment(150);
|
|
61
|
+
expect(progress.getProgress()).toBe(150);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe("getProgress", () => {
|
|
66
|
+
it("should return indeterminate for indeterminate state", () => {
|
|
67
|
+
const progress = ProgressState.indeterminate();
|
|
68
|
+
progress.increment(50); // increment has no visible effect
|
|
69
|
+
expect(progress.getProgress()).toBe("indeterminate");
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("should return correct percentage", () => {
|
|
73
|
+
const progress = new ProgressState(200);
|
|
74
|
+
progress.increment(50);
|
|
75
|
+
expect(progress.getProgress()).toBe(25); // 50/200 * 100 = 25
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("should return 0 when no progress made", () => {
|
|
79
|
+
const progress = new ProgressState(100);
|
|
80
|
+
expect(progress.getProgress()).toBe(0);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("should return 100 when complete", () => {
|
|
84
|
+
const progress = new ProgressState(100);
|
|
85
|
+
progress.increment(100);
|
|
86
|
+
expect(progress.getProgress()).toBe(100);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe("subscribe", () => {
|
|
91
|
+
it("should notify listeners on increment", () => {
|
|
92
|
+
const progress = new ProgressState(100);
|
|
93
|
+
const listener = vi.fn();
|
|
94
|
+
progress.subscribe(listener);
|
|
95
|
+
|
|
96
|
+
progress.increment(25);
|
|
97
|
+
expect(listener).toHaveBeenCalledWith(25);
|
|
98
|
+
|
|
99
|
+
progress.increment(25);
|
|
100
|
+
expect(listener).toHaveBeenCalledWith(50);
|
|
101
|
+
expect(listener).toHaveBeenCalledTimes(2);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("should notify listeners on addTotal", () => {
|
|
105
|
+
const progress = new ProgressState(100);
|
|
106
|
+
const listener = vi.fn();
|
|
107
|
+
progress.subscribe(listener);
|
|
108
|
+
|
|
109
|
+
progress.addTotal(100);
|
|
110
|
+
expect(listener).toHaveBeenCalledWith(0); // 0/200 = 0%
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("should notify listeners when converting from indeterminate", () => {
|
|
114
|
+
const progress = ProgressState.indeterminate();
|
|
115
|
+
const listener = vi.fn();
|
|
116
|
+
progress.subscribe(listener);
|
|
117
|
+
|
|
118
|
+
progress.addTotal(100);
|
|
119
|
+
expect(listener).toHaveBeenCalledWith(0);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("should return unsubscribe function", () => {
|
|
123
|
+
const progress = new ProgressState(100);
|
|
124
|
+
const listener = vi.fn();
|
|
125
|
+
const unsubscribe = progress.subscribe(listener);
|
|
126
|
+
|
|
127
|
+
progress.increment(25);
|
|
128
|
+
expect(listener).toHaveBeenCalledTimes(1);
|
|
129
|
+
|
|
130
|
+
unsubscribe();
|
|
131
|
+
progress.increment(25);
|
|
132
|
+
expect(listener).toHaveBeenCalledTimes(1); // no additional calls
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("should support multiple listeners", () => {
|
|
136
|
+
const progress = new ProgressState(100);
|
|
137
|
+
const listener1 = vi.fn();
|
|
138
|
+
const listener2 = vi.fn();
|
|
139
|
+
progress.subscribe(listener1);
|
|
140
|
+
progress.subscribe(listener2);
|
|
141
|
+
|
|
142
|
+
progress.increment(50);
|
|
143
|
+
expect(listener1).toHaveBeenCalledWith(50);
|
|
144
|
+
expect(listener2).toHaveBeenCalledWith(50);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it("should pass indeterminate to listeners", () => {
|
|
148
|
+
const progress = ProgressState.indeterminate();
|
|
149
|
+
const listener = vi.fn();
|
|
150
|
+
progress.subscribe(listener);
|
|
151
|
+
|
|
152
|
+
progress.increment(50);
|
|
153
|
+
expect(listener).toHaveBeenCalledWith("indeterminate");
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
});
|
package/src/utils/download.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
|
+
import React from "react";
|
|
3
4
|
import { toast } from "@/components/ui/use-toast";
|
|
4
5
|
import { type CellId, CellOutputId } from "@/core/cells/ids";
|
|
5
6
|
import { getRequestClient } from "@/core/network/requests";
|
|
@@ -9,6 +10,8 @@ import { prettyError } from "./errors";
|
|
|
9
10
|
import { toPng } from "./html-to-image";
|
|
10
11
|
import { captureIframeAsImage } from "./iframe";
|
|
11
12
|
import { Logger } from "./Logger";
|
|
13
|
+
import { ProgressState } from "./progress";
|
|
14
|
+
import { ToastProgress } from "./toast-progress";
|
|
12
15
|
|
|
13
16
|
/**
|
|
14
17
|
* Show a loading toast while an async operation is in progress.
|
|
@@ -16,14 +19,16 @@ import { Logger } from "./Logger";
|
|
|
16
19
|
*/
|
|
17
20
|
export async function withLoadingToast<T>(
|
|
18
21
|
title: string,
|
|
19
|
-
fn: () => Promise<T>,
|
|
22
|
+
fn: (progress: ProgressState) => Promise<T>,
|
|
20
23
|
): Promise<T> {
|
|
24
|
+
const progress = ProgressState.indeterminate();
|
|
21
25
|
const loadingToast = toast({
|
|
22
26
|
title,
|
|
27
|
+
description: React.createElement(ToastProgress, { progress }),
|
|
23
28
|
duration: Infinity,
|
|
24
29
|
});
|
|
25
30
|
try {
|
|
26
|
-
const result = await fn();
|
|
31
|
+
const result = await fn(progress);
|
|
27
32
|
loadingToast.dismiss();
|
|
28
33
|
return result;
|
|
29
34
|
} catch (error) {
|
package/src/utils/objects.ts
CHANGED
|
@@ -32,6 +32,9 @@ export const Objects = {
|
|
|
32
32
|
keys<K extends string | number>(obj: Record<K, unknown>): K[] {
|
|
33
33
|
return Object.keys(obj) as K[];
|
|
34
34
|
},
|
|
35
|
+
size<K extends string | number>(obj: Record<K, unknown>): number {
|
|
36
|
+
return Object.keys(obj).length;
|
|
37
|
+
},
|
|
35
38
|
/**
|
|
36
39
|
* Type-safe keyBy
|
|
37
40
|
*/
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
export type ProgressListener = (progress: number | "indeterminate") => void;
|
|
3
|
+
|
|
4
|
+
export class ProgressState {
|
|
5
|
+
private progress = 0;
|
|
6
|
+
private total: number | "indeterminate";
|
|
7
|
+
private listeners = new Set<ProgressListener>();
|
|
8
|
+
|
|
9
|
+
constructor(total: number | "indeterminate") {
|
|
10
|
+
this.total = total;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
static indeterminate(): ProgressState {
|
|
14
|
+
return new ProgressState("indeterminate");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
addTotal(total: number) {
|
|
18
|
+
if (this.total === "indeterminate") {
|
|
19
|
+
this.total = total;
|
|
20
|
+
} else {
|
|
21
|
+
this.total += total;
|
|
22
|
+
}
|
|
23
|
+
this.notifyListeners();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Update the progress by the given increment.
|
|
28
|
+
*/
|
|
29
|
+
increment(increment: number) {
|
|
30
|
+
this.progress += increment;
|
|
31
|
+
this.notifyListeners();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Get the progress as a percentage (0-100)
|
|
36
|
+
*/
|
|
37
|
+
getProgress(): number | "indeterminate" {
|
|
38
|
+
if (this.total === "indeterminate") {
|
|
39
|
+
return "indeterminate";
|
|
40
|
+
}
|
|
41
|
+
return (this.progress / this.total) * 100;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Subscribe to progress updates.
|
|
46
|
+
* Returns an unsubscribe function.
|
|
47
|
+
*/
|
|
48
|
+
subscribe(listener: ProgressListener): () => void {
|
|
49
|
+
this.listeners.add(listener);
|
|
50
|
+
return () => {
|
|
51
|
+
this.listeners.delete(listener);
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private notifyListeners() {
|
|
56
|
+
const progress = this.getProgress();
|
|
57
|
+
for (const listener of this.listeners) {
|
|
58
|
+
listener(progress);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { useSyncExternalStore } from "react";
|
|
4
|
+
import { Progress } from "@/components/ui/progress";
|
|
5
|
+
import type { ProgressState } from "./progress";
|
|
6
|
+
|
|
7
|
+
interface ToastProgressProps {
|
|
8
|
+
progress: ProgressState;
|
|
9
|
+
showPercentage?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* A progress bar component that subscribes to a ProgressState and updates reactively.
|
|
14
|
+
* Designed to be used inside toasts.
|
|
15
|
+
*/
|
|
16
|
+
export const ToastProgress = ({
|
|
17
|
+
progress,
|
|
18
|
+
showPercentage = false,
|
|
19
|
+
}: ToastProgressProps) => {
|
|
20
|
+
const value = useSyncExternalStore(
|
|
21
|
+
(callback) => progress.subscribe(callback),
|
|
22
|
+
() => progress.getProgress(),
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
// if we are at 100%, we want to show the indeterminate progress bar
|
|
26
|
+
const isIndeterminate = value === "indeterminate" || value === 100;
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<div className="mt-2 w-full min-w-[200px]">
|
|
30
|
+
<Progress
|
|
31
|
+
value={isIndeterminate ? undefined : value}
|
|
32
|
+
indeterminate={isIndeterminate}
|
|
33
|
+
/>
|
|
34
|
+
{!isIndeterminate && showPercentage && (
|
|
35
|
+
<div className="mt-1 text-xs text-muted-foreground text-right">
|
|
36
|
+
{Math.round(value)}%
|
|
37
|
+
</div>
|
|
38
|
+
)}
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
41
|
+
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{t as F}from"./chunk-LvLJmgfZ.js";import{t as I}from"./react-BGmjiNul.js";import{ii as W,zt as C}from"./cells-Cvo5AIrZ.js";import{d as z}from"./hotkeys-DghjL7BQ.js";import{t as H}from"./requests-BsVD4CdD.js";import{t as Y}from"./createLucideIcon-CnW3RofX.js";import{t as k}from"./use-toast-rmUWldD_.js";import{d as _}from"./popover-D16ZremR.js";import{r as J}from"./errors-2SszdW9t.js";import{t as A}from"./html-to-image-CJgqxZci.js";var K=Y("chevron-left",[["path",{d:"m15 18-6-6 6-6",key:"1wnfg3"}]]),N=t=>{let e,n=new Set,r=(l,R)=>{let d=typeof l=="function"?l(e):l;if(!Object.is(d,e)){let c=e;e=R??(typeof d!="object"||!d)?d:Object.assign({},e,d),n.forEach(s=>s(e,c))}},o=()=>e,i={setState:r,getState:o,getInitialState:()=>a,subscribe:l=>(n.add(l),()=>n.delete(l)),destroy:()=>{n.clear()}},a=e=t(r,o,i);return i},Q=t=>t?N(t):N,X=F((t=>{var e=I(),n=_();function r(c,s){return c===s&&(c!==0||1/c==1/s)||c!==c&&s!==s}var o=typeof Object.is=="function"?Object.is:r,i=n.useSyncExternalStore,a=e.useRef,l=e.useEffect,R=e.useMemo,d=e.useDebugValue;t.useSyncExternalStoreWithSelector=function(c,s,P,j,v){var m=a(null);if(m.current===null){var p={hasValue:!1,value:null};m.current=p}else p=m.current;m=R(function(){function D(f){if(!O){if(O=!0,h=f,f=j(f),v!==void 0&&p.hasValue){var y=p.value;if(v(y,f))return b=y}return b=f}if(y=b,o(h,f))return y;var U=j(f);return v!==void 0&&v(y,U)?(h=f,y):(h=f,b=U)}var O=!1,h,b,T=P===void 0?null:P;return[function(){return D(s())},T===null?void 0:function(){return D(T())}]},[s,P,j,v]);var g=i(c,m[0],m[1]);return l(function(){p.hasValue=!0,p.value=g},[g]),d(g),g}})),Z=F(((t,e)=>{e.exports=X()}));const u={toMarkdown:t=>u.replace(t,"md"),toHTML:t=>u.replace(t,"html"),toPNG:t=>u.replace(t,"png"),toPDF:t=>u.replace(t,"pdf"),toPY:t=>u.replace(t,"py"),withoutExtension:t=>{let e=t.split(".");return e.length===1?t:e.slice(0,-1).join(".")},replace:(t,e)=>t.endsWith(`.${e}`)?t:`${u.withoutExtension(t)}.${e}`};var w=320,x=180;function G(t){if(!t||t==="about:blank")return null;try{let e=new URL(t,window.location.href);return e.origin===window.location.origin?null:e.href}catch{return t}}function tt(t,e,n){let r=[],o="";for(let i of e){let a=o+i;t.measureText(a).width<=n?o=a:(o&&r.push(o),o=i)}return o&&r.push(o),r}function L(t){let e=window.devicePixelRatio||1,n=document.createElement("canvas");n.width=w*e,n.height=x*e;let r=n.getContext("2d");if(!r)return n.toDataURL("image/png");r.scale(e,e),r.fillStyle="#f3f4f6",r.fillRect(0,0,w,x),r.strokeStyle="#d1d5db",r.strokeRect(.5,.5,w-1,x-1),r.fillStyle="#6b7280",r.font="8px sans-serif",r.textAlign="center",r.textBaseline="middle";let o=w-32,i=t?tt(r,t,o):[],a=(x-(1+i.length)*14)/2+14/2;r.fillText("External iframe",w/2,a),a+=14;for(let l of i)r.fillText(l,w/2,a),a+=14;return n.toDataURL("image/png")}async function M(t){var o;let e=t.querySelector("iframe");if(!e)return null;let n=G(e.getAttribute("src"));if(n)return L(n);let r;try{let i=e.contentDocument||((o=e.contentWindow)==null?void 0:o.document);if(!(i!=null&&i.body))return null;r=i}catch{return L(null)}for(let i of r.querySelectorAll("iframe")){let a=G(i.getAttribute("src"));if(a)return L(a)}try{return await A(r.body)}catch{return L(null)}}async function et(t,e){let n=k({title:t,duration:1/0});try{let r=await e();return n.dismiss(),r}catch(r){throw n.dismiss(),r}}function V(t){let e=document.getElementById(W.create(t));if(!e){z.error(`Output element not found for cell ${t}`);return}return e}var S=0;function nt(){S++,S===1&&document.body.classList.add("printing")}function rt(){S--,S===0&&document.body.classList.remove("printing")}function $(t,e){t.classList.add("printing-output"),e&&nt();let n=t.style.overflow;return t.style.overflow="auto",()=>{t.classList.remove("printing-output"),e&&rt(),t.style.overflow=n}}async function ot(t,e=!0){let n=V(t);if(!n)return;let r=await M(n);if(r)return r;let o=$(n,e);try{return await A(n)}finally{o()}}async function it(t,e){let n=V(t);if(!n)return;let r=await M(n);if(r){E(r,u.toPNG(e));return}await q({element:n,filename:e,prepare:()=>$(n,!0)})}async function q(t){let{element:e,filename:n,prepare:r}=t,o=document.getElementById("App"),i=(o==null?void 0:o.scrollTop)??0,a;r?a=r(e):document.body.classList.add("printing");try{E(await A(e),u.toPNG(n))}catch{k({title:"Error",description:"Failed to download as PNG.",variant:"danger"})}finally{a==null||a(),document.body.classList.contains("printing")&&document.body.classList.remove("printing"),requestAnimationFrame(()=>{o==null||o.scrollTo(0,i)})}}function E(t,e){let n=document.createElement("a");n.href=t,n.download=e,n.click(),n.remove()}function B(t,e){let n=URL.createObjectURL(t);E(n,e),URL.revokeObjectURL(n)}async function at(t){let e=H(),{filename:n,webpdf:r}=t;try{let o=await e.exportAsPDF({webpdf:r}),i=C.basename(n);B(o,u.toPDF(i))}catch(o){throw k({title:"Failed to download",description:J(o),variant:"danger"}),o}}export{q as a,u as c,K as d,it as i,Z as l,B as n,ot as o,E as r,et as s,at as t,Q as u};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{l as S,p as _,u as E}from"./useEvent-DO6uJBas.js";import{o as I}from"./cells-Cvo5AIrZ.js";import{t as N}from"./compiler-runtime-DeeZ7FnK.js";import{a as k,d as x}from"./hotkeys-DghjL7BQ.js";import{i as P}from"./utils-CSDCHxwI.js";import{A as T,w as F}from"./config-BgpK7vqH.js";import{r as V}from"./requests-BsVD4CdD.js";import{f as D}from"./layout-BkGjQBnQ.js";import{o as $}from"./download-kUMZIq8-.js";import{t as B}from"./use-toast-rmUWldD_.js";import{t as j}from"./useInterval-Cu9ChZf-.js";var C=N(),A=5e3;function H(){let t=(0,C.c)(14),a=E(P),{state:e}=E(F),n=a.auto_download.includes("markdown"),s=a.auto_download.includes("html"),m=a.auto_download.includes("ipynb"),r=e===T.OPEN,d=!n||!r,p=!s||!r,u=!m||!r,{autoExportAsHTML:i,autoExportAsIPYNB:o,autoExportAsMarkdown:l,updateCellOutputs:c}=V(),b=M(),f;t[0]===l?f=t[1]:(f=async()=>{await l({download:!1})},t[0]=l,t[1]=f);let w;t[2]===d?w=t[3]:(w={delayMs:A,whenVisible:!0,disabled:d},t[2]=d,t[3]=w),j(f,w);let h;t[4]===i?h=t[5]:(h=async()=>{await i({download:!1,includeCode:!0,files:D.INSTANCE.filenames()})},t[4]=i,t[5]=h);let y;t[6]===p?y=t[7]:(y={delayMs:A,whenVisible:!0,disabled:p},t[6]=p,t[7]=y),j(h,y);let v;t[8]!==o||t[9]!==b||t[10]!==c?(v=async()=>{await O(b,c),await o({download:!1})},t[8]=o,t[9]=b,t[10]=c,t[11]=v):v=t[11];let g;t[12]===u?g=t[13]:(g={delayMs:A,whenVisible:!0,disabled:u,skipIfRunning:!0},t[12]=u,t[13]=g),j(v,g)}var L=_({}),R=new Set(["text/html","application/vnd.vegalite.v5+json","application/vnd.vega.v5+json","application/vnd.vegalite.v6+json","application/vnd.vega.v6+json"]);function M(){let t=(0,C.c)(4),[a,e]=S(L),n=E(I),s;return t[0]!==n||t[1]!==a||t[2]!==e?(s=async()=>{var p,u;let m={},r=[];for(let[i,o]of k.entries(n)){let l=(p=o.output)==null?void 0:p.data,c=a[i]!==l;m[i]=l,(u=o.output)!=null&&u.mimetype&&R.has(o.output.mimetype)&&l&&c&&r.push([i,o])}if(e(m),r.length===0)return{};let d=await Promise.all(r.map(q));return k.fromEntries(d.filter(Y))},t[0]=n,t[1]=a,t[2]=e,t[3]=s):s=t[3],s}function Y(t){return t!==null}async function q(t){let[a]=t;try{let e=await $(a,!1);return e?[a,["image/png",e]]:(x.error(`Failed to capture screenshot for cell ${a}`),null)}catch(e){let n=e;return x.error(`Error screenshotting cell ${a}:`,n),null}}async function O(t,a){try{let e=await t();Object.keys(e).length>0&&await a({cellIdsToOutput:e})}catch(e){x.error("Error updating cell outputs with screenshots:",e),B({title:"Failed to capture cell outputs",description:"Some outputs may not appear in the PDF. Continuing with export.",variant:"danger"})}}export{H as n,M as r,O as t};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const a={NOOP:()=>{},ASYNC_NOOP:async()=>{},THROW:()=>{throw Error("Should not be called")},asUpdater:e=>typeof e=="function"?e:()=>e,identity:e=>e};function M(){throw Error("Not implemented")}var k=(e,o)=>{let n=`[${e}]`;return{debug:(...t)=>console.debug(n,...t),log:(...t)=>o.log(n,...t),warn:(...t)=>o.warn(n,...t),error:(...t)=>o.error(n,...t),trace:(...t)=>o.trace(n,...t),get:t=>k(`${e}:${t}`,o),disabled:(t=!0)=>t?d:o}},c={debug:(...e)=>{console.debug(...e)},log:(...e)=>{console.log(...e)},warn:(...e)=>{console.warn(...e)},error:(...e)=>{console.error(...e)},trace:(...e)=>{console.trace(...e)},get:e=>k(`marimo:${e}`,c),disabled:(e=!0)=>e?d:c},d={debug:()=>a.NOOP,log:()=>a.NOOP,warn:()=>a.NOOP,error:()=>a.NOOP,trace:()=>a.NOOP,get:()=>d,disabled:()=>d};function C(){return typeof window<"u"&&window.Logger||c}const u=C();function s(){if(typeof window>"u")return u.warn("isPlatformMac() called without window"),!1;let e=window.navigator.userAgentData?window.navigator.userAgentData.platform:window.navigator.platform;return/mac/i.test(e)}function y(){if(typeof window>"u")return u.warn("isPlatformWindows() called without window"),!1;let e=window.navigator.userAgentData?window.navigator.userAgentData.platform:window.navigator.platform;return/win/i.test(e)}function b(e,o){let n=!0;for(let t of e){switch(t){case"mod":n&&(n=o.metaKey||o.ctrlKey);break;case"ctrl":n&&(n=o.ctrlKey);break;case"meta":n&&(n=o.metaKey);break;case"shift":n&&(n=o.shiftKey);break;case"alt":n&&(n=o.altKey);break;case"space":n&&(n=o.code==="Space");break;default:/^\d$/.test(t)&&o.shiftKey?n&&(n=o.code===`Digit${t}`):n&&(n=o.key.toLowerCase()===t);break}if(!n)return!1}return e.includes("shift")||n&&(n=!o.shiftKey),!e.includes("ctrl")&&!e.includes("mod")&&n&&(n=!o.ctrlKey),!e.includes("meta")&&!e.includes("mod")&&n&&(n=!o.metaKey),e.includes("alt")||n&&(n=!o.altKey),n}function S(e){return{control:"ctrl",command:"mod",cmd:"mod",option:"alt",return:"enter"}[e.toLowerCase()]||e.toLowerCase()}function v(e){if(e===g||e==="")return()=>!1;let o=e.includes("+")?"+":"-",n=e.split(o).map(S);return t=>b(n,t)}function f(){return s()?"mac":y()?"windows":"linux"}function E(e){var o;return!s()||!((o=e.key)!=null&&o.includes("Cmd"))||e.key.includes("Ctrl")?[e]:[e,{...e,key:e.key.replaceAll("Cmd","Ctrl")}]}const l={EMPTY:Object.freeze({}),mapValues(e,o){return e&&l.fromEntries(l.entries(e).map(([n,t])=>[n,o(t,n)]))},fromEntries(e){return Object.fromEntries(e)},entries(e){return Object.entries(e)},keys(e){return Object.keys(e)},keyBy(e,o){let n={};for(let t of e){let r=o(t);r!==void 0&&(n[r]=t)}return n},collect(e,o,n){return l.mapValues(l.keyBy(e,o),n)},groupBy(e,o,n){let t={};for(let r of e){let i=o(r);if(i===void 0)continue;let p=n(r);i in t?t[i].push(p):t[i]=[p]}return t},filter(e,o){let n={};for(let[t,r]of l.entries(e))o(r,t)&&(n[t]=e[t]);return n},omit(e,o){let n=new Set(o);return l.filter(e,(t,r)=>!n.has(r))}},g=Symbol("NOT_SET");var m={"cell.focusUp":{name:"Go to previous cell",group:"Navigation",key:"Mod-Shift-k"},"cell.focusDown":{name:"Go to next cell",group:"Navigation",key:"Mod-Shift-j"},"cell.moveUp":{name:"Move cell up",group:"Creation and Ordering",key:"Mod-Shift-9"},"cell.moveDown":{name:"Move cell down",group:"Creation and Ordering",key:"Mod-Shift-0"},"cell.moveLeft":{name:"Move left",group:"Creation and Ordering",key:"Mod-Shift-7"},"cell.moveRight":{name:"Move right",group:"Creation and Ordering",key:"Mod-Shift-8"},"cell.createAbove":{name:"New cell above",group:"Creation and Ordering",key:"Mod-Shift-o"},"cell.createBelow":{name:"New cell below",group:"Creation and Ordering",key:"Mod-Shift-p"},"cell.sendToTop":{name:"Send to top",group:"Creation and Ordering",key:"Mod-Shift-1"},"cell.sendToBottom":{name:"Send to bottom",group:"Creation and Ordering",key:"Mod-Shift-2"},"cell.addColumnBreakpoint":{name:"Add column breakpoint",group:"Creation and Ordering",key:"Mod-Shift-3"},"cell.run":{name:"Run",group:"Running Cells",key:"Mod-Enter"},"cell.runAndNewBelow":{name:"Run and new below",group:"Running Cells",key:"Shift-Enter"},"cell.runAndNewAbove":{name:"Run and new above",group:"Running Cells",key:"Mod-Shift-Enter"},"global.runAll":{name:"Re-run all cells",group:"Running Cells",key:g},"cell.format":{name:"Format cell",group:"Editing",key:"Mod-b"},"cell.viewAsMarkdown":{name:"View as Markdown",group:"Editing",key:"Mod-Shift-m"},"cell.complete":{name:"Code completion",group:"Editing",key:"Ctrl-Space"},"cell.signatureHelp":{name:"Signature help",group:"Editing",key:"Mod-Shift-Space"},"cell.undo":{name:"Undo",group:"Editing",key:"Mod-z"},"cell.redo":{name:"Redo",group:"Editing",key:{main:"Mod-Shift-z",windows:"Mod-y"}},"cell.findAndReplace":{name:"Find and Replace",group:"Editing",key:"Mod-f"},"cell.selectNextOccurrence":{name:"Add selection to next Find match",group:"Editing",key:"Mod-d"},"cell.fold":{name:"Fold region",group:"Editing",key:{main:"Mod-Alt-[",windows:"Mod-Shift-["}},"cell.unfold":{name:"Unfold region",group:"Editing",key:{main:"Mod-Alt-]",windows:"Mod-Shift-]"}},"cell.foldAll":{name:"Fold all regions",group:"Editing",key:"Ctrl-Alt-["},"cell.unfoldAll":{name:"Unfold all regions",group:"Editing",key:"Ctrl-Alt-]"},"cell.delete":{name:"Delete cell",group:"Editing",key:"Shift-Backspace"},"cell.hideCode":{name:"Hide cell code",group:"Editing",key:"Mod-h"},"cell.aiCompletion":{name:"AI completion",group:"Editing",key:"Mod-Shift-e"},"cell.cellActions":{name:"Open cell actions",group:"Editing",key:"Mod-p"},"cell.splitCell":{name:"Split cell",group:"Editing",key:"Mod-Shift-'"},"cell.toggleComment":{name:"Toggle comment",group:"Editing",key:"Mod-/"},"cell.toggleBlockComment":{name:"Toggle block comment",group:"Editing",key:"Alt-A"},"cell.renameSymbol":{name:"Rename symbol",group:"Editing",key:"F2"},"cell.copyLineUp":{name:"Copy line(s) up",group:"Editing",key:"Alt-Shift-ArrowUp",editable:!1},"cell.copyLineDown":{name:"Copy line(s) down",group:"Editing",key:"Alt-Shift-ArrowDown",editable:!1},"markdown.bold":{name:"Bold",group:"Markdown",key:"Mod-b"},"markdown.italic":{name:"Italic",group:"Markdown",key:"Mod-i"},"markdown.link":{name:"Convert to Link",group:"Markdown",key:"Mod-k"},"markdown.orderedList":{name:"Convert to Ordered list",group:"Markdown",key:"Mod-Shift-7"},"markdown.unorderedList":{name:"Convert to Unordered list",group:"Markdown",key:"Mod-Shift-8"},"markdown.blockquote":{name:"Convert to Blockquote",group:"Markdown",key:"Mod-Shift-9"},"markdown.code":{name:"Convert to Code",group:"Markdown",key:"Mod-Shift-0"},"global.hideCode":{name:"Toggle app view",group:"Other",key:"Mod-."},"global.foldCode":{name:"Fold all cells",group:"Editing",key:{main:"Ctrl-Cmd-l",windows:"Mod-Shift-l"}},"global.unfoldCode":{name:"Unfold all cells",group:"Editing",key:{main:"Ctrl-Cmd-;",windows:"Mod-Shift-:"}},"global.showHelp":{name:"Show keyboard shortcuts",group:"Other",key:"Mod-Shift-h"},"global.save":{name:"Save file",group:"Other",key:"Mod-s"},"global.commandPalette":{name:"Show command palette",group:"Other",key:"Mod-k"},"global.runStale":{name:"Run all stale cells",group:"Running Cells",key:"Mod-Shift-r"},"global.interrupt":{name:"Stop (interrupt) execution",group:"Running Cells",key:"Mod-i"},"global.formatAll":{name:"Format all",group:"Editing",key:"Mod-Shift-b"},"global.toggleLanguage":{name:"Toggle language to markdown (if supported)",group:"Editing",key:"F4"},"global.toggleTerminal":{name:"Show integrated terminal",group:"Other",key:"Ctrl-`"},"global.togglePanel":{name:"Toggle developer panel",group:"Other",key:"Mod-j"},"global.collapseAllSections":{name:"Collapse all sections",group:"Editing",key:"Mod-Shift-\\"},"global.expandAllSections":{name:"Expand all sections",group:"Editing",key:"Mod-Shift-/"},"global.toggleMinimap":{name:"Toggle Minimap",group:"Other",key:"Mod-Shift-i"},"global.focusTop":{name:"Focus top",group:"Navigation",key:"Mod-Shift-f"},"global.focusBottom":{name:"Focus bottom",group:"Navigation",key:"Mod-Shift-g"},"global.toggleSidebar":{name:"Toggle helper panel",group:"Navigation",key:"Mod-Shift-s"},"cell.goToDefinition":{name:"Go to Definition",group:"Navigation",key:"F12"},"completion.moveDown":{name:"Move completion selection down",group:"Editing",key:"Ctrl-j"},"completion.moveUp":{name:"Move completion selection up",group:"Editing",key:"Ctrl-k"},"command.vimEnterCommandMode":{name:"Enter command mode (vim)",group:"Command",key:{main:"Mod-Escape",windows:"Shift-Escape"}},"command.createCellBefore":{name:"Create a cell before current cell",group:"Command",key:"a"},"command.createCellAfter":{name:"Create a cell after current cell",group:"Command",key:"b"},"command.copyCell":{name:"Copy cell",group:"Command",key:"c"},"command.pasteCell":{name:"Paste cell",group:"Command",key:"v"}};function O(e){return e in m}function A(e){return new w(m).getHotkey(e)}var w=class h{static create(o){return new h(m,{platform:o})}constructor(o,n={}){this.hotkeys=o,this.platform=n.platform??f(),this.mod=this.platform==="mac"?"Cmd":"Ctrl"}iterate(){return l.keys(this.hotkeys)}getHotkey(o){let{name:n,key:t}=this.hotkeys[o];return typeof t=="string"?{name:n,key:t.replace("Mod",this.mod)}:t===g?{name:n,key:""}:{name:n,key:(t[this.platform]||t.main).replace("Mod",this.mod)}}getHotkeyDisplay(o){return this.hotkeys[o].name}isEditable(o){return this.hotkeys[o].editable!==!1}getHotkeyGroups(){return l.groupBy(l.entries(this.hotkeys),([,o])=>o.group,([o])=>o)}},N=class extends w{constructor(e,o={}){super(m,o),this.overrides=e}getHotkey(e){let o=super.getHotkey(e),n=this.overrides[e]||o.key;return{name:o.name,key:n}}};export{l as a,y as c,u as d,a as f,O as i,v as l,N as n,E as o,M as p,A as r,s,g as t,f as u};
|