Skip to content

Windows fixes: caption clearance, GUI subsystem, elision budget, dark titlebars#122

Merged
ctate merged 12 commits into
mainfrom
fix/windows-preannounce
Jul 13, 2026
Merged

Windows fixes: caption clearance, GUI subsystem, elision budget, dark titlebars#122
ctate merged 12 commits into
mainfrom
fix/windows-preannounce

Conversation

@ctate

@ctate ctate commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator

Four fixes from the post-0.5.0 Windows headed smoke of the TypeScript apps, each verified headed on a real Windows box.

  • Text elision budget refunds the full snap quantum (aaa5f256): pixelSnapGeometryRect rounds frame edges independently, so a hug-sized text frame at a fractional position can lose up to a full device pixel of width — but the wrap budget only refunded half, so the scaffold counter's digits ellipsized ("2" → "…"). Pinned by a fractional-origin × digits × scale sweep plus the exact centered-row scaffold shape.
  • Drag-headers clear the Windows caption cluster (cd067a70): the chrome channel already reported the caption-button rect; nothing consumed it. UiApp.rebuild now does a single content-aware retry: if drag-header content (text/controls, not backgrounds/spacers) lands under the platform-reported cluster, layout trims the header row's content box on that side. Covers the macOS leading mirror. Apps that already pad stay byte-identical (soundboard-ts verified unchanged; system-monitor-ts status text now ends exactly at the cluster's leading edge).
  • Release exes get the GUI subsystem (970e63e8): every Windows app exe was /SUBSYSTEM:CONSOLE, flashing a console window on launch. Release-shaped builds now link .windows (Debug keeps the console for dev logs), packaging reads the wrapped exe's PE subsystem and teaches a rebuild when it wraps a console binary, and the package report carries the verdict. Redirected logging on GUI exes verified intact.
  • Dark-mode titlebars + scaffold empty state (7f1c22fa): standard-chrome windows set DWMWA_USE_IMMERSIVE_DARK_MODE from the OS scheme at creation and on appearance broadcasts; the scaffold status bar shows "press Stamp for a timestamp" instead of "stamped: -1ms".

Suites green on this tree: test, validate, test-tooling, test-examples-native, test-windows-web-layer-audit (cross-compiles the Windows host C++), test-ts-core-e2e. Changelog fragments included for all four.

