Skip to content

Keep Windows sidebar usable at narrow widths - #909

Merged
coneilen merged 8 commits into
mainfrom
coneilen-responsive-sidebar-767-435
Oct 21, 2026
Merged

coneilen merged 8 commits into
mainfrom
coneilen-responsive-sidebar-767-435

Conversation

@coneilen

@coneilen coneilen commented Oct 21, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

This PR makes the parent-painted Windows canvas sidebar responsive at narrow widths, enforces a 452-DIP client minimum, and keeps narrow header actions reachable through a native » overflow menu. Terminal workspaces retain the safe docked rail reservation because their separate child HWNDs cannot be covered by the parent-painted overlay; this is not full #767 closure.

Refs #767

Changes

  • Paint active navigation selection blue while keeping inactive selection neutral; cover selected/unselected rows, hover-only row controls, and clipped UIA controls.
  • Share the rail's overlay/docked/hidden bounds across parent-painted canvas rendering, input, and UIA; block canvas hover/release targets under the visible rail, clip the rail background before the full-width Activity strip, and center empty states in the visible detail region.
  • Enforce the 452-DIP client minimum at 96/144/192 DPI without reducing larger OS minima; render a native » overflow menu and route its ordered actions through existing handlers.
  • Preserve Finish Windows Quick Chat canvas and workspace parity #906's centered painted/UIA Quick Chat empty-state action and keep native New Chat at responsive top-right add_bounds, including when empty.
  • Integrate Finish Windows workspace chrome parity #910's workspace chrome order (Split Right, Split Down, New Tab) and focused backend pane detail in TerminalSurface, preserving pane identity and control-order behavior alongside the responsive sidebar.
  • Expose header-overflow:actions as a native UIA Button with Invoke/focus routing and stable AutomationId; normalize focus when resize moves a direct action into overflow.
  • Keep WorkspaceControls a pure Zig test root. Document and test the terminal child-HWND limitation: with WS_CLIPCHILDREN and separate Winghostty child surfaces, terminal workspaces retain a 220-DIP rail reservation at 452 DIP rather than placing the rail over child windows.

Test plan

RED: C:\gct\c767r\sidebar\zig-0.15.2\zig.exe test src\Sidebar.zig -target x86_64-windows-msvc -lc -luser32 -lgdi32 -IC:\gcp\c767r\winghostty\include --test-filter "remaining sidebar interaction parity" -> inherited c767r implementation log: active fill expected blue 16745482, found neutral 3816004.
GREEN: C:\gcs\c767p\zig-0.15.2\zig.exe test src\WorkspaceControls.zig --test-filter "responsive sidebar" -> 2 listed: 2 passed, 0 skipped, 0 failed.
REGRESSION: C:\gcs\c767p\zig-0.15.2\zig.exe test src\GraphCanvas.zig -target x86_64-windows-msvc -lc -luser32 -lgdi32 -lgdiplus -IC:\gcp\c767p\winghostty\include --cache-dir C:\gct\c767p\cache\canvas-post910 --global-cache-dir C:\gct\c767p\global\canvas-post910 -> 411 listed: 394 passed, 17 skipped, 0 failed.

Current-head test setup: Commands below ran from graphcode-windows with the pinned environment loaded using . 'C:\gcs\c767p\environment.ps1'. TEMP/TMP and all caches were under C:\gct\c767p; the provider was under C:\gcp\c767p. Sidebar and GraphCanvas roots used their native target/link flags. WorkspaceControls was run with the canonical bare command, without importing GraphCanvas or native headers. App commands used these exact arguments and inputs:

