From d61ea56b3e2282e93069049d763a3768c12bbb34 Mon Sep 17 00:00:00 2001 From: iamtoruk Date: Wed, 29 Apr 2026 14:12:24 -0700 Subject: [PATCH] Add collapsible models section with bar chart and token summary --- desktop/src/App.tsx | 9 +++ desktop/src/components/ModelsSection.tsx | 71 ++++++++++++++++++ desktop/src/styles.css | 96 ++++++++++++++++++++++++ 3 files changed, 176 insertions(+) create mode 100644 desktop/src/components/ModelsSection.tsx diff --git a/desktop/src/App.tsx b/desktop/src/App.tsx index 7ef9e03..b93f727 100644 --- a/desktop/src/App.tsx +++ b/desktop/src/App.tsx @@ -9,6 +9,7 @@ import { USD, formatCompactCurrency, formatCurrency } from './lib/currency' import { PayloadCache } from './lib/cache' import { AgentTabStrip } from './components/AgentTabStrip' import type { Provider } from './components/AgentTabStrip' +import { ModelsSection } from './components/ModelsSection' const payloadCache = new PayloadCache() @@ -175,6 +176,14 @@ export function App() { )} + + {payload.optimize.findingCount > 0 && (
+ + {expanded && ( + <> + {models.map(m => { + const fillPct = maxCost > 0 ? (m.cost / maxCost) * 100 : 0 + return ( +
+
+
+
+
{m.name}
+
{formatCompactCurrency(m.cost, currency)}
+
{m.calls}
+
+ ) + })} + + {(inputTokens > 0 || outputTokens > 0) && ( +
+ Tokens + {formatTokens(inputTokens)} in + · + {formatTokens(outputTokens)} out + · + {Math.round(cacheHitPercent)}% cache hit +
+ )} + + )} +
+ ) +} diff --git a/desktop/src/styles.css b/desktop/src/styles.css index dd242f0..4b93cb4 100644 --- a/desktop/src/styles.css +++ b/desktop/src/styles.css @@ -182,6 +182,102 @@ html, body, #root { border-radius: 3px; } +/* ---- collapsible section header ---- */ +.section-header { + display: flex; + align-items: center; + gap: 6px; + width: 100%; + border: 0; + background: transparent; + padding: 0 0 var(--spacing-sm); + cursor: pointer; + color: var(--text-primary); + font-size: 11px; + font-weight: 600; + letter-spacing: 0.2px; + text-transform: uppercase; +} +.section-dot { + width: 5px; height: 5px; border-radius: 50%; + background: var(--brand-accent); + flex-shrink: 0; +} +.section-caption { color: var(--brand-accent); } +.section-columns { + margin-left: auto; + display: flex; + gap: 16px; + font-size: 10px; + font-weight: 500; + text-transform: uppercase; + color: var(--text-tertiary); + letter-spacing: 0.3px; +} +.chevron { + font-size: 10px; + color: var(--text-tertiary); + transition: transform 0.15s ease; + margin-left: 4px; +} +.chevron-open { transform: rotate(90deg); } + +/* ---- models ---- */ +.models-section { + padding: var(--spacing-sm) var(--spacing-lg) var(--spacing-md); + border-top: 1px solid rgba(0, 0, 0, 0.06); +} +.model-row { + display: grid; + grid-template-columns: 56px 1fr auto 36px; + align-items: center; + gap: var(--spacing-sm); + padding: 3px 0; + font-size: 11px; + font-variant-numeric: tabular-nums; +} +.row-bar-container { + height: 6px; + background: rgba(0, 0, 0, 0.06); + border-radius: 3px; + overflow: hidden; +} +.row-bar-fill { + height: 100%; + background: var(--brand-accent); + border-radius: 3px; + min-width: 1px; +} +.row-calls { + color: var(--text-secondary); + text-align: right; + font-size: 10.5px; +} + +/* ---- tokens line ---- */ +.tokens-line { + display: flex; + align-items: center; + gap: 5px; + padding-top: var(--spacing-sm); + margin-top: var(--spacing-xs); + border-top: 1px solid rgba(0, 0, 0, 0.04); + font-size: 10px; +} +.tokens-label { + font-weight: 600; + color: var(--text-secondary); + text-transform: uppercase; + letter-spacing: 0.2px; +} +.tokens-value { + font-family: var(--font-mono); + color: var(--text-secondary); +} +.tokens-sep { + color: var(--text-tertiary); +} + /* ---- findings ---- */ .findings { padding: 0 var(--spacing-lg) var(--spacing-sm); } .findings-cta {