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
17 changes: 10 additions & 7 deletions packages/web/src/components/dashboard/event-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@ function getSinglePreview(event: TimelineEvent): string {
return normalized.slice(0, 80);
}
if (event.isSkillCall && event.skillName) return `Skill: ${event.skillName}`;
if (event.isAgentCall && event.agentType) return `Subagent: ${event.agentType}`;
if (event.isAgentCall && event.agentType)
return `Subagent: ${event.agentType}`;
return event.toolName;
}

Expand Down Expand Up @@ -164,8 +165,9 @@ function RowView({
<button
type="button"
onClick={onClick}
aria-expanded={chevron !== undefined ? chevron === "expanded" : undefined}
className={cn(
"w-full h-full text-left flex items-center gap-3 py-2 border-b border-border/60 transition-colors",
"w-full h-full text-left flex items-center gap-3 py-2 border-b border-border/60 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring",
indented ? "pl-10 pr-3" : "px-3",
isSelected
? "border-l-2 border-l-brand bg-brand-subtle"
Expand Down Expand Up @@ -238,11 +240,12 @@ function Row({
}

const label = row.labelOverride ?? getSingleLabel(row.event);
const preview = row.labelOverride === "Tool"
? row.event.kind === "tool"
? row.event.toolName
: getSinglePreview(row.event)
: getSinglePreview(row.event);
const preview =
row.labelOverride === "Tool"
? row.event.kind === "tool"
? row.event.toolName
: getSinglePreview(row.event)
: getSinglePreview(row.event);

return (
<div style={style} role="listitem">
Expand Down
103 changes: 66 additions & 37 deletions packages/web/src/components/dashboard/overview-stats.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,43 @@
'use client'
"use client";

import { useState } from 'react'
import { cn } from '@/lib/utils'
import { formatTokens, formatCost } from '@/lib/format'
import { useState } from "react";
import { cn } from "@/lib/utils";
import { formatTokens, formatCost } from "@/lib/format";

interface OverviewStatsProps {
periodLabel: string
sessions: number
turns: number
inputTokens: number
outputTokens: number
cacheReadTokens: number
cacheCreationTokens: number
estimatedCostUsd: number
rangeSelector?: React.ReactNode
periodLabel: string;
sessions: number;
turns: number;
inputTokens: number;
outputTokens: number;
cacheReadTokens: number;
cacheCreationTokens: number;
estimatedCostUsd: number;
rangeSelector?: React.ReactNode;
}

interface StatTileProps {
label: string
value: string
valueTone?: 'default' | 'success'
subtext?: string
label: string;
value: string;
valueTone?: "default" | "success";
subtext?: string;
}

function StatTile({ label, value, valueTone = 'default', subtext }: StatTileProps) {
function StatTile({
label,
value,
valueTone = "default",
subtext,
}: StatTileProps) {
return (
<div className="flex flex-col gap-1 rounded-lg bg-card-elevated ring-1 ring-foreground/5 px-4 py-3 min-w-[130px]">
<span className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground">
{label}
</span>
<span
className={cn(
'text-2xl font-semibold leading-tight tabular-nums',
valueTone === 'success' ? 'text-success' : 'text-foreground',
"text-2xl font-semibold leading-tight tabular-nums",
valueTone === "success" ? "text-success" : "text-foreground",
)}
>
{value}
Expand All @@ -41,7 +46,7 @@ function StatTile({ label, value, valueTone = 'default', subtext }: StatTileProp
<span className="text-[10px] text-muted-foreground">{subtext}</span>
)}
</div>
)
);
}

export function OverviewStats({
Expand All @@ -55,7 +60,7 @@ export function OverviewStats({
estimatedCostUsd,
rangeSelector,
}: OverviewStatsProps) {
const [expanded, setExpanded] = useState(false)
const [expanded, setExpanded] = useState(false);

return (
<div className="rounded-xl bg-card ring-1 ring-foreground/10 p-4 sm:p-5">
Expand All @@ -73,7 +78,10 @@ export function OverviewStats({
<StatTile label="Input" value={formatTokens(inputTokens)} />
<StatTile label="Output" value={formatTokens(outputTokens)} />
<StatTile label="Cache Read" value={formatTokens(cacheReadTokens)} />
<StatTile label="Cache Create" value={formatTokens(cacheCreationTokens)} />
<StatTile
label="Cache Create"
value={formatTokens(cacheCreationTokens)}
/>
<StatTile
label="Est. Cost"
value={formatCost(estimatedCostUsd)}
Expand All @@ -84,37 +92,58 @@ export function OverviewStats({

<button
type="button"
onClick={() => setExpanded(v => !v)}
className="mt-4 flex items-center gap-2 text-xs text-muted-foreground hover:text-foreground transition-colors"
onClick={() => setExpanded((v) => !v)}
aria-expanded={expanded}
aria-controls="stats-explanation"
className="mt-4 flex items-center gap-2 text-xs text-muted-foreground hover:text-foreground transition-colors rounded-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
>
<span className={cn('inline-block transition-transform', expanded && 'rotate-90')}>β–Έ</span>
<span className="font-medium text-foreground">What do these numbers mean?</span>
<span>β€” {expanded ? 'click to collapse' : 'click to expand'}</span>
<span
className={cn(
"inline-block transition-transform",
expanded && "rotate-90",
)}
>
β–Έ
</span>
<span className="font-medium text-foreground">
What do these numbers mean?
</span>
<span>β€” {expanded ? "click to collapse" : "click to expand"}</span>
</button>

{expanded && (
<div className="mt-3 text-xs text-muted-foreground space-y-2 leading-relaxed">
<div
id="stats-explanation"
className="mt-3 text-xs text-muted-foreground space-y-2 leading-relaxed"
>
<p>
<span className="font-medium text-foreground">Sessions</span> β€” νŒ€μ›λ“€μ΄ μ‹œμž‘ν•œ Claude Code μ„Έμ…˜ 수.
<span className="font-medium text-foreground">Sessions</span> β€”
νŒ€μ›λ“€μ΄ μ‹œμž‘ν•œ Claude Code μ„Έμ…˜ 수.
</p>
<p>
<span className="font-medium text-foreground">Turns</span> β€” Human β†’ Assistant ν•œ 번의 왕볡(Stop 이벀트 κΈ°μ€€).
<span className="font-medium text-foreground">Turns</span> β€” Human β†’
Assistant ν•œ 번의 왕볡(Stop 이벀트 κΈ°μ€€).
</p>
<p>
<span className="font-medium text-foreground">Input / Output</span> β€” Claude에 보낸 토큰과 Claudeκ°€ μ‘λ‹΅μœΌλ‘œ μ“΄ ν† ν°μ˜ 합계.
<span className="font-medium text-foreground">Input / Output</span>{" "}
β€” Claude에 보낸 토큰과 Claudeκ°€ μ‘λ‹΅μœΌλ‘œ μ“΄ ν† ν°μ˜ 합계.
</p>
<p>
<span className="font-medium text-foreground">Cache Create</span> β€” μž¬μ‚¬μš©μ„ μœ„ν•΄ μ €μž₯된 토큰(예: CLAUDE.md).
ν•œ 번 μ§€λΆˆν•˜λ©΄ 이후 μ½κΈ°λŠ” 훨씬 μ €λ ΄ν•΄μ§‘λ‹ˆλ‹€.
<span className="font-medium text-foreground">Cache Create</span> β€”
μž¬μ‚¬μš©μ„ μœ„ν•΄ μ €μž₯된 토큰(예: CLAUDE.md). ν•œ 번 μ§€λΆˆν•˜λ©΄ 이후 μ½κΈ°λŠ”
훨씬 μ €λ ΄ν•΄μ§‘λ‹ˆλ‹€.
</p>
<p>
<span className="font-medium text-foreground">Cache Read</span> β€” μΊμ‹œμ—μ„œ μž¬μ‚¬μš©λœ 토큰. 일반 input λŒ€λΉ„ μ•½ ~10Γ— μ‹Έλ―€λ‘œ 이 μˆ«μžκ°€ 높은 건 쒋은 μ‹ ν˜Έμž…λ‹ˆλ‹€.
<span className="font-medium text-foreground">Cache Read</span> β€”
μΊμ‹œμ—μ„œ μž¬μ‚¬μš©λœ 토큰. 일반 input λŒ€λΉ„ μ•½ ~10Γ— μ‹Έλ―€λ‘œ 이 μˆ«μžκ°€
높은 건 쒋은 μ‹ ν˜Έμž…λ‹ˆλ‹€.
</p>
<p>
<span className="font-medium text-foreground">Est. Cost</span> β€” λͺ¨λΈλ³„ 곡식 λ‹¨κ°€λ‘œ κ³„μ‚°ν•œ μΆ”μ • 청ꡬ앑 (USD).
<span className="font-medium text-foreground">Est. Cost</span> β€”
λͺ¨λΈλ³„ 곡식 λ‹¨κ°€λ‘œ κ³„μ‚°ν•œ μΆ”μ • 청ꡬ앑 (USD).
</p>
</div>
)}
</div>
)
);
}
Loading