style: typography pass — drop hardcoded sizes + NSColor.white onto the design scale#18
Merged
Conversation
…e design scale User feedback: "the font is off as well as button look + styles everywhere in the app". A targeted sweep here, focused on the specific offenders rather than a wholesale rewrite: NSColor.white → AppColors.textPrimary ===================================== Three callsites total in the app, all on dark-themed checkboxes / labels: - PacketAnalysisWindowController: autoScrollCheckbox tint, filterLabel - VMLibraryWindowController: arpCheckbox tint `NSColor.white` is the maximum-contrast value (pure 1.0,1.0,1.0) which against the tactical dark grey reads as visually too hot. `AppColors.textPrimary` is the same color the rest of the dark UI uses (RGB 0.910 / 0.922 / 0.941) — slightly off-white, calibrated to look like text, not headlight. Font sizes onto the LayoutConstants scale ========================================= Five hardcoded sizes pulled onto the existing type scale: - VMLibraryWindowController sidebar brand wordmark: 16 → fontSizeHeader (15) - VMLibraryWindowController info labels: 13/11 → fontSizeSubtitle/Body - VMLibraryWindowController log section buttons: 13 → fontSizeSubtitle - VMLibraryWindowController ARP filter checkbox: 11 → fontSizeBody - SwitchStatisticsWindowController text view: 12 → fontSizeBody - ISOCacheManagerWindow toolbar button: 12 → fontSizeBody Why this set: anything ≥ 12 and ≤ 17 that wasn't already routed through `LayoutConstants.fontSize*` was off-scale. The chosen mapping is the closest scale value in each case — no semantic shifts (e.g. body text isn't promoted to subtitle just because it was 12pt before). Out of scope ============ Sizes 9, 10, and 11 that match `fontSizeCaption` / `fontSizeSmall` / `fontSizeBody` exactly are not the user's complaint — those are already on-scale numerically, just not symbolically. Pulling them onto symbolic names is mechanical busywork that doesn't change how anything looks. A separate refactor can do that pass if it matters later. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
62db98f to
cafb0f8
Compare
7 tasks
DaxxSec
added a commit
that referenced
this pull request
May 14, 2026
* fix(ui): three layout regressions from the typography pass merge 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> * fix(ui): the VM-card row pill (the one user actually meant) 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> --------- Co-authored-by: DaxxSec <dax@example.com> 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
User feedback after the merged redesign:
Targeted sweep at the specific offenders rather than a wholesale rewrite.
NSColor.white → AppColors.textPrimary
Three callsites total — all on dark-themed checkboxes / labels:
NSColor.whiteis the maximum-contrast value (1.0/1.0/1.0). Against the tactical dark grey palette that reads as visually too hot — like a headlight.AppColors.textPrimary(0.910 / 0.922 / 0.941) is slightly-off-white, calibrated to look like text, not glare, and it's the value every other label in the dark UI already uses.Hardcoded font sizes → LayoutConstants scale
Five offenders pulled onto the existing scale:
fontSizeHeader(15)fontSizeSubtitle/fontSizeBodyfontSizeSubtitlefontSizeBodyfontSizeBodyfontSizeBodyChosen mappings are the closest scale value in each case — no semantic shifts. Body text doesn't get promoted to subtitle just because it was 12pt before.
Out of scope
Sizes 9 / 10 / 11 that already match
fontSizeCaption/fontSizeSmall/fontSizeBodynumerically aren't the user's complaint — they look right, they're just spelled with raw integers instead of symbolic names. A separate mechanical refactor can pull those onto symbols if it matters later.Test plan
🤖 Generated with Claude Code