From 3f2b2a722effa62e26bf6436700e7a40ce64b33d Mon Sep 17 00:00:00 2001 From: Stephen Oni Date: Wed, 16 Aug 2023 19:40:00 +0100 Subject: [PATCH] change the score function to prevent drag (#325) * change the score function to prevent drag * pass fuse variable to useInternalMatches * change minMatchCharLength to 1. to match single character * remove command-score --- package-lock.json | 29 ++++++++++++++++------------- package.json | 4 ++-- src/useMatches.tsx | 46 ++++++++++++++++++++++++++++++++-------------- 3 files changed, 50 insertions(+), 29 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9b77c1c..73c63e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17 +1,17 @@ { "name": "kbar", - "version": "0.1.0-beta.40", + "version": "0.1.0-beta.41", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "kbar", - "version": "0.1.0-beta.40", + "version": "0.1.0-beta.41", "license": "MIT", "dependencies": { "@radix-ui/react-portal": "^1.0.1", - "command-score": "^0.1.2", "fast-equals": "^2.0.3", + "fuse.js": "^6.6.2", "react-virtual": "^2.8.2", "tiny-invariant": "^1.2.0" }, @@ -3860,11 +3860,6 @@ "node": ">= 0.8" } }, - "node_modules/command-score": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/command-score/-/command-score-0.1.2.tgz", - "integrity": "sha1-uYatfowL66F1UqVmNsRK44Nj04E=" - }, "node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -6126,6 +6121,14 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "node_modules/fuse.js": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-6.6.2.tgz", + "integrity": "sha512-cJaJkxCCxC8qIIcPBF9yGxY0W/tVZS3uEISDxhYIdtk8OL93pe+6Zj7LjCqVV4dzbqcriOZ+kQ/NE4RXZHsIGA==", + "engines": { + "node": ">=10" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -17161,11 +17164,6 @@ "delayed-stream": "~1.0.0" } }, - "command-score": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/command-score/-/command-score-0.1.2.tgz", - "integrity": "sha1-uYatfowL66F1UqVmNsRK44Nj04E=" - }, "commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -18932,6 +18930,11 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "fuse.js": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-6.6.2.tgz", + "integrity": "sha512-cJaJkxCCxC8qIIcPBF9yGxY0W/tVZS3uEISDxhYIdtk8OL93pe+6Zj7LjCqVV4dzbqcriOZ+kQ/NE4RXZHsIGA==" + }, "gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", diff --git a/package.json b/package.json index 4d33439..e35df5f 100644 --- a/package.json +++ b/package.json @@ -58,8 +58,8 @@ }, "dependencies": { "@radix-ui/react-portal": "^1.0.1", - "command-score": "^0.1.2", "fast-equals": "^2.0.3", + "fuse.js": "^6.6.2", "react-virtual": "^2.8.2", "tiny-invariant": "^1.2.0" }, @@ -77,4 +77,4 @@ "type": "git", "url": "https://github.com/timc1/kbar.git" } -} +} \ No newline at end of file diff --git a/src/useMatches.tsx b/src/useMatches.tsx index abf070f..16f7fb8 100644 --- a/src/useMatches.tsx +++ b/src/useMatches.tsx @@ -2,7 +2,7 @@ import * as React from "react"; import type { ActionImpl } from "./action/ActionImpl"; import { useKBar } from "./useKBar"; import { Priority, useThrottledValue } from "./utils"; -import commandScore from "command-score"; +import Fuse from "fuse.js"; export const NO_GROUP = { name: "none", @@ -74,7 +74,29 @@ export function useMatches() { return getDeepResults(rootResults); }, [getDeepResults, rootResults, emptySearch]); - const matches = useInternalMatches(filtered, search); + const fuseOptions = { + keys: [{ + name: "name", + weight: 0.5 + }, + { + name: "keywords", + getFn: (item) => item.keywords.split(","), // make keyword an array. so fuse can look through words individually + weight: 0.5 + }, + "subtitle"], + includeScore: true, + includeMatches: true, + threshold: 0.2, + minMatchCharLength: 1, + tokenize: (str) => { + // Example: Preserve hyphens and special characters as separate tokens + return str.split(/[\s\-,.!()]+/).filter(Boolean); + }, + }; + const fuse = new Fuse(filtered, fuseOptions); + + const matches = useInternalMatches(filtered, search, fuse); const results = React.useMemo(() => { /** @@ -168,7 +190,7 @@ type Match = { score: number; }; -function useInternalMatches(filtered: ActionImpl[], search: string) { +function useInternalMatches(filtered: ActionImpl[], search: string, fuse: Fuse) { const value = React.useMemo( () => ({ filtered, @@ -186,17 +208,13 @@ function useInternalMatches(filtered: ActionImpl[], search: string) { } let matches: Match[] = []; - - for (let i = 0; i < throttledFiltered.length; i++) { - const action = throttledFiltered[i]; - const score = commandScore( - [action.name, action.keywords, action.subtitle].join(" "), - throttledSearch - ); - if (score > 0) { - matches.push({ score, action }); - } - } + // Use Fuse's `search` method to perform the search efficiently + const searchResults = fuse.search(throttledSearch); + // Format the search results to match the existing structure + matches = searchResults.map(({ item: action, score }) => ({ + score: 1 / (score + 1), // Convert the Fuse score to the format used in the original code + action, + })); return matches; }, [throttledFiltered, throttledSearch]) as Match[];