modern-cmdk 1.2.1 → 1.2.3
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/README.md +9 -5
- package/dist/codemod/cli.d.mts +1 -1
- package/dist/codemod/cli.mjs +14677 -61044
- package/dist/codemod/transforms/data-attrs.d.mts +4 -8
- package/dist/codemod/transforms/data-attrs.mjs +1 -2
- package/dist/codemod/transforms/forward-ref.d.mts +4 -8
- package/dist/codemod/transforms/forward-ref.mjs +1 -2
- package/dist/codemod/transforms/import-rewrite.d.mts +4 -8
- package/dist/codemod/transforms/import-rewrite.mjs +1 -2
- package/dist/codemod/transforms/should-filter.d.mts +3 -7
- package/dist/codemod/transforms/should-filter.mjs +1 -2
- package/dist/core/index.d.mts +149 -181
- package/dist/core/index.mjs +42 -47
- package/dist/react/index.d.mts +78 -217
- package/dist/react/index.mjs +141 -166
- package/package.json +8 -8
package/dist/react/index.mjs
CHANGED
|
@@ -4,10 +4,10 @@ import { Component, Suspense, createContext, use, useCallback, useEffect, useId,
|
|
|
4
4
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
import { Dialog } from "radix-ui";
|
|
6
6
|
import { createPortal } from "react-dom";
|
|
7
|
-
|
|
7
|
+
// src/react/activity.tsx
|
|
8
8
|
const ActivityComponent = "Activity" in React ? React.Activity : null;
|
|
9
9
|
/**
|
|
10
|
-
* Wraps content in React
|
|
10
|
+
* Wraps content in React's <Activity> API (React 19.2+).
|
|
11
11
|
* When mode="hidden", the component tree is preserved in memory but not rendered.
|
|
12
12
|
* Falls back to conditional rendering if Activity is unavailable.
|
|
13
13
|
*/
|
|
@@ -19,8 +19,7 @@ function CommandActivity({ mode, children }) {
|
|
|
19
19
|
if (mode === "hidden") return null;
|
|
20
20
|
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
21
21
|
}
|
|
22
|
-
|
|
23
|
-
//#region src/react/context.ts
|
|
22
|
+
// src/react/context.ts
|
|
24
23
|
const CommandStableContext = createContext(null);
|
|
25
24
|
CommandStableContext.displayName = "CommandStableContext";
|
|
26
25
|
const CommandStateContext = createContext(null);
|
|
@@ -42,8 +41,7 @@ CommandGroupContext.displayName = "CommandGroupContext";
|
|
|
42
41
|
/** Context for the status container rendered outside the listbox — used by Command.Empty */
|
|
43
42
|
const CommandListStatusContext = createContext(null);
|
|
44
43
|
CommandListStatusContext.displayName = "CommandListStatusContext";
|
|
45
|
-
|
|
46
|
-
//#region src/react/async-items.tsx
|
|
44
|
+
// src/react/async-items.tsx
|
|
47
45
|
/** Inner component that suspends via use() until items resolve */
|
|
48
46
|
function AsyncItemsInner({ items: itemsPromise, children }) {
|
|
49
47
|
const stable = useStableContext("Command.AsyncItems");
|
|
@@ -68,8 +66,7 @@ function CommandAsyncItems({ items, fallback, children }) {
|
|
|
68
66
|
})
|
|
69
67
|
});
|
|
70
68
|
}
|
|
71
|
-
|
|
72
|
-
//#region src/react/badge.tsx
|
|
69
|
+
// src/react/badge.tsx
|
|
73
70
|
/** Inline badge for categorization or status indicators on command items. */
|
|
74
71
|
function CommandBadge({ ref, children, ...props }) {
|
|
75
72
|
return /* @__PURE__ */ jsx("span", {
|
|
@@ -79,8 +76,7 @@ function CommandBadge({ ref, children, ...props }) {
|
|
|
79
76
|
children
|
|
80
77
|
});
|
|
81
78
|
}
|
|
82
|
-
|
|
83
|
-
//#region src/core/utils/string-wellformed.ts
|
|
79
|
+
// src/core/utils/string-wellformed.ts
|
|
84
80
|
/**
|
|
85
81
|
* Regex matching lone surrogates (unpaired high or low surrogates).
|
|
86
82
|
* A lone high surrogate (U+D800–U+DBFF) not followed by a low surrogate,
|
|
@@ -97,84 +93,7 @@ const REPLACEMENT_CHAR = "�";
|
|
|
97
93
|
function ensureWellFormed(str) {
|
|
98
94
|
return str.replace(LONE_SURROGATE_RE, REPLACEMENT_CHAR);
|
|
99
95
|
}
|
|
100
|
-
|
|
101
|
-
//#region src/core/keyboard/parser.ts
|
|
102
|
-
const isMac = typeof navigator !== "undefined" && (navigator.userAgentData?.platform === "macOS" || /Mac|iPod|iPhone|iPad/.test(navigator.platform));
|
|
103
|
-
/** Modifier aliases — cross-platform Mod resolution */
|
|
104
|
-
const MODIFIER_MAP = {
|
|
105
|
-
mod: isMac ? "meta" : "ctrl",
|
|
106
|
-
meta: "meta",
|
|
107
|
-
cmd: "meta",
|
|
108
|
-
command: "meta",
|
|
109
|
-
ctrl: "ctrl",
|
|
110
|
-
control: "ctrl",
|
|
111
|
-
shift: "shift",
|
|
112
|
-
alt: "alt",
|
|
113
|
-
option: "alt",
|
|
114
|
-
opt: "alt"
|
|
115
|
-
};
|
|
116
|
-
/**
|
|
117
|
-
* Parse a shortcut string like "Mod+Shift+K" into a structured ParsedShortcut.
|
|
118
|
-
* Ensures valid Unicode input via ensureWellFormed helper.
|
|
119
|
-
*/
|
|
120
|
-
function parseShortcut(shortcut) {
|
|
121
|
-
const safeShortcut = ensureWellFormed(shortcut);
|
|
122
|
-
const parts = safeShortcut.split("+").map((p) => p.trim().toLowerCase());
|
|
123
|
-
let meta = false;
|
|
124
|
-
let ctrl = false;
|
|
125
|
-
let shift = false;
|
|
126
|
-
let alt = false;
|
|
127
|
-
let key = "";
|
|
128
|
-
for (const part of parts) {
|
|
129
|
-
const modifier = MODIFIER_MAP[part];
|
|
130
|
-
if (modifier) switch (modifier) {
|
|
131
|
-
case "meta":
|
|
132
|
-
meta = true;
|
|
133
|
-
break;
|
|
134
|
-
case "ctrl":
|
|
135
|
-
ctrl = true;
|
|
136
|
-
break;
|
|
137
|
-
case "shift":
|
|
138
|
-
shift = true;
|
|
139
|
-
break;
|
|
140
|
-
case "alt":
|
|
141
|
-
alt = true;
|
|
142
|
-
break;
|
|
143
|
-
}
|
|
144
|
-
else key = part;
|
|
145
|
-
}
|
|
146
|
-
const modifiers = [];
|
|
147
|
-
if (meta) modifiers.push("meta");
|
|
148
|
-
if (ctrl) modifiers.push("ctrl");
|
|
149
|
-
if (shift) modifiers.push("shift");
|
|
150
|
-
if (alt) modifiers.push("alt");
|
|
151
|
-
modifiers.push(key);
|
|
152
|
-
return {
|
|
153
|
-
key,
|
|
154
|
-
meta,
|
|
155
|
-
ctrl,
|
|
156
|
-
shift,
|
|
157
|
-
alt,
|
|
158
|
-
raw: safeShortcut,
|
|
159
|
-
normalized: modifiers.join("+")
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* Formats a ParsedShortcut for display using platform-appropriate symbols.
|
|
164
|
-
* On macOS: renders glyphs (⌘, ⇧, ⌥, ⌃). On other platforms: text labels joined by "+".
|
|
165
|
-
*/
|
|
166
|
-
function formatShortcut(parsed) {
|
|
167
|
-
const parts = [];
|
|
168
|
-
if (parsed.meta) parts.push(isMac ? "⌘" : "Ctrl");
|
|
169
|
-
if (parsed.ctrl && !(!isMac && parsed.meta)) parts.push(isMac ? "⌃" : "Ctrl");
|
|
170
|
-
if (parsed.shift) parts.push(isMac ? "⇧" : "Shift");
|
|
171
|
-
if (parsed.alt) parts.push(isMac ? "⌥" : "Alt");
|
|
172
|
-
const displayKey = parsed.key.length === 1 ? parsed.key.toUpperCase() : parsed.key.charAt(0).toUpperCase() + parsed.key.slice(1);
|
|
173
|
-
parts.push(displayKey);
|
|
174
|
-
return isMac ? parts.join("") : parts.join("+");
|
|
175
|
-
}
|
|
176
|
-
//#endregion
|
|
177
|
-
//#region src/core/types.ts
|
|
96
|
+
// src/core/types.ts
|
|
178
97
|
/** Create a branded {@link ItemId} from a plain string. Ensures well-formed Unicode. */
|
|
179
98
|
function itemId(id) {
|
|
180
99
|
return ensureWellFormed(id);
|
|
@@ -206,8 +125,7 @@ function createInitialState(options) {
|
|
|
206
125
|
lastUpdated: Date.now()
|
|
207
126
|
};
|
|
208
127
|
}
|
|
209
|
-
|
|
210
|
-
//#region src/core/frecency/memory-storage.ts
|
|
128
|
+
// src/core/frecency/memory-storage.ts
|
|
211
129
|
/**
|
|
212
130
|
* In-memory implementation of FrecencyStorage using a Map.
|
|
213
131
|
* Data does not persist across page reloads. Suitable for ephemeral
|
|
@@ -229,8 +147,7 @@ var MemoryFrecencyStorage = class {
|
|
|
229
147
|
this.#store.clear();
|
|
230
148
|
}
|
|
231
149
|
};
|
|
232
|
-
|
|
233
|
-
//#region src/core/frecency/index.ts
|
|
150
|
+
// src/core/frecency/index.ts
|
|
234
151
|
/** Shared empty bonus map — avoids allocation when no frecency records exist */
|
|
235
152
|
const EMPTY_BONUS_MAP = /* @__PURE__ */ new Map();
|
|
236
153
|
/** Pre-computed bucket boundaries in hours — avoids Duration.total() per call */
|
|
@@ -332,8 +249,7 @@ var FrecencyEngine = class {
|
|
|
332
249
|
this.#storage[Symbol.dispose]();
|
|
333
250
|
}
|
|
334
251
|
};
|
|
335
|
-
|
|
336
|
-
//#region src/core/utils/group-by.ts
|
|
252
|
+
// src/core/utils/group-by.ts
|
|
337
253
|
/**
|
|
338
254
|
* Groups items from an iterable into a Map keyed by the result of `keyFn`.
|
|
339
255
|
* Cross-browser replacement for `Map.groupBy()`.
|
|
@@ -348,8 +264,7 @@ function mapGroupBy(items, keyFn) {
|
|
|
348
264
|
}
|
|
349
265
|
return map;
|
|
350
266
|
}
|
|
351
|
-
|
|
352
|
-
//#region src/core/keyboard/matcher.ts
|
|
267
|
+
// src/core/keyboard/matcher.ts
|
|
353
268
|
/**
|
|
354
269
|
* Tests whether a KeyboardEvent exactly matches a ParsedShortcut,
|
|
355
270
|
* comparing all four modifier keys (meta, ctrl, shift, alt) and the primary key.
|
|
@@ -370,8 +285,80 @@ function detectConflicts(shortcuts) {
|
|
|
370
285
|
const grouped = mapGroupBy(shortcuts, (s) => s.normalized);
|
|
371
286
|
return new Map(grouped.entries().filter(([, group]) => group.length > 1));
|
|
372
287
|
}
|
|
373
|
-
|
|
374
|
-
|
|
288
|
+
// src/core/keyboard/parser.ts
|
|
289
|
+
const isMac = typeof navigator !== "undefined" && (navigator.userAgentData?.platform === "macOS" || /Mac|iPod|iPhone|iPad/.test(navigator.platform));
|
|
290
|
+
/** Modifier aliases — cross-platform Mod resolution */
|
|
291
|
+
const MODIFIER_MAP = {
|
|
292
|
+
mod: isMac ? "meta" : "ctrl",
|
|
293
|
+
meta: "meta",
|
|
294
|
+
cmd: "meta",
|
|
295
|
+
command: "meta",
|
|
296
|
+
ctrl: "ctrl",
|
|
297
|
+
control: "ctrl",
|
|
298
|
+
shift: "shift",
|
|
299
|
+
alt: "alt",
|
|
300
|
+
option: "alt",
|
|
301
|
+
opt: "alt"
|
|
302
|
+
};
|
|
303
|
+
/**
|
|
304
|
+
* Parse a shortcut string like "Mod+Shift+K" into a structured ParsedShortcut.
|
|
305
|
+
* Ensures valid Unicode input via ensureWellFormed helper.
|
|
306
|
+
*/
|
|
307
|
+
function parseShortcut(shortcut) {
|
|
308
|
+
const safeShortcut = ensureWellFormed(shortcut);
|
|
309
|
+
const parts = safeShortcut.split("+").map((p) => p.trim().toLowerCase());
|
|
310
|
+
let meta = false;
|
|
311
|
+
let ctrl = false;
|
|
312
|
+
let shift = false;
|
|
313
|
+
let alt = false;
|
|
314
|
+
let key = "";
|
|
315
|
+
for (const part of parts) {
|
|
316
|
+
const modifier = MODIFIER_MAP[part];
|
|
317
|
+
if (modifier) switch (modifier) {
|
|
318
|
+
case "meta":
|
|
319
|
+
meta = true;
|
|
320
|
+
break;
|
|
321
|
+
case "ctrl":
|
|
322
|
+
ctrl = true;
|
|
323
|
+
break;
|
|
324
|
+
case "shift":
|
|
325
|
+
shift = true;
|
|
326
|
+
break;
|
|
327
|
+
case "alt": alt = true;
|
|
328
|
+
}
|
|
329
|
+
else key = part;
|
|
330
|
+
}
|
|
331
|
+
const modifiers = [];
|
|
332
|
+
if (meta) modifiers.push("meta");
|
|
333
|
+
if (ctrl) modifiers.push("ctrl");
|
|
334
|
+
if (shift) modifiers.push("shift");
|
|
335
|
+
if (alt) modifiers.push("alt");
|
|
336
|
+
modifiers.push(key);
|
|
337
|
+
return {
|
|
338
|
+
key,
|
|
339
|
+
meta,
|
|
340
|
+
ctrl,
|
|
341
|
+
shift,
|
|
342
|
+
alt,
|
|
343
|
+
raw: safeShortcut,
|
|
344
|
+
normalized: modifiers.join("+")
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Formats a ParsedShortcut for display using platform-appropriate symbols.
|
|
349
|
+
* On macOS: renders glyphs (⌘, ⇧, ⌥, ⌃). On other platforms: text labels joined by "+".
|
|
350
|
+
*/
|
|
351
|
+
function formatShortcut(parsed) {
|
|
352
|
+
const parts = [];
|
|
353
|
+
if (parsed.meta) parts.push(isMac ? "⌘" : "Ctrl");
|
|
354
|
+
if (parsed.ctrl && !(!isMac && parsed.meta)) parts.push(isMac ? "⌃" : "Ctrl");
|
|
355
|
+
if (parsed.shift) parts.push(isMac ? "⇧" : "Shift");
|
|
356
|
+
if (parsed.alt) parts.push(isMac ? "⌥" : "Alt");
|
|
357
|
+
const displayKey = parsed.key.length === 1 ? parsed.key.toUpperCase() : parsed.key.charAt(0).toUpperCase() + parsed.key.slice(1);
|
|
358
|
+
parts.push(displayKey);
|
|
359
|
+
return isMac ? parts.join("") : parts.join("+");
|
|
360
|
+
}
|
|
361
|
+
// src/core/keyboard/index.ts
|
|
375
362
|
/**
|
|
376
363
|
* Manages keyboard shortcut bindings with global keydown listening.
|
|
377
364
|
* Supports `using` for automatic cleanup of both individual bindings and the
|
|
@@ -442,8 +429,7 @@ var KeyboardShortcutRegistry = class {
|
|
|
442
429
|
this.#bindings.clear();
|
|
443
430
|
}
|
|
444
431
|
};
|
|
445
|
-
|
|
446
|
-
//#region src/core/utils/set-ops.ts
|
|
432
|
+
// src/core/utils/set-ops.ts
|
|
447
433
|
/** Returns a new Set containing elements present in both `a` and `b`. */
|
|
448
434
|
function setIntersection(a, b) {
|
|
449
435
|
const result = /* @__PURE__ */ new Set();
|
|
@@ -486,8 +472,7 @@ function setSymmetricDifference(a, b) {
|
|
|
486
472
|
else result.add(item);
|
|
487
473
|
return result;
|
|
488
474
|
}
|
|
489
|
-
|
|
490
|
-
//#region src/core/registry.ts
|
|
475
|
+
// src/core/registry.ts
|
|
491
476
|
/**
|
|
492
477
|
* Manages registration and lookup of command items and groups.
|
|
493
478
|
* Uses set operation helpers (union/intersection/difference) for efficient ID operations.
|
|
@@ -648,8 +633,23 @@ var CommandRegistry = class {
|
|
|
648
633
|
this.clear();
|
|
649
634
|
}
|
|
650
635
|
};
|
|
651
|
-
|
|
652
|
-
|
|
636
|
+
// src/core/utils/env.ts
|
|
637
|
+
/**
|
|
638
|
+
* `true` unless `process.env.NODE_ENV` is `'production'`.
|
|
639
|
+
*
|
|
640
|
+
* Consumer bundlers (Vite, webpack, Next, esbuild) replace `process.env.NODE_ENV` statically,
|
|
641
|
+
* so the dev-only branches guarded by this flag fold away in production builds. A browser
|
|
642
|
+
* running the unbundled module throws a ReferenceError on the bare `process` reference;
|
|
643
|
+
* the catch turns that into the production value (`false`), so dev-only code stays off.
|
|
644
|
+
*/
|
|
645
|
+
const isDev = (() => {
|
|
646
|
+
try {
|
|
647
|
+
return process.env.NODE_ENV !== "production";
|
|
648
|
+
} catch {
|
|
649
|
+
return false;
|
|
650
|
+
}
|
|
651
|
+
})();
|
|
652
|
+
// src/core/search/default-scorer.ts
|
|
653
653
|
function isWordSeparator(code) {
|
|
654
654
|
return code === 32 || code === 9 || code === 45 || code === 95 || code === 46 || code === 47 || code === 92;
|
|
655
655
|
}
|
|
@@ -771,11 +771,9 @@ function scoreFuzzy(query, qLen, lowerTarget, tLen) {
|
|
|
771
771
|
matches
|
|
772
772
|
};
|
|
773
773
|
}
|
|
774
|
-
|
|
775
|
-
//#region src/core/search/index.ts
|
|
776
|
-
const __DEV__$1 = globalThis.process ? globalThis.process.env.NODE_ENV !== "production" : false;
|
|
774
|
+
// src/core/search/index.ts
|
|
777
775
|
let leakRegistry;
|
|
778
|
-
if (
|
|
776
|
+
if (isDev && typeof window !== "undefined") leakRegistry = new FinalizationRegistry((label) => {
|
|
779
777
|
console.warn(`[modern-cmdk] ${label} was garbage collected without being disposed. Use \`using\` or call [Symbol.dispose]() to prevent leaks.`);
|
|
780
778
|
});
|
|
781
779
|
/**
|
|
@@ -791,7 +789,7 @@ function createSearchEngine(options) {
|
|
|
791
789
|
let previousQuery = "";
|
|
792
790
|
let previousResults = /* @__PURE__ */ new Set();
|
|
793
791
|
const sentinel = {};
|
|
794
|
-
if (
|
|
792
|
+
if (leakRegistry) leakRegistry.register(sentinel, "SearchEngine", sentinel);
|
|
795
793
|
return {
|
|
796
794
|
index(items) {
|
|
797
795
|
for (const item of items) indexedEntries.set(item.id, {
|
|
@@ -841,12 +839,11 @@ function createSearchEngine(options) {
|
|
|
841
839
|
[Symbol.dispose]() {
|
|
842
840
|
indexedEntries.clear();
|
|
843
841
|
previousResults.clear();
|
|
844
|
-
if (
|
|
842
|
+
if (leakRegistry) leakRegistry.unregister(sentinel);
|
|
845
843
|
}
|
|
846
844
|
};
|
|
847
845
|
}
|
|
848
|
-
|
|
849
|
-
//#region src/core/utils/event-emitter.ts
|
|
846
|
+
// src/core/utils/event-emitter.ts
|
|
850
847
|
/**
|
|
851
848
|
* Type-safe event emitter with strong references and Disposable subscriptions.
|
|
852
849
|
* Returns Disposable from `on()` for automatic unsubscription via `using`.
|
|
@@ -889,8 +886,7 @@ var TypedEmitter = class {
|
|
|
889
886
|
this.#listeners.clear();
|
|
890
887
|
}
|
|
891
888
|
};
|
|
892
|
-
|
|
893
|
-
//#region src/core/utils/scheduler.ts
|
|
889
|
+
// src/core/utils/scheduler.ts
|
|
894
890
|
/** Whether we're in a browser environment with rAF available */
|
|
895
891
|
const hasRAF = typeof requestAnimationFrame === "function";
|
|
896
892
|
/** Feature detection for cutting-edge scheduling APIs */
|
|
@@ -951,8 +947,7 @@ function createScheduler() {
|
|
|
951
947
|
}
|
|
952
948
|
};
|
|
953
949
|
}
|
|
954
|
-
|
|
955
|
-
//#region src/core/machine.ts
|
|
950
|
+
// src/core/machine.ts
|
|
956
951
|
/**
|
|
957
952
|
* Creates a headless command palette state machine with search, frecency,
|
|
958
953
|
* and keyboard shortcut support. Implements Disposable for `using` cleanup.
|
|
@@ -1059,9 +1054,10 @@ function createCommandMachine(options = {}) {
|
|
|
1059
1054
|
const loop = options.loop ?? true;
|
|
1060
1055
|
const currentIdx = activeId ? filteredIdIndex.get(activeId) ?? -1 : -1;
|
|
1061
1056
|
let newIdx;
|
|
1062
|
-
if (direction === "next")
|
|
1063
|
-
|
|
1064
|
-
|
|
1057
|
+
if (direction === "next") {
|
|
1058
|
+
if (currentIdx === -1 || currentIdx >= filteredIds.length - 1) newIdx = loop ? 0 : filteredIds.length - 1;
|
|
1059
|
+
else newIdx = currentIdx + 1;
|
|
1060
|
+
} else if (currentIdx <= 0) newIdx = loop ? filteredIds.length - 1 : 0;
|
|
1065
1061
|
else newIdx = currentIdx - 1;
|
|
1066
1062
|
const newActiveId = filteredIds[newIdx] ?? null;
|
|
1067
1063
|
if (newActiveId !== activeId) {
|
|
@@ -1177,7 +1173,6 @@ function createCommandMachine(options = {}) {
|
|
|
1177
1173
|
case "UNREGISTER_GROUP":
|
|
1178
1174
|
registry.unregisterGroup(event.id);
|
|
1179
1175
|
refilter(state.search);
|
|
1180
|
-
break;
|
|
1181
1176
|
}
|
|
1182
1177
|
}
|
|
1183
1178
|
return {
|
|
@@ -1218,8 +1213,7 @@ function createCommandMachine(options = {}) {
|
|
|
1218
1213
|
}
|
|
1219
1214
|
};
|
|
1220
1215
|
}
|
|
1221
|
-
|
|
1222
|
-
//#region src/react/hooks/use-command.ts
|
|
1216
|
+
// src/react/hooks/use-command.ts
|
|
1223
1217
|
/** Core hook that binds a CommandMachine to React via useSyncExternalStore, useTransition, and useOptimistic. */
|
|
1224
1218
|
function useCommand(machine) {
|
|
1225
1219
|
const id = useId();
|
|
@@ -1252,8 +1246,7 @@ function useCommand(machine) {
|
|
|
1252
1246
|
id
|
|
1253
1247
|
};
|
|
1254
1248
|
}
|
|
1255
|
-
|
|
1256
|
-
//#region src/react/hooks/use-keyboard.ts
|
|
1249
|
+
// src/react/hooks/use-keyboard.ts
|
|
1257
1250
|
/** Navigation key to machine direction mapping */
|
|
1258
1251
|
const NAVIGATION_KEYS = {
|
|
1259
1252
|
ArrowDown: "next",
|
|
@@ -1294,12 +1287,10 @@ function createKeydownHandler(machine, getState) {
|
|
|
1294
1287
|
case "Escape":
|
|
1295
1288
|
event.preventDefault();
|
|
1296
1289
|
machine.send({ type: "CLOSE" });
|
|
1297
|
-
break;
|
|
1298
1290
|
}
|
|
1299
1291
|
};
|
|
1300
1292
|
}
|
|
1301
|
-
|
|
1302
|
-
//#region src/react/hooks/use-command-setup.ts
|
|
1293
|
+
// src/react/hooks/use-command-setup.ts
|
|
1303
1294
|
/**
|
|
1304
1295
|
* Shared setup for Command and Command.Dialog — creates machine once,
|
|
1305
1296
|
* subscribes via useSyncExternalStore, memoizes context values, and
|
|
@@ -1359,8 +1350,7 @@ function useCommandSetup(machineFactory, label) {
|
|
|
1359
1350
|
])
|
|
1360
1351
|
};
|
|
1361
1352
|
}
|
|
1362
|
-
|
|
1363
|
-
//#region src/react/dialog.tsx
|
|
1353
|
+
// src/react/dialog.tsx
|
|
1364
1354
|
/** Dialog variant that wraps the command palette in a Radix UI Dialog with focus trap and overlay. */
|
|
1365
1355
|
function CommandDialog({ children, className, overlayClassName, contentClassName, label = "Command palette", description = "Type a command or search...", open: controlledOpen, onOpenChange, container, ...machineOptions }) {
|
|
1366
1356
|
const onOpenChangeRef = useRef(onOpenChange);
|
|
@@ -1434,8 +1424,7 @@ function CommandDialog({ children, className, overlayClassName, contentClassName
|
|
|
1434
1424
|
})
|
|
1435
1425
|
});
|
|
1436
1426
|
}
|
|
1437
|
-
|
|
1438
|
-
//#region src/react/empty.tsx
|
|
1427
|
+
// src/react/empty.tsx
|
|
1439
1428
|
/** Rendered when no items match the current search query. */
|
|
1440
1429
|
function CommandEmpty({ ref, children, ...props }) {
|
|
1441
1430
|
const stateCtx = useStateContext("Command.Empty");
|
|
@@ -1452,8 +1441,7 @@ function CommandEmpty({ ref, children, ...props }) {
|
|
|
1452
1441
|
if (statusContainerRef?.current) return createPortal(content, statusContainerRef.current);
|
|
1453
1442
|
return content;
|
|
1454
1443
|
}
|
|
1455
|
-
|
|
1456
|
-
//#region src/react/hooks/use-register.ts
|
|
1444
|
+
// src/react/hooks/use-register.ts
|
|
1457
1445
|
/** Register a command item with the machine — auto-deregisters on unmount */
|
|
1458
1446
|
function useRegisterItem(value, options) {
|
|
1459
1447
|
const stable = useStableContext("useRegisterItem");
|
|
@@ -1525,8 +1513,7 @@ function useRegisterGroup(heading, options) {
|
|
|
1525
1513
|
]);
|
|
1526
1514
|
return id;
|
|
1527
1515
|
}
|
|
1528
|
-
|
|
1529
|
-
//#region src/react/group.tsx
|
|
1516
|
+
// src/react/group.tsx
|
|
1530
1517
|
/** Module-level constant — avoids allocating a new object on every render of hidden groups */
|
|
1531
1518
|
const HIDDEN_STYLE = { display: "none" };
|
|
1532
1519
|
/** Visual grouping container with an optional heading that hides when no items match. */
|
|
@@ -1570,8 +1557,7 @@ function CommandGroup({ ref, children, heading, priority, forceId, ...props }) {
|
|
|
1570
1557
|
})
|
|
1571
1558
|
});
|
|
1572
1559
|
}
|
|
1573
|
-
|
|
1574
|
-
//#region src/react/highlight.tsx
|
|
1560
|
+
// src/react/highlight.tsx
|
|
1575
1561
|
/** Renders search match highlighting by splitting text at scored match ranges. */
|
|
1576
1562
|
function CommandHighlight({ text, ranges, highlightClassName }) {
|
|
1577
1563
|
return useMemo(() => {
|
|
@@ -1596,8 +1582,7 @@ function CommandHighlight({ text, ranges, highlightClassName }) {
|
|
|
1596
1582
|
highlightClassName
|
|
1597
1583
|
]);
|
|
1598
1584
|
}
|
|
1599
|
-
|
|
1600
|
-
//#region src/react/input.tsx
|
|
1585
|
+
// src/react/input.tsx
|
|
1601
1586
|
/** Combobox input that drives the command palette search state. */
|
|
1602
1587
|
function CommandInput({ ref, placeholder = "Search...", onValueChange, ...props }) {
|
|
1603
1588
|
const stable = useStableContext("Command.Input");
|
|
@@ -1627,8 +1612,7 @@ function CommandInput({ ref, placeholder = "Search...", onValueChange, ...props
|
|
|
1627
1612
|
...props
|
|
1628
1613
|
});
|
|
1629
1614
|
}
|
|
1630
|
-
|
|
1631
|
-
//#region src/react/item.tsx
|
|
1615
|
+
// src/react/item.tsx
|
|
1632
1616
|
/** Individual selectable item in the command list with keyboard and pointer activation. */
|
|
1633
1617
|
function CommandItem({ ref, children, value, keywords, onSelect, disabled = false, shortcut, forceId, ...props }) {
|
|
1634
1618
|
const stable = useStableContext("Command.Item");
|
|
@@ -1698,8 +1682,7 @@ function CommandItem({ ref, children, value, keywords, onSelect, disabled = fals
|
|
|
1698
1682
|
children
|
|
1699
1683
|
});
|
|
1700
1684
|
}
|
|
1701
|
-
|
|
1702
|
-
//#region src/react/hooks/use-virtualizer.ts
|
|
1685
|
+
// src/react/hooks/use-virtualizer.ts
|
|
1703
1686
|
/** Lightweight virtualizer for large item lists using ResizeObserver and scroll tracking. */
|
|
1704
1687
|
function useVirtualizer(options) {
|
|
1705
1688
|
const { count, estimateSize, overscan = 8, scrollElement, enabled = true } = options;
|
|
@@ -1803,8 +1786,7 @@ function useVirtualizer(options) {
|
|
|
1803
1786
|
}, [enabled])
|
|
1804
1787
|
};
|
|
1805
1788
|
}
|
|
1806
|
-
|
|
1807
|
-
//#region src/react/list.tsx
|
|
1789
|
+
// src/react/list.tsx
|
|
1808
1790
|
/** Scrollable list container with auto-virtualization and aria-live announcements. */
|
|
1809
1791
|
function CommandList({ ref, children, virtualize, estimateSize = 44, overscan = 8, announceResults, style, ...props }) {
|
|
1810
1792
|
const stable = useStableContext("Command.List");
|
|
@@ -1944,8 +1926,7 @@ function CommandList({ ref, children, virtualize, estimateSize = 44, overscan =
|
|
|
1944
1926
|
]
|
|
1945
1927
|
});
|
|
1946
1928
|
}
|
|
1947
|
-
|
|
1948
|
-
//#region src/react/loading.tsx
|
|
1929
|
+
// src/react/loading.tsx
|
|
1949
1930
|
/** Shown during async operations, with configurable loading state override. */
|
|
1950
1931
|
function CommandLoading({ ref, children, loading: loadingOverride, ...props }) {
|
|
1951
1932
|
const stateCtx = useStateContext("Command.Loading");
|
|
@@ -1960,8 +1941,7 @@ function CommandLoading({ ref, children, loading: loadingOverride, ...props }) {
|
|
|
1960
1941
|
children
|
|
1961
1942
|
});
|
|
1962
1943
|
}
|
|
1963
|
-
|
|
1964
|
-
//#region src/react/page.tsx
|
|
1944
|
+
// src/react/page.tsx
|
|
1965
1945
|
/** Page navigation container that only renders when its ID matches the active page. */
|
|
1966
1946
|
function CommandPage({ id, children }) {
|
|
1967
1947
|
if (useStateContext("Command.Page").state.page !== id) return null;
|
|
@@ -1971,8 +1951,7 @@ function CommandPage({ id, children }) {
|
|
|
1971
1951
|
children
|
|
1972
1952
|
});
|
|
1973
1953
|
}
|
|
1974
|
-
|
|
1975
|
-
//#region src/react/separator.tsx
|
|
1954
|
+
// src/react/separator.tsx
|
|
1976
1955
|
/** Visual divider between items or groups, rendered as a presentational element. */
|
|
1977
1956
|
function CommandSeparator({ ref, ...props }) {
|
|
1978
1957
|
return /* @__PURE__ */ jsx("div", {
|
|
@@ -1982,8 +1961,7 @@ function CommandSeparator({ ref, ...props }) {
|
|
|
1982
1961
|
...props
|
|
1983
1962
|
});
|
|
1984
1963
|
}
|
|
1985
|
-
|
|
1986
|
-
//#region src/react/shortcut.tsx
|
|
1964
|
+
// src/react/shortcut.tsx
|
|
1987
1965
|
/** Keyboard shortcut display with platform-aware formatting (e.g., Cmd vs Ctrl). */
|
|
1988
1966
|
function CommandShortcut({ ref, shortcut, children, ...props }) {
|
|
1989
1967
|
const formattedShortcut = useMemo(() => shortcut ? formatShortcut(parseShortcut(shortcut)) : null, [shortcut]);
|
|
@@ -1994,8 +1972,7 @@ function CommandShortcut({ ref, shortcut, children, ...props }) {
|
|
|
1994
1972
|
children: formattedShortcut ?? children
|
|
1995
1973
|
});
|
|
1996
1974
|
}
|
|
1997
|
-
|
|
1998
|
-
//#region src/react/command.tsx
|
|
1975
|
+
// src/react/command.tsx
|
|
1999
1976
|
/** Root compound component that provides the command state machine context to all children. */
|
|
2000
1977
|
function CommandRoot({ children, className, label = "Command palette", ...machineOptions }) {
|
|
2001
1978
|
const rootRef = useRef(null);
|
|
@@ -2044,8 +2021,7 @@ const Command = Object.assign(CommandRoot, {
|
|
|
2044
2021
|
AsyncItems: CommandAsyncItems,
|
|
2045
2022
|
Activity: CommandActivity
|
|
2046
2023
|
});
|
|
2047
|
-
|
|
2048
|
-
//#region src/react/error-boundary.tsx
|
|
2024
|
+
// src/react/error-boundary.tsx
|
|
2049
2025
|
/** Initial state — no error captured */
|
|
2050
2026
|
const INITIAL_STATE = { error: null };
|
|
2051
2027
|
/** Error boundary for graceful failure recovery in command subtrees. */
|
|
@@ -2067,8 +2043,7 @@ var CommandErrorBoundary = class extends Component {
|
|
|
2067
2043
|
return this.props.children;
|
|
2068
2044
|
}
|
|
2069
2045
|
};
|
|
2070
|
-
|
|
2071
|
-
//#region src/react/hooks/use-command-state.ts
|
|
2046
|
+
// src/react/hooks/use-command-state.ts
|
|
2072
2047
|
function useCommandState(selector) {
|
|
2073
2048
|
const stable = useStableContext("useCommandState");
|
|
2074
2049
|
const selectorRef = useRef(selector);
|
|
@@ -2076,19 +2051,20 @@ function useCommandState(selector) {
|
|
|
2076
2051
|
const getSnapshot = useCallback(() => selectorRef.current ? selectorRef.current(stable.machine.getState()) : stable.machine.getState(), [stable.machine]);
|
|
2077
2052
|
return useSyncExternalStore(stable.machine.subscribe, getSnapshot, getSnapshot);
|
|
2078
2053
|
}
|
|
2079
|
-
|
|
2080
|
-
//#region src/react/hooks/use-devtools.ts
|
|
2054
|
+
// src/react/hooks/use-devtools.ts
|
|
2081
2055
|
/** Branded key for the global devtools instance map */
|
|
2082
2056
|
const DEVTOOLS_KEY = "__CRIMSON_COMMAND_DEVTOOLS__";
|
|
2083
2057
|
/**
|
|
2084
2058
|
* Exposes command palette internals to browser devtools.
|
|
2085
|
-
*
|
|
2059
|
+
* Development only: the effect is gated on `process.env.NODE_ENV` and returns early in
|
|
2060
|
+
* production builds (the hook itself still ships). Call it from a component rendered inside
|
|
2061
|
+
* `<Command>`: outside one there is no machine to inspect and the hook does nothing.
|
|
2086
2062
|
*
|
|
2087
2063
|
* @example
|
|
2088
2064
|
* ```tsx
|
|
2089
|
-
* function
|
|
2065
|
+
* function DevtoolsBridge() {
|
|
2090
2066
|
* useCommandDevtools('main');
|
|
2091
|
-
* return
|
|
2067
|
+
* return null;
|
|
2092
2068
|
* }
|
|
2093
2069
|
* ```
|
|
2094
2070
|
*/
|
|
@@ -2097,7 +2073,7 @@ function useCommandDevtools(label = "default") {
|
|
|
2097
2073
|
const stateCtx = use(CommandStateContext);
|
|
2098
2074
|
const stateRef = useRef(null);
|
|
2099
2075
|
useEffect(() => {
|
|
2100
|
-
if (!
|
|
2076
|
+
if (!isDev) return;
|
|
2101
2077
|
if (!stable || !stateCtx) return;
|
|
2102
2078
|
const update = () => {
|
|
2103
2079
|
const s = stateCtx.state;
|
|
@@ -2132,5 +2108,4 @@ function useCommandDevtools(label = "default") {
|
|
|
2132
2108
|
label
|
|
2133
2109
|
]);
|
|
2134
2110
|
}
|
|
2135
|
-
//#endregion
|
|
2136
2111
|
export { Command, CommandActivity, CommandAsyncItems, CommandBadge, CommandDialog, CommandEmpty, CommandErrorBoundary, CommandGroup, CommandHighlight, CommandInput, CommandItem, CommandList, CommandLoading, CommandPage, CommandSeparator, CommandShortcut, CommandStableContext, CommandStateContext, createKeydownHandler, useCommand, useCommandDevtools, useCommandState, useRegisterGroup, useRegisterItem, useVirtualizer };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "modern-cmdk",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The definitive command palette engine for the modern web",
|
|
6
6
|
"license": "MIT",
|
|
@@ -57,13 +57,13 @@
|
|
|
57
57
|
}
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"idb-keyval": "6.
|
|
60
|
+
"idb-keyval": "6.3.0"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
|
-
"globby": "16.2.
|
|
64
|
-
"jscodeshift": "17.
|
|
65
|
-
"radix-ui": "1.
|
|
66
|
-
"@types/jscodeshift": "
|
|
63
|
+
"globby": "16.2.4",
|
|
64
|
+
"jscodeshift": "17.4.0",
|
|
65
|
+
"radix-ui": "1.8.2-rc.1791984254107",
|
|
66
|
+
"@types/jscodeshift": "17.3.0"
|
|
67
67
|
},
|
|
68
68
|
"keywords": [
|
|
69
69
|
"command-palette",
|
|
@@ -80,8 +80,8 @@
|
|
|
80
80
|
"directory": "packages/modern-cmdk"
|
|
81
81
|
},
|
|
82
82
|
"scripts": {
|
|
83
|
-
"build": "tsdown
|
|
84
|
-
"dev": "tsdown
|
|
83
|
+
"build": "tsdown",
|
|
84
|
+
"dev": "tsdown --watch",
|
|
85
85
|
"typecheck": "tsc --noEmit"
|
|
86
86
|
}
|
|
87
87
|
}
|