@marimo-team/frontend 0.25.3-dev29 → 0.25.3-dev30
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-C_6iw05f.js → CellStatus-wFCGHpDX.js} +1 -1
- package/dist/assets/{ErrorBoundary-B6yKaNNU.js → ErrorBoundary-D1ozl9e0.js} +2 -2
- package/dist/assets/{JsonOutput-D7ovfzRT.js → JsonOutput-Dqg7qwkl.js} +1 -1
- package/dist/assets/{MarimoErrorOutput-x1b8BEel.js → MarimoErrorOutput-BYSTEwo1.js} +1 -1
- package/dist/assets/{RenderHTML-CHgN4Sj3.js → RenderHTML-B18UiW8r.js} +1 -1
- package/dist/assets/{RunButton-CjebHh_3.js → RunButton-CWvRnb98.js} +1 -1
- package/dist/assets/{add-cell-with-ai-Hyi0ODfx.js → add-cell-with-ai-C4pN_ho7.js} +1 -1
- package/dist/assets/{add-connection-dialog-DtE-EeJT.js → add-connection-dialog-BErVOfUV.js} +1 -1
- package/dist/assets/{agent-panel-C56B-F79.js → agent-panel-QQDTP8Vb.js} +1 -1
- package/dist/assets/{ai-model-dropdown-BSXpoT5O.js → ai-model-dropdown-D5Fe5GS-.js} +1 -1
- package/dist/assets/{app-config-button-ClnrveEl.js → app-config-button-DBHN-U2l.js} +1 -1
- package/dist/assets/{blob-DlOuX2uQ.js → blob-DY_W1wFZ.js} +1 -1
- package/dist/assets/{cell-editor-Ca6rrpD_.js → cell-editor-CCFDuQIT.js} +1 -1
- package/dist/assets/{cell-link-amlgOuMH.js → cell-link-BOqoitvE.js} +1 -1
- package/dist/assets/{cells-CJf50CB9.js → cells-CN1I_0eH.js} +1 -1
- package/dist/assets/{chat-display-CrPNkPHG.js → chat-display-D1_UV6v7.js} +1 -1
- package/dist/assets/{chat-panel-9sMQH6CP.js → chat-panel-C4OJDIt6.js} +1 -1
- package/dist/assets/{chat-ui-DJx7LIGF.js → chat-ui-wuDYPZ-w.js} +1 -1
- package/dist/assets/{column-preview-Br2kWTnb.js → column-preview-z4bqk4HW.js} +1 -1
- package/dist/assets/command-palette-TblDi15Q.js +1 -0
- package/dist/assets/{common-CaLv3bfC.js → common-Cbk0KIh7.js} +1 -1
- package/dist/assets/{components-CfzE8WQz.js → components-C88UxBXz.js} +1 -1
- package/dist/assets/{datasource-D7lJC0XO.js → datasource-DZJ6qqVI.js} +1 -1
- package/dist/assets/{dependency-graph-panel-CSi0twlb.js → dependency-graph-panel-DeZ66gIo.js} +1 -1
- package/dist/assets/{documentation-panel-DjTdVTMN.js → documentation-panel-Tl5G1cPt.js} +1 -1
- package/dist/assets/{download-CDIt5qnG.js → download-s8iwxGDb.js} +1 -1
- package/dist/assets/{edit-page-s5f2C8qH.js → edit-page-CjoAJvyN.js} +4 -4
- package/dist/assets/{error-entries-Yxnz11PL.js → error-entries-D11Bn1Hk.js} +1 -1
- package/dist/assets/{error-panel-CKKofC1g.js → error-panel-kfxFDscu.js} +1 -1
- package/dist/assets/feedback-button-3bZHpuSh.js +1 -0
- package/dist/assets/{feedback-button-CngqEjnR.js → feedback-button-DVABjD9Y.js} +1 -1
- package/dist/assets/{file-explorer-panel-DmRPr6jB.js → file-explorer-panel-CvmK-rEU.js} +1 -1
- package/dist/assets/{file-name-input-BG72jLLC.js → file-name-input-DAj9a3RQ.js} +1 -1
- package/dist/assets/{floating-outline-Bgn_3A2O.js → floating-outline-DDavqWJx.js} +1 -1
- package/dist/assets/{gallery-page-Dkdclwy-.js → gallery-page-xP6VqDyd.js} +1 -1
- package/dist/assets/{glide-data-editor-D3wKJL3e.js → glide-data-editor-32vBJJf8.js} +1 -1
- package/dist/assets/{home-page-qTKg4Ads.js → home-page-CvGzjVr4.js} +1 -1
- package/dist/assets/{hooks-BqWhLYfF.js → hooks-C1_gQ8kr.js} +1 -1
- package/dist/assets/{index-DTBCxL4Q.js → index-Dtv0HH-7.js} +4 -4
- package/dist/assets/{layout-C9xXio49.js → layout-BccvBeZy.js} +3 -3
- package/dist/assets/{logs-panel-D7219WDO.js → logs-panel-BhXo1sVJ.js} +1 -1
- package/dist/assets/{markdown-renderer-BPIzalVh.js → markdown-renderer-D2dafr8r.js} +1 -1
- package/dist/assets/{name-cell-input-4uJovCYk.js → name-cell-input-SHvgfcBR.js} +1 -1
- package/dist/assets/{outline-panel-BHVikyc1.js → outline-panel-CpY3FH-u.js} +1 -1
- package/dist/assets/{packages-panel-BtC7izyo.js → packages-panel-oZ0qZBec.js} +1 -1
- package/dist/assets/{panel-accordion-state-DPO3I0D8.js → panel-accordion-state-ChFIY2WQ.js} +1 -1
- package/dist/assets/panels-91ywfwgc.js +4 -0
- package/dist/assets/{process-output-CWvtHmic.js → process-output-Dvq4b2kJ.js} +1 -1
- package/dist/assets/{readonly-code-display-C1vCt8xX.js → readonly-code-display-C1eH5BDP.js} +1 -1
- package/dist/assets/{readonly-python-code-DcHbbnwj.js → readonly-python-code-BaElyj-4.js} +1 -1
- package/dist/assets/{reveal-component-CFfNa2YY.js → reveal-component-FCuqfC5H.js} +1 -1
- package/dist/assets/{run-page-Ci01VgVL.js → run-page-CbyMlRt1.js} +1 -1
- package/dist/assets/{runtime-details-state-GfolX-MI.js → runtime-details-state-Bpn67vTN.js} +1 -1
- package/dist/assets/{scratchpad-panel-Deuf7tpT.js → scratchpad-panel-OF9_e0l_.js} +1 -1
- package/dist/assets/{session-panel-dS5R90fQ.js → session-panel-CExxjkjV.js} +1 -1
- package/dist/assets/{snippets-panel-CDidd2MC.js → snippets-panel-DfRtTFJ-.js} +1 -1
- package/dist/assets/{state-DI_45AC3.js → state-BUqF6L9e.js} +1 -1
- package/dist/assets/{state-ZLQj4kZ7.js → state-CHojzKpZ.js} +1 -1
- package/dist/assets/{tracing-Be3zVYH3.js → tracing-BnfAB-BH.js} +1 -1
- package/dist/assets/{tracing-panel-UnSwXNoE.js → tracing-panel-DNabsbtA.js} +2 -2
- package/dist/assets/{tree-actions-d-fO9KIX.js → tree-actions-BXvnBv45.js} +1 -1
- package/dist/assets/{useAddCell-BUAQpDsD.js → useAddCell-6ECZ0xPG.js} +1 -1
- package/dist/assets/{useCellActionButton-DbgnioC5.js → useCellActionButton-Djyiwayj.js} +1 -1
- package/dist/assets/{useDeleteCell-CkcC1fWW.js → useDeleteCell-KX6SEwki.js} +1 -1
- package/dist/assets/{useDependencyPanelTab-C6YUVupC.js → useDependencyPanelTab-B8ke9iTY.js} +1 -1
- package/dist/assets/{useNotebookActions-BxtuPdK2.js → useNotebookActions-CluLEhAd.js} +1 -1
- package/dist/assets/{useRunCells-B5s8dBxS.js → useRunCells-B847kPLO.js} +1 -1
- package/dist/assets/{useSplitCell-5ootkjG6.js → useSplitCell-CnvTmIw7.js} +1 -1
- package/dist/index.html +20 -20
- package/package.json +1 -1
- package/src/components/editor/__tests__/dynamic-favicon.test.tsx +111 -18
- package/src/components/editor/controls/__tests__/command-palette.test.tsx +53 -0
- package/src/components/editor/controls/command-palette.tsx +22 -10
- package/src/components/editor/dynamic-favicon.tsx +21 -40
- package/src/core/cells/__tests__/notebook-outline.test.ts +113 -0
- package/src/core/cells/cells.ts +2 -1
- package/dist/assets/command-palette-RAkvowQQ.js +0 -1
- package/dist/assets/feedback-button-LtN1kjRE.js +0 -1
- package/dist/assets/panels-9EUsphaD.js +0 -4
|
@@ -1,17 +1,41 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
act,
|
|
5
|
+
render as renderComponent,
|
|
6
|
+
waitFor,
|
|
7
|
+
} from "@testing-library/react";
|
|
4
8
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
|
-
import {
|
|
9
|
+
import { Provider, createStore } from "jotai";
|
|
10
|
+
import type { ReactNode } from "react";
|
|
11
|
+
import { MockNotebook } from "@/__mocks__/notebook";
|
|
12
|
+
import { cellId } from "@/__tests__/branded";
|
|
13
|
+
import { notebookAtom } from "@/core/cells/cells";
|
|
6
14
|
import { DynamicFavicon } from "../dynamic-favicon";
|
|
7
15
|
|
|
8
|
-
// Mock useCellErrors hook
|
|
9
|
-
vi.mock("@/core/cells/cells", () => ({
|
|
10
|
-
useCellErrors: vi.fn(),
|
|
11
|
-
}));
|
|
12
|
-
|
|
13
16
|
describe("DynamicFavicon", () => {
|
|
14
17
|
let favicon: HTMLLinkElement;
|
|
18
|
+
let store: ReturnType<typeof createStore>;
|
|
19
|
+
const id = cellId("cell");
|
|
20
|
+
|
|
21
|
+
function render(ui: ReactNode) {
|
|
22
|
+
return renderComponent(ui, {
|
|
23
|
+
wrapper: ({ children }) => <Provider store={store}>{children}</Provider>,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function setErrors() {
|
|
28
|
+
store.set(
|
|
29
|
+
notebookAtom,
|
|
30
|
+
MockNotebook.notebookStateWithErrors([
|
|
31
|
+
{
|
|
32
|
+
cellId: id,
|
|
33
|
+
cellName: "cell",
|
|
34
|
+
errorData: [MockNotebook.errors.syntax("Invalid syntax")],
|
|
35
|
+
},
|
|
36
|
+
]),
|
|
37
|
+
);
|
|
38
|
+
}
|
|
15
39
|
|
|
16
40
|
beforeEach(() => {
|
|
17
41
|
document
|
|
@@ -27,15 +51,20 @@ describe("DynamicFavicon", () => {
|
|
|
27
51
|
// Mock document.hasFocus
|
|
28
52
|
vi.spyOn(document, "hasFocus").mockReturnValue(true);
|
|
29
53
|
|
|
30
|
-
|
|
31
|
-
|
|
54
|
+
store = createStore();
|
|
55
|
+
store.set(
|
|
56
|
+
notebookAtom,
|
|
57
|
+
MockNotebook.notebookState({
|
|
58
|
+
cellData: { [id]: { code: "x = 1" } },
|
|
59
|
+
}),
|
|
60
|
+
);
|
|
32
61
|
});
|
|
33
62
|
|
|
34
63
|
afterEach(() => {
|
|
35
64
|
document
|
|
36
65
|
.querySelectorAll("link[rel~='icon']")
|
|
37
66
|
.forEach((element) => element.remove());
|
|
38
|
-
vi.
|
|
67
|
+
vi.restoreAllMocks();
|
|
39
68
|
vi.useRealTimers();
|
|
40
69
|
});
|
|
41
70
|
|
|
@@ -67,9 +96,7 @@ describe("DynamicFavicon", () => {
|
|
|
67
96
|
});
|
|
68
97
|
|
|
69
98
|
it("should show error favicon when run completes with errors", async () => {
|
|
70
|
-
(
|
|
71
|
-
{ error: "mock error" },
|
|
72
|
-
]);
|
|
99
|
+
setErrors();
|
|
73
100
|
|
|
74
101
|
const { rerender } = render(<DynamicFavicon isRunning={true} />);
|
|
75
102
|
|
|
@@ -147,6 +174,72 @@ describe("DynamicFavicon", () => {
|
|
|
147
174
|
});
|
|
148
175
|
});
|
|
149
176
|
|
|
177
|
+
it("does not look up or rewrite the favicon when code changes", async () => {
|
|
178
|
+
const { rerender } = render(<DynamicFavicon isRunning={true} />);
|
|
179
|
+
await waitFor(() => expect(favicon.href).toContain("circle-play.ico"));
|
|
180
|
+
rerender(<DynamicFavicon isRunning={false} />);
|
|
181
|
+
await waitFor(() => expect(favicon.href).toContain("circle-check.ico"));
|
|
182
|
+
const lookup = vi.spyOn(document, "querySelector");
|
|
183
|
+
const write = vi.spyOn(favicon, "href", "set");
|
|
184
|
+
|
|
185
|
+
await act(async () => {
|
|
186
|
+
store.set(notebookAtom, (state) => ({
|
|
187
|
+
...state,
|
|
188
|
+
cellData: {
|
|
189
|
+
...state.cellData,
|
|
190
|
+
[id]: { ...state.cellData[id], code: "x = 2" },
|
|
191
|
+
},
|
|
192
|
+
}));
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
expect(
|
|
196
|
+
lookup.mock.calls.filter(
|
|
197
|
+
([selector]) => selector === "link[rel~='icon']",
|
|
198
|
+
),
|
|
199
|
+
).toEqual([]);
|
|
200
|
+
expect(write).not.toHaveBeenCalled();
|
|
201
|
+
lookup.mockRestore();
|
|
202
|
+
write.mockRestore();
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it("updates the favicon when the error count changes", async () => {
|
|
206
|
+
const { rerender } = render(<DynamicFavicon isRunning={true} />);
|
|
207
|
+
await waitFor(() => expect(favicon.href).toContain("circle-play.ico"));
|
|
208
|
+
rerender(<DynamicFavicon isRunning={false} />);
|
|
209
|
+
await waitFor(() => expect(favicon.href).toContain("circle-check.ico"));
|
|
210
|
+
|
|
211
|
+
act(() => setErrors());
|
|
212
|
+
await waitFor(() => expect(favicon.href).toContain("circle-x.ico"));
|
|
213
|
+
|
|
214
|
+
act(() =>
|
|
215
|
+
store.set(
|
|
216
|
+
notebookAtom,
|
|
217
|
+
MockNotebook.notebookState({ cellData: { [id]: {} } }),
|
|
218
|
+
),
|
|
219
|
+
);
|
|
220
|
+
await waitFor(() => expect(favicon.href).toContain("circle-check.ico"));
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it("keeps the completion icon until its own reset timer expires", async () => {
|
|
224
|
+
vi.useFakeTimers();
|
|
225
|
+
const { rerender } = render(<DynamicFavicon isRunning={true} />);
|
|
226
|
+
await act(() => vi.advanceTimersByTimeAsync(1000));
|
|
227
|
+
rerender(<DynamicFavicon isRunning={false} />);
|
|
228
|
+
await act(() => vi.advanceTimersByTimeAsync(2000));
|
|
229
|
+
expect(favicon.href).toContain("circle-check.ico");
|
|
230
|
+
await act(() => vi.advanceTimersByTimeAsync(1000));
|
|
231
|
+
expect(favicon.href).toContain("favicon.ico");
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it("cancels the reset timer when unmounted", async () => {
|
|
235
|
+
vi.useFakeTimers();
|
|
236
|
+
const { unmount } = render(<DynamicFavicon isRunning={true} />);
|
|
237
|
+
await act(() => vi.advanceTimersByTimeAsync(0));
|
|
238
|
+
expect(vi.getTimerCount()).toBe(1);
|
|
239
|
+
unmount();
|
|
240
|
+
expect(vi.getTimerCount()).toBe(0);
|
|
241
|
+
});
|
|
242
|
+
|
|
150
243
|
describe("notifications", () => {
|
|
151
244
|
beforeEach(() => {
|
|
152
245
|
vi.spyOn(document, "visibilityState", "get").mockReturnValue("hidden");
|
|
@@ -156,7 +249,7 @@ describe("DynamicFavicon", () => {
|
|
|
156
249
|
global.Notification.permission = "granted";
|
|
157
250
|
});
|
|
158
251
|
|
|
159
|
-
it("should send success notification when run completes without errors", () => {
|
|
252
|
+
it("should send success notification when run completes without errors", async () => {
|
|
160
253
|
// @ts-expect-error ok in tests
|
|
161
254
|
global.Notification = vi
|
|
162
255
|
.fn()
|
|
@@ -172,12 +265,11 @@ describe("DynamicFavicon", () => {
|
|
|
172
265
|
|
|
173
266
|
const { rerender } = render(<DynamicFavicon isRunning={true} />);
|
|
174
267
|
rerender(<DynamicFavicon isRunning={false} />);
|
|
268
|
+
await waitFor(() => expect(Notification).toHaveBeenCalledOnce());
|
|
175
269
|
});
|
|
176
270
|
|
|
177
|
-
it("should send error notification when run completes with errors", () => {
|
|
178
|
-
(
|
|
179
|
-
{ error: "mock error" },
|
|
180
|
-
]);
|
|
271
|
+
it("should send error notification when run completes with errors", async () => {
|
|
272
|
+
setErrors();
|
|
181
273
|
|
|
182
274
|
// @ts-expect-error ok in tests
|
|
183
275
|
global.Notification = vi
|
|
@@ -194,6 +286,7 @@ describe("DynamicFavicon", () => {
|
|
|
194
286
|
|
|
195
287
|
const { rerender } = render(<DynamicFavicon isRunning={true} />);
|
|
196
288
|
rerender(<DynamicFavicon isRunning={false} />);
|
|
289
|
+
await waitFor(() => expect(Notification).toHaveBeenCalledOnce());
|
|
197
290
|
});
|
|
198
291
|
|
|
199
292
|
it("should not send notification when document is visible", () => {
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
|
4
|
+
import { createStore, Provider } from "jotai";
|
|
5
|
+
import { expect, it, vi } from "vitest";
|
|
6
|
+
import { useNotebookActions } from "../../actions/useNotebookActions";
|
|
7
|
+
import CommandPalette from "../command-palette";
|
|
8
|
+
|
|
9
|
+
vi.mock("../../actions/useNotebookActions", () => ({
|
|
10
|
+
useNotebookActions: vi.fn(),
|
|
11
|
+
}));
|
|
12
|
+
vi.mock("../../actions/useCellActionButton", () => ({
|
|
13
|
+
useCellActionButtons: () => [],
|
|
14
|
+
}));
|
|
15
|
+
vi.mock("../../actions/useConfigActions", () => ({
|
|
16
|
+
useConfigActions: () => [],
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
it("builds current actions only while open and keeps the opening shortcut active", async () => {
|
|
20
|
+
HTMLElement.prototype.scrollIntoView = vi.fn();
|
|
21
|
+
const firstAction = vi.fn();
|
|
22
|
+
const nextAction = vi.fn();
|
|
23
|
+
vi.mocked(useNotebookActions).mockReturnValue([
|
|
24
|
+
{ label: "First action", handle: firstAction },
|
|
25
|
+
]);
|
|
26
|
+
render(
|
|
27
|
+
<Provider store={createStore()}>
|
|
28
|
+
<CommandPalette />
|
|
29
|
+
</Provider>,
|
|
30
|
+
);
|
|
31
|
+
expect(useNotebookActions).not.toHaveBeenCalled();
|
|
32
|
+
|
|
33
|
+
fireEvent.keyDown(document, { key: "k", ctrlKey: true });
|
|
34
|
+
await screen.findByPlaceholderText("Type to search...");
|
|
35
|
+
fireEvent.click(screen.getByText("First action"));
|
|
36
|
+
await waitFor(() => expect(firstAction).toHaveBeenCalledOnce());
|
|
37
|
+
await waitFor(() =>
|
|
38
|
+
expect(screen.queryByPlaceholderText("Type to search...")).toBeNull(),
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
vi.mocked(useNotebookActions)
|
|
42
|
+
.mockClear()
|
|
43
|
+
.mockReturnValue([{ label: "Updated action", handle: nextAction }]);
|
|
44
|
+
fireEvent.keyDown(document, { key: "x" });
|
|
45
|
+
expect(useNotebookActions).not.toHaveBeenCalled();
|
|
46
|
+
|
|
47
|
+
fireEvent.keyDown(document, { key: "k", ctrlKey: true });
|
|
48
|
+
await screen.findByPlaceholderText("Type to search...");
|
|
49
|
+
expect(screen.queryByText("First action")).toBeNull();
|
|
50
|
+
fireEvent.click(screen.getByText("Updated action"));
|
|
51
|
+
await waitFor(() => expect(nextAction).toHaveBeenCalledOnce());
|
|
52
|
+
expect(firstAction).toHaveBeenCalledOnce();
|
|
53
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
|
-
import { useAtom, useAtomValue } from "jotai";
|
|
3
|
+
import { useAtom, useAtomValue, useSetAtom } from "jotai";
|
|
4
4
|
import {
|
|
5
5
|
CommandDialog,
|
|
6
6
|
CommandEmpty,
|
|
@@ -32,6 +32,25 @@ import { commandPaletteAtom } from "./state";
|
|
|
32
32
|
|
|
33
33
|
const CommandPalette = () => {
|
|
34
34
|
const [open, setOpen] = useAtom(commandPaletteAtom);
|
|
35
|
+
const hotkeys = useAtomValue(hotkeysAtom);
|
|
36
|
+
|
|
37
|
+
useEventListener(document, "keydown", (e) => {
|
|
38
|
+
if (parseShortcut(hotkeys.getHotkey("global.commandPalette").key)(e)) {
|
|
39
|
+
e.preventDefault();
|
|
40
|
+
setOpen((open) => !open);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<CommandDialog open={open} onOpenChange={setOpen}>
|
|
46
|
+
<CommandPaletteContent />
|
|
47
|
+
</CommandDialog>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// The dialog mounts these subscriptions only while its content is present.
|
|
52
|
+
const CommandPaletteContent = () => {
|
|
53
|
+
const setOpen = useSetAtom(commandPaletteAtom);
|
|
35
54
|
const registeredActions = useRegisteredActions();
|
|
36
55
|
const lastFocusedCell = useAtomValue(lastFocusedCellAtom);
|
|
37
56
|
const hotkeys = useAtomValue(hotkeysAtom);
|
|
@@ -55,13 +74,6 @@ const CommandPalette = () => {
|
|
|
55
74
|
const { recentCommands, addRecentCommand } = useRecentCommands();
|
|
56
75
|
const recentCommandsSet = new Set(recentCommands);
|
|
57
76
|
|
|
58
|
-
useEventListener(document, "keydown", (e) => {
|
|
59
|
-
if (parseShortcut(hotkeys.getHotkey("global.commandPalette").key)(e)) {
|
|
60
|
-
e.preventDefault();
|
|
61
|
-
setOpen((open) => !open);
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
|
|
65
77
|
const renderShortcutCommandItem = (
|
|
66
78
|
shortcut: HotkeyAction,
|
|
67
79
|
props: {
|
|
@@ -142,7 +154,7 @@ const CommandPalette = () => {
|
|
|
142
154
|
};
|
|
143
155
|
|
|
144
156
|
return (
|
|
145
|
-
|
|
157
|
+
<>
|
|
146
158
|
<CommandInput placeholder="Type to search..." />
|
|
147
159
|
<CommandList>
|
|
148
160
|
<CommandEmpty>No results found.</CommandEmpty>
|
|
@@ -211,7 +223,7 @@ const CommandPalette = () => {
|
|
|
211
223
|
})}
|
|
212
224
|
</CommandGroup>
|
|
213
225
|
</CommandList>
|
|
214
|
-
|
|
226
|
+
</>
|
|
215
227
|
);
|
|
216
228
|
};
|
|
217
229
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
3
|
import { usePrevious } from "@dnd-kit/utilities";
|
|
4
|
+
import { useAtomValue } from "jotai";
|
|
4
5
|
import { useEffect } from "react";
|
|
5
|
-
import {
|
|
6
|
+
import { cellErrorCount } from "@/core/cells/cells";
|
|
6
7
|
import { useEventListener } from "@/hooks/useEventListener";
|
|
7
8
|
import successFaviconUrl from "../../assets/circle-check.ico";
|
|
8
9
|
import runningFaviconUrl from "../../assets/circle-play.ico";
|
|
@@ -15,13 +16,6 @@ const FAVICON_PATHS = {
|
|
|
15
16
|
error: errorFaviconUrl,
|
|
16
17
|
} as const;
|
|
17
18
|
|
|
18
|
-
// Cache favicon object URLs lazily
|
|
19
|
-
type FaviconKey = keyof typeof FAVICON_PATHS;
|
|
20
|
-
|
|
21
|
-
async function getFaviconUrl(key: FaviconKey): Promise<string> {
|
|
22
|
-
return FAVICON_PATHS[key];
|
|
23
|
-
}
|
|
24
|
-
|
|
25
19
|
function getOrCreateFavicon(): HTMLLinkElement {
|
|
26
20
|
const existingFavicon =
|
|
27
21
|
document.querySelector<HTMLLinkElement>("link[rel~='icon']");
|
|
@@ -44,16 +38,16 @@ function maybeSendNotification(numErrors: number) {
|
|
|
44
38
|
return;
|
|
45
39
|
}
|
|
46
40
|
|
|
47
|
-
const sendNotification =
|
|
41
|
+
const sendNotification = () => {
|
|
48
42
|
if (numErrors === 0) {
|
|
49
43
|
new Notification("Execution completed", {
|
|
50
44
|
body: "Your notebook run completed successfully.",
|
|
51
|
-
icon:
|
|
45
|
+
icon: FAVICON_PATHS.success,
|
|
52
46
|
});
|
|
53
47
|
} else {
|
|
54
48
|
new Notification("Execution failed", {
|
|
55
49
|
body: `Your notebook run encountered ${numErrors} error(s).`,
|
|
56
|
-
icon:
|
|
50
|
+
icon: FAVICON_PATHS.error,
|
|
57
51
|
});
|
|
58
52
|
}
|
|
59
53
|
};
|
|
@@ -75,7 +69,7 @@ function maybeSendNotification(numErrors: number) {
|
|
|
75
69
|
|
|
76
70
|
export const DynamicFavicon = (props: Props) => {
|
|
77
71
|
const { isRunning } = props;
|
|
78
|
-
const
|
|
72
|
+
const numErrors = useAtomValue(cellErrorCount);
|
|
79
73
|
|
|
80
74
|
useEffect(() => {
|
|
81
75
|
const favicon = getOrCreateFavicon();
|
|
@@ -86,46 +80,33 @@ export const DynamicFavicon = (props: Props) => {
|
|
|
86
80
|
return;
|
|
87
81
|
}
|
|
88
82
|
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
// When notebook is running, display running favicon
|
|
92
|
-
if (isRunning) {
|
|
93
|
-
key = "running";
|
|
94
|
-
} else {
|
|
95
|
-
// When run is complete, display success or error favicon
|
|
96
|
-
key = errors.length === 0 ? "success" : "error";
|
|
97
|
-
}
|
|
98
|
-
favicon.href = await getFaviconUrl(key);
|
|
83
|
+
const key = isRunning ? "running" : numErrors === 0 ? "success" : "error";
|
|
84
|
+
favicon.href = FAVICON_PATHS[key];
|
|
99
85
|
|
|
100
|
-
|
|
101
|
-
//
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const timeoutId = setTimeout(async () => {
|
|
107
|
-
favicon.href = await getFaviconUrl("idle");
|
|
108
|
-
}, 3000);
|
|
109
|
-
|
|
110
|
-
return () => clearTimeout(timeoutId);
|
|
111
|
-
};
|
|
86
|
+
if (!document.hasFocus()) {
|
|
87
|
+
// The focus listener resets the favicon when the user returns.
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
112
90
|
|
|
113
|
-
|
|
114
|
-
|
|
91
|
+
const timeoutId = setTimeout(() => {
|
|
92
|
+
favicon.href = FAVICON_PATHS.idle;
|
|
93
|
+
}, 3000);
|
|
94
|
+
return () => clearTimeout(timeoutId);
|
|
95
|
+
}, [isRunning, numErrors]);
|
|
115
96
|
|
|
116
97
|
// Send user notification when run has completed
|
|
117
98
|
const prevRunning = usePrevious(isRunning) ?? isRunning;
|
|
118
99
|
useEffect(() => {
|
|
119
100
|
if (prevRunning && !isRunning) {
|
|
120
|
-
maybeSendNotification(
|
|
101
|
+
maybeSendNotification(numErrors);
|
|
121
102
|
}
|
|
122
|
-
}, [
|
|
103
|
+
}, [numErrors, prevRunning, isRunning]);
|
|
123
104
|
|
|
124
105
|
// When notebook comes back in focus, reset favicon
|
|
125
|
-
useEventListener(window, "focus",
|
|
106
|
+
useEventListener(window, "focus", () => {
|
|
126
107
|
if (!isRunning) {
|
|
127
108
|
const favicon = getOrCreateFavicon();
|
|
128
|
-
favicon.href =
|
|
109
|
+
favicon.href = FAVICON_PATHS.idle;
|
|
129
110
|
}
|
|
130
111
|
});
|
|
131
112
|
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { createStore } from "jotai";
|
|
4
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
import { MockNotebook } from "@/__mocks__/notebook";
|
|
6
|
+
import { cellId } from "@/__tests__/branded";
|
|
7
|
+
import {
|
|
8
|
+
createNotebookActions,
|
|
9
|
+
notebookAtom,
|
|
10
|
+
notebookOutline,
|
|
11
|
+
notebookReducer,
|
|
12
|
+
} from "../cells";
|
|
13
|
+
import { exportedForTesting } from "../document-changes";
|
|
14
|
+
import type { Outline } from "../outline";
|
|
15
|
+
|
|
16
|
+
const first = cellId("first");
|
|
17
|
+
const second = cellId("second");
|
|
18
|
+
const firstOutline: Outline = {
|
|
19
|
+
items: [{ name: "First", level: 1, by: { id: "first" } }],
|
|
20
|
+
};
|
|
21
|
+
const secondOutline: Outline = {
|
|
22
|
+
items: [{ name: "Second", level: 2, by: { id: "second" } }],
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
function setup() {
|
|
26
|
+
const store = createStore();
|
|
27
|
+
store.set(
|
|
28
|
+
notebookAtom,
|
|
29
|
+
MockNotebook.notebookState({
|
|
30
|
+
cellData: { [first]: { code: "x = 1" }, [second]: {} },
|
|
31
|
+
cellRuntime: {
|
|
32
|
+
[first]: { outline: firstOutline },
|
|
33
|
+
[second]: { outline: secondOutline },
|
|
34
|
+
},
|
|
35
|
+
}),
|
|
36
|
+
);
|
|
37
|
+
const actions = createNotebookActions((action) =>
|
|
38
|
+
store.set(notebookAtom, (state) => notebookReducer(state, action)),
|
|
39
|
+
);
|
|
40
|
+
return { store, actions };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
afterEach(() => {
|
|
44
|
+
exportedForTesting.cancelPendingChanges();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe("notebookOutline", () => {
|
|
48
|
+
it("does not notify outline subscribers when cell code changes", () => {
|
|
49
|
+
const { store, actions } = setup();
|
|
50
|
+
const initial = store.get(notebookOutline);
|
|
51
|
+
const listener = vi.fn();
|
|
52
|
+
const unsubscribe = store.sub(notebookOutline, listener);
|
|
53
|
+
|
|
54
|
+
actions.updateCellCode({
|
|
55
|
+
cellId: first,
|
|
56
|
+
code: "x = 2",
|
|
57
|
+
formattingChange: false,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
expect(store.get(notebookOutline)).toBe(initial);
|
|
61
|
+
expect(listener).not.toHaveBeenCalled();
|
|
62
|
+
unsubscribe();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("updates headings when runtime outlines change or are removed", () => {
|
|
66
|
+
const { store } = setup();
|
|
67
|
+
const updated: Outline = {
|
|
68
|
+
items: [{ name: "Updated", level: 1, by: { id: "updated" } }],
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
for (const outline of [updated, null]) {
|
|
72
|
+
store.set(notebookAtom, (state) => ({
|
|
73
|
+
...state,
|
|
74
|
+
cellRuntime: {
|
|
75
|
+
...state.cellRuntime,
|
|
76
|
+
[first]: { ...state.cellRuntime[first], outline },
|
|
77
|
+
},
|
|
78
|
+
}));
|
|
79
|
+
expect(store.get(notebookOutline)).toEqual({
|
|
80
|
+
items: [...(outline?.items ?? []), ...secondOutline.items],
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("keeps headings in cell order after moving and deleting cells", () => {
|
|
86
|
+
const { store, actions } = setup();
|
|
87
|
+
actions.moveCell({ cellId: second, before: true });
|
|
88
|
+
expect(store.get(notebookOutline)).toEqual({
|
|
89
|
+
items: [...secondOutline.items, ...firstOutline.items],
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
actions.deleteCell({ cellId: second });
|
|
93
|
+
expect(store.get(notebookOutline)).toEqual(firstOutline);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("notifies subscribers when output is replaced with the same headings", () => {
|
|
97
|
+
const { store } = setup();
|
|
98
|
+
const listener = vi.fn();
|
|
99
|
+
const unsubscribe = store.sub(notebookOutline, listener);
|
|
100
|
+
|
|
101
|
+
store.set(notebookAtom, (state) => ({
|
|
102
|
+
...state,
|
|
103
|
+
cellRuntime: {
|
|
104
|
+
...state.cellRuntime,
|
|
105
|
+
[first]: { ...state.cellRuntime[first], outline: { ...firstOutline } },
|
|
106
|
+
},
|
|
107
|
+
}));
|
|
108
|
+
|
|
109
|
+
// Consumers must reconnect to replacement heading elements in the DOM.
|
|
110
|
+
expect(listener).toHaveBeenCalledOnce();
|
|
111
|
+
unsubscribe();
|
|
112
|
+
});
|
|
113
|
+
});
|
package/src/core/cells/cells.ts
CHANGED
|
@@ -1761,7 +1761,8 @@ export const cellErrorsAtom = atom((get) => {
|
|
|
1761
1761
|
});
|
|
1762
1762
|
|
|
1763
1763
|
export const notebookOutline = atom((get) => {
|
|
1764
|
-
const
|
|
1764
|
+
const cellIds = get(cellIdsAtom);
|
|
1765
|
+
const cellRuntime = get(cellsRuntimeAtom);
|
|
1765
1766
|
const outlines = cellIds.inOrderIds.map(
|
|
1766
1767
|
(cellId) => cellRuntime[cellId].outline,
|
|
1767
1768
|
);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{c as e,l as t}from"./useEvent-Dfcbgjhg.js";import{Ft as n,_n as r,bn as i,gn as a,hn as o,mn as s,pn as c,vn as l,yn as u}from"./cells-CJf50CB9.js";import{t as d}from"./compiler-runtime-BrajqUja.js";import{C as f,S as p,h as m}from"./config-CA1V-wjh.js";import{m as h,t as g,u as _}from"./useEventListener-D9cftcHj.js";import{t as v}from"./objects-snlMn4yk.js";import{t as y}from"./jsx-runtime-B74pBk57.js";import{O as b,k as x}from"./ai-model-dropdown-BSXpoT5O.js";import{t as S}from"./useCellActionButton-DbgnioC5.js";import{a as C,t as w}from"./useNotebookActions-BxtuPdK2.js";import{r as T}from"./requests-lt0JK8Jt.js";import{t as E}from"./renderShortcut-C1JwYcqW.js";import{r as D}from"./feedback-button-CngqEjnR.js";var O=d(),k=3;function A(){let e=(0,O.c)(7),t;e[0]===Symbol.for(`react.memo_cache_sentinel`)?(t=[],e[0]=t):t=e[0];let[n,r]=D(`marimo:commands`,t),i;e[1]!==n||e[2]!==r?(i=e=>{let t=j([e,...n]);r(t.slice(0,k))},e[1]=n,e[2]=r,e[3]=i):i=e[3];let a;return e[4]!==n||e[5]!==i?(a={recentCommands:n,addRecentCommand:i},e[4]=n,e[5]=i,e[6]=a):a=e[6],a}function j(e){return[...new Set(e)]}function M(e){return e.dropdown!==void 0}function N(e,t=``){return e.flatMap(e=>!e.label||e.hidden?[]:M(e)?N(e.dropdown,`${t+e.label} > `):{...e,label:t+e.label,additionalKeywords:e.additionalKeywords})}function P(){let e=(0,O.c)(87),[t,n]=f(),[r,i]=p(),{saveAppConfig:a,saveUserConfig:o}=T(),s;e[0]!==o||e[1]!==n?(s=async e=>{await o({config:e}).then(()=>{n(t=>({...t,...e}))})},e[0]=o,e[1]=n,e[2]=s):s=e[2];let c=s,l;e[3]!==a||e[4]!==i?(l=async e=>{await a({config:e}).then(()=>{i(e)})},e[3]=a,e[4]=i,e[5]=l):l=e[5];let u=l,d;if(e[6]!==r||e[7]!==t.completion||e[8]!==t.display||e[9]!==t.keymap||e[10]!==u||e[11]!==c){let n;e[13]===r?n=e[14]:(n=e=>e!==r.width,e[13]=r,e[14]=n);let i;e[15]!==r||e[16]!==u?(i=e=>({label:`App config > Set width=${e}`,handle:()=>{u({...r,width:e})}}),e[15]=r,e[16]=u,e[17]=i):i=e[17];let a;e[18]!==t.display||e[19]!==c?(a={label:`Config > Set theme: dark`,handle:()=>{c({display:{...t.display,theme:`dark`}})}},e[18]=t.display,e[19]=c,e[20]=a):a=e[20];let o;e[21]!==t.display||e[22]!==c?(o={label:`Config > Set theme: light`,handle:()=>{c({display:{...t.display,theme:`light`}})}},e[21]=t.display,e[22]=c,e[23]=o):o=e[23];let s;e[24]!==t.display||e[25]!==c?(s={label:`Config > Set theme: system`,handle:()=>{c({display:{...t.display,theme:`system`}})}},e[24]=t.display,e[25]=c,e[26]=s):s=e[26];let l=t.keymap.preset===`vim`,f;e[27]!==t.keymap||e[28]!==c?(f=()=>{c({keymap:{...t.keymap,preset:`vim`}})},e[27]=t.keymap,e[28]=c,e[29]=f):f=e[29];let p;e[30]!==l||e[31]!==f?(p={label:`Config > Switch keymap to VIM`,hidden:l,handle:f},e[30]=l,e[31]=f,e[32]=p):p=e[32];let m=t.keymap.preset==="default",h;e[33]!==t.keymap||e[34]!==c?(h=()=>{c({keymap:{...t.keymap,preset:`default`}})},e[33]=t.keymap,e[34]=c,e[35]=h):h=e[35];let g;e[36]!==m||e[37]!==h?(g={label:`Config > Switch keymap to default (current: VIM)`,hidden:m,handle:h},e[36]=m,e[37]=h,e[38]=g):g=e[38];let _;e[39]!==t.completion||e[40]!==c?(_=()=>{c({completion:{...t.completion,copilot:!1}})},e[39]=t.completion,e[40]=c,e[41]=_):_=e[41];let v=t.completion.copilot!==`github`,y;e[42]!==_||e[43]!==v?(y={label:`Config > Disable GitHub Copilot`,handle:_,hidden:v},e[42]=_,e[43]=v,e[44]=y):y=e[44];let b;e[45]!==t.completion||e[46]!==c?(b=()=>{c({completion:{...t.completion,copilot:`github`}})},e[45]=t.completion,e[46]=c,e[47]=b):b=e[47];let S=t.completion.copilot===`github`,C;e[48]!==b||e[49]!==S?(C={label:`Config > Enable GitHub Copilot`,handle:b,hidden:S},e[48]=b,e[49]=S,e[50]=C):C=e[50];let w=!t.display.reference_highlighting,T;e[51]!==t.display||e[52]!==c?(T=()=>{c({display:{...t.display,reference_highlighting:!1}})},e[51]=t.display,e[52]=c,e[53]=T):T=e[53];let E;e[54]!==w||e[55]!==T?(E={label:`Config > Disable reference highlighting`,hidden:w,handle:T},e[54]=w,e[55]=T,e[56]=E):E=e[56];let D;e[57]!==t.display||e[58]!==c?(D=()=>{c({display:{...t.display,reference_highlighting:!0}})},e[57]=t.display,e[58]=c,e[59]=D):D=e[59];let O;e[60]!==t.display.reference_highlighting||e[61]!==D?(O={label:`Config > Enable reference highlighting`,hidden:t.display.reference_highlighting,handle:D},e[60]=t.display.reference_highlighting,e[61]=D,e[62]=O):O=e[62];let k=!(t.display.code_lens??!0),A;e[63]!==t.display||e[64]!==c?(A=()=>{c({display:{...t.display,code_lens:!1}})},e[63]=t.display,e[64]=c,e[65]=A):A=e[65];let j;e[66]!==k||e[67]!==A?(j={label:`Config > Disable code lens`,hidden:k,handle:A},e[66]=k,e[67]=A,e[68]=j):j=e[68];let M=t.display.code_lens??!0,N;e[69]!==t.display||e[70]!==c?(N=()=>{c({display:{...t.display,code_lens:!0}})},e[69]=t.display,e[70]=c,e[71]=N):N=e[71];let P;e[72]!==M||e[73]!==N?(P={label:`Config > Enable code lens`,hidden:M,handle:N},e[72]=M,e[73]=N,e[74]=P):P=e[74];let F=t.display.cell_output===`above`,I;e[75]!==t.display||e[76]!==c?(I=()=>{c({display:{...t.display,cell_output:`above`}})},e[75]=t.display,e[76]=c,e[77]=I):I=e[77];let L;e[78]!==F||e[79]!==I?(L={label:`Config > Set cell output area: above`,hidden:F,handle:I},e[78]=F,e[79]=I,e[80]=L):L=e[80];let R=t.display.cell_output===`below`,z;e[81]!==t.display||e[82]!==c?(z=()=>{c({display:{...t.display,cell_output:`below`}})},e[81]=t.display,e[82]=c,e[83]=z):z=e[83];let B;e[84]!==R||e[85]!==z?(B={label:`Config > Set cell output area: below`,hidden:R,handle:z},e[84]=R,e[85]=z,e[86]=B):B=e[86],d=[...x().filter(n).map(i),a,o,s,p,g,y,C,E,O,j,P,L,B].filter(ee),e[6]=r,e[7]=t.completion,e[8]=t.display,e[9]=t.keymap,e[10]=u,e[11]=c,e[12]=d}else d=e[12];return d}function ee(e){return!e.hidden}var F=y(),I=()=>{let d=(0,O.c)(37),[f,p]=e(C),y=b(),x=t(n),T=t(m),D;d[0]===x?D=d[1]:(D={cell:x},d[0]=x,d[1]=D);let k=S(D).flat();k=N(k);let j=P(),ee=w();ee=[...N(ee),...N(j)];let I=ee.filter(L),B=v.keyBy(I,R),{recentCommands:V,addRecentCommand:H}=A(),U;d[2]===V?U=d[3]:(U=new Set(V),d[2]=V,d[3]=U);let W=U,G;d[4]!==T||d[5]!==p?(G=e=>{h(T.getHotkey(`global.commandPalette`).key)(e)&&(e.preventDefault(),p(z))},d[4]=T,d[5]=p,d[6]=G):G=d[6],g(document,`keydown`,G);let K;d[7]!==H||d[8]!==T||d[9]!==y||d[10]!==p?(K=(e,t)=>{let n=y[e];if(!n)return null;let a=T.getHotkey(e);return(0,F.jsxs)(r,{disabled:t.disabled,keywords:a.additionalKeywords,onSelect:()=>{H(e),p(!1),requestAnimationFrame(()=>{n()})},value:a.name,children:[(0,F.jsxs)(`span`,{children:[a.name,t.tooltip&&(0,F.jsx)(`span`,{className:`ml-2`,children:t.tooltip})]}),(0,F.jsx)(i,{children:(0,F.jsx)(E,{shortcut:a.key})})]},e)},d[7]=H,d[8]=T,d[9]=y,d[10]=p,d[11]=K):K=d[11];let te=K,q;d[12]!==H||d[13]!==T||d[14]!==p?(q=e=>{let{label:t,handle:n,props:a,hotkey:o,additionalKeywords:s}=e,c=a===void 0?{}:a;return(0,F.jsxs)(r,{disabled:c.disabled,keywords:s,onSelect:()=>{H(t),p(!1),requestAnimationFrame(()=>{n()})},value:t,children:[(0,F.jsxs)(`span`,{children:[t,c.tooltip&&(0,F.jsxs)(`span`,{className:`ml-2`,children:[`(`,c.tooltip,`)`]})]}),o&&(0,F.jsx)(i,{children:(0,F.jsx)(E,{shortcut:T.getHotkey(o).key})})]},t)},d[12]=H,d[13]=T,d[14]=p,d[15]=q):q=d[15];let J=q,ne=c,Y;d[16]===Symbol.for(`react.memo_cache_sentinel`)?(Y=(0,F.jsx)(a,{placeholder:`Type to search...`}),d[16]=Y):Y=d[16];let re=l,X;d[17]===Symbol.for(`react.memo_cache_sentinel`)?(X=(0,F.jsx)(s,{children:`No results found.`}),d[17]=X):X=d[17];let ie=V.length>0&&(0,F.jsxs)(F.Fragment,{children:[(0,F.jsx)(o,{heading:`Recently Used`,children:V.map(e=>{let t=B[e];return _(e)?te(e,{disabled:t?.disabled,tooltip:t?.tooltip}):t&&!M(t)?J({label:t.label,handle:t.handleHeadless||t.handle,props:{disabled:t.disabled,tooltip:t.tooltip},additionalKeywords:t.additionalKeywords}):null})}),(0,F.jsx)(u,{})]}),ae=o,oe=T.iterate().map(e=>{if(W.has(e))return null;let t=B[e];return te(e,{disabled:t?.disabled,tooltip:t?.tooltip})}),se=I.map(e=>W.has(e.label)?null:J({label:e.label,handle:e.handleHeadless||e.handle,props:{disabled:e.disabled,tooltip:e.tooltip},additionalKeywords:e.additionalKeywords})),Z;d[18]!==W||d[19]!==J?(Z=e=>W.has(e.label)?null:J({label:`Cell > ${e.label}`,handle:e.handleHeadless||e.handle,props:{disabled:e.disabled,tooltip:e.tooltip},additionalKeywords:e.additionalKeywords}),d[18]=W,d[19]=J,d[20]=Z):Z=d[20];let ce=k.map(Z),Q;d[21]!==ae||d[22]!==se||d[23]!==ce||d[24]!==oe?(Q=(0,F.jsxs)(ae,{heading:`Commands`,children:[oe,se,ce]}),d[21]=ae,d[22]=se,d[23]=ce,d[24]=oe,d[25]=Q):Q=d[25];let $;d[26]!==re||d[27]!==Q||d[28]!==X||d[29]!==ie?($=(0,F.jsxs)(re,{children:[X,ie,Q]}),d[26]=re,d[27]=Q,d[28]=X,d[29]=ie,d[30]=$):$=d[30];let le;return d[31]!==ne||d[32]!==f||d[33]!==p||d[34]!==$||d[35]!==Y?(le=(0,F.jsxs)(ne,{open:f,onOpenChange:p,children:[Y,$]}),d[31]=ne,d[32]=f,d[33]=p,d[34]=$,d[35]=Y,d[36]=le):le=d[36],le};function L(e){return!e.hotkey}function R(e){return e.label}function z(e){return!e}export{I as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{n as e}from"./feedback-button-CngqEjnR.js";export{e as FeedbackModal};
|