Skip to content

fix(vscode): optimize model selector search and auto-jump to active match - #12810

Merged
marius-kilocode merged 5 commits into
mainfrom
optimize-model-selector-search-speed
Aug 3, 2026
Merged

fix(vscode): optimize model selector search and auto-jump to active match#12810
marius-kilocode merged 5 commits into
mainfrom
optimize-model-selector-search-speed

Conversation

@marius-kilocode

Copy link
Copy Markdown
Collaborator

Model search now updates instantly on every keystroke and keeps the active match visible while typing.

Problem
The model selector had a 250 ms debounce that made every search feel sluggish. After removing the debounce, a second bug surfaced: when the virtualized list was scrolled far from the active match, filtering would mount the selected row above the viewport without scrolling it into view.

Changes

  • Remove the 250 ms search debounce in ModelSelector.tsx. Filtering, grouping, favorites, and keyboard selection now update directly from each keystroke.
  • Add a latest-search-only requestAnimationFrame scroll that keeps the active match visible when filtering from a deeply scrolled list.
  • Track pointer coordinates on mousemove so rows moving beneath a stationary pointer (from virtualization or programmatic scroll) no longer steal selection, while real pointer movement still immediately transfers selection to the hovered model.
  • Remove the layout-sensitive onMouseEnter selection path that caused hover to hijack search results when content moved under a still cursor.

Performance

  • Before: 260 ms input-to-visible-results.
  • After: 18 ms, settling on the next animation frame (93% reduction).
  • Incremental typing: 3-5 ms synchronous work per keystroke.
  • Auto-jump from deep scroll: 9 ms, zero long tasks.

Validation

  • Full ModelSelector browser suite: 16 passed.
  • Auto-jump regression repeated three times: 3 passed.
  • 89 focused unit tests passed.
  • Typecheck, ESLint, formatting, and webview build passed.

…atch

