@marimo-team/islands 0.18.2 → 0.18.4

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.
Files changed (43) hide show
  1. package/dist/{constants-DWBOe162.js → constants-D_G8vnDk.js} +5 -4
  2. package/dist/{formats-7RSCCoSI.js → formats-Bi_tbdwB.js} +21 -22
  3. package/dist/{glide-data-editor-D-Ia_Jsv.js → glide-data-editor-DXF8E-QD.js} +2 -2
  4. package/dist/main.js +280 -148
  5. package/dist/style.css +1 -1
  6. package/dist/{types-Dunk85GC.js → types-DclGb0Yh.js} +1 -1
  7. package/dist/{vega-component-kU4hFYYJ.js → vega-component-BFcH2SqR.js} +8 -8
  8. package/package.json +1 -1
  9. package/src/components/app-config/user-config-form.tsx +14 -1
  10. package/src/components/data-table/context-menu.tsx +7 -3
  11. package/src/components/data-table/filter-pills.tsx +2 -1
  12. package/src/components/data-table/filters.ts +11 -2
  13. package/src/components/editor/cell/CreateCellButton.tsx +5 -3
  14. package/src/components/editor/cell/collapse.tsx +2 -2
  15. package/src/components/editor/chrome/components/contribute-snippet-button.tsx +22 -103
  16. package/src/components/editor/controls/duplicate-shortcut-banner.tsx +50 -0
  17. package/src/components/editor/controls/keyboard-shortcuts.tsx +25 -2
  18. package/src/components/editor/notebook-banner.tsx +1 -1
  19. package/src/components/editor/notebook-cell.tsx +4 -3
  20. package/src/components/editor/output/__tests__/ansi-reduce.test.ts +6 -6
  21. package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +3 -3
  22. package/src/components/pages/home-page.tsx +6 -0
  23. package/src/components/scratchpad/scratchpad.tsx +2 -1
  24. package/src/core/constants.ts +10 -0
  25. package/src/core/layout/useTogglePresenting.ts +69 -25
  26. package/src/core/state/__mocks__/mocks.ts +1 -0
  27. package/src/hooks/__tests__/useDuplicateShortcuts.test.ts +449 -0
  28. package/src/hooks/useDuplicateShortcuts.ts +145 -0
  29. package/src/plugins/impl/NumberPlugin.tsx +1 -1
  30. package/src/plugins/impl/__tests__/NumberPlugin.test.tsx +1 -1
  31. package/src/plugins/impl/anywidget/AnyWidgetPlugin.tsx +67 -47
  32. package/src/plugins/impl/anywidget/__tests__/AnyWidgetPlugin.test.tsx +2 -57
  33. package/src/plugins/impl/anywidget/__tests__/model.test.ts +23 -19
  34. package/src/plugins/impl/anywidget/model.ts +68 -41
  35. package/src/plugins/impl/data-frames/utils/__tests__/operators.test.ts +2 -0
  36. package/src/plugins/impl/data-frames/utils/operators.ts +1 -0
  37. package/src/plugins/impl/vega/vega.css +5 -0
  38. package/src/plugins/layout/NavigationMenuPlugin.tsx +24 -22
  39. package/src/plugins/layout/StatPlugin.tsx +43 -23
  40. package/src/utils/__tests__/data-views.test.ts +495 -13
  41. package/src/utils/__tests__/json-parser.test.ts +1 -1
  42. package/src/utils/data-views.ts +134 -16
  43. package/src/utils/json/base64.ts +8 -0
@@ -2,47 +2,91 @@
2
2
 
3
3
  import { useSetAtom } from "jotai";
4
4
  import { useCallback } from "react";
5
+ import { Logger } from "@/utils/Logger";
5
6
  import { type CellId, HTMLCellId } from "../cells/ids";
7
+ import { CSSClasses } from "../constants";
6
8
  import { toggleAppMode, viewStateAtom } from "../mode";
7
9
 
