Skip to content

Rewrite the TUI on HQTUI - #35

Merged
ralyodio merged 1 commit into
mainfrom
worktree-tui-hqtui
Sep 8, 2026
Merged

Rewrite the TUI on HQTUI#35
ralyodio merged 1 commit into
mainfrom
worktree-tui-hqtui

Conversation

@ralyodio

@ralyodio ralyodio commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

The two-pane browser was ~940 lines of hand-rolled ANSI: its own escape codes, its own key parser, its own box drawing, and a full-screen repaint on every keystroke. It now runs on @profullstack/hqtui, and the file is split three ways:

file holds
model.ts state and the pure functions over it — no terminal, no ssh, no rsync
view.ts the whole screen as one pure function of a snapshot
app.ts state and effects only

That split is what makes the screen testable. The old TUI wrote straight to stdout, so the only way to know what it drew was to run it in a pty and look. A frame is now a value, and view.test.ts asserts on rendered text.

New, beyond the same two panes

  • A live transfer panel — progress bar, rate, ETA, running add/update/unchanged counts, and the files as rsync reports them. s used to be a status line that changed once at the end. esc cancels it, through an AbortSignal into runToCompletion.
  • / filters a listing as you type; o cycles name/size/time sort (O reverses); . toggles dotfiles; ? lists every binding.
  • The endpoint picker filters as you type, over names and hostnames both.
  • Modification times, right-aligned beside the sizes.
  • The mouse: click a pane to focus it, click a row to select it, wheel to scroll.

Three defects found by rendering frames rather than reading code

  • Title and path share the panel's top border row and the box gives the path priority. Budgeted against the full pane width, one pane lost its title and the other lost its path — decided by a one-column rounding difference between two panes showing the very same directory.
  • hqtui's truncate keeps the head of a string, which is right for a label and wrong for a path: every worktree under one repo rendered as the identical string. Paths are shortened from the left now.
  • A finished transfer showed rsync's last printed figure (80% on a preview that had completed) instead of 100%.

The host-key prompt keeps its behaviour and its regression test: q still quits from inside it, everything else is still swallowed, and a connect that fails still takes its unanswered question with it.

Verification

601 tests pass (59 of them for the TUI, up from 15), pnpm typecheck and pnpm build are clean, and the real binary was driven through a pty: both panes list, the picker loads ~17 ssh_config hosts and narrows to 2 on "see", a real rsync preview and a real sync stream through the transfer panel, and both q and Ctrl+C exit 0 with the alternate screen and cursor restored.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AKCDDzxxcypufp9u8cjoAk

The two-pane browser was ~940 lines of hand-rolled ANSI: its own escape
codes, its own key parser, its own box drawing, and a full-screen repaint
on every keystroke. It is now built on @profullstack/hqtui, which owns the
terminal, diffs frames and parses input, and the file is split three ways:

  model.ts  state and the pure functions over it (no terminal, no ssh)
  view.ts   the whole screen as one pure function of a snapshot
  app.ts    state and effects only

That split is what makes the screen testable. The old TUI wrote straight
to stdout, so the only way to know what it drew was to run it in a pty and
look; a frame is now a value, and view.test.ts asserts on rendered text.

What is new, beyond the same two panes:

  - a live transfer panel: progress bar, rate, ETA, running add/update/
    unchanged counts, and the files as rsync reports them. `s` used to be a
    status line that changed once at the end. `esc` cancels it, through an
    AbortSignal into runToCompletion.
  - `/` filters a listing as you type, `o` cycles name/size/time sort (`O`
    reverses), `.` toggles dotfiles, `?` lists every binding.
  - the endpoint picker filters as you type, over names and hostnames both.
  - modification times, right-aligned beside the sizes.
  - the mouse: click a pane to focus it, click a row to select it, wheel to
    scroll.

Three defects found by rendering frames rather than reading the code:

  - Title and path share the panel's top border row and the box gives the
    path priority. Budgeted against the full pane width, one pane lost its
    title and the other lost its path — decided by a one-column rounding
    difference between two panes showing the very same directory.
  - hqtui's `truncate` keeps the head of a string, which is right for a
    label and wrong for a path: every worktree under one repo rendered as
    the identical string. Paths are shortened from the left now.
  - A finished transfer showed rsync's last printed figure (80% on a
    preview that had completed) instead of 100%.

The host-key prompt keeps its behaviour and its regression test: `q` still
quits from inside it, everything else is still swallowed, and a connect
that fails still takes its unanswered question with it.

Verified against the real binary through a pty: both panes list, the picker
loads ~17 ssh_config hosts and narrows to 2 on "see", a real rsync preview
and a real sync stream through the transfer panel, and both `q` and Ctrl+C
exit 0 with the alternate screen and cursor restored.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AKCDDzxxcypufp9u8cjoAk
@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​profullstack/​hqtui@​0.2.0781009995100

View full report

@socket-security

Copy link
Copy Markdown

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn Medium
Low adoption: npm @profullstack/hqtui

Location: Package overview

From: apps/cli/package.jsonnpm/@profullstack/hqtui@0.2.0

ℹ Read more on: This package | This alert | What are unpopular packages?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Unpopular packages may have less maintenance and contain other problems.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@profullstack/hqtui@0.2.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

@ralyodio
ralyodio marked this pull request as ready for review September 8, 2026 14:26
@ralyodio
ralyodio merged commit c47b210 into main Sep 8, 2026
4 checks passed
@ralyodio
ralyodio deleted the worktree-tui-hqtui branch September 8, 2026 14:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant