Skip to content

fix(web): align keyboard interactions and reveal grouped search matches - #321

Merged
cxxxxxn (cxxxxxn) merged 10 commits into
microsoft:mainfrom
cxxxxxn:fix/ui-search-keyboard-review
Oct 21, 2026
Merged

cxxxxxn (cxxxxxn) merged 10 commits into
microsoft:mainfrom
cxxxxxn:fix/ui-search-keyboard-review

Conversation

@cxxxxxn

Copy link
Copy Markdown
Contributor

Summary

  • Align shared menu focus, keyboard navigation, submenu entry/exit, and form-panel semantics; retain ordinary input editing and native link activation.
  • Keep Canvas search focus and active rows aligned, localize search feedback in English and Chinese, and expose toolbar pressed states without changing the intentional main/context icon stroke distinction.
  • Group search results by node and field, showing each field heading once.
  • Hand Note content matches to preview-local find/highlight without stealing focus, and reveal cached or paginated older Question turns before locating the requested conversation occurrence.
  • Preserve independent highlight ownership and account for nested panel clipping when scrolling matches into view.
  • Fix the two review findings: vertical navigation stays in the parent menu at a submenu trigger, and ArrowRight can enter an already hover-opened submenu.
  • Update the corresponding architecture documentation and regression tests.

Validation

  • Repository-level pnpm typecheck
  • Repository-level pnpm format (no additional changes)
  • Repository-level pnpm lint:fix (0 errors; existing warnings remain; no additional changes)
  • 14 targeted Web test files: 288 tests passed
  • pnpm check:i18n
  • pnpm --filter @huabu/web build
  • git diff --check
  • Browser validation with actual production components and synthetic Note/conversation data: field grouping, second Note occurrence, nested scrolling, cached older turns, two simulated older-history pages, later conversation occurrence, and the reviewed submenu keyboard cases.

Scope and limitations

  • Five focused commits replayed onto current main. The inherited node-placement commit is deliberately excluded.
  • Browser component checks are not a real-backend end-to-end search test.
  • Conversation navigation retains the existing bounded timeout and reports an unavailable match rather than silently jumping to another conversation occurrence; very long histories can require a follow-up targeted-address optimization.
  • No contrast, design-token, dependency, or main/context icon-stroke changes.

cxxxxxn (cxxxxxn) and others added 5 commits October 20, 2026 13:41
Unify shared menu focus and keyboard behavior, preserve custom panel semantics, and synchronize canvas search activation with focused results. Add regression tests and document the interaction contracts.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Translate search status messages, conversation timeout feedback, and all result field labels in English and Simplified Chinese. Cover language switching and localized result states with rendered tests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep navigation and placement aria-pressed states aligned with visual activation through the shared SplitSelect primary action. Add state-transition tests and document the intentional main/context toolbar icon stroke tiers without changing their styling.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Group results by field, hand Note matches to preview find, reveal and load older Question turns, and preserve highlight and focus ownership across surfaces.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep vertical navigation in the parent menu and let ArrowRight enter hover-opened submenus. Cover both reviewed keyboard regressions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 20, 2026 05:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Preview navigation readiness, match-count consistency, and offscreen focus handling still need correction.

4 open issues
What changed in this PR

This PR improves keyboard interaction across shared menus and Canvas search, and connects search results to preview-local navigation.

Changes:

  • Align menu focus, submenu navigation, panel semantics, and toolbar pressed states.
  • Group and localize search results, preserve highlight ownership, and reveal preview matches and older conversation turns.
  • Update architecture documentation and regression coverage.
