Add sorting to task history

This commit is contained in:
Saoud Rizwan
2024-09-11 15:51:44 -04:00
parent 1fbf657410
commit dd01fc7fc4

View File

@@ -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