7.7 KiB
Command Palette Implementation & Tests
Date: 2026-06-02
Status: Draft (pending review)
Parent Track: command_palette_and_performance_20260602 (continuing Phase 2 + adding Phase 3)
Spec: conductor/tracks/command_palette_and_performance_20260602/spec.md (existing)
Context & Motivation
A command_palette_and_performance_20260602 track was started in early June 2026. Phase 1 (Async Context Preview) is complete. Phase 2 (Command Palette) is unstarted — no src/command_palette.py, no src/commands.py, no test files. The user reports the palette doesn't pop up on Ctrl+Shift+P.
The existing spec says Ctrl+P; this design uses Ctrl+Shift+P (per the user's expectation and VSCode convention documented in docs/guide_command_palette.md).
This design finishes Phase 2 of the existing track and adds Phase 3 (tests).
Scope
In Scope
src/command_palette.py— Module-level:Commanddataclass,CommandRegistry,fuzzy_match(),render_palette_modal(app),render_everything_modal(app)src/commands.py— Static command definitions (~30-50 commands across categories)src/gui_2.py—self.show_command_palette: boolinApp.__init__,_render_command_palette(self)thin wrapper,Ctrl+Shift+Pkeyboard handlertests/test_command_palette.py— Unit tests for fuzzy matcher, command registry, mode detectiontests/test_command_palette_sim.py— Integration tests vialive_gui
Out of Scope
- Async context preview (Phase 1; already complete)
- The "Everything" mode async search worker (mentioned in the existing spec; defer to a follow-up track)
- Visual theming of the palette (use existing ImGui style)
Design
Data Model: Command
@dataclass
class Command:
id: str # Unique identifier
title: str # Display name
category: str # Category for grouping
shortcut: Optional[str] # Optional default shortcut (e.g., "Ctrl+S")
description: str = "" # Optional help text
enabled_when: Optional[str] = None # Optional condition expression
action: Callable = None # Function to execute when selected
Command Registry
# src/commands.py
from src.command_palette import Command, CommandRegistry
registry = CommandRegistry()
@registry.register
def save_file(app: App) -> None:
"""Save File — File category, Ctrl+S"""
# ... call app's save logic
Registration patterns:
- Decorator:
@registry.registerfor top-level functions - Explicit:
registry.register(Command(id=..., title=..., action=...))for closures or classes
Fuzzy Matcher
Implemented in src/command_palette.py as a pure function:
def fuzzy_match(query: str, candidates: List[Command], top_n: int = 20) -> List[ScoredCommand]:
"""
Returns the top_n candidates matching query, ranked by score.
Algorithm:
1. Subsequence check: query chars must appear in title, in order
2. Score calculation:
- Exact prefix match: +1.0
- Word boundary match: +0.5
- Contiguous match: +0.3
- Character distance penalty: -0.1 per gap
3. Sort by score descending
4. Return top_n
"""
Modal Rendering
The palette is a centered ImGui modal. Module-level function (per delegation pattern):
# src/command_palette.py
def render_palette_modal(app: App) -> None:
"""Render the Command Palette modal. Called from gui_2.py when app.show_command_palette is True."""
if not app.show_command_palette:
return
imgui.set_next_window_position(...) # Centered
imgui.set_next_window_size(...)
if imgui.begin("Command Palette##palette", closable=True):
# Search input
# Fuzzy-matched results list
# Keyboard navigation
imgui.end()
Keyboard Handler
In gui_2.py's main event loop:
io = imgui.get_io()
if io.key_ctrl and io.key_shift and imgui.is_key_pressed(imgui.Key.p):
app.show_command_palette = not app.show_command_palette
File Structure
src/command_palette.py— NEW: Command, CommandRegistry, fuzzy_match, render_palette_modalsrc/commands.py— NEW: Static command definitions and registrysrc/gui_2.py— MODIFY: addself.show_command_palette, add_render_command_palettewrapper, add Ctrl+Shift+P handler, register the palette moduletests/test_command_palette.py— NEW: Unit teststests/test_command_palette_sim.py— NEW: Integration tests via live_gui
Tests
Unit Tests (tests/test_command_palette.py)
def test_fuzzy_match_prefix_ranks_first():
from src.command_palette import fuzzy_match
candidates = [
Command(id="find", title="Find in Selection"),
Command(id="fold", title="Fold All"),
Command(id="config", title="Configure Settings"),
]
results = fuzzy_match("fin", candidates)
assert results[0].command.id == "find"
assert results[0].score > 0.5
def test_fuzzy_match_rejects_no_match():
from src.command_palette import fuzzy_match
candidates = [Command(id="x", title="foo bar")]
results = fuzzy_match("xyz", candidates)
assert len(results) == 0
def test_command_registry_register_and_list():
from src.command_palette import CommandRegistry
from src.commands import registry
assert "save_file" in registry.all()
# All commands have id, title, category
for cmd in registry.all():
assert cmd.id and cmd.title and cmd.category
def test_command_registry_duplicate_raises():
from src.command_palette import CommandRegistry, Command
reg = CommandRegistry()
reg.register(Command(id="x", title="X", category="test"))
with pytest.raises(ValueError):
reg.register(Command(id="x", title="X", category="test"))
Integration Tests (tests/test_command_palette_sim.py)
def test_ctrl_shift_p_opens_palette(live_gui):
client = live_gui[1]
# Press Ctrl+Shift+P
client.press_key_combo("Ctrl+Shift+P")
# Verify the palette is visible
state = client.get_window_state("command_palette")
assert state["visible"] == True
def test_palette_filters_as_user_types(live_gui):
client = live_gui[1]
client.press_key_combo("Ctrl+Shift+P")
client.type_in_palette("save")
results = client.get_palette_results()
assert any("Save" in r.title for r in results)
# Other commands not shown
assert not any("Compress" in r.title for r in results)
def test_palette_executes_command_on_enter(live_gui):
client = live_gui[1]
client.press_key_combo("Ctrl+Shift+P")
client.type_in_palette("Reset")
client.press_key("Down")
client.press_key("Enter")
# Verify the reset command was executed (check via Hook API)
state = client.get_session_state()
assert state.get("discussion_history", []) == []
Acceptance Criteria
Ctrl+Shift+Popens the palette (verified vialive_guitest)- Typing in the palette filters results via fuzzy match
- Selecting a command (Enter key) executes it and closes the palette
- Escape closes the palette without executing
- All unit tests pass
- All integration tests pass
- The palette respects the existing theme (dark/light/nerv)
- No new lint errors
Risks
- Keyboard handler conflicts: The Ctrl+Shift+P combo might be intercepted by other subsystems. Mitigation: check for other handlers in the codebase first; if conflicts, document them.
- Pyodide build dependencies: The image_bundle web backend (for Track 4) has a different architecture than this track. The two are independent but should be aware of each other.
- Test flakiness:
live_guitests can be flaky if the GUI doesn't initialize in time. Mitigation: the standard 15-second readiness polling is sufficient.