File Description
docs/​architecture/​web-architecture.md Documents keyboard and search conventions.
docs/​architecture/​preview-workspace.md Documents search handoff and history navigation.
docs/​architecture/​note-node.md Documents Note search handoff.
apps/​web/​src/​store/​previewWorkspace/​actions.ts Allows opening previews without editor focus.
apps/​web/​src/​store/​previewSearchStore.ts Adds addressed search navigation requests.
apps/​web/​src/​store/​previewSearchStore.test.ts Tests request ownership and completion.
apps/​web/​src/​pages/​playground/​NodeToolbarPlaygroundPage.tsx Marks layout panels as dialogs.
apps/​web/​src/​i18n/​resources/​zh-CN/​common.json Adds Chinese search labels and feedback.
apps/​web/​src/​i18n/​resources/​en/​common.json Adds English search labels and feedback.
apps/​web/​src/​hooks/​useTextHighlight.ts Preserves independent highlight ownership.
apps/​web/​src/​hooks/​useTextHighlight.test.tsx Tests highlight merging and cleanup.
apps/​web/​src/​hooks/​searchDom.ts Handles clipping and conversation readiness.
apps/​web/​src/​hooks/​searchDom.test.ts Tests scrolling and occurrence resolution.
apps/​web/​src/​components/​Settings/​agent-profiles/​AgentIconPicker.tsx Declares dialog semantics.
apps/​web/​src/​components/​Panels/​PreviewWorkspace/​PreviewWorkspace.test.tsx Tests editor-focus suppression.
apps/​web/​src/​components/​Panels/​PreviewWorkspace/​PreviewTab.tsx Preserves custom menu semantics.
apps/​web/​src/​components/​Panels/​ExpandedNodePanel/​InPreviewSearchBar.tsx Consumes preview navigation requests.
apps/​web/​src/​components/​Panels/​ExpandedNodePanel/​InPreviewSearchBar.test.tsx Tests asynchronous handoff and ownership.
apps/​web/​src/​components/​Panels/​ExpandedNodePanel/​ExpandedNodePanel.tsx Preserves custom menu handling.
apps/​web/​src/​components/​Panels/​CanvasLayerPanel/​CanvasSearchResults.tsx Groups results and coordinates focus/navigation.
apps/​web/​src/​components/​Panels/​CanvasLayerPanel/​CanvasSearchResults.keyboard.test.tsx Tests grouping, localization, and keyboard behavior.
apps/​web/​src/​components/​Panels/​Canvas/​FloatingToolbars/​NodeFloatingToolbar.tsx Uses shared menu handling and dialog semantics.
apps/​web/​src/​components/​Panels/​Canvas/​FloatingToolbars/​NodeFloatingToolbar.test.tsx Tests overflow-menu keyboard navigation.
apps/​web/​src/​components/​Panels/​Canvas/​FloatingToolbars/​MultiSelectToolbar.tsx Declares size-panel dialog semantics.
apps/​web/​src/​components/​Panels/​Canvas/​FloatingToolbars/​InkAgentDestinationPicker.tsx Preserves custom panel semantics.
apps/​web/​src/​components/​Panels/​Canvas/​CanvasZoomMenu.tsx Preserves custom menu handling.
apps/​web/​src/​components/​Panels/​Canvas/​CanvasToolbar.tsx Exposes active controls as pressed.
apps/​web/​src/​components/​Panels/​Canvas/​CanvasToolbar.test.tsx Tests toolbar pressed states.
apps/​web/​src/​components/​Nodes/​pdf/​PDFZoomControls.tsx Preserves custom zoom-menu handling.
apps/​web/​src/​components/​Nodes/​frame/​FrameNode.tsx Declares layout-panel dialog semantics.
apps/​web/​src/​components/​Messages/​MessageList.tsx Pages older history during search navigation.
apps/​web/​src/​components/​Messages/​MessageList.test.tsx Tests search-driven history loading.
apps/​web/​src/​components/​Common/​SplitSelect.tsx Adds optional primary pressed state.
apps/​web/​src/​components/​Common/​SplitSelect.test.tsx Tests primary-button semantics.
apps/​web/​src/​components/​Common/​Popover.tsx Forwards accessibility and keyboard properties.
apps/​web/​src/​components/​Common/​FloatingToolbar.tsx Declares alignment-panel dialog semantics.
apps/​web/​src/​components/​Common/​DropdownMenu.tsx Adds shared focus and keyboard behavior.
apps/​web/​src/​components/​Common/​DropdownMenu.test.tsx Tests menu and submenu interaction contracts.

Review effort: Balanced

Comment thread apps/web/src/components/Common/DropdownMenu.tsx Outdated
Comment thread apps/web/src/components/Panels/CanvasLayerPanel/CanvasSearchResults.tsx Outdated
Comment thread apps/web/src/components/Panels/ExpandedNodePanel/InPreviewSearchBar.tsx Outdated
Comment thread apps/web/src/components/Panels/ExpandedNodePanel/InPreviewSearchBar.tsx Outdated
Preserve virtualized result focus, reveal menu edges, await PDF indexing and exact asynchronous Note matches, and separate complete navigation counts from bounded highlights.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 20, 2026 06:37

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Search handoffs can select stale or unrelated occurrences, and Zoom dialog Tab navigation regresses.

