@marimo-team/islands 0.20.5-dev24 → 0.20.5-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.
Files changed (32) hide show
  1. package/dist/{Combination-DPrRlMyq.js → Combination-Dk6JxauT.js} +1 -1
  2. package/dist/{ConnectedDataExplorerComponent-Bp9TusI-.js → ConnectedDataExplorerComponent-jj3D9AQT.js} +10 -10
  3. package/dist/{any-language-editor-BGcQxkAL.js → any-language-editor-BIj11a2e.js} +4 -4
  4. package/dist/{button-BJaX_M34.js → button-DQpBib29.js} +17 -9
  5. package/dist/{check-C9c-GtvX.js → check-DpqPQmzz.js} +1 -1
  6. package/dist/{copy-DhB8Zxuz.js → copy-BkBF0Xgk.js} +2 -2
  7. package/dist/{dist-DpeHj-vP.js → dist-WIWVvdBh.js} +2 -2
  8. package/dist/{error-banner-CJWv_j2x.js → error-banner-BctofTCP.js} +2 -2
  9. package/dist/{esm-DxAOF89q.js → esm-BBkPJL8N.js} +4 -4
  10. package/dist/{glide-data-editor-C2dhh3XY.js → glide-data-editor-B03jwvVe.js} +6 -6
  11. package/dist/{label-kL_UbziI.js → label-D036zdVU.js} +4 -4
  12. package/dist/{loader-C6xHQhFz.js → loader-C62dRCuy.js} +1 -1
  13. package/dist/main.js +128 -40
  14. package/dist/{mermaid-CzRLRASw.js → mermaid-BgeZPIms.js} +5 -5
  15. package/dist/{slides-component-D9AVSv2B.js → slides-component-DwvL_HJi.js} +2 -2
  16. package/dist/{spec-CXpiliig.js → spec-BI4Eyctn.js} +4 -4
  17. package/dist/{tooltip-CRsmtPeF.js → tooltip-SPkubVH3.js} +3 -3
  18. package/dist/{types-ChPKdlru.js → types-DqFw5XXR.js} +6 -6
  19. package/dist/{useAsyncData-CHYjZCox.js → useAsyncData-Ioeh75f8.js} +1 -1
  20. package/dist/{useDeepCompareMemoize-C1YH9SDF.js → useDeepCompareMemoize-DtbTAJq3.js} +4 -4
  21. package/dist/{useIframeCapabilities-Cc4UBysp.js → useIframeCapabilities-DFGZKWkO.js} +1 -1
  22. package/dist/{useTheme-B-ZI3PUl.js → useTheme-OvBNH9t3.js} +2 -2
  23. package/dist/{vega-component-BX_oDKB3.js → vega-component-B_4Lp3hK.js} +8 -8
  24. package/package.json +1 -1
  25. package/src/components/editor/actions/types.ts +6 -1
  26. package/src/components/editor/actions/useNotebookActions.tsx +16 -11
  27. package/src/components/editor/chrome/types.ts +17 -0
  28. package/src/components/editor/controls/command-palette.tsx +7 -0
  29. package/src/components/ui/command.tsx +2 -0
  30. package/src/core/hotkeys/hotkeys.ts +11 -1
  31. package/src/utils/__tests__/smartMatch.test.ts +61 -0
  32. package/src/utils/smartMatch.ts +62 -0
@@ -379,17 +379,20 @@ export function useNotebookActions() {
379
379
  label: "Helper panel",
380
380
  redundant: true,
381
381
  handle: NOOP_HANDLER,
382
- dropdown: PANELS.flatMap(({ type: id, Icon, hidden }) => {
383
- if (hidden) {
384
- return [];
385
- }
386
- return {
387
- label: startCase(id),
388
- rightElement: renderCheckboxElement(selectedPanel === id),
389
- icon: <Icon size={14} strokeWidth={1.5} />,
390
- handle: () => toggleApplication(id),
391
- };
392
- }),
382
+ dropdown: PANELS.flatMap(
383
+ ({ type: id, Icon, hidden, additionalKeywords }) => {
384
+ if (hidden) {
385
+ return [];
386
+ }
387
+ return {
388
+ label: startCase(id),
389
+ rightElement: renderCheckboxElement(selectedPanel === id),
390
+ icon: <Icon size={14} strokeWidth={1.5} />,
391
+ handle: () => toggleApplication(id),
392
+ additionalKeywords,
393
+ };
394
+ },
395
+ ),
393
396
  },
