Skip to content
Merged
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
39 changes: 28 additions & 11 deletions src/lib/dashboard/client.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -791,27 +791,42 @@ export const JS = `
return out;
}

// One chip per HOST, not per sourceHealth field: Codex carries two fields
// One pill per HOST, not per sourceHealth field: Codex carries two fields
// (its transcript root, plus the thread ledger that corrects subagent-replay
// token inflation) but is still one host, so the worse of the two drives the
// chip's status and both are visible in its detail text.
// pill's status and both stay reachable via the status side's tooltip.
var SOURCE_HEALTH_GROUPS=[
{label:"Claude",title:"~/.claude/projects/**/*.jsonl",parts:[{key:"claude"}]},
{label:"Codex",title:"~/.codex/sessions/**/rollout-*.jsonl, plus Codex's own thread ledger",
{host:"claude",label:"Claude",title:"Claude Code — ~/.claude/projects/**/*.jsonl",parts:[{key:"claude"}]},
{host:"codex",label:"Codex",title:"Codex — ~/.codex/sessions/**/rollout-*.jsonl, plus Codex's own thread ledger",
parts:[{key:"codex",sub:"transcripts"},{key:"codexLedger",sub:"ledger"}]},
{label:"OpenCode",title:"OpenCode's SQLite session store",parts:[{key:"opencode"}]}
{host:"opencode",label:"OpenCode",title:"OpenCode — its SQLite session store",parts:[{key:"opencode"}]}
];
var SOURCE_HEALTH_RANK={degraded:0,"not-read":1,absent:2,ok:3}; // lower sorts first = worse
function sourceHealthRank(status){
var r=SOURCE_HEALTH_RANK[status];
return r===undefined?1:r;
}
// Same brand marks as the Observability Live view's hostIcon() (live/client.mjs)
// — reused verbatim so a host reads as the same glyph everywhere in the
// dashboard, not a second, subtly different icon set.
function sourceHostIcon(host){
var view="0 0 16 16",body;
if(host==="claude")body='<path d="M8 1.5v13M1.5 8h13M3.4 3.4l9.2 9.2M12.6 3.4l-9.2 9.2"/>';
else if(host==="codex"){
view="146 227 268 267";
body='<path d="M249.176 323.434V298.276C249.176 296.158 249.971 294.569 251.825 293.509L302.406 264.381C309.29 260.409 317.5 258.555 325.973 258.555C357.75 258.555 377.877 283.185 377.877 309.399C377.877 311.253 377.877 313.371 377.611 315.49L325.178 284.771C322.001 282.919 318.822 282.919 315.645 284.771L249.176 323.434ZM367.283 421.415V361.301C367.283 357.592 365.694 354.945 362.516 353.092L296.048 314.43L317.763 301.982C319.617 300.925 321.206 300.925 323.058 301.982L373.639 331.112C388.205 339.586 398.003 357.592 398.003 375.069C398.003 395.195 386.087 413.733 367.283 421.412V421.415ZM233.553 368.452L211.838 355.742C209.986 354.684 209.19 353.095 209.19 350.975V292.718C209.19 264.383 230.905 242.932 260.301 242.932C271.423 242.932 281.748 246.641 290.49 253.26L238.321 283.449C235.146 285.303 233.555 287.951 233.555 291.659V368.455L233.553 368.452ZM280.292 395.462L249.176 377.985V340.913L280.292 323.436L311.407 340.913V377.985L280.292 395.462ZM300.286 475.968C289.163 475.968 278.837 472.259 270.097 465.64L322.264 435.449C325.441 433.597 327.03 430.949 327.03 427.239V350.445L349.011 363.155C350.865 364.213 351.66 365.802 351.66 367.922V426.179C351.66 454.514 329.679 475.965 300.286 475.965V475.968ZM237.525 416.915L186.944 387.785C172.378 379.31 162.582 361.305 162.582 343.827C162.582 323.436 174.763 305.164 193.563 297.485V357.861C193.563 361.571 195.154 364.217 198.33 366.071L264.535 404.467L242.82 416.915C240.967 417.972 239.377 417.972 237.525 416.915ZM234.614 460.343C204.689 460.343 182.71 437.833 182.71 410.028C182.71 407.91 182.976 405.792 183.238 403.672L235.405 433.863C238.582 435.715 241.763 435.715 244.938 433.863L311.407 395.466V420.622C311.407 422.742 310.612 424.331 308.758 425.389L258.179 454.519C251.293 458.491 243.083 460.343 234.611 460.343H234.614ZM300.286 491.854C332.329 491.854 359.073 469.082 365.167 438.892C394.825 431.211 413.892 403.406 413.892 375.073C413.892 356.535 405.948 338.529 391.648 325.552C392.972 319.991 393.766 314.43 393.766 308.87C393.766 271.003 363.048 242.666 327.562 242.666C320.413 242.666 313.528 243.723 306.644 246.109C294.725 234.457 278.307 227.042 260.301 227.042C228.258 227.042 201.513 249.815 195.42 280.004C165.761 287.685 146.694 315.49 146.694 343.824C146.694 362.362 154.638 380.368 168.938 393.344C167.613 398.906 166.819 404.467 166.819 410.027C166.819 447.894 197.538 476.231 233.024 476.231C240.172 476.231 247.058 475.173 253.943 472.788C265.859 484.441 282.278 491.854 300.286 491.854Z"/>';
} else if(host==="opencode"){
view="0 0 300 300";
body='<g transform="translate(30 0)"><path d="M180 240H60V120H180V240Z" fill="#4B4646"/><path d="M180 60H60V240H180V60ZM240 300H0V0H240V300Z" fill="#F1ECEC" fill-rule="evenodd"/></g>';
} else body='<path d="M3 8h10M8 3v10"/>';
return'<svg class="live-host-icon" viewBox="'+view+'" focusable="false" aria-hidden="true" data-host="'+esc(host)+'">'+body+'</svg>';
}

function renderSourceHealth(health){
var el=document.getElementById("u-source-health");
if(!el)return;
health=health||{};
var chips=[];
var pills=[];
for(var g=0; g<SOURCE_HEALTH_GROUPS.length; g++){
var grp=SOURCE_HEALTH_GROUPS[g],present=[];
for(var p=0; p<grp.parts.length; p++){
Expand All @@ -822,15 +837,17 @@ export const JS = `
var lead=present.slice().sort(function(a,b){
return sourceHealthRank(a.status)-sourceHealthRank(b.status);
})[0];
var detail=present.map(function(pt){
var detail=grp.label+": "+present.map(function(pt){
var d=pt.status+(pt.reason?" · "+pt.reason:"");
return pt.sub?pt.sub+": "+d:d;
}).join(" · ");
chips.push('<span class="source-chip" data-status="'+esc(lead.status)+'" title="'+esc(grp.title)+'">'
+esc(grp.label)+": "+esc(detail)+"</span>");
pills.push('<span class="source-pill" data-status="'+esc(lead.status)+'">'
+'<span class="sp-icon live-host" data-host="'+esc(grp.host)+'" title="'+esc(grp.title)+'">'+sourceHostIcon(grp.host)+'</span>'
+'<span class="sp-status" title="'+esc(detail)+'">'+esc(lead.status)+'</span>'
+'</span>');
}
el.hidden=chips.length===0;
el.innerHTML=chips.length?'<span class="source-label">local sources</span>'+chips.join(""):"";
el.hidden=pills.length===0;
el.innerHTML=pills.join("");
}

