z widens the readme panel at the brief card's expense - #64
Merged
Conversation
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>
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.
Sometimes the readme wants to be wider than 34% of the terminal.
ztoggles 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
zfires fromfocusBrief || focusHelp— the gateR/H/Malready 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 zoomand flips nothing rather than setting a flag nothing follows —toggleGroupByTag's refusal, same shape.!m.readyis 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 overm.helpZoom. Parameterizing it (rather than copying aModelwith the flag flipped) is what lets the toggle compare the two states.applyLayout()is now the single relayout definition — theWindowSizeMsghandler 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
prevWrapWcapture must stay above thecalcPanelWidthsassignment.helpWrapWidth()reads the storedm.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
j/kspotlight in[3]is lost onz, and the README re-renders through glamour synchronously (width is part ofreadmeRenderCache's key). Both are exactly what a width resize already costs.[3]update-log takeover:showsUpdateLog()owns content, zoom owns width. Asserted, not left to a manual check.Chrome
[3]'s footer gains az zoomcell, 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 aj/k navigatecell. 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 intoe/# note / tags(theo/cidiom) to buy the row forz 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:
prevWrapWmoved below the recompute, thesetToolsContenttail dropped, the!readyguard removed, the narrow refusal removed, the gate widened to every focus, the flag flipped withoutapplyLayout, 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 zoomjoinsTestStatusBarNeverWraps' sweep down to 24 columns: it is the one status whose trigger condition is a narrow terminal, andrenderStatusBar'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.