From dc08fa132b95801cb580945dbed6eaaac1418e9e Mon Sep 17 00:00:00 2001 From: stroblme Date: Mon, 14 Sep 2026 14:40:17 +0200 Subject: [PATCH] Spin the Add remote user button; stop the search prefilter dropping typos "Add remote user" was the one call site swapping its label instead of using LoadingButton, so it alone had no spinner. Global search's couldMatch was a strict subsequence test while cmdk's scorer steps over one query character for a transposition, so a typo it would have scored never reached it. The prefilter now carries that one skip, which recovers every single-transposition query in a sweep of the index shapes and admits nothing cmdk scores zero. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01VfgHWr7AQDiLsTQVu7hpKB --- .../src/components/Common/GlobalSearch.tsx | 27 ++++++++++++++----- .../components/UserSettings/RemoteAccess.tsx | 10 ++++--- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/frontend/src/components/Common/GlobalSearch.tsx b/frontend/src/components/Common/GlobalSearch.tsx index 41d9c5a..f05874c 100644 --- a/frontend/src/components/Common/GlobalSearch.tsx +++ b/frontend/src/components/Common/GlobalSearch.tsx @@ -71,17 +71,32 @@ function normalise(text: string): string { /** * 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. + * in the value, in order, bar the one its scorer steps over for a transposed + * or mistyped letter. Asking the cheap half of that question here is what lets + * GROUP_CAP cap the candidates rather than the index. + * + * Plain subsequence was the wrong half to ask: cmdk still scores "dahsboard" + * against "dashboard", and this dropped it before anything could. One skip is + * where it stops, because the scorer allows another for each further tenth of + * the score, and a budget that follows it all the way down is no filter at all. */ function couldMatch(value: string, query: string): boolean { const text = normalise(value) - let from = 0 + // Where the rest of the query can resume from with the skip still in hand, + // and where with it spent. Infinity is a path that has died — `indexOf` past + // the end of the string answers -1 by itself. + let intact = 0 + let spent = Number.POSITIVE_INFINITY for (const char of normalise(query)) { - from = text.indexOf(char, from) + 1 - if (from === 0) return false + const resume = (from: number) => { + const at = text.indexOf(char, from) + return at < 0 ? Number.POSITIVE_INFINITY : at + 1 + } + // Skipping this character leaves the intact path where it already stood. + spent = Math.min(intact, resume(spent)) + intact = resume(intact) } - return true + return Math.min(intact, spent) < Number.POSITIVE_INFINITY } /** diff --git a/frontend/src/components/UserSettings/RemoteAccess.tsx b/frontend/src/components/UserSettings/RemoteAccess.tsx index 9c1ef26..1b4ec3f 100644 --- a/frontend/src/components/UserSettings/RemoteAccess.tsx +++ b/frontend/src/components/UserSettings/RemoteAccess.tsx @@ -21,6 +21,7 @@ import { } from "@/components/ui/dialog" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" +import { LoadingButton } from "@/components/ui/loading-button" import { Separator } from "@/components/ui/separator" import useCustomToast from "@/hooks/useCustomToast" import { handleError } from "@/utils" @@ -242,14 +243,15 @@ export function RemoteAccess() {

- + Add remote user +