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) => {
))}
-
-
+
+ className="font-normal text-vscode-descriptionForeground">
+ View all history
+
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()
+}