# Command Palette

> ⌘K-style centred modal with a search input and filtered list of actions. Items group automatically, support icons, descriptions, and shortcut hints. Bind a global hotkey (default `mod+k`) or open it programmatically.

Source: https://ngwr.dev/reference/components/command-palette  
Kind: Component, Standalone

## Installation

```angular-ts
import { WrCommandPalette, type WrCommandItem } from 'ngwr/command-palette';

@Component({ imports: [WrCommandPalette] })
export class AppShell {
  protected readonly commands: WrCommandItem[] = [
    { id: 'theme.light', label: 'Switch to light theme', group: 'Theme', shortcut: 'T L' },
    { id: 'theme.dark',  label: 'Switch to dark theme',  group: 'Theme', shortcut: 'T D' },
    { id: 'docs.search', label: 'Search docs',           group: 'Docs',  shortcut: '/' },
  ];

  protected onPicked(item: WrCommandItem) {
    console.log('picked', item.id);
  }
}
```

## Basic usage

Drop a single palette at your app shell. The default hotkey `mod+k` (⌘K on macOS, Ctrl+K elsewhere) opens it. The `(picked)` output fires after the item's own `action()` callback if it has one.

```angular-html
<!-- Drop at the root once; opens via global hotkey (default: mod+k). -->
<wr-command-palette
  [items]="commands"
  trigger="mod+k"
  (picked)="onPicked($event)"
/>
```

## Live demo

Click the button or press ⌘K / Ctrl+K. Type to filter. ↑/↓ to move, Enter to pick, Esc to close.

## Responsive (full-screen)

With `responsive` — or app-wide via `provideWrResponsiveOverlays()` — the palette fills the screen from the top edge on small viewports instead of a centred modal (it auto-focuses its input, so it stays clear of the on-screen keyboard). Open this on a phone (or narrow the window below 640px).

```html
<wr-command-palette responsive [items]="items" [(open)]="open" />
```

## Programmatic control

Set `[(open)]` and pass `[trigger]` of `null` to disable the auto-bound hotkey when you want to open it from your own UI.

```angular-html
<button (click)="open.set(true)">Open palette</button>

<wr-command-palette
  [items]="commands"
  [(open)]="open"
  [trigger]="null"
  (picked)="onPicked($event)"
/>
```

## Backed by a server

`[items]` does not have to be a fixed list. Bind `[(query)]` to read what the user typed, dispatch on `(searchChange)` — which fires once the query settles, not once per keystroke — and feed the answer back into `[items]`. Add `serverSearch` so the built-in substring filter steps aside: a backend that ranks or tolerates typos returns rows whose labels do not contain the query at all, and filtering them again on the client would throw away the good ones. `[loading]` swaps the empty row for “Searching…”, because a palette that reports “No results” between every keystroke and its answer is stating something that is not true yet.

```angular-html
<wr-command-palette
  [items]="results()"
  [(query)]="query"
  [loading]="loading()"
  serverSearch
  [debounceMs]="250"
  (searchChange)="search($event)"
/>
```

## API

| Name | Description | Type | Default |
| --- | --- | --- | --- |
| `paletteLabel` | Accessible name. Falls back to `commandPalette.label`, then `'Command palette'`. | `string \| null` | `null` |
| `items` | Items shown in the palette. | `readonly WrCommandItem[]` | `[]` |
| `open` | Controlled open state (two-way bindable). | `boolean` | `false` |
| `trigger` | Global hotkey that opens the palette. `null` disables auto-binding. | `WrHotkeySpec \| null` | `'mod+k'` |
| `placeholder` | Search input placeholder. Falls back to `commandPalette.placeholder`. | `string \| null` | `null` |
| `emptyText` | Text shown when no items match. Falls back to `commandPalette.noResults`. | `string \| null` | `null` |
| `loadingText` | Overrides the `commandPalette.loading` catalog string, shown instead of {@link emptyText} while {@link loading} is true. | `string \| null` | `null` |
| `closeOnPick` | Auto-close on `(picked)`. Set false to keep open. | `boolean` | `true` |
| `responsive` | Present the palette full-screen on small viewports instead of a centred modal. `undefined` follows the app-wide `provideWrResponsiveOverlays()` setting; `true`/`false` overrides it. The palette docks to the top (it auto-focuses its input, so the on-screen keyboard stays clear). | `boolean \| undefined` | `undefined` |
| `(picked)` | Fires when the user commits an item (Enter / click). | `WrCommandItem` | — |
| `query` | The search text. Two-way, so a host can seed it, clear it, or read what the user is looking for — the palette used to keep this to itself, which is what made it impossible to back with anything but a static `items` array. Writing it does NOT re-run a host's search on its own: the debounced {@link searchChange} output is what fires, on exactly the cadence {@link debounceMs} sets. | `string` | `''` |
| `serverSearch` | The item list is already scoped to the query upstream — skip the built-in client-side filter. Set it when `[items]` is fed from a server via the {@link searchChange} output. Without this, a backend that ranks or tolerates typos (returning rows whose labels do not literally contain the query) would have those rows hidden again on the client — the same rule `wr-select` follows, and the same reason. | `boolean` | `false` |
| `loading` | A host-driven search is in flight. Swaps the empty row for {@link loadingText} — an async palette that says "No results" between every keystroke and its answer is stating something false. | `boolean` | `false` |
| `debounceMs` | How long the query must settle before {@link searchChange} fires, in ms. A search request per keystroke is what exhausts a metered search backend: typing `select` is six requests at 0 and roughly one at 250. | `number` | `250` |
| `(searchChange)` | The settled query. This is the hook for a server-backed palette: dispatch on it, then feed the result into `[items]` with `[serverSearch]` set. Fires on the {@link debounceMs} cadence, NOT per keystroke — `queryChange` is the per-keystroke signal if that is what you want. | `string` | — |

## Types

| Name | Description | Type | Default |
| --- | --- | --- | --- |
| `WrCommandItem` | `{ id, label, description?, group?, icon?, keywords?, shortcut?, action? }` | `interface` | `—` |
