-
Notifications
You must be signed in to change notification settings - Fork 0
State Machine
ABCrimson edited this page Mar 11, 2026
·
2 revisions
The core of modern-cmdk is a pure TypeScript state machine that manages all command palette state.
import { createCommandMachine, itemId } from 'modern-cmdk';
// `using` ensures automatic cleanup
using machine = createCommandMachine({
items: [
{ id: itemId('copy'), value: 'Copy to Clipboard', shortcut: 'Mod+C' },
{ id: itemId('paste'), value: 'Paste', shortcut: 'Mod+V' },
{ id: itemId('settings'), value: 'Open Settings', keywords: ['preferences'] },
],
loop: true, // Arrow keys wrap around
frecency: { enabled: true },
});Every state change produces an immutable CommandState snapshot:
interface CommandState {
readonly search: string; // Current search query
readonly activeId: ItemId | null; // Currently highlighted item
readonly filteredIds: readonly ItemId[]; // Items passing the filter
readonly groupedIds: ReadonlyMap<GroupId, readonly ItemId[]>;
readonly filteredCount: number; // Shortcut for filteredIds.length
readonly loading: boolean; // Async items loading
readonly page: string; // Current page
readonly pageStack: readonly string[]; // Page history
}// useSyncExternalStore-compatible (returns unsubscribe function)
const unsubscribe = machine.subscribe(() => {
const state = machine.getState();
console.log('Active item:', state.activeId);
});
// Or use the Disposable pattern
using subscription = machine.subscribeState((state) => {
console.log('Filtered count:', state.filteredCount);
});// Search
machine.send({ type: 'SEARCH_CHANGE', query: 'settings' });
// Navigation
machine.send({ type: 'NAVIGATE', direction: 'next' });
machine.send({ type: 'NAVIGATE', direction: 'prev' });
machine.send({ type: 'NAVIGATE', direction: 'first' });
machine.send({ type: 'NAVIGATE', direction: 'last' });
// Selection
machine.send({ type: 'ITEM_SELECT', id: itemId('copy') });
// Dialog
machine.send({ type: 'OPEN' });
machine.send({ type: 'CLOSE' });
machine.send({ type: 'TOGGLE' });
// Pages
machine.send({ type: 'PAGE_PUSH', page: 'settings' });
machine.send({ type: 'PAGE_POP' });
// Dynamic items
machine.send({ type: 'REGISTER_ITEM', item: { id: itemId('new'), value: 'New Item' } });
machine.send({ type: 'UNREGISTER_ITEM', id: itemId('new') });The machine implements Disposable for automatic cleanup:
{
using machine = createCommandMachine({ items });
// ... use machine
} // machine[Symbol.dispose]() called automatically — all listeners removed