import { ReactNode } from 'react'; /** A single command item in the palette */ interface CommandItem { /** Unique identifier */ id: string; /** Display label shown in the palette */ label: string; /** Optional description text */ description?: string; /** Icon — string, emoji, or React element (e.g. ``) */ icon?: ReactNode; /** Additional search terms for fuzzy matching */ keywords?: string[]; /** Group this command belongs to */ group?: string; /** Manual ordering weight (higher = appears first) */ priority?: number; /** Navigation target URL/path for route commands */ href?: string; /** Execution callback when command is selected */ action?: (item: CommandItem) => void | Promise; /** Whether this command is disabled */ disabled?: boolean; /** Hidden from results but still searchable */ hidden?: boolean; /** Required permissions to see this command */ permissions?: string[]; /** Keyboard shortcut display (e.g., ["g", "h"]) */ shortcut?: string[]; /** Extensible metadata for consumer use */ meta?: Record; /** Scopes where this command is most relevant (e.g., ['/billing', '/billing/*']) */ scope?: string[]; /** Child commands for nested/hierarchical menus */ children?: CommandItem[]; /** Parent command ID (set automatically when flattening) */ parentId?: string; } /** Command registry — the central store for all commands */ interface CommandRegistry { /** Register a single command. Returns an unregister function. */ register(command: CommandItem): () => void; /** Register multiple commands. Returns an unregister function for all. */ registerMany(commands: CommandItem[]): () => void; /** Update a command by ID with partial fields */ update(id: string, partial: Partial): void; /** Remove a command by ID */ unregister(id: string): void; /** Get all registered commands */ getAll(): CommandItem[]; /** Get a command by ID */ getById(id: string): CommandItem | undefined; /** Get commands in a specific group */ getByGroup(groupId: string): CommandItem[]; /** Subscribe to changes. Returns an unsubscribe function. */ subscribe(listener: () => void): () => void; /** Get a stable snapshot for useSyncExternalStore */ getSnapshot(): CommandItem[]; } /** A search engine implementation */ interface SearchEngine { /** Search items against a query string. Returns scored results. */ search(query: string, items: CommandItem[]): ScoredItem[]; } /** A command item with a relevance score */ interface ScoredItem { /** The matched command item */ item: CommandItem; /** Relevance score from 0 (worst) to 1 (best match) */ score: number; } /** Interface for checking user permissions */ interface AccessControlProvider { /** Check if user has a specific permission */ hasPermission(permission: string): boolean; /** Check if user has ANY of the given permissions */ hasAnyPermission(permissions: string[]): boolean; /** Check if user has ALL of the given permissions */ hasAllPermissions(permissions: string[]): boolean; } /** Access control check mode */ type AccessCheckMode = 'any' | 'all'; /** A single frecency tracking entry */ interface FrecencyEntry { /** Command ID */ id: string; /** Number of times used */ count: number; /** Unix timestamp of last use */ lastUsed: number; /** Decayed score based on frequency + recency */ halfLifeScore: number; } /** Storage backend for frecency data */ interface FrecencyStorage { /** Get entry by key */ get(key: string): FrecencyEntry | null; /** Set entry */ set(key: string, entry: FrecencyEntry): void; /** Get all entries */ getAll(): FrecencyEntry[]; /** Clear all entries */ clear(): void; } /** Frecency engine configuration */ interface FrecencyOptions { /** Storage backend (defaults to localStorage) */ storage?: FrecencyStorage; /** localStorage key prefix (default: 'cmdk-frecency') */ storageKey?: string; /** Max age in days before entries are removed (default: 30) */ maxAge?: number; /** Half-life in days for exponential decay (default: 7) */ halfLife?: number; } /** A command group definition */ interface CommandGroup { /** Unique group identifier */ id: string; /** Display label for the group */ label: string; /** Rendering priority (higher = rendered first) */ priority?: number; /** Optional icon for the group */ icon?: ReactNode; } /** Map of keyword → synonyms */ interface SynonymMap { [keyword: string]: string[]; } /** Metadata for a route command, colocated in route definitions */ interface RouteCommandMeta { /** Display label (falls back to path-derived label) */ label?: string; /** Description text */ description?: string; /** Additional search keywords */ keywords?: string[]; /** Group ID */ group?: string; /** Icon — string, emoji, or React element */ icon?: ReactNode; /** Required permissions */ permissions?: string[]; /** Ordering priority */ priority?: number; /** Whether to hide from palette */ hidden?: boolean; } /** Configuration file shape for cmdk-engine.config.ts */ interface CmdkEngineConfig { /** Framework to scan (auto-detected if not set) */ framework?: 'react-router' | 'nextjs-app' | 'nextjs-pages' | 'custom'; /** Directory to scan for routes */ routesDir?: string; /** Output file for generated route map */ output?: string; /** Custom keyword/metadata overrides per route path */ overrides?: Record>; /** Route paths/patterns to exclude */ exclude?: string[]; /** Synonym dictionary */ synonyms?: SynonymMap; } /** Generated route sitemap entry */ interface SitemapRoute { /** Unique route ID */ id: string; /** Route path */ path: string; /** Display label */ label: string; /** Search keywords */ keywords: string[]; /** Group name */ group?: string; /** Source file path */ source?: string; } /** Full generated sitemap output */ interface Sitemap { /** Schema version */ version: number; /** Generation timestamp (ISO 8601) */ generatedAt: string; /** Detected framework */ framework: string; /** All discovered routes */ routes: SitemapRoute[]; } /** Current app context for scope-aware command boosting */ interface CommandContext { /** Current path/URL (e.g., '/billing/overview') */ path?: string; /** Current page/section tags for matching */ tags?: string[]; } /** Translation function — maps a key to a localized string */ type TranslationFn = (key: string, params?: Record) => string; /** A recorded search query */ interface SearchHistoryEntry { /** The search query string */ query: string; /** Timestamp when the search was performed */ timestamp: number; /** Number of results returned */ resultCount: number; } /** Configuration for search history tracking */ interface SearchHistoryConfig { /** Enable search history tracking (default: false) */ enabled?: boolean; /** Maximum history entries to keep (default: 20) */ maxEntries?: number; /** localStorage key prefix (default: 'cmdk-search-history') */ storageKey?: string; /** Minimum query length to record (default: 2) */ minQueryLength?: number; } /** Full command palette state */ interface CommandPaletteState { /** Current search query */ search: string; /** Filtered and ranked results */ results: ScoredItem[]; /** Available groups (only groups with visible items) */ groups: CommandGroup[]; /** Whether the palette is open */ isOpen: boolean; /** Whether results are loading (async) */ isLoading: boolean; /** Breadcrumb trail of parent commands (for nested navigation) */ breadcrumbs: CommandItem[]; /** Current nesting depth (0 = root) */ depth: number; } /** Configuration for the CommandEngine React provider */ interface CommandEngineConfig { /** Custom search engine (defaults to built-in fuzzy search) */ searchEngine?: SearchEngine; /** Access control provider for permission filtering */ accessControl?: AccessControlProvider; /** Access check mode: 'any' or 'all' permissions required */ accessCheckMode?: AccessCheckMode; /** Synonym dictionary for keyword expansion */ synonyms?: SynonymMap; /** Frecency configuration */ frecency?: FrecencyOptions & RecentCommandsConfig; /** Group definitions and ordering */ groups?: CommandGroup[]; /** Maximum results to return */ maxResults?: number; /** Centralized handler when a command is selected. Auto-records frecency. */ onSelect?: (item: CommandItem) => void; /** Current context for scope-aware command boosting */ context?: CommandContext; /** Boost weight for in-scope commands (0-1, default: 0.2) */ contextBoostWeight?: number; /** Translation function for UI strings (defaults to English) */ t?: TranslationFn; /** Locale for collation-aware operations (default: 'en') */ locale?: string; /** Search history configuration */ searchHistory?: SearchHistoryConfig; } /** Configuration for the "Recent" commands group */ interface RecentCommandsConfig { /** Show a "Recent" group when search is empty (default: false) */ showRecent?: boolean; /** Number of recent items to show (default: 5) */ recentCount?: number; /** Label for the recent group (default: "Recent") */ recentLabel?: string; } /** Helper to define a cmdk-engine config file with type checking */ declare function defineConfig(config: CmdkEngineConfig): CmdkEngineConfig; /** * Create a new command registry — the central store for all commands. * * The registry implements a pub/sub pattern compatible with React's * useSyncExternalStore. Commands can be registered, updated, and removed, * and subscribers are notified of changes via batched microtask updates. */ declare function createRegistry(): CommandRegistry; /** * Built-in lightweight fuzzy search engine. * Searches label, description, and keywords fields. * No external dependencies. Target: < 1KB gzipped. * * Scoring bonuses: * - Exact prefix match (highest) * - Word boundary match * - Consecutive character matches * - Case-sensitive exact match */ declare function createFuzzySearch(): SearchEngine; /** * Create a keyword engine for synonym expansion and user alias management. * * The engine enriches command items with additional searchable keywords * based on a synonym dictionary and optional user-defined aliases. */ declare function createKeywordEngine(synonyms?: SynonymMap, userAliases?: Map): { /** * Expand a search query using the synonym dictionary. * Returns the original query plus any synonym matches. * * e.g., query "money" with synonyms { billing: ["money", "payment"] } * returns ["money", "billing"] */ expandQuery(query: string): string[]; /** * Enrich a command item's keywords with synonyms and user aliases. * Returns a new CommandItem with original keywords preserved and * synonym-expanded keywords stored separately in meta._synonymKeywords. * This lets the search engine score original keywords higher. */ enrichItem(item: CommandItem): CommandItem; /** * Enrich all items in a list. */ enrichAll(items: CommandItem[]): CommandItem[]; /** * Add a user alias for a command. */ addAlias(commandId: string, alias: string): void; /** * Remove a user alias for a command. */ removeAlias(commandId: string, alias: string): void; /** * Get all user aliases. */ getAliases(): Map; /** * Set the synonym dictionary. */ setSynonyms(newSynonyms: SynonymMap): void; }; /** * Create an access control filter that removes commands the user * doesn't have permission to see. * * This is a UI filter, not a security boundary. Always enforce * permissions server-side. * * @param provider - The permission checking backend * @param mode - 'any' = user needs any listed permission, 'all' = needs all (default: 'any') */ declare function createAccessFilter(provider: AccessControlProvider, mode?: AccessCheckMode): (items: CommandItem[]) => CommandItem[]; /** * Create a simple access control provider from a set of permissions. * Useful for static permission sets. */ declare function createSimpleAccessProvider(userPermissions: string[] | Set): AccessControlProvider; /** * Create a frecency engine that tracks usage patterns and * ranks items by frequency + recency using exponential decay. * * Algorithm: * score = count * 2^(-timeSinceLastUse / halfLife) * * This means: * - An item used 10 times yesterday ranks higher than one used 100 times a month ago * - The half-life (default 7 days) controls how quickly old usage decays */ declare function createFrecencyEngine(options?: FrecencyOptions): { recordUsage: (commandId: string) => void; getScore: (commandId: string) => number; getRecent: (count?: number) => string[]; rank: (items: ScoredItem[], frecencyWeight?: number) => ScoredItem[]; cleanup: () => void; clear: () => void; }; /** * In-memory frecency storage (for testing or SSR). */ declare function createInMemoryStorage(): FrecencyStorage; /** * Create a localStorage-backed frecency storage. * Falls back gracefully in SSR or when localStorage is unavailable. * * @param storageKey - localStorage key prefix (default: 'cmdk-frecency') */ declare function createLocalStorageFrecencyStorage(storageKey?: string): FrecencyStorage; /** * Create a group manager that tracks command groups and their ordering. */ declare function createGroupManager(initialGroups?: CommandGroup[]): { /** Add or update a group definition */ addGroup(group: CommandGroup): void; /** Remove a group */ removeGroup(id: string): void; /** Get a group by ID */ getGroup(id: string): CommandGroup | undefined; /** Get all defined groups sorted by priority (higher first) */ getAllGroups(): CommandGroup[]; /** * Group scored items by their group field. * When query is empty: groups ordered by priority (higher first). * When query is non-empty: groups ordered by best item score (relevance). * Items without a group go into an "Other" bucket at the end. */ groupResults(items: ScoredItem[], query?: string): GroupedResults; /** * Extract unique groups from a list of commands. * Returns only groups that have at least one command. */ extractGroups(commands: CommandItem[]): CommandGroup[]; }; /** A group with its scored items */ interface GroupedResult { group: CommandGroup; items: ScoredItem[]; } /** Array of grouped results */ type GroupedResults = GroupedResult[]; /** * Create a context engine that boosts commands matching the current app context. * * Commands with a `scope` field (e.g., ['/billing', '/billing/*']) get a score * boost when the current context path matches. * * @param boostWeight - How much to boost in-scope commands (0-1, default: 0.2) */ declare function createContextEngine(boostWeight?: number): { /** * Boost scores for items whose scope matches the current context. * Items without a scope are unaffected. */ boost(items: ScoredItem[], context: CommandContext): ScoredItem[]; }; /** * Create the default English translation function. * Returns the English string for known keys, or the key itself as fallback. */ declare function createDefaultTranslation(): TranslationFn; /** * Get the list of all known translation keys. * Useful for consumers building custom translation dictionaries. */ declare function getTranslationKeys(): string[]; /** * Create a search history tracker that records search queries. * Separate from frecency (which tracks command usage). * * Uses localStorage for persistence (same pattern as frecency-storage.ts). */ declare function createSearchHistory(config?: SearchHistoryConfig): { /** Record a search query. Deduplicates by query string (updates timestamp). */ record(query: string, resultCount: number): void; /** Get recent search queries, sorted by timestamp descending. */ getRecent(count?: number): SearchHistoryEntry[]; /** Remove a specific search query from history. */ remove(query: string): void; /** Clear all search history. */ clear(): void; }; /** * In-memory search history (for testing or SSR). */ declare function createInMemorySearchHistory(config?: SearchHistoryConfig): { record(query: string, resultCount: number): void; getRecent(count?: number): SearchHistoryEntry[]; remove(query: string): void; clear(): void; }; /** * Convert a path segment to a readable label. * Handles kebab-case, snake_case, camelCase, PascalCase. * * Examples: * "user-settings" -> "User Settings" * "phone_numbers" -> "Phone Numbers" * "phoneNumbers" -> "Phone Numbers" * "BillingOverview" -> "Billing Overview" */ declare function pathSegmentToLabel(segment: string): string; /** * Convert a full path to a readable label. * Uses the last meaningful segment. * * "/billing/overview" -> "Billing Overview" * "/settings" -> "Settings" * "/" -> "Home" */ declare function pathToLabel(path: string): string; /** * Convert a full path to a group name. * Uses the first meaningful segment. * * "/billing/overview" -> "Billing" * "/settings/team" -> "Settings" */ declare function pathToGroup(path: string): string | undefined; /** * Generate a stable ID from a route path. * "/billing/overview" -> "billing-overview" */ declare function pathToId(path: string): string; export { type AccessCheckMode, type AccessControlProvider, type CmdkEngineConfig, type CommandContext, type CommandEngineConfig, type CommandGroup, type CommandItem, type CommandPaletteState, type CommandRegistry, type FrecencyEntry, type FrecencyOptions, type FrecencyStorage, type GroupedResult, type GroupedResults, type RecentCommandsConfig, type RouteCommandMeta, type ScoredItem, type SearchEngine, type SearchHistoryConfig, type SearchHistoryEntry, type Sitemap, type SitemapRoute, type SynonymMap, type TranslationFn, createAccessFilter, createContextEngine, createDefaultTranslation, createFrecencyEngine, createFuzzySearch, createGroupManager, createInMemorySearchHistory, createInMemoryStorage, createKeywordEngine, createLocalStorageFrecencyStorage, createRegistry, createSearchHistory, createSimpleAccessProvider, defineConfig, getTranslationKeys, pathSegmentToLabel, pathToGroup, pathToId, pathToLabel };