Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 88 additions & 10 deletions webview-ui/src/components/chat/ChatRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ import { findMatchingResourceOrTemplate } from "@src/utils/mcp"
import { vscode } from "@src/utils/vscode"
import { removeLeadingNonAlphanumeric } from "@src/utils/removeLeadingNonAlphanumeric"
import { getLanguageFromPath } from "@src/utils/getLanguageFromPath"
import { Button } from "@src/components/ui"
import { formatTokenStats } from "@src/utils/formatTokens"
import { Button, StandardTooltip } from "@src/components/ui"

import { ToolUseBlock, ToolUseBlockHeader } from "../common/ToolUseBlock"
import UpdateTodoListToolBlock from "./UpdateTodoListToolBlock"
Expand Down Expand Up @@ -179,13 +180,20 @@ export const ChatRowContent = ({
vscode.postMessage({ type: "selectImages", context: "edit", messageTs: message.ts })
}, [message.ts])

const [cost, apiReqCancelReason, apiReqStreamingFailedMessage] = useMemo(() => {
const [cost, apiReqCancelReason, apiReqStreamingFailedMessage, tokensIn, tokensOut, cacheReads] = useMemo(() => {
if (message.text !== null && message.text !== undefined && message.say === "api_req_started") {
const info = safeJsonParse<ClineApiReqInfo>(message.text)
return [info?.cost, info?.cancelReason, info?.streamingFailedMessage]
return [
info?.cost,
info?.cancelReason,
info?.streamingFailedMessage,
info?.tokensIn,
info?.tokensOut,
info?.cacheReads,
]
}

return [undefined, undefined, undefined]
return [undefined, undefined, undefined, undefined, undefined, undefined]
}, [message.text, message.say])

// When resuming task, last wont be api_req_failed but a resume_task
Expand Down Expand Up @@ -1093,6 +1101,54 @@ export const ChatRowContent = ({
/>
)
case "api_req_started":
const tokenStats = formatTokenStats(
tokensIn,
tokensOut,
cacheReads,
t("chat:task.tokenStats.cacheLabel"),
)
const hasTokenData = tokensIn !== undefined || tokensOut !== undefined

const showPrice = cost !== null && cost !== undefined && cost > 0

const tooltipContent = (
<div className="flex flex-col gap-1">
<div className="flex items-center gap-2">
<span>{t("chat:task.tokenStats.inputLabel")}</span>
<span className="font-mono">{tokenStats.input}</span>
</div>
<div className="flex items-center gap-2">
<span>{t("chat:task.tokenStats.outputLabel")}</span>
<span className="font-mono">{tokenStats.output}</span>
</div>
</div>
)

const titleSpan = (
<span
className="api-request-text"
style={{
display: "inline-block",
fontWeight: "bold",
color: "var(--vscode-foreground)",
whiteSpace: "nowrap",
overflow: "hidden",
textOverflow: "ellipsis",
flexShrink: 1,
minWidth: 0,
}}>
{title}
</span>
)

const badgeStyle = {
opacity: showPrice ? 1 : 0,
flexShrink: 0,
...(hasTokenData ? { cursor: "default" } : {}),
}

const costBadge = <VSCodeBadge style={badgeStyle}>${Number(cost || 0)?.toFixed(4)}</VSCodeBadge>

return (
<>
<div
Expand All @@ -1111,13 +1167,35 @@ export const ChatRowContent = ({
msUserSelect: "none",
}}
onClick={handleToggleExpand}>
<div style={{ display: "flex", alignItems: "center", gap: "10px", flexGrow: 1 }}>
<div
style={{
display: "flex",
alignItems: "center",
gap: "10px",
flexGrow: 1,
minWidth: 0,
}}>
{icon}
{title}
<VSCodeBadge
style={{ opacity: cost !== null && cost !== undefined && cost > 0 ? 1 : 0 }}>
${Number(cost || 0)?.toFixed(4)}
</VSCodeBadge>
{hasTokenData && !showPrice ? (
<StandardTooltip content={tooltipContent} side="top">
{titleSpan}
</StandardTooltip>
) : (
titleSpan
)}
<div style={{ display: "flex", alignItems: "center", gap: "8px", flexShrink: 0 }}>
{hasTokenData ? (
showPrice ? (
<StandardTooltip content={tooltipContent} side="top">
{costBadge}
</StandardTooltip>
) : (
costBadge
)
) : (
costBadge
)}
</div>
</div>
<span className={`codicon codicon-chevron-${isExpanded ? "up" : "down"}`}></span>
</div>
Expand Down
7 changes: 6 additions & 1 deletion webview-ui/src/i18n/locales/ca/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions webview-ui/src/i18n/locales/de/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions webview-ui/src/i18n/locales/en/chat.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@
"sharingDisabledByOrganization": "Sharing disabled by organization",
"shareSuccessOrganization": "Organization link copied to clipboard",
"shareSuccessPublic": "Public link copied to clipboard",
"tokenStats": {
"inputLabel": "↑ Input:",
"outputLabel": "↓ Output:",
"cacheLabel": "cache"
},
"openInCloud": "Open task in Roo Code Cloud",
"openInCloudIntro": "Keep monitoring or interacting with Roo from anywhere. Scan, click or copy to open."
},
Expand Down
5 changes: 5 additions & 0 deletions webview-ui/src/i18n/locales/es/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions webview-ui/src/i18n/locales/fr/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions webview-ui/src/i18n/locales/hi/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions webview-ui/src/i18n/locales/id/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions webview-ui/src/i18n/locales/it/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion webview-ui/src/i18n/locales/ja/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions webview-ui/src/i18n/locales/ko/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions webview-ui/src/i18n/locales/nl/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions webview-ui/src/i18n/locales/pl/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions webview-ui/src/i18n/locales/pt-BR/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions webview-ui/src/i18n/locales/ru/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion webview-ui/src/i18n/locales/tr/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions webview-ui/src/i18n/locales/vi/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion webview-ui/src/i18n/locales/zh-CN/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions webview-ui/src/i18n/locales/zh-TW/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions webview-ui/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -490,3 +490,23 @@ input[cmdk-input]:focus {
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}

/* Hide API Request text when container is too narrow */
@media (max-width: 400px) {
.api-request-text {
display: none !important;
}
}

/* Alternative: Use container query for more precise control */
@supports (container-type: inline-size) {
.api-request-container {
container-type: inline-size;
}

@container (max-width: 350px) {
.api-request-text {
display: none !important;
}
}
}
Loading
Loading