Command palette
⌘K-style launcher. Document-level shortcut opens the overlay, typed text filters the registered commands, arrow keys move the highlight, Enter runs the command. The visible list is a filtered() method — registered commands stay in state.commands.
Live preview
Press ⌘K or Ctrl K — or use the button below.
esc
No commands match ""
Last command:
Markup
<div data-component="cmdk-demo">
<div class="cmdk-backdrop" data-if="visible" @click.self="close">
<div class="cmdk-panel" role="dialog" aria-modal="true">
<div class="cmdk-search">
<input data-ref="input"
data-model="query"
@keydown="onKey"
placeholder="Type a command…" />
</div>
<div class="cmdk-list">
<template data-each="filtered()" data-key="id">
<button class="cmdk-row"
@mouseenter="setHighlight"
@click="run"
data-bind="data-id:item.id, data-i:$index"
data-class="highlight:$index === highlight">
<span data-text="item.label"></span>
<span data-text="item.group"></span>
</button>
</template>
<p data-if="filtered().length === 0">No matches</p>
</div>
</div>
</div>
</div>
Definition
Micra.define('cmdk-demo', {
state: {
visible: false,
query: '',
highlight: 0,
lastRan: '',
commands: [
{ id: 'new-doc', label: 'New document', group: 'Files', icon: 'icon-document-text' },
{ id: 'search', label: 'Search files', group: 'Files', icon: 'icon-magnifying-glass' },
{ id: 'invite', label: 'Invite teammate', group: 'Team', icon: 'icon-bolt' },
{ id: 'theme', label: 'Toggle theme', group: 'Appearance', icon: 'icon-bolt' },
{ id: 'shortcuts',label: 'Show keyboard shortcuts', group: 'Help', icon: 'icon-command-line' },
{ id: 'logout', label: 'Sign out', group: 'Account', icon: 'icon-bolt' },
],
},
filtered() {
const q = this.state.query.trim().toLowerCase()
if (!q) return this.state.commands
return this.state.commands.filter(c =>
c.label.toLowerCase().includes(q) || c.group.toLowerCase().includes(q),
)
},
onCreate() {
this._shortcut = (e) => {
const isCmdK = (e.metaKey || e.ctrlKey) && e.key === 'k'
if (isCmdK) {
e.preventDefault()
this.open()
}
}
document.addEventListener('keydown', this._shortcut)
},
onDestroy() {
document.removeEventListener('keydown', this._shortcut)
},
open() {
this.state.visible = true
this.state.query = ''
this.state.highlight = 0
queueMicrotask(() => this.refs.input?.focus())
},
close() { this.state.visible = false },
setHighlight(e) {
const i = parseInt(e.currentTarget.dataset.i, 10)
if (!Number.isNaN(i)) this.state.highlight = i
},
onKey(e) {
const list = this.filtered()
if (e.key === 'ArrowDown') {
e.preventDefault()
this.state.highlight = (this.state.highlight + 1) % list.length
} else if (e.key === 'ArrowUp') {
e.preventDefault()
this.state.highlight = (this.state.highlight - 1 + list.length) % list.length
} else if (e.key === 'Enter') {
e.preventDefault()
const cmd = list[this.state.highlight]
if (cmd) this._run(cmd)
} else if (e.key === 'Escape') {
this.close()
}
},
run(e) {
const id = e.currentTarget.dataset.id
const cmd = this.state.commands.find(c => c.id === id)
if (cmd) this._run(cmd)
},
_run(cmd) {
this.state.lastRan = cmd.label
this.close()
Micra.emit('command:run', cmd)
},
})
Integration
- Global shortcut. A single document keydown listener in onCreate matches ⌘K / Ctrl+K. Removed in onDestroy so detached instances stop responding.
- Focus on open. queueMicrotask(() => this.refs.input.focus()) waits for the data-if="visible" render to attach the input to the DOM, then focuses it.
- Commands publish on the bus. Micra.emit('command:run', cmd) — wire any other component to this.on('command:run', …) and dispatch on cmd.id.
- Filtering matches both label and group, so a user can type "team" to find "Invite teammate".
Pitfalls
- Don't bind @keydown.enter on the input — Enter must compete with arrow / Escape handling. One onKey branching on e.key beats four separate modifier-suffixed handlers.
- Don't mount more than one cmdk-demo instance — both will register the ⌘K listener and open simultaneously.
- Don't store filtered in state. Query updates already trigger a re-render; the method recomputes on read.