diff --git a/extensions/nerdfont-search/package-lock.json b/extensions/nerdfont-search/package-lock.json index 95624067..315f76b8 100644 --- a/extensions/nerdfont-search/package-lock.json +++ b/extensions/nerdfont-search/package-lock.json @@ -912,7 +912,6 @@ "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", "hasInstallScript": true, "license": "MIT", - "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -2904,7 +2903,6 @@ "version": "4.0.3", "inBundle": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -3578,7 +3576,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -4208,7 +4205,6 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/extensions/nerdfont-search/src/filtering.ts b/extensions/nerdfont-search/src/filtering.ts index aebb6a98..a9e7a0d3 100644 --- a/extensions/nerdfont-search/src/filtering.ts +++ b/extensions/nerdfont-search/src/filtering.ts @@ -20,11 +20,15 @@ function filterIconIndex({ fuseInstance, searchText, selectedPack, + packIndex, }: { iconIndex: IconIndex[]; - fuseInstance: { search: (query: string) => Array<{ score?: number; item: IconIndex }> } | null; + fuseInstance: { + search: (query: string) => Array<{ score?: number; item: IconIndex }>; + } | null; searchText: string; selectedPack: string; + packIndex?: Map | null; }): IconIndex[] { if (iconIndex.length === 0) { return []; @@ -32,12 +36,16 @@ function filterIconIndex({ if (searchText.length < 3) { if (selectedPack === PACK_FILTER_ALL) { - return []; + return iconIndex.slice(0, SEARCH_RESULT_LIMIT); + } + + if (packIndex) { + const packIcons = packIndex.get(selectedPack); + return packIcons ? packIcons.slice(0, SEARCH_RESULT_LIMIT) : []; } return iconIndex .filter((icon) => icon.pack === selectedPack) - .sort((a, b) => a.displayName.localeCompare(b.displayName)) .slice(0, SEARCH_RESULT_LIMIT); } @@ -48,7 +56,9 @@ function filterIconIndex({ let searchResults = fuseInstance.search(searchText); if (selectedPack !== PACK_FILTER_ALL) { - searchResults = searchResults.filter((result) => result.item.pack === selectedPack); + searchResults = searchResults.filter( + (result) => result.item.pack === selectedPack, + ); } return sortFuseResultsByScoreThenId(searchResults) diff --git a/extensions/nerdfont-search/src/fuse-options.json b/extensions/nerdfont-search/src/fuse-options.json index 4f2a2cb1..63c4fa94 100644 --- a/extensions/nerdfont-search/src/fuse-options.json +++ b/extensions/nerdfont-search/src/fuse-options.json @@ -1,31 +1,31 @@ { - "keys": [ - { - "name": "displayName", - "weight": 0.3 - }, - { - "name": "id", - "weight": 0.5 - }, - { - "name": "searchTokens", - "weight": 0.8 - }, - { - "name": "pack", - "weight": 1 - } - ], - "threshold": 0.4, - "location": 0, - "distance": 100, - "ignoreLocation": false, - "ignoreFieldNorm": false, - "fieldNormWeight": 1, - "minMatchCharLength": 2, - "shouldSort": true, - "includeScore": true, - "findAllMatches": false, - "useExtendedSearch": false + "keys": [ + { + "name": "displayName", + "weight": 0.3 + }, + { + "name": "id", + "weight": 0.5 + }, + { + "name": "searchTokens", + "weight": 0.8 + }, + { + "name": "pack", + "weight": 1 + } + ], + "threshold": 0.4, + "location": 0, + "distance": 100, + "ignoreLocation": false, + "ignoreFieldNorm": false, + "fieldNormWeight": 1, + "minMatchCharLength": 2, + "shouldSort": true, + "includeScore": true, + "findAllMatches": false, + "useExtendedSearch": false } diff --git a/extensions/nerdfont-search/src/hooks/useIconData.ts b/extensions/nerdfont-search/src/hooks/useIconData.ts index 7ef96666..dcb4a6a2 100644 --- a/extensions/nerdfont-search/src/hooks/useIconData.ts +++ b/extensions/nerdfont-search/src/hooks/useIconData.ts @@ -3,54 +3,56 @@ import { ensureIconIndexLoaded, getCachedIconIndex, getFuseInstance, + getPackIndex, type IconIndex, } from "../icon-index-store"; export function useIconData() { - const [iconIndex, setIconIndex] = useState( - () => getCachedIconIndex() ?? [], - ); - const [isLoading, setIsLoading] = useState(getCachedIconIndex() === null); - - useEffect(() => { - const cached = getCachedIconIndex(); - if (cached) { - setIconIndex(cached); - setIsLoading(false); - return; - } - - let cancelled = false; - - setIsLoading(true); - - ensureIconIndexLoaded() - .then((data) => { - if (cancelled) { - return; - } - - setIconIndex(data); - setIsLoading(false); - }) - .catch(() => { - if (cancelled) { - return; - } - - setIsLoading(false); - }); - - return () => { - cancelled = true; - }; - }, []); - - return { - iconIndex, - isLoading, - fuseInstance: getFuseInstance(), - }; + const [iconIndex, setIconIndex] = useState( + () => getCachedIconIndex() ?? [], + ); + const [isLoading, setIsLoading] = useState(getCachedIconIndex() === null); + + useEffect(() => { + const cached = getCachedIconIndex(); + if (cached) { + setIconIndex(cached); + setIsLoading(false); + return; + } + + let cancelled = false; + + setIsLoading(true); + + ensureIconIndexLoaded() + .then((data) => { + if (cancelled) { + return; + } + + setIconIndex(data); + setIsLoading(false); + }) + .catch(() => { + if (cancelled) { + return; + } + + setIsLoading(false); + }); + + return () => { + cancelled = true; + }; + }, []); + + return { + iconIndex, + isLoading, + fuseInstance: getFuseInstance(), + packIndex: getPackIndex(), + }; } export type { IconIndex }; diff --git a/extensions/nerdfont-search/src/hooks/useIconSearch.ts b/extensions/nerdfont-search/src/hooks/useIconSearch.ts index 0585ba1f..e1888733 100644 --- a/extensions/nerdfont-search/src/hooks/useIconSearch.ts +++ b/extensions/nerdfont-search/src/hooks/useIconSearch.ts @@ -3,132 +3,132 @@ import { type IconIndex, useIconData } from "./useIconData"; import { filterIconIndex } from "../filtering"; type IconEntry = { - id: string; - packLabel: string; - displayName: string; - char: string; - code: string; - hexCode: string; - htmlEntity: string; - nerdFontId: string; - keywords: string[]; - markdown: string; - iconPath: string; + id: string; + packLabel: string; + displayName: string; + char: string; + code: string; + hexCode: string; + htmlEntity: string; + nerdFontId: string; + keywords: string[]; + markdown: string; + iconPath: string; }; const iconCache = new Map(); function createIconDataURL(char: string, _code: string): string { - const svg = `${char}`; - return `data:image/svg+xml,${encodeURIComponent(svg)}`; + const svg = `${char}`; + return `data:image/svg+xml,${encodeURIComponent(svg)}`; } function splitNameIntoWords(value: string): string[] { - if (!value) return []; - return value - .split(/[_-]/g) - .map((part) => part.trim()) - .filter(Boolean); + if (!value) return []; + return value + .split(/[_-]/g) + .map((part) => part.trim()) + .filter(Boolean); } function createIconEntry( - id: string, - char: string, - code: string, - displayName: string, - packLabel: string, + id: string, + char: string, + code: string, + displayName: string, + packLabel: string, ): IconEntry { - const [pack, ...rest] = id.split("-"); - const rawName = rest.join("-"); - const words = splitNameIntoWords(rawName); - - const codeUpper = code.toUpperCase(); - const nerdFontId = `nf-${id.replace(/_/g, "-")}`; - const htmlEntity = `&#x${code};`; - const iconPath = createIconDataURL(char, code); - const keywordSet = new Set(); - - keywordSet.add(id.toLowerCase()); - keywordSet.add(id.replace(/_/g, " ").toLowerCase()); - keywordSet.add(nerdFontId.toLowerCase()); - keywordSet.add(nerdFontId.replace(/-/g, " ")); - keywordSet.add(pack.toLowerCase()); - keywordSet.add(packLabel.toLowerCase()); - packLabel - .toLowerCase() - .split(/\s+/) - .forEach((token) => { - if (token) keywordSet.add(token); - }); - keywordSet.add(code.toLowerCase()); - keywordSet.add(codeUpper); - keywordSet.add(`0x${code.toLowerCase()}`); - keywordSet.add(`0x${codeUpper}`); - keywordSet.add(`\\u${codeUpper}`); - keywordSet.add(htmlEntity.toLowerCase()); - keywordSet.add(htmlEntity); - keywordSet.add(displayName.toLowerCase()); - - words.forEach((word) => { - const normalized = word.toLowerCase(); - keywordSet.add(normalized); - - if (normalized.includes("+")) { - keywordSet.add(normalized.replace("+", "plus")); - keywordSet.add("+"); - } - if (normalized.includes("-")) { - keywordSet.add(normalized.replace("-", " ")); - } - }); - - const markdown = [ - `# ${char} ${displayName}`, - "", - `- **Nerd Font name:** \`${nerdFontId}\``, - `- **Codepoint:** \`${codeUpper}\``, - `- **HTML entity:** \`${htmlEntity}\``, - ].join("\n"); - - return { - id, - packLabel, - displayName, - char, - code, - hexCode: `0x${codeUpper}`, - htmlEntity, - nerdFontId, - keywords: Array.from(keywordSet), - markdown, - iconPath, - }; + const [pack, ...rest] = id.split("-"); + const rawName = rest.join("-"); + const words = splitNameIntoWords(rawName); + + const codeUpper = code.toUpperCase(); + const nerdFontId = `nf-${id.replace(/_/g, "-")}`; + const htmlEntity = `&#x${code};`; + const iconPath = createIconDataURL(char, code); + const keywordSet = new Set(); + + keywordSet.add(id.toLowerCase()); + keywordSet.add(id.replace(/_/g, " ").toLowerCase()); + keywordSet.add(nerdFontId.toLowerCase()); + keywordSet.add(nerdFontId.replace(/-/g, " ")); + keywordSet.add(pack.toLowerCase()); + keywordSet.add(packLabel.toLowerCase()); + packLabel + .toLowerCase() + .split(/\s+/) + .forEach((token) => { + if (token) keywordSet.add(token); + }); + keywordSet.add(code.toLowerCase()); + keywordSet.add(codeUpper); + keywordSet.add(`0x${code.toLowerCase()}`); + keywordSet.add(`0x${codeUpper}`); + keywordSet.add(`\\u${codeUpper}`); + keywordSet.add(htmlEntity.toLowerCase()); + keywordSet.add(htmlEntity); + keywordSet.add(displayName.toLowerCase()); + + words.forEach((word) => { + const normalized = word.toLowerCase(); + keywordSet.add(normalized); + + if (normalized.includes("+")) { + keywordSet.add(normalized.replace("+", "plus")); + keywordSet.add("+"); + } + if (normalized.includes("-")) { + keywordSet.add(normalized.replace("-", " ")); + } + }); + + const markdown = [ + `# ${char} ${displayName}`, + "", + `- **Nerd Font name:** \`${nerdFontId}\``, + `- **Codepoint:** \`${codeUpper}\``, + `- **HTML entity:** \`${htmlEntity}\``, + ].join("\n"); + + return { + id, + packLabel, + displayName, + char, + code, + hexCode: `0x${codeUpper}`, + htmlEntity, + nerdFontId, + keywords: Array.from(keywordSet), + markdown, + iconPath, + }; } function getIconEntry(index: IconIndex): IconEntry { - const cached = iconCache.get(index.id); - if (cached) { - return cached; - } - - const entry = createIconEntry( - index.id, - index.char, - index.code, - index.displayName, - index.packLabel, - ); - - iconCache.set(index.id, entry); - return entry; + const cached = iconCache.get(index.id); + if (cached) { + return cached; + } + + const entry = createIconEntry( + index.id, + index.char, + index.code, + index.displayName, + index.packLabel, + ); + + iconCache.set(index.id, entry); + return entry; } function loadIconEntries(filteredIndex: IconIndex[]): IconEntry[] { - return filteredIndex.map((idx) => getIconEntry(idx)); + return filteredIndex.map((idx) => getIconEntry(idx)); } export function useIconSearch(searchText: string, selectedPack: string) { - const { iconIndex, isLoading, fuseInstance } = useIconData(); + const { iconIndex, isLoading, fuseInstance, packIndex } = useIconData(); const filteredIndex = useMemo(() => { return filterIconIndex({ @@ -136,8 +136,9 @@ export function useIconSearch(searchText: string, selectedPack: string) { fuseInstance, searchText, selectedPack, + packIndex, }); - }, [iconIndex, selectedPack, searchText, fuseInstance]); + }, [iconIndex, selectedPack, searchText, fuseInstance, packIndex]); const icons = useMemo(() => { if (filteredIndex.length === 0) { diff --git a/extensions/nerdfont-search/src/icon-index-store.ts b/extensions/nerdfont-search/src/icon-index-store.ts index eec4d8fa..299b5014 100644 --- a/extensions/nerdfont-search/src/icon-index-store.ts +++ b/extensions/nerdfont-search/src/icon-index-store.ts @@ -12,6 +12,7 @@ import { let fuseInstance: Fuse | null = null; let cachedIconIndex: IconIndex[] | null = null; let iconIndexLoadPromise: Promise | null = null; +let packIndex: Map | null = null; const cache = new Cache(); const NERD_FONTS_VERSION = "3.4.0"; @@ -106,7 +107,9 @@ function buildSearchTokens({ return Array.from(searchTokens); } -function buildSerializedIconIndex(glyphnames: Record) { +function buildSerializedIconIndex( + glyphnames: Record, +) { const tokenSet = new Set(); const entries: Array = []; @@ -145,7 +148,9 @@ function buildSerializedIconIndex(glyphnames: Record [token, index])); + const tokenToIndex = new Map( + dictionary.map((token, index) => [token, index]), + ); return { dictionary, @@ -161,10 +166,15 @@ function buildSerializedIconIndex(glyphnames: Record; + const source = (await response.json()) as Record< + string, + { char: string; code: string } + >; const glyphnames = Object.fromEntries( Object.entries(source).filter(([key]) => key !== "METADATA"), ) as Record; @@ -196,6 +206,18 @@ async function loadIconIndex(): Promise { searchTokens: icon.searchTokens.map((idx) => dictionary[idx]), })); + decodedIndex.sort((a, b) => a.displayName.localeCompare(b.displayName)); + + packIndex = new Map(); + for (const icon of decodedIndex) { + let arr = packIndex.get(icon.pack); + if (!arr) { + arr = []; + packIndex.set(icon.pack, arr); + } + arr.push(icon); + } + fuseInstance = new Fuse(decodedIndex, FUSE_OPTIONS); return decodedIndex; @@ -230,5 +252,14 @@ function getFuseInstance() { return fuseInstance; } -export { ensureIconIndexLoaded, getCachedIconIndex, getFuseInstance }; +function getPackIndex() { + return packIndex; +} + +export { + ensureIconIndexLoaded, + getCachedIconIndex, + getFuseInstance, + getPackIndex, +}; export type { IconIndex }; diff --git a/extensions/nerdfont-search/src/nerdfonts-search.tsx b/extensions/nerdfont-search/src/nerdfonts-search.tsx index 4b40ee2f..7e02c46f 100644 --- a/extensions/nerdfont-search/src/nerdfonts-search.tsx +++ b/extensions/nerdfont-search/src/nerdfonts-search.tsx @@ -1,11 +1,11 @@ import { - Action, - ActionPanel, - Color, - Grid, - Icon, - showHUD, - type Image, + Action, + ActionPanel, + Color, + Grid, + Icon, + showHUD, + type Image, } from "@vicinae/api"; import { useMemo, useState } from "react"; import { ENABLE_PACK_FILTER, PACK_FILTER_ALL } from "./constants"; @@ -16,208 +16,204 @@ import searchConfig from "./search-config.json"; const PACK_LABELS = searchConfig.packLabels as Record; function createThemedIcon(source: string): Image { - return { - source, - tintColor: Color.PrimaryText, - }; + return { + source, + tintColor: Color.PrimaryText, + }; } type CopyType = - | "glyph" - | "Nerd Font name" - | "identifier" - | "Unicode codepoint" - | "HTML entity"; + | "glyph" + | "Nerd Font name" + | "identifier" + | "Unicode codepoint" + | "HTML entity"; function IconActions({ - icon, - onCopy, - onClearRecent, + icon, + onCopy, + onClearRecent, }: { - icon: IconEntry; - onCopy: (copyType: CopyType) => void; - onClearRecent?: () => void; + icon: IconEntry; + onCopy: (copyType: CopyType) => void; + onClearRecent?: () => void; }) { - return ( - - - onCopy("glyph")} - shortcut={{ modifiers: ["cmd"], key: "c" }} - /> - onCopy("Nerd Font name")} - shortcut={{ modifiers: ["cmd", "shift"], key: "c" }} - /> - onCopy("identifier")} - /> - onCopy("Unicode codepoint")} - /> - onCopy("HTML entity")} - /> - - {onClearRecent && ( - - - - )} - - ); + return ( + + + onCopy("glyph")} + shortcut={{ modifiers: ["cmd"], key: "c" }} + /> + onCopy("Nerd Font name")} + shortcut={{ modifiers: ["cmd", "shift"], key: "c" }} + /> + onCopy("identifier")} + /> + onCopy("Unicode codepoint")} + /> + onCopy("HTML entity")} + /> + + {onClearRecent && ( + + + + )} + + ); } export default function NerdFontSearch() { - const [searchText, setSearchText] = useState(""); - const [selectedPack, setSelectedPack] = useState(PACK_FILTER_ALL); - const activePack = ENABLE_PACK_FILTER ? selectedPack : PACK_FILTER_ALL; + const [searchText, setSearchText] = useState(""); + const [selectedPack, setSelectedPack] = useState(PACK_FILTER_ALL); + const activePack = ENABLE_PACK_FILTER ? selectedPack : PACK_FILTER_ALL; - // Use custom hooks for data management - const { recentIcons, addRecent, clearRecent } = useRecentIcons(); - const { icons: searchResults, isLoading } = useIconSearch( - searchText, - activePack, - ); + // Use custom hooks for data management + const { recentIcons, addRecent, clearRecent } = useRecentIcons(); + const { icons: searchResults, isLoading } = useIconSearch( + searchText, + activePack, + ); - const displayIcons = useMemo(() => { - if (searchText.length === 0 && activePack === PACK_FILTER_ALL) { - return recentIcons.map((icon) => ({ - ...icon, - keywords: [], - markdown: "", - })); - } - if (searchText.length < 3 && activePack === PACK_FILTER_ALL) { - return []; - } - return searchResults; - }, [searchText, activePack, recentIcons, searchResults]); + const displayIcons = useMemo(() => { + if (searchText.length === 0 && activePack === PACK_FILTER_ALL) { + // Show recent icons if available, otherwise show all icons + if (recentIcons.length > 0) { + return recentIcons.map((icon) => ({ + ...icon, + keywords: [], + markdown: "", + })); + } + return searchResults; + } + return searchResults; + }, [searchText, activePack, recentIcons, searchResults]); - // Pack filter options - const packFilterOptions = useMemo<{ value: string; label: string }[]>(() => { - return Object.entries(PACK_LABELS) - .map(([value, label]) => ({ value, label })) - .sort((a, b) => a.label.localeCompare(b.label)); - }, []); + // Pack filter options + const packFilterOptions = useMemo<{ value: string; label: string }[]>(() => { + return Object.entries(PACK_LABELS) + .map(([value, label]) => ({ value, label })) + .sort((a, b) => a.label.localeCompare(b.label)); + }, []); - const handleCopyIcon = async (icon: IconEntry, copyType: CopyType) => { - addRecent(icon); - await showHUD(`Copied ${copyType}: ${icon.displayName}`); - }; + const handleCopyIcon = async (icon: IconEntry, copyType: CopyType) => { + addRecent(icon); + await showHUD(`Copied ${copyType}: ${icon.displayName}`); + }; - return ( - { - setSelectedPack(newValue); - }} - defaultValue={PACK_FILTER_ALL} - > - - {packFilterOptions.map((option) => ( - - ))} - - ) : undefined - } - > - {displayIcons.length === 0 ? ( - 0 && searchText.length < 3 - ? "Keep typing..." - : searchText.length === 0 && activePack !== PACK_FILTER_ALL - ? "No icons found" - : searchText.length >= 3 - ? "No icons found" - : "Start searching" - } - description={ - searchText.length > 0 && searchText.length < 3 - ? "Enter at least 3 characters to search" - : searchText.length === 0 && activePack !== PACK_FILTER_ALL - ? "Try selecting another icon pack" - : searchText.length >= 3 - ? "Try a different search term or pick another icon pack" - : recentIcons.length > 0 - ? "Your recently copied icons will appear here" - : "Enter at least 3 characters to search for icons" - } - icon={Icon.MagnifyingGlass} - /> - ) : ( - 0 - ? "Recently Copied" - : activePack === PACK_FILTER_ALL - ? "All icon packs" - : (PACK_LABELS[activePack] ?? activePack.toUpperCase()) - } - subtitle={`${displayIcons.length.toLocaleString()} icons`} - > - {displayIcons.map((icon: IconEntry) => ( - { - void handleCopyIcon(icon, copyType); - }} - onClearRecent={ - recentIcons.length > 0 ? clearRecent : undefined - } - /> - } - /> - ))} - - )} - - ); + + return ( + { + setSelectedPack(newValue); + }} + defaultValue={PACK_FILTER_ALL} + > + + {packFilterOptions.map((option) => ( + + ))} + + ) : undefined + } + > + {displayIcons.length === 0 ? ( + 0 && searchText.length < 3 + ? "Keep typing..." + : searchText.length >= 3 + ? "No icons found" + : "Loading icons..." + } + description={ + searchText.length > 0 && searchText.length < 3 + ? "Enter at least 3 characters to search" + : searchText.length >= 3 + ? "Try a different search term or pick another icon pack" + : "Your icon library is loading. Please wait..." + } + icon={Icon.MagnifyingGlass} + /> + ) : ( + 0 + ? "Recently Copied" + : activePack === PACK_FILTER_ALL + ? "All icon packs" + : (PACK_LABELS[activePack] ?? activePack.toUpperCase()) + } + subtitle={`${displayIcons.length.toLocaleString()} icons`} + > + {displayIcons.map((icon: IconEntry) => ( + { + void handleCopyIcon(icon, copyType); + }} + onClearRecent={ + recentIcons.length > 0 ? clearRecent : undefined + } + /> + } + /> + ))} + + )} + + ); } diff --git a/extensions/nerdfont-search/test/pack-filter-behavior.test.ts b/extensions/nerdfont-search/test/pack-filter-behavior.test.ts index 2645d23d..5acd0b29 100644 --- a/extensions/nerdfont-search/test/pack-filter-behavior.test.ts +++ b/extensions/nerdfont-search/test/pack-filter-behavior.test.ts @@ -11,7 +11,7 @@ const indexData = require("./fixtures/icon-index.fixture.json"); const decodedIndex = indexData.icons.map((icon) => ({ ...icon, searchTokens: icon.searchTokens.map((idx) => indexData.dictionary[idx]), -})); +})).sort((a, b) => a.displayName.localeCompare(b.displayName)); const fuse = new Fuse(decodedIndex, fuseOptions); const PACK_FILTER_ALL = "all"; @@ -31,7 +31,11 @@ test("pack filter behavior for short and full search terms", () => { searchText: "", selectedPack: PACK_FILTER_ALL, }); - assert.equal(noSearchAll.length, 0, "all-pack short search should return []"); + assert.ok(noSearchAll.length > 0, "all-pack short search should return all icons"); + assert.ok( + noSearchAll.every((a, i, arr) => i === 0 || a.displayName >= arr[i - 1].displayName), + "all-pack short search should return icons sorted alphabetically" + ); const noSearchPack = filterIconIndex({ iconIndex: decodedIndex,