10
+ interface ScrollAnchor {
11
+ cellId: CellId;
12
+ }
13
+
14
+ function findScrollAnchor(): ScrollAnchor | null {
15
+ const outputAreas = document.getElementsByClassName(CSSClasses.outputArea);
16
+
17
+ for (const elem of Array.from(outputAreas)) {
18
+ const rect = elem.getBoundingClientRect();
19
+
20
+ // Find first visible output area
21
+ if (rect.bottom > 0 && rect.top < window.innerHeight) {
22
+ const cellEl = HTMLCellId.findElement(elem);
23
+ if (!cellEl) {
24
+ Logger.warn("Could not find HTMLCellId for visible output area", elem);
25
+ continue;
26
+ }
27
+ return {
28
+ cellId: HTMLCellId.parse(cellEl.id),
29
+ };
30
+ }
31
+ }
32
+
33
+ Logger.warn("No visible output area found for scroll anchor");
34
+ return null;
35
+ }
36
+
37
+ function restoreScrollPosition(anchor: ScrollAnchor | null): void {
38
+ if (!anchor) {
39
+ Logger.warn("No scroll anchor provided to restore scroll position");
40
+ return;
41
+ }
42
+
43
+ // Find the cell element
44
+ const cellElement = document.getElementById(HTMLCellId.create(anchor.cellId));
45
+ if (!cellElement) {
46
+ Logger.warn(
47
+ "Could not find cell element to restore scroll position",
48
+ anchor.cellId,
49
+ );
50
+ return;
51
+ }
52
+
53
+ // Find its output area
54
+ const outputArea = cellElement.querySelector(`.${CSSClasses.outputArea}`);
55
+ if (!outputArea) {
56
+ Logger.warn(
57
+ "Could not find output area to restore scroll position",
58
+ anchor.cellId,
59
+ );
60
+ return;
61
+ }
62
+
63
+ // Adjust scroll to restore visual position
64
+ cellElement.scrollIntoView({ block: "start", behavior: "auto" });
65
+ }
66
+
8
67
  /**
9
68
  * Toggle the notebook's presentation state and scroll to current visible cell
10
69
  */
