modern-cmdk 1.2.2 → 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 +3 -3
- package/dist/codemod/cli.mjs +14001 -60832
- package/dist/codemod/transforms/data-attrs.d.mts +1 -3
- package/dist/codemod/transforms/data-attrs.mjs +1 -2
- package/dist/codemod/transforms/forward-ref.d.mts +1 -3
- package/dist/codemod/transforms/forward-ref.mjs +1 -2
- package/dist/codemod/transforms/import-rewrite.d.mts +1 -3
- package/dist/codemod/transforms/import-rewrite.mjs +1 -2
- package/dist/codemod/transforms/should-filter.d.mts +1 -3
- package/dist/codemod/transforms/should-filter.mjs +1 -2
- package/dist/core/index.d.mts +0 -32
- package/dist/core/index.mjs +37 -40
- package/dist/react/index.d.mts +6 -62
- package/dist/react/index.mjs +65 -87
- package/package.json +4 -4
- package/CHANGELOG.md +0 -98
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,8 +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/types.ts
|
|
96
|
+
// src/core/types.ts
|
|
102
97
|
/** Create a branded {@link ItemId} from a plain string. Ensures well-formed Unicode. */
|
|
103
98
|
function itemId(id) {
|
|
104
99
|
return ensureWellFormed(id);
|
|
@@ -130,8 +125,7 @@ function createInitialState(options) {
|
|
|
130
125
|
lastUpdated: Date.now()
|
|
131
126
|
};
|
|
132
127
|
}
|
|
133
|
-
|
|
134
|
-
//#region src/core/frecency/memory-storage.ts
|
|
128
|
+
// src/core/frecency/memory-storage.ts
|
|
135
129
|
/**
|
|
136
130
|
* In-memory implementation of FrecencyStorage using a Map.
|
|
137
131
|
* Data does not persist across page reloads. Suitable for ephemeral
|
|
@@ -153,8 +147,7 @@ var MemoryFrecencyStorage = class {
|
|
|
153
147
|
this.#store.clear();
|
|
154
148
|
}
|
|
155
149
|
};
|
|
156
|
-
|
|
157
|
-
//#region src/core/frecency/index.ts
|
|
150
|
+
// src/core/frecency/index.ts
|
|
158
151
|
/** Shared empty bonus map — avoids allocation when no frecency records exist */
|
|
159
152
|
const EMPTY_BONUS_MAP = /* @__PURE__ */ new Map();
|
|
160
153
|
/** Pre-computed bucket boundaries in hours — avoids Duration.total() per call */
|
|
@@ -256,8 +249,7 @@ var FrecencyEngine = class {
|
|
|
256
249
|
this.#storage[Symbol.dispose]();
|
|
257
250
|
}
|
|
258
251
|
};
|
|
259
|
-
|
|
260
|
-
//#region src/core/utils/group-by.ts
|
|
252
|
+
// src/core/utils/group-by.ts
|
|
261
253
|
/**
|
|
262
254
|
* Groups items from an iterable into a Map keyed by the result of `keyFn`.
|
|
263
255
|
* Cross-browser replacement for `Map.groupBy()`.
|
|
@@ -272,8 +264,7 @@ function mapGroupBy(items, keyFn) {
|
|
|
272
264
|
}
|
|
273
265
|
return map;
|
|
274
266
|
}
|
|
275
|
-
|
|
276
|
-
//#region src/core/keyboard/matcher.ts
|
|
267
|
+
// src/core/keyboard/matcher.ts
|
|
277
268
|
/**
|
|
278
269
|
* Tests whether a KeyboardEvent exactly matches a ParsedShortcut,
|
|
279
270
|
* comparing all four modifier keys (meta, ctrl, shift, alt) and the primary key.
|
|
@@ -294,8 +285,7 @@ function detectConflicts(shortcuts) {
|
|
|
294
285
|
const grouped = mapGroupBy(shortcuts, (s) => s.normalized);
|
|
295
286
|
return new Map(grouped.entries().filter(([, group]) => group.length > 1));
|
|
296
287
|
}
|
|
297
|
-
|
|
298
|
-
//#region src/core/keyboard/parser.ts
|
|
288
|
+
// src/core/keyboard/parser.ts
|
|
299
289
|
const isMac = typeof navigator !== "undefined" && (navigator.userAgentData?.platform === "macOS" || /Mac|iPod|iPhone|iPad/.test(navigator.platform));
|
|
300
290
|
/** Modifier aliases — cross-platform Mod resolution */
|
|
301
291
|
const MODIFIER_MAP = {
|
|
@@ -368,8 +358,7 @@ function formatShortcut(parsed) {
|
|
|
368
358
|
parts.push(displayKey);
|
|
369
359
|
return isMac ? parts.join("") : parts.join("+");
|
|
370
360
|
}
|
|
371
|
-
|
|
372
|
-
//#region src/core/keyboard/index.ts
|
|
361
|
+
// src/core/keyboard/index.ts
|
|
373
362
|
/**
|
|
374
363
|
* Manages keyboard shortcut bindings with global keydown listening.
|
|
375
364
|
* Supports `using` for automatic cleanup of both individual bindings and the
|
|
@@ -440,8 +429,7 @@ var KeyboardShortcutRegistry = class {
|
|
|
440
429
|
this.#bindings.clear();
|
|
441
430
|
}
|
|
442
431
|
};
|
|
443
|
-
|
|
444
|
-
//#region src/core/utils/set-ops.ts
|
|
432
|
+
// src/core/utils/set-ops.ts
|
|
445
433
|
/** Returns a new Set containing elements present in both `a` and `b`. */
|
|
446
434
|
function setIntersection(a, b) {
|
|
447
435
|
const result = /* @__PURE__ */ new Set();
|
|
@@ -484,8 +472,7 @@ function setSymmetricDifference(a, b) {
|
|
|
484
472
|
else result.add(item);
|
|
485
473
|
return result;
|
|
486
474
|
}
|
|
487
|
-
|
|
488
|
-
//#region src/core/registry.ts
|
|
475
|
+
// src/core/registry.ts
|
|
489
476
|
/**
|
|
490
477
|
* Manages registration and lookup of command items and groups.
|
|
491
478
|
* Uses set operation helpers (union/intersection/difference) for efficient ID operations.
|
|
@@ -646,8 +633,23 @@ var CommandRegistry = class {
|
|
|
646
633
|
this.clear();
|
|
647
634
|
}
|
|
648
635
|
};
|
|
649
|
-
|
|
650
|
-
|
|
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
|
|
651
653
|
function isWordSeparator(code) {
|
|
652
654
|
return code === 32 || code === 9 || code === 45 || code === 95 || code === 46 || code === 47 || code === 92;
|
|
653
655
|
}
|
|
@@ -769,11 +771,9 @@ function scoreFuzzy(query, qLen, lowerTarget, tLen) {
|
|
|
769
771
|
matches
|
|
770
772
|
};
|
|
771
773
|
}
|
|
772
|
-
|
|
773
|
-
//#region src/core/search/index.ts
|
|
774
|
-
const __DEV__$1 = globalThis.process ? globalThis.process.env.NODE_ENV !== "production" : false;
|
|
774
|
+
// src/core/search/index.ts
|
|
775
775
|
let leakRegistry;
|
|
776
|
-
if (
|
|
776
|
+
if (isDev && typeof window !== "undefined") leakRegistry = new FinalizationRegistry((label) => {
|
|
777
777
|
console.warn(`[modern-cmdk] ${label} was garbage collected without being disposed. Use \`using\` or call [Symbol.dispose]() to prevent leaks.`);
|
|
778
778
|
});
|
|
779
779
|
/**
|
|
@@ -789,7 +789,7 @@ function createSearchEngine(options) {
|
|
|
789
789
|
let previousQuery = "";
|
|
790
790
|
let previousResults = /* @__PURE__ */ new Set();
|
|
791
791
|
const sentinel = {};
|
|
792
|
-
if (
|
|
792
|
+
if (leakRegistry) leakRegistry.register(sentinel, "SearchEngine", sentinel);
|
|
793
793
|
return {
|
|
794
794
|
index(items) {
|
|
795
795
|
for (const item of items) indexedEntries.set(item.id, {
|
|
@@ -839,12 +839,11 @@ function createSearchEngine(options) {
|
|
|
839
839
|
[Symbol.dispose]() {
|
|
840
840
|
indexedEntries.clear();
|
|
841
841
|
previousResults.clear();
|
|
842
|
-
if (
|
|
842
|
+
if (leakRegistry) leakRegistry.unregister(sentinel);
|
|
843
843
|
}
|
|
844
844
|
};
|
|
845
845
|
}
|
|
846
|
-
|
|
847
|
-
//#region src/core/utils/event-emitter.ts
|
|
846
|
+
// src/core/utils/event-emitter.ts
|
|
848
847
|
/**
|
|
849
848
|
* Type-safe event emitter with strong references and Disposable subscriptions.
|
|
850
849
|
* Returns Disposable from `on()` for automatic unsubscription via `using`.
|
|
@@ -887,8 +886,7 @@ var TypedEmitter = class {
|
|
|
887
886
|
this.#listeners.clear();
|
|
888
887
|
}
|
|
889
888
|
};
|
|
890
|
-
|
|
891
|
-
//#region src/core/utils/scheduler.ts
|
|
889
|
+
// src/core/utils/scheduler.ts
|
|
892
890
|
/** Whether we're in a browser environment with rAF available */
|
|
893
891
|
const hasRAF = typeof requestAnimationFrame === "function";
|
|
894
892
|
/** Feature detection for cutting-edge scheduling APIs */
|
|
@@ -949,8 +947,7 @@ function createScheduler() {
|
|
|
949
947
|
}
|
|
950
948
|
};
|
|
951
949
|
}
|
|
952
|
-
|
|
953
|
-
//#region src/core/machine.ts
|
|
950
|
+
// src/core/machine.ts
|
|
954
951
|
/**
|
|
955
952
|
* Creates a headless command palette state machine with search, frecency,
|
|
956
953
|
* and keyboard shortcut support. Implements Disposable for `using` cleanup.
|
|
@@ -1216,8 +1213,7 @@ function createCommandMachine(options = {}) {
|
|
|
1216
1213
|
}
|
|
1217
1214
|
};
|
|
1218
1215
|
}
|
|
1219
|
-
|
|
1220
|
-
//#region src/react/hooks/use-command.ts
|
|
1216
|
+
// src/react/hooks/use-command.ts
|
|
1221
1217
|
/** Core hook that binds a CommandMachine to React via useSyncExternalStore, useTransition, and useOptimistic. */
|
|
1222
1218
|
function useCommand(machine) {
|
|
1223
1219
|
const id = useId();
|
|
@@ -1250,8 +1246,7 @@ function useCommand(machine) {
|
|
|
1250
1246
|
id
|
|
1251
1247
|
};
|
|
1252
1248
|
}
|
|
1253
|
-
|
|
1254
|
-
//#region src/react/hooks/use-keyboard.ts
|
|
1249
|
+
// src/react/hooks/use-keyboard.ts
|
|
1255
1250
|
/** Navigation key to machine direction mapping */
|
|
1256
1251
|
const NAVIGATION_KEYS = {
|
|
1257
1252
|
ArrowDown: "next",
|
|
@@ -1295,8 +1290,7 @@ function createKeydownHandler(machine, getState) {
|
|
|
1295
1290
|
}
|
|
1296
1291
|
};
|
|
1297
1292
|
}
|
|
1298
|
-
|
|
1299
|
-
//#region src/react/hooks/use-command-setup.ts
|
|
1293
|
+
// src/react/hooks/use-command-setup.ts
|
|
1300
1294
|
/**
|
|
1301
1295
|
* Shared setup for Command and Command.Dialog — creates machine once,
|
|
1302
1296
|
* subscribes via useSyncExternalStore, memoizes context values, and
|
|
@@ -1356,8 +1350,7 @@ function useCommandSetup(machineFactory, label) {
|
|
|
1356
1350
|
])
|
|
1357
1351
|
};
|
|
1358
1352
|
}
|
|
1359
|
-
|
|
1360
|
-
//#region src/react/dialog.tsx
|
|
1353
|
+
// src/react/dialog.tsx
|
|
1361
1354
|
/** Dialog variant that wraps the command palette in a Radix UI Dialog with focus trap and overlay. */
|
|
1362
1355
|
function CommandDialog({ children, className, overlayClassName, contentClassName, label = "Command palette", description = "Type a command or search...", open: controlledOpen, onOpenChange, container, ...machineOptions }) {
|
|
1363
1356
|
const onOpenChangeRef = useRef(onOpenChange);
|
|
@@ -1431,8 +1424,7 @@ function CommandDialog({ children, className, overlayClassName, contentClassName
|
|
|
1431
1424
|
})
|
|
1432
1425
|
});
|
|
1433
1426
|
}
|
|
1434
|
-
|
|
1435
|
-
//#region src/react/empty.tsx
|
|
1427
|
+
// src/react/empty.tsx
|
|
1436
1428
|
/** Rendered when no items match the current search query. */
|
|
1437
1429
|
function CommandEmpty({ ref, children, ...props }) {
|
|
1438
1430
|
const stateCtx = useStateContext("Command.Empty");
|
|
@@ -1449,8 +1441,7 @@ function CommandEmpty({ ref, children, ...props }) {
|
|
|
1449
1441
|
if (statusContainerRef?.current) return createPortal(content, statusContainerRef.current);
|
|
1450
1442
|
return content;
|
|
1451
1443
|
}
|
|
1452
|
-
|
|
1453
|
-
//#region src/react/hooks/use-register.ts
|
|
1444
|
+
// src/react/hooks/use-register.ts
|
|
1454
1445
|
/** Register a command item with the machine — auto-deregisters on unmount */
|
|
1455
1446
|
function useRegisterItem(value, options) {
|
|
1456
1447
|
const stable = useStableContext("useRegisterItem");
|
|
@@ -1522,8 +1513,7 @@ function useRegisterGroup(heading, options) {
|
|
|
1522
1513
|
]);
|
|
1523
1514
|
return id;
|
|
1524
1515
|
}
|
|
1525
|
-
|
|
1526
|
-
//#region src/react/group.tsx
|
|
1516
|
+
// src/react/group.tsx
|
|
1527
1517
|
/** Module-level constant — avoids allocating a new object on every render of hidden groups */
|
|
1528
1518
|
const HIDDEN_STYLE = { display: "none" };
|
|
1529
1519
|
/** Visual grouping container with an optional heading that hides when no items match. */
|
|
@@ -1567,8 +1557,7 @@ function CommandGroup({ ref, children, heading, priority, forceId, ...props }) {
|
|
|
1567
1557
|
})
|
|
1568
1558
|
});
|
|
1569
1559
|
}
|
|
1570
|
-
|
|
1571
|
-
//#region src/react/highlight.tsx
|
|
1560
|
+
// src/react/highlight.tsx
|
|
1572
1561
|
/** Renders search match highlighting by splitting text at scored match ranges. */
|
|
1573
1562
|
function CommandHighlight({ text, ranges, highlightClassName }) {
|
|
1574
1563
|
return useMemo(() => {
|
|
@@ -1593,8 +1582,7 @@ function CommandHighlight({ text, ranges, highlightClassName }) {
|
|
|
1593
1582
|
highlightClassName
|
|
1594
1583
|
]);
|
|
1595
1584
|
}
|
|
1596
|
-
|
|
1597
|
-
//#region src/react/input.tsx
|
|
1585
|
+
// src/react/input.tsx
|
|
1598
1586
|
/** Combobox input that drives the command palette search state. */
|
|
1599
1587
|
function CommandInput({ ref, placeholder = "Search...", onValueChange, ...props }) {
|
|
1600
1588
|
const stable = useStableContext("Command.Input");
|
|
@@ -1624,8 +1612,7 @@ function CommandInput({ ref, placeholder = "Search...", onValueChange, ...props
|
|
|
1624
1612
|
...props
|
|
1625
1613
|
});
|
|
1626
1614
|
}
|
|
1627
|
-
|
|
1628
|
-
//#region src/react/item.tsx
|
|
1615
|
+
// src/react/item.tsx
|
|
1629
1616
|
/** Individual selectable item in the command list with keyboard and pointer activation. */
|
|
1630
1617
|
function CommandItem({ ref, children, value, keywords, onSelect, disabled = false, shortcut, forceId, ...props }) {
|
|
1631
1618
|
const stable = useStableContext("Command.Item");
|
|
@@ -1695,8 +1682,7 @@ function CommandItem({ ref, children, value, keywords, onSelect, disabled = fals
|
|
|
1695
1682
|
children
|
|
1696
1683
|
});
|
|
1697
1684
|
}
|
|
1698
|
-
|
|
1699
|
-
//#region src/react/hooks/use-virtualizer.ts
|
|
1685
|
+
// src/react/hooks/use-virtualizer.ts
|
|
1700
1686
|
/** Lightweight virtualizer for large item lists using ResizeObserver and scroll tracking. */
|
|
1701
1687
|
function useVirtualizer(options) {
|
|
1702
1688
|
const { count, estimateSize, overscan = 8, scrollElement, enabled = true } = options;
|
|
@@ -1800,8 +1786,7 @@ function useVirtualizer(options) {
|
|
|
1800
1786
|
}, [enabled])
|
|
1801
1787
|
};
|
|
1802
1788
|
}
|
|
1803
|
-
|
|
1804
|
-
//#region src/react/list.tsx
|
|
1789
|
+
// src/react/list.tsx
|
|
1805
1790
|
/** Scrollable list container with auto-virtualization and aria-live announcements. */
|
|
1806
1791
|
function CommandList({ ref, children, virtualize, estimateSize = 44, overscan = 8, announceResults, style, ...props }) {
|
|
1807
1792
|
const stable = useStableContext("Command.List");
|
|
@@ -1941,8 +1926,7 @@ function CommandList({ ref, children, virtualize, estimateSize = 44, overscan =
|
|
|
1941
1926
|
]
|
|
1942
1927
|
});
|
|
1943
1928
|
}
|
|
1944
|
-
|
|
1945
|
-
//#region src/react/loading.tsx
|
|
1929
|
+
// src/react/loading.tsx
|
|
1946
1930
|
/** Shown during async operations, with configurable loading state override. */
|
|
1947
1931
|
function CommandLoading({ ref, children, loading: loadingOverride, ...props }) {
|
|
1948
1932
|
const stateCtx = useStateContext("Command.Loading");
|
|
@@ -1957,8 +1941,7 @@ function CommandLoading({ ref, children, loading: loadingOverride, ...props }) {
|
|
|
1957
1941
|
children
|
|
1958
1942
|
});
|
|
1959
1943
|
}
|
|
1960
|
-
|
|
1961
|
-
//#region src/react/page.tsx
|
|
1944
|
+
// src/react/page.tsx
|
|
1962
1945
|
/** Page navigation container that only renders when its ID matches the active page. */
|
|
1963
1946
|
function CommandPage({ id, children }) {
|
|
1964
1947
|
if (useStateContext("Command.Page").state.page !== id) return null;
|
|
@@ -1968,8 +1951,7 @@ function CommandPage({ id, children }) {
|
|
|
1968
1951
|
children
|
|
1969
1952
|
});
|
|
1970
1953
|
}
|
|
1971
|
-
|
|
1972
|
-
//#region src/react/separator.tsx
|
|
1954
|
+
// src/react/separator.tsx
|
|
1973
1955
|
/** Visual divider between items or groups, rendered as a presentational element. */
|
|
1974
1956
|
function CommandSeparator({ ref, ...props }) {
|
|
1975
1957
|
return /* @__PURE__ */ jsx("div", {
|
|
@@ -1979,8 +1961,7 @@ function CommandSeparator({ ref, ...props }) {
|
|
|
1979
1961
|
...props
|
|
1980
1962
|
});
|
|
1981
1963
|
}
|
|
1982
|
-
|
|
1983
|
-
//#region src/react/shortcut.tsx
|
|
1964
|
+
// src/react/shortcut.tsx
|
|
1984
1965
|
/** Keyboard shortcut display with platform-aware formatting (e.g., Cmd vs Ctrl). */
|
|
1985
1966
|
function CommandShortcut({ ref, shortcut, children, ...props }) {
|
|
1986
1967
|
const formattedShortcut = useMemo(() => shortcut ? formatShortcut(parseShortcut(shortcut)) : null, [shortcut]);
|
|
@@ -1991,8 +1972,7 @@ function CommandShortcut({ ref, shortcut, children, ...props }) {
|
|
|
1991
1972
|
children: formattedShortcut ?? children
|
|
1992
1973
|
});
|
|
1993
1974
|
}
|
|
1994
|
-
|
|
1995
|
-
//#region src/react/command.tsx
|
|
1975
|
+
// src/react/command.tsx
|
|
1996
1976
|
/** Root compound component that provides the command state machine context to all children. */
|
|
1997
1977
|
function CommandRoot({ children, className, label = "Command palette", ...machineOptions }) {
|
|
1998
1978
|
const rootRef = useRef(null);
|
|
@@ -2041,8 +2021,7 @@ const Command = Object.assign(CommandRoot, {
|
|
|
2041
2021
|
AsyncItems: CommandAsyncItems,
|
|
2042
2022
|
Activity: CommandActivity
|
|
2043
2023
|
});
|
|
2044
|
-
|
|
2045
|
-
//#region src/react/error-boundary.tsx
|
|
2024
|
+
// src/react/error-boundary.tsx
|
|
2046
2025
|
/** Initial state — no error captured */
|
|
2047
2026
|
const INITIAL_STATE = { error: null };
|
|
2048
2027
|
/** Error boundary for graceful failure recovery in command subtrees. */
|
|
@@ -2064,8 +2043,7 @@ var CommandErrorBoundary = class extends Component {
|
|
|
2064
2043
|
return this.props.children;
|
|
2065
2044
|
}
|
|
2066
2045
|
};
|
|
2067
|
-
|
|
2068
|
-
//#region src/react/hooks/use-command-state.ts
|
|
2046
|
+
// src/react/hooks/use-command-state.ts
|
|
2069
2047
|
function useCommandState(selector) {
|
|
2070
2048
|
const stable = useStableContext("useCommandState");
|
|
2071
2049
|
const selectorRef = useRef(selector);
|
|
@@ -2073,19 +2051,20 @@ function useCommandState(selector) {
|
|
|
2073
2051
|
const getSnapshot = useCallback(() => selectorRef.current ? selectorRef.current(stable.machine.getState()) : stable.machine.getState(), [stable.machine]);
|
|
2074
2052
|
return useSyncExternalStore(stable.machine.subscribe, getSnapshot, getSnapshot);
|
|
2075
2053
|
}
|
|
2076
|
-
|
|
2077
|
-
//#region src/react/hooks/use-devtools.ts
|
|
2054
|
+
// src/react/hooks/use-devtools.ts
|
|
2078
2055
|
/** Branded key for the global devtools instance map */
|
|
2079
2056
|
const DEVTOOLS_KEY = "__CRIMSON_COMMAND_DEVTOOLS__";
|
|
2080
2057
|
/**
|
|
2081
2058
|
* Exposes command palette internals to browser devtools.
|
|
2082
|
-
*
|
|
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.
|
|
2083
2062
|
*
|
|
2084
2063
|
* @example
|
|
2085
2064
|
* ```tsx
|
|
2086
|
-
* function
|
|
2065
|
+
* function DevtoolsBridge() {
|
|
2087
2066
|
* useCommandDevtools('main');
|
|
2088
|
-
* return
|
|
2067
|
+
* return null;
|
|
2089
2068
|
* }
|
|
2090
2069
|
* ```
|
|
2091
2070
|
*/
|
|
@@ -2094,7 +2073,7 @@ function useCommandDevtools(label = "default") {
|
|
|
2094
2073
|
const stateCtx = use(CommandStateContext);
|
|
2095
2074
|
const stateRef = useRef(null);
|
|
2096
2075
|
useEffect(() => {
|
|
2097
|
-
if (!
|
|
2076
|
+
if (!isDev) return;
|
|
2098
2077
|
if (!stable || !stateCtx) return;
|
|
2099
2078
|
const update = () => {
|
|
2100
2079
|
const s = stateCtx.state;
|
|
@@ -2129,5 +2108,4 @@ function useCommandDevtools(label = "default") {
|
|
|
2129
2108
|
label
|
|
2130
2109
|
]);
|
|
2131
2110
|
}
|
|
2132
|
-
//#endregion
|
|
2133
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",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"devDependencies": {
|
|
63
63
|
"globby": "16.2.4",
|
|
64
64
|
"jscodeshift": "17.4.0",
|
|
65
|
-
"radix-ui": "1.
|
|
65
|
+
"radix-ui": "1.8.2-rc.1791984254107",
|
|
66
66
|
"@types/jscodeshift": "17.3.0"
|
|
67
67
|
},
|
|
68
68
|
"keywords": [
|
|
@@ -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
|
}
|
package/CHANGELOG.md
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
# modern-cmdk
|
|
2
|
-
|
|
3
|
-
## 1.2.2
|
|
4
|
-
|
|
5
|
-
### Patch Changes
|
|
6
|
-
|
|
7
|
-
- [`05d3b74`](https://github.com/ABCrimson/modern-cmdk/commit/05d3b74868419c5076452bcdf23d9b7bc6a3fabd) Thanks [@ABCrimson](https://github.com/ABCrimson)! - Evergreen dependency refresh: bump runtime dependency `idb-keyval` to 6.3.0; rebuild with TypeScript 7.0.2, tsdown 0.23.0-rc.0, and React 19.3 canary 20260826 toolchain.
|
|
8
|
-
|
|
9
|
-
- docs: refresh npm README + modernized toolchain — disclose the sole runtime dependency (`idb-keyval`, lazy-loaded only for IndexedDB frecency persistence), note ESM-only + `engines.node >= 26.4.0` and the unpublished/experimental WASM engine, and correct the virtualization threshold to `virtualizeThreshold` (default 100).
|
|
10
|
-
|
|
11
|
-
## 1.2.1
|
|
12
|
-
|
|
13
|
-
### Patch Changes
|
|
14
|
-
|
|
15
|
-
- **docs: documentation accuracy pass** — Corrected the codemod CLI usage everywhere to its real positional form `npx modern-cmdk <transform> <glob>` (one transform per run; only `--dry-run` and `--concurrency=N` are accepted). The previous `migrate` / `--transform` / `--verbose` / `--extensions` / `--ignore` forms never matched the implementation. Replaced the inaccurate `sideEffects: false` claim with the actual scoped array (`./dist/react/styles.css`, `./dist/react/index.mjs`), and clarified that the optional `modern-cmdk-search-wasm` engine is experimental and not yet published to npm. No code, API, or behavior changes — republished so the corrected README ships to npm.
|
|
16
|
-
|
|
17
|
-
## 1.2.0
|
|
18
|
-
|
|
19
|
-
### Minor Changes
|
|
20
|
-
|
|
21
|
-
- **chore: bleeding-edge evergreen upgrade** — Moved the entire toolchain and dependencies to their newest releases: TypeScript 7.0.1-rc (native compiler), React 19.3 canary, radix-ui 1.6.0, Vite 8.1, Vitest 5, Playwright 1.62, Biome 2.5.1, pnpm 11.9, tsdown 0.22.3, and the Rust/WASM crate to edition 2024 + wasm-bindgen 0.2.126. Raised `engines.node` to `>=26.4.0` and bumped the `idb-keyval` runtime dependency to 6.2.5. No public API or behavior changes.
|
|
22
|
-
|
|
23
|
-
- **chore: size-limit measures own code** — The React adapter budget now externalizes the `react`/`react-dom`/`radix-ui` peers and reports the adapter's own gzipped size (~10.2 KB), so the gate stays stable across peer version bumps.
|
|
24
|
-
|
|
25
|
-
- **docs: full accuracy pass** — Corrected every version reference, bundle-size figure, and API example across the README, npm page, and the documentation site to match the upgraded code (including the real `FrecencyEngine`, `KeyboardShortcutRegistry`, and `ParsedShortcut` signatures).
|
|
26
|
-
|
|
27
|
-
## 1.1.5
|
|
28
|
-
|
|
29
|
-
### Patch Changes
|
|
30
|
-
|
|
31
|
-
- **fix(ci): remove `--coverage` from sharded tests** — Vitest coverage thresholds are incompatible with sharding; removed flag to fix CI failures.
|
|
32
|
-
|
|
33
|
-
- **fix: pre-launch readiness** — Aligned package exports, `engines` field, and docs across `modern-cmdk` and `command-search-wasm`. Fixed bundle size limits in root `package.json` and benchmarks documentation (25 KB -> 25.5 KB for React bundle).
|
|
34
|
-
|
|
35
|
-
- **docs: comprehensive API reference expansion** — Expanded `apps/docs/api/command.md` with full core engine documentation covering state machine, search engine, frecency, keyboard registry, telemetry, and branded types.
|
|
36
|
-
|
|
37
|
-
- **chore: docs and build cleanup** — Fixed `Iterator.range` bug in virtualization guide, aligned tsdown targets across packages, updated README bundle sizes and pnpm version.
|
|
38
|
-
|
|
39
|
-
## 1.1.4
|
|
40
|
-
|
|
41
|
-
### Patch Changes
|
|
42
|
-
|
|
43
|
-
- **fix(ci): resolve all 7 Biome lint/format errors** — Trailing zero in OKLCH value, import ordering (value before type), regex line width wrapping, `useExhaustiveDependencies` dependency extraction, `expect.poll()` chain formatting, `noUncheckedIndexedAccess` narrowing in `objectGroupBy`.
|
|
44
|
-
|
|
45
|
-
- **chore(ci): modernize all GitHub Actions workflows** — Added explicit `permissions` blocks (principle of least privilege) and `timeout-minutes` to all jobs across ci.yml, benchmarks.yml, codeql.yml, docs.yml, and release.yml. Improved CI summary table with status labels. Added `security-extended` query suite to CodeQL. Optimized build commands in benchmarks and release workflows.
|
|
46
|
-
|
|
47
|
-
- **chore(deps): enhance Dependabot configuration** — Added dependency groups for TypeScript, Biome, Playwright, and Vitest. Added ignore rules to prevent major version bumps on pinned React pre-releases.
|
|
48
|
-
|
|
49
|
-
## 1.1.3
|
|
50
|
-
|
|
51
|
-
### Patch Changes
|
|
52
|
-
|
|
53
|
-
- **fix(react): virtualization now removes off-screen items from the DOM** — Items check `visibleIdSet` before rendering, reducing DOM nodes from thousands to ~30 in virtualized lists. Latch-based auto-virtualization with hysteresis prevents race conditions.
|
|
54
|
-
|
|
55
|
-
- **fix(core): replace browser-incompatible ES2026 APIs with cross-browser helpers** — Replaced `Temporal.Now.instant()` / `Temporal.Duration` with `Date.now()`, `Math.sumPrecise` with `+=` loops, `Map.groupBy` / `Object.groupBy` with helper functions, `Set.intersection` / `.difference` / `.union` / `.isSubsetOf` with set operation helpers, `Promise.try` with `Promise.resolve().then()`, `String.isWellFormed` with regex-based surrogate replacement. All Iterator Helpers, `using`/`await using`, `Promise.withResolvers`, and `RegExp.escape` remain (supported in target browsers).
|
|
56
|
-
|
|
57
|
-
- **fix(css): WCAG 2.1 AA contrast compliance** — Updated OKLCH color values in dark mode (`--text-muted: oklch(0.83)`, `--text-secondary: oklch(0.75)`) and light mode (`--text-secondary: oklch(0.38)`, `--text-muted: oklch(0.48)`, `--accent: oklch(0.45)`) to meet 4.5:1 contrast ratio requirements. Re-enabled axe-core color-contrast checks in E2E tests.
|
|
58
|
-
|
|
59
|
-
- **fix(css): RTL support** — Changed `transform-origin: left center` to `transform-origin: inline-start center` for logical property compliance.
|
|
60
|
-
|
|
61
|
-
- **fix(e2e): test reliability improvements** — Replaced snapshot reads (`getAttribute`) with auto-retrying assertions (`toHaveAttribute`, `expect.poll()`) throughout virtualization, accessibility, and keyboard test suites. Virtualization tests use `?count=2000` instead of being skipped in CI.
|
|
62
|
-
|
|
63
|
-
## 1.0.2
|
|
64
|
-
|
|
65
|
-
### Patch Changes
|
|
66
|
-
|
|
67
|
-
- [`f5576bb`](https://github.com/ABCrimson/modern-cmdk/commit/f5576bb917e8d254aae20b419ea02dccfbcab7cd) Thanks [@ABCrimson](https://github.com/ABCrimson)! - Performance: split React context to prevent all-item re-renders, O(1) filtered set lookup, single-pass index build, lazy iterators. Fix CSS sideEffects, invalid transform-origin, GPU will-change misuse. Fix broken docs examples and README links.
|
|
68
|
-
|
|
69
|
-
## 1.0.1
|
|
70
|
-
|
|
71
|
-
### Patch Changes
|
|
72
|
-
|
|
73
|
-
- [`f6a9294`](https://github.com/ABCrimson/modern-cmdk/commit/f6a9294d0f868b4848cfad40770f455f3745d56e) Thanks [@ABCrimson](https://github.com/ABCrimson)! - Fix lint issues, O(1) navigate lookup, error-resilient fuzzy scorer, and modernize branding
|
|
74
|
-
|
|
75
|
-
## 1.0.0
|
|
76
|
-
|
|
77
|
-
### Major Changes
|
|
78
|
-
|
|
79
|
-
- [`f00ac85`](https://github.com/ABCrimson/modern-cmdk/commit/f00ac85f23a9823234f507015c423d0aa066a12c) Thanks [@ABCrimson](https://github.com/ABCrimson)! - modern-cmdk 1.0.0 — consolidated package replacing @crimson_dev/command + @crimson_dev/command-react + @crimson_dev/command-codemod.
|
|
80
|
-
|
|
81
|
-
### Import paths
|
|
82
|
-
|
|
83
|
-
- `import { createCommandMachine } from 'modern-cmdk'` — core engine
|
|
84
|
-
- `import { Command } from 'modern-cmdk/react'` — React 19 adapter
|
|
85
|
-
- `import 'modern-cmdk/styles.css'` — default styles
|
|
86
|
-
- `npx modern-cmdk <transform> <glob>` — codemods
|
|
87
|
-
|
|
88
|
-
### Highlights
|
|
89
|
-
|
|
90
|
-
- Pure TypeScript state machine with zero DOM dependencies
|
|
91
|
-
- 14 React 19 compound components with Radix UI Dialog
|
|
92
|
-
- Built-in fuzzy search with incremental filtering
|
|
93
|
-
- Frecency ranking with time-based decay
|
|
94
|
-
- Keyboard shortcut registry
|
|
95
|
-
- GPU-composited CSS animations
|
|
96
|
-
- Full WAI-ARIA combobox pattern
|
|
97
|
-
- Automatic virtualization at 100+ items
|
|
98
|
-
- ES2026: Iterator Helpers, Explicit Resource Management, cross-browser helpers
|