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) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VfgHWr7AQDiLsTQVu7hpKB
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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() {
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<Button
|
||||
<LoadingButton
|
||||
className="rounded-full"
|
||||
variant="outline"
|
||||
disabled={!joinCode.trim() || addRemoteUser.isPending}
|
||||
loading={addRemoteUser.isPending}
|
||||
disabled={!joinCode.trim()}
|
||||
onClick={() => addRemoteUser.mutate()}
|
||||
>
|
||||
{addRemoteUser.isPending ? "Adding…" : "Add remote user"}
|
||||
</Button>
|
||||
Add remote user
|
||||
</LoadingButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user