# Command palette

> A command palette with fuzzy search and shortcuts. A native Pyzed component for Python desktop GUIs, with a live preview, code examples and its API.

Source: https://albakore.github.io/pyzed/library/command-palette/

:::tip[In plain words]
A search box for every action of the app: type what you want to do and press Enter.
:::

A command palette: it fuzzy-filters `commands` (`ui.PaletteItem(label, on_press, keys=)`) by `query`, highlights the matched letters and shows each shortcut. You own the query and the `selected` index; Enter calls `on_submit`.

`ui.command_palette` · Category: [Overlays](https://albakore.github.io/pyzed/library/#overlays) · [Full reference →](https://albakore.github.io/pyzed/reference/ui/#command_palette)

**Code:**

```python
ui.command_palette("tog", on_query, [
    ui.PaletteItem("editor: toggle soft wrap", on_wrap, keys="alt-z"),
    ui.PaletteItem("theme selector: toggle", on_theme, keys="ctrl-k ctrl-t"),
    ui.PaletteItem("workspace: toggle left dock", on_left, keys="ctrl-b"),
    ui.PaletteItem("terminal panel: toggle", on_terminal, keys="ctrl-j"),
], selected=0, width=520)
```

## When to use

- To make every action reachable from the keyboard (usually with Ctrl+Shift+P).

## Usage

```python
import pyzed as pz
from pyzed import ui

class App(pz.App):
    def __init__(self):
        self.open, self.query, self.selected = False, "", 0

    def commands(self):
        return [ui.PaletteItem("editor: toggle soft wrap", self.toggle_wrap, keys="alt-z"),
                ui.PaletteItem("workspace: toggle left dock", self.toggle_left, keys="ctrl-b")]

    def on_key(self, combo):
        if combo == "ctrl-shift-p":
            self.open, self.query, self.selected = True, "", 0
            return pz.Command.focus("command-palette")
        if self.open and combo in ("up", "down"):
            self.selected += 1 if combo == "down" else -1
        elif combo == "escape":
            self.open = False

    def run_selected(self):
        matches = ui.fuzzy_filter(self.query, self.commands(), key=lambda c: c.label)
        if matches:
            self.open = False
            return matches[self.selected % len(matches)][0].on_press()

    def view(self):
        palette = ui.command_palette(self.query, lambda q: setattr(self, "query", q), self.commands(),
                                     selected=self.selected, on_submit=self.run_selected)
        return ui.layer(self.main_view(), palette if self.open else None,
                        on_dismiss=lambda: setattr(self, "open", False))
```

## Variants

:::note[About these captures]
Each capture is recorded by pyzed itself, off-screen at 2x, in One Dark or One Light to match this site's theme, and cropped to the component so it shows at its real size. The pointer hovers, presses or types into it; a component that does not react to the pointer cycles through a few themes. With reduced motion you see the first frame. Open **Code** under a capture to see the snippet that draws it; free names such as `on_save` stand for your own callbacks.
:::

### Query

```python
ui.command_palette("", on_query, [
    ui.PaletteItem("file: new", on_new, keys="ctrl-n"),
    ui.PaletteItem("file: open", on_open, keys="ctrl-o"),
    ui.PaletteItem("theme selector: toggle", on_theme),
], width=420)
```

```python
ui.command_palette("op", on_query, [
    ui.PaletteItem("file: new", on_new, keys="ctrl-n"),
    ui.PaletteItem("file: open", on_open, keys="ctrl-o"),
    ui.PaletteItem("workspace: open recent", on_recent, keys="ctrl-r"),
], width=420)
```

```python
ui.command_palette("", on_query, [
    ui.PaletteItem("file: new", on_new, keys="ctrl-n"),
    ui.PaletteItem("file: open", on_open, keys="ctrl-o"),
    ui.PaletteItem("file: save", on_save, keys="ctrl-s"),
], selected=1, width=420)
```

## Events

Command palette has **3 events of its own**, listed below. Like every widget it also has the **18 common events** (`click`, `double_click`, `right_click`, `mouse_enter`, `drag`, `key_down`, `resize`…), attached with `.on(...)`: see [Events](https://albakore.github.io/pyzed/guides/events/) and [Events of every component](https://albakore.github.io/pyzed/library/events/).

| Event | Of | Handler gets | When |
| --- | --- | --- | --- |
| `on_query` | `ui.command_palette` | `(text)` | Callable that receives the new text. |
| `on_submit` | `ui.command_palette` | `()` | Message or callable sent when Enter is pressed. |
| `on_press` | `ui.PaletteItem` | `()` | Message or callable sent on press. `None` disables the control. |

**Handler gets** is what Pyzed passes to your callable: `()` means nothing. Any handler can also be a message (a value that is not callable), which goes to `update()`.

### Handling the events

The same events written three ways. **Imperative**: methods of the app change `self`. **Declarative**: the handlers produce messages and one `update()` decides. **Functional**: a pure function draws an immutable state and asks for changes with `set_state(...)`. See [Imperative, declarative and functional](https://albakore.github.io/pyzed/guides/code-styles/).

**Imperative:**

```python
class App(pz.App):
    def __init__(self):
        self.open, self.query, self.wrap = True, "", False

    def toggle_wrap(self):
        self.wrap, self.open = not self.wrap, False

    def commands(self):                  # each item's on_press runs the command
        return [ui.PaletteItem("editor: toggle soft wrap", self.toggle_wrap, keys="alt-z")]

    def run_first(self):                 # on_submit: Enter runs the first match
        matches = ui.fuzzy_filter(self.query, self.commands(), key=lambda c: c.label)
        return matches[0][0].on_press() if matches else None

    def view(self):
        palette = ui.command_palette(self.query, lambda q: setattr(self, "query", q),   # on_query
                                     self.commands(), on_submit=self.run_first)
        return ui.layer(main_view(), palette if self.open else None,
                        on_dismiss=lambda: setattr(self, "open", False))
```

**Declarative:**

```python
from dataclasses import dataclass

@dataclass
class Query:
    text: str

class App(pz.App):
    open, query, wrap = True, "", False

    def update(self, message):
        match message:
            case Query(text):
                self.query = text
            case "toggle wrap" | "submit":
                self.wrap, self.open = not self.wrap, False

    def view(self):
        palette = ui.command_palette(self.query, Query,
                                     [ui.PaletteItem("editor: toggle soft wrap", "toggle wrap")],
                                     on_submit="submit")
        return ui.layer(main_view(), palette if self.open else None)
```

**Functional:**

```python
from dataclasses import dataclass

@dataclass(frozen=True)
class State:
    query: str = ""
    wrap: bool = False

def view(state, set_state):
    items = [ui.PaletteItem("editor: toggle soft wrap", lambda: set_state(wrap=not state.wrap))]
    return ui.command_palette(state.query, lambda q: set_state(query=q), items,
                              on_submit=lambda: set_state(wrap=not state.wrap))
```

## Properties and API

### `ui.command_palette`

```python
command_palette(
    query,
    on_query,
    commands,
    *,
    selected=0,
    on_submit=None,
    placeholder='Execute a command…',
    input_id='command-palette',
    theme=None,
    **kwargs,
)
```

A command palette: fuzzy-filters `commands` by `query` and shows their shortcuts.

| Property | Default | Description |
| --- | --- | --- |
| `query` | *required* | Search text. |
| `commands` | *required* | List of `PaletteItem`. |
| `selected` | `0` | Highlighted index (for arrow-key navigation). |
| `placeholder` | `"Execute a command…"` | Hint text shown when empty. |
| `input_id` | `"command-palette"` | Field id (for `Command.focus`). |
| `theme` | `None` | Theme to use instead of the current one (`pyzed.theme.current()`). |
| `**kwargs` | — | Same as `picker`. |

Events: `on_query`, `on_submit` ([events](#events)).

[Full reference →](https://albakore.github.io/pyzed/reference/ui/#command_palette)

### `ui.PaletteItem`

```python
@dataclass
class PaletteItem(label, on_press=None, keys=None, icon=None, detail=None)
```

An entry of `command_palette` or `picker`.

| Property | Default | Description |
| --- | --- | --- |
| `label` | *required* | Label text. |
| `keys` | `None` | Shortcut shown at the end, written as a string: `"ctrl-s"`, `"cmd-k cmd-s"`. |
| `icon` | `None` | Name of a Lucide icon (`pyzed icons` lists them). |
| `detail` | `None` | Secondary text (for example, a file path). |

Events: `on_press` ([events](#events)).

[Full reference →](https://albakore.github.io/pyzed/reference/ui/#paletteitem)

## Related

- [Picker](https://albakore.github.io/pyzed/library/picker/): A searchable list on a floating surface, with fuzzy matching.
- [Layer](https://albakore.github.io/pyzed/library/layer/): Draws an overlay over your view; clicking outside dismisses it.
- [Kbd](https://albakore.github.io/pyzed/library/kbd/): Keyboard shortcuts drawn as keys, with symbols on macOS.
