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 React, { memo, useMemo, useState, useEffect } from "react" import { Fzf } from "fzf" import { formatLargeNumber } from "../../utils/format" import { highlightFzfMatch } from "../../utils/highlight" 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("newest") const [lastNonRelevantSort, setLastNonRelevantSort] = useState("newest") const [showCopyModal, setShowCopyModal] = useState(false) 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 }) } const handleDeleteHistoryItem = (id: string) => { vscode.postMessage({ type: "deleteTaskWithId", text: id }) } const handleCopyTask = async (e: React.MouseEvent, task: string) => { e.stopPropagation() try { await navigator.clipboard.writeText(task) setShowCopyModal(true) setTimeout(() => setShowCopyModal(false), 2000) } catch (error) { console.error("Failed to copy to clipboard:", error) } } const formatDate = (timestamp: number) => { const date = new Date(timestamp) return date ?.toLocaleString("en-US", { month: "long", day: "numeric", hour: "numeric", minute: "2-digit", hour12: true, }) .replace(", ", " ") .replace(" at", ",") .toUpperCase() } const presentableTasks = useMemo(() => { return taskHistory.filter((item) => item.ts && item.task) }, [taskHistory]) const fzf = useMemo(() => { return new Fzf(presentableTasks, { selector: (item) => item.task, }) }, [presentableTasks]) const taskHistorySearchResults = useMemo(() => { let results = presentableTasks if (searchQuery) { const searchResults = fzf.find(searchQuery) results = searchResults.map((result) => ({ ...result.item, task: highlightFzfMatch(result.item.task, Array.from(result.positions)), })) } // First apply search if needed const searchResults = searchQuery ? results : presentableTasks // Then sort the results return [...searchResults].sort((a, b) => { switch (sortOption) { case "oldest": return (a.ts || 0) - (b.ts || 0) case "mostExpensive": return (b.totalCost || 0) - (a.totalCost || 0) case "mostTokens": const aTokens = (a.tokensIn || 0) + (a.tokensOut || 0) + (a.cacheWrites || 0) + (a.cacheReads || 0) const bTokens = (b.tokensIn || 0) + (b.tokensOut || 0) + (b.cacheWrites || 0) + (b.cacheReads || 0) return bTokens - aTokens case "mostRelevant": // Keep fuse order if searching, otherwise sort by newest return searchQuery ? 0 : (b.ts || 0) - (a.ts || 0) case "newest": default: return (b.ts || 0) - (a.ts || 0) } }) }, [presentableTasks, searchQuery, fzf, sortOption]) return ( <> {showCopyModal &&
Prompt Copied to Clipboard
}

History

Done
{ const newValue = (e.target as HTMLInputElement)?.value setSearchQuery(newValue) if (newValue && !searchQuery && sortOption !== "mostRelevant") { setLastNonRelevantSort(sortOption) setSortOption("mostRelevant") } }}>
{searchQuery && (
setSearchQuery("")} slot="end" style={{ display: "flex", justifyContent: "center", alignItems: "center", height: "100%", }} /> )} setSortOption((e.target as HTMLInputElement).value as SortOption)}> Newest Oldest Most Expensive Most Tokens Most Relevant
(
)), }} itemContent={(index, item) => (
handleHistorySelect(item.id)}>
{formatDate(item.ts)}
Tokens: {formatLargeNumber(item.tokensIn || 0)} {formatLargeNumber(item.tokensOut || 0)}
{!item.totalCost && }
{!!item.cacheWrites && (
Cache: +{formatLargeNumber(item.cacheWrites || 0)} {formatLargeNumber(item.cacheReads || 0)}
)} {!!item.totalCost && (
API Cost: ${item.totalCost?.toFixed(4)}
)}
)} />
) } const ExportButton = ({ itemId }: { itemId: string }) => ( { e.stopPropagation() vscode.postMessage({ type: "exportTaskWithId", text: itemId }) }}>
EXPORT
) export default memo(HistoryView)