$env:TEMP = 'C:\gct\c767p'
$env:TMP = 'C:\gct\c767p'
$env:ZIG_LOCAL_CACHE_DIR = 'C:\gct\c767p\cache\workspace-post910'
$env:ZIG_GLOBAL_CACHE_DIR = 'C:\gct\c767p\global\workspace-post910'
$zig = $env:GRAPHCODE_ZIG0152
$include = 'C:\gcp\c767p\winghostty\include'
$winghosttyLib = 'C:\gcp\c767p\winghostty\zig-out\lib\winghostty-win32-host.lib'
$terminalVtLib = 'C:\gct\c767p\terminal-vt-out\lib\ghostty-vt-static.lib'
$appCache = 'C:\gct\c767p\cache\app-post910'
$appGlobal = 'C:\gct\c767p\global\app-post910'
$canvasCache = 'C:\gct\c767p\cache\canvas-post910'
$canvasGlobal = 'C:\gct\c767p\global\canvas-post910'
$sidebarCache = 'C:\gct\c767p\cache\sidebar-post910'
$sidebarGlobal = 'C:\gct\c767p\global\sidebar-post910'
$terminalCache = 'C:\gct\c767p\cache\terminal-post910'
$terminalGlobal = 'C:\gct\c767p\global\terminal-post910'
$appArgs = @(
  'src\App.zig', 'src\AccessibilityProvider.cpp', 'src\FilePicker.c', 'src\FolderPicker.c',
  $winghosttyLib, '-DUNICODE', '-D_UNICODE', '-target', 'x86_64-windows-msvc',
  '-lc', '-luser32', '-lgdi32', '-lgdiplus', '-lmsimg32', '-lopengl32', '-lkernel32',
  '-limm32', '-lole32', '-loleaut32', '-luiautomationcore', '-lshell32', '-ladvapi32',
  '-lwinhttp', "-I$include", $terminalVtLib
)
$terminalArgs = @(
  'src\TerminalSurface.zig', '-target', 'x86_64-windows-msvc', '-lc', '-luser32',
  '-lgdi32', '-ladvapi32', "-I$include", $terminalVtLib
)

