mirror of
https://github.com/cline/cline.git
synced 2026-09-06 12:28:08 +08:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e8f2326139 |
@@ -103,6 +103,8 @@ message TaskItem {
|
||||
int32 cache_writes = 9;
|
||||
int32 cache_reads = 10;
|
||||
string model_id = 11;
|
||||
bool is_current_workspace = 12;
|
||||
string workspace_name = 13;
|
||||
}
|
||||
|
||||
// Request for ask response operation
|
||||
|
||||
@@ -15,7 +15,28 @@ export async function getTaskHistory(controller: Controller, request: GetTaskHis
|
||||
|
||||
// Get task history from global state
|
||||
const taskHistory = controller.stateManager.getGlobalStateKey("taskHistory")
|
||||
const workspacePath = await getWorkspacePath()
|
||||
// Ensure workspace manager is initialized for workspace path detection
|
||||
const wm = await controller.ensureWorkspaceManager()
|
||||
const workspacePath = wm?.getPrimaryRoot()?.path || (await getWorkspacePath().catch(() => ""))
|
||||
|
||||
const getWorkspaceName = (item: (typeof taskHistory)[number]): string => {
|
||||
const p = item.cwdOnTaskInitialization || item.shadowGitConfigWorkTree
|
||||
if (!p) return ""
|
||||
const segments = p.replace(/\\/g, "/").split("/").filter(Boolean)
|
||||
return segments[segments.length - 1] || ""
|
||||
}
|
||||
|
||||
const isTaskInCurrentWorkspace = (item: (typeof taskHistory)[number]) => {
|
||||
if (item.cwdOnTaskInitialization && arePathsEqual(item.cwdOnTaskInitialization, workspacePath)) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (item.shadowGitConfigWorkTree && arePathsEqual(item.shadowGitConfigWorkTree, workspacePath)) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Apply filters
|
||||
let filteredTasks = taskHistory.filter((item) => {
|
||||
@@ -32,23 +53,7 @@ export async function getTaskHistory(controller: Controller, request: GetTaskHis
|
||||
|
||||
// Apply current workspace filter if requested
|
||||
if (currentWorkspaceOnly) {
|
||||
let isInWorkspace = false
|
||||
|
||||
// First check the cwdOnTaskInitialization property - Only present on tasks from this change forward
|
||||
if (item.cwdOnTaskInitialization) {
|
||||
if (arePathsEqual(item.cwdOnTaskInitialization, workspacePath)) {
|
||||
isInWorkspace = true
|
||||
}
|
||||
}
|
||||
|
||||
// For tasks without cwdOnTaskInitialization, check the older shadowGitConfigWorkTree property
|
||||
if (!isInWorkspace && item.shadowGitConfigWorkTree) {
|
||||
if (arePathsEqual(item.shadowGitConfigWorkTree, workspacePath)) {
|
||||
isInWorkspace = true
|
||||
}
|
||||
}
|
||||
|
||||
if (!isInWorkspace) {
|
||||
if (!isTaskInCurrentWorkspace(item)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -105,6 +110,8 @@ export async function getTaskHistory(controller: Controller, request: GetTaskHis
|
||||
cacheWrites: item.cacheWrites || 0,
|
||||
cacheReads: item.cacheReads || 0,
|
||||
modelId: item.modelId || "",
|
||||
isCurrentWorkspace: isTaskInCurrentWorkspace(item),
|
||||
workspaceName: getWorkspaceName(item),
|
||||
}))
|
||||
|
||||
return TaskHistoryArray.create({
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { HistoryItem } from "@shared/HistoryItem"
|
||||
import { StringRequest } from "@shared/proto/cline/common"
|
||||
import { memo } from "react"
|
||||
import { memo, useCallback, useMemo, useState } from "react"
|
||||
import { useExtensionState } from "@/context/ExtensionStateContext"
|
||||
import { TaskServiceClient } from "@/services/grpc-client"
|
||||
|
||||
@@ -7,8 +8,76 @@ type HistoryPreviewProps = {
|
||||
showHistoryView: () => void
|
||||
}
|
||||
|
||||
type FilterMode = "all" | "workspace"
|
||||
|
||||
const STORAGE_KEY = "historyPreviewFilter"
|
||||
|
||||
function getSavedFilter(): FilterMode {
|
||||
try {
|
||||
const saved = localStorage.getItem(STORAGE_KEY)
|
||||
if (saved === "workspace") return "workspace"
|
||||
} catch {}
|
||||
return "all"
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a display name for the workspace from a task's path fields.
|
||||
*/
|
||||
function getWorkspaceLabel(item: HistoryItem): string {
|
||||
const p = item.cwdOnTaskInitialization || item.shadowGitConfigWorkTree
|
||||
if (!p) return "Unknown Workspace"
|
||||
const segments = p.replace(/\\/g, "/").split("/").filter(Boolean)
|
||||
return segments[segments.length - 1] || "Unknown Workspace"
|
||||
}
|
||||
|
||||
/**
|
||||
* Group tasks by workspace label while preserving order.
|
||||
*/
|
||||
function groupByWorkspace(items: HistoryItem[]): { label: string; tasks: HistoryItem[] }[] {
|
||||
const groups: { label: string; tasks: HistoryItem[] }[] = []
|
||||
const seen = new Map<string, number>()
|
||||
|
||||
for (const item of items) {
|
||||
const label = getWorkspaceLabel(item)
|
||||
const idx = seen.get(label)
|
||||
if (idx !== undefined) {
|
||||
groups[idx].tasks.push(item)
|
||||
} else {
|
||||
seen.set(label, groups.length)
|
||||
groups.push({ label, tasks: [item] })
|
||||
}
|
||||
}
|
||||
|
||||
return groups
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract workspace folder name from workspaceRoots paths.
|
||||
*/
|
||||
function getWorkspaceFolderName(workspaceRoots: Array<{ path?: string; name?: string }>): string {
|
||||
if (!workspaceRoots || workspaceRoots.length === 0) return ""
|
||||
const root = workspaceRoots[0]
|
||||
if (root.name) return root.name
|
||||
const p = root.path
|
||||
if (!p) return ""
|
||||
const segments = p.replace(/\\/g, "/").split("/").filter(Boolean)
|
||||
return segments[segments.length - 1] || ""
|
||||
}
|
||||
|
||||
const HistoryPreview = ({ showHistoryView }: HistoryPreviewProps) => {
|
||||
const { taskHistory } = useExtensionState()
|
||||
const { taskHistory, workspaceRoots } = useExtensionState()
|
||||
const [filter, setFilter] = useState<FilterMode>(getSavedFilter)
|
||||
|
||||
const workspaceName = useMemo(() => getWorkspaceFolderName(workspaceRoots || []), [workspaceRoots])
|
||||
|
||||
const handleFilterChange = useCallback((e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
const newFilter = e.target.value as FilterMode
|
||||
setFilter(newFilter)
|
||||
try {
|
||||
localStorage.setItem(STORAGE_KEY, newFilter)
|
||||
} catch {}
|
||||
}, [])
|
||||
|
||||
const handleHistorySelect = (id: string) => {
|
||||
TaskServiceClient.showTaskWithId(StringRequest.create({ value: id })).catch((error) =>
|
||||
console.error("Error showing task:", error),
|
||||
@@ -23,6 +92,36 @@ const HistoryPreview = ({ showHistoryView }: HistoryPreviewProps) => {
|
||||
})
|
||||
}
|
||||
|
||||
// Compute workspace path from workspaceRoots for local matching
|
||||
const currentWorkspacePath = useMemo(() => {
|
||||
if (!workspaceRoots || workspaceRoots.length === 0) return ""
|
||||
return workspaceRoots[0]?.path || ""
|
||||
}, [workspaceRoots])
|
||||
|
||||
const isInCurrentWorkspace = useCallback(
|
||||
(item: HistoryItem) => {
|
||||
if (!currentWorkspacePath) return false
|
||||
const taskPath = item.cwdOnTaskInitialization || item.shadowGitConfigWorkTree
|
||||
if (!taskPath) return false
|
||||
// Normalize paths for comparison
|
||||
const normalize = (p: string) => p.replace(/\\/g, "/").replace(/\/+$/, "")
|
||||
return normalize(taskPath) === normalize(currentWorkspacePath)
|
||||
},
|
||||
[currentWorkspacePath],
|
||||
)
|
||||
|
||||
const allValidTasks = useMemo(() => taskHistory.filter((item) => item.ts && item.task), [taskHistory])
|
||||
|
||||
const displayTasks = useMemo(() => {
|
||||
if (filter === "workspace") {
|
||||
return allValidTasks.filter((item) => isInCurrentWorkspace(item)).slice(0, 5)
|
||||
}
|
||||
return [...allValidTasks].sort((a, b) => b.ts - a.ts).slice(0, 5)
|
||||
}, [allValidTasks, filter, isInCurrentWorkspace])
|
||||
|
||||
const workspaceGroups = useMemo(() => groupByWorkspace(displayTasks), [displayTasks])
|
||||
const hasMultipleWorkspaces = filter === "all" && workspaceGroups.length > 1
|
||||
|
||||
return (
|
||||
<div style={{ flexShrink: 0 }}>
|
||||
<style>
|
||||
@@ -33,7 +132,7 @@ const HistoryPreview = ({ showHistoryView }: HistoryPreviewProps) => {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
margin-bottom: 8px;
|
||||
margin-bottom: 6px;
|
||||
padding: 10px 12px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
@@ -100,6 +199,41 @@ const HistoryPreview = ({ showHistoryView }: HistoryPreviewProps) => {
|
||||
.history-view-all-btn:hover {
|
||||
color: var(--vscode-foreground);
|
||||
}
|
||||
.history-workspace-divider {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 0 4px 0;
|
||||
color: var(--vscode-descriptionForeground);
|
||||
font-size: 0.75em;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
.history-workspace-divider::after {
|
||||
content: '';
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
background: color-mix(in srgb, var(--vscode-descriptionForeground) 25%, transparent);
|
||||
}
|
||||
.history-filter-select {
|
||||
background: none;
|
||||
border: 1px solid color-mix(in srgb, var(--vscode-descriptionForeground) 30%, transparent);
|
||||
border-radius: 3px;
|
||||
color: var(--vscode-descriptionForeground);
|
||||
font-weight: 500;
|
||||
font-size: 0.8em;
|
||||
cursor: pointer;
|
||||
padding: 1px 4px;
|
||||
margin-left: 6px;
|
||||
outline: none;
|
||||
font-family: inherit;
|
||||
}
|
||||
.history-filter-select:hover,
|
||||
.history-filter-select:focus {
|
||||
color: var(--vscode-foreground);
|
||||
border-color: color-mix(in srgb, var(--vscode-descriptionForeground) 60%, transparent);
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
|
||||
@@ -118,17 +252,24 @@ const HistoryPreview = ({ showHistoryView }: HistoryPreviewProps) => {
|
||||
style={{
|
||||
marginRight: "4px",
|
||||
transform: "scale(0.9)",
|
||||
}}></span>
|
||||
}}
|
||||
/>
|
||||
<span
|
||||
style={{
|
||||
fontWeight: 500,
|
||||
fontSize: "0.85em",
|
||||
textTransform: "uppercase",
|
||||
}}>
|
||||
Recent
|
||||
Recent Tasks
|
||||
</span>
|
||||
{workspaceName && (
|
||||
<select className="history-filter-select" onChange={handleFilterChange} value={filter}>
|
||||
<option value="all">All</option>
|
||||
<option value="workspace">{workspaceName}</option>
|
||||
</select>
|
||||
)}
|
||||
</div>
|
||||
{taskHistory.filter((item) => item.ts && item.task).length > 0 && (
|
||||
{displayTasks.length > 0 && (
|
||||
<button
|
||||
aria-label="View all history"
|
||||
className="history-view-all-btn"
|
||||
@@ -140,13 +281,17 @@ const HistoryPreview = ({ showHistoryView }: HistoryPreviewProps) => {
|
||||
)}
|
||||
</div>
|
||||
|
||||
{
|
||||
<div className="px-4">
|
||||
{taskHistory.filter((item) => item.ts && item.task).length > 0 ? (
|
||||
taskHistory
|
||||
.filter((item) => item.ts && item.task)
|
||||
.slice(0, 3)
|
||||
.map((item) => (
|
||||
<div className="px-4">
|
||||
{displayTasks.length > 0 ? (
|
||||
workspaceGroups.map((group) => (
|
||||
<div key={group.label}>
|
||||
{hasMultipleWorkspaces && (
|
||||
<div className="history-workspace-divider">
|
||||
<span className="codicon codicon-folder" style={{ fontSize: "0.9em" }} />
|
||||
{group.label}
|
||||
</div>
|
||||
)}
|
||||
{group.tasks.map((item) => (
|
||||
<div className="history-preview-item" key={item.id} onClick={() => handleHistorySelect(item.id)}>
|
||||
<div className="history-task-content">
|
||||
{item.isFavorited && (
|
||||
@@ -168,20 +313,21 @@ const HistoryPreview = ({ showHistoryView }: HistoryPreviewProps) => {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
textAlign: "center",
|
||||
color: "var(--vscode-descriptionForeground)",
|
||||
fontSize: "var(--vscode-font-size)",
|
||||
padding: "10px 0",
|
||||
}}>
|
||||
No recent tasks
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
))
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
textAlign: "center",
|
||||
color: "var(--vscode-descriptionForeground)",
|
||||
fontSize: "var(--vscode-font-size)",
|
||||
padding: "10px 0",
|
||||
}}>
|
||||
{filter === "workspace" ? "No tasks in this workspace" : "No recent tasks"}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { BooleanRequest, EmptyRequest, StringArrayRequest } from "@shared/proto/cline/common"
|
||||
import { GetTaskHistoryRequest, TaskFavoriteRequest } from "@shared/proto/cline/task"
|
||||
import { GetTaskHistoryRequest, TaskFavoriteRequest, type TaskItem } from "@shared/proto/cline/task"
|
||||
import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
|
||||
import Fuse, { FuseResult } from "fuse.js"
|
||||
import { FunnelIcon } from "lucide-react"
|
||||
@@ -50,7 +50,7 @@ const HistoryView = ({ onDone }: HistoryViewProps) => {
|
||||
const [pendingFavoriteToggles, setPendingFavoriteToggles] = useState<Record<string, boolean>>({})
|
||||
|
||||
// Load filtered task history with gRPC
|
||||
const [tasks, setTasks] = useState<any[]>([])
|
||||
const [tasks, setTasks] = useState<TaskItem[]>([])
|
||||
|
||||
// Load and refresh task history
|
||||
const loadTaskHistory = useCallback(async () => {
|
||||
@@ -157,9 +157,8 @@ const HistoryView = ({ onDone }: HistoryViewProps) => {
|
||||
setSelectedItems((prev) => {
|
||||
if (checked) {
|
||||
return [...prev, itemId]
|
||||
} else {
|
||||
return prev.filter((id) => id !== itemId)
|
||||
}
|
||||
return prev.filter((id) => id !== itemId)
|
||||
})
|
||||
}, [])
|
||||
|
||||
@@ -233,20 +232,58 @@ const HistoryView = ({ onDone }: HistoryViewProps) => {
|
||||
// Group tasks into "Today" and "Older" (only for date-based sorts)
|
||||
const { groupedTasks, groupCounts, groupLabels } = useMemo(() => {
|
||||
const isDateSort = sortOption === "newest" || sortOption === "oldest"
|
||||
const workspaceTasks: TaskItem[] = []
|
||||
const otherWorkspaceTasks: TaskItem[] = []
|
||||
|
||||
taskHistorySearchResults.forEach((task) => {
|
||||
if (task.isCurrentWorkspace) {
|
||||
workspaceTasks.push(task)
|
||||
} else {
|
||||
otherWorkspaceTasks.push(task)
|
||||
}
|
||||
})
|
||||
|
||||
const groups: { tasks: TaskItem[]; label: string }[] = []
|
||||
|
||||
// Derive workspace label from first task's workspaceName
|
||||
const currentWorkspaceLabel =
|
||||
workspaceTasks.length > 0 && workspaceTasks[0].workspaceName ? workspaceTasks[0].workspaceName : "This Workspace"
|
||||
|
||||
// When workspace filter is active, all tasks are already workspace-only from backend
|
||||
// When not active, show workspace tasks grouped at top
|
||||
if (showCurrentWorkspaceOnly) {
|
||||
// All tasks are workspace tasks, treat them as the full set (no special grouping needed)
|
||||
// They'll be date-grouped below
|
||||
} else if (workspaceTasks.length > 0) {
|
||||
groups.push({ tasks: workspaceTasks, label: currentWorkspaceLabel })
|
||||
}
|
||||
|
||||
if (!isDateSort) {
|
||||
// No grouping for non-date sorts
|
||||
if (otherWorkspaceTasks.length > 0) {
|
||||
groups.push({ tasks: otherWorkspaceTasks, label: workspaceTasks.length > 0 ? "Other Workspaces" : "All Tasks" })
|
||||
}
|
||||
|
||||
if (groups.length === 0) {
|
||||
return {
|
||||
groupedTasks: taskHistorySearchResults,
|
||||
groupCounts: [taskHistorySearchResults.length],
|
||||
groupLabels: [] as string[],
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
groupedTasks: taskHistorySearchResults,
|
||||
groupCounts: [taskHistorySearchResults.length],
|
||||
groupLabels: [] as string[],
|
||||
groupedTasks: groups.flatMap((g) => g.tasks),
|
||||
groupCounts: groups.map((g) => g.tasks.length),
|
||||
groupLabels: groups.map((g) => g.label),
|
||||
}
|
||||
}
|
||||
|
||||
const todayTasks: any[] = []
|
||||
const olderTasks: any[] = []
|
||||
// Date-group the remaining tasks (non-workspace, or all tasks if workspace filter is active)
|
||||
const tasksToDateGroup = showCurrentWorkspaceOnly ? taskHistorySearchResults : otherWorkspaceTasks
|
||||
const todayTasks: TaskItem[] = []
|
||||
const olderTasks: TaskItem[] = []
|
||||
|
||||
taskHistorySearchResults.forEach((task) => {
|
||||
tasksToDateGroup.forEach((task) => {
|
||||
if (isToday(task.ts)) {
|
||||
todayTasks.push(task)
|
||||
} else {
|
||||
@@ -254,12 +291,21 @@ const HistoryView = ({ onDone }: HistoryViewProps) => {
|
||||
}
|
||||
})
|
||||
|
||||
const groups: { tasks: any[]; label: string }[] = []
|
||||
if (todayTasks.length > 0) {
|
||||
groups.push({ tasks: todayTasks, label: "Today" })
|
||||
const hasWorkspaceGroup = !showCurrentWorkspaceOnly && workspaceTasks.length > 0
|
||||
groups.push({ tasks: todayTasks, label: hasWorkspaceGroup ? "Today (Other Workspaces)" : "Today" })
|
||||
}
|
||||
if (olderTasks.length > 0) {
|
||||
groups.push({ tasks: olderTasks, label: "Older" })
|
||||
const hasWorkspaceGroup = !showCurrentWorkspaceOnly && workspaceTasks.length > 0
|
||||
groups.push({ tasks: olderTasks, label: hasWorkspaceGroup ? "Older (Other Workspaces)" : "Older" })
|
||||
}
|
||||
|
||||
if (groups.length === 0) {
|
||||
return {
|
||||
groupedTasks: taskHistorySearchResults,
|
||||
groupCounts: [taskHistorySearchResults.length],
|
||||
groupLabels: [] as string[],
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -267,7 +313,7 @@ const HistoryView = ({ onDone }: HistoryViewProps) => {
|
||||
groupCounts: groups.map((g) => g.tasks.length),
|
||||
groupLabels: groups.map((g) => g.label),
|
||||
}
|
||||
}, [taskHistorySearchResults, sortOption])
|
||||
}, [taskHistorySearchResults, sortOption, showCurrentWorkspaceOnly])
|
||||
|
||||
// Calculate total size of selected items
|
||||
const selectedItemsSize = useMemo(() => {
|
||||
@@ -275,8 +321,8 @@ const HistoryView = ({ onDone }: HistoryViewProps) => {
|
||||
return 0
|
||||
}
|
||||
|
||||
return taskHistory.filter((item) => selectedItems.includes(item.id)).reduce((total, item) => total + (item.size || 0), 0)
|
||||
}, [selectedItems, taskHistory])
|
||||
return tasks.filter((item) => selectedItems.includes(item.id)).reduce((total, item) => total + (item.size || 0), 0)
|
||||
}, [selectedItems, tasks])
|
||||
|
||||
const handleBatchHistorySelect = useCallback(
|
||||
(selectAll: boolean) => {
|
||||
@@ -402,6 +448,7 @@ const HistoryView = ({ onDone }: HistoryViewProps) => {
|
||||
groupCounts={groupCounts}
|
||||
itemContent={(index) => {
|
||||
const item = groupedTasks[index]
|
||||
if (!item) return null
|
||||
return (
|
||||
<HistoryViewItem
|
||||
handleDeleteHistoryItem={handleDeleteHistoryItem}
|
||||
@@ -460,7 +507,7 @@ const HistoryView = ({ onDone }: HistoryViewProps) => {
|
||||
}
|
||||
|
||||
// https://gist.github.com/evenfrost/1ba123656ded32fb7a0cd4651efd4db0
|
||||
export const highlight = (fuseSearchResult: FuseResult<any>[], highlightClassName: string = "history-item-highlight") => {
|
||||
export const highlight = (fuseSearchResult: FuseResult<any>[], highlightClassName = "history-item-highlight") => {
|
||||
const set = (obj: Record<string, any>, path: string, value: any) => {
|
||||
const pathValue = path.split(".")
|
||||
let i: number
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { HistoryItem } from "@shared/HistoryItem"
|
||||
import { StringRequest } from "@shared/proto/cline/common"
|
||||
import { type TaskItem } from "@shared/proto/cline/task"
|
||||
import { VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react"
|
||||
import {
|
||||
ArrowDownIcon,
|
||||
@@ -19,7 +19,7 @@ import { TaskServiceClient } from "@/services/grpc-client"
|
||||
import { formatLargeNumber, formatSize } from "@/utils/format"
|
||||
|
||||
type HistoryViewItemProps = {
|
||||
item: HistoryItem
|
||||
item: TaskItem
|
||||
index: number
|
||||
selectedItems: string[]
|
||||
pendingFavoriteToggles: Record<string, boolean>
|
||||
@@ -95,8 +95,10 @@ const HistoryViewItem = ({
|
||||
handleShowTaskWithId(item.id)
|
||||
}}>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="line-clamp-1 overflow-hidden break-words whitespace-pre-wrap flex-1 min-w-0">
|
||||
<span className="ph-no-capture">{item.task}</span>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="line-clamp-1 overflow-hidden break-words whitespace-pre-wrap">
|
||||
<span className="ph-no-capture">{item.task}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2 flex-shrink-0">
|
||||
<Button
|
||||
@@ -138,7 +140,10 @@ const HistoryViewItem = ({
|
||||
}}
|
||||
variant="icon">
|
||||
<div className="flex items-center justify-between w-full">
|
||||
<div className="text-description text-xs uppercase">{formatDate(item.ts)}</div>
|
||||
<div className="text-description text-xs uppercase">
|
||||
{item.workspaceName && <span className="font-medium">{item.workspaceName} · </span>}
|
||||
{formatDate(item.ts)}
|
||||
</div>
|
||||
<div className="self-end flex items-center text-xs">
|
||||
<span className="text-description">${item.totalCost?.toFixed(4) ?? 0}</span>
|
||||
{expanded ? (
|
||||
|
||||
Reference in New Issue
Block a user