Saoud Rizwan 1 год назад
Родитель
Сommit
dd01fc7fc4
1 измененных файлов с 102 добавлено и 33 удалено
  1. 102 33
      webview-ui/src/components/HistoryView.tsx

+ 102 - 33
webview-ui/src/components/HistoryView.tsx

@@ -1,17 +1,31 @@
-import { VSCodeButton, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
+import { VSCodeButton, VSCodeTextField, VSCodeRadioGroup, VSCodeRadio } from "@vscode/webview-ui-toolkit/react"
 import { useExtensionState } from "../context/ExtensionStateContext"
 import { vscode } from "../utils/vscode"
 import { Virtuoso } from "react-virtuoso"
-import { memo, useMemo, useState } from "react"
+import { memo, useMemo, useState, useEffect } from "react"
 import Fuse, { FuseResult } from "fuse.js"
 
 type HistoryViewProps = {
 	onDone: () => void
 }
 
+type SortOption = "newest" | "oldest" | "mostExpensive" | "mostTokens" | "mostRelevant"
+
 const HistoryView = ({ onDone }: HistoryViewProps) => {
 	const { taskHistory } = useExtensionState()
 	const [searchQuery, setSearchQuery] = useState("")
+	const [sortOption, setSortOption] = useState<SortOption>("newest")
+	const [lastNonRelevantSort, setLastNonRelevantSort] = useState<SortOption | null>("newest")
+
+	useEffect(() => {
+		if (searchQuery && sortOption !== "mostRelevant" && !lastNonRelevantSort) {
+			setLastNonRelevantSort(sortOption)
+			setSortOption("mostRelevant")
+		} else if (!searchQuery && sortOption === "mostRelevant" && lastNonRelevantSort) {
+			setSortOption(lastNonRelevantSort)
+			setLastNonRelevantSort(null)
+		}
+	}, [searchQuery, sortOption, lastNonRelevantSort])
 
 	const handleHistorySelect = (id: string) => {
 		vscode.postMessage({ type: "showTaskWithId", text: id })
@@ -43,20 +57,42 @@ const HistoryView = ({ onDone }: HistoryViewProps) => {
 	const fuse = useMemo(() => {
 		return new Fuse(presentableTasks, {
 			keys: ["task"],
-			threshold: 0.4,
+			threshold: 0.6,
 			shouldSort: true,
 			isCaseSensitive: false,
-			ignoreLocation: true,
+			ignoreLocation: false,
 			includeMatches: true,
 			minMatchCharLength: 1,
 		})
 	}, [presentableTasks])
 
 	const taskHistorySearchResults = useMemo(() => {
-		if (!searchQuery) return presentableTasks
-		const searchResults = fuse.search(searchQuery)
-		return highlight(searchResults)
-	}, [presentableTasks, searchQuery, fuse])
+		let results = searchQuery ? highlight(fuse.search(searchQuery)) : presentableTasks
+
+		results.sort((a, b) => {
+			switch (sortOption) {
+				case "oldest":
+					return a.ts - b.ts
+				case "mostExpensive":
+					return (b.totalCost || 0) - (a.totalCost || 0)
+				case "mostTokens":
+					return (
+						(b.tokensIn || 0) +
+						(b.tokensOut || 0) +
+						(b.cacheWrites || 0) +
+						(b.cacheReads || 0) -
+						((a.tokensIn || 0) + (a.tokensOut || 0) + (a.cacheWrites || 0) + (a.cacheReads || 0))
+					)
+				case "mostRelevant":
+					return searchQuery ? 0 : b.ts - a.ts // Keep fuse order if searching, otherwise sort by newest
+				case "newest":
+				default:
+					return b.ts - a.ts
+			}
+		})
+
+		return results
+	}, [presentableTasks, searchQuery, fuse, sortOption])
 
 	return (
 		<>
@@ -78,6 +114,13 @@ const HistoryView = ({ onDone }: HistoryViewProps) => {
 						background-color: var(--vscode-editor-findMatchHighlightBackground);
 						color: inherit;
 					}
+					.clear-search-button {
+						cursor: pointer;
+						opacity: 0.5;
+					}
+					.clear-search-button:hover {
+						opacity: 1;
+					}
 				`}
 			</style>
 			<div
@@ -101,26 +144,56 @@ const HistoryView = ({ onDone }: HistoryViewProps) => {
 					<h3 style={{ color: "var(--vscode-foreground)", margin: 0 }}>History</h3>
 					<VSCodeButton onClick={onDone}>Done</VSCodeButton>
 				</div>
-				<div style={{ padding: "5px 17px 10px 17px" }}>
-					<VSCodeTextField
-						style={{ width: "100%" }}
-						placeholder="Search history..."
-						value={searchQuery}
-						onInput={(e) => setSearchQuery((e.target as HTMLInputElement)?.value)}>
-						<div
-							slot="start"
-							className="codicon codicon-search"
-							style={{ fontSize: 13, marginTop: 2.5, opacity: 0.8 }}></div>
-						{searchQuery && (
-							<VSCodeButton
-								appearance="icon"
-								aria-label="Clear search"
-								onClick={() => setSearchQuery("")}
-								slot="end">
-								<span className="codicon codicon-close"></span>
-							</VSCodeButton>
-						)}
-					</VSCodeTextField>
+				<div style={{ padding: "5px 17px 6px 17px" }}>
+					<div style={{ display: "flex", flexDirection: "column", gap: "6px" }}>
+						<VSCodeTextField
+							style={{ width: "100%" }}
+							placeholder="Fuzzy search history..."
+							value={searchQuery}
+							onInput={(e) => {
+								const newValue = (e.target as HTMLInputElement)?.value
+								setSearchQuery(newValue)
+								if (newValue && !searchQuery && sortOption !== "mostRelevant") {
+									setLastNonRelevantSort(sortOption)
+									setSortOption("mostRelevant")
+								}
+							}}>
+							<div
+								slot="start"
+								className="codicon codicon-search"
+								style={{ fontSize: 13, marginTop: 2.5, opacity: 0.8 }}></div>
+							{searchQuery && (
+								<div
+									className="clear-search-button"
+									aria-label="Clear search"
+									onClick={() => setSearchQuery("")}
+									slot="end"
+									style={{
+										display: "flex",
+										justifyContent: "center",
+										alignItems: "center",
+										height: "100%",
+									}}>
+									<span className="codicon codicon-close"></span>
+								</div>
+							)}
+						</VSCodeTextField>
+						<VSCodeRadioGroup
+							style={{ display: "flex", flexWrap: "wrap" }}
+							value={sortOption}
+							onChange={(e) => setSortOption((e.target as HTMLInputElement).value as SortOption)}>
+							<VSCodeRadio value="newest">Newest</VSCodeRadio>
+							<VSCodeRadio value="oldest">Oldest</VSCodeRadio>
+							<VSCodeRadio value="mostExpensive">Most Expensive</VSCodeRadio>
+							<VSCodeRadio value="mostTokens">Most Tokens</VSCodeRadio>
+							<VSCodeRadio
+								value="mostRelevant"
+								disabled={!searchQuery}
+								style={{ opacity: searchQuery ? 1 : 0.5 }}>
+								Most Relevant
+							</VSCodeRadio>
+						</VSCodeRadioGroup>
+					</div>
 				</div>
 				<div style={{ flexGrow: 1, overflowY: "auto", margin: 0 }}>
 					{presentableTasks.length === 0 && (
@@ -139,11 +212,7 @@ const HistoryView = ({ onDone }: HistoryViewProps) => {
 							<span
 								className="codicon codicon-archive"
 								style={{ fontSize: "50px", marginBottom: "15px" }}></span>
-							<div>
-								No history found,
-								<br />
-								start a new task to see it here...
-							</div>
+							<div>No history found</div>
 						</div>
 					)}
 					<Virtuoso