Skip to content

Stabilize Algolia search feedback#1069

Closed
jottakka wants to merge 3 commits into
mainfrom
split/improve-algolia-search-feedback
Closed

Stabilize Algolia search feedback#1069
jottakka wants to merge 3 commits into
mainfrom
split/improve-algolia-search-feedback

Conversation

@jottakka

@jottakka jottakka commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Summary

  • debounce interactive Algolia queries
  • avoid rendering stale hits and literal highlight markers
  • show clear loading, empty, and error states

Test plan

  • pnpm exec vitest run tests/algolia-search.test.ts

Note

Low Risk
Docs-only UI and client-side Algolia behavior; no auth, data, or API surface changes beyond search UX.

Overview
Improves the docs Algolia modal so typing, loading, and results stay in sync and highlights render correctly.

Debounced queries (150ms via SearchBox queryHook and scheduleSearch) cut redundant Algolia requests while typing; clearing the input still searches immediately.

Unified result UI replaces separate empty/no-results components with SearchResults, which shows a start-typing prompt, Searching… until searchResultsAreCurrent matches the typed query and status is idle, zero-hit messaging, or hits—and an alert on search error (catchError + getSearchErrorMessage).

Configure is centralized in ALGOLIA_SEARCH_CONFIG, including React InstantSearch highlight tags (__ais-highlight__) so snippets don’t show raw markers. Modal markup moves into SearchContent with timer cleanup on unmount.

Adds Vitest coverage for debounce, highlight config, stale-result guard, and error copy.

Reviewed by Cursor Bugbot for commit 5cf7f8b. Bugbot is set up for automated code reviews on this repo. Configure here.

@vercel

vercel Bot commented Jul 16, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview, Comment Jul 16, 2026 8:20pm

Request Review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 5cf7f8b. Configure here.

>
{errorMessage}
</p>
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale error during debounced typing

Medium Severity

After a search fails, the SearchResults component can display a stale error message. The typedQuery updates immediately, but the debounced search() call (which would update the search status) is delayed. This causes the error message to persist for a new query, even as the user continues typing.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 5cf7f8b. Configure here.

}
if (!query.trim()) {
search(query);
return null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Whitespace skips clear search

Low Severity

scheduleSearch treats whitespace-only input as empty via trim, but still calls search with the raw spaces instead of clearing to an empty query. SearchResults meanwhile shows the empty prompt for that input, so InstantSearch can run a whitespace query the UI never reflects.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 5cf7f8b. Configure here.

jottakka and others added 3 commits July 16, 2026 17:18
Debounce queries and suppress stale result renders so the search dialog reports loading, empty, and error states without flicker or literal highlight tags.

Co-authored-by: Cursor <cursoragent@cursor.com>
Only render a search failure when it belongs to the query currently shown in the dialog.

Co-authored-by: Cursor <cursoragent@cursor.com>
Track the dispatched query independently so current failures stay visible while stale failures disappear for new input.

Co-authored-by: Cursor <cursoragent@cursor.com>

@teallarson teallarson 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.

Code review — 4 findings across correctness, efficiency, and reuse. Ranked highest-severity first.

const normalizedQuery = query.trim();
return (
normalizedQuery.length > 0 &&
status === "idle" &&

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.

Bug: status === "stalled" hides valid cached results

React InstantSearch sets status to "stalled" when a request exceeds stalledSearchDelay (default 200ms). At that point results still holds the previous matching response — results.query equals typedQuery and results.nbHits > 0 — but searchResultsAreCurrent returns false because it gates on status === "idle" strictly. The caller falls through to the "Searching…" placeholder, replacing valid results with a loading message for the entire stall duration.

Suggested fix:

return (
  normalizedQuery.length > 0 &&
  (status === "idle" || status === "stalled") &&
  resultsQuery.trim() === normalizedQuery
);

query: string;
search: (nextQuery: string) => void;
setTypedQuery: (nextQuery: string) => void;
setDispatchedQuery?: (nextQuery: string) => void;

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.

Misleading optional on a consequence-bearing parameter

setDispatchedQuery is typed as optional (?), so external callers of the exported scheduleSearch can legally omit it. When omitted, dispatchedQuery stays at "", making searchErrorIsCurrent always return false — Algolia errors are silently swallowed and the UI shows an indefinite loading state.

Since this parameter is mandatory for correct error display, consider removing the ? or adding a JSDoc warning.

}
if (!query.trim()) {
setDispatchedQuery?.(query);
search(query);

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.

Unnecessary Algolia request on every input clear

The empty-query fast path calls search(query) (i.e. search("")) immediately without debouncing. SearchResults then discards the response via its !query.trim() early return. This fires a real Algolia API round-trip on every clear, consuming operations quota for a response that is always thrown away.

delayMs?: number;
};

export function scheduleSearch({

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.

scheduleSearch duplicates useDebounce from @uidotdev/usehooks (already a dep)

@uidotdev/usehooks v2.4.1 is already in the dep tree (used in app/en/resources/integrations/components/use-toolkit-filters.ts). scheduleSearch + the useRef<setTimeout> block in SearchContent re-implements the same debounce logic, creating two divergent patterns to maintain.

Replacing with the existing hook would remove the manual timer management, eliminate the exported scheduleSearch, and keep both debounced flows in the codebase consistent.

@jottakka jottakka closed this Jul 16, 2026
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