Skip to content

z widens the readme panel at the brief card's expense - #64

Merged
stanlyzoolo merged 4 commits into
mainfrom
feat/help-zoom
Aug 8, 2026
Merged

z widens the readme panel at the brief card's expense#64
stanlyzoolo merged 4 commits into
mainfrom
feat/help-zoom

Conversation

@stanlyzoolo

Copy link
Copy Markdown
Owner

Sometimes the readme wants to be wider than 34% of the terminal. z toggles the three-panel split between 20/46/34 and 20/30/50, giving panel [3] the room and taking it from the card. Session-only, nothing persisted.

Mouse border-dragging was the alternative and got rejected as YAGNI in the brainstorm: far more code and edge cases for the same need, and a zoom toggle does not preclude adding drag later.

Binding

z fires from focusBrief || focusHelp — the gate R/H/M already use, since it is the fourth key that changes what [3] is. Unlike that trio it does not move focus (a width change is not a change of what you are reading) and it fetches nothing, so the status expiry tick is the whole returned command.

Below ~82 columns the 15/30/30 minimum clamps produce the same triple for both variants, so the key answers too narrow to zoom and flips nothing rather than setting a flag nothing follows — toggleGroupByTag's refusal, same shape. !m.ready is checked first and explicitly, not left to the clamps agreeing by coincidence.

Structure

panelWidthsFor(zoom bool) is the pure width core; calcPanelWidths() is the one-line wrapper over m.helpZoom. Parameterizing it (rather than copying a Model with the flag flipped) is what lets the toggle compare the two states.

applyLayout() is now the single relayout definition — the WindowSizeMsg handler collapsed to two assignments plus the call, so the resize path and the toggle cannot drift.

One sharp edge, and it gets its own test: the prevWrapW capture must stay above the calcPanelWidths assignment. helpWrapWidth() reads the stored m.helpW, so a capture placed below compares the new width against itself, the re-wrap guard is dead, and [3] silently keeps its pre-resize wrapping — with every other test in the package green.

