feat(herd): port the sidebar to hqtui, as a folding tree - #546
Merged
Merged
Conversation
"are we using hqtui.com library for this tui? it looks pretty old and
outdated. upgrade it to hqtui"
moshcode did not use hqtui at all: zero references in src/ or bin/, and
the TUI is hand-rolled escape sequences throughout. So this is a port,
not a version bump. @profullstack/hqtui joins as a dependency at ^0.7.0,
a house package with zero runtime dependencies.
SCOPE. The herd sidebar, which is the surface Anthony is looking at when
he complains, and nothing else. src/herd-workspace.mjs keeps the tmux
half and only the tmux half; src/herd-sidebar.mjs is the new view. The
right-hand pane is still a real tmux pane running a real agent, because
no renderer can substitute for one.
WHAT THE LIBRARY BUYS. Three bugs came out of the hand-rolled shape and
none of them were ever about herds:
- the click map was a second source of truth. sidebarRows() numbered
the lines and renderSidebar() printed them, and when they drifted
every click landed on the row below the pointer. hqtui's tree
reports the row it drew each node on, so the map is built by the
thing that draws it.
- there was no hover, so a click had to be spent selecting, which is
where the double-click came from. onHoverRow plus hovered is now
the whole of it.
- the restore path was escape sequences only. hqtui's Terminal
restores on SIGINT, SIGTERM and an uncaught error by default.
A FOLDING TREE, IN PLACE. The herd is herds containing members, which is
a tree, and the expectation for a pane of things-containing-things is
that it unfolds in place on ONE click with the row under the pointer
lit. Clicking a herd folds it; clicking a member opens it, meaning shown
and given the keyboard. Nothing is swapped out for a different screen.
Actions are real buttons with onPress.
Neither known trap is tripped: collapseBorders only merges where two
bordered siblings touch and there is nothing here to merge, so it is off
with the reasoning written down; and no panel carries both a title and a
subtitle, which is where near-identical panels truncate differently.
The tests are the part worth having. renderToScreen draws the real view
headlessly and then presses real cells, so "is the thing that says api
the thing that opens api" is answered by clicking the cell that says
api, rather than by two pieces of code agreeing about a line number.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
ThreatCrush Security Scan0 finding(s) in the 7 file(s) this pull request changes. Nothing in the files this pull request changes. 104 pre-existing finding(s) elsewhere in the repository — **HIGH/CRITICAL**: 8 | **MEDIUM**: 85 | **LOW**: 11Not introduced by this pull request. The full set is in the Security tab.
…and 84 more. Full results in the Security tab. Snippets are redacted; ThreatCrush never prints matched credential material. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #545. Base it there so the crash fix is not held hostage by this; rebase onto main once #545 lands.
First, the fact
moshcode did not use hqtui at all. Zero references in
src/orbin/. The TUI is hand-rolled escape sequences throughout, so this is a port, not a version bump.@profullstack/hqtuiis now a dependency at^0.7.0(a house package, zero runtime dependencies, TypeScript shipping ESM).Scope: the herd sidebar, and only the herd sidebar
The hand-rolled TUI is roughly 3,500 lines across five files. This ports the one Anthony is actually looking at when he complains, and leaves the rest running exactly as it did. What porting the others would take is written up at the bottom.
src/herd-workspace.mjsis now the tmux half and only the tmux half (221 lines: which pane is which, parking and joining, title pinning).src/herd-sidebar.mjsis the new view. The right-hand pane is still a real tmux pane running a real agent, because no renderer can substitute for one: it has a real cursor, a real mouse inside the agent, and its own full-screen UI.What the library actually buys
Three bugs came out of the hand-rolled shape, and none of them were ever about herds.
The click map was a second source of truth.
sidebarRows()numbered the lines andrenderSidebar()printed them, and when they drifted every click landed on the row below the pointer. hqtui's tree reports the row it drew each node on viaonRow, so the map is built by the thing that draws. The tests press screen cells:screen.find("web")thenscreen.click(x, y), and assert the pane that gets joined in isweb's.There was no hover, so a click had to be spent selecting. That is where the double-click came from.
onHoverRowplushoveredis the whole of it now.The restore path was escape sequences only. hqtui's Terminal restores on SIGINT, SIGTERM and an uncaught error by default (
installExitHandlers), so that class of damage is the library's problem rather than a thing this file has to keep remembering.A folding tree, in place
The herd is herds containing members, which is a tree, and Anthony's standing expectation for a pane of things-containing-things is that it unfolds in place on one click with the row under the pointer lit. Verified live in a real tmux workspace:
Clicking a herd folds it; clicking a member opens it, which means shown and given the keyboard. Nothing is ever swapped out for a different screen. The state mark is a right-aligned column with PRD 0019's
?for an inferred state, suppressed onunknownwhose mark is already?.Actions are real buttons with
onPress, so they join the Tab order and are clickable, and the shortcut sits in a column rather than two spaces after a ragged label.The two traps, and why neither is tripped
collapseBordersonly merges where two BORDERED siblings touch, needs gap 0 and an explicitborderedflag on any wrapper. The sidebar is one unborderedboxin a 26-column pane, so there is nothing for it to merge; it is explicitly off, with the reasoning in a comment rather than left to be rediscovered.headingwidget inside the box, and the actions are separated by adividerrather than by a second panel.Tests
node --testover the whole repo, on the committed tree:tests 3514 pass 3510 fail 0 skipped 4test/herd-sidebar-click.test.mjswas rewritten aroundrenderToScreen, which is the part worth having: it renders the real view headlessly and then presses real cells.clicksherdSidebaritself wired to a fake App and a stand-in tmux: a click joins the pane in and hands it the keyboard; a tmux call that throws paints the reason instead of ending the sidebar; a dead member says so; enter still opens;xstill stopstest/herd-workspace.test.mjskeeps the real-tmux integration test unchanged, and the row/line agreement tests are gone because that agreement is no longer something this repo has to maintain.Verified live as well, with the real
moshcode herd sidebarrunning in a real tmux workspace pane and clicks delivered through tmux.What porting the rest would take
Written up here rather than guessed at in a follow-up ticket.
src/herd-ui.mjs, the plain list (348 lines). Small and nearly free: it is already alayout()+render()+parseInput()triple that maps onto atablewithonSelectRow/onHoverRow, and it already has the alt-screen and restore dance hqtui would take over. The one real question is the hand-off: the list gives the terminal to a realtmux attachand takes it back, so the app has tostop()and be rebuilt around the attach rather than merely redraw. Half a day, and it deletes the last hand-written SGR mouse parser in the repo.src/herd-tile.mjs(168 lines). Nothing to port. It has no renderer at all: it is tmuxjoin-paneplusselect-layout tiledplus key bindings, and the only thing it draws is a tmux status line. Leave it.src/herd-bar.mjs(321 lines). This is the interesting one and the one most likely to go wrong. It is a one-row pane that resizes itself to 14 rows when it has output and back to 1 when it does not, which is exactly hqtui'sinlineviewport.textInputpluslogwould replace the hand-rolled line editing and the\x1b[2J\x1b[Hrepaints. The risk is that the bar also lives under a plainattach, not only in the workspace, and it drivesresize-paneon its own pane while hqtui believes it owns a fixed rectangle; those two have to be reconciled before any of it is written. A day, and it should not be bundled with anything else.src/tui.mjs, the pit shell (1,557 lines). Explicitly out of scope here, and it should stay out for a while. It contains no escape sequences at all: it is a readline REPL, and readline is doing real work for it (history, editing, completion). hqtui'stextInputis not a readline replacement, andinlinemode plus a prompt is a different program from what is there now. This is a rewrite of the pit's input model, not a port of its rendering, and it wants its own PRD.src/rss-ui.mjs(553 lines). Not herd at all, but it is the other hand-rolled full-screen surface in the repo (9 escape-sequence sites) and it would port the same way the plain list does. Worth doing right afterherd-ui.mjs, while the pattern is fresh.Not done
moshcode psroster, the mosh bar and the tiled layout are untouched.