No open issues 🎉

4 previously missed issues

In code that hasn't changed since last review

Medium severity Restore CanvasZoomMenu action tab traversal

apps/​web/​src/​components/​Common/​DropdownMenu.tsx:86

The new tabIndex={-1} default breaks CanvasZoomMenu's custom dialog, whose handler relies on native Tab traversal between the percentage input and action buttons. Tab from the input now skips every action, and the boundary handler does not dismiss because it expects focus on the last action (CanvasZoomMenu.tsx:109–132). Keep the shared menu default, but add explicit tabIndex={0} overrides to that dialog's five action items.

Medium severity Resolve question content ordinals within their originating message

apps/​web/​src/​components/​Panels/​CanvasLayerPanel/​CanvasSearchResults.tsx:330

Question content ordinals are local to the node body, not the whole conversation. Saving a Chat copies its first cached user message into that body (ChatPanel/index.tsx:876–891), which can be a recent message rather than the thread's first prompt. After this branch loads older turns, content occurrence 0 can therefore select an unrelated earlier match. Resolve content matches within their originating message, or keep them separate from whole-thread ordinal navigation until that address is available.

Medium severity Prevent stale PDF search adapters from consuming new handoffs

apps/​web/​src/​components/​Panels/​ExpandedNodePanel/​InPreviewSearchBar.tsx:132

A handoff to an already searched PDF can consume the previous query's adapter. useRegisterPreviewSearchAdapter publishes registration in a passive effect, so this render still sees the old match count and navigation callback when reveal() changes the query. It can navigate the old query or report no matches, then consume the new request before the replacement adapter arrives. Include query identity in the adapter contract and wait for a matching registration before consuming the nonce. Add a regression for two successive handoffs with different queries to the same PDF.

Medium severity Reject out-of-range PDF match ordinals instead of clamping

apps/​web/​src/​components/​Panels/​ExpandedNodePanel/​InPreviewSearchBar.tsx:143

If the completed PDF index has fewer matches than the requested ordinal, this clamp silently navigates to the last available occurrence and consumes the request. This can happen after document changes or when indexing skips a malformed page. Treat an out-of-range ordinal as unavailable and show the existing notification instead of selecting another match.

4 resolved since last review

Review effort: Balanced

Restore Zoom dialog tab stops, resolve Question body ordinals within a unique source prompt, and reject stale-query or unavailable PDF navigation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 20, 2026 07:18
@cxxxxxn

Copy link
Copy Markdown
Contributor Author

Addressed the four previously missed findings from the latest Copilot review in aa5a120, after reproducing all four on the prior revision:

  • Zoom dialog: explicitly retain native Tab stops on its five action items. Real-browser Tab traversal now visits all five and closes at the boundary.
  • Question content: reuse the shared conversation reveal/paging scheduler, but resolve body-local ordinals only inside the unique complete source prompt. A dedicated prompt-text marker excludes quoted comments, skill chips, interpretation text and attachments. Missing or duplicate source prompts report unavailable rather than selecting another message. Clicking an actual search result with older history loading now scrolls to the recent source message, not the unrelated older match.
  • PDF query identity: adapters carry their query, and handoffs wait for a matching registration. A real four-page PDF switching alpha to beta now consumes the request only with the beta adapter and scrolls to page 4, rather than page 1.
  • PDF ordinal bounds: unavailable ordinals notify instead of clamping. Browser-verified requesting beta occurrence 4 when only one exists: no navigation, localized unavailable notification.

Validation: 329 tests across 19 targeted files passed; repository pnpm typecheck, pnpm format, pnpm lint:fix, and the Web production build passed. Related architecture documentation updated. Temporary fixtures and servers removed; no merge performed.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Search navigation still has unresolved occurrence-mapping, query-normalization, scrolling, and lifecycle defects.

No open issues 🎉

4 previously missed issues

In code that hasn't changed since last review

Medium severity Trim queries before Canvas reveal navigation

apps/​web/​src/​components/​Panels/​CanvasLayerPanel/​CanvasSearchResults.tsx:397

Use the same normalized query as the Canvas request. searchStore.setQuery retains the input verbatim but calls runSearch(query.trim()). Searching needle can therefore return a Note containing only needle, then open its find bar with zero matches and time out. Trim the query passed to reveal and add leading/trailing-whitespace coverage.

