@@ -61,9 +61,9 @@
# meta-line { color : var ( - - subtle ) ; font-size : 11 px ; }
# hero { margin : 14 px 0 6 px ; }
# hero-total { font-size : 5 6px ; font-weight : 700 ; line-height : 1 ; }
# hero-total { font-size : 3 6px ; font-weight : 700 ; line-height : 1 ; }
# hero-total . unit { color : var ( - - clay ) ; }
# hero-total . label { font-size : 18 px ; font-weight : 400 ; color : var ( - - dim ) ; margin-left : 8 px ; }
# hero-total . label { font-size : 14 px ; font-weight : 400 ; color : var ( - - dim ) ; margin-left : 8 px ; }
# hero-split { color : var ( - - dim ) ; margin-top : 8 px ; }
# hero-split b { color : var ( - - term - fg ) ; font-weight : 500 ; }
# hero-split . ok { color : var ( - - green ) ; }
@@ -102,6 +102,42 @@
color : var ( - - dim ) ; margin : 6 px 0 ; }
. callout b , . callout code { color : var ( - - term - fg ) ; }
/* ——— day pills + session gantt ——— */
. days { display : flex ; gap : 8 px ; flex-wrap : wrap ; margin-bottom : 14 px ; }
. dpill { flex : 1 ; min-width : 84 px ; max-width : 140 px ; background : none ;
border : 1 px solid var ( - - subtle ) ; border-radius : 4 px ;
padding : 9 px 6 px ; font : inherit ; color : var ( - - dim ) ;
cursor : pointer ; text-align : center ; }
. dpill : hover { border-color : var ( - - dim ) ; background : var ( - - hover ) ; }
. dpill . dow { font-size : 10 px ; color : var ( - - subtle ) ; display : block ; }
. dpill . date { font-size : 11 px ; color : var ( - - term - fg ) ; font-weight : 500 ;
display : block ; margin : 2 px 0 4 px ; }
. dpill . pct { font-size : 16 px ; font-weight : 700 ; color : var ( - - term - fg ) ; display : block ; }
. dpill . ns { font-size : 10 px ; color : var ( - - subtle ) ; display : block ; margin-top : 2 px ; }
. dpill . heaviest . pct { color : var ( - - clay ) ; }
. dpill . sel { border-color : var ( - - clay ) ; background : rgba ( 217 , 119 , 87 , 0.10 ) ; }
. gantt-hd { display : flex ; justify-content : space-between ; align-items : baseline ;
margin-bottom : 6 px ; }
. gantt-hd . day { color : var ( - - term - fg ) ; font-weight : 500 ; }
. gantt-hd . stats { font-size : 11 px ; color : var ( - - dim ) ; }
. gantt-hd . stats b { color : var ( - - clay ) ; }
. gantt { position : relative ; border-top : 1 px solid var ( - - outline ) ;
border-bottom : 1 px solid var ( - - outline ) ; min-height : 32 px ; }
. lane { position : relative ; height : 16 px ;
border-bottom : 1 px dashed rgba ( 255 , 255 , 255 , 0.04 ) ; }
. seg { position : absolute ; top : 2 px ; height : 12 px ; border-radius : 2 px ;
opacity : .85 ; cursor : crosshair ; }
. seg : hover { opacity : 1 ; outline : 1 px solid var ( - - term - fg ) ; z-index : 2 ; }
. gantt-rule { position : absolute ; top : 0 ; bottom : 0 ; width : 0 ;
border-left : 1 px dashed var ( - - subtle ) ; opacity : .4 ;
pointer-events : none ; }
. gantt-axis { display : flex ; justify-content : space-between ;
font-size : 10 px ; color : var ( - - subtle ) ; padding : 4 px 0 ; }
. gantt-leg { font-size : 10 px ; color : var ( - - subtle ) ; margin-top : 8 px ;
display : flex ; gap : 14 px ; flex-wrap : wrap ; }
. gantt-leg . sw { display : inline-block ; width : 14 px ; height : 10 px ;
border-radius : 2 px ; vertical-align : middle ; margin-right : 4 px ; }
/* ——— block-char bars ——— */
. bar { display : grid ; grid-template-columns : 26 ch 1 fr 8 ch ; gap : 14 px ;
padding : 2 px 0 ; align-items : center ; }
@@ -177,7 +213,7 @@
@ media ( max-width : 760px ) {
body { padding : 20 px 12 px 48 px ; }
. term-body { padding : 16 px 16 px 24 px ; }
# hero-total { font-size : 40 px ; }
# hero-total { font-size : 28 px ; }
. bar { grid-template-columns : 14 ch 1 fr 7 ch ; gap : 8 px ; }
. drill summary { grid-template-columns : 6 ch 1 fr ; }
. drill . body { padding-left : 12 px ; }
@@ -231,6 +267,21 @@
< div class = "section-body" id = "project-bars" > < / div >
< / section >
< section id = "timeline-section" >
< div class = "hr" > < / div >
< h2 > session timeline by day< span class = "hint" > click a day · ←/→ to navigate< / span > < / h2 >
< div class = "section-body" >
< div class = "days" id = "day-pills" > < / div >
< div class = "gantt-hd" >
< span class = "day" id = "g-day" > —< / span >
< span class = "stats" id = "g-stats" > < / span >
< / div >
< div class = "gantt-axis" > < span > 00:00< / span > < span > 06:00< / span > < span > 12:00< / span > < span > 18:00< / span > < span > 24:00< / span > < / div >
< div class = "gantt" id = "gantt" > < / div >
< div class = "gantt-leg" id = "gantt-leg" > < / div >
< / div >
< / section >
< section >
< div class = "hr" > < / div >
< h2 > most expensive prompts< span class = "hint" > click to expand context< / span > < / h2 >
@@ -294,6 +345,7 @@
const esc = s => String ( s ) . replace ( /[&<>"]/g , c =>
( { '&' : '&' , '<' : '<' , '>' : '>' , '"' : '"' } [ c ] ) ) ;
const short = p => String ( p || '' ) . replace ( /^-Users-[^-]+-/ , '' ) . replace ( /^code-/ , '' ) ;
const sid = s => String ( s || '' ) . slice ( 0 , 8 ) ;
const MON = [ 'Jan' , 'Feb' , 'Mar' , 'Apr' , 'May' , 'Jun' , 'Jul' , 'Aug' , 'Sep' , 'Oct' , 'Nov' , 'Dec' ] ;
const niceDate = iso => { const d = new Date ( iso ) ; return isNaN ( d ) ? '' :
` ${ MON [ d . getMonth ( ) ] } ${ d . getDate ( ) } · ${ String ( d . getHours ( ) ) . padStart ( 2 , '0' ) } : ${ String ( d . getMinutes ( ) ) . padStart ( 2 , '0' ) } ` ; } ;
@@ -308,8 +360,7 @@
$ ( 'cmd-flags' ) . innerHTML = DATA . since
? ` <span class="flag">--since</span> ${ esc ( DATA . since ) } ` : '' ;
$ ( 'meta-line' ) . textContent =
( span ? ` ${ span . from . slice ( 0 , 10 ) } → ${ span . to . slice ( 0 , 10 ) } ` : '' ) +
` · ${ DATA . root || '' } ` ;
span ? ` ${ span . from . slice ( 0 , 10 ) } → ${ span . to . slice ( 0 , 10 ) } ` : '' ;
$ ( 'foot-gen' ) . textContent = ` generated ${ DATA . generated _at ? . slice ( 0 , 16 ) . replace ( 'T' , ' ' ) || '' } ` ;
$ ( 'foot-stats' ) . textContent =
` ${ o . sessions } sessions · ${ o . api _calls } api calls · ${ o . human _messages } prompts ` ;
@@ -319,8 +370,9 @@
` ${ m ? m [ 1 ] : num } <span class="unit"> ${ m ? m [ 2 ] : '' } </span><span class="label">tokens</span> ` ;
const cacheCls = it . pct _cached >= 85 ? 'ok' : 'warn' ;
$ ( 'hero-split' ) . innerHTML =
` <b> ${ fm t( it . total ) } </b> input <span class=" ${ cacheCls } ">( ${ it . pct _cached } % cache-read)</span> · ` +
` <b> ${ fmt ( o . output _tokens ) } </b> output · all figures below are % of this total ` ;
` <b> ${ pc t( it . total , GRAND )} </b> input · ` +
` <span class=" ${ cacheCls } "><b> ${ it . pct _cached } % </b> of input cached</span> · ` +
` <b> ${ pct ( o . output _tokens , GRAND ) } </b> output ` ;
// overall stat grid
$ ( 'overall-grid' ) . innerHTML = [
@@ -335,6 +387,65 @@
` <div class="val"> ${ typeof v === 'number' && v >= 1e4 ? fmt ( v ) : v } </div> ` +
( d ? ` <div class="detail"> ${ d } </div> ` : '' ) + ` </div> ` ) . join ( '' ) ;
// session timeline by day
( function ( ) {
const days = ( DATA . by _day || [ ] ) . slice ( - 14 ) ;
if ( ! days . length ) { $ ( 'timeline-section' ) . style . display = 'none' ; return ; }
const PCOL = [ 'rgb(177,185,249)' , 'rgb(78,186,101)' , '#D97757' , 'rgb(255,193,7)' ,
'rgb(255,107,128)' , '#9b8cff' , '#6ec1d6' , '#c792ea' ] ;
const dayTotal = days . reduce ( ( a , d ) => a + d . tokens , 0 ) || 1 ;
const tokMax = Math . max ( ... days . map ( d => d . tokens ) ) ;
const projects = [ ... new Set ( days . flatMap ( d => d . sessions . map ( s => s . project ) ) ) ] ;
const colorOf = p => PCOL [ projects . indexOf ( p ) % PCOL . length ] ;
const hhmm = m => ( m >= 1440 ? ` + ${ Math . floor ( m / 1440 ) } d ` : '' ) +
` ${ String ( Math . floor ( m / 60 ) % 24 ) . padStart ( 2 , '0' ) } : ${ String ( m % 60 ) . padStart ( 2 , '0' ) } ` ;
const md = iso => { const [ , mo , da ] = iso . split ( '-' ) ; return ` ${ MON [ + mo - 1 ] } ${ + da } ` ; } ;
let sel = days . findIndex ( d => d . tokens === tokMax ) ;
function pills ( ) {
$ ( 'day-pills' ) . innerHTML = days . map ( ( d , i ) =>
` <button class="dpill ${ d . tokens === tokMax ? ' heaviest' : '' } ${ i === sel ? ' sel' : '' } " data-i=" ${ i } "> ` +
` <span class="dow"> ${ esc ( d . dow ) } </span> ` +
` <span class="date"> ${ esc ( md ( d . date ) ) } </span> ` +
` <span class="pct"> ${ ( 100 * d . tokens / dayTotal ) . toFixed ( 1 ) } %</span> ` +
` <span class="ns"> ${ d . sessions . length } sess</span></button> `
) . join ( '' ) ;
$ ( 'day-pills' ) . querySelectorAll ( '.dpill' ) . forEach ( el =>
el . onclick = ( ) => { sel = + el . dataset . i ; pills ( ) ; gantt ( ) ; } ) ;
}
function gantt ( ) {
const d = days [ sel ] , DAY = 1440 ;
$ ( 'g-day' ) . textContent = ` ${ d . dow } ${ md ( d . date ) } ` ;
$ ( 'g-stats' ) . innerHTML = ` ${ d . sessions . length } sessions · ${ fmt ( d . tokens ) } tokens ` +
` · peak <b> ${ d . peak } </b> concurrent at <b> ${ hhmm ( d . peak _at _min ) } </b> ` ;
const lanes = [ ] ;
for ( const s of d . sessions ) {
let placed = false ;
for ( const L of lanes ) if ( L [ L . length - 1 ] . end _min <= s . start _min ) { L . push ( s ) ; placed = true ; break ; }
if ( ! placed ) lanes . push ( [ s ] ) ;
}
let h = '' ;
for ( let t = 0 ; t <= 24 ; t += 6 ) h += ` <div class="gantt-rule" style="left: ${ 100 * t / 24 } %"></div> ` ;
h += lanes . map ( L => ` <div class="lane"> ${ L . map ( s => {
const end = Math . min ( s . end _min , DAY ) ;
const w = Math . max ( 0.15 , 100 * ( end - s . start _min ) / DAY ) ;
const tip = ` folder: ${ short ( s . project ) } \n ` +
` ${ hhmm ( s . start _min ) } – ${ hhmm ( s . end _min ) } · ${ fmt ( s . tokens ) } tokens \n ` +
` session ${ s . id } ` ;
return ` <span class="seg" style="left: ${ 100 * s . start _min / DAY } %;width: ${ w } %; ` +
` background: ${ colorOf ( s . project ) } " title=" ${ esc ( tip ) } "></span> ` ;
} ).join('')}</div> ` ) . join ( '' ) ;
$ ( 'gantt' ) . innerHTML = h || '<div class="callout">no sessions</div>' ;
}
document . addEventListener ( 'keydown' , e => {
if ( e . key === 'ArrowRight' && sel < days . length - 1 ) { sel ++ ; pills ( ) ; gantt ( ) ; e . preventDefault ( ) ; }
if ( e . key === 'ArrowLeft' && sel > 0 ) { sel -- ; pills ( ) ; gantt ( ) ; e . preventDefault ( ) ; }
} ) ;
$ ( 'gantt-leg' ) . innerHTML = projects . slice ( 0 , 12 ) . map ( p =>
` <span><span class="sw" style="background: ${ colorOf ( p ) } "></span> ${ esc ( short ( p ) ) } </span> ` ) . join ( '' ) ;
pills ( ) ; gantt ( ) ;
} ) ( ) ;
// block-char project bars
( function ( ) {
const W = 48 ;
@@ -366,57 +477,54 @@
return h + '</div>' ;
}
// top prompts — share of grand total
( function ( ) {
const ps = ( DATA . top _prompts || [ ] ) . slice ( 0 , 100 ) ;
// expandable drill-down list with "show N more" toggle
function drillList ( hostId , items , rowFn , empty ) {
const SHOW = 5 ;
const row = p => {
const inTot = p . input . uncached + p . input . cache _create + p . input . cache _read ;
return ` <details><summary> ` +
` <span class="amt"> ${ share ( p . total _tokens ) } </span> ` +
` <span class="desc"> ${ esc ( p . text ) } </span> ` +
` <span class="meta"> ${ niceDate ( p . ts ) } · ${ esc ( short ( p . project ) ) } · ${ p . api _calls } calls ` +
( p . subagent _calls ? ` · ${ p . subagent _calls } subagents ` : '' ) +
` · ${ pct ( p . input . cache _read , inTot ) } cached</span> ` +
` </summary><div class="body"> ` +
renderContext ( p . context ) +
` <div>session <code> ${ esc ( p . session ) } </code></div> ` +
` <div>in: uncached ${ fmt ( p . input . uncached ) } · cache-create ${ fmt ( p . input . cache _create ) } · ` +
` cache-read ${ fmt ( p . input . cache _read ) } · out ${ fmt ( p . output ) } </div> ` +
` </div></details> ` ;
} ;
const head = ps . slice ( 0 , SHOW ) . map ( row ) . join ( '' ) ;
const rest = ps . slice ( SHOW ) . map ( row ) . join ( '' ) ;
$ ( 'top-prompts' ) . innerHTML = ps . length
? head + ( rest
? ` <div id="tp-rest" hidden> ${ rest } </div> ` +
` <button id="tp-more" class="more-btn">show ${ ps . length - SHOW } more</button> `
: '' )
: '<div class="callout">No prompts in range.</div>' ;
const btn = $ ( 'tp-more' ) ;
const host = $ ( hostId ) ;
if ( ! items . length ) { host . innerHTML = ` <div class="callout"> ${ empty } </div> ` ; return ; }
const head = items . slice ( 0 , SHOW ) . map ( rowFn ) . join ( '' ) ;
const rest = items . slice ( SHOW ) . map ( rowFn ) . join ( '' ) ;
host . innerHTML = head + ( rest
? ` <div hidden> ${ rest } </div><button class="more-btn">show ${ items . length - SHOW } more</button> `
: '' ) ;
const btn = host . querySelector ( '.more-btn' ) ;
if ( btn ) btn . onclick = ( ) => {
const r = $ ( 'tp-rest' ) ; r . hidden = ! r . hidden ;
btn . textContent = r . hidden ? ` show ${ p s. length - SHOW } more ` : 'show less' ;
const r = btn . previousElementSibling ; r . hidden = ! r . hidden ;
btn . textContent = r . hidden ? ` show ${ item s. length - SHOW } more ` : 'show less' ;
} ;
} ) ( ) ;
}
// cache breaks
( function ( ) {
const bs = ( DATA . cache _breaks || [ ] ) . slice ( 0 , 100 ) ;
$ ( 'cache-breaks' ) . innerHTML = bs . map ( b =>
` <details><summary > ` +
` <span class="amt "> ${ fmt ( b . uncached ) } </span> ` +
` <span class="desc" >${ esc ( short ( b . project ) ) } · ` +
` ${ b . kind === 'subagent' ? esc ( b . agentType || ' subagent' ) : 'main' } </span> ` +
` <span class="meta"> ${ niceDate ( b . ts ) } · ${ pct ( b . un cached , b . total ) } of ${ fmt ( b . total ) } un cached</span> ` +
drillList ( 'top-prompts' , ( DATA . top _prompts || [ ] ) . slice ( 0 , 100 ) , p => {
const inTot = p . input . uncached + p . input . cache _create + p . input . cache _read ;
return ` <details><summary> ` +
` <span class="amt"> ${ share ( p . total _tokens ) } </span> ` +
` <span class="desc"> ${ esc ( p . text ) } </span > ` +
` <span class="meta "> ${ niceDate ( p . ts ) } · ${ esc ( short ( p . project ) ) } · ` +
` sess <code >${ esc ( sid ( p . session ) ) } </code> · ${ p . api _calls } calls ` +
( p . subagent _calls ? ` · ${ p . sub agent_calls } subagents ` : '' ) +
` · ${ pct ( p . input . cache_read , inTot ) } cached</span> ` +
` </summary><div class="body"> ` +
renderContext ( b . context ,
` <div class="ctx-break"><b > ${ fmt ( b . uncached ) } </b> uncached ` +
` ( ${ pc t( b . uncached , b . total ) } of ${ fmt ( b . total ) } ) — cache break here</div> ` ) +
` <div>session <code> ${ esc ( b . session ) } </code></ div> ` +
` </div></details> `
) . join ( '' ) || '<div class="callout">No cache breaks over threshold.</div>' ;
} ) ( ) ;
renderContext ( p . context ) +
` <div>session <code > ${ esc ( p . session ) } </code></div> ` +
` <div>in: uncached ${ fm t( p . input . uncached ) } · cache-create ${ fmt ( p . input . cache _create ) } · ` +
` cache-read ${ fmt ( p . input . cache _read ) } · out ${ fmt ( p . output ) } </div> ` +
` </div></details> ` ;
} , 'No prompts in range.' ) ;
drillList ( 'cache-breaks' , ( DATA . cache _breaks || [ ] ) . slice ( 0 , 100 ) , b =>
` <details><summary> ` +
` <span class="amt"> ${ fmt ( b . uncached ) } </span> ` +
` <span class="desc"> ${ esc ( short ( b . project ) ) } · ` +
` ${ b . kind === 'subagent' ? esc ( b . agentType || 'subagent' ) : 'main' } </span> ` +
` <span class="meta"> ${ niceDate ( b . ts ) } · sess <code> ${ esc ( sid ( b . session ) ) } </code> · ` +
` ${ pct ( b . uncached , b . total ) } of ${ fmt ( b . total ) } uncached</span> ` +
` </summary><div class="body"> ` +
renderContext ( b . context ,
` <div class="ctx-break"><b> ${ fmt ( b . uncached ) } </b> uncached ` +
` ( ${ pct ( b . uncached , b . total ) } of ${ fmt ( b . total ) } ) — cache break here</div> ` ) +
` <div>session <code> ${ esc ( b . session ) } </code></div> ` +
` </div></details> ` ,
'No cache breaks over threshold.' ) ;
// sortable table
function table ( el , cols , rows ) {
@@ -457,7 +565,8 @@
] ;
table ( $ ( 'tbl-projects' ) , statCols , statRows ( DATA . by _project ) ) ;
table ( $ ( 'tbl-subagents' ) , statCols , statRows ( DATA . by _subagent _type ) ) ;
table ( $ ( 'tbl-skills' ) , statCols , statRows ( DATA . by _skill ) ) ;
table ( $ ( 'tbl-skills' ) , statCols . slice ( 0 , 8 ) ,
statRows ( DATA . by _skill ) . map ( r => r . slice ( 0 , 8 ) ) ) ;
} ) ( ) ;
< / script >
< / body >