# Command

> Keyboard-first command palette / filterable menu. Compound API with case-insensitive substring filter, groups, and full a11y (combobox/listbox/option).

- Package: `@sisyphos-ui/command`
- Docs: https://sisyphosui.com/docs/components/command
- npm: https://www.npmjs.com/package/@sisyphos-ui/command
- WAI-ARIA pattern: [Combobox](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/)

## Installation

```bash
pnpm add @sisyphos-ui/command @sisyphos-ui/core
```

Or use the umbrella package:

```bash
pnpm add @sisyphos-ui/ui
```

## Import

```tsx
import "@sisyphos-ui/command/styles.css";
import { Command } from "@sisyphos-ui/command";
```

## Examples

### Default

Type to filter. Arrow keys move the selection; Enter runs the active item.

```tsx
import { Command, toast } from "@sisyphos-ui/ui";

export function Example() {
  return (
    <Command onSelect={(v) => toast.success(`Ran ${v}`)}>
      <Command.Input placeholder="Type a command or search…" />
      <Command.List>
        <Command.Empty>No results.</Command.Empty>
        <Command.Group heading="Suggestions">
          <Command.Item value="calendar">Calendar</Command.Item>
          <Command.Item value="emojis">Search Emojis</Command.Item>
          <Command.Item value="calculator">Calculator</Command.Item>
        </Command.Group>
        <Command.Separator />
        <Command.Group heading="Settings">
          <Command.Item value="profile">Profile</Command.Item>
          <Command.Item value="billing">Billing</Command.Item>
          <Command.Item value="preferences">Preferences</Command.Item>
        </Command.Group>
      </Command.List>
    </Command>
  );
}
```

### Inside a Dialog

The classic ⌘K experience — drop `<Command>` inside `<Dialog>` and the input takes focus on open.

```tsx
import { useState } from "react";
import { Button, Command, Dialog, toast } from "@sisyphos-ui/ui";

export function Example() {
  const [open, setOpen] = useState(false);
  return (
    <>
      <Button onClick={() => setOpen(true)}>Open command menu</Button>
      <Dialog open={open} onOpenChange={setOpen} size="sm">
        <Command
          onSelect={(v) => {
            toast.success(`Ran ${v}`);
            setOpen(false);
          }}
        >
          <Command.Input placeholder="Type a command or search…" />
          <Command.List>
            <Command.Empty>No results.</Command.Empty>
            <Command.Group heading="Navigate">
              <Command.Item value="dashboard">Go to Dashboard</Command.Item>
              <Command.Item value="projects">Go to Projects</Command.Item>
            </Command.Group>
          </Command.List>
        </Command>
      </Dialog>
    </>
  );
}
```

### With shortcuts

Drop a `<Kbd>` inside each `<Command.Item>` — it flows to the right automatically via `margin-left: auto` on the flex row.

```tsx
import { Command, Kbd } from "@sisyphos-ui/ui";

export function Example() {
  return (
    <Command>
      <Command.Input placeholder="Search…" />
      <Command.List>
        <Command.Group heading="File">
          <Command.Item value="new">
            <span className="flex-1">New file</span>
            <Kbd size="xs" shortcut="cmd+n" />
          </Command.Item>
          <Command.Item value="open">
            <span className="flex-1">Open…</span>
            <Kbd size="xs" shortcut="cmd+o" />
          </Command.Item>
          <Command.Item value="save">
            <span className="flex-1">Save</span>
            <Kbd size="xs" shortcut="cmd+s" />
          </Command.Item>
        </Command.Group>
      </Command.List>
    </Command>
  );
}
```

### Disabled items

`disabled` items are still rendered but don't match, aren't focusable, and can't be activated.

```tsx
import { Command } from "@sisyphos-ui/ui";

export function Example() {
  return (
    <Command>
      <Command.Input />
      <Command.List>
        <Command.Item value="edit">Edit</Command.Item>
        <Command.Item value="copy" disabled>Copy (disabled)</Command.Item>
        <Command.Item value="paste">Paste</Command.Item>
      </Command.List>
    </Command>
  );
}
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| value | `string` | — | Controlled search value. Omit to let the component manage its own state. |
| defaultValue | `string` | `""` | Initial search value when uncontrolled. |
| onValueChange | `(value: string) => void` | — | Fires whenever the search value changes. |
| onSelect | `(value: string) => void` | — | Fires when an item is activated (click or Enter). Receives the item's `value` prop. |
| label | `string` | `"Command menu"` | Accessible name for the combobox root. |

## Anatomy

```tsx
<Command.Root>
  <Command.Input /> // Text input that drives the filter. (required)
  <Command.List /> // Scrollable listbox region. (required)
  <Command.Empty /> // Rendered when no items match.
  <Command.Group /> // Optional section with an `heading`.
  <Command.Item /> // Selectable row filtered by `value`. (required)
  <Command.Separator /> // Visual divider.
</Command.Root>
```

## Keyboard interactions

- **ArrowDown + ArrowUp** — Move the active item (wraps at the edges).
- **Home + End** — First / last matching item.
- **Enter** — Activate the current item.