function loadUsage(force){
Expand Down
2 changes: 1 addition & 1 deletion src/lib/dashboard/page.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ export function renderPage({ name, version }) {
<button class="seg-btn" role="tab" id="tab-usage" data-tab="usage" aria-selected="false" aria-controls="panel-usage" type="button">Usage</button>
<button class="seg-btn" role="tab" id="tab-observability" data-tab="observability" aria-selected="false" aria-controls="panel-observability" type="button">Observability</button>
</div>
<div class="source-health" id="u-source-health" role="status" aria-live="polite" hidden></div>
</nav>

<div class="secondary-shell">
Expand Down Expand Up @@ -270,7 +271,6 @@ export function renderPage({ name, version }) {
<h2 id="usage-view-title">Usage scorecard</h2>
<p id="usage-view-description">Token consumption, API-equivalent cost, efficiency, and trends.</p>
</header>
<div class="source-health" id="u-source-health" role="status" aria-live="polite" hidden></div>

<section class="view" id="v-score">
<div class="hero" id="u-hero"></div>
Expand Down
33 changes: 22 additions & 11 deletions src/lib/dashboard/styles.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ body.gated .band,body.gated .tabbar,body.gated main{display:none}
/* ── sticky frosted segmented control ── */
.tabbar{
position:sticky; top:0; z-index:20;
display:flex; padding:8px clamp(16px,4vw,40px) 10px;
display:flex; align-items:center; gap:12px; padding:8px clamp(16px,4vw,40px) 10px;
background:var(--material);
-webkit-backdrop-filter:saturate(180%) blur(20px);
backdrop-filter:saturate(180%) blur(20px);
Expand Down Expand Up @@ -407,19 +407,30 @@ body.gated .band,body.gated .tabbar,body.gated main{display:none}
}
.note b{color:var(--ink)}
.note .i{color:var(--accent); font-weight:700}
.source-health{
display:flex; flex-wrap:wrap; align-items:center; gap:7px; margin:-2px 0 16px;
/* local-source pills — sit right-aligned in the sticky tabbar, one per host */
.tabbar .source-health{
display:flex; align-items:stretch; gap:6px; margin-left:auto;
color:var(--ink-2); font-size:11.5px;
}
.source-health[hidden]{display:none}
.source-health .source-label{font-weight:600; color:var(--ink-dim); margin-right:2px}
.source-chip{
border:1px solid var(--line); border-radius:999px; padding:4px 9px;
background:var(--panel); color:var(--ink-2);
}
.source-chip[data-status="ok"]{border-color:var(--ok); color:var(--ok)}
.source-chip[data-status="degraded"]{border-color:var(--warn); color:var(--warn); background:color-mix(in srgb,var(--warn) 10%,transparent)}
.source-chip[data-status="absent"],.source-chip[data-status="not-read"]{color:var(--ink-dim)}
.source-pill{
display:inline-flex; align-items:stretch; border-radius:999px;
background:var(--panel-2); overflow:hidden;
}
.source-pill .sp-icon{display:flex; align-items:center; padding:4px 10px 4px 5px}
.tabbar .source-pill .live-host{
width:32px; height:32px; display:grid; place-items:center;
border-radius:50%; background:var(--bg); border:1px solid var(--line);
}
.tabbar .source-pill .live-host-icon{width:20px; height:20px; stroke-width:1.8}
.source-pill .sp-status{
display:flex; align-items:center; padding:5px 14px 5px 10px; font-size:13px;
font-weight:600; letter-spacing:-.006em; text-transform:lowercase; color:var(--ink-2);
}
.source-pill[data-status="ok"] .sp-status{color:var(--ok)}
.source-pill[data-status="degraded"]{background:color-mix(in srgb,var(--warn) 16%,var(--panel-2))}
.source-pill[data-status="degraded"] .sp-status{color:var(--warn)}
.source-pill[data-status="absent"] .sp-status,.source-pill[data-status="not-read"] .sp-status{color:var(--ink-dim)}
.sh{display:flex; align-items:baseline; justify-content:space-between; gap:12px; margin-bottom:14px}
.sh h2{font-size:15px; font-weight:600; letter-spacing:-.014em; margin:0}
.sh .n{color:var(--ink-dim); font-size:11.5px}
Expand Down
Loading