Let the search reach past the twentieth of a kind, and unbreak two specs

The global search capped each category before cmdk had matched anything, so
nothing past the twentieth node or widget could be found at all — this
instance has 28 nodes and 97 widgets. The cap now trims the candidates the
query could reach rather than the raw index.

The admin teardown asked /users/ for limit=1000, which the route stopped
accepting when its bounds went in; a 422 body has no `data` to iterate, so the
hook threw and took the tests it was attributed to with it. It asks for the
500 the route allows.

And `submit` folds every declared initial into a run's params, so an input the
run never passed can no longer read as the flow's own. That assertion is gone;
what the panel does show — the value the run actually started from, passed or
not — is what the test checks.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CL9zvnnvcp1mvA8o7impxk
This commit is contained in:
2026-09-06 14:46:59 +02:00
co-authored by Claude Opus 5
parent 8398a87ebd
commit 062a2aac60
3 changed files with 45 additions and 12 deletions
@@ -31,9 +31,11 @@ export const searchQueryOptions = () => ({
})
/** The categories, in the order they are offered, with what to draw each as. */
//: The most entries one group shows. cmdk scores what is mounted, so this is
//: a cap on what is offered rather than on what is searched — and twenty of a
//: kind is already more than anybody reads before typing another letter.
//: The most entries one group shows. cmdk scores what is mounted, so the cap
//: has to come after the query has had its say, never before: capping the raw
//: index instead put everything past the twentieth node or widget beyond reach
//: of the search altogether. Twenty of what matches is already more than
//: anybody reads before typing another letter.
const GROUP_CAP = 20
const GROUPS: {
@@ -57,6 +59,31 @@ function hint(entry: SearchEntry): string {
return [entry.parent, entry.kind].filter(Boolean).join(" · ")
}
/** Everything about an entry that is worth matching against. */
function searchValue(entry: SearchEntry): string {
return `${entry.name} ${entry.title ?? ""} ${entry.parent ?? ""} ${entry.kind ?? ""}`
}
/** cmdk lowercases and flattens whitespace and hyphens before it scores. */
function normalise(text: string): string {
return text.toLowerCase().replace(/[\s-]/g, " ")
}
/**
* Whether cmdk could score this entry at all: it needs the query's characters
* in the value, in order. Asking the cheap half of that question here is what
* lets GROUP_CAP cap the candidates rather than the index.
*/
function couldMatch(value: string, query: string): boolean {
const text = normalise(value)
let from = 0
for (const char of normalise(query)) {
from = text.indexOf(char, from) + 1
if (from === 0) return false
}
return true
}
/**
* Everything in this instance, by name, from anywhere.
*
@@ -85,12 +112,16 @@ export function GlobalSearch({
if (!open) return null
const entries = data ?? []
const typing = query.trim().length > 0
const typed = query.trim()
const typing = typed.length > 0
// One pass over the index rather than one per group — nine passes over
// every entry on each keystroke. Not memoised: the component returns early
// while closed, so a hook cannot go here, and one pass is already the win.
// The same pass drops what the query cannot reach, so each bucket is already
// candidates by the time GROUP_CAP trims it.
const byCategory = new Map<string, SearchEntry[]>()
for (const entry of entries) {
if (!couldMatch(searchValue(entry), typed)) continue
const bucket = byCategory.get(entry.category)
if (bucket) bucket.push(entry)
else byCategory.set(entry.category, [entry])
@@ -167,7 +198,7 @@ export function GlobalSearch({
{found.map((entry) => (
<CommandItem
key={`${category}:${entry.parent ?? ""}:${entry.name}`}
value={`${entry.name} ${entry.title ?? ""} ${entry.parent ?? ""} ${entry.kind ?? ""}`}
value={searchValue(entry)}
onSelect={() => go(entry)}
className="min-h-11 md:min-h-8"
>