Medium severity Preserve Canvas handoffs during StrictMode effect replay

apps/​web/​src/​components/​Panels/​ExpandedNodePanel/​InPreviewSearchBar.tsx:84

The app mounts under StrictMode (main.tsx:38). On a cold preview mount in development, effect replay runs this cleanup while the Canvas-search handoff already owns this node. close() clears the pending request before asynchronous Note content or the PDF adapter becomes ready, so the requested jump is lost. Make session disposal safe during effect replay while retaining cleanup on real unmount, and add a StrictMode cold-handoff regression test.

Medium severity Map raw Markdown search occurrences to rendered positions

apps/​web/​src/​components/​Panels/​ExpandedNodePanel/​InPreviewSearchBar.tsx:149

Canvas content search counts occurrences in raw Markdown (canvas-search.ts:201–203), but this handoff counts rendered text nodes. For [needle](https://example.test/needle) followed by a paragraph containing needle, selecting the URL occurrence jumps to the paragraph and consumes the request; selecting the paragraph's source occurrence times out. fallbackToFirst: false cannot prevent shifted ordinals. Map the selected source occurrence to a rendered position, or navigate in source mode; report unavailable when it cannot be mapped. Add a Markdown-link regression case.

Medium severity Scroll matched ranges into view, not entire parent elements

apps/​web/​src/​hooks/​searchDom.ts:175

The new clipping check detects an offscreen occurrence, but the scroll below centers its entire parent element rather than the matched Range. If a paragraph or prompt is taller than the panel, an occurrence near its end can remain offscreen after scrolling. The scheduler nevertheless reports success and consumes the navigation request. Position the Range within the scrollable ancestors using its coordinates, and add coverage for distant occurrences in one tall text element.

Review effort: Balanced

Unify normalized query identity, renderer-owned handoffs, verified source-to-rendered addressing, and range-based visibility. Preserve raw-source matches with explicit unavailable feedback and wait for indexed PDF text-layer completion.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 20, 2026 08:49
@cxxxxxn

Copy link
Copy Markdown
Contributor Author

Unified search-contract fixes (50e33932)

This addresses the underlying contracts across the search chain, rather than only the individual review examples:

  • Query identity: preserve raw input, but share the trimmed query between requests, highlights and navigation. Invalidate old rows synchronously before debounce, including filter changes.
  • Lifecycle ownership: register individual renderer owners. StrictMode replay preserves cold handoffs; disposal of an older same-node renderer cannot erase a newer request. Actual final disposal still closes abandoned sessions.
  • Source addressing: retain raw-source results and their ordering, but navigate with a source SHA-256 identity and original UTF-16 span. Reuse the shared Markdown parser and Milkdown/ProseMirror model to map the exact visible position, without changing editor selection. Conversation source positions never fall through to an unrelated later user message. The same literal matcher handles server, DOM and PDF offsets, including Unicode case-fold expansion.
  • Actual visibility: scroll the selected Range through nested clipping/scrolling ancestors and acknowledge only a laid-out, visible target. PDF adapters wait for text-layer completion and map indexed positions across spans; explicit PDF whitespace is not doubled. Selected highlights refresh after text-layer replacement and remain bounded.

Validation

  • Frontend: 586 tests across 36 files passed.
  • Server: 39 tests across 2 files passed.
  • Shared parser/matcher: 41 tests across 2 files passed.
  • Repository pnpm typecheck, pnpm format, pnpm lint:fix, Web production build, i18n parity and whitespace checks passed.
  • Real-browser acceptance: cold StrictMode Note and PDF handoffs; padded queries; a 3,070px paragraph with the actual match inside the 300px panel; visible paragraph after a hidden URL; collapsed historical assistant Markdown with code and a later unrelated user match; PDF source-ordinal correction and a phrase spanning multiple PDF text-layer nodes; and Note 501/600 with exactly 500 painted ranges.

Explicit boundaries

Hidden source URLs/syntax remain searchable but report unavailable instead of jumping elsewhere or forcing source mode. Changed or ambiguous sources and unsupported rendering transformations also fail closed with localized feedback. Office reuses the tested Milkdown registration/model path; sandboxed Web/media have no exact-source bridge. Dedicated real-browser Office/Web/media acceptance was not performed in this pass.

The architecture documentation is updated. The new commit is pushed; its remote CI is separate from the previously green head.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved search matching, highlighting, focus, and navigation lifecycle defects affect supported usage.

6 open issues

Review effort: Balanced

: inheritedAttributes),
...(ariaLabel ? { 'aria-label': ariaLabel } : {}),
'data-link-activation': linkActivation,
'data-preview-search-content': '',

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Original sibling-exclusion problem fixed in 80e81cf. The global getSearchRoots narrowing was removed; isSearchableTextNode scopes document/chrome exclusion only to the closest data-search-renderer. Larger Canvas/conversation roots retain sibling surfaces. Mixed Canvas and conversation regressions re-pass; prior actual Milkdown browser fixture painted all five title/Text/assistant/prompt/quote matches and zero Task List chrome matches. Re-ran these tests on current head; full Web suite is 4,091 passing.