Focused invocations and results:

  • & $zig test src\Sidebar.zig -target x86_64-windows-msvc -lc -luser32 -lgdi32 "-I$include" --test-filter "remaining sidebar interaction parity" --cache-dir $sidebarCache --global-cache-dir $sidebarGlobal -> 2 listed, 2 passed, 0 skipped, 0 failed.
  • & $zig test src\WorkspaceControls.zig --test-filter "responsive sidebar" -> 2 listed, 2 passed, 0 skipped, 0 failed.
  • & $zig test src\GraphCanvas.zig -target x86_64-windows-msvc -lc -luser32 -lgdi32 -lgdiplus "-I$include" --test-filter "responsive sidebar" --cache-dir $canvasCache --global-cache-dir $canvasGlobal -> 7 listed, 7 passed, 0 skipped, 0 failed.
  • & $zig test src\GraphCanvas.zig -target x86_64-windows-msvc -lc -luser32 -lgdi32 -lgdiplus "-I$include" --test-filter "header overflow" --cache-dir $canvasCache --global-cache-dir $canvasGlobal -> 1 listed, 1 passed, 0 skipped, 0 failed.
  • & $zig test src\GraphCanvas.zig -target x86_64-windows-msvc -lc -luser32 -lgdi32 -lgdiplus "-I$include" --test-filter "responsive overlay centers" --cache-dir $canvasCache --global-cache-dir $canvasGlobal -> 1 listed, 1 passed, 0 skipped, 0 failed.
  • & $zig test src\GraphCanvas.zig -target x86_64-windows-msvc -lc -luser32 -lgdi32 -lgdiplus "-I$include" --test-filter "responsive overlay rail background" --cache-dir $canvasCache --global-cache-dir $canvasGlobal -> 1 listed, 1 passed, 0 skipped, 0 failed.
  • & $zig test src\GraphCanvas.zig -target x86_64-windows-msvc -lc -luser32 -lgdi32 -lgdiplus "-I$include" --test-filter "responsive overlay paints the Quick Chat empty action" --cache-dir $canvasCache --global-cache-dir $canvasGlobal -> 1 listed, 1 passed, 0 skipped, 0 failed.
  • & $zig test src\WorkspaceControls.zig -> 3 listed, 3 passed, 0 skipped, 0 failed.
  • & $zig test src\Sidebar.zig -target x86_64-windows-msvc -lc -luser32 -lgdi32 "-I$include" --cache-dir $sidebarCache --global-cache-dir $sidebarGlobal -> 232 listed, 232 passed, 0 skipped, 0 failed.
  • & $zig test src\GraphCanvas.zig -target x86_64-windows-msvc -lc -luser32 -lgdi32 -lgdiplus "-I$include" --cache-dir $canvasCache --global-cache-dir $canvasGlobal -> 411 listed, 394 passed, 17 skipped, 0 failed.
  • & $zig test @appArgs --test-filter "responsive sidebar" --cache-dir $appCache --global-cache-dir $appGlobal -> 11 listed, 11 passed, 0 skipped, 0 failed.
  • & $zig test @appArgs --test-filter "header overflow" --cache-dir $appCache --global-cache-dir $appGlobal -> 2 listed, 2 passed, 0 skipped, 0 failed.
  • & $zig test @appArgs --test-filter "Quick Chat remaining parity" --cache-dir $appCache --global-cache-dir $appGlobal -> 9 listed, 9 passed, 0 skipped, 0 failed.
  • & $zig test @appArgs --test-filter "parity sidebar navigation UIA" --cache-dir $appCache --global-cache-dir $appGlobal -> 1 listed, 1 passed, 0 skipped, 0 failed.
  • & $zig test @appArgs --test-filter "responsive overlay rail blocks" --cache-dir $appCache --global-cache-dir $appGlobal -> 1 listed, 1 passed, 0 skipped, 0 failed.
  • & $zig test @appArgs --test-filter "responsive header focus moves" --cache-dir $appCache --global-cache-dir $appGlobal -> 1 listed, 1 passed, 0 skipped, 0 failed.
  • & $zig test @appArgs --test-filter "responsive terminal workspace" --cache-dir $appCache --global-cache-dir $appGlobal -> 1 listed, 1 passed, 0 skipped, 0 failed.
  • & $zig test @appArgs --test-filter "DPI UIA terminal tab close and controls retain physical geometry" --cache-dir $appCache --global-cache-dir $appGlobal -> 1 listed, 1 passed, 0 skipped, 0 failed.
  • & $zig test @appArgs --test-filter "workspace surface: native New Tab publishes every drawn tab" --cache-dir $appCache --global-cache-dir $appGlobal -> 1 listed, 1 passed, 0 skipped, 0 failed.
  • & $zig test @appArgs --test-filter "sidebar loop opens, New Tab, and Split Right" --cache-dir $appCache --global-cache-dir $appGlobal -> 1 listed, 1 passed, 0 skipped, 0 failed.
  • & $zig test @appArgs --test-filter "Quick Chat parity UIA and Show in Graph" --cache-dir $appCache --global-cache-dir $appGlobal -> 1 listed, 1 passed, 0 skipped, 0 failed.
  • & $zig test @appArgs --test-filter "application activation tracks" --cache-dir $appCache --global-cache-dir $appGlobal -> 1 listed, 1 passed, 0 skipped, 0 failed.
  • & $zig test @appArgs --test-filter "the minimum window height" --cache-dir $appCache --global-cache-dir $appGlobal -> 1 listed, 1 passed, 0 skipped, 0 failed.
  • & $zig test @appArgs --test-filter "DPI header layout" --cache-dir $appCache --global-cache-dir $appGlobal -> 1 listed, 1 passed, 0 skipped, 0 failed.
  • & $zig test @appArgs --test-filter "DPI UIA" --cache-dir $appCache --global-cache-dir $appGlobal -> 3 listed, 3 passed, 0 skipped, 0 failed.
  • & $zig test @appArgs --test-filter "header UIA identities" --cache-dir $appCache --global-cache-dir $appGlobal -> 1 listed, 1 passed, 0 skipped, 0 failed.
  • & $zig test @appArgs --test-filter "header presentation follows" --cache-dir $appCache --global-cache-dir $appGlobal -> 1 listed, 1 passed, 0 skipped, 0 failed.
  • & $zig test @appArgs --cache-dir $appCache --global-cache-dir $appGlobal -> 1457 listed, 1440 passed, 17 skipped, 0 failed.
  • & $zig test @terminalArgs --test-filter "workspace chrome actions occupy distinct visible buttons" --cache-dir $terminalCache --global-cache-dir $terminalGlobal -> 1 listed, 1 passed, 0 skipped, 0 failed.
  • & $zig test @terminalArgs --test-filter "issue 772 workspace chrome matches mac control order and focused backend identity" --cache-dir $terminalCache --global-cache-dir $terminalGlobal -> 1 listed, 1 passed, 0 skipped, 0 failed.
  • & $zig test @terminalArgs --test-filter "workspace chrome split glyphs retain their existing action columns" --cache-dir $terminalCache --global-cache-dir $terminalGlobal -> 1 listed, 1 passed, 0 skipped, 0 failed.
  • & $zig test @terminalArgs --test-filter "lane 9 tab names use primary pane type and actual backend without guessing" --cache-dir $terminalCache --global-cache-dir $terminalGlobal -> 1 listed, 1 passed, 0 skipped, 0 failed.
  • & $zig test @terminalArgs --test-filter "workspace chrome pane headers follow selected layout and focus" --cache-dir $terminalCache --global-cache-dir $terminalGlobal -> 1 listed, 1 passed, 0 skipped, 0 failed.
  • & $zig test @terminalArgs --cache-dir $terminalCache --global-cache-dir $terminalGlobal -> 291 listed, 291 passed, 0 skipped, 0 failed.
  • pwsh -NoProfile -File Tools\windows\Tests\AccessibilityProvider.Native.Tests.ps1 -> 224 native checks, 0 failures.
  • pwsh -NoProfile -File Tools\windows\Tests\AccessibilityProvider.Native.Tests.Harness.Tests.ps1 -> 25 harness checks, 0 failures.

