Files
hqt/docs/superpowers/specs/2026-06-09-project-edit-frappe-theme-design.md
T
felixm 90944948f5 Initial commit: hqt — HQ Terminal TUI
TUI for orchestrating AI coding harness sessions (Claude Code, Codex,
Kiro, etc.) via tmux. Click CLI bootstraps a Textual TUI over
ProjectService/SessionService backed by SQLite, spawning harness
sessions as tmux windows through TmuxManager.

Includes recent fixes:
- Visible Tab focus highlight on dialog OK/Cancel buttons
- Auto-select first project on launch
- Auto-select first session + per-project session-selection memory
- tmux new-window targets an explicit free index, fixing
  "index N in use" failures (broken spawn/attach in attached sessions)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 17:06:57 -04:00

6.3 KiB

Project Editing + Catppuccin Frappé Theme Completion

Date: 2026-06-09 Status: Approved

Problem

  1. Projects cannot be edited after creation. If a repository moves on disk, the only options are archiving the project or editing the database by hand.
  2. The TUI registers a Catppuccin Frappé theme (FRAPPE_THEME in src/hqt/tui/app.py) but only sets the basic semantic fields. Textual auto-derives every other color (footer keys, borders, selection highlights), which drifts off-palette, and the modal dialogs, panel headers, and status symbols are entirely unstyled. The result does not look like Frappé.

Part 1: Project editing

Service layer

Add to ProjectService (src/hqt/projects/service.py):

def update(self, project_id: int, name: str, path: str) -> Project
  • Updates both fields and commits.
  • Raises ValueError("Project not found") for an unknown id.
  • A duplicate path violates the unique constraint on Project.path: catch IntegrityError, roll back, raise ValueError with a readable message (e.g. "Another project already uses path <path>").
  • No filesystem validation (consistent with create, which accepts any string).

Form screen

Rename AddProjectScreen (src/hqt/tui/screens/add_project.py) to ProjectFormScreen:

  • Constructor: ProjectFormScreen(title="Add Project", initial_name="", initial_path="").
  • The dialog heading label shows title; inputs are pre-filled with the initial values.
  • Return type unchanged: tuple[str, str] | None of (name, path).
  • Add-mode behavior unchanged: empty name defaults to Path(path).name; empty path dismisses with None.

Project list

Add ProjectList.get_selected_project_id() -> int | None (src/hqt/tui/widgets/project_list.py), same pattern as SessionList.get_selected_session_id(): read data off the ListView's highlighted child.

App wiring

In HqtApp (src/hqt/tui/app.py):

  • New binding: Binding("e", "edit_project", "Edit Project").
  • action_edit_project:
    • No project highlighted → notify("Select a project first", severity="warning") and return.
    • Otherwise push ProjectFormScreen(title="Edit Project", initial_name=project.name, initial_path=project.path).
    • On dismiss with a result: call ProjectService.update, refresh the project list. Catch ValueError and show its message as an error notification.
  • action_add_project switches to the renamed ProjectFormScreen with defaults; behavior otherwise unchanged.

Session semantics

Editing a path never touches tmux. Running sessions keep the working directory they were spawned with; only sessions created after the edit use the new path. This matches archive semantics (DB-only operation).

Part 2: Frappé theme completion

Theme definition

Replace FRAPPE_THEME with a fully specified Theme, mirroring the structure of Textual's built-in catppuccin-mocha theme with Frappé values:

Field Frappé color Hex
primary Blue #8caaee
secondary Mauve #ca9ee6
accent Peach #ef9f76
success Green #a6d189
warning Yellow #e5c890
error Red #e78284
foreground Text #c6d0f5
background Mantle #292c3c
surface Surface0 #414559
panel Surface1 #51576d
dark True

variables dict (same keys the built-in Mocha theme sets):

Variable Frappé color Value
input-cursor-foreground Crust #232634
input-cursor-background Rosewater #f2d5cf
input-selection-background Overlay2 30% #949cbb 30%
border Lavender #babbf1
border-blurred Surface2 #626880
footer-background Surface1 #51576d
block-cursor-foreground Base #303446
block-cursor-text-style none
button-color-foreground Mantle #292c3c

Note: background moves from Base to Mantle to match how the built-in Catppuccin themes layer surfaces — panels (Surface0/1) then sit visibly above the background.

Widget styling (src/hqt/tui/styles.tcss)

  • Keep the existing ProjectList/SessionList layout rules.
  • Modal dialogs: ProjectFormScreen renames its container id to #project-form-dialog; NewSessionScreen keeps #new-session-dialog. Both get: centered (align: center middle on the modal screen), fixed width (~60 cells), $surface background, Lavender border, padding.
  • Panel headers (#project-header, #session-header): bold text on $surface background.

Status symbol colors (src/hqt/tui/widgets/session_list.py)

Color the status symbol via Rich markup with Frappé hex literals:

Status Frappé color Hex
working Green #a6d189
waiting Yellow #e5c890
active Teal #81c8be
idle Teal #81c8be
dead Overlay0 #737994

Testing

  • Service (tests/test_services.py): update renames and repaths; duplicate path raises ValueError and leaves the DB session usable (rollback); unknown id raises ValueError.
  • TUI (pilot tests, same style as tests/test_tui.py):
    • Pressing e with a project highlighted opens the form pre-filled with that project's name and path.
    • Submitting the edit form updates the DB and refreshes the list.
    • Pressing e with no project selected shows a warning notification.
    • The add-project flow still works through the renamed screen.
  • Theme: assert app.current_theme.name == "catppuccin-frappe" and spot-check resolved CSS variables (e.g. background is #292c3c, border variable is #babbf1) so a regression to auto-derived colors is caught.

Out of scope

  • Archiving/un-archiving projects from the TUI.
  • Filesystem validation of project paths.
  • Moving or migrating existing tmux sessions when a path changes.
  • Light-mode (Latte) theme variant.