component

Command Palette

A ⌘K-style launcher: a modal Dialog whose header is a search field and whose body is a fuzzy-filtered ListBox of commands. It composes three existing primitives — the Dialog shell (with its new header slot), the Input search field, and the ListBox option engine — behind one headless connectCommandPalette core.

Like the Combobox, the field keeps DOM focus and drives the list with aria-activedescendant (the ListBox’s focusStrategy="active-descendant", revealStrategy="deferred" modes). Arrow keys move the cursor without leaving the input; Enter runs the focused command and closes the palette. Unlike a combobox, nothing is “selected” — every row is an action (selectionMode="none"), so activating one fires onCommand and dismisses.

Default

Open the palette and type to filter. Matching is fuzzynf finds “New File” — and results rank best-match-first. Each command can carry a leading icon, extra keywords (matched but not shown), and a shortcut hint rendered on the trailing edge.

  • New File

    ⌘ N
  • Open File…

    ⌘ O
  • Open Settings

    ⌘ ,
  • View Profile

  • Toggle Theme

---
import CommandPalette from "../command-palette.astro";
import Button from "@pindoba/astro-button";
import { stack } from "@pindoba/styled-system/patterns";
---

<div class={stack({ gap: "sm", direction: "column", align: "start" })}>
  <Button id="astro-cmdp-default-trigger">Open command palette</Button>

  <CommandPalette
    id="astro-cmdp-default"
    items={[
      {
        id: "new-file",
        label: "New File",
        shortcut: ["", "N"],
        keywords: ["create"],
      },
      { id: "open-file", label: "Open File…", shortcut: ["", "O"] },
      {
        id: "settings",
        label: "Open Settings",
        shortcut: ["", ","],
        keywords: ["preferences"],
      },
      { id: "profile", label: "View Profile" },
      { id: "theme", label: "Toggle Theme", keywords: ["dark", "light"] },
    ]}
  />
</div>

<script>
  document
    .getElementById("astro-cmdp-default-trigger")
    ?.addEventListener("click", () => {
      window.__PindobaDialogManager?.open("astro-cmdp-default-dialog");
    });
</script>

Grouped commands

Group commands into sections with { type: "section", title, items }. Sections render as labelled groups while the query is empty; once you start typing, results flatten and rank globally so the strongest match is always the top row — the norm for a command palette.

  • Navigation
  • Actions
---
import CommandPalette from "../command-palette.astro";
import Button from "@pindoba/astro-button";
import { stack } from "@pindoba/styled-system/patterns";
---

<div class={stack({ gap: "sm", direction: "column", align: "start" })}>
  <Button id="astro-cmdp-grouped-trigger">Open palette</Button>

  <CommandPalette
    id="astro-cmdp-grouped"
    items={[
      {
        id: "navigation",
        type: "section",
        title: "Navigation",
        items: [
          { id: "go-home", label: "Go to Home", shortcut: "g h" },
          { id: "go-inbox", label: "Go to Inbox", shortcut: "g i" },
          { id: "go-calendar", label: "Go to Calendar", shortcut: "g c" },
        ],
      },
      {
        id: "actions",
        type: "section",
        title: "Actions",
        items: [
          { id: "archive", label: "Archive", shortcut: "e" },
          { id: "share", label: "Share…" },
          { id: "delete", label: "Delete", shortcut: ["", ""] },
        ],
      },
    ]}
  />
</div>

<script>
  document
    .getElementById("astro-cmdp-grouped-trigger")
    ?.addEventListener("click", () => {
      window.__PindobaDialogManager?.open("astro-cmdp-grouped-dialog");
    });
</script>

Async commands

Set isLoading while a command source is being fetched; the body shows a Loading spinner in place of results. The Svelte demo simulates a fetch on open, then swaps in the commands.

  • Loading…
---
import CommandPalette from "../command-palette.astro";
import Button from "@pindoba/astro-button";
import { stack } from "@pindoba/styled-system/patterns";

// Static demo of the loading state — `isLoading` renders the spinner in place
// of results (an async source would flip this off once its commands arrive).
---

<div class={stack({ gap: "sm", direction: "column", align: "start" })}>
  <Button id="astro-cmdp-async-trigger">Open (loading state)</Button>

  <CommandPalette
    id="astro-cmdp-async"
    isLoading
    labels={{ placeholder: "Search deployments…" }}
    items={[
      { id: "deploy", label: "Deploy to Production", shortcut: ["", "D"] },
      { id: "rollback", label: "Roll Back Last Deploy" },
      { id: "logs", label: "Open Logs" },
    ]}
  />
