Skip to content

tui: reserve the session row's disclosure and lineage gutter - #1158

Merged
edwin-zvs merged 12 commits into
mainfrom
session-row-gutter-alignment
Aug 2, 2026
Merged

tui: reserve the session row's disclosure and lineage gutter#1158
edwin-zvs merged 12 commits into
mainfrom
session-row-gutter-alignment

Conversation

@edwin-zvs

@edwin-zvs edwin-zvs commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Problem

The hierarchy pass (#1156) appends the children disclosure (/) and the
fork lineage mark () as extra cells after the tree rails. So two
sessions at the same depth no longer share a left edge: the moment a session
grows a subagent or a fork, its own title jumps one column right of its
childless siblings'.

Before #1156 the indent helper subtracted a cell whenever the disclosure was
present, which is why a project's members lined up whether or not they had
nested sessions. That compensation was dropped when the indent became tree
rails.

Fix

Give every row hanging off the tree one shared marker column, one step
past identical rails, reserved whether or not it is used:

row marker
session with children / disclosure
fork lineage mark
archived-children row / disclosure
plain session space
▼ construct-dev  (3)
├─  ● No Nested Session          <- no nested sessions
├─▼ ● Has A Nested Fork          <- has nested sessions; same left edge
├─⑂ ● A Fork                     <- lineage in the same column
└─▸ 2 archived                   <- as the archive disclosure already was

So nesting state changes the glyph, never the geometry; a child's status
glyph lands under its parent's title again; and a fork row and an archived
row differ by their glyph rather than by their offset.

Where a session could claim the cell twice, the disclosure wins — it's the
affordance the operator can act on. No row is both today: the list gives forks
no disclosure, always rendering their descendants expanded. If that changes,
lineage needs a new home rather than a second reserved column, which the spec
and a code comment both now say.

Net width is unchanged from main. The disclosure's click column still
follows the rails, so hit-testing needed no update.

Screenshots

Top row is main, bottom is this branch — same fleet, same terminal size.
In the "before" half Prepare Patch Release's status dot sits one column left
of Session Statuses'; in the "after" half they align.

before/after

Full frames

before:

before

after:

after

The demo uses forks at depth 0/1/2 because groups can't be created from the
CLI, but a project's members hit the identical shift — they're depth-1 rows
whose only difference is the reserved cell.

Tests

  • Every marker is pinned to one cell across both fork/non-fork and all four
    has-children/expanded combinations, glyphs and precedence included.
  • A second test pins that a fork row and an archived-children row under the
    same parent carry the same rails and the same marker width — i.e. they share
    the column rather than each choosing an offset.
  • cargo test --workspace green.

Relevant binary: construct (this PR only touches crates/cli).

The hierarchy pass appended the children disclosure and the fork lineage
mark as extra cells, so a project member that had a subagent or a fork
rendered its title one column right of its childless siblings. Reserve
both cells on every session row instead — a childless, unforked session
paints spaces there — so nesting state changes the glyphs, not the
geometry, and a child's status glyph again lands under its parent's
title.
The gutter already guarantees a blank column between the tree rails and
the status glyph on every row, so the glyph's own leading space was a
third consecutive blank on a childless, unforked session. Drop it: each
row reclaims a column, and a fork's mark now sits directly against its
status dot.
With lineage in its own cell it sat beside the disclosure column and
read as a second disclosure — a fork row and an archived-children row
differed only by which small glyph followed the rails. Close the fork's
branch corner with its mark instead of the plain connector: lineage
becomes structure on the rail, the cell after the rails is unambiguously
the disclosure, and the row spends no column on either.
Lineage rode the branch corner, which kept forks off the column the
archived-children rows use and left a fork's mark two cells from its
status glyph. Put the disclosure, the lineage mark, and the archived
disclosure in the same reserved cell one step past identical rails: a
fork reads '|-Y * Title' exactly as an archive reads '|-> N archived'.
The disclosure wins the cell where a row could claim it twice, since it
is the affordance the operator can act on.
@edwin-zvs
edwin-zvs merged commit d9df4e1 into main Aug 2, 2026
1 check passed
@edwin-zvs
edwin-zvs deleted the session-row-gutter-alignment branch August 2, 2026 06:20
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