diff --git a/webview-ui/src/components/history/CopyButton.tsx b/webview-ui/src/components/history/CopyButton.tsx new file mode 100644 index 0000000000..0e693b4470 --- /dev/null +++ b/webview-ui/src/components/history/CopyButton.tsx @@ -0,0 +1,32 @@ +import { useCallback } from "react" + +import { useClipboard } from "@/components/ui/hooks" +import { Button } from "@/components/ui" +import { cn } from "@/lib/utils" + +type CopyButtonProps = { + itemTask: string +} + +export const CopyButton = ({ itemTask }: CopyButtonProps) => { + const { isCopied, copy } = useClipboard() + + const onCopy = useCallback( + (e: React.MouseEvent) => { + e.stopPropagation() + !isCopied && copy(itemTask) + }, + [isCopied, copy, itemTask], + ) + + return ( + + ) +} diff --git a/webview-ui/src/components/history/ExportButton.tsx b/webview-ui/src/components/history/ExportButton.tsx new file mode 100644 index 0000000000..6617e475bd --- /dev/null +++ b/webview-ui/src/components/history/ExportButton.tsx @@ -0,0 +1,16 @@ +import { vscode } from "@/utils/vscode" +import { Button } from "@/components/ui" + +export const ExportButton = ({ itemId }: { itemId: string }) => ( + +) diff --git a/webview-ui/src/components/history/HistoryPreview.tsx b/webview-ui/src/components/history/HistoryPreview.tsx index b2898fc6a8..bf53845da7 100644 --- a/webview-ui/src/components/history/HistoryPreview.tsx +++ b/webview-ui/src/components/history/HistoryPreview.tsx @@ -1,9 +1,11 @@ -import { VSCodeButton } from "@vscode/webview-ui-toolkit/react" -import { useExtensionState } from "../../context/ExtensionStateContext" -import { vscode } from "../../utils/vscode" import { memo } from "react" -import { formatLargeNumber } from "../../utils/format" -import { useCopyToClipboard } from "../../utils/clipboard" + +import { vscode } from "@/utils/vscode" +import { formatLargeNumber, formatDate } from "@/utils/format" +import { Button } from "@/components/ui" + +import { useExtensionState } from "../../context/ExtensionStateContext" +import { CopyButton } from "./CopyButton" type HistoryPreviewProps = { showHistoryView: () => void @@ -11,52 +13,15 @@ type HistoryPreviewProps = { const HistoryPreview = ({ showHistoryView }: HistoryPreviewProps) => { const { taskHistory } = useExtensionState() - const { showCopyFeedback, copyWithFeedback } = useCopyToClipboard() + const handleHistorySelect = (id: string) => { vscode.postMessage({ type: "showTaskWithId", text: id }) } - 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() - } - return (
- {showCopyFeedback &&
Prompt Copied to Clipboard
} -
{ display: "flex", alignItems: "center", }}> - - - Recent Tasks - + + Recent Tasks
- -
+
{taskHistory .filter((item) => item.ts && item.task) .slice(0, 3) @@ -103,48 +57,25 @@ const HistoryPreview = ({ showHistoryView }: HistoryPreviewProps) => { key={item.id} className="history-preview-item" onClick={() => handleHistorySelect(item.id)}> -
-
- +
+
+ {formatDate(item.ts)} - +
{item.task}
-
+
Tokens: ↑{formatLargeNumber(item.tokensIn || 0)} ↓ {formatLargeNumber(item.tokensOut || 0)} @@ -168,21 +99,14 @@ const HistoryPreview = ({ showHistoryView }: HistoryPreviewProps) => {
))} -
- +
diff --git a/webview-ui/src/components/history/HistoryView.tsx b/webview-ui/src/components/history/HistoryView.tsx index 49d71e5ddd..d50a569c8d 100644 --- a/webview-ui/src/components/history/HistoryView.tsx +++ b/webview-ui/src/components/history/HistoryView.tsx @@ -5,12 +5,14 @@ import prettyBytes from "pretty-bytes" import { Virtuoso } from "react-virtuoso" import { VSCodeButton, VSCodeTextField, VSCodeRadioGroup, VSCodeRadio } from "@vscode/webview-ui-toolkit/react" +import { vscode } from "@/utils/vscode" +import { formatLargeNumber, formatDate } from "@/utils/format" +import { highlightFzfMatch } from "@/utils/highlight" +import { Button } from "@/components/ui" + import { useExtensionState } from "../../context/ExtensionStateContext" -import { vscode } from "../../utils/vscode" -import { formatLargeNumber } from "../../utils/format" -import { highlightFzfMatch } from "../../utils/highlight" -import { useCopyToClipboard } from "../../utils/clipboard" -import { Button } from "../ui" +import { ExportButton } from "./ExportButton" +import { CopyButton } from "./CopyButton" type HistoryViewProps = { onDone: () => void @@ -40,21 +42,6 @@ const HistoryView = ({ onDone }: HistoryViewProps) => { const [deleteTaskId, setDeleteTaskId] = useState(null) - 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]) @@ -409,28 +396,4 @@ const HistoryView = ({ onDone }: HistoryViewProps) => { ) } -const CopyButton = ({ itemTask }: { itemTask: string }) => { - const { showCopyFeedback, copyWithFeedback } = useCopyToClipboard() - - return ( - - ) -} - -const ExportButton = ({ itemId }: { itemId: string }) => ( - -) - export default memo(HistoryView) diff --git a/webview-ui/src/index.css b/webview-ui/src/index.css index 53025be01a..0e80d1b0c6 100644 --- a/webview-ui/src/index.css +++ b/webview-ui/src/index.css @@ -23,6 +23,8 @@ @theme { --font-display: var(--vscode-font-family); + + --text-xs: calc(var(--vscode-font-size) * 0.85); --text-sm: calc(var(--vscode-font-size) * 0.9); --text-base: var(--vscode-font-size); --text-lg: calc(var(--vscode-font-size) * 1.1); diff --git a/webview-ui/src/utils/__tests__/format.test.ts b/webview-ui/src/utils/__tests__/format.test.ts new file mode 100644 index 0000000000..7377874fd0 --- /dev/null +++ b/webview-ui/src/utils/__tests__/format.test.ts @@ -0,0 +1,51 @@ +// npx jest src/utils/__tests__/format.test.ts + +import { formatDate } from "../format" + +describe("formatDate", () => { + it("formats a timestamp correctly", () => { + // January 15, 2023, 10:30 AM + const timestamp = new Date(2023, 0, 15, 10, 30).getTime() + const result = formatDate(timestamp) + + expect(result).toBe("JANUARY 15, 10:30 AM") + }) + + it("handles different months correctly", () => { + // February 28, 2023, 3:45 PM + const timestamp1 = new Date(2023, 1, 28, 15, 45).getTime() + expect(formatDate(timestamp1)).toBe("FEBRUARY 28, 3:45 PM") + + // December 31, 2023, 11:59 PM + const timestamp2 = new Date(2023, 11, 31, 23, 59).getTime() + expect(formatDate(timestamp2)).toBe("DECEMBER 31, 11:59 PM") + }) + + it("handles AM/PM correctly", () => { + // Morning time - 7:05 AM + const morningTimestamp = new Date(2023, 5, 15, 7, 5).getTime() + expect(formatDate(morningTimestamp)).toBe("JUNE 15, 7:05 AM") + + // Noon - 12:00 PM + const noonTimestamp = new Date(2023, 5, 15, 12, 0).getTime() + expect(formatDate(noonTimestamp)).toBe("JUNE 15, 12:00 PM") + + // Evening time - 8:15 PM + const eveningTimestamp = new Date(2023, 5, 15, 20, 15).getTime() + expect(formatDate(eveningTimestamp)).toBe("JUNE 15, 8:15 PM") + }) + + it("handles single-digit minutes with leading zeros", () => { + // 9:05 AM + const timestamp = new Date(2023, 3, 10, 9, 5).getTime() + expect(formatDate(timestamp)).toBe("APRIL 10, 9:05 AM") + }) + + it("converts the result to uppercase", () => { + const timestamp = new Date(2023, 8, 21, 16, 45).getTime() + const result = formatDate(timestamp) + + expect(result).toBe(result.toUpperCase()) + expect(result).toBe("SEPTEMBER 21, 4:45 PM") + }) +}) diff --git a/webview-ui/src/utils/format.ts b/webview-ui/src/utils/format.ts index 2e473c9b8a..12e9996205 100644 --- a/webview-ui/src/utils/format.ts +++ b/webview-ui/src/utils/format.ts @@ -10,3 +10,18 @@ export function formatLargeNumber(num: number): string { } return num.toString() } + +export 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() +}