Repository navigation
Switchboard v0.2 — new layouts and filter primitives for the kanban, broadcast in the command palette, full-text search across pane scrollback, session templates, the bulk of an external security audit closed out, and a batch of perf work.
Highlights
New layouts & filter ergonomics
- Grid view — every pane on screen at once, grouped by session.
- List view — dense single-column rundown.
- Layout suggestion chip — header hint nudges you toward the layout that fits your current pane count.
- Pinned windows — pin button in the card foot promotes a card to the top of its column with an accent border and header badge, and keeps it visible even when the active filter would normally hide it. Persisted in
localStorageby pane id. - Saved filter presets — chip-bar in the subhead saves the current
(status, kind, query)filter under a name and lets you recall or delete it in one click. Persisted inlocalStorage.
Command palette: broadcast mode
- Open the palette with multiple target panes and it switches to broadcast mode: amber pill in the header, one chip per target with an inline X to prune before sending, footer reads target: N panes. Submit iterates
POST /api/sendper remaining target; per-target errors are swallowed so one bad pane doesn't abort the rest. - NeedsStrip gains a Broadcast button (visible when there are ≥2 pending panes) that opens the palette pre-loaded with the pending list as targets.
Session templates
services/templates.pydefinesTemplate/TemplateWindowmodels with two built-ins (web-project,agent-grid) and loads user templates from~/.switchboard/templates/*.json.${VAR}substitution acrosssession, windowname/cwd/cmd; the modal collects values per template, the backend substitutes at instantiation time, and unknown vars pass through unchanged.GET /api/templatesreturns lightweight summaries;POST /api/templates/instantiatecreates the session and starts each window's command.
Pane history search (⌘⇧F)
GET /api/search?q=<term>&lines=500fans the per-pane capture + grep out viaasyncio.gather(asyncio.to_thread(...)). Case-insensitive substring match on ANSI-stripped lines; returns pane id, session, window name + index, 1-based line number, and 3 lines of context per match. Capped at 50 matches per pane and 5,000 lines per pane; empty query returns 400.- New
<SearchModal>opened via⌘⇧F/Ctrl+Shift+F(also from inside inputs). Live search-as-you-type with a 200 ms debounce and an AbortController on every keystroke.
Cards that do more
- Per-kind quick actions — agent cards expose
y/n/Ctrl-C; shell cards exposeclear. - Live activity timers — Xm ago spans tick at 1Hz via a dedicated
AgoSpanleaf component. - Needs-strip dismiss persistence — dismissals stored as a per-pane id set in
sessionStorage, so they survive reloads in the same tab.
Security audit
A full external audit landed; every finding is now closed:
- 5 high-severity auth findings fixed.
- 9 medium-severity findings fixed.
- 6 low-severity findings closed out.
Performance
- Backend
parse_paneoutput cached keyed by(lines-hash, cwd).pane.capture_pane()cached under modal-open polling so the modal and the kanban poll can share the capture.- libtmux calls in the WS recv loop offloaded via
asyncio.to_thread. - Branch cache TTL 2→5s, PR cache TTL 30→60s.
- Frontend
/api/stateresponse deduped so unchanged windows keep their object references.WindowCardmemo upgraded withprUrl+ a content-aware preview check.xtermstream rewriter gets a fast-path for chunks with no truecolor background.- OSC 4 theme packets precomputed at module load.
- Terminal-modal
fit()debounced viarAFinstead of an 80 mssetTimeout. - Cached pane preview painted at modal mount; replaced on the first snapshot.
- New
useSetting(key)selector hook lets the terminal modal subscribe to a single settings key instead of the whole object. - Favicon and title effects merged into one.
Bug fixes
.rename-modalnow has the same chrome as the other modals (panel background, border, radius, drop shadow, rise animation). It was previously transparent — invisible in dark mode by accident, and unreadable in light mode.- Removed the hardcoded
#282c34inline background on.term-bodyso the theme-aware CSS rule (added in the original theme work) wins. Fixes the light/high-contrast terminal modal where the body sandwiched the xterm viewport between light chrome and a dark padding band. - Size button now scales modal height in lockstep with width.
- CI flake in
test_open.pyfixed by neutralizing the prewarm-scrape daemon inconftest.
Docs
- Wordmark banner + install/python/license badges replace the centered logo + heading on the README.
- New Contributing and Reporting a security issue sections; tightened Layout.
Requirements
Unchanged from v0.1.0:
- Backend: Python ≥ 3.11,
tmux,uv(FastAPI + pydantic-settings). - Frontend: Node ≥ 20 (Vite + React 19, xterm.js).
- Optional: an editor on the IDE allowlist for path-click open,
ghfor PR / CI chip enrichment.
Upgrading from v0.1.0
git fetch --tags
git checkout v0.2.0
cd backend && uv sync && cd ..
cd frontend && npm install && cd ..
./scripts/dev.shNo config migrations. Pins and saved filter presets persist in localStorage and start empty; needs-strip dismissals live in sessionStorage and clear on tab close.
Full changelog: v0.1.0...v0.2.0