Failure provenance: The inherited behavioral REDs above are from the implementation-run c767r logs, not reruns of the current head. The other inherited REDs were rail content-left expected 0/found 220 at 451, 452, and 671 DIP; GraphCanvas still exposed a direct action at 480 DIP; and App minimum width expected 452/found 100. The Sidebar RED log also has a vswhere.exe probe warning, but the Zig tests ran and produced the behavioral failure.

The previous exact-head CI run 39525402589 had two independent, session-caused failures: shard 0's App selection-paint contract expected neutral pixel 4471354 but found active blue 689407; shard 1 could not compile the registered bare WorkspaceControls root because its redundant GraphCanvas import reached windows.h. Neither was a cascade or flake. The App test now deliberately covers active blue, inactive neutral, and unselected non-blue rows; WorkspaceControls is again a pure root. Shard 2, integration, and packaging passed. GraphContextMenu's expected/found diagnostic in its semantic-fallback test was not a failure.

Before the #910 merge, exact-head d29b2fe8 CI fully passed: Windows shell run 39534470567 (all three unit shards, integration, packaging contracts, and real-product packaging), Windows port validation 39534470558, Windows release hardening 39534470532, DCO, TDD evidence, and macOS shared Swift regression. The final branch merges current origin/main 46e22f2d with no conflicts. The listed local tests passed on post-merge head affb9d9e; fresh CI for this head is pending.

Other implementation-time failures are classified separately from the inherited REDs: one App header-overflow fixture temporarily selected Local instead of expected Remote, and an intermediate overflow-threshold assertion failed; both were corrected before the current focused filters passed. A duplicate App test declaration and the native-provider fragment-root compile mismatch were compile-only issues, both corrected; the fragment-root test now explicitly queries IRawElementProviderFragmentRoot. The first GraphCanvas count wrapper expected 408 while its subprocess was green at 409 listed (392 passed, 17 skipped); a later wrapper expected 410 while its subprocess was green at 411 listed (394 passed, 17 skipped). Those were stale wrapper assertions, not product failures. A placeholder-path call executed zero tests and is recorded as harness-only.

Sidebar.zig, WorkspaceControls.zig, and TerminalSurface.zig pass pinned zig fmt --check. App.zig and GraphCanvas.zig still report formatter differences; no broad formatting was applied. Scoped diff/EOL checks cover all eight PR files.

Known limitation: The parent-painted overlay applies to canvas surfaces. The workspace terminal keeps the 220-DIP reservation at 452 DIP because WS_CLIPCHILDREN and separate Winghostty child HWNDs prevent parent paint from covering those children; this docked behavior is documented and tested.

Not verified: live HWND clamp/composition; native overflow popup keyboard/focus/assistive-technology behavior; physical hover/activation; screen-reader behavior; mixed/per-monitor DPI; paired visual comparison; captures; daemon persistence; macOS make test/make check; fresh post-#910-merge CI on the final pushed head; or full #767 closure.

Checklist

  • I have read the Contributing Guidelines
  • I have signed off my commits (git commit -s) per the DCO
  • Tests pass locally (make test)
  • Code follows the existing style (make check)
  • I added the test/contract before the implementation and observed the intended RED failure

coneilen and others added 8 commits October 20, 2026 17:48
Keep inactive navigation neutral and cover hover controls through the memory-DIB path.

Signed-off-by: Colin Neilens <coneilen@microsoft.com>

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Colin Neilens <coneilen@microsoft.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Colin Neilens <coneilen@microsoft.com>
Preserve the Quick Chats placement invariant for overlays while leaving centered empty states on other canvases. Restore unrelated App and GraphCanvas formatting without broad formatting.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Colin Neilens <coneilen@microsoft.com>
Keep overlay bounds consistent for canvas painting and hit testing, bind the rail background to its viewport, and expose the overflow action through UIA.

Signed-off-by: Colin Neilens <coneilen@microsoft.com>

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Colin Neilens <coneilen@microsoft.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Colin Neilens <coneilen@microsoft.com>
Remove the duplicate resize contract, document the native workspace HWND limit, and complete overflow UIA focus coverage.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Colin Neilens <coneilen@microsoft.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Colin Neilens <coneilen@microsoft.com>
Signed-off-by: Colin Neilens <coneilen@microsoft.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@coneilen
coneilen marked this pull request as ready for review October 21, 2026 04:01
@coneilen
coneilen merged commit ebab7f0 into main Oct 21, 2026
32 of 33 checks passed
@coneilen coneilen mentioned this pull request Oct 21, 2026
3 of 5 tasks
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