diff --git a/apps/desktop/src/features/chat/transcript/ActivityGroup.tsx b/apps/desktop/src/features/chat/transcript/ActivityGroup.tsx index 8b725b69c..2d614a75b 100644 --- a/apps/desktop/src/features/chat/transcript/ActivityGroup.tsx +++ b/apps/desktop/src/features/chat/transcript/ActivityGroup.tsx @@ -52,6 +52,7 @@ import { useAutomaticDisclosure, } from "./shared"; import { TranscriptSearchContext } from "../../../lib/transcript-search-context"; +import type { LiveTokenRate } from "./hooks/useLiveTokenRate"; import { useAppStore } from "../../../stores/app-store"; import { resolveThinkingDisplayMode } from "../../../lib/turn-process"; @@ -421,8 +422,39 @@ export const ActivityGroup = memo(function ActivityGroup({ ); }, activityGroupPropsEqual); +function formatLiveTokenCount(value: number): string { + if (value >= 1_000_000) return `${(value / 1_000_000).toFixed(value >= 10_000_000 ? 0 : 1)}M`; + if (value >= 10_000) return `${Math.round(value / 1000)}k`; + if (value >= 1000) return `${(value / 1000).toFixed(1)}k`; + return String(value); +} + +/** Compact tok/s chip for the run-activity / working status strip. */ +export function LiveTokenRateLabel({ rate }: { rate: LiveTokenRate }) { + const { t } = useTranslation(); + if (rate.tokensPerSecond === undefined) return null; + return ( + + ); +} + /** Keep the running turn visible when no more specific runtime phase is known. */ -export function WorkingIndicator({ startedAt }: { startedAt?: number } = {}) { +export function WorkingIndicator({ + startedAt, + tokenRate, +}: { startedAt?: number; tokenRate?: LiveTokenRate } = {}) { const { t } = useTranslation(); const [elapsed, setElapsed] = useState(0); const startedAtRef = useRef(startedAt ?? Date.now()); @@ -450,6 +482,9 @@ export function WorkingIndicator({ startedAt }: { startedAt?: number } = {}) { {t("chat.running")} + {tokenRate != null && tokenRate.tokensPerSecond !== undefined ? ( + + ) : null} {elapsed > 0 ? (