fix(ui): three layout regressions from the typography pass merge#20
Merged
Conversation
Three visible regressions that the user reported after #15+#18 landed: 1. Sidebar section titles truncating ("ERATING SYSTEM", "TWORK", "GS") ================================================================== `TacticalSidebarSection.rebuild()` runs inside `init` before the caller has set the section's frame. It read `bounds.width` (=0 at that moment) and gave the title label / row views `width: bounds.width - inset * 2` = **-24pt**. The autoresizing mask (`.width`) compensates when the parent's bounds change, but it adds delta only — a section that grows from `bounds.width = 0` to `220` produces a title width of `-24 + 220 = 196`. That should be visible end-to-end, but NSTextField that's first sized with a negative-width frame caches its rendering metrics and the title gets visually left-truncated by ~inset pixels on every paint after. Fix: defer layout. `rebuild` now adds the subviews without sizing them; a new `relayoutContents()` runs from both `rebuild` (so the initial paint has a valid frame once the caller assigns one) AND from `layout()` (Cocoa hook fired whenever the section's bounds resolve). Layout constants moved to type-level statics so the two call sites share them. 2. VM table rows squished to ~20pt — multi-line card not showing ================================================================ The XIB hardcodes `tableView rowHeight="20"`. `switchTableToCardMode` sets `tableView.rowHeight = 62`, which SHOULD be authoritative — but when the XIB latches the lower value before the awakeFromNib call that runs the swap, NSTableView's first layout pass uses the latched 20pt and the card cell renders as a single squished line with row-3 chips spilling into row-1's name area. Fix: implement `tableView(_:heightOfRow:)` returning `VMCardCellView.rowHeight`. The delegate is queried on every row draw, so the value can't be stale or pre-empted by the XIB. 3. Detail-card status pill drifts off-center inside its 80pt bounds ==================================================================== The pill is an NSTextField with `alignment = .center`, `isBordered = false`, layer-backed background. On some macOS revisions the plain `alignment` property doesn't fully center text inside a borderless layer-backed label — the text drifts leftward, producing the dead-space halo on the right that appears as "the pill is spaced wrong". Fix: use `attributedStringValue` with an explicit centered NSMutableParagraphStyle. The paragraph-style centering bypasses NSTextField's plain alignment quirk and works deterministically. No behavior changes — these are all visual/positioning fixes. Full test suite still 292/292 passing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User clarified the "pill is spaced wrong" report — they meant the status pill inside the VM-card row (e.g. "● RUNNING" / "○ STOPPED" on the right edge of row 1), not the detail card pill at the bottom of the window. My previous commit on this PR fixed the wrong pill. Same root cause + fix: NSTextField's plain `alignment = .center` drifts leftward on layer-backed borderless labels. Apply explicit centered NSMutableParagraphStyle via attributedStringValue on the VMCardCellView statusPill (running-state path) and the AI Sandbox TEMPLATE/SESSION pill (sandbox-bundle path). The previous fix to the detail card's makeStatusPill stays — same class of bug, two separate widgets, both needed the treatment. Co-Authored-By: Claude Opus 4.7 (1M context) <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.
Why
You reported three visible regressions after #15 + #18 merged. All three are layout bugs that the merge surfaced — none of them are regressions in #18 itself, but #15 and #18 together exposed them on screen.
#1 — Sidebar section titles truncating ("ERATING SYSTEM", "TWORK", "GS")
`TacticalSidebarSection.rebuild()` runs inside
initbefore the caller has set the section's frame. It readbounds.width(= 0 at that moment) and gave the title label / row viewswidth: bounds.width - inset * 2= -24pt. The autoresizing mask adds delta only — section growing frombounds.width = 0to220produces a title width of-24 + 220 = 196. That should be visible end-to-end, but NSTextField first sized with a negative-width frame caches its rendering metrics and the title gets visually left-truncated by ~inset pixels on every paint after.Fix: Defer layout.
rebuildnow adds subviews without sizing them; a newrelayoutContents()runs from bothrebuild(initial paint, once the caller assigns a frame) ANDlayout()(Cocoa hook fired whenever the section's bounds resolve). Layout constants moved to type-level statics so the two call sites share them.#2 — VM table rows squished to ~20pt, multi-line card not showing
The XIB hardcodes
tableView rowHeight=\"20\".switchTableToCardModesetstableView.rowHeight = 62, which should be authoritative — but when the XIB latches the lower value before theawakeFromNibcall that runs the swap, NSTableView's first layout pass uses the latched 20pt and the card cell renders as a single squished line with row-3 chips (network mode, packets, sparkline) spilling into row-1's name area. That's what you saw as the "all the columns" complaint.Fix: Implement
tableView(_:heightOfRow:)returningVMCardCellView.rowHeight. The delegate is queried on every row draw, so the value can't be stale or pre-empted by the XIB.#3 — Detail-card status pill drifts off-center inside its 80pt bounds
The pill is an NSTextField with
alignment = .center,isBordered = false, layer-backed background. On some macOS revisions the plainalignmentproperty doesn't fully center text inside a borderless layer-backed label — the text drifts leftward, producing the dead-space halo on the right that appears as "the pill is spaced wrong".Fix: Use
attributedStringValuewith an explicit centeredNSMutableParagraphStyle. The paragraph-style centering bypasses NSTextField's plain alignment quirk and works deterministically.Test plan
🤖 Generated with Claude Code