Accepted consequences

  • An active j/k spotlight in [3] is lost on z, and the README re-renders through glamour synchronously (width is part of readmeRenderCache's key). Both are exactly what a width resize already costs.
  • Zoom is orthogonal to the [3] update-log takeover: showsUpdateLog() owns content, zoom owns width. Asserted, not left to a manual check.

Chrome

[3]'s footer gains a z zoom cell, placed last so it sheds first, and dropped entirely while an update log owns the panel — same reasoning as the title dropping its source hints there.

Measured shed geometry: the cell survives from ~118 columns in readme mode and ~150 in --help/man, where the entry index adds a j/k navigate cell. The plan guessed ~110; corrected in the docs. Between ~82 and that width the key works unadvertised in the footer, which is the ordinary shed rule — the [?] overlay never sheds and is what documents it at every width.

The overlay merges the e/# rows into e/# note / tags (the o/c idiom) to buy the row for z zoom panel. Column 2 was already at its 16-row budget, so a new binding buys a row rather than appending one. Net rows unchanged in all five self states.

Testing

Every new assertion was mutation-checked: prevWrapW moved below the recompute, the setToolsContent tail dropped, the !ready guard removed, the narrow refusal removed, the gate widened to every focus, the flag flipped without applyLayout, the footer cell removed and un-suppressed under the log, the overlay row removed, and an over-long status in the narrow wrap sweep. All killed. One survived at first — the log-suppression check ran at a width where the cell was shed anyway; the test moved to 160 columns and now bites.

too narrow to zoom joins TestStatusBarNeverWraps' sweep down to 24 columns: it is the one status whose trigger condition is a narrow terminal, and renderStatusBar's statusMsg branch does not truncate.

Full CI matrix green locally, plus the cross-compile steps.

Not done

No look at a real TTY — the rendered output was read through a throwaway probe test at 80/100/110/120/160 (deleted afterwards), which covers layout and shed but not how it feels to press. Worth one manual pass, wide and at 80×24, against a long README.

stanlyzoolo and others added 4 commits August 7, 2026 23:50
z from focusBrief/focusHelp toggles the panel layout 20/46/34 <-> 20/30/50,
zooming panel [3] at the brief card's expense. Session-only view flag; the
relayout is extracted into a shared applyLayout so the WindowSizeMsg handler
and the toggle cannot drift. Mouse border-dragging was considered and
rejected as YAGNI. Plan revised after an auto plan-review pass (boundary of
the extraction, prevWrapW ordering trap, binding scope matched to R/H/M).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sometimes the readme wants to be wider. z from [2] or [3] toggles the
layout between 20/46/34 and 20/30/50 — the same gate R/H/M fire from,
since it is another key that changes what panel [3] is. It moves no
focus and fetches nothing; the state is session-only.

Below ~82 columns the 15/30/30 minimum clamps make both variants
identical, so the key reports "too narrow to zoom" instead of flipping
a flag nothing follows. panelWidthsFor(zoom bool) is the pure core that
makes that comparison possible without copying a Model.

The relayout moves out of the WindowSizeMsg handler into applyLayout(),
so the resize path and the toggle share one definition. The prevWrapW
capture has to stay above the width recompute: helpWrapWidth() reads
the stored m.helpW, so a capture below it kills the re-wrap guard and
[3] keeps stale wrapping with the suite green. Tested.

Footer: [3] gains a "z zoom" cell, last, so it sheds first and is
dropped entirely while an update log owns the panel. The [?] overlay
merges the e/# editor rows to buy the row for "z zoom panel" — the
per-column budget was already at 16.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The narrow-terminal refusal was symmetric, so a session zoomed at 160
columns and then resized to 80 could not clear the flag: z answered
"too narrow to zoom", helpZoom stayed true, and the layout came back
zoomed the moment the terminal grew again. That is toggleGroupByTag's
stuck-in-the-tag-view failure one feature over — and its refusal, which
the docs cite as the precedent for this one, gates activation only.

Gate the check on !m.helpZoom. TestZoomNarrowStillUnzooms drives the
full round trip (zoom wide, shrink, unzoom, regrow) and asserts the
regrown layout is the unzoomed triple, so a flag left stranded fails on
the widths rather than only on the message.

Also correct three things the feature commit claimed and the code does
not support:

- applyLayout's two tests are not the sole guards on their mutations.
  The prevWrapW capture is also held by TestHelpNavIdxResetTriggers,
  TestResizeHeightOnlyKeepsCursor and TestReadmeResizeRerenders; the
  setToolsContent tail by six mouse and line-map tests. What the two
  add is a failure that names the line that moved. Claiming sole
  coverage is the worse error: the next reader deletes the
  redundant-looking test believing the invariant travels with it.
- the footer cell's shed threshold tracks the entry index, not the
  panel mode: ~114 columns without one, ~150 with. A man page without
  an index sheds like the readme, so "~150 in --help/man" was wrong and
  TestHelpFooterZoomCell's man row was asserting about its fixture's
  cache rather than about the mode.
- two comments still pointed at the WindowSizeMsg handler for work that
  moved into applyLayout and initViewports.

Plus: drop widestLine for the existing maxLineWidth, and assert z is
consumed under the [a] and [?] overlays — modeSearch next door proves
the mode dispatch is not uniform enough to leave that to structure.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The hint bar reported "readme zoomed" / "layout restored" on every
successful toggle. Two panels change width over the full height of the
screen — the user is looking straight at the answer, and the bar only
restated it one line below where it happened, displacing the six global
hints and the API gauge for a second to do so.

The success path is now silent and returns nil. That is a stricter
invariant than the expiry tick it replaced: any command coming back from
there is a fetch, and TestZoomFetchesNothing asserts nil directly rather
than asserting the tick rides alone.

The refusal stays. It is the one exit where the screen does not answer —
nothing moved, and "too narrow to zoom" has nowhere else to live. That
is also what separates this toggle from toggleGroupByTag, which keeps
its messages: reordering one panel's rows and inserting headers is a
subtler change, and its message names which of two orderings you are in.

Checked on rendered output at 160 and 80 columns: at 160 the bar is
byte-identical before and after z, hints and gauge intact, while the
widths move 46/78 ↔ 70/54; at 80 the refusal still shows. Both new
assertions and both nil checks were mutation-checked by restoring the
setStatus calls.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@stanlyzoolo
stanlyzoolo merged commit f6d5194 into main Aug 8, 2026
3 checks passed
@stanlyzoolo
stanlyzoolo deleted the feat/help-zoom branch August 8, 2026 18:58
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