Measure refresh latency independently and retain intended result selection

This commit is contained in:
karl committed 2026-10-13 06:50:38 +02:00
1 parent 373ed1ed49
commit 8b993b23d7
7 files changed
+188 -11

No files matched your search

+19 -9
View File
@@ -25,6 +25,7 @@ import {
} from "./search-performance";
import type { MeasuredResponse, SearchLane } from "./search-performance";
import { displayPath } from "./path-display";
import { ResultSelection } from "./selection-state";
import {
AiRevision,
aiConfigurationProblem,
@@ -146,6 +147,7 @@ let displayedLiteralQuery = literalQuery("");
let displayedCodeScope = false;
let explainedSelection = "";
let selected = -1;
const resultSelection = new ResultSelection();
let requestId = 0;
let timer: ReturnType<typeof setTimeout>;
let indexingActive = false;
@@ -700,10 +702,11 @@ element("ai-citation").addEventListener("click", () => {
block: "nearest",
});
});
function select(index: number, focus = false) {
function select(index: number, focus = false, userChoice = true) {
if (!results.length) return;
selected = Math.max(0, Math.min(index, results.length - 1));
const result = results[selected];
if (userChoice) resultSelection.choose(result.path);
list.querySelectorAll<HTMLButtonElement>("button").forEach((button, i) => {
button.classList.toggle("selected", i === selected);
button.setAttribute("aria-current", String(i === selected));
@@ -923,7 +926,6 @@ function applyResults(
)
return;
const renderStarted = performance.now();
const selectedPath = results[selected]?.path;
const resultsHadFocus = list.contains(document.activeElement);
shownLane = lane;
displayedRequestId = request.id;
@@ -932,9 +934,10 @@ function applyResults(
results = found;
selected = -1;
render();
const preserved = results.findIndex((result) => result.path === selectedPath);
if (preserved >= 0) select(preserved, resultsHadFocus);
else if (results.length) select(0, resultsHadFocus);
const preserved = resultSelection.resolve(
results.map((result) => result.path),
);
if (preserved >= 0) select(preserved, resultsHadFocus, false);
if (!found.length) refreshEmptySearchState();
request.measurement.displayed(
lane,
@@ -1064,14 +1067,18 @@ function refreshIndexedSearch() {
query.value.trim() !== previous.query
)
return;
const refreshStarted = performance.now();
clearTimeout(hybridTimer);
lexicalLane.clear();
hybridLane.clear();
querySemanticFailed = false;
shownLane = "lexical";
// Keep the original input/first-response timing while rejecting responses
// captured before the index changed, even when the query text stays identical.
const refreshed: SearchRequest = { ...previous, id: ++requestId };
const refreshed: SearchRequest = {
...previous,
id: ++requestId,
started: refreshStarted,
measurement: previous.measurement.freshForRefresh(refreshStarted),
};
latestSearch = refreshed;
submitSearch(refreshed, "lexical");
if (canSearchSemantically()) {
@@ -1126,6 +1133,7 @@ async function search(inputAt = performance.now()) {
shownLane = "lexical";
const value = query.value.trim();
if (!value) {
resultSelection.begin("");
element("performance-panel").hidden = true;
results = [];
displayedLiteralQuery = literalQuery("");
@@ -1140,10 +1148,12 @@ async function search(inputAt = performance.now()) {
return;
}
try {
const filters = captureFilters();
resultSelection.begin(JSON.stringify([value, filters]));
const request: SearchRequest = {
id: requestId,
query: value,
filters: captureFilters(),
filters,
started: performance.now(),
measurement: new SearchMeasurement(
inputAt,