394
397
 
395
398
  {
@@ -510,6 +513,7 @@ export function useNotebookActions() {
510
513
  label: "Restart kernel",
511
514
  variant: "danger",
512
515
  handle: restartKernel,
516
+ additionalKeywords: ["reset", "reload", "restart"],
513
517
  },
514
518
  {
515
519
  icon: <FastForwardIcon size={14} strokeWidth={1.5} />,
@@ -567,6 +571,7 @@ export function useNotebookActions() {
567
571
  label: "User settings",
568
572
  handle: () => setSettingsDialogOpen((open) => !open),
569
573
  redundant: true,
574
+ additionalKeywords: ["preferences", "options", "configuration"],
570
575
  },
571
576
  {
572
577
  icon: <ExternalLinkIcon size={14} strokeWidth={1.5} />,
@@ -59,6 +59,8 @@ export interface PanelDescriptor {
59
59
  defaultSection: PanelSection;
60
60
  /** Capability required for this panel to be visible. If the capability is false, the panel is hidden. */
61
61
  requiredCapability?: keyof Capabilities;
62
+ /** Additional search keywords for the command palette */
63
+ additionalKeywords?: string[];
62
64
  }
63
65
 
64
66
  /**
@@ -73,6 +75,7 @@ export const PANELS: PanelDescriptor[] = [
73
75
  label: "Files",
74
76
  tooltip: "View files",
75
77
  defaultSection: "sidebar",
78
+ additionalKeywords: ["explorer", "browser", "directory"],
76
79
  },
77
80
  {
78
81
  type: "variables",
@@ -80,6 +83,7 @@ export const PANELS: PanelDescriptor[] = [
80
83
  label: "Variables",
81
84
  tooltip: "Explore variables and data sources",
82
85
  defaultSection: "sidebar",
86
+ additionalKeywords: ["state", "scope", "inspector"],
83
87
  },
84
88
  {
85
89
  type: "packages",
@@ -87,6 +91,7 @@ export const PANELS: PanelDescriptor[] = [
87
91
  label: "Packages",
88
92
  tooltip: "Manage packages",
89
93
  defaultSection: "sidebar",
94
+ additionalKeywords: ["dependencies", "pip", "install"],
90
95
  },
91
96
  {
92
97
  type: "ai",
@@ -94,6 +99,7 @@ export const PANELS: PanelDescriptor[] = [
94
99
  label: "AI",
95
100
  tooltip: "Chat & Agents",
96
101
  defaultSection: "sidebar",
102
+ additionalKeywords: ["chat", "copilot", "assistant"],
97
103
  },
98
104
  {
99
105
  type: "outline",
@@ -101,6 +107,7 @@ export const PANELS: PanelDescriptor[] = [
101
107
  label: "Outline",
102
108
  tooltip: "View outline",
103
109
  defaultSection: "sidebar",
110
+ additionalKeywords: ["toc", "structure", "headings"],
104
111
  },
105
112
  {
106
113
  type: "documentation",
@@ -108,6 +115,7 @@ export const PANELS: PanelDescriptor[] = [
108
115
  label: "Docs",
109
116
  tooltip: "View live docs",
110
117
  defaultSection: "sidebar",
118
+ additionalKeywords: ["reference", "api"],
111
119
  },
112
120
  {
113
121
  type: "dependencies",
@@ -115,6 +123,7 @@ export const PANELS: PanelDescriptor[] = [
115
123
  label: "Dependencies",
116
124
  tooltip: "Explore dependencies",
117
125
  defaultSection: "sidebar",
126
+ additionalKeywords: ["graph", "imports"],
118
127
  },
119
128
  // Developer panel defaults
120
129
  {
@@ -123,6 +132,7 @@ export const PANELS: PanelDescriptor[] = [
123
132
  label: "Errors",
124
133
  tooltip: "View errors",
125
134
  defaultSection: "developer-panel",
135
+ additionalKeywords: ["exceptions", "problems", "diagnostics"],
126
136
  },
127
137
  {
128
138
  type: "scratchpad",
@@ -130,6 +140,7 @@ export const PANELS: PanelDescriptor[] = [
130
140
  label: "Scratchpad",
131
141
  tooltip: "Scratchpad",
132
142
  defaultSection: "developer-panel",
143
+ additionalKeywords: ["scratch", "draft", "playground"],
133
144
  },
134
145
  {
135
146
  type: "tracing",
@@ -137,6 +148,7 @@ export const PANELS: PanelDescriptor[] = [
137
148
  label: "Tracing",
138
149
  tooltip: "View tracing",
139
150
  defaultSection: "developer-panel",
151
+ additionalKeywords: ["profiling", "performance"],
140
152
  },
141
153
  {
142
154
  type: "secrets",
@@ -145,6 +157,7 @@ export const PANELS: PanelDescriptor[] = [
145
157
  tooltip: "Manage secrets",
146
158
  defaultSection: "developer-panel",
147
159
  hidden: isWasm(),
160
+ additionalKeywords: ["env", "environment", "keys", "credentials"],
148
161
  },
149
162
  {
150
163
  type: "logs",
@@ -152,6 +165,7 @@ export const PANELS: PanelDescriptor[] = [
152
165
  label: "Logs",
153
166
  tooltip: "View logs",
154
167
  defaultSection: "developer-panel",
168
+ additionalKeywords: ["console", "stdout"],
155
169
  },
156
170
  {
157
171
  type: "terminal",
@@ -161,6 +175,7 @@ export const PANELS: PanelDescriptor[] = [
161
175
  hidden: isWasm(),
162
176
  defaultSection: "developer-panel",
163
177
  requiredCapability: "terminal",
178
+ additionalKeywords: ["shell", "console", "bash", "command"],
164
179
  },
165
180
  {
166
181
  type: "snippets",
@@ -168,6 +183,7 @@ export const PANELS: PanelDescriptor[] = [
168
183
  label: "Snippets",
169
184
  tooltip: "Snippets",
170
185
  defaultSection: "developer-panel",
186
+ additionalKeywords: ["templates", "examples"],
171
187
  },
172
188
  {
173
189
  type: "cache",
@@ -176,6 +192,7 @@ export const PANELS: PanelDescriptor[] = [
176
192
  tooltip: "View cache",
177
193
  defaultSection: "developer-panel",
178
194
  hidden: !getFeatureFlag("cache_panel"),
195
+ additionalKeywords: ["memory", "memoize"],
179
196
  },
180
197
  ];
181
198
 
@@ -78,6 +78,7 @@ const CommandPalette = () => {
78
78
  return (
79
79
  <CommandItem
80
80
  disabled={props.disabled}
81
+ keywords={hotkey.additionalKeywords}
81
82
  onSelect={() => {
82
83
  addRecentCommand(shortcut);
83
84
  // Close first and then run the action, so the dialog doesn't steal focus
@@ -105,15 +106,18 @@ const CommandPalette = () => {
105
106
  handle,
106
107
  props = {},
107
108
  hotkey,
109
+ additionalKeywords,
108
110
  }: {
109
111
  label: string;
110
112
  handle: () => void;
111
113
  props?: { disabled?: boolean; tooltip?: React.ReactNode };
112
114
  hotkey?: HotkeyAction;
115
+ additionalKeywords?: string[];
113
116
  }) => {
114
117
  return (
115
118
  <CommandItem
116
119
  disabled={props.disabled}
120
+ keywords={additionalKeywords}
117
121
  onSelect={() => {
118
122
  addRecentCommand(label);
119
123
  setOpen(false);
@@ -163,6 +167,7 @@ const CommandPalette = () => {
163
167
  disabled: action.disabled,
164
168
  tooltip: action.tooltip,
165
169
  },
170
+ additionalKeywords: action.additionalKeywords,
166
171
  });
167
172
  }
168
173
  return null;
@@ -190,6 +195,7 @@ const CommandPalette = () => {
190
195
  label: action.label,
191
196
  handle: action.handleHeadless || action.handle,
192
197
  props: { disabled: action.disabled, tooltip: action.tooltip },
198
+ additionalKeywords: action.additionalKeywords,
193
199
  });
194
200
  })}
195
201
  {cellActions.map((action) => {
@@ -200,6 +206,7 @@ const CommandPalette = () => {
200
206
  label: `Cell > ${action.label}`,
201
207
  handle: action.handleHeadless || action.handle,
202
208
  props: { disabled: action.disabled, tooltip: action.tooltip },
209
+ additionalKeywords: action.additionalKeywords,
203
210
  });
204
211
  })}
205
212
  </CommandGroup>
@@ -7,6 +7,7 @@ import { Search } from "lucide-react";
7
7
  import * as React from "react";
8
8
  import { Dialog, DialogContent } from "@/components/ui/dialog";
9
9
  import { cn } from "@/utils/cn";
10
+ import { smartMatchFilter } from "@/utils/smartMatch";
10
11
  import { Strings } from "@/utils/strings";
11
12
  import {
12
13
  MENU_ITEM_DISABLED,
@@ -21,6 +22,7 @@ const Command = React.forwardRef<
21
22
  >(({ className, ...props }, ref) => (
22
23
  <CommandPrimitive
23
24
  ref={ref}
25
+ filter={smartMatchFilter}
24
26
  className={cn(
25
27
  "flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
26
28
  className,
@@ -27,11 +27,13 @@ export interface Hotkey {
27
27
  * @default true
28
28
  */
29
29
  editable?: boolean;
30
+ additionalKeywords?: string[];
30
31
  }
31
32
 
32
33
  interface ResolvedHotkey {
33
34
  name: string;
34
35
  key: string;
36
+ additionalKeywords?: string[];
35
37
  }
36
38
 
37
39
  type ModKey = "Cmd" | "Ctrl";
@@ -110,6 +112,7 @@ const DEFAULT_HOT_KEY = {
110
112
  name: "Run",
111
113
  group: "Running Cells",
112
114
  key: "Mod-Enter",
115
+ additionalKeywords: ["execute", "submit"],
113
116
  },
114
117
  "cell.runAndNewBelow": {
115
118
  name: "Run and new below",
@@ -132,6 +135,7 @@ const DEFAULT_HOT_KEY = {
132
135
  name: "Format cell",
133
136
  group: "Editing",
134
137
  key: "Mod-b",
138
+ additionalKeywords: ["lint"],
135
139
  },
136
140
  "cell.viewAsMarkdown": {
137
141
  name: "View as Markdown",
@@ -201,6 +205,7 @@ const DEFAULT_HOT_KEY = {
201
205
  name: "Delete cell",
202
206
  group: "Editing",
203
207
  key: "Shift-Backspace",
208
+ additionalKeywords: ["remove"],
204
209
  },
205
210
  "cell.hideCode": {
206
211
  name: "Hide cell code",
@@ -320,6 +325,7 @@ const DEFAULT_HOT_KEY = {
320
325
  name: "Save file",
321
326
  group: "Other",
322
327
  key: "Mod-s",
328
+ additionalKeywords: ["write", "persist"],
323
329
  },
324
330
  "global.commandPalette": {
325
331
  name: "Show command palette",
@@ -485,23 +491,26 @@ export class HotkeyProvider implements IHotkeyProvider {
485
491
  }
486
492
 
487
493
  getHotkey(action: HotkeyAction): ResolvedHotkey {
488
- const { name, key } = this.hotkeys[action];
494
+ const { name, key, additionalKeywords } = this.hotkeys[action];
489
495
  if (typeof key === "string") {
490
496
  return {
491
497
  name,
492
498
  key: key.replace("Mod", this.mod),
499
+ additionalKeywords,
493
500
  };
494
501
  }
495
502
  if (key === NOT_SET) {
496
503
  return {
497
504
  name,
498
505
  key: "",
506
+ additionalKeywords,
499
507
  };
500
508
  }
501
509
  const platformKey = key[this.platform] || key.main;
502
510
  return {
503
511
  name,
504
512
  key: platformKey.replace("Mod", this.mod),
513
+ additionalKeywords,
505
514
  };
506
515
  }
507
516
 
@@ -539,6 +548,7 @@ export class OverridingHotkeyProvider extends HotkeyProvider {
539
548
  return {
540
549
  name: base.name,
541
550
  key: override ? normalizeKeyString(override) : base.key,
551
+ additionalKeywords: base.additionalKeywords,
542
552
  };
543
553
  }
544
554
  }
@@ -0,0 +1,61 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+ import { describe, expect, it } from "vitest";
3
+ import { smartMatch, smartMatchFilter } from "../smartMatch";
4
+
5
+ describe("smartMatch", () => {
6
+ it("matches exact word", () => {
7
+ expect(smartMatch("run", "Run cell")).toBe(true);
8
+ });
9
+
10
+ it("matches word prefix", () => {
11
+ expect(smartMatch("exe", "execute start")).toBe(true);
12
+ });
13
+
14
+ it("matches across array of haystacks", () => {
15
+ expect(smartMatch("exe", ["Run", "execute start"])).toBe(true);
16
+ });
17
+
18
+ it("does not match unrelated terms", () => {
19
+ expect(smartMatch("xyz", "Run cell")).toBe(false);
20
+ });
21
+
22
+ it("empty search matches everything", () => {
23
+ expect(smartMatch("", "anything")).toBe(true);
24
+ });
25
+
26
+ it("multi-word needle requires all words to match", () => {
27
+ expect(smartMatch("run cell", "Run cell")).toBe(true);
28
+ expect(smartMatch("run xyz", "Run cell")).toBe(false);
29
+ });
30
+
31
+ it("is case-insensitive", () => {
32
+ expect(smartMatch("RUN", "run cell")).toBe(true);
33
+ });
34
+
35
+ it("skips null/undefined haystacks", () => {
36
+ expect(smartMatch("run", [null, undefined, "Run cell"])).toBe(true);
37
+ expect(smartMatch("run", [null, undefined])).toBe(false);
38
+ });
39
+ });
40
+
41
+ describe("smartMatchFilter", () => {
42
+ it("returns 1 for value match", () => {
43
+ expect(smartMatchFilter("Run cell", "run")).toBe(1);
44
+ });
45
+
46
+ it("returns 0 for no match", () => {
47
+ expect(smartMatchFilter("Run cell", "xyz")).toBe(0);
48
+ });
49
+
50
+ it("returns 0.8 for keyword-only match", () => {
51
+ expect(smartMatchFilter("Run", "execute", ["execute", "start"])).toBe(0.8);
52
+ });
53
+
54
+ it("returns 1 when value matches even if keywords also match", () => {
55
+ expect(smartMatchFilter("Run", "run", ["execute", "start"])).toBe(1);
56
+ });
57
+
58
+ it("does not match without relevant keywords", () => {
59
+ expect(smartMatchFilter("Run", "preferences", ["execute"])).toBe(0);
60
+ });
61
+ });
@@ -0,0 +1,62 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ /**
4
+ * Split a string into lowercase words (letters/digits only).
5
+ */
6
+ function words(s: string): string[] {
7
+ return s.toLowerCase().match(/[\da-z]+/g) || [];
8
+ }
9
+
10
+ /**
11
+ * Returns true when every word in `needle` is a prefix of at least one word
12
+ * in one of the `haystacks`.
13
+ *
14
+ * Examples:
15
+ * smartMatch("run", "Run cell") // true – "run" prefixes "run"
16
+ * smartMatch("exe", ["Run", "execute start"]) // true – "exe" prefixes "execute"
17
+ * smartMatch("xyz", "Run cell") // false
18
+ */
19
+ export function smartMatch(
20
+ needle: string,
21
+ haystackOrHaystacks: string | (string | null | undefined)[],
22
+ ): boolean {
23
+ const needleWords = words(needle);
24
+ if (needleWords.length === 0) {
25
+ return true; // empty search matches everything
26
+ }
27
+
28
+ const haystacks = Array.isArray(haystackOrHaystacks)
29
+ ? haystackOrHaystacks
30
+ : [haystackOrHaystacks];
31
+
32
+ // Collect all words from all haystacks
33
+ const haystackWords: string[] = [];
34
+ for (const h of haystacks) {
35
+ if (h) {
36
+ haystackWords.push(...words(h));
37
+ }
38
+ }
39
+
40
+ // Every needle word must be a prefix of at least one haystack word
41
+ return needleWords.every((nw) => {
42
+ return haystackWords.some((hw) => hw.startsWith(nw));
43
+ });
44
+ }
45
+
46
+ /**
47
+ * cmdk-compatible filter function.
48
+ * Returns 1 for a value match, 0.8 for a keyword-only match, 0 for no match.
49
+ */
50
+ export function smartMatchFilter(
51
+ value: string,
52
+ search: string,
53
+ keywords?: string[],
54
+ ): number {
55
+ if (smartMatch(search, value)) {
56
+ return 1;
57
+ }
58
+ if (keywords && smartMatch(search, keywords)) {
59
+ return 0.8;
60
+ }
61
+ return 0;
62
+ }