Skip to content

fix(ui): three layout regressions from the typography pass merge#20

Merged
DaxxSec merged 2 commits into
mainfrom
fix/sidebar-and-card-regressions
May 14, 2026
Merged

fix(ui): three layout regressions from the typography pass merge#20
DaxxSec merged 2 commits into
mainfrom
fix/sidebar-and-card-regressions

Conversation

@DaxxSec
Copy link
Copy Markdown
Owner

@DaxxSec DaxxSec commented May 13, 2026

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 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 adds delta only — section growing from bounds.width = 0 to 220 produces 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. rebuild now adds subviews without sizing them; a new relayoutContents() runs from both rebuild (initial paint, once the caller assigns a frame) AND 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 (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:) 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.

Test plan

  • `xcodebuild build` succeeds
  • `xcodebuild test` — 292/292 passing
  • Launch app, open library window
  • Sidebar section titles read full text ("OPERATING SYSTEM", "NETWORK", "LOGS"), no left truncation
  • VM rows render as 62pt-tall multi-line cards (name + status pill on row 1, OS · CPU · disk on row 2, network chip + rate + packets + sparkline on row 3)
  • Click a sidebar filter row ("Running", "Linux", etc.) — table updates, count badge dimmer styling reflects selection
  • Bottom detail card: "● RUNNING" / "○ STOPPED" pill text reads centered inside its 80pt bounds, no left-bias halo

🤖 Generated with Claude Code

DaxxSec and others added 2 commits May 13, 2026 11:36
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>
@DaxxSec DaxxSec merged commit 0d01e58 into main May 14, 2026
1 check passed
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