feat(maestro): add the web ctrl-k command bar

The overlay, the query line and the session half of the results
(docs/COMMAND-BAR.md): an empty query lists sessions most recently
focused first, typing ranks through `matchAndRank` with the winning
field's ranges highlighted, and results render under kind headers
ordered by their best-scoring member.

Enter dispatches `SESSION_SELECT_EVENT`, the path the sidebar's click
and the hash router already share, so a bar attach cannot leave the
layout in a state a click could not produce.

The global key listener moves to the capture phase, which #148 left
alone: an attached xterm swallows every keystroke it is given, so
ctrl-k has to be seen ahead of the terminal.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Miguel Palhas
2026-09-03 14:28:42 +01:00
co-authored by Claude Opus 5
parent 1fe2ae6575
commit 8dde63a2ab
5 changed files with 862 additions and 7 deletions
+122
View File
@@ -1425,6 +1425,128 @@ input.leaf-rename:focus {
color: var(--muted);
}
/* ---------- command bar (Ctrl+K) — docs/COMMAND-BAR.md ---------- */
/* Same family of surface as the spawn modal and the help overlay: the overlay
tokens below are theirs, not a third set. */
.cmdbar-overlay {
position: fixed;
inset: 0;
z-index: var(--z-modal);
display: grid;
grid-template-rows: 12vh auto 1fr;
justify-items: center;
padding: var(--sp-4);
background: oklch(0 0 0 / 0.5);
backdrop-filter: blur(4px);
}
.cmdbar-panel {
grid-row: 2;
display: flex;
flex-direction: column;
width: min(560px, 100%);
max-height: min(60vh, 520px);
overflow: hidden;
background: var(--surface);
border: 1px solid var(--border-strong);
border-radius: var(--r-3);
box-shadow: var(--shadow-2);
}
.cmdbar-input {
appearance: none;
background: none;
border: 0;
border-bottom: 1px solid var(--border);
border-radius: 0;
color: var(--ink);
font-family: var(--font-mono);
font-size: var(--fs-15);
padding: var(--sp-3) var(--sp-4);
}
.cmdbar-input:focus-visible {
outline: none;
}
.cmdbar-results {
overflow-y: auto;
padding: var(--sp-2) 0;
}
.cmdbar-group-title {
padding: var(--sp-2) var(--sp-4) var(--sp-1);
font-size: var(--fs-11);
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--faint);
}
.cmdbar-row {
display: flex;
align-items: baseline;
gap: var(--sp-3);
padding: var(--sp-1) var(--sp-4);
cursor: pointer;
}
.cmdbar-row.is-active {
background: var(--surface-3);
}
.cmdbar-row-name {
font-size: var(--fs-13);
color: var(--ink);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* A dead session still lists — it just is not where you just were. */
.cmdbar-row.is-dead .cmdbar-row-name {
color: var(--muted);
}
.cmdbar-row-meta {
flex: 1 1 auto;
min-width: 0;
font-size: var(--fs-11);
color: var(--faint);
text-align: right;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.cmdbar-hit {
background: none;
color: var(--accent);
font-weight: 600;
}
.cmdbar-empty {
padding: var(--sp-3) var(--sp-4);
font-size: var(--fs-13);
color: var(--muted);
}
/* Phone: the bottom-sheet treatment the spawn modal already uses. */
@media (max-width: 600px) {
.cmdbar-overlay {
grid-template-rows: 1fr auto;
padding: 0;
}
.cmdbar-panel {
grid-row: 2;
width: 100%;
max-height: 70vh;
border-radius: var(--r-3) var(--r-3) 0 0;
border-bottom: 0;
}
}
/* Line 2 — dot + duration in the state's colour, then muted fragments. The
state word itself is never spelled out here; the colour carries it. */
.card-line2 {