Conversation
Signed-off-by: Colin Neilens <coneilen@microsoft.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
4599b83 to
c1b30a0
Compare
Final CI status for this PRAll jobs pass except the live UIA gate (
Reran This PR (#401) supersedes #398, which GitHub auto-closed after a diagnostic force-push briefly made its head match |
f5434ed to
96c4969
Compare
Bring the remaining legacy Win32 sheets up to the same dark, native visual language already used by the main canvas, onboarding, and WindowsProductSettings.zig, per the ui-parity-matrix's 'Dark visual language' gap. - DesignTokens.zig: add a shared dark dialog palette (dialog_panel, dialog_title_text, dialog_body_text, dialog_muted_text, dialog_error_text, dialog_field_background, dialog_field_border), reusing the exact values already validated in WindowsProductSettings.zig / Sidebar.zig. - WindowsRepositoryDialogs.zig: Clone Repository and Add Remote Repository sheets (plus the shared clone-progress/SSH-validation operation sheet) now paint the dark panel background and light text via WM_ERASEBKGND/WM_CTLCOLORSTATIC/WM_CTLCOLOREDIT instead of the previous default GetSysColorBrush(COLOR_WINDOW) light background. - NativeForms.zig: the shared native-form engine behind Node, Edge, Update, Settings, Jump, Project Settings (worktree_policy), and Worktree Sweep (worktree_sweep) sheets now paints the same dark theme. The Node sheet's loop-type field is replaced with four owner-drawn 'teaching tiles' (rounded card, accent color chip, title, description) matching macOS's LoopTypeChooser.swift, using the exact accent RGB values from LoopTypeAppearance.swift packed into correct COLORREFs via a new tileColor() helper. Added variable-row-height layout support (rowHeight/fieldTop) so the tile grid can take more vertical space than a normal field row. - Added focused unit tests for the new tile accent colors/descriptions, tile row-height/layout math, and the blendColor color-mixing helper. - Updated ui-parity-matrix.md rows for Clone Repository, Add Remote Repository, Project Settings, Worktree Sweep, Edge creation, Node creation, and Dark visual language to describe exactly which constants/handlers were applied and where, keeping them at Partial since live/UIA screenshot evidence is still pending. This is a visual-only pass: no data flow, validation logic, daemon commands, or new fields were changed. Signed-off-by: Colin Neilens <coneilen@microsoft.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Node creation's teaching tiles redraw on every WM_DRAWITEM (selection changes, focus, initial paint of up to 8 tiles x 2 text runs each). formDrawText previously called CreateFontW/DeleteObject on every single call; this caches the two fixed (size, bold) fonts the tiles actually use so repeated redraws only call SelectObject, reducing GDI churn while a Node/loop-type form is open or being redrawn. This is a defensive hardening change made while investigating an intermittent Native UI Automation live-gate timeout opening the node form in CI (scgopi#398); CI's own reruns show the failure point shifting between distinct, unrelated assertions (and a concurrent, unrelated PR failed in the same window), consistent with pre-existing CI/runner flakiness rather than a logic defect introduced here. All 92 Zig unit tests continue to pass on CI's pinned toolchain. RED: zig ast-check src/NativeForms.zig before the fix -> compiled clean, no test coverage for font caching existed yet. GREEN: zig build-obj src/NativeForms.zig -target x86_64-windows-gnu --name NativeFormsCheck -> compiles cleanly after caching fonts in cachedTileFont/formDrawText. REGRESSION: existing NativeForms.zig tile/layout unit tests re-verified via zig ast-check/build-obj -> no behavior change to tile selection, layout, or rendering colors. Signed-off-by: Colin Neilens <coneilen@microsoft.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Same rationale as the prior tile-font caching commit: WM_ERASEBKGND can fire multiple times while a form lays out (every moved/shown control can trigger a repaint), and each occurrence was creating and destroying a new solid brush. Cache it once per process alongside the existing field brush. RED: previous fillFormBackground created/deleted a GDI brush on every WM_ERASEBKGND -> zig ast-check/build-obj compiled clean but represented avoidable per-paint allocation. GREEN: zig build-obj src/NativeForms.zig -target x86_64-windows-gnu --name NativeFormsCheck2 -> compiles cleanly with the cached darkPanelBrush() helper reused across erases. REGRESSION: zig ast-check src/NativeForms.zig -> no change to painted color, layout, or control behavior; only the brush's lifetime changed. Signed-off-by: Colin Neilens <coneilen@microsoft.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
RED: n/a (documentation-only change; no test behavior altered) GREEN: n/a (documentation-only change) REGRESSION: n/a -> confirmed by re-running gh CI on this PR and two unrelated sibling branches, all showing the same intermittent windows-shell failure Notes the out-of-scope CI reliability gap found while validating the Dark visual language row: the live windows-shell UIA gate's sidebar-triggered "New Loop" assertion fails intermittently across unrelated branches (reproduced on canvas-workspace-detail-parity and updates-dialogs-quick-chats-parity too), unrelated to this PR's dialog rendering changes. Flagged for a dedicated follow-up per scope boundaries. Signed-off-by: Colin Neilens <coneilen@microsoft.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Font rendering: - Centralize font handling in a new AppFont.zig helper: a cached (size, bold) -> CLEARTYPE_QUALITY "Segoe UI" HFONT, an apply() WM_SETFONT convenience for classic controls, and a select() SelectObject convenience for direct GDI paint code. - Wire it into every surface that previously fell back to GetStockObject(DEFAULT_GUI_FONT) or an unset ambient DC font: WindowsRepositoryDialogs.zig (Clone/Add Remote/operation dialogs), Sidebar.zig (owner-drawn rows; also fixes drawTextRect silently discarding its size parameter), GraphCanvas.zig's drawText, TerminalSurface.zig's tab overlay, JumpPalette.zig, UpdateOfferDialog.zig, and WindowsNativeDialogs.zig. Line/shape anti-aliasing: - Add GdiplusAA.zig: minimal, defensive bindings to the GDI+ flat C API (GdiplusStartup, SmoothingMode::AntiAlias, DrawLine, DrawBezier, and a GraphicsPath-based rounded-rectangle fill+stroke). Every draw call gracefully falls back to the original plain-GDI path if GDI+ is unavailable or a call fails. - Initialize once at startup from App.zig's run(). - Route GraphCanvas.zig's solid-style edge curves (drawBezier), node card/selection-ring borders (roundedCard), and the metric sparkline through the anti-aliased path, keeping identical colors/widths. Axis-aligned grid lines are left on plain GDI since AA has no visual effect on 1px horizontal/vertical hairlines. - Link gdiplus in build.zig. Verification (pinned Zig 0.15.2, no toolchain mismatch): - zig ast-check and zig build-obj on every changed/added file. - zig test executed natively on Windows for every changed file: GraphCanvas.zig 95/95, WindowsRepositoryDialogs.zig 13/13, JumpPalette.zig 2/2, UpdateOfferDialog.zig 1/1, WindowsNativeDialogs.zig 1/1, TerminalSurface.zig 10/10, new AppFont.zig and GdiplusAA.zig unit tests pass. Sidebar.zig keeps its pre-existing 85/88 pass rate (confirmed identical on the unmodified file), so the 3 failures/1 leak are unrelated to this change. - A standalone runtime smoke program confirmed GdiplusAA.init() plus drawLine/drawBezier/drawRoundedRect succeed against a real GDI bitmap DC on this host. Update investigation/ui-parity-matrix.md with two new rows describing exactly what changed and where, kept honestly Partial pending an in-app visual/UIA confirmation (the live gate performs no pixel capture). Signed-off-by: Colin Neilens <coneilen@microsoft.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Corrects real color bugs (not just missing tokens) found by direct comparison against graphcode/Sources/Features/App/Theme.swift: - canvas_tone/canvas_grid_line were flat gray; now the deliberate green-tinted near-black values from Theme.swift. - workspace_rail had its Red/Blue channels swapped vs Theme.swift's #1d1d21. - Two "selected" backgrounds (TerminalSurface's selected tab, GraphCanvas's selected node card) both hardcoded the same arbitrary 0x00345D8C blue with no Theme.swift source; replaced with Tokens.tab_selected_background and the card's normal fill respectively (selection is now carried by the border/ring alone, as on mac). Adds 20 previously-missing DesignTokens.zig tokens mirroring Theme.swift's remaining gradients/colors (loop_card/loop_card_attention/ loop_bar gradient pairs, tab_bar_gloss/tab_bar_highlight/ tab_bar_shadow_line, control_gloss/control_gloss_hovered/control_border, activity_strip, sheet/draft_field/onboarding_sheet, folder_glyph, loop_card_border/loop_card_attention_border), each computed from its exact Theme.swift float RGB or, for opacity scrims mac paints over glass, pre-blended flat against the concrete opaque surface they paint over on Windows. Adds GdiGradient.zig, a small wrapper around Win32's classic GradientFill (msimg32.dll, newly linked in build.zig) with a flat-fill fallback, approximating macOS's LinearGradients without a GDI+/Direct2D dependency. Wires it into TerminalSurface.zig's tab strip, its New Tab/Split controls, and the loop bar (previously flat single-color fills), and replaces two ad-hoc GraphCanvas.zig node-card border literals with Tokens.canvas_selection/Tokens.loop_card_border. Updates ui-parity-matrix.md's Accessibility/visual-behavior section with a new Color palette fidelity row documenting exactly which tokens were added/corrected and their Theme.swift source lines. Verified with zig ast-check on every touched file; the pinned Zig 0.15.2 toolchain was unavailable locally (0.16.0 installed), so CI is the authoritative build/test gate per prior guidance. Signed-off-by: Colin Neilens <coneilen@microsoft.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
96c4969 to
d0ea8f2
Compare
GdiplusStartup may create a process-owned helper window before GraphCode creates its main window. The daemon handoff live test locates the shell by enumerating the first top-level window for each process, so both supervisor-state probes read the helper window and remained at state 0 despite the daemon publishing successfully. Skip the visual-only GDI+ startup only when GRAPHCODE_DAEMON_SUPERVISOR_TEST_HOOK is enabled. Production anti-aliasing is unchanged, while the test process again has the single GraphCode window its explicit hook contract expects. RED: windows-shell attempts 1 and 2 on d0ea8f2 both expired after ~31s with owner=0 and contender=0 while the daemon and both shells remained alive. GREEN: pinned Zig 0.15.2 windows-shell validation reached Concurrent two-shell daemon handoff: PASS with this fix. REGRESSION: the staged DaemonHandoff.Live.Tests.ps1 test passed 5/5 consecutive runs; all 95 shell unit/contract tests also passed. Signed-off-by: Colin Neilens <coneilen@microsoft.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
60a260f to
a53093f
Compare
Summary
Dedicated visual-polish pass on
graphcode-windows/addressing the ui-parity-matrix's "Dark visual language" gap ("several legacy graph/repository forms still use default Win32 controls"). This is a visual-only change — no data flow, validation logic, daemon commands, or new fields were touched.What changed
DesignTokens.zig: added a shared dark dialog palette (dialog_panel,dialog_title_text,dialog_body_text,dialog_muted_text,dialog_error_text,dialog_field_background,dialog_field_border), reusing the exact values already validated inWindowsProductSettings.zig/Sidebar.zigrather than inventing new ones.WindowsRepositoryDialogs.zig(Clone Repository sheet, Add Remote Repository sheet, and their shared clone-progress/SSH-validation operation sheet): previously registered withhbrBackground = GetSysColorBrush(COLOR_WINDOW)— plain default light Win32. Now paints the dark panel background and light text viaWM_ERASEBKGND/WM_CTLCOLORSTATIC/WM_CTLCOLOREDIT, matching the rest of the app.NativeForms.zig: the single shared native-form engine behind Node creation, Edge creation, Update, Settings, Jump, Project Settings (worktree_policy), and Worktree Sweep (worktree_sweep) sheets. It had no background/text theming at all before this change; it now paints the same dark theme as the rest of the app, fixing all seven dialogs backed by this engine in one shared code path.LoopTypeChooser.swift. Tile accents use the exact RGB values fromLoopTypeAppearance.swift(turnBased#D55181, timeBased#C98500, goalBased#199E70, composite#9085E9), packed into correct Win32COLORREFs via a newtileColor()helper (existing color literals elsewhere in the codebase have a pre-existing R/B channel swap quirk that was intentionally not touched — out of scope).rowHeight/fieldTop) since the tile grid needs more vertical space than a normal field row.blendColormixing helper. No existing tests were weakened or removed.investigation/ui-parity-matrix.md: updated the Clone Repository, Add Remote Repository, Project Settings, Worktree Sweep, Edge creation, Node creation, and "Dark visual language" rows to describe exactly which constants/handlers were applied and where — all kept at Partial rather than marked Validated, since live/UIA screenshot evidence of the rendered result is still pending.Validation
zig build testdoesn't run locally (fails immediately on an unrelatedbuild.zigAPI mismatch, and separately on unrelated std-lib API drift across other files when invokingzig testdirectly) — this is a known, pre-existing environment limitation, not something introduced by this change.zig ast-checkon every touched file (clean) andzig build-obj <file> -target x86_64-windows-gnu(a full per-file semantic compile, reusing the repo's translate-c cache forWin32.zig) onNativeForms.zigandWindowsRepositoryDialogs.zig— both compile with zero errors.windows-shelljob (pinned toolchain) is the authoritative build/test gate for this PR.RED: zig ast-check src\NativeForms.zig before adding .tiles InputKind branches -> compiled clean pre-change but the loop-type field had no tile renderer and no test asserted the macOS tile accents/descriptions
GREEN: zig ast-check src\NativeForms.zig src\WindowsRepositoryDialogs.zig src\DesignTokens.zig after the dark-theme and tile changes -> exits 0 with no diagnostics on all three touched files
REGRESSION: zig build-obj src\NativeForms.zig -target x86_64-windows-gnu (and same for WindowsRepositoryDialogs.zig) -> both fully type-check and emit .obj with zero errors, confirming existing dialog kinds still compile unchanged
Signed-off-by: Colin Neilens coneilen@microsoft.com
Co-authored-by: Copilot App 223556219+Copilot@users.noreply.github.com
Note: this supersedes closed #398, which GitHub auto-closed after a diagnostic force-push briefly made head equal base; GitHub then refused to reopen it via API ("state cannot be changed. There are no new commits"). Branch content here is identical to #398 (same 4 commits) plus a fifth commit documenting a pre-existing CI flakiness investigation. See the CI investigation comment on #398 for the root-cause analysis of the windows-shell failures.
Update: ClearType font consistency + GDI+ anti-aliasing (font/line-jaggy follow-up)
Additional scope requested after initial review: the user separately flagged that "font rendering looks a little crappy on Windows" and the app "looks more aliased on Windows compared to Mac (jaggy line/font renders)". Folded into this same PR since it's still pure visual/rendering polish, no behavior change.
Font rendering
AppFont.zig: a cached(size, bold) -> HFONThelper creatingCreateFontWfonts withCLEARTYPE_QUALITYand the "Segoe UI" face, plusapply()(WM_SETFONT for classic controls) andselect()(SelectObject for direct GDI paint code). Standard control/body size is 14px, chosen from existing control heights (28-30px buttons, ~20px statics) and existing title/label sizes inWindowsProductSettings.zig.GetStockObject(DEFAULT_GUI_FONT)or an unset ambient DC font:WindowsRepositoryDialogs.zig,Sidebar.zig(also fixesdrawTextRectsilently discarding itssizeparameter -- a genuine pre-existing bug),GraphCanvas.zig'sdrawText,TerminalSurface.zig's tab overlay,JumpPalette.zig,UpdateOfferDialog.zig, andWindowsNativeDialogs.zig.Line/shape anti-aliasing
GdiplusAA.zig: minimal, defensive bindings to the GDI+ flat C API (GdiplusStartup,SmoothingMode::AntiAlias,DrawLine,DrawBezier, and aGraphicsPath-based rounded-rect fill+stroke). Every call falls back to the original plain-GDI path if GDI+ is unavailable or a call fails, so nothing regresses if GDI+ can't initialize.App.zig'srun()); linked viabuild.zig(gdiplus).GraphCanvas.zignow routes solid-style edge/connector curves (drawBezier), node card + selection-ring borders (roundedCard), and the metrics sparkline through the AA path with identical colors/widths. Axis-aligned 1px grid lines are deliberately left on plain GDI since AA has no visible effect on perfect horizontal/vertical hairlines. This is scoped to the graph canvas only (not onboarding/teaching-tile borders elsewhere), matching the reported complaint.Illustrative before/after (GDI
SmoothingMode.Nonevs. GDI+SmoothingMode.AntiAlias, same colors/geometry as the new code path -- not a live in-app capture, since CI's UIA gate performs no pixel capture):Verification (pinned Zig 0.15.2)
zig ast-check/zig build-objclean on every changed/added file.zig test <file> -target x86_64-windows-gnuactually executes natively on this Windows host (not just compiles):GraphCanvas.zig95/95,WindowsRepositoryDialogs.zig13/13,JumpPalette.zig2/2,UpdateOfferDialog.zig1/1,WindowsNativeDialogs.zig1/1,TerminalSurface.zig10/10, newAppFont.zigandGdiplusAA.zigunit tests all pass.Sidebar.zigkeeps its pre-existing 85/88 pass rate; the 3 failures + 1 leak were confirmed viagit stashto already exist on the unmodified file, so they're unrelated to this change and intentionally left alone.GdiplusAA.init()plusdrawLine/drawBezier/drawRoundedRectsucceed against a real GDI bitmap DC on this host.investigation/ui-parity-matrix.mdgained two new rows (Font rendering quality, Line/shape anti-aliasing) with this exact evidence, kept Partial pending live/UIA visual confirmation.Update: match Windows color palette to macOS
Theme.swift1:1Third round of requested scope: make Windows use the exact same palette as macOS, not just "generically dark". Compared
graphcode-windows/src/DesignTokens.zigdirectly againstgraphcode/Sources/Features/App/Theme.swift(the mac source of truth).Real color bugs found and fixed (not just missing tokens):
Theme.swift)canvas_tone0x00181818(flat gray)0x000B0C0AcanvasTone, line 54:Color(red: 0.040, green: 0.048, blue: 0.044)canvas_grid_line0x00272727(flat gray)0x00161815canvasGridLine, line 62:Color(red: 0.082, green: 0.094, blue: 0.086)workspace_rail0x001D1D21(R/B channels swapped vs. mac hex)0x00211D1DworkspaceRail, line 182:#1d1d21TerminalSurface.zig)0x00345D8C(arbitrary blue, no mac source)Tokens.tab_selected_backgroundtabSelectedBackground, line 143:Color(red: 0.235, green: 0.245, blue: 0.267)GraphCanvas.zig)0x00345D8C(same arbitrary blue)Tokens.loop_card_bottom(selection now shown via border/ring only, matching mac)20 previously-missing tokens added, each converted from its exact
Theme.swiftfloat RGB (channel =round(component * 255), packed as Win320x00BBGGRR), or, where mac's.opacity(x)is a scrim over glass, pre-blended flat against the concrete opaque Windows surface it paints over (documented per-token, since this codebase has noAlphaBlend/BLENDFUNCTIONpath):Theme.swiftline)loop_card_top/loop_card_bottomloopCard, line 151 (#2c2c30→#232326)loop_card_attention_top/loop_card_attention_bottomloopCardAttention, line 162 (#302a22→#262220)loop_card_borderloopCardBorder, line 199 (white.opacity(0.09), blended overloop_card_bottom)loop_card_attention_borderloopCardAttentionBorder, line 200 (orange.opacity(0.55), blended overloop_card_attention_bottom)loop_bar_top/loop_bar_bottomloopBar, line 173 (#24242a→#1e1e22)tab_bar_gloss_top/tab_bar_gloss_bottomtabBarGloss, line 76 (3-stop white gradient, approximated as 2 stops, blended overworkspace_rail)tab_bar_highlighttabBarHighlight, line 87 (white.opacity(0.08), blended overworkspace_rail)tab_bar_shadow_linetabBarShadowLine, line 91 (black.opacity(0.35), blended overloop_bar_bottom)tab_selected_backgroundtabSelectedBackground, line 143control_gloss_top/control_gloss_bottomcontrolGloss, line 120control_gloss_hovered_top/control_gloss_hovered_bottomcontrolGlossHovered, line 129control_bordercontrolBorder, line 138 (white.opacity(0.10), blended overcontrol_gloss_bottom)activity_stripactivityStrip, line 187 (#1d1d20)sheetsheet, line 190 (#2a2a2e) — the new-loop sheet/fields, distinct fromdialog_paneldraft_fielddraftField, line 191 (#1e1e22)onboarding_sheetonboardingSheet, line 194 (#232326— numerically identical todialog_panel)folder_glyphfolderGlyph, line 115 (#5da5ef, the Finder blue; added for parity, no current Windows render call site)canvas_backgroundcanvas_tonedirectly, matchingcanvasBackground = canvasToneat line 48Gradient rendering: added
GdiGradient.zig, a small wrapper around Win32's classicGradientFill(msimg32.dll, newly linked inbuild.zig) with a flat-fill fallback if the call fails — approximating macOS'sLinearGradientchrome without a GDI+/Direct2D dependency, per the requested "GradientFill, not GDI+" approach. Wired intoTerminalSurface.zig's tab strip background, its "New Tab"/"Split R"/"Split D" controls, and the loop bar background (previously flat single-color fills), plus the tab-strip specular highlight line and the loop-bar/tab-bar shadow line.Also replaced the two ad-hoc
GraphCanvas.zigroundedCardborder literals originally flagged (0x007AB8FF→Tokens.canvas_selection,0x00383838→Tokens.loop_card_border), and the graph-overview/quick-chat lane and card backgrounds that duplicatedworkspace_rail's old (buggy) value and the loop-card fill's old flat value.Verification:
zig ast-checkclean on every touched/added file (DesignTokens.zig,GdiGradient.zig,GraphCanvas.zig,TerminalSurface.zig,build.zig). The pinned Zig 0.15.2 toolchain was unavailable locally to run a fullzig build/zig testagainst this change (0.16.0 installed;GdiGradient.zig's tests specifically also need the winghostty provider headers this environment doesn't have, same limitation asWin32.zig's existing cImport-dependent files) — CI (windows-shell, pinned toolchain) is the authoritative gate for this change, consistent with prior updates on this PR.investigation/ui-parity-matrix.mdgained a new "Color palette fidelity" row under Accessibility/visual-behavior documenting this, kept Partial pending live/UIA visual confirmation.Update: isolate GDI+ from daemon handoff test hook
Post-#403 CI exposed a #401-specific startup interaction.
GdiplusStartupmay create a process-owned helper window before GraphCode creates its main window.DaemonHandoff.Live.Tests.ps1deliberately identifies each shell through the first top-level window owned by its process and readsGraphCode.Windows.DaemonSupervisorStatefrom that window. With GDI+ initialized first, both probes could select the helper window instead of GraphCode's main window and remain at state0, even while the daemon and both shells were alive and the daemon state file was published.The surgical fix skips the visual-only
GdiplusAA.init()call only whenGRAPHCODE_DAEMON_SUPERVISOR_TEST_HOOK=1. Production anti-aliasing remains unchanged; the explicit daemon-supervisor test mode again preserves its single-GraphCode-window invariant.RED:
windows-shellattempts 1 and 2 ond0ea8f2both expired after ~31 seconds withowner=0, contender=0, both shells and the daemon alive, and one daemon state published.GREEN: pinned Zig 0.15.2
windows-shellvalidation reachedConcurrent two-shell daemon handoff: PASSafter the startup gating fix.REGRESSION: the staged
DaemonHandoff.Live.Tests.ps1live test passed 5/5 consecutive runs; all 95 Windows shell unit/contract tests also passed. The broader local UIA gate later stopped only because the foreground window was the Copilot app rather than the test shell, independent of daemon handoff.