11
70
  export function useTogglePresenting() {
12
71
  const setViewState = useSetAtom(viewStateAtom);
13
72
 
14
- // Toggle the array's presenting state, and sets a cell to scroll to
73
+ // Toggle the array's presenting state and preserve scroll position
15
74
  const togglePresenting = useCallback(() => {
16
- const outputAreas = document.getElementsByClassName("output-area");
17
- const viewportEnd =
18
- window.innerHeight || document.documentElement.clientHeight;
19
- let cellAnchor: CellId | null = null;
20
-
21
- // Find the first output area that is visible
22
- // eslint-disable-next-line unicorn/prefer-spread
23
- for (const elem of Array.from(outputAreas)) {
24
- const rect = elem.getBoundingClientRect();
25
- if (
26
- (rect.top >= 0 && rect.top <= viewportEnd) ||
27
- (rect.bottom >= 0 && rect.bottom <= viewportEnd)
28
- ) {
29
- cellAnchor = HTMLCellId.parse(
30
- (elem.parentNode as HTMLElement).id as HTMLCellId,
31
- );
32
- break;
33
- }
34
- }
75
+ // Capture scroll anchor BEFORE toggle
76
+ const scrollAnchor = findScrollAnchor();
35
77
 
78
+ // Toggle the mode
36
79
  setViewState((prev) => ({
37
80
  mode: toggleAppMode(prev.mode),
38
- cellAnchor: cellAnchor,
81
+ cellAnchor: scrollAnchor?.cellId ?? null,
39
82
  }));
40
83
 
84
+ // Restore scroll position AFTER DOM updates
85
+ // Double RAF ensures React commits changes and browser completes layout
41
86
  requestAnimationFrame(() => {
42
- if (cellAnchor === null) {
43
- return;
44
- }
45
- document.getElementById(HTMLCellId.create(cellAnchor))?.scrollIntoView();
87
+ requestAnimationFrame(() => {
88
+ restoreScrollPosition(scrollAnchor);
89
+ });
46
90
  });
47
91
  }, [setViewState]);
48
92
 
@@ -1,3 +1,4 @@
1
+ /* Copyright 2024 Marimo. All rights reserved. */
1
2
  import type { Observable } from "../observable";
2
3
 
3
4
  export function createMockObservable<T>(
@@ -0,0 +1,449 @@
1
+ /* Copyright 2024 Marimo. All rights reserved. */
2
+ import { describe, expect, it } from "vitest";
3
+ import {
4
+ type Hotkey,
5
+ type HotkeyAction,
6
+ HotkeyProvider,
7
+ } from "@/core/hotkeys/hotkeys";
8
+ import {
9
+ findDuplicateShortcuts,
10
+ normalizeShortcutKey,
11
+ } from "../useDuplicateShortcuts";
12
+
13
+ /**
14
+ * Helper to create a minimal hotkey configuration for testing.
15
+ */
16
+ function createHotkeys(
17
+ keys: Partial<Record<HotkeyAction, Hotkey>>,
18
+ ): Record<HotkeyAction, Hotkey> {
19
+ return new Proxy(keys as Record<HotkeyAction, Hotkey>, {
20
+ // biome-ignore lint: ok to have three arguments here (It's a web API)
21
+ get(target, p, receiver) {
22
+ const key = Reflect.get(target, p, receiver);
23
+ if (key === "undefined") {
24
+ throw new Error("Missing required hotkey.");
25
+ }
26
+ return key;
27
+ },
28
+ });
29
+ }
30
+
31
+ describe("normalizeShortcutKey", () => {
32
+ it("should convert to lowercase", () => {
33
+ expect(normalizeShortcutKey("Ctrl-Shift-A")).toBe("ctrl-shift-a");
34
+ expect(normalizeShortcutKey("MOD-ENTER")).toBe("mod-enter");
35
+ });
36
+
37
+ it("should replace + with -", () => {
38
+ expect(normalizeShortcutKey("Ctrl+Shift+A")).toBe("ctrl-shift-a");
39
+ expect(normalizeShortcutKey("Cmd+Enter")).toBe("cmd-enter");
40
+ });
41
+
42
+ it("should trim whitespace", () => {
43
+ expect(normalizeShortcutKey(" Ctrl-A ")).toBe("ctrl-a");
44
+ expect(normalizeShortcutKey(" Mod-Enter ")).toBe("mod-enter");
45
+ });
46
+
47
+ it("should handle mixed separators", () => {
48
+ expect(normalizeShortcutKey("Ctrl+Shift-A")).toBe("ctrl-shift-a");
49
+ expect(normalizeShortcutKey("Mod-Alt+K")).toBe("mod-alt-k");
50
+ });
51
+ });
52
+
53
+ describe("findDuplicateShortcuts", () => {
54
+ it("should detect no duplicates when all shortcuts are unique", () => {
55
+ const hotkeys = createHotkeys({
56
+ "cell.run": {
57
+ name: "Run cell",
58
+ group: "Running Cells",
59
+ key: "Mod-Enter",
60
+ },
61
+ "cell.format": {
62
+ name: "Format cell",
63
+ group: "Editing",
64
+ key: "Mod-b",
65
+ },
66
+ "cell.delete": {
67
+ name: "Delete cell",
68
+ group: "Editing",
69
+ key: "Shift-Backspace",
70
+ },
71
+ });
72
+
73
+ const provider = new HotkeyProvider(hotkeys, { platform: "mac" });
74
+ const result = findDuplicateShortcuts(provider);
75
+
76
+ expect(result.duplicates).toHaveLength(0);
77
+ expect(result.hasDuplicate("cell.run")).toBe(false);
78
+ expect(result.hasDuplicate("cell.format")).toBe(false);
79
+ expect(result.hasDuplicate("cell.delete")).toBe(false);
80
+ });
81
+
82
+ it("should detect duplicates when two actions share the same key", () => {
83
+ const hotkeys = createHotkeys({
84
+ "cell.format": {
85
+ name: "Format cell",
86
+ group: "Editing",
87
+ key: "Mod-b",
88
+ },
89
+ "markdown.bold": {
90
+ name: "Bold",
91
+ group: "Markdown",
92
+ key: "Mod-b",
93
+ },
94
+ "cell.run": {
95
+ name: "Run cell",
96
+ group: "Running Cells",
97
+ key: "Mod-Enter",
98
+ },
99
+ });
100
+
101
+ const provider = new HotkeyProvider(hotkeys, { platform: "mac" });
102
+ const result = findDuplicateShortcuts(provider);
103
+
104
+ expect(result.duplicates).toHaveLength(1);
105
+ expect(result.duplicates[0].key).toBe("cmd-b");
106
+ expect(result.duplicates[0].actions).toHaveLength(2);
107
+
108
+ expect(result.hasDuplicate("cell.format")).toBe(true);
109
+ expect(result.hasDuplicate("markdown.bold")).toBe(true);
110
+ expect(result.hasDuplicate("cell.run")).toBe(false);
111
+ });
112
+
113
+ it("should detect multiple duplicate groups", () => {
114
+ const hotkeys = createHotkeys({
115
+ "cell.format": {
116
+ name: "Format cell",
117
+ group: "Editing",
118
+ key: "Mod-b",
119
+ },
120
+ "markdown.bold": {
121
+ name: "Bold",
122
+ group: "Markdown",
123
+ key: "Mod-b",
124
+ },
125
+ "cell.run": {
126
+ name: "Run cell",
127
+ group: "Running Cells",
128
+ key: "Mod-Enter",
129
+ },
130
+ "cell.complete": {
131
+ name: "Code completion",
132
+ group: "Editing",
133
+ key: "Ctrl-Space",
134
+ },
135
+ "cell.signatureHelp": {
136
+ name: "Signature help",
137
+ group: "Editing",
138
+ key: "Mod-Enter",
139
+ },
140
+ });
141
+
142
+ const provider = new HotkeyProvider(hotkeys, { platform: "mac" });
143
+ const result = findDuplicateShortcuts(provider);
144
+
145
+ expect(result.duplicates).toHaveLength(2);
146
+
147
+ // Check that both duplicate groups are detected
148
+ const duplicateKeys = result.duplicates.map((d) => d.key).sort();
149
+ expect(duplicateKeys).toEqual(["cmd-b", "cmd-enter"]);
150
+
151
+ expect(result.hasDuplicate("cell.format")).toBe(true);
152
+ expect(result.hasDuplicate("markdown.bold")).toBe(true);
153
+ expect(result.hasDuplicate("cell.run")).toBe(true);
154
+ expect(result.hasDuplicate("cell.signatureHelp")).toBe(true);
155
+ expect(result.hasDuplicate("cell.complete")).toBe(false);
156
+ });
157
+
158
+ it("should handle three or more actions with the same key", () => {
159
+ const hotkeys = createHotkeys({
160
+ "cell.format": {
161
+ name: "Format cell",
162
+ group: "Editing",
163
+ key: "Mod-k",
164
+ },
165
+ "markdown.link": {
166
+ name: "Convert to Link",
167
+ group: "Markdown",
168
+ key: "Mod-k",
169
+ },
170
+ "global.commandPalette": {
171
+ name: "Show command palette",
172
+ group: "Other",
173
+ key: "Mod-k",
174
+ },
175
+ });
176
+
177
+ const provider = new HotkeyProvider(hotkeys, { platform: "mac" });
178
+ const result = findDuplicateShortcuts(provider);
179
+
180
+ expect(result.duplicates).toHaveLength(1);
181
+ expect(result.duplicates[0].actions).toHaveLength(3);
182
+ expect(result.duplicates[0].key).toBe("cmd-k");
183
+ });
184
+
185
+ it("should ignore empty or unset shortcuts", () => {
186
+ const hotkeys = createHotkeys({
187
+ "cell.run": {
188
+ name: "Run cell",
189
+ group: "Running Cells",
190
+ key: "Mod-Enter",
191
+ },
192
+ "global.runAll": {
193
+ name: "Re-run all cells",
194
+ group: "Running Cells",
195
+ key: "",
196
+ },
197
+ "cell.format": {
198
+ name: "Format cell",
199
+ group: "Editing",
200
+ key: "Mod-b",
201
+ },
202
+ });
203
+
204
+ const provider = new HotkeyProvider(hotkeys, { platform: "mac" });
205
+ const result = findDuplicateShortcuts(provider);
206
+
207
+ expect(result.duplicates).toHaveLength(0);
208
+ expect(result.hasDuplicate("global.runAll")).toBe(false);
209
+ });
210
+
211
+ it("should normalize keys for comparison (case and separator insensitive)", () => {
212
+ const hotkeys = createHotkeys({
213
+ "cell.format": {
214
+ name: "Format cell",
215
+ group: "Editing",
216
+ key: "Cmd-B",
217
+ },
218
+ "markdown.bold": {
219
+ name: "Bold",
220
+ group: "Markdown",
221
+ key: "cmd+b",
222
+ },
223
+ });
224
+
225
+ const provider = new HotkeyProvider(hotkeys, { platform: "mac" });
226
+ const result = findDuplicateShortcuts(provider);
227
+
228
+ expect(result.duplicates).toHaveLength(1);
229
+ expect(result.duplicates[0].actions).toHaveLength(2);
230
+ });
231
+
232
+ it("getDuplicatesFor should return other actions with the same key", () => {
233
+ const hotkeys = createHotkeys({
234
+ "cell.format": {
235
+ name: "Format cell",
236
+ group: "Editing",
237
+ key: "Mod-b",
238
+ },
239
+ "markdown.bold": {
240
+ name: "Bold",
241
+ group: "Markdown",
242
+ key: "Mod-b",
243
+ },
244
+ "markdown.italic": {
245
+ name: "Italic",
246
+ group: "Markdown",
247
+ key: "Mod-i",
248
+ },
249
+ });
250
+
251
+ const provider = new HotkeyProvider(hotkeys, { platform: "mac" });
252
+ const result = findDuplicateShortcuts(provider);
253
+
254
+ const duplicatesForFormat = result.getDuplicatesFor("cell.format");
255
+ expect(duplicatesForFormat).toEqual(["markdown.bold"]);
256
+
257
+ const duplicatesForBold = result.getDuplicatesFor("markdown.bold");
258
+ expect(duplicatesForBold).toEqual(["cell.format"]);
259
+
260
+ const duplicatesForItalic = result.getDuplicatesFor("markdown.italic");
261
+ expect(duplicatesForItalic).toEqual([]);
262
+ });
263
+
264
+ it("getDuplicatesFor should return all other actions when three or more share a key", () => {
265
+ const hotkeys = createHotkeys({
266
+ "cell.format": {
267
+ name: "Format cell",
268
+ group: "Editing",
269
+ key: "Mod-k",
270
+ },
271
+ "markdown.link": {
272
+ name: "Convert to Link",
273
+ group: "Markdown",
274
+ key: "Mod-k",
275
+ },
276
+ "global.commandPalette": {
277
+ name: "Show command palette",
278
+ group: "Other",
279
+ key: "Mod-k",
280
+ },
281
+ });
282
+
283
+ const provider = new HotkeyProvider(hotkeys, { platform: "mac" });
284
+ const result = findDuplicateShortcuts(provider);
285
+
286
+ const duplicatesForFormat = result.getDuplicatesFor("cell.format");
287
+ expect(duplicatesForFormat).toHaveLength(2);
288
+ expect(duplicatesForFormat).toContain("markdown.link");
289
+ expect(duplicatesForFormat).toContain("global.commandPalette");
290
+ });
291
+
292
+ it("should respect platform-specific key overrides", () => {
293
+ const hotkeys = createHotkeys({
294
+ "cell.format": {
295
+ name: "Format cell",
296
+ group: "Editing",
297
+ key: {
298
+ main: "Mod-Shift-F",
299
+ mac: "Cmd-Option-F",
300
+ windows: "Ctrl-Alt-F",
301
+ },
302
+ },
303
+ "markdown.bold": {
304
+ name: "Bold",
305
+ group: "Markdown",
306
+ key: "Cmd-Option-F", // Duplicate on Mac only
307
+ },
308
+ });
309
+
310
+ const macProvider = new HotkeyProvider(hotkeys, { platform: "mac" });
311
+ const macResult = findDuplicateShortcuts(macProvider);
312
+ expect(macResult.duplicates).toHaveLength(1);
313
+ expect(macResult.hasDuplicate("cell.format")).toBe(true);
314
+ expect(macResult.hasDuplicate("markdown.bold")).toBe(true);
315
+
316
+ const windowsProvider = new HotkeyProvider(hotkeys, {
317
+ platform: "windows",
318
+ });
319
+ const windowsResult = findDuplicateShortcuts(windowsProvider);
320
+ expect(windowsResult.duplicates).toHaveLength(0);
321
+ expect(windowsResult.hasDuplicate("cell.format")).toBe(false);
322
+ expect(windowsResult.hasDuplicate("markdown.bold")).toBe(false);
323
+ });
324
+
325
+ describe("ignoreGroup parameter", () => {
326
+ it("should ignore duplicates from the specified group", () => {
327
+ const hotkeys = createHotkeys({
328
+ "cell.format": {
329
+ name: "Format cell",
330
+ group: "Editing",
331
+ key: "Mod-b",
332
+ },
333
+ "markdown.bold": {
334
+ name: "Bold",
335
+ group: "Markdown",
336
+ key: "Mod-b",
337
+ },
338
+ "cell.run": {
339
+ name: "Run cell",
340
+ group: "Running Cells",
341
+ key: "Mod-Enter",
342
+ },
343
+ });
344
+
345
+ const provider = new HotkeyProvider(hotkeys, { platform: "mac" });
346
+ const result = findDuplicateShortcuts(provider, "Markdown");
347
+
348
+ // markdown.bold should be ignored, so no duplicates should be found
349
+ expect(result.duplicates).toHaveLength(0);
350
+ expect(result.hasDuplicate("cell.format")).toBe(false);
351
+ expect(result.hasDuplicate("markdown.bold")).toBe(false);
352
+ });
353
+
354
+ it("should still detect duplicates outside the ignored group", () => {
355
+ const hotkeys = createHotkeys({
356
+ "cell.format": {
357
+ name: "Format cell",
358
+ group: "Editing",
359
+ key: "Mod-b",
360
+ },
361
+ "markdown.bold": {
362
+ name: "Bold",
363
+ group: "Markdown",
364
+ key: "Mod-b",
365
+ },
366
+ "cell.run": {
367
+ name: "Run cell",
368
+ group: "Running Cells",
369
+ key: "Mod-Enter",
370
+ },
371
+ "cell.complete": {
372
+ name: "Code completion",
373
+ group: "Editing",
374
+ key: "Mod-Enter",
375
+ },
376
+ });
377
+
378
+ const provider = new HotkeyProvider(hotkeys, { platform: "mac" });
379
+ const result = findDuplicateShortcuts(provider, "Markdown");
380
+
381
+ // markdown.bold is ignored, but cell.run and cell.complete should still be detected
382
+ expect(result.duplicates).toHaveLength(1);
383
+ expect(result.duplicates[0].key).toBe("cmd-enter");
384
+ expect(result.hasDuplicate("cell.run")).toBe(true);
385
+ expect(result.hasDuplicate("cell.complete")).toBe(true);
386
+ expect(result.hasDuplicate("markdown.bold")).toBe(false);
387
+ expect(result.hasDuplicate("cell.format")).toBe(false);
388
+ });
389
+
390
+ it("should handle ignoring a group that doesn't exist", () => {
391
+ const hotkeys = createHotkeys({
392
+ "cell.format": {
393
+ name: "Format cell",
394
+ group: "Editing",
395
+ key: "Mod-b",
396
+ },
397
+ "markdown.bold": {
398
+ name: "Bold",
399
+ group: "Markdown",
400
+ key: "Mod-b",
401
+ },
402
+ });
403
+
404
+ const provider = new HotkeyProvider(hotkeys, { platform: "mac" });
405
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
406
+ const result = findDuplicateShortcuts(provider, "NonExistent" as any);
407
+
408
+ // Should still work normally and detect the duplicate
409
+ expect(result.duplicates).toHaveLength(1);
410
+ expect(result.hasDuplicate("cell.format")).toBe(true);
411
+ expect(result.hasDuplicate("markdown.bold")).toBe(true);
412
+ });
413
+
414
+ it("should ignore multiple actions from the same group", () => {
415
+ const hotkeys = createHotkeys({
416
+ "cell.format": {
417
+ name: "Format cell",
418
+ group: "Editing",
419
+ key: "Mod-b",
420
+ },
421
+ "markdown.bold": {
422
+ name: "Bold",
423
+ group: "Markdown",
424
+ key: "Mod-b",
425
+ },
426
+ "markdown.italic": {
427
+ name: "Italic",
428
+ group: "Markdown",
429
+ key: "Mod-i",
430
+ },
431
+ "cell.hideCode": {
432
+ name: "Hide cell code",
433
+ group: "Editing",
434
+ key: "Mod-i",
435
+ },
436
+ });
437
+
438
+ const provider = new HotkeyProvider(hotkeys, { platform: "mac" });
439
+ const result = findDuplicateShortcuts(provider, "Markdown");
440
+
441
+ // Both markdown actions should be ignored
442
+ expect(result.duplicates).toHaveLength(0);
443
+ expect(result.hasDuplicate("cell.format")).toBe(false);
444
+ expect(result.hasDuplicate("cell.hideCode")).toBe(false);
445
+ expect(result.hasDuplicate("markdown.bold")).toBe(false);
446
+ expect(result.hasDuplicate("markdown.italic")).toBe(false);
447
+ });
448
+ });
449
+ });