feat: add Pane Search Phase 2 checkpoint (v1.3.2) - #45
Merged
Conversation
Restyle the pane-local search box to the winTerm compact overlay language: search glyph, input, current/total counter, case and regex toggles, previous/next, and close at chrome density with theme-aware system brushes, plus width-driven Normal/Compact/Minimal layout states so narrow panes keep the input and close button usable. The counter keeps the existing localized status semantics fed by the search core. Show a scrollbar search overview while search is open, reusing the existing mark bitmap and throttled update path: one right-aligned pip per matching buffer row across the full scrollback with same-row occurrences deduplicated, and the current match row drawn at double width into the empty center stripe. Decouple the pips from ShowMarks - generic marks keep obeying the setting while search pips render whenever search is open - guard the hidden-scrollbar case, collapse the canvas when nothing remains to draw, and refresh on navigation so the emphasized pip tracks the search core. No timers or polling. Add ControlCore::SearchCurrentMatchRow, the SearchUxHelpers header (layout thresholds, mark-surface eligibility, distinct-row enumeration), and four ControlCoreTests covering invalid regex, current-row navigation, same-row deduplication, and the helper contracts including the ShowMarks/search eligibility matrix. Advance the engineering version to 1.3.2 following the checkpoint convention, append v1.3.2 to the release workflow checkpoint-tag lists, update every pinned version literal, and document the Phase 2 behavior in CHANGELOG.md, keyboard-shortcuts.md, and current-progress.md.
Owner
Author
|
Local compiled test results (x64 Release, TAEF):
Gates: |
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.
Summary
Phase 2 of the Pane Search roadmap toward v1.4: the Phase 1 functional
search becomes the winTerm search experience. Two deliverables: the
restyled, width-adaptive search box, and the scrollbar search overview
with markers decoupled from the
ShowMarkssetting. Engineeringcheckpoint
1.3.2— not an alpha, not a beta, not a downloadablemilestone. GitHub Latest and WinGet keep pointing at v1.2.0, and
v1.3.0-beta3 remains the newest published prerelease.
Do not add the
buildlabel — quick validation is the intended CIdepth for this PR.
Related issues
None — roadmap work item (Pane Search Phase 2), building on #44.
Detailed changes
Phase 1 baseline (unchanged)
Ctrl+F/Ctrl+Shift+F→ active pane → pane-localSearchBoxControl→live search over the full buffer/scrollback → all matches highlighted,
Enter/Shift+Enter wrap-around navigation, Esc cleanup, split-pane and
shell-input isolation. The upstream pipeline (
SearchBoxControl,ControlCore::Search,Search/TextBuffer::SearchText, rendererhighlights) stays the engine; Phase 2 adds no second search engine, index,
or buffer copy.
SearchBox UX changes
SearchBoxControl.xamlis restyled in place — same control, sameownership, same events, same
x:Uids:SystemControlBackgroundChromeMediumLowBrushsurface, subtle border, 8pxoverlay radius, 27px chrome-density controls
(
Design/DensityTokens.h/Design/RadiusTokens.hvalues), and aleading search glyph marked
AccessibilityView="Raw".current / totalstatus, case toggle, regextoggle, previous, next, close.
SetStatus/_FormatStatuskeep thelocalized
m/n, "No results", "Searching", bounded999+/?, andinvalid-regex semantics, fed by
ControlCore::Searchresults — no secondcount, no XAML-side recounting.
TermControlpushes the pane width into the box(
RootGrid.SizeChanged→SetAvailableWidth), which maps it through apure helper to three visual states — Normal, Compact (case/regex toggles
collapse, input narrows), Minimal (status and prev/next also collapse) —
so the input and the close button stay usable at any width. Hidden
toggles keep their checked state, so search options stay in effect. The
mapping is monotonic in the pane width, so states cannot oscillate.
Scrollbar overview implementation
Reuses the existing scrollbar mark pipeline end to end — the
ScrollBarCanvasWriteableBitmap, the throttled_throttledUpdateScrollbarpath, and the existing right-third search pipstripe with same-row deduplication. The dedup loop moved into a tiny shared
helper (
ForEachDistinctSearchRowin the newSearchUxHelpers.h) used byboth the drawing code and the tests. Marker positions map buffer rows over
newMaximum + newViewportSizeexactly as before, i.e. across the fullsearchable scrollback, not the viewport. No new canvas, renderer, timer, or
marker database; a closed search performs no marker work.
ShowMarks decoupling
The surface gate changed from
_showMarksInScrollbaralone togeneric || searchOpen(ShouldRenderScrollbarMarkSurface), with eachcategory keeping its own rule inside the draw:
settings.ShowMarks()is true (default false, unchanged);
Refresh sites follow the same split:
_handleSearchResultsredraws whilesearch is open (also on plain navigation, so the current-match pip tracks),
and closing search always requests one redraw, which clears the pips and —
new — collapses the canvas when nothing remains to show (previously the
canvas could keep a stale bitmap visible after
ShowMarksturned off atruntime). A zero-dimension guard covers the deliberately hidden scrollbar
(
scrollbarState: hidden): search stays fully functional, simply withoutthe overview, and the user's scrollbar preference is never overridden.
Current-match behavior
ControlCore::SearchCurrentMatchRow()projects the searcher's focusedindex (
Search::CurrentMatch()— the single source of truth) onto itsbuffer row. The pip for that row draws at double width, extending into the
bitmap's empty center stripe, so it is clearly distinguishable from
ordinary matches without new colors, flashing, animation, or timers — and
therefore theme- and high-contrast-safe by construction (it uses the same
terminal foreground color as the other search pips). In-terminal
distinction was already native: the renderer paints the focused span with
dedicated
highlightFocuscolors (AtlasEngine_drawHighlighted), whichis retained untouched.
Accessibility
All existing
x:Uids, tooltips, automation names, the accessible status(
GetAccessibleStatus+SearchBoxResultAnnouncementnotifications), andTab/Shift+Tab/Enter/Shift+Enter/Esc handling are preserved. The new search
glyph is decorative and excluded from the accessibility tree. The current
result is never color-only: the counter states it textually, the terminal
focus highlight uses distinct colors, and the scrollbar pip differs by
width.
Validation performed
Environment: Windows 11 x64, MSVC 14.44 + Windows SDK (local toolchain).
pwsh scripts\winterm\verify-version.ps1— passed (all 1.3.2literals and the extended checkpoint-tag list).
pwsh scripts\winterm\verify-branding.ps1— passed.pwsh scripts\winterm\test-release-workflow.ps1— passed (checkpointguard now covers v1.3.2 at all four sites).
pwsh scripts\winterm\test.ps1 -Suite Smoke -Configuration Release -Platform x64— run before push; result recorded in the PR conversation.
Control.Unit.Tests.dllx64 Release built locally (includes the XAMLcompile of the restyled
SearchBoxControl) and run under TAEF; resultsin the PR conversation. CI's no-label quick validation intentionally does
not compile native tests.
New/updated tests (
ControlCoreTests):TestSearchInvalidRegexNoStaleResults— invalid regex reports the errorstate with zero results and no stale highlight rows; fixing the
expression recovers the matches.
TestSearchCurrentMatchRowTracksNavigation— the current row is -1without a search, focuses the first match on live typing, follows
Enter navigation including wrap-around, and clears with
ClearSearch().TestSearchSameRowOccurrences—ERROR foo ERROR bar ERRORyields 3occurrences for the counter and exactly 1 distinct row for the overview.
TestSearchUxHelperContracts— the §27 eligibility matrix(ShowMarks × search active), the width→layout-state mapping including
exact thresholds and the unmeasured-host default, the state names the
XAML defines, and distinct-row enumeration for empty and multi-row
collections.
KeyBindings defaults) re-run as regression.
Manual smoke checklist (§29) for user validation:
1..20 | ForEach-Object { Write-Host "line $_ ERROR test" },Ctrl+F,ERROR: 20 matches,n/20counter, all highlighted,overview distribution on the scrollbar.
the wider current pip follows.
Write-Host "ERROR foo ERROR bar ERROR": counter 3,one scrollbar row marker.
own independent search, counter, highlights, and pips.
stay hidden. Mandatory test.
marks remain per ShowMarks; focus returns to the terminal.
and close stay usable; no pane layout damage.
match vs current match distinguishable.
scrollbarState: hidden, search worksfully without an overview surface.
Version change
1.3.1→1.3.2following the same engineering-checkpoint mechanism:plain version,
channel: stable, no prerelease suffix, package1.3.2.0,v1.3.2appended to the release workflow's checkpoint-taglists (four guard sites) and to the pinned copies in
test-release-workflow.ps1andverify-version.ps1. All other versionsurfaces advanced together (both
version.jsonfiles,ReleaseMetadata.h, MSIX manifest, three.rcfiles, PowerShell modulemanifest/runtime, workspace fallbacks, script literals, both READMEs,
docs/current-progress.md,CHANGELOG.md). Nothing is published; Lateststays v1.2.0.
Deferred Phase 3 work
Performance investigation and hardening: debouncing, large-scrollback
optimization, caching/indexing, worker threads. Also explicitly out of
scope, unchanged: clickable/hoverable markers, result previews, cross-pane
or global search, persistent search state.
Checklist
CHANGELOG.md.Development-Changes.mdledger has been pushed with the final source SHA, link, summary, and checkpoint/release.winterm-siteasset, or no screenshot was added.winterm.exe, and Microsoft Terminal coexistence remain isolated.