@@ -301,12 +301,12 @@ function ChartFrame({
301301
302302function ActivityHeatmap ( {
303303 points,
304- cellPx ,
304+ height ,
305305 reduced,
306306 tooltip,
307307} : {
308308 points : AdeUsageDailyPoint [ ] ;
309- cellPx : number ;
309+ height : number ;
310310 reduced : boolean ;
311311 tooltip : ReturnType < typeof useDayTooltip > ;
312312} ) {
@@ -316,31 +316,52 @@ function ActivityHeatmap({
316316 return ordered . map ( ( point ) => ( { point, intensity : Math . max ( 0 , Math . min ( 1 , dayValue ( point ) / max ) ) } ) ) ;
317317 } , [ points ] ) ;
318318
319+ // Fill the chart box instead of leaving it mostly blank: a short range lays out
320+ // as one tall row of large cells; longer ranges use a 7-row calendar-style grid
321+ // (columns = weeks) whose cells stretch to fill the available width and height.
322+ const rows = cells . length <= 7 ? 1 : 7 ;
323+ const cols = Math . max ( 1 , Math . ceil ( cells . length / rows ) ) ;
324+
319325 return (
320- < div className = "flex min-h-0 flex-1 flex-col justify-center" >
321- < div
322- className = "grid w-full justify-start gap-[3px]"
323- style = { { gridAutoFlow : "column" , gridTemplateRows : `repeat(7, ${ cellPx } px)` , gridAutoColumns : `${ cellPx } px` } }
324- aria-label = "Daily activity heatmap"
325- >
326- { cells . map ( ( { point, intensity } ) => (
327- < span
328- key = { point . date }
329- className = "rounded-[2px]"
330- style = { {
331- background :
332- intensity === 0
333- ? "color-mix(in srgb, var(--color-fg) 7%, transparent)"
334- : `color-mix(in srgb, ${ HEATMAP_HUE } ${ Math . round ( 24 + intensity * 68 ) } %, var(--color-card))` ,
335- transition : reduced ? undefined : "background 120ms ease" ,
336- cursor : "default" ,
337- } }
338- onPointerEnter = { ( event ) => tooltip . show ( point , event . currentTarget ) }
339- onPointerLeave = { tooltip . hide }
340- onClick = { ( event ) => tooltip . toggle ( point , event . currentTarget ) }
341- />
342- ) ) }
343- </ div >
326+ < div
327+ className = "grid min-h-0 w-full flex-1 gap-[3px]"
328+ style = { {
329+ height,
330+ gridAutoFlow : "column" ,
331+ gridTemplateRows : `repeat(${ rows } , minmax(0, 1fr))` ,
332+ gridTemplateColumns : `repeat(${ cols } , minmax(0, 1fr))` ,
333+ } }
334+ role = "img"
335+ aria-label = "Daily activity heatmap"
336+ data-heatmap-rows = { rows }
337+ >
338+ { cells . map ( ( { point, intensity } ) => (
339+ < span
340+ key = { point . date }
341+ className = "rounded-[2px]"
342+ style = { {
343+ background :
344+ intensity === 0
345+ ? "color-mix(in srgb, var(--color-fg) 7%, transparent)"
346+ : `color-mix(in srgb, ${ HEATMAP_HUE } ${ Math . round ( 24 + intensity * 68 ) } %, var(--color-card))` ,
347+ transition : reduced ? undefined : "background 120ms ease" ,
348+ cursor : "default" ,
349+ } }
350+ onPointerEnter = { ( event ) => tooltip . show ( point , event . currentTarget ) }
351+ onPointerLeave = { tooltip . hide }
352+ onClick = { ( event ) => tooltip . toggle ( point , event . currentTarget ) }
353+ />
354+ ) ) }
355+ </ div >
356+ ) ;
357+ }
358+
359+ /** Muted, centered hint for a tab whose own series is empty while the module
360+ * has data on other tabs (so the global warm-empty state does not apply). */
361+ function TabEmptyHint ( { message } : { message : string } ) {
362+ return (
363+ < div className = "flex min-h-0 flex-1 items-center justify-center text-center text-[11px] text-muted-fg" >
364+ { message }
344365 </ div >
345366 ) ;
346367}
@@ -358,8 +379,12 @@ function TokenBars({
358379} ) {
359380 const max = Math . max ( 1 , ...points . map ( ( p ) => p . totalTokens ) ) ;
360381 const anyCache = points . some ( ( p ) => ( p . cachedTokens ?? 0 ) > 0 ) ;
382+ const anyTokens = points . some ( ( p ) => p . totalTokens > 0 ) ;
361383 return (
362384 < div className = "flex min-h-0 flex-1 flex-col justify-end gap-2" >
385+ { ! anyTokens ? (
386+ < TabEmptyHint message = "No token usage in this range." />
387+ ) : (
363388 < ChartFrame height = { height } ariaLabel = "Token usage by day, split by input, output, and cache" >
364389 { points . map ( ( point ) => {
365390 const total = Math . max ( 0 , point . inputTokens + point . outputTokens + ( point . cachedTokens ?? 0 ) ) ;
@@ -381,6 +406,7 @@ function TokenBars({
381406 ) ;
382407 } ) }
383408 </ ChartFrame >
409+ ) }
384410 < Legend
385411 items = { [
386412 { color : TOKEN_COLORS . input , label : "Input" } ,
@@ -409,8 +435,12 @@ function CodeBars({
409435 ? Math . max ( 1 , ...points . map ( ( p ) => ( p . githubAdditions ?? 0 ) + ( p . githubDeletions ?? 0 ) ) )
410436 : 1 ;
411437 const max = Math . max ( localMax , githubMax ) ;
438+ const anyCode = points . some ( ( p ) => p . insertions + p . deletions > 0 ) || anyGithub ;
412439 return (
413440 < div className = "flex min-h-0 flex-1 flex-col justify-end gap-2" >
441+ { ! anyCode ? (
442+ < TabEmptyHint message = "No code changes in this range." />
443+ ) : (
414444 < ChartFrame height = { height } ariaLabel = "Code changes by day, additions and deletions" >
415445 { points . map ( ( point ) => {
416446 const total = Math . max ( 0 , point . insertions + point . deletions ) ;
@@ -443,6 +473,7 @@ function CodeBars({
443473 ) ;
444474 } ) }
445475 </ ChartFrame >
476+ ) }
446477 < Legend
447478 items = { [
448479 { color : CODE_COLORS . insertions , label : "Added" } ,
@@ -463,6 +494,9 @@ function ClientMix({ stats }: { stats: AdeUsageStats }) {
463494 if ( client === "web" ) return < Globe size = { 12 } /> ;
464495 return < Monitor size = { 12 } /> ;
465496 } ;
497+ if ( clients . length === 0 ) {
498+ return < TabEmptyHint message = "No client activity in this range." /> ;
499+ }
466500 return (
467501 < div className = "flex min-h-0 flex-1 flex-col justify-center gap-3" >
468502 < div className = "flex h-3 overflow-hidden rounded-full" style = { { background : "color-mix(in srgb, var(--color-fg) 6%, transparent)" } } >
@@ -748,7 +782,6 @@ export function ActivityModule({
748782
749783 const compactMode = variant === "compact" ;
750784 const chartHeight = compactMode ? 84 : 132 ;
751- const cellPx = compactMode ? 8 : 11 ;
752785 const maxBars = compactMode ? 40 : 64 ;
753786 const chartPoints = useChartPoints ( stats ?. daily ?? [ ] , maxBars ) ;
754787 const hasActivity = ( stats ?. daily ?? [ ] ) . some ( dayHasActivity ) ;
@@ -770,7 +803,7 @@ export function ActivityModule({
770803 } else if ( ! hasActivity ) {
771804 chart = < WarmEmpty height = { chartHeight } /> ;
772805 } else if ( tab === "activity" ) {
773- chart = < ActivityHeatmap points = { stats . daily } cellPx = { cellPx } reduced = { reduced } tooltip = { tooltip } /> ;
806+ chart = < ActivityHeatmap points = { stats . daily } height = { chartHeight } reduced = { reduced } tooltip = { tooltip } /> ;
774807 } else if ( tab === "tokens" ) {
775808 chart = < TokenBars points = { chartPoints } height = { chartHeight } reduced = { reduced } tooltip = { tooltip } /> ;
776809 } else if ( tab === "code" ) {
0 commit comments