</div>

<script>
  document
    .getElementById("astro-cmdp-async-trigger")
    ?.addEventListener("click", () => {
      window.__PindobaDialogManager?.open("astro-cmdp-async-dialog");
    });
</script>

Global hotkey

Set hotkey to register a global open shortcut — true binds ⌘K / Ctrl+K, or pass a key string (still requires ⌘/Ctrl). Leave it off (the default) to control open yourself (a trigger button, a menu item, your own key handler).

Press ⌘ K / Ctrl K to open the palette.
  • Run Command

    ⌘ ⇧ P
  • Go to Line

    ⌘ G
  • Format Document

    ⌥ ⇧ F
---
import CommandPalette from "../command-palette.astro";
import Badge from "@pindoba/astro-badge";
import { stack } from "@pindoba/styled-system/patterns";
---

<div class={stack({ gap: "sm", direction: "column", align: "start" })}>
  <span>
    Press <Badge size="sm">⌘ K</Badge> / <Badge size="sm">Ctrl K</Badge> to open the
    palette.
  </span>

  <CommandPalette
    id="astro-cmdp-hotkey"
    hotkey
    items={[
      { id: "search", label: "Search Everywhere", shortcut: ["", "P"] },
      { id: "command", label: "Run Command", shortcut: ["", "", "P"] },
      { id: "goto", label: "Go to Line", shortcut: ["", "G"] },
      { id: "format", label: "Format Document", shortcut: ["", "", "F"] },
    ]}
  />
</div>

Custom filtering

The built-in fuzzy matcher (filter={true}, the default) ranks by match quality. To plug in your own ranking — or to drive results from a server — pass a filter function (command, query) => number | boolean | null: return a score (higher ranks first), true/false to keep/drop without ranking, or null to drop. Pass filter={false} to disable client filtering entirely and feed pre-filtered items yourself.

props · 20 shown · 20 total
element binding svelte
HTMLInputElementnull

The bound `<input>` element.

empty slot svelte
Snippet

Custom empty state (replaces "No commands found").

feedback
CommandPaletteFeedback

Feedback (color) scheme applied to the palette.

filter
boolean(item: CommandPaletteItemInput, query: string) => number | boolean | null | undefined
default true

Filter/ranking strategy. `true` = built-in fuzzy, `false` = no filtering (consumer pre-filters), or a `(command, query) => score | boolean | null` predicate (higher score ranks first).

footer slot svelte
Snippet

Optional footer row under the list (e.g. nav hints).

hotkey svelte
booleanstring
default false

Register a global open hotkey. `true` = ⌘K / Ctrl+K; a string sets the key (still requires ⌘/Ctrl). `false` (default) leaves open control to the consumer.

ids
CommandPaletteIds

Stable id overrides for the palette's dialog, input, and listbox elements.

isLoading
boolean
default false

Whether command sources are still loading (shows the loading state).

items required
CommandPaletteNodeInput[]

Commands to show, flat or grouped into sections.

labels
CommandPaletteLabels

Accessible names + the search placeholder copy.

leading slot svelte
Snippet

Custom field leading content, rendered inside the leading `<Affix>` (replaces the default search glyph). The affix is decorative by default — opt out via `passThrough.searchIcon.props.decorative = false`.

loading slot svelte
Snippet

Custom loading state (replaces the default spinner).

onCommand svelte
(id: Key) => void

Fired when a command is activated (Enter / click). The palette then closes.

onOpenChange svelte
(open: boolean) => void

Fired when the open state changes.

onQueryChange svelte
(query: string) => void

Fired when the query text changes.

open
boolean
default false

Whether the palette is open (baked onto the field's `aria-expanded`).

passThrough
CommandPalettePassThrough

Per-slot style/attribute overrides for the Dialog, field, and ListBox.

query
string

Current search query (the host owns the text; the connect filters by it).

showShortcuts
boolean
default true

Show each command's `shortcut` hint on its trailing edge.

size
"sm""md""lg"
default "md"

Size variant for the field and result rows.

Plus all standard <dialog> HTML attributes.

Type

  • Components
  • Blocks