diff --git a/webview-ui/src/components/history/HistoryView.tsx b/webview-ui/src/components/history/HistoryView.tsx index 21cb066..efdce4d 100644 --- a/webview-ui/src/components/history/HistoryView.tsx +++ b/webview-ui/src/components/history/HistoryView.tsx @@ -85,6 +85,7 @@ const HistoryView = ({ onDone }: HistoryViewProps) => { ((a.tokensIn || 0) + (a.tokensOut || 0) + (a.cacheWrites || 0) + (a.cacheReads || 0)) ) case "mostRelevant": + // NOTE: you must never sort directly on object since it will cause members to be reordered return searchQuery ? 0 : b.ts - a.ts // Keep fuse order if searching, otherwise sort by newest case "newest": default: @@ -427,7 +428,10 @@ const ExportButton = ({ itemId }: { itemId: string }) => ( ) // https://gist.github.com/evenfrost/1ba123656ded32fb7a0cd4651efd4db0 -const highlight = (fuseSearchResult: FuseResult[], highlightClassName: string = "history-item-highlight") => { +export const highlight = ( + fuseSearchResult: FuseResult[], + highlightClassName: string = "history-item-highlight" +) => { const set = (obj: Record, path: string, value: any) => { const pathValue = path.split(".") let i: number diff --git a/webview-ui/src/components/settings/OpenRouterModelPicker.tsx b/webview-ui/src/components/settings/OpenRouterModelPicker.tsx index db147cf..7f018eb 100644 --- a/webview-ui/src/components/settings/OpenRouterModelPicker.tsx +++ b/webview-ui/src/components/settings/OpenRouterModelPicker.tsx @@ -1,11 +1,13 @@ -import React, { useMemo, useState, useRef, useEffect, memo } from "react" import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" +import Fuse from "fuse.js" +import React, { memo, useEffect, useMemo, useRef, useState } from "react" +import { useRemark } from "react-remark" +import { useMount } from "react-use" import styled from "styled-components" import { useExtensionState } from "../../context/ExtensionStateContext" -import { useMount } from "react-use" import { vscode } from "../../utils/vscode" import { ModelInfoView, normalizeApiConfiguration } from "./ApiOptions" -import { useRemark } from "react-remark" +import { highlight } from "../history/HistoryView" const OpenRouterModelPicker: React.FC = () => { const { apiConfiguration, setApiConfiguration, openRouterModels } = useExtensionState() @@ -31,12 +33,6 @@ const OpenRouterModelPicker: React.FC = () => { vscode.postMessage({ type: "refreshOpenRouterModels" }) }) - const filteredModelIds = useMemo(() => { - return Object.keys(openRouterModels) - .filter((modelId) => modelId.toLowerCase().includes(searchTerm.toLowerCase())) - .sort((a, b) => a.localeCompare(b)) - }, [openRouterModels, searchTerm]) - useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { @@ -50,8 +46,45 @@ const OpenRouterModelPicker: React.FC = () => { } }, []) + const searchableItems = useMemo(() => { + return Object.keys(openRouterModels) + .sort((a, b) => a.localeCompare(b)) + .map((id) => ({ + id, + html: id, + })) + }, [openRouterModels]) + + const fuse = useMemo(() => { + return new Fuse(searchableItems, { + keys: ["html"], // highlight function will update this + threshold: 0.6, + shouldSort: true, + isCaseSensitive: false, + ignoreLocation: false, + includeMatches: true, + minMatchCharLength: 1, + }) + }, [searchableItems]) + + const modelSearchResults = useMemo(() => { + let results: { id: string; html: string }[] = searchTerm + ? highlight(fuse.search(searchTerm), "model-item-highlight") + : searchableItems + // results.sort((a, b) => a.id.localeCompare(b.id)) NOTE: sorting like this causes ids in objects to be reordered and mismatched + return results + }, [searchableItems, searchTerm, fuse]) + return ( <> +