modern-cmdk 1.2.2 → 1.3.0
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 +109 -54
- package/dist/codemod/cli.d.mts +16 -1
- package/dist/codemod/cli.mjs +14173 -60921
- package/dist/codemod/transforms/data-attrs.d.mts +1 -3
- package/dist/codemod/transforms/data-attrs.mjs +2 -3
- 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 +10 -42
- package/dist/core/index.mjs +46 -48
- package/dist/react/index.d.mts +18 -70
- package/dist/react/index.mjs +92 -101
- package/dist/react/styles.css +16 -7
- package/package.json +4 -4
- package/CHANGELOG.md +0 -98
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { API, FileInfo } from "jscodeshift";
|
|
2
|
-
//#region src/codemod/transforms/data-attrs.d.ts
|
|
3
2
|
/**
|
|
4
3
|
* jscodeshift transform — renames cmdk data attributes in JSX and
|
|
5
4
|
* CSS selectors within string/template literals.
|
|
6
5
|
*/
|
|
7
|
-
export default function transform(fileInfo: FileInfo, api: API): string;
|
|
8
|
-
//#endregion
|
|
6
|
+
export default function transform(fileInfo: FileInfo, api: API): string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/codemod/transforms/data-attrs.ts
|
|
2
2
|
/** Mapping from old bare attribute names to new data-attribute names */
|
|
3
3
|
const ATTR_MAP = /* @__PURE__ */ new Map([
|
|
4
4
|
["cmdk-root", "data-command"],
|
|
@@ -14,7 +14,7 @@ const ATTR_MAP = /* @__PURE__ */ new Map([
|
|
|
14
14
|
const CSS_VAR_MAP = /* @__PURE__ */ new Map([["--cmdk-list-height", "--command-list-height"]]);
|
|
15
15
|
/**
|
|
16
16
|
* Replace all cmdk attribute selectors (bracketed) and CSS vars in a string.
|
|
17
|
-
*
|
|
17
|
+
* Runs replaceAll for each ATTR_MAP entry (bracketed selectors), then for each CSS_VAR_MAP entry.
|
|
18
18
|
*/
|
|
19
19
|
function replaceInString(input) {
|
|
20
20
|
let result = input;
|
|
@@ -62,5 +62,4 @@ function transform(fileInfo, api) {
|
|
|
62
62
|
});
|
|
63
63
|
return hasChanges ? root.toSource() : fileInfo.source;
|
|
64
64
|
}
|
|
65
|
-
//#endregion
|
|
66
65
|
export { transform as default };
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { API, FileInfo } from "jscodeshift";
|
|
2
|
-
//#region src/codemod/transforms/forward-ref.d.ts
|
|
3
2
|
/**
|
|
4
3
|
* jscodeshift transform — removes forwardRef wrappers and merges the
|
|
5
4
|
* ref parameter into the props object as a regular prop.
|
|
6
5
|
*/
|
|
7
|
-
export default function transform(fileInfo: FileInfo, api: API): string;
|
|
8
|
-
//#endregion
|
|
6
|
+
export default function transform(fileInfo: FileInfo, api: API): string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/codemod/transforms/forward-ref.ts
|
|
2
2
|
/**
|
|
3
3
|
* jscodeshift transform — removes forwardRef wrappers and merges the
|
|
4
4
|
* ref parameter into the props object as a regular prop.
|
|
@@ -47,5 +47,4 @@ function transform(fileInfo, api) {
|
|
|
47
47
|
}
|
|
48
48
|
return hasChanges ? root.toSource() : fileInfo.source;
|
|
49
49
|
}
|
|
50
|
-
//#endregion
|
|
51
50
|
export { transform as default };
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { API, FileInfo } from "jscodeshift";
|
|
2
|
-
//#region src/codemod/transforms/import-rewrite.d.ts
|
|
3
2
|
/**
|
|
4
3
|
* jscodeshift transform — rewrites all import/require/re-export paths
|
|
5
4
|
* from `cmdk` to `modern-cmdk/react`.
|
|
6
5
|
*/
|
|
7
|
-
export default function transform(fileInfo: FileInfo, api: API): string;
|
|
8
|
-
//#endregion
|
|
6
|
+
export default function transform(fileInfo: FileInfo, api: API): string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/codemod/transforms/import-rewrite.ts
|
|
2
2
|
const OLD_PACKAGE = "cmdk";
|
|
3
3
|
const NEW_PACKAGE = "modern-cmdk/react";
|
|
4
4
|
/**
|
|
@@ -39,5 +39,4 @@ function transform(fileInfo, api) {
|
|
|
39
39
|
});
|
|
40
40
|
return hasChanges ? root.toSource() : fileInfo.source;
|
|
41
41
|
}
|
|
42
|
-
//#endregion
|
|
43
42
|
export { transform as default };
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { API, FileInfo } from "jscodeshift";
|
|
2
|
-
//#region src/codemod/transforms/should-filter.d.ts
|
|
3
2
|
/**
|
|
4
3
|
* jscodeshift transform — migrates shouldFilter prop to filter.
|
|
5
4
|
*/
|
|
6
|
-
export default function transform(fileInfo: FileInfo, api: API): string;
|
|
7
|
-
//#endregion
|
|
5
|
+
export default function transform(fileInfo: FileInfo, api: API): string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/codemod/transforms/should-filter.ts
|
|
2
2
|
/**
|
|
3
3
|
* jscodeshift transform — migrates shouldFilter prop to filter.
|
|
4
4
|
*/
|
|
@@ -15,5 +15,4 @@ function transform(fileInfo, api) {
|
|
|
15
15
|
});
|
|
16
16
|
return hasChanges ? root.toSource() : fileInfo.source;
|
|
17
17
|
}
|
|
18
|
-
//#endregion
|
|
19
18
|
export { transform as default };
|
package/dist/core/index.d.mts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
//#region src/core/search/types.d.ts
|
|
2
1
|
/** Result of scoring a single item against a query */
|
|
3
2
|
interface SearchResult {
|
|
4
3
|
readonly id: ItemId;
|
|
5
4
|
readonly score: number;
|
|
6
|
-
/** Character ranges that matched (for highlighting) — [start, end
|
|
5
|
+
/** Character ranges that matched (for highlighting) — half-open [start, end) tuples, end exclusive */
|
|
7
6
|
readonly matches: ReadonlyArray<readonly [start: number, end: number]>;
|
|
8
7
|
}
|
|
9
8
|
/** A scoring function that evaluates a single item against a query */
|
|
@@ -14,15 +13,13 @@ interface SearchEngine extends Disposable {
|
|
|
14
13
|
index(items: readonly CommandItem[]): void;
|
|
15
14
|
/** Search items by query, return ordered results — uses Iterator pipeline */
|
|
16
15
|
search(query: string, items: readonly CommandItem[]): IteratorObject<SearchResult>;
|
|
17
|
-
/** Remove items from index
|
|
16
|
+
/** Remove items from index by id */
|
|
18
17
|
remove(ids: ReadonlySet<ItemId>): void;
|
|
19
18
|
/** Clear the entire index */
|
|
20
19
|
clear(): void;
|
|
21
20
|
/** Dispose resources */
|
|
22
21
|
[Symbol.dispose](): void;
|
|
23
22
|
}
|
|
24
|
-
//#endregion
|
|
25
|
-
//#region src/core/types.d.ts
|
|
26
23
|
/**
|
|
27
24
|
* Branded type for command item IDs.
|
|
28
25
|
* Prevents accidental mixing with plain strings at the type level.
|
|
@@ -165,7 +162,7 @@ interface FrecencyStorage extends Disposable {
|
|
|
165
162
|
load(namespace: string): FrecencyData | Promise<FrecencyData>;
|
|
166
163
|
save(namespace: string, data: FrecencyData): void | Promise<void>;
|
|
167
164
|
}
|
|
168
|
-
/** Frecency
|
|
165
|
+
/** Frecency step-weight configuration per recency bucket (hour/day/week/month/older) */
|
|
169
166
|
interface FrecencyDecayConfig {
|
|
170
167
|
readonly hourWeight?: number | undefined;
|
|
171
168
|
readonly dayWeight?: number | undefined;
|
|
@@ -182,7 +179,7 @@ interface FrecencyOptions {
|
|
|
182
179
|
}
|
|
183
180
|
/** Default frecency decay configuration */
|
|
184
181
|
export declare const DEFAULT_FRECENCY_DECAY: Required<FrecencyDecayConfig>;
|
|
185
|
-
/** Options for creating a command machine
|
|
182
|
+
/** Options for creating a command machine */
|
|
186
183
|
interface CommandMachineOptions {
|
|
187
184
|
/** Pre-registered items to populate on creation */
|
|
188
185
|
readonly items?: readonly CommandItem[] | undefined;
|
|
@@ -208,17 +205,15 @@ interface CommandMachineOptions {
|
|
|
208
205
|
readonly onSearchChange?: ((query: string) => void) | undefined;
|
|
209
206
|
/** Whether keyboard navigation loops from last to first item (default: true) */
|
|
210
207
|
readonly loop?: boolean | undefined;
|
|
211
|
-
/**
|
|
208
|
+
/** Accepted but not read yet: Command.List uses fixed thresholds (on above 100 matches, off at 50 or fewer) (default: 100) */
|
|
212
209
|
readonly virtualizeThreshold?: number | undefined;
|
|
213
210
|
}
|
|
214
211
|
/** Default machine options */
|
|
215
212
|
export declare const DEFAULT_MACHINE_OPTIONS: Partial<CommandMachineOptions>;
|
|
216
213
|
/** Initial state factory */
|
|
217
214
|
export declare function createInitialState(options?: CommandMachineOptions): CommandState;
|
|
218
|
-
//#endregion
|
|
219
|
-
//#region src/core/frecency/idb-storage.d.ts
|
|
220
215
|
/**
|
|
221
|
-
* IndexedDB-backed frecency storage using idb-keyval
|
|
216
|
+
* IndexedDB-backed frecency storage using idb-keyval.
|
|
222
217
|
*
|
|
223
218
|
* Persists frecency data across browser sessions. Implements both
|
|
224
219
|
* `Disposable` and `AsyncDisposable` for use with `using` and `await using`.
|
|
@@ -252,12 +247,10 @@ export declare class IdbFrecencyStorage implements FrecencyStorage, AsyncDisposa
|
|
|
252
247
|
/** Async dispose — marks as disposed, suitable for `await using` */
|
|
253
248
|
[Symbol.asyncDispose](): Promise<void>;
|
|
254
249
|
}
|
|
255
|
-
//#endregion
|
|
256
|
-
//#region src/core/frecency/index.d.ts
|
|
257
250
|
/**
|
|
258
251
|
* Computes a frecency bonus for an item based on usage frequency and recency.
|
|
259
252
|
* Uses hour/day/week/month bucket boundaries with
|
|
260
|
-
* configurable
|
|
253
|
+
* configurable step weights per recency bucket (hour/day/week/month/older).
|
|
261
254
|
*/
|
|
262
255
|
export declare function computeFrecencyBonus(history: FrecencyRecord, now?: number, config?: FrecencyDecayConfig): number;
|
|
263
256
|
/**
|
|
@@ -288,8 +281,6 @@ export declare class FrecencyEngine implements Disposable, AsyncDisposable {
|
|
|
288
281
|
[Symbol.asyncDispose](): Promise<void>;
|
|
289
282
|
[Symbol.dispose](): void;
|
|
290
283
|
}
|
|
291
|
-
//#endregion
|
|
292
|
-
//#region src/core/frecency/memory-storage.d.ts
|
|
293
284
|
/**
|
|
294
285
|
* In-memory implementation of FrecencyStorage using a Map.
|
|
295
286
|
* Data does not persist across page reloads. Suitable for ephemeral
|
|
@@ -299,14 +290,12 @@ export declare class MemoryFrecencyStorage implements FrecencyStorage {
|
|
|
299
290
|
#private;
|
|
300
291
|
load(namespace: string): FrecencyData;
|
|
301
292
|
save(namespace: string, data: FrecencyData): void;
|
|
302
|
-
/** Serialize all stored data — Iterator Helpers pipeline (
|
|
293
|
+
/** Serialize all stored data — Iterator Helpers pipeline (ES2025) */
|
|
303
294
|
toJSON(): Record<string, {
|
|
304
295
|
records: Array<[string, FrecencyRecord]>;
|
|
305
296
|
}>;
|
|
306
297
|
[Symbol.dispose](): void;
|
|
307
298
|
}
|
|
308
|
-
//#endregion
|
|
309
|
-
//#region src/core/keyboard/parser.d.ts
|
|
310
299
|
/**
|
|
311
300
|
* Structured representation of a keyboard shortcut after parsing.
|
|
312
301
|
* The `normalized` field provides a deterministic string for dedup and comparison.
|
|
@@ -330,8 +319,6 @@ export declare function parseShortcut(shortcut: string): ParsedShortcut;
|
|
|
330
319
|
* On macOS: renders glyphs (⌘, ⇧, ⌥, ⌃). On other platforms: text labels joined by "+".
|
|
331
320
|
*/
|
|
332
321
|
export declare function formatShortcut(parsed: ParsedShortcut): string;
|
|
333
|
-
//#endregion
|
|
334
|
-
//#region src/core/keyboard/matcher.d.ts
|
|
335
322
|
/**
|
|
336
323
|
* Tests whether a KeyboardEvent exactly matches a ParsedShortcut,
|
|
337
324
|
* comparing all four modifier keys (meta, ctrl, shift, alt) and the primary key.
|
|
@@ -345,8 +332,6 @@ export declare function findMatchingShortcut(event: KeyboardEvent, shortcuts: re
|
|
|
345
332
|
* mapGroupBy returns a Map directly — no Object.entries conversion needed.
|
|
346
333
|
*/
|
|
347
334
|
export declare function detectConflicts(shortcuts: readonly ParsedShortcut[]): ReadonlyMap<string, readonly ParsedShortcut[]>;
|
|
348
|
-
//#endregion
|
|
349
|
-
//#region src/core/keyboard/index.d.ts
|
|
350
335
|
interface ShortcutBinding {
|
|
351
336
|
readonly shortcut: ParsedShortcut;
|
|
352
337
|
readonly itemId: ItemId;
|
|
@@ -355,7 +340,7 @@ interface ShortcutBinding {
|
|
|
355
340
|
/**
|
|
356
341
|
* Manages keyboard shortcut bindings with global keydown listening.
|
|
357
342
|
* Supports `using` for automatic cleanup of both individual bindings and the
|
|
358
|
-
* global document listener. Detects conflicts
|
|
343
|
+
* global document listener. Detects conflicts with the local mapGroupBy helper (core/utils/group-by.ts).
|
|
359
344
|
*/
|
|
360
345
|
export declare class KeyboardShortcutRegistry implements Disposable {
|
|
361
346
|
#private;
|
|
@@ -376,8 +361,6 @@ export declare class KeyboardShortcutRegistry implements Disposable {
|
|
|
376
361
|
getBindings(): ReadonlyMap<string, ShortcutBinding>;
|
|
377
362
|
[Symbol.dispose](): void;
|
|
378
363
|
}
|
|
379
|
-
//#endregion
|
|
380
|
-
//#region src/core/registry.d.ts
|
|
381
364
|
/**
|
|
382
365
|
* Manages registration and lookup of command items and groups.
|
|
383
366
|
* Uses set operation helpers (union/intersection/difference) for efficient ID operations.
|
|
@@ -431,8 +414,6 @@ export declare class CommandRegistry implements Disposable {
|
|
|
431
414
|
clear(): void;
|
|
432
415
|
[Symbol.dispose](): void;
|
|
433
416
|
}
|
|
434
|
-
//#endregion
|
|
435
|
-
//#region src/core/machine.d.ts
|
|
436
417
|
/** Command machine — explicit resource management via `using` */
|
|
437
418
|
interface CommandMachine extends Disposable {
|
|
438
419
|
getState(): CommandState;
|
|
@@ -460,8 +441,6 @@ interface CommandMachine extends Disposable {
|
|
|
460
441
|
* ```
|
|
461
442
|
*/
|
|
462
443
|
export declare function createCommandMachine(options?: CommandMachineOptions): CommandMachine;
|
|
463
|
-
//#endregion
|
|
464
|
-
//#region src/core/search/default-scorer.d.ts
|
|
465
444
|
/**
|
|
466
445
|
* Public API: scores a command item against a search query.
|
|
467
446
|
* Multi-strategy: exact → prefix → substring → word-boundary → fuzzy.
|
|
@@ -469,8 +448,6 @@ export declare function createCommandMachine(options?: CommandMachineOptions): C
|
|
|
469
448
|
* which pre-caches lowercase and normalizes query once.
|
|
470
449
|
*/
|
|
471
450
|
export declare function scoreItem(query: string, item: CommandItem): SearchResult | null;
|
|
472
|
-
//#endregion
|
|
473
|
-
//#region src/core/search/fuzzy-scorer.d.ts
|
|
474
451
|
/**
|
|
475
452
|
* Async-safe fuzzy scorer — wraps scoring in Promise for error resilience.
|
|
476
453
|
* Falls back gracefully on error via synchronous-throw capture.
|
|
@@ -482,8 +459,6 @@ export declare function scoreItemAsync(query: string, item: CommandItem): Promis
|
|
|
482
459
|
* entire scoring pipeline in one synchronous pass, wrapped for error safety.
|
|
483
460
|
*/
|
|
484
461
|
export declare function batchScoreItems(query: string, items: readonly CommandItem[]): Promise<SearchResult[]>;
|
|
485
|
-
//#endregion
|
|
486
|
-
//#region src/core/search/index.d.ts
|
|
487
462
|
interface SearchEngineOptions {
|
|
488
463
|
readonly scorer?: ScorerFn | undefined;
|
|
489
464
|
}
|
|
@@ -491,11 +466,9 @@ interface SearchEngineOptions {
|
|
|
491
466
|
* Creates a search engine with pluggable scorer and incremental filtering.
|
|
492
467
|
* When the query extends the previous one, only prior matches are re-scored.
|
|
493
468
|
* Pre-caches lowercase at index time — zero toLowerCase() calls during search.
|
|
494
|
-
* Uses Iterator Helpers (
|
|
469
|
+
* Uses Iterator Helpers (ES2025) for result pipelines.
|
|
495
470
|
*/
|
|
496
471
|
export declare function createSearchEngine(options?: SearchEngineOptions): SearchEngine;
|
|
497
|
-
//#endregion
|
|
498
|
-
//#region src/core/telemetry.d.ts
|
|
499
472
|
interface CommandTelemetryHooks {
|
|
500
473
|
/** Called when the command palette opens */
|
|
501
474
|
readonly onPaletteOpen?: () => void;
|
|
@@ -526,8 +499,6 @@ interface TelemetryMiddleware {
|
|
|
526
499
|
* ```
|
|
527
500
|
*/
|
|
528
501
|
export declare function createTelemetryMiddleware(hooks: CommandTelemetryHooks): TelemetryMiddleware;
|
|
529
|
-
//#endregion
|
|
530
|
-
//#region src/core/utils/event-emitter.d.ts
|
|
531
502
|
type EventMap = {
|
|
532
503
|
[key: string]: unknown;
|
|
533
504
|
};
|
|
@@ -549,8 +520,6 @@ export declare class TypedEmitter<T extends EventMap> implements Disposable {
|
|
|
549
520
|
removeAll(): void;
|
|
550
521
|
[Symbol.dispose](): void;
|
|
551
522
|
}
|
|
552
|
-
//#endregion
|
|
553
|
-
//#region src/core/utils/scheduler.d.ts
|
|
554
523
|
/**
|
|
555
524
|
* Batched update scheduler that coalesces state updates into a single tick.
|
|
556
525
|
* Uses requestAnimationFrame in browsers, queueMicrotask in Node.js/tests.
|
|
@@ -565,5 +534,4 @@ interface Scheduler extends Disposable {
|
|
|
565
534
|
* execution pass using rAF (browser) or queueMicrotask (Node.js).
|
|
566
535
|
*/
|
|
567
536
|
export declare function createScheduler(): Scheduler;
|
|
568
|
-
//#endregion
|
|
569
537
|
export type { CommandEvent, CommandGroup, CommandItem, CommandMachine, CommandMachineOptions, CommandState, CommandTelemetryHooks, FrecencyData, FrecencyDecayConfig, FrecencyOptions, FrecencyRecord, FrecencyStorage, GroupId, ItemId, ParsedShortcut, Scheduler, ScorerFn, SearchEngine, SearchResult, TelemetryMiddleware };
|
package/dist/core/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/core/utils/string-wellformed.ts
|
|
2
2
|
/**
|
|
3
3
|
* Regex matching lone surrogates (unpaired high or low surrogates).
|
|
4
4
|
* A lone high surrogate (U+D800–U+DBFF) not followed by a low surrogate,
|
|
@@ -15,8 +15,7 @@ const REPLACEMENT_CHAR = "�";
|
|
|
15
15
|
function ensureWellFormed(str) {
|
|
16
16
|
return str.replace(LONE_SURROGATE_RE, REPLACEMENT_CHAR);
|
|
17
17
|
}
|
|
18
|
-
|
|
19
|
-
//#region src/core/frecency/idb-storage.ts
|
|
18
|
+
// src/core/frecency/idb-storage.ts
|
|
20
19
|
/** Serialize a FrecencyRecord to a JSON-safe format */
|
|
21
20
|
function serializeRecord(record) {
|
|
22
21
|
return {
|
|
@@ -45,7 +44,7 @@ function storageKey(namespace) {
|
|
|
45
44
|
}
|
|
46
45
|
let idbModule;
|
|
47
46
|
/**
|
|
48
|
-
* IndexedDB-backed frecency storage using idb-keyval
|
|
47
|
+
* IndexedDB-backed frecency storage using idb-keyval.
|
|
49
48
|
*
|
|
50
49
|
* Persists frecency data across browser sessions. Implements both
|
|
51
50
|
* `Disposable` and `AsyncDisposable` for use with `using` and `await using`.
|
|
@@ -119,8 +118,7 @@ var IdbFrecencyStorage = class {
|
|
|
119
118
|
this.#disposed = true;
|
|
120
119
|
}
|
|
121
120
|
};
|
|
122
|
-
|
|
123
|
-
//#region src/core/types.ts
|
|
121
|
+
// src/core/types.ts
|
|
124
122
|
/** Create a branded {@link ItemId} from a plain string. Ensures well-formed Unicode. */
|
|
125
123
|
function itemId(id) {
|
|
126
124
|
return ensureWellFormed(id);
|
|
@@ -158,8 +156,7 @@ function createInitialState(options) {
|
|
|
158
156
|
lastUpdated: Date.now()
|
|
159
157
|
};
|
|
160
158
|
}
|
|
161
|
-
|
|
162
|
-
//#region src/core/frecency/memory-storage.ts
|
|
159
|
+
// src/core/frecency/memory-storage.ts
|
|
163
160
|
/**
|
|
164
161
|
* In-memory implementation of FrecencyStorage using a Map.
|
|
165
162
|
* Data does not persist across page reloads. Suitable for ephemeral
|
|
@@ -173,7 +170,7 @@ var MemoryFrecencyStorage = class {
|
|
|
173
170
|
save(namespace, data) {
|
|
174
171
|
this.#store.set(namespace, data);
|
|
175
172
|
}
|
|
176
|
-
/** Serialize all stored data — Iterator Helpers pipeline (
|
|
173
|
+
/** Serialize all stored data — Iterator Helpers pipeline (ES2025) */
|
|
177
174
|
toJSON() {
|
|
178
175
|
return Object.fromEntries(this.#store.entries().map(([namespace, data]) => [namespace, { records: data.records.entries().toArray() }]));
|
|
179
176
|
}
|
|
@@ -181,11 +178,10 @@ var MemoryFrecencyStorage = class {
|
|
|
181
178
|
this.#store.clear();
|
|
182
179
|
}
|
|
183
180
|
};
|
|
184
|
-
|
|
185
|
-
//#region src/core/frecency/index.ts
|
|
181
|
+
// src/core/frecency/index.ts
|
|
186
182
|
/** Shared empty bonus map — avoids allocation when no frecency records exist */
|
|
187
183
|
const EMPTY_BONUS_MAP = /* @__PURE__ */ new Map();
|
|
188
|
-
/**
|
|
184
|
+
/** Bucket boundaries as hour constants — no per-call unit conversion */
|
|
189
185
|
const BUCKET_HOURS = {
|
|
190
186
|
hour: 1,
|
|
191
187
|
day: 24,
|
|
@@ -195,7 +191,7 @@ const BUCKET_HOURS = {
|
|
|
195
191
|
/**
|
|
196
192
|
* Computes a frecency bonus for an item based on usage frequency and recency.
|
|
197
193
|
* Uses hour/day/week/month bucket boundaries with
|
|
198
|
-
* configurable
|
|
194
|
+
* configurable step weights per recency bucket (hour/day/week/month/older).
|
|
199
195
|
*/
|
|
200
196
|
function computeFrecencyBonus(history, now = Date.now(), config = DEFAULT_FRECENCY_DECAY) {
|
|
201
197
|
const hours = (now - history.lastUsed) / 36e5;
|
|
@@ -284,8 +280,7 @@ var FrecencyEngine = class {
|
|
|
284
280
|
this.#storage[Symbol.dispose]();
|
|
285
281
|
}
|
|
286
282
|
};
|
|
287
|
-
|
|
288
|
-
//#region src/core/utils/group-by.ts
|
|
283
|
+
// src/core/utils/group-by.ts
|
|
289
284
|
/**
|
|
290
285
|
* Groups items from an iterable into a Map keyed by the result of `keyFn`.
|
|
291
286
|
* Cross-browser replacement for `Map.groupBy()`.
|
|
@@ -300,8 +295,7 @@ function mapGroupBy(items, keyFn) {
|
|
|
300
295
|
}
|
|
301
296
|
return map;
|
|
302
297
|
}
|
|
303
|
-
|
|
304
|
-
//#region src/core/keyboard/matcher.ts
|
|
298
|
+
// src/core/keyboard/matcher.ts
|
|
305
299
|
/**
|
|
306
300
|
* Tests whether a KeyboardEvent exactly matches a ParsedShortcut,
|
|
307
301
|
* comparing all four modifier keys (meta, ctrl, shift, alt) and the primary key.
|
|
@@ -326,8 +320,7 @@ function detectConflicts(shortcuts) {
|
|
|
326
320
|
const grouped = mapGroupBy(shortcuts, (s) => s.normalized);
|
|
327
321
|
return new Map(grouped.entries().filter(([, group]) => group.length > 1));
|
|
328
322
|
}
|
|
329
|
-
|
|
330
|
-
//#region src/core/keyboard/parser.ts
|
|
323
|
+
// src/core/keyboard/parser.ts
|
|
331
324
|
const isMac = typeof navigator !== "undefined" && (navigator.userAgentData?.platform === "macOS" || /Mac|iPod|iPhone|iPad/.test(navigator.platform));
|
|
332
325
|
/** Modifier aliases — cross-platform Mod resolution */
|
|
333
326
|
const MODIFIER_MAP = {
|
|
@@ -400,12 +393,11 @@ function formatShortcut(parsed) {
|
|
|
400
393
|
parts.push(displayKey);
|
|
401
394
|
return isMac ? parts.join("") : parts.join("+");
|
|
402
395
|
}
|
|
403
|
-
|
|
404
|
-
//#region src/core/keyboard/index.ts
|
|
396
|
+
// src/core/keyboard/index.ts
|
|
405
397
|
/**
|
|
406
398
|
* Manages keyboard shortcut bindings with global keydown listening.
|
|
407
399
|
* Supports `using` for automatic cleanup of both individual bindings and the
|
|
408
|
-
* global document listener. Detects conflicts
|
|
400
|
+
* global document listener. Detects conflicts with the local mapGroupBy helper (core/utils/group-by.ts).
|
|
409
401
|
*/
|
|
410
402
|
var KeyboardShortcutRegistry = class {
|
|
411
403
|
#bindings = /* @__PURE__ */ new Map();
|
|
@@ -472,8 +464,7 @@ var KeyboardShortcutRegistry = class {
|
|
|
472
464
|
this.#bindings.clear();
|
|
473
465
|
}
|
|
474
466
|
};
|
|
475
|
-
|
|
476
|
-
//#region src/core/utils/set-ops.ts
|
|
467
|
+
// src/core/utils/set-ops.ts
|
|
477
468
|
/** Returns a new Set containing elements present in both `a` and `b`. */
|
|
478
469
|
function setIntersection(a, b) {
|
|
479
470
|
const result = /* @__PURE__ */ new Set();
|
|
@@ -516,8 +507,7 @@ function setSymmetricDifference(a, b) {
|
|
|
516
507
|
else result.add(item);
|
|
517
508
|
return result;
|
|
518
509
|
}
|
|
519
|
-
|
|
520
|
-
//#region src/core/registry.ts
|
|
510
|
+
// src/core/registry.ts
|
|
521
511
|
/**
|
|
522
512
|
* Manages registration and lookup of command items and groups.
|
|
523
513
|
* Uses set operation helpers (union/intersection/difference) for efficient ID operations.
|
|
@@ -678,8 +668,23 @@ var CommandRegistry = class {
|
|
|
678
668
|
this.clear();
|
|
679
669
|
}
|
|
680
670
|
};
|
|
681
|
-
|
|
682
|
-
|
|
671
|
+
// src/core/utils/env.ts
|
|
672
|
+
/**
|
|
673
|
+
* `true` unless `process.env.NODE_ENV` is `'production'`.
|
|
674
|
+
*
|
|
675
|
+
* Consumer bundlers (Vite, webpack, Next, esbuild) replace `process.env.NODE_ENV` statically,
|
|
676
|
+
* so the dev-only branches guarded by this flag fold away in production builds. A browser
|
|
677
|
+
* running the unbundled module throws a ReferenceError on the bare `process` reference;
|
|
678
|
+
* the catch turns that into the production value (`false`), so dev-only code stays off.
|
|
679
|
+
*/
|
|
680
|
+
const isDev = (() => {
|
|
681
|
+
try {
|
|
682
|
+
return process.env.NODE_ENV !== "production";
|
|
683
|
+
} catch {
|
|
684
|
+
return false;
|
|
685
|
+
}
|
|
686
|
+
})();
|
|
687
|
+
// src/core/search/default-scorer.ts
|
|
683
688
|
function isWordSeparator(code) {
|
|
684
689
|
return code === 32 || code === 9 || code === 45 || code === 95 || code === 46 || code === 47 || code === 92;
|
|
685
690
|
}
|
|
@@ -841,18 +846,16 @@ function scoreFuzzy(query, qLen, lowerTarget, tLen) {
|
|
|
841
846
|
matches
|
|
842
847
|
};
|
|
843
848
|
}
|
|
844
|
-
|
|
845
|
-
//#region src/core/search/index.ts
|
|
846
|
-
const __DEV__ = globalThis.process ? globalThis.process.env.NODE_ENV !== "production" : false;
|
|
849
|
+
// src/core/search/index.ts
|
|
847
850
|
let leakRegistry;
|
|
848
|
-
if (
|
|
851
|
+
if (isDev && typeof window !== "undefined") leakRegistry = new FinalizationRegistry((label) => {
|
|
849
852
|
console.warn(`[modern-cmdk] ${label} was garbage collected without being disposed. Use \`using\` or call [Symbol.dispose]() to prevent leaks.`);
|
|
850
853
|
});
|
|
851
854
|
/**
|
|
852
855
|
* Creates a search engine with pluggable scorer and incremental filtering.
|
|
853
856
|
* When the query extends the previous one, only prior matches are re-scored.
|
|
854
857
|
* Pre-caches lowercase at index time — zero toLowerCase() calls during search.
|
|
855
|
-
* Uses Iterator Helpers (
|
|
858
|
+
* Uses Iterator Helpers (ES2025) for result pipelines.
|
|
856
859
|
*/
|
|
857
860
|
function createSearchEngine(options) {
|
|
858
861
|
const customScorer = options?.scorer;
|
|
@@ -861,7 +864,7 @@ function createSearchEngine(options) {
|
|
|
861
864
|
let previousQuery = "";
|
|
862
865
|
let previousResults = /* @__PURE__ */ new Set();
|
|
863
866
|
const sentinel = {};
|
|
864
|
-
if (
|
|
867
|
+
if (leakRegistry) leakRegistry.register(sentinel, "SearchEngine", sentinel);
|
|
865
868
|
return {
|
|
866
869
|
index(items) {
|
|
867
870
|
for (const item of items) indexedEntries.set(item.id, {
|
|
@@ -911,12 +914,11 @@ function createSearchEngine(options) {
|
|
|
911
914
|
[Symbol.dispose]() {
|
|
912
915
|
indexedEntries.clear();
|
|
913
916
|
previousResults.clear();
|
|
914
|
-
if (
|
|
917
|
+
if (leakRegistry) leakRegistry.unregister(sentinel);
|
|
915
918
|
}
|
|
916
919
|
};
|
|
917
920
|
}
|
|
918
|
-
|
|
919
|
-
//#region src/core/utils/event-emitter.ts
|
|
921
|
+
// src/core/utils/event-emitter.ts
|
|
920
922
|
/**
|
|
921
923
|
* Type-safe event emitter with strong references and Disposable subscriptions.
|
|
922
924
|
* Returns Disposable from `on()` for automatic unsubscription via `using`.
|
|
@@ -959,13 +961,13 @@ var TypedEmitter = class {
|
|
|
959
961
|
this.#listeners.clear();
|
|
960
962
|
}
|
|
961
963
|
};
|
|
962
|
-
|
|
963
|
-
//#region src/core/utils/scheduler.ts
|
|
964
|
+
// src/core/utils/scheduler.ts
|
|
964
965
|
/** Whether we're in a browser environment with rAF available */
|
|
965
966
|
const hasRAF = typeof requestAnimationFrame === "function";
|
|
966
|
-
/** Feature detection for cutting-edge scheduling APIs
|
|
967
|
+
/** Feature detection for cutting-edge scheduling APIs, read through globalThis: typeof guards only a bare
|
|
968
|
+
* identifier, so `typeof navigator?.scheduling` would throw where navigator is undeclared (runtimes without one). */
|
|
967
969
|
const canYield = typeof globalThis.scheduler?.yield === "function";
|
|
968
|
-
const hasInputPending = typeof navigator?.scheduling?.isInputPending === "function";
|
|
970
|
+
const hasInputPending = typeof globalThis.navigator?.scheduling?.isInputPending === "function";
|
|
969
971
|
/**
|
|
970
972
|
* Creates a batched update scheduler. Coalesces rapid state updates into a single
|
|
971
973
|
* execution pass using rAF (browser) or queueMicrotask (Node.js).
|
|
@@ -1021,8 +1023,7 @@ function createScheduler() {
|
|
|
1021
1023
|
}
|
|
1022
1024
|
};
|
|
1023
1025
|
}
|
|
1024
|
-
|
|
1025
|
-
//#region src/core/machine.ts
|
|
1026
|
+
// src/core/machine.ts
|
|
1026
1027
|
/**
|
|
1027
1028
|
* Creates a headless command palette state machine with search, frecency,
|
|
1028
1029
|
* and keyboard shortcut support. Implements Disposable for `using` cleanup.
|
|
@@ -1288,8 +1289,7 @@ function createCommandMachine(options = {}) {
|
|
|
1288
1289
|
}
|
|
1289
1290
|
};
|
|
1290
1291
|
}
|
|
1291
|
-
|
|
1292
|
-
//#region src/core/search/fuzzy-scorer.ts
|
|
1292
|
+
// src/core/search/fuzzy-scorer.ts
|
|
1293
1293
|
/**
|
|
1294
1294
|
* Async-safe fuzzy scorer — wraps scoring in Promise for error resilience.
|
|
1295
1295
|
* Falls back gracefully on error via synchronous-throw capture.
|
|
@@ -1309,8 +1309,7 @@ function batchScoreItems(query, items) {
|
|
|
1309
1309
|
resolve(items.values().map((item) => scoreItem(query, item)).filter((r) => r != null).toArray().toSorted((a, b) => b.score - a.score));
|
|
1310
1310
|
});
|
|
1311
1311
|
}
|
|
1312
|
-
|
|
1313
|
-
//#region src/core/telemetry.ts
|
|
1312
|
+
// src/core/telemetry.ts
|
|
1314
1313
|
/**
|
|
1315
1314
|
* Creates a telemetry middleware that measures command palette interactions.
|
|
1316
1315
|
* Uses Date.now() for millisecond-precision timestamps
|
|
@@ -1347,5 +1346,4 @@ function createTelemetryMiddleware(hooks) {
|
|
|
1347
1346
|
}
|
|
1348
1347
|
};
|
|
1349
1348
|
}
|
|
1350
|
-
//#endregion
|
|
1351
1349
|
export { CommandRegistry, DEFAULT_FRECENCY_DECAY, DEFAULT_MACHINE_OPTIONS, FrecencyEngine, IdbFrecencyStorage, KeyboardShortcutRegistry, MemoryFrecencyStorage, TypedEmitter, batchScoreItems, computeFrecencyBonus, createCommandMachine, createInitialState, createScheduler, createSearchEngine, createTelemetryMiddleware, detectConflicts, findMatchingShortcut, formatShortcut, groupId, itemId, matchesShortcut, parseShortcut, scoreItem, scoreItemAsync };
|