Command Palette
oge-command-palette is a modal search box over every action of an app — opened with Ctrl+K (⌘K on a Mac) or from code.
The query fuzzy-matches the labels — accent- and case-insensitive, prefixes and word starts first — plus each command's keywords and description; matches stay in their groups and the matched letters are highlighted. An empty query lists the recently run commands first. The search box is an APG combobox over a listbox, the dialog the shared modal shell: focus trap, scroll lock, Escape and a backdrop press to close, focus back where it was.
Basics
Commands carry a label, an optional group, keywords, description, shortcut and icon; a disabled one is listed but never runs. Type “gtst” or “prefs” to see the fuzzy match. The default hotkey is Mod+K; this site already uses Ctrl+K for its search, so the demo listens for Ctrl+/ (⌘/).
Nothing run yet
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { OgeCommandPalette } from '@oge-ui/overlay';
import type { OgeCommand, OgeCommandExecuteEvent } from '@oge-ui/overlay';
@Component({
selector: 'demo-root',
imports: [OgeCommandPalette],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<!-- The default hotkey is Mod+K (Ctrl+K, or ⌘K on a Mac); this site
uses Ctrl+K for its own search, so the demo takes Mod+/. -->
<button type="button" (click)="palette.open()">Open the palette</button>
<oge-command-palette
#palette
[commands]="commands"
hotkey="Mod+/"
(commandExecute)="ran($event)"
/>
<p>{{ last() }}</p>
`,
})
export class Demo {
protected readonly last = signal('Nothing run yet');
protected readonly commands: OgeCommand[] = [
{ id: 'new-invoice', label: 'New invoice', group: 'Create', shortcut: 'Mod+Alt+N' },
{ id: 'new-customer', label: 'New customer', group: 'Create', keywords: ['client'] },
{ id: 'go-dashboard', label: 'Go to dashboard', group: 'Navigate', keywords: ['home'] },
{
id: 'go-reports',
label: 'Go to reports',
group: 'Navigate',
description: 'Sales, stock and cash flow',
},
{
id: 'go-settings',
label: 'Go to settings',
group: 'Navigate',
keywords: ['preferences'],
shortcut: 'Mod+,',
},
{ id: 'toggle-theme', label: 'Toggle dark theme', group: 'Preferences' },
{ id: 'export-pdf', label: 'Export as PDF', group: 'Preferences', disabled: true },
];
protected ran(event: OgeCommandExecuteEvent): void {
this.last.set(`Ran "${event.command.label}"`);
}
}Recent commands & keepOpen
[(recent)] is two-way — store it to keep the “Recent” section across visits. A handler that sets keepOpen leaves the palette open, for commands people repeat.
Count: 0 · recent: none
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { OgeCommandPalette } from '@oge-ui/overlay';
import type { OgeCommand, OgeCommandExecuteEvent } from '@oge-ui/overlay';
@Component({
selector: 'demo-root',
imports: [OgeCommandPalette],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<!-- [(recent)] is two-way: store it to keep the "Recent" section across
visits. keepOpen leaves the palette open for repeatable commands. -->
<button type="button" (click)="palette.open()">Open with recent commands</button>
<oge-command-palette
#palette
[commands]="counterCommands"
[(recent)]="recent"
[recentLimit]="3"
[hotkey]="null"
placeholder="Search counter commands…"
(commandExecute)="run($event)"
/>
<p>Count: {{ count() }} · recent: {{ recent().join(', ') || 'none' }}</p>
`,
})
export class Demo {
protected readonly count = signal(0);
protected readonly recent = signal<readonly string[]>([]);
protected readonly counterCommands: OgeCommand[] = [
{ id: 'increment', label: 'Increment', description: 'Keeps the palette open' },
{ id: 'decrement', label: 'Decrement', description: 'Keeps the palette open' },
{ id: 'reset', label: 'Reset the counter' },
];
protected run(event: OgeCommandExecuteEvent): void {
const id = event.command.id;
if (id === 'reset') this.count.set(0);
else this.count.update((n) => n + (id === 'increment' ? 1 : -1));
event.keepOpen = id !== 'reset';
}
}