Remove the 250 ms search debounce so the model list updates on every
keystroke. Add a latest-search-only animation-frame scroll that keeps
the active match visible when filtering from a deeply scrolled list,
and track pointer coordinates so rows moving beneath a stationary
pointer no longer steal selection while real hover still transfers
immediately.
@marius-kilocode
marius-kilocode enabled auto-merge (squash) August 3, 2026 12:22
setPreviewKey(next)
if (!open()) return
if (scrollFrame !== undefined) cancelAnimationFrame(scrollFrame)
scrollFrame = requestAnimationFrame(() => {

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.

SUGGESTION: The auto-scroll is keyed to filtered(), not to a search change

This effect re-runs on every filtered() recompute, and filtered() depends on visibleModels(), which returns a fresh array whenever models() or connected() changes (and whenever a parent passes a new props.models identity). So a background provider/model refresh while the popover is open will now also scroll the list back to the active/first row, throwing away wherever the user had scrolled to — previously that recompute only reset selection, with no visible jump.

Since the intent is "latest search only", consider keeping the previous search text in a closure variable and scheduling the frame only when search() actually changed.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

}

function pointerMove(e: MouseEvent, key: string) {
const moved = pointerX === undefined || pointerY === undefined || e.clientX !== pointerX || e.clientY !== pointerY

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.

SUGGESTION: The first mousemove a row sees is always treated as real movement

pointerX === undefined makes moved true, so the first mousemove delivered to any row steals selection even when the pointer never moved. That is exactly the case this guard is meant to protect: if the cursor is resting over the list region but no row has received a mousemove yet (popover opened through the openModelPicker shortcut, or the virtualized rows mount under a stationary cursor), the synthetic mousemove the browser dispatches after the auto-jump scroll passes the check and overrides the first search match.

Recording the baseline coordinates without selecting on the very first event, or tracking coordinates from the list container instead of per row, would close that gap. Real hover always produces follow-up moves, so behavior for genuine pointer movement is unchanged.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

@kilo-code-bot

kilo-code-bot Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 1
Issue Details (click to expand)

SUGGESTION

File Line Issue
packages/kilo-vscode/webview-ui/src/components/shared/ModelSelector.tsx 567 Delegated pointerMove returns before recording pointerX/pointerY when the event target is not a treeitem (star button, row/list padding), so stale coordinates can make a stationary cursor look like it moved and steal keyboard selection after a virtualized scroll
Files Reviewed (2 files)
  • packages/kilo-vscode/webview-ui/src/components/shared/ModelSelector.tsx - 1 issue
  • packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/agentmanager/full-screen-diff-with-changes-chromium-linux.png - 0 issues (CI-generated baseline, not reviewed)
Notes
  • Incremental review: since ca14e42 the only change to PR-owned files is a regenerated visual-regression baseline (plus a merge from main); ModelSelector.tsx, the changeset, and the accessibility spec are unchanged.
  • The line-567 pointer-coordinate finding was re-verified against current HEAD (bd2aedb) and still reproduces: pointerMove returns before updating pointerX/pointerY when the target is not inside a treeitem.
  • The two earlier findings remain resolved: the auto-scroll is gated on searchChanged (line 474), and moved no longer treats an undefined pointer position as movement (line 568).

Fix these issues in Kilo Cloud

Previous Review Summaries (2 snapshots, latest commit ca14e42)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit ca14e42)

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 1
Issue Details (click to expand)

SUGGESTION

File Line Issue
packages/kilo-vscode/webview-ui/src/components/shared/ModelSelector.tsx 567 Delegated pointerMove returns before recording pointerX/pointerY when the event target is not a treeitem (star button, row/list padding), so stale coordinates can make a stationary cursor look like it moved and steal keyboard selection after a virtualized scroll
Files Reviewed (2 files)
  • packages/kilo-vscode/webview-ui/src/components/shared/ModelSelector.tsx - 1 issue
  • packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/agentmanager/full-screen-diff-with-changes-chromium-linux.png - 0 issues (CI-generated baseline, not reviewed)
Notes
  • Both previous findings are resolved: the auto-scroll is now gated on searchChanged so background models()/props.models refreshes no longer yank the list, and moved no longer treats an undefined pointer position as movement.
  • Moving mousemove to a single delegated handler on the list container reduces per-row listeners; scrollFrame is still cancelled on close and in onCleanup, so no new leaked frames or listeners.
  • Head moved from ca14e42 to f9e0119 mid-review; the only added change was a regenerated visual-regression baseline, which is out of review scope.

Fix these issues in Kilo Cloud

Previous review (commit 6c31e56)

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 2
Issue Details (click to expand)

SUGGESTION

File Line Issue
packages/kilo-vscode/webview-ui/src/components/shared/ModelSelector.tsx 472 Auto-scroll frame is keyed to any filtered() recompute, so a background models()/connected()/props.models refresh while the popover is open now yanks the list back to the active row
packages/kilo-vscode/webview-ui/src/components/shared/ModelSelector.tsx 559 pointerX === undefined counts as pointer movement, so the first mousemove a row receives can still steal selection from the first search match under a stationary cursor
Files Reviewed (3 files)
  • packages/kilo-vscode/webview-ui/src/components/shared/ModelSelector.tsx - 2 issues
  • packages/kilo-vscode/tests/model-selector-accessibility.spec.ts - 0 issues
  • .changeset/quick-model-search.md - 0 issues
Notes
  • Memory/leak check: the new scrollFrame handle is cancelled both on popover close and in onCleanup, and the per-row onMouseMove handlers are removed with their rows, so no new leaked frames or listeners.
  • Changeset is present and user-facing.
  • Assertions of the form toHaveAttribute("aria-activedescendant", await locator.getAttribute("id")) follow the existing pattern in this spec, so they were not flagged.

Fix these issues in Kilo Cloud


Reviewed by kimi-k3 · Input: 43K · Output: 5.4K · Cached: 244.7K

Review guidance: REVIEW.md from base branch main

if (!(target instanceof Element)) return
const item = target.closest<HTMLElement>('[role="treeitem"][data-key]')
const key = item?.dataset.key
if (!key) return

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.

SUGGESTION: Pointer coordinates are only recorded when the event lands on a treeitem, so a stationary cursor can still be seen as "moved"

Now that the handler is delegated to the whole list, it receives mousemove for regions that are not tree items (the star button, which is a sibling of the treeitem div, and the row/list padding). Those events hit this early return before pointerX/pointerY are updated, so the last recorded position can be from a different part of the list than where the cursor actually rests.

Concrete case: the cursor sits over a row's star button while pointerX/Y still hold the coordinates of the item cell it passed through. Keyboard navigation (setPointer(false) + scrollToIndex) then shifts the list, Chrome replays a synthetic mousemove at the unchanged cursor position, and if a group label (no star button) is now under that x/y, moved evaluates to true because the stale coordinates differ. That flips pointer() back to true and reassigns selectedKey, which is the exact hijack this change is meant to prevent.

Recording the position unconditionally before the key check keeps the stationary-pointer invariant intact regardless of what the cursor is over:

function pointerMove(e: MouseEvent) {
  const moved = pointerX !== undefined && pointerY !== undefined && (e.clientX !== pointerX || e.clientY !== pointerY)
  pointerX = e.clientX
  pointerY = e.clientY
  const target = e.target
  if (!moved || !(target instanceof Element)) return
  const key = target.closest<HTMLElement>('[role="treeitem"][data-key]')?.dataset.key
  if (!key) return
  setPointer(true)
  setSelectedKey(key)
}

Reply with @kilocode-bot fix it to have Kilo Code address this issue.

@marius-kilocode
marius-kilocode merged commit e04f653 into main Aug 3, 2026
26 checks passed
@marius-kilocode
marius-kilocode deleted the optimize-model-selector-search-speed branch August 3, 2026 14:57
t7tran pushed a commit to t7tran/kilocode that referenced this pull request Aug 14, 2026
…atch (Kilo-Org#12810)

* fix(vscode): optimize model selector search and auto-jump to active match

Remove the 250 ms search debounce so the model list updates on every
keystroke. Add a latest-search-only animation-frame scroll that keeps
the active match visible when filtering from a deeply scrolled list,
and track pointer coordinates so rows moving beneath a stationary
pointer no longer steal selection while real hover still transfers
immediately.

* fix(vscode): stabilize model selector search navigation

* chore: update kilo-vscode visual regression baselines

* chore: update kilo-vscode visual regression baselines

---------

Co-authored-by: kilo-maintainer[bot] <kilo-maintainer[bot]@users.noreply.github.com>
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