From 3c937c38275a994913cadca2692ebc4d03d23e23 Mon Sep 17 00:00:00 2001
From: Chris Estreich
Date: Thu, 17 Apr 2025 15:28:11 -0700
Subject: [PATCH] Task header theme fixes (#2721)
---
evals/packages/db/scripts/copy-run.mts | 12 ++-
webview-ui/src/components/chat/ChatRow.tsx | 92 ++++---------------
.../src/components/chat/FollowUpSuggest.tsx | 67 +++++++-------
webview-ui/src/components/chat/TaskHeader.tsx | 43 ++++-----
webview-ui/src/components/ui/badge.tsx | 2 +-
webview-ui/src/components/ui/button.tsx | 4 +-
6 files changed, 85 insertions(+), 135 deletions(-)
diff --git a/evals/packages/db/scripts/copy-run.mts b/evals/packages/db/scripts/copy-run.mts
index 9901058d7a..fa82907181 100644
--- a/evals/packages/db/scripts/copy-run.mts
+++ b/evals/packages/db/scripts/copy-run.mts
@@ -57,7 +57,17 @@ const copyRun = async (runId: number) => {
tasks,
async (task) => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
- const { id: _, ...newTaskMetricsValues } = task.taskMetrics!
+ const { id: _, ...newTaskMetricsValues } = task.taskMetrics || {
+ duration: 0,
+ tokensIn: 0,
+ tokensOut: 0,
+ tokensContext: 0,
+ cacheWrites: 0,
+ cacheReads: 0,
+ cost: 0,
+ createdAt: new Date(),
+ }
+
const [newTaskMetrics] = await destDb.insert(schema.taskMetrics).values(newTaskMetricsValues).returning()
if (!newTaskMetrics) {
diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx
index e2c7495b65..60526e11d1 100644
--- a/webview-ui/src/components/chat/ChatRow.tsx
+++ b/webview-ui/src/components/chat/ChatRow.tsx
@@ -1,9 +1,12 @@
-import { VSCodeBadge, VSCodeButton, VSCodeProgressRing } from "@vscode/webview-ui-toolkit/react"
-import deepEqual from "fast-deep-equal"
import React, { memo, useEffect, useMemo, useRef, useState } from "react"
import { useSize } from "react-use"
-import { useCopyToClipboard } from "../../utils/clipboard"
import { useTranslation, Trans } from "react-i18next"
+import deepEqual from "fast-deep-equal"
+import { VSCodeBadge, VSCodeButton, VSCodeProgressRing } from "@vscode/webview-ui-toolkit/react"
+
+import { Button } from "@/components/ui"
+
+import { useCopyToClipboard } from "../../utils/clipboard"
import { safeJsonParse } from "../../utils/json"
import {
ClineApiReqInfo,
@@ -25,7 +28,7 @@ import McpResourceRow from "../mcp/McpResourceRow"
import McpToolRow from "../mcp/McpToolRow"
import { highlightMentions } from "./TaskHeader"
import { CheckpointSaved } from "./checkpoints/CheckpointSaved"
-import FollowUpSuggest from "./FollowUpSuggest"
+import { FollowUpSuggest } from "./FollowUpSuggest"
interface ChatRowProps {
message: ClineMessage
@@ -230,7 +233,8 @@ export const ChatRowContent = ({
return [
,
+ style={{ color: normalColor, marginBottom: "-1.5px" }}
+ />,
{t("chat:questions.hasQuestion")},
]
default:
@@ -795,39 +799,6 @@ export const ChatRowContent = ({
>
)}
-
- {/* {apiProvider === "" && (
-
-
-
- Uh-oh, this could be a problem on end. We've been alerted and
- will resolve this ASAP. You can also{" "}
-
- contact us
-
- .
-
-
- )} */}
>
)}
@@ -853,46 +824,19 @@ export const ChatRowContent = ({
)
case "user_feedback":
return (
-
-
-
- {highlightMentions(message.text)}
-
-
+
+
{highlightMentions(message.text)}
+
{message.images && message.images.length > 0 && (
diff --git a/webview-ui/src/components/chat/FollowUpSuggest.tsx b/webview-ui/src/components/chat/FollowUpSuggest.tsx
index 25371dc502..b300add5fb 100644
--- a/webview-ui/src/components/chat/FollowUpSuggest.tsx
+++ b/webview-ui/src/components/chat/FollowUpSuggest.tsx
@@ -1,7 +1,8 @@
import { useCallback } from "react"
-import { cn } from "../../lib/utils"
-import { Button } from "../ui/button"
-import { Edit } from "lucide-react"
+import { ArrowRight, Edit } from "lucide-react"
+
+import { Button } from "@/components/ui"
+
import { useAppTranslation } from "../../i18n/TranslationContext"
interface FollowUpSuggestProps {
@@ -10,7 +11,7 @@ interface FollowUpSuggestProps {
ts: number
}
-const FollowUpSuggest = ({ suggestions = [], onSuggestionClick, ts = 1 }: FollowUpSuggestProps) => {
+export const FollowUpSuggest = ({ suggestions = [], onSuggestionClick, ts = 1 }: FollowUpSuggestProps) => {
const { t } = useAppTranslation()
const handleSuggestionClick = useCallback(
(suggestion: string, event: React.MouseEvent) => {
@@ -19,45 +20,39 @@ const FollowUpSuggest = ({ suggestions = [], onSuggestionClick, ts = 1 }: Follow
[onSuggestionClick],
)
- // Don't render if there are no suggestions or no click handler
+ // Don't render if there are no suggestions or no click handler.
if (!suggestions?.length || !onSuggestionClick) {
return null
}
return (
-
-
-
- {suggestions.map((suggestion) => (
-
-
-
{
- e.stopPropagation()
- // Simulate shift-click by directly calling the handler with shiftKey=true
- onSuggestionClick?.(suggestion, { ...e, shiftKey: true })
- }}
- title={t("chat:followUpSuggest.copyToInput")}>
-
-
+
+ {suggestions.map((suggestion) => (
+
+
+
{
+ e.stopPropagation()
+ // Simulate shift-click by directly calling the handler with shiftKey=true.
+ onSuggestionClick?.(suggestion, { ...e, shiftKey: true })
+ }}
+ title={t("chat:followUpSuggest.copyToInput")}>
+
+
-
+ ))}
)
}
-
-export default FollowUpSuggest
diff --git a/webview-ui/src/components/chat/TaskHeader.tsx b/webview-ui/src/components/chat/TaskHeader.tsx
index 6a2c91b303..3053099b10 100644
--- a/webview-ui/src/components/chat/TaskHeader.tsx
+++ b/webview-ui/src/components/chat/TaskHeader.tsx
@@ -1,13 +1,12 @@
import React, { memo, useMemo, useRef, useState } from "react"
import { useWindowSize } from "react-use"
-import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
import prettyBytes from "pretty-bytes"
import { useTranslation } from "react-i18next"
import { vscode } from "@/utils/vscode"
import { formatLargeNumber } from "@/utils/format"
import { calculateTokenDistribution, getMaxTokensForModel } from "@/utils/model-utils"
-import { Button } from "@/components/ui"
+import { Button, Badge } from "@/components/ui"
import { ClineMessage } from "../../../../src/shared/ExtensionMessage"
import { mentionRegexGlobal } from "../../../../src/shared/context-mentions"
@@ -17,6 +16,7 @@ import { useExtensionState } from "../../context/ExtensionStateContext"
import Thumbnails from "../common/Thumbnails"
import { normalizeApiConfiguration } from "../settings/ApiOptions"
import { DeleteTaskDialog } from "../history/DeleteTaskDialog"
+import { cn } from "@/lib/utils"
interface TaskHeaderProps {
task: ClineMessage
@@ -55,10 +55,15 @@ const TaskHeader: React.FC
= ({
const shouldShowPromptCacheInfo = doesModelSupportPromptCache && apiConfiguration?.apiProvider !== "openrouter"
return (
-
+
-
+ className={cn(
+ "rounded-xs p-2.5 flex flex-col gap-1.5 relative z-1 border",
+ !!isTaskExpanded
+ ? "border-vscode-panel-border text-vscode-foreground"
+ : "border-vscode-panel-border/80 text-vscode-foreground/80",
+ )}>
+
setIsTaskExpanded(!isTaskExpanded)}>
@@ -73,14 +78,14 @@ const TaskHeader: React.FC = ({
{!isTaskExpanded && {highlightMentions(task.text, false)}}
-
-
-
-
+ title={t("chat:task.closeAndStart")}
+ className="shrink-0 w-5 h-5">
+
+
{/* Collapsed state: Track context and cost if we have any */}
{!isTaskExpanded && contextWindow > 0 && (
@@ -90,11 +95,7 @@ const TaskHeader: React.FC
= ({
contextTokens={contextTokens || 0}
maxTokens={getMaxTokensForModel(selectedModelInfo, apiConfiguration)}
/>
- {!!totalCost && (
-
- ${totalCost?.toFixed(2)}
-
- )}
+ {!!totalCost && ${totalCost.toFixed(2)}}
)}
{/* Expanded state: Show task text and images */}
@@ -271,7 +272,7 @@ const ContextWindowProgress = ({ contextWindow, contextTokens, maxTokens }: Cont
{/* Invisible overlay for hover area */}
@@ -282,7 +283,7 @@ const ContextWindowProgress = ({ contextWindow, contextTokens, maxTokens }: Cont
{/* Invisible overlay for current tokens section */}
{/* Invisible overlay for reserved section */}
{/* Invisible overlay for available space */}