diff --git a/entrypoints/sidepanel/components/ActiveConversation.tsx b/entrypoints/sidepanel/components/ActiveConversation.tsx index b3b8bb0..6e1e137 100644 --- a/entrypoints/sidepanel/components/ActiveConversation.tsx +++ b/entrypoints/sidepanel/components/ActiveConversation.tsx @@ -79,7 +79,7 @@ export default function ActiveConversation({ conv, health }: Props) {
{conv.turnCount} turn{conv.turnCount === 1 ? '' : 's'} - {formatTokens(conv.totalInputTokens + conv.totalOutputTokens)} tokens + {formatTokens(conv.totalInputTokens + conv.totalOutputTokens)} tok {showDelta ? {totalDelta.toFixed(1)}% of session : {formatCost(conv.estimatedCost)} diff --git a/entrypoints/sidepanel/components/TodayCard.tsx b/entrypoints/sidepanel/components/TodayCard.tsx index bfc159d..8ac0cae 100644 --- a/entrypoints/sidepanel/components/TodayCard.tsx +++ b/entrypoints/sidepanel/components/TodayCard.tsx @@ -1,5 +1,5 @@ // entrypoints/sidepanel/components/TodayCard.tsx -// Today's aggregate stats in a 2x2 grid. +// Today's aggregate stats as a single dense row matching overlay typography. import React from 'react'; import type { DailySummary } from '../../../lib/conversation-store'; @@ -18,22 +18,10 @@ export default function TodayCard({ summary }: Props) { return (
-
- {conversations} - Conversations -
-
- {turns} - Turns -
-
- {formatTokens(tokens)} - Tokens -
-
- {formatCost(cost)} - Cost -
+ today + + {conversations} conv · {turns} turn{turns !== 1 ? 's' : ''} · {formatTokens(tokens)} tok · {formatCost(cost)} +
); } diff --git a/entrypoints/sidepanel/dashboard.css b/entrypoints/sidepanel/dashboard.css index 6522690..7a5b110 100644 --- a/entrypoints/sidepanel/dashboard.css +++ b/entrypoints/sidepanel/dashboard.css @@ -50,7 +50,7 @@ body { font-family: var(--lco-font); - font-size: 13px; + font-size: 11px; line-height: 1.5; color: var(--lco-text); background: var(--lco-bg); @@ -64,6 +64,12 @@ body { max-width: 400px; margin: 0 auto; padding: 16px 12px; + /* Flex column + min-height keeps the feedback widget pinned to the viewport + bottom when sections are collapsed. When History is tall, the panel scrolls + and the widget sits naturally at the end of content (no overlap, no stickiness). */ + min-height: 100vh; + display: flex; + flex-direction: column; } .lco-dash-loading { @@ -208,66 +214,41 @@ body { /* ── Today card ─────────────────────────────────────────────────────────────── */ .lco-dash-today { - display: grid; - grid-template-columns: 1fr 1fr; + display: flex; + align-items: baseline; gap: 8px; + padding: 2px 0; } .lco-dash-today--empty { opacity: 0.5; } -.lco-dash-metric { - background: var(--lco-bg-card); - border: 1px solid var(--lco-border); - border-radius: var(--lco-radius); - padding: 12px; - display: flex; - flex-direction: column; - gap: 2px; - transition: transform 0.15s ease, box-shadow 0.15s ease; -} - -.lco-dash-metric:hover { - transform: translateY(-1px); - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); -} - -@media (prefers-color-scheme: dark) { - .lco-dash-metric:hover { - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25); - } -} - -.lco-dash-metric-value { - font-size: 20px; - font-weight: 700; - font-family: var(--lco-font-mono); - letter-spacing: -0.02em; - color: var(--lco-text); +.lco-dash-today-label { + font-size: 10px; + color: var(--lco-text-muted); + flex-shrink: 0; } -.lco-dash-metric-label { +.lco-dash-today-stats { font-size: 11px; color: var(--lco-text-secondary); - text-transform: uppercase; - letter-spacing: 0.04em; + font-variant-numeric: tabular-nums; } /* ── Active conversation ────────────────────────────────────────────────────── */ .lco-dash-active { - background: var(--lco-bg-card); - border: 1px solid var(--lco-border); - border-radius: var(--lco-radius); - padding: 12px; + padding: 8px 0; /* GPU-accelerated: only transform + opacity for 60/120fps compositing. */ will-change: transform, opacity; transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.25s ease; } .lco-dash-active--empty { - border-style: dashed; + border: 1px dashed var(--lco-border); + border-radius: var(--lco-radius); + padding: 8px; } .lco-dash-active--hidden { @@ -296,8 +277,8 @@ body { } .lco-dash-health-dot { - width: 8px; - height: 8px; + width: 6px; + height: 6px; border-radius: 50%; flex-shrink: 0; /* No transition: health state changes snap instantly (rare, not per-frame). */ @@ -305,16 +286,16 @@ body { .lco-dash-health-dot--healthy { background: var(--lco-health-green); box-shadow: 0 0 4px rgba(76, 175, 80, 0.4); } .lco-dash-health-dot--degrading { background: var(--lco-health-yellow); box-shadow: 0 0 4px rgba(245, 166, 35, 0.4); } -.lco-dash-health-dot--critical { background: var(--lco-health-red); box-shadow: 0 0 4px rgba(229, 57, 53, 0.4); } +.lco-dash-health-dot--critical { background: var(--lco-health-red); animation: lco-dot-pulse 2s ease-in-out infinite; } .lco-dash-health-label { - font-size: 12px; + font-size: 10px; font-weight: 500; color: var(--lco-text-secondary); } .lco-dash-active-subject { - font-size: 14px; + font-size: 12px; font-weight: 500; color: var(--lco-text); margin-bottom: 10px; @@ -330,9 +311,9 @@ body { .lco-dash-context-bar { flex: 1; - height: 6px; + height: 4px; background: var(--lco-context-track); - border-radius: 3px; + border-radius: 2px; overflow: hidden; } @@ -360,9 +341,15 @@ body { .lco-dash-active-stats { display: flex; - gap: 12px; - font-size: 12px; + font-size: 11px; color: var(--lco-text-secondary); + font-variant-numeric: tabular-nums; +} + +/* Dot separators between stat spans; avoids hardcoding · in JSX. */ +.lco-dash-active-stats > span + span::before { + content: ' · '; + color: var(--lco-text-muted); } /* ── Non-Claude tab banner ──────────────────────────────────────────────────── */ @@ -418,14 +405,14 @@ body { .lco-dash-budget-dot--critical { background: var(--lco-health-red); box-shadow: 0 0 4px rgba(229, 57, 53, 0.4); } .lco-dash-budget-zone-label { - font-size: 12px; + font-size: 10px; font-weight: 500; color: var(--lco-text-secondary); } /* Primary status line: "11% used; resets in 53 min" */ .lco-dash-budget-status { - font-size: 13px; + font-size: 11px; font-weight: 500; color: var(--lco-text); margin-bottom: 10px; @@ -475,6 +462,7 @@ body { font-size: 11px; color: var(--lco-text-muted); font-family: var(--lco-font-mono); + font-variant-numeric: tabular-nums; width: 28px; /* Fixed width so the percentage numbers don't shift layout */ text-align: right; flex-shrink: 0; @@ -532,7 +520,7 @@ body { } .lco-dash-conv-subject { - font-size: 13px; + font-size: 12px; font-weight: 500; color: var(--lco-text); flex: 1; @@ -568,6 +556,7 @@ body { .lco-dash-conv-turns, .lco-dash-conv-cost { font-family: var(--lco-font-mono); + font-variant-numeric: tabular-nums; font-size: 11px; } @@ -576,7 +565,10 @@ body { /* Open: textarea + cancel/send. Sent: green confirmation, auto-resets in 4s. */ .lco-dash-feedback { - margin-top: 12px; + /* margin-top: auto fills available flex space, pushing the widget to the + viewport bottom when content is short. Collapses to 0 when content fills + the panel, so History (if expanded) still has border-top separation below it. */ + margin-top: auto; padding-top: 12px; border-top: 1px solid var(--lco-border); padding-bottom: 4px; @@ -699,6 +691,11 @@ body { /* ── Animations ─────────────────────────────────────────────────────────────── */ +@keyframes lco-dot-pulse { + 0%, 100% { box-shadow: 0 0 4px rgba(229, 57, 53, 0.4); } + 50% { box-shadow: 0 0 10px rgba(229, 57, 53, 0.7); } +} + @keyframes lco-dash-slide-in { from { opacity: 0; @@ -727,8 +724,8 @@ body { .lco-dash-budget-fill { transition: none; } - .lco-dash-metric { - transition: none; + .lco-dash-health-dot--critical { + animation: none; } .lco-dash-skeleton { animation: none;