ctate added 4 commits July 12, 2026 23:41
- Edge snapping rounds each frame edge independently, so an exact-fit text frame at a fractional position can lose up to a FULL device pixel of width — the old 0.5/scale hand-back plus the 0.125 elision slack under-covered it, and the TS scaffold's centered counter digit painted as "…" on Windows at scale 1
- textWrapMaxWidth now returns width + 1/scale with the epsilon policy documented at the seam; regression tests sweep fractional origins for every digit at scales 1/1.25/1.5/2 plus the exact centered-row scaffold shape
- A window-drag header that never consumed the chrome channel's trailing inset laid right-aligned content under the DWM min/max/close cluster; the caption punch-out then visibly truncated it (system-monitor-ts's header status)
- The runtime now runs a one-retry clearance pass: when the built layout leaves drag-header CONTENT under the platform-reported control cluster, it stamps the cluster into DesignTokens.window_controls and rebuilds once, and widget layout trims the drag row's content box on the cluster's side - apps that already pad (soundboard) never trigger and keep byte-identical layout
- Regression: engine tests for both cluster sides and the content-only trigger, plus runtime tests faking Windows-shaped chrome through the null platform for the naive and the contract header shapes
- Every app exe was console-subsystem (zig's default), so dev AND packaged apps flashed a terminal behind the window; the posture is now: Debug keeps the console (dev logs live there), release-shaped builds get /SUBSYSTEM:WINDOWS - redirected logging still works because only console AUTO-allocation is subsystem-gated
- native package reads the wrapped exe's PE subsystem (peSubsystem), warns with the rebuild teaching when it wraps a console binary, and reports it in PackageStats.windows_console_subsystem; tests pin the reader and both packaging outcomes over synthetic PE headers
- Verified on the Windows box: ReleaseFast ts-smoke.exe reads subsystem=2, launches headed with no console, automation and file-redirected logs intact
- Standard-chrome windows set DWMWA_USE_IMMERSIVE_DARK_MODE from the OS app scheme at creation (pre-show, no light-caption flash) and on appearance broadcasts; hidden-titlebar windows keep their pixel-sampled caption fidelity
- The TS scaffold's status bar branches on {stampedMs < 0} to say "press Stamp for a timestamp" instead of "stamped: -1ms"; fresh scaffold verified through native check and the ts-core e2e scaffold pins
@vercel

vercel Bot commented Jul 13, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
native-sdk Ready Ready Preview, Comment Jul 13, 2026 1:33pm

@ctate ctate changed the title Windows pre-announce fixes: caption clearance, GUI subsystem, elision budget, dark titlebars Windows fixes: caption clearance, GUI subsystem, elision budget, dark titlebars Jul 13, 2026
ctate added 2 commits July 13, 2026 01:04
- The web-frontend scaffold (Next/Vite/React/Svelte/Vue, native init --full) generates its own build.zig and never set exe.subsystem, so those apps still shipped console-subsystem Windows release exes - and the packaging warn had no remedy, since rebuilding can't fix a scaffold that never sets it
- The generated build.zig now carries the same release-only assignment as the SDK build graph (Debug keeps the console for dev logs), with the condensed rationale
- The Vite template test pins the emitted condition and assignment lines
- The collision scan counts anchored descendants of the drag header, but the remedy only trimmed the flow content box - an anchored floater triggered the one retry and then stayed under the caption cluster (retry paid, nothing moved)
- layoutWidgetDepth now passes the anchor base through windowControlsClearedContent for window_drag widgets only, covering both the trailing (Windows) and leading (macOS) clusters; non-drag widgets' anchored children are untouched by construction
- Engine test pins the anchored-only collision converging in one pass (scan fires, remedy moves it, re-scan stays quiet), the macOS mirror, and byte-identical non-drag anchoring under stamped tokens
ctate added 2 commits July 13, 2026 01:47
- rebuildWindowSlot laid out exactly once, so a model-declared hidden-inset window's drag header still rendered content under the OS caption cluster; it now runs the same collision scan + one-retry pass as the main rebuild, stamping the cluster into a local copy of the slot's tokens.
- windowControlsReservation takes the canvas label so both rebuild paths share it, and the slot's build+layout pass is factored into buildWindowSlotPass (same arena reuse as the main path's buildLayoutPass retry).
- ui_app_window_tests: a colliding secondary drag header re-lays clear of the cluster while the main canvas stays unstamped, and a padded secondary header keeps its own layout.
- peIsConsoleSubsystem slurped the whole exe through readPath (capped at 128 MiB) and swallowed every error as false, so a console exe over the cap packaged without the promised warning and packaging allocated the entire binary to read 2 bytes.
- peSubsystemAtPath reads the 0x40-byte DOS header, bounds the rest to e_lfanew + 94 bytes through the existing pure peSubsystem parser, and rejects e_lfanew past a 1 MiB ceiling as not-a-real-PE; only genuinely-unreadable files still degrade to no-claim.
- Tests: a sparse console exe past the old cap still warns, and a bogus 8 MiB e_lfanew answers nothing under the failing allocator (no full-file or offset-sized allocation).
- package-manifest.zon now carries .subsystem = "gui"/"console" whenever the posture check ran (a Windows package with a binary), threaded through writeReport like asset_count; other targets make no claim
- the stats summary prints "subsystem: gui" or "subsystem: console (a terminal window opens behind the app - rebuild with `native build`)" alongside the web-layer and signing verdicts
- PackageStats.windows_console_subsystem is now a ternary (?bool) so an unprobed package cannot pass for a GUI verdict; the synthetic-PE tests pin both report fields and the no-binary null
- Replace the bool verdict with WindowsSubsystem { gui, console, unknown }: gui and console only when the optional header said so, unknown for non-PE/truncated bytes, benign read errors, and unmodeled subsystem values; null still means the check never ran
- Diagnostic prints "subsystem: unknown (unrecognized executable format)" and the report writes .subsystem = "unknown" instead of affirming gui; console warning behavior and OutOfMemory propagation unchanged
- Pin unknown for a non-PE file, a truncated PE, and a native-subsystem exe; retarget the sparse-oversized and offset-ceiling probes onto the verdict helper
ctate added 2 commits July 13, 2026 08:32
- The emitted build.zig defaulted -Doptimize to Debug for everything, so the documented `zig build package` wrapped a Debug, console-subsystem exe; the package step now builds its own exe that defaults to ReleaseFast (mirroring `native build`) while an explicit -Doptimize or --release still pins both roles.
- Registers -Doptimize by hand instead of standardOptimizeOption so the graph can tell unset from explicit, the same optimizeMode split build/app.zig uses; the --optimize arg, the artifact name, and the Windows GUI-subsystem posture all key on the package exe's actual mode.
- Extends the Vite template pins to the package-exe shape and forbids the stale dev-exe wiring.
- The collision scan intersected each candidate's full frame with the caption cluster, so a grow/stretch centered title spanning the header row false-positived while its glyphs sat clear — and the paid retry visibly shifted the title.
- Single-line text leaves now intersect their aligned painted bounds: measured width through the same tokens seam the layout ran with, capped at the frame, placed per text_alignment; controls keep the frame test, and span paragraphs or explicit newlines fall back to it conservatively.
- Threads tokens from both windowControlsReservation call sites, pins the centered-title no-retry case at engine and runtime level, and keeps the trailing true-positive, the macOS leading mirror, and post-remedy re-scan convergence green.
@ctate ctate merged commit 8e37536 into main Jul 13, 2026
36 of 37 checks 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