Comment thread apps/web/src/components/Nodes/pdf/PDFPreview.tsx Outdated
Comment on lines +195 to +197
return scheduleScrollToMatch(() => scopeEl, query, occurrenceIndex, {
source: navigationRequest.source,
fallbackToFirst: false,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Original missing raw-editor bridge fixed in 80e81cf. RawMarkdownEditor now registers unprojected source and the existing indexed adapter, resolves source identity + original offsets, scrolls CodeMirror to render off-viewport content, and acknowledges only the verified visible Range. Re-ran editable/read-only exact raw-URL tests: selection, content and external focus unchanged. Prior real-browser verification: 1,205 lines / only 34 rendered, exact source offset 19,321, 2/3 counter, Range inside the 300px panel. 0dbb244 separately closes cancellation of manual jumps using that async bridge.

Comment thread apps/web/src/hooks/useSearchSource.ts Outdated
if (index < 0) return;
const start = offsets?.[index] ?? index;
const end = offsets?.[index + needle.length] ?? index + needle.length;
if (text.slice(start, end).toLowerCase() === needle) yield { start, end };

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original isolated-slice contextual-lowercase problem was fixed in 80e81cf: both matching and resolveSourceRange use whole-source semantics. Reverified ΟΣ / ς -> original span 1–2 and İ ΟΣ / ς -> 3–4, plus the source-resolution regression. The follow-up rejected-boundary stepping example is a distinct real bug, now fixed in 0dbb244: rejected candidates advance one folded unit; accepted candidates keep full-length non-overlap. Added İİi / i\u0307i -> 1–3 and repeated-instance coverage. Full shared suite: 759 passed.

Reuse the canonical match schema across Canvas and Space queries. Preserve sibling search surfaces and stable streamed-result focus. Support raw CodeMirror source navigation, ordinary-HTTP SHA-256, contextual Unicode matching, and terminal PDF failures with registration-owned completion.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 20, 2026 09:46
@cxxxxxn

Copy link
Copy Markdown
Contributor Author

Regression fixes: 80e81cf6

Addressed the six current review findings plus the confirmed CI contract failure:

  1. CI / Space SEARCH: reuse canvasSearchMatchSchema.strict() instead of maintaining a duplicate response shape. The real RFS route test now asserts source hash, original offset and length, with additional strict-schema rejection coverage.
  2. Mixed search surfaces: document boundaries are local to each renderer. Milkdown chrome stays excluded while sibling titles, Text nodes, user prompts and quoted comments remain searchable.
  3. PDF failure lifecycle: terminal load/source errors settle the handoff with localized feedback; adapter-readiness waits are bounded. Failures belong to their source, and retired adapter registrations cannot process or complete replacement requests. A regression verifies that a late old-source error cannot consume a replacement-source handoff.
  4. Streaming focus: active selection uses the stable row key, not the current array index. Appending results or regrouping fields preserves keyboard ownership; effective query, scope and filter changes reset selection.
  5. Raw Markdown: reuse the existing indexed preview-adapter contract with CodeMirror document offsets and scroll effects. Full-document matching works beyond the rendered viewport, including visible raw link URLs, without editing, changing selection, focusing the editor or switching modes.
  6. Ordinary HTTP: retain Web Crypto when available and lazy-load the declared @noble/hashes SHA-256 fallback otherwise. Fallback digests are checked against Node SHA-256 for empty, ASCII and Unicode input.
  7. Contextual Unicode: validate whole-source lowercase offset boundaries rather than re-lowercasing isolated slices. Both scanning and source resolution cover final Greek sigma and reject partial lowercase expansions.

Validation

  • Whole-workspace run: 8,001 passing tests, 2 failing tests. Web: 4,084 / 319 files; shared: 757 / 61 files; server: 2,647 passing, plus the two failures below. Other workspace test packages passed.
  • The original CI failure in rfs.route.test.ts is fixed.
  • The remaining local failures are the runtime-bundle daemon-help assertion and native filesystem-watcher directory-recreation assertion. Both were reproduced with the same assertions in a temporary pre-refactor aa5a120d checkout using existing dependencies; the watcher is intermittent there. These are not claimed as passing, and no unrelated implementation was changed. The baseline checkout has been removed.
  • Root format, lint (zero errors; existing warnings remain), and typecheck passed. Latest Web production build, i18n parity, license headers and whitespace checks passed.
  • Actual component-browser acceptance on authenticated, non-secure HTTP (crypto.subtle absent): CodeMirror navigated to exact original offset 19,321 in a 1,205-line document with only 34 lines rendered, showing 2/3, unchanged selection at 0, and the Range inside the 300px panel. Mixed real Milkdown/sibling surfaces produced all 5 expected highlights and no menu match. A real invalid PDF settled at 0/0 with the localized notification. Actual Virtuoso field regrouping moved the selected row 4 → 5 while preserving the same focused DOM element.

Architecture docs and focused regressions are updated. Temporary fixtures, HTTP credentials/header overrides and servers are cleaned up; the existing user dev service is preserved. The pushed commit needs its own remote CI result; prior green checks are not evidence for this head.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Search correctness and asynchronous navigation cleanup issues remain unresolved.

4 open issues
1 previously missed issue

In code that hasn't changed since last review

Medium severity Advance matcher by one unit after rejected boundary

packages/​shared/​src/​utils/​text-search.ts:35

A rejected case-fold boundary still advances by the full query length, skipping a later valid match. For example, literalTextMatches('İİi', 'i\u0307i') returns no results, although the second İ plus i matches at source offsets 1–3. This affects every scanner using the shared matcher. Advance by one folded UTF-16 unit after rejecting a candidate; keep the full-length advance for accepted, non-overlapping matches. Add this example to the matcher tests.

3 resolved since last review
1 file not reviewed
  • pnpm-lock.yaml: Generated file

Review effort: Balanced

Comment thread apps/web/src/components/Panels/ExpandedNodePanel/InPreviewSearchBar.tsx Outdated
Own manual adapter cancellation across replacement, session changes and disposal, and reject stale completions. Advance rejected Unicode expansion candidates one folded unit without changing accepted-match non-overlap.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 21, 2026 02:23
@cxxxxxn

Copy link
Copy Markdown
Contributor Author

Confirmed follow-up fixes (0dbb2448)

  • Manual Next/Previous now owns adapter cancellation, cancels old work on replacement, query/session/owner changes, adapter changes and disposal, and ignores late callbacks unless the navigation identity, active query/session sequence, renderer owner and adapter registration still match. Seven delayed-completion regressions cover query changes, close, same-query reopen, new handoffs, adapter replacement, unmount and a superseding jump.
  • The shared matcher advances only one folded UTF-16 unit after rejecting an expansion-boundary candidate. Accepted matches remain non-overlapping. The review example İİi / i\u0307i now yields source span 1–3; repeated valid instances are also covered.
  • Rechecked the three retained original findings (contextual lowercase, raw Markdown bridge, and mixed sibling search). Their existing fixes and tests remain valid; evidence is now attached directly to each review thread rather than only the PR summary.

Validation on this change: 4,091 Web tests / 319 files, 759 shared tests / 61 files, and 96 server search/RFS tests / 2 files, all passing. Root format, lint (zero errors; existing warnings), typecheck, Web production build, i18n, headers and diff checks passed. No new dependencies. Architecture documentation updated.

This pass did not repeat real-browser acceptance or the full unrelated server suites; previous real-browser results are explicitly identified as prior evidence in the thread replies. New-head remote CI must still finish before claiming a green handoff.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Cross-renderer focus ownership and asynchronous history navigation warrant final human review, particularly without real-backend end-to-end validation.

3 open issues
1 resolved since last review
1 file not reviewed
  • pnpm-lock.yaml: Generated file

Review effort: Balanced

@cxxxxxn
cxxxxxn (cxxxxxn) merged commit c008f19 into microsoft:main Oct 21, 2026
4 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.

2 participants