ccdeck 3.41.0 → 3.41.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -66,8 +66,8 @@
66
66
  document.documentElement.setAttribute("data-color-scheme", lightThemes.indexOf(theme) >= 0 ? "light" : "dark");
67
67
  })();
68
68
  </script>
69
- <script type="module" crossorigin src="/assets/index-B9pQ4U7M.js"></script>
70
- <link rel="stylesheet" crossorigin href="/assets/index-BhNayHbp.css">
69
+ <script type="module" crossorigin src="/assets/index-2Z0Tr6cq.js"></script>
70
+ <link rel="stylesheet" crossorigin href="/assets/index-BgSnAnP6.css">
71
71
  </head>
72
72
  <body>
73
73
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccdeck",
3
- "version": "3.41.0",
3
+ "version": "3.41.1",
4
4
  "description": "Local dashboard for Claude Code and Codex CLI sessions: which Claude Code session needs input, every tool call and Claude Code subagent live, token cost and quota, several Claude accounts. Run npx ccdeck.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -77,6 +77,24 @@
77
77
  "3.39.4",
78
78
  "3.40.1", "3.40.2"
79
79
  ],
80
+ "3.41.1": [
81
+ {
82
+ "title": "🪟 More reliable Windows updates",
83
+ "body": "Restart to update stops the app’s background server before installation begins."
84
+ },
85
+ {
86
+ "title": "📊 More accurate account capacity",
87
+ "body": "Elapsed resets and older readings no longer imply newly available quota. Account refreshes keep the latest valid readings."
88
+ },
89
+ {
90
+ "title": "🖥️ Better layouts on portrait displays",
91
+ "body": "Sessions fill the right columns, and the canvas starts near the top. Usage history controls stay reachable in narrow windows."
92
+ },
93
+ {
94
+ "title": "✨ Clearer load history and consistent icons",
95
+ "body": "Load history includes the current core-capacity multiplier below the charts. Refresh and external-link icons look consistent across system fonts."
96
+ }
97
+ ],
80
98
  "3.41.0": [
81
99
  {
82
100
  "title": "📅 Plan account resets",
@@ -253,7 +253,7 @@ async function readUsageRows(root) {
253
253
  * trust a row the other two refuse. Exported for its test.
254
254
  */
255
255
  export function usageRows(usage) {
256
- return usage?.schemaVersion === 2 ? (usage.accounts ?? {}) : {};
256
+ return usage?.schemaVersion === 2 && usage.accounts && typeof usage.accounts === "object" && !Array.isArray(usage.accounts) ? usage.accounts : {};
257
257
  }
258
258
 
259
259
  /**
@@ -267,14 +267,14 @@ export function usageRows(usage) {
267
267
  * cannot disagree about whose row it is. Exported for its test.
268
268
  */
269
269
  export function rowIsFor(row, acct) {
270
- return Boolean(row)
270
+ return Boolean(row) && typeof acct?.email === "string" && acct.email.trim().length > 0
271
271
  && row.email === acct.email
272
272
  && (row.organizationUuid ?? "") === (acct.organizationUuid ?? "");
273
273
  }
274
274
 
275
275
  function pctOf(win) {
276
276
  const p = win?.pct;
277
- return typeof p === "number" && Number.isFinite(p) ? p : null;
277
+ return typeof p === "number" && Number.isFinite(p) ? Math.min(100, Math.max(0, p)) : null;
278
278
  }
279
279
 
280
280
  /** One lane, in the shape the panel's bars already speak. */
@@ -288,7 +288,7 @@ function lane(id, label, win) {
288
288
  pct,
289
289
  // claude-swap stores a countdown string too, but it was computed when the
290
290
  // row was written and drifts — the client recomputes from the timestamp.
291
- resetAt: isNaN(resetAt) ? null : Math.floor(resetAt / 1000),
291
+ resetAt: !Number.isFinite(resetAt) || resetAt <= 0 ? null : Math.floor(resetAt / 1000),
292
292
  };
293
293
  }
294
294
 
@@ -480,9 +480,12 @@ async function readRoster(now, gen) {
480
480
  ? await currentIdentity().catch(() => null)
481
481
  : null;
482
482
 
483
- const order = Array.isArray(seq.sequence) && seq.sequence.length
484
- ? seq.sequence.map(String)
485
- : Object.keys(seq.accounts).sort((a, b) => Number(a) - Number(b));
483
+ // Sequence is an ordering hint, not a second roster. Preserve all slots
484
+ // exactly once when an older or partially written order omits one.
485
+ const order = [...new Set([
486
+ ...(Array.isArray(seq.sequence) ? seq.sequence.map(String) : []),
487
+ ...Object.keys(seq.accounts).sort((a, b) => Number(a) - Number(b)),
488
+ ])].filter(num => Object.hasOwn(seq.accounts, num));
486
489
 
487
490
  const accounts = [];
488
491
  const recovered = [];
@@ -515,14 +518,19 @@ async function readRoster(now, gen) {
515
518
  * verdict cache, and asks the registered repair about a `stale-copy` row —
516
519
  * which is what starts that repair (see repairStaleCopyWith).
517
520
  */
521
+ function observedMs(seconds) {
522
+ const ms = typeof seconds === "number" ? seconds * 1000 : NaN;
523
+ return Number.isFinite(ms) && ms > 0 && Number.isFinite(new Date(ms).getTime()) ? ms : null;
524
+ }
525
+
518
526
  function rosterRow({ seq, num, acct, row, identity, now, key, origin = null }) {
519
527
  const matches = rowIsFor(row, acct);
520
528
  const good = matches ? row.lastGood : null;
521
529
 
522
- const fetchedAtMs = matches && typeof row.fetchedAt === "number" ? row.fetchedAt * 1000 : null;
530
+ const fetchedAtMs = matches ? observedMs(row.fetchedAt) : null;
523
531
  // When claude-swap last TRIED, well or not. A refusal counts against a login
524
532
  // only when it came after the deck last signed that login in (#1893).
525
- const attemptedAtMs = matches && typeof row.lastAttemptAt === "number" ? row.lastAttemptAt * 1000 : null;
533
+ const attemptedAtMs = matches ? observedMs(row.lastAttemptAt) : null;
526
534
  const isActive = String(seq.activeAccountNumber) === num;
527
535
  const trouble = authTrouble(row, {
528
536
  matches, isActive, identity, email: acct.email, fetchedAt: fetchedAtMs, now,
@@ -703,7 +711,7 @@ export async function activeAccountUsage() {
703
711
  const row = (await readUsageRows(root))[num];
704
712
  // Same identity guard the panel uses: rows are keyed by slot, and slots are
705
713
  // reused, so a row can outlive the account it was written for.
706
- if (!row?.lastGood || !rowIsFor(row, acct) || typeof row.fetchedAt !== "number") return null;
714
+ if (!row?.lastGood || !rowIsFor(row, acct) || observedMs(row.fetchedAt) == null) return null;
707
715
 
708
716
  return {
709
717
  num: Number(num),
@@ -35,7 +35,8 @@ export const WIN_7D_SEC = 604800;
35
35
  * Exported for its test.
36
36
  */
37
37
  export function clampPct(v) {
38
- return Math.min(100, Math.max(0, Math.round(v)));
38
+ return typeof v === "number" && Number.isFinite(v)
39
+ ? Math.min(100, Math.max(0, Math.round(v))) : null;
39
40
  }
40
41
 
41
42
  // ISO-8601 → "Jun 19, 1:19pm" (local time, matching the CLI display format).
@@ -48,13 +49,13 @@ export function clampPct(v) {
48
49
  const fmtResetIso = resetLabelIso;
49
50
 
50
51
  function isoToSec(iso) {
51
- if (!iso) return null;
52
+ if (typeof iso !== "string" || !iso) return null;
52
53
  const t = new Date(iso).getTime();
53
- return isNaN(t) ? null : Math.floor(t / 1000);
54
+ return !Number.isFinite(t) || t <= 0 ? null : Math.floor(t / 1000);
54
55
  }
55
56
 
56
57
  // Map the OAuth usage JSON to our quota result shape.
57
- // utilization is already a 0–100 percentage. 5h falls back to 7d if absent.
58
+ // utilization is already a 0–100 percentage. Each window stays independent.
58
59
  //
59
60
  // A 7d window that is absent is UNKNOWN, and published as null (#1627). The
60
61
  // endpoint can send `seven_day: null`, and this used to fill in a zero there —
@@ -67,25 +68,26 @@ export function mapOAuthUsage(data) {
67
68
  const son = data?.seven_day_sonnet;
68
69
  const opus = data?.seven_day_opus;
69
70
 
70
- const primary = (fh?.utilization != null) ? fh : sd;
71
- if (!primary || primary.utilization == null) return null;
71
+ const fivePct = clampPct(fh?.utilization);
72
+ const sevenPct = clampPct(sd?.utilization);
73
+ if (fivePct == null && sevenPct == null) return null;
72
74
 
73
75
  const result = {
74
- session5hPct: clampPct(primary.utilization),
76
+ session5hPct: fivePct,
75
77
  session5hWindowSec: WIN_5H_SEC,
76
- session5hReset: fmtResetIso(primary.resets_at),
77
- session5hResetAt: isoToSec(primary.resets_at),
78
+ session5hReset: fmtResetIso(fivePct == null ? null : fh?.resets_at),
79
+ session5hResetAt: isoToSec(fivePct == null ? null : fh?.resets_at),
78
80
  week7dWindowSec: WIN_7D_SEC,
79
81
  };
80
- if (sd?.utilization != null) {
81
- result.week7dPct = clampPct(sd.utilization);
82
+ if (sevenPct != null) {
83
+ result.week7dPct = sevenPct;
82
84
  result.week7dReset = fmtResetIso(sd.resets_at);
83
85
  result.week7dResetAt = isoToSec(sd.resets_at);
84
86
  } else {
85
87
  result.week7dPct = null;
86
88
  }
87
- if (son?.utilization != null) result.weekSonnetPct = clampPct(son.utilization);
88
- if (opus?.utilization != null) result.weekOpusPct = clampPct(opus.utilization);
89
+ if (clampPct(son?.utilization) != null) result.weekSonnetPct = clampPct(son.utilization);
90
+ if (clampPct(opus?.utilization) != null) result.weekOpusPct = clampPct(opus.utilization);
89
91
 
90
92
  // extra usage credits (pay-as-you-go top-up), if enabled
91
93
  const extra = data?.extra_usage;
@@ -114,21 +116,22 @@ export function quotaFromStore(entry) {
114
116
  const good = entry?.lastGood;
115
117
  const fh = good?.five_hour;
116
118
  const sd = good?.seven_day;
117
- const primary = (typeof fh?.pct === "number") ? fh : sd;
118
- if (typeof primary?.pct !== "number") return null;
119
+ const fivePct = clampPct(fh?.pct);
120
+ const sevenPct = clampPct(sd?.pct);
121
+ if (fivePct == null && sevenPct == null) return null;
119
122
 
120
123
  const out = {
121
124
  ok: true,
122
125
  source: "claude-swap",
123
- session5hPct: clampPct(primary.pct),
126
+ session5hPct: fivePct,
124
127
  session5hWindowSec: WIN_5H_SEC,
125
- session5hReset: fmtResetIso(primary.resets_at),
126
- session5hResetAt: isoToSec(primary.resets_at),
128
+ session5hReset: fmtResetIso(fivePct == null ? null : fh?.resets_at),
129
+ session5hResetAt: isoToSec(fivePct == null ? null : fh?.resets_at),
127
130
  week7dWindowSec: WIN_7D_SEC,
128
131
  // Unknown rather than 0 when the row has no 7d window, as mapOAuthUsage
129
132
  // publishes it (#1627): claude-swap writes no `seven_day` at all when the
130
133
  // endpoint sent none.
131
- week7dPct: typeof sd?.pct === "number" ? clampPct(sd.pct) : null,
134
+ week7dPct: sevenPct,
132
135
  week7dReset: fmtResetIso(sd?.resets_at),
133
136
  week7dResetAt: isoToSec(sd?.resets_at),
134
137
  // The age of the DATA, not of our read of it. The panel prints this, and
@@ -139,7 +142,7 @@ export function quotaFromStore(entry) {
139
142
  // claude-swap keeps per-model windows in a named list rather than fixed
140
143
  // fields, because which ones an account has depends on its plan.
141
144
  for (const s of Array.isArray(good.scoped) ? good.scoped : []) {
142
- if (typeof s?.pct !== "number") continue;
145
+ if (clampPct(s?.pct) == null) continue;
143
146
  if (/sonnet/i.test(s.name ?? "")) out.weekSonnetPct = clampPct(s.pct);
144
147
  else if (/opus/i.test(s.name ?? "")) out.weekOpusPct = clampPct(s.pct);
145
148
  }
@@ -1 +0,0 @@
1
- import{j as e,r,l as Q,m as _,n as Y,v as z,o as V,q as X,t as Z,w as ee,s as se,u as te,d as ne,x as ae,y as ie,h as re}from"./index-B9pQ4U7M.js";function oe({snap:s}){return e.jsxs("section",{className:"bw-feed",children:[e.jsxs("h4",{className:"bw-sec-head bw-head-row",children:["Live activity",e.jsxs("span",{className:"bw-feed-count",children:[s.log.length," ",s.log.length===1?"event":"events"]})]}),e.jsx("p",{className:"bw-since bw-feed-note",children:"Pages you opened yourself — counted to run the rule, never written down. Addresses appear under Findings, and only for pages a program opened."}),e.jsx("div",{className:"bw-log","aria-live":"polite","aria-relevant":"additions",children:s.log.length===0?e.jsx("p",{className:"bw-note",children:"The deck writes a line here each time it looks at a profile."}):s.log.map(a=>e.jsxs("div",{className:`bw-log-line ${a.level}${a.parts?"":" sys"}`,children:[e.jsx("span",{className:"bw-log-time",children:new Date(a.atMs).toLocaleTimeString("en-GB",{hour12:!1})}),a.parts?e.jsxs(e.Fragment,{children:[e.jsx("span",{className:"bw-log-browser",children:a.parts.browser}),e.jsx("span",{className:"bw-log-profile",title:a.parts.profile,children:a.parts.profile}),e.jsxs("span",{className:"bw-log-value",children:[a.parts.value,a.parts.flagged>0&&e.jsxs("span",{className:"bw-log-flag",children:[" · ",a.parts.flagged," flagged"]})]})]}):e.jsx("span",{className:"bw-log-sys",children:a.text})]},`${a.atMs}-${a.level}-${a.text}`))})]})}const E=s=>`${s.host}-${s.startMs}`;function le(s){const a=h=>new Date(h).toLocaleTimeString("en-GB",{hour:"2-digit",minute:"2-digit",hour12:!1});return s.count===1||s.endMs-s.startMs<6e4?a(s.startMs):`${a(s.startMs)} → ${a(s.endMs)}`}function ce(s){return new Date(s).toLocaleDateString(void 0,{weekday:"short",day:"numeric",month:"short"})}function U(s){const a=Math.round((s.endMs-s.startMs)/6e4);return a<1?"":`${a} min`}function de({snap:s,dismiss:a,refreshRef:h}){const[c,d]=r.useState(null),u=r.useRef(new Map),t=r.useRef(null);r.useEffect(()=>{var i,w;const o=t.current;!o||s.episodes.some(b=>E(b)===o.id)||(t.current=null,Q(((i=document.activeElement)==null?void 0:i.tagName)??null)&&((w=(o.next?u.current.get(o.next):void 0)??h.current)==null||w.focus()))},[s]);const n=o=>{t.current={id:E(o),next:_(s.episodes.map(E),E(o))},a(o)},l=r.useMemo(()=>{const o=[];for(const i of s.episodes){const w=ce(i.startMs),b=o[o.length-1];b&&b.label===w?b.episodes.push(i):o.push({label:w,episodes:[i]})}return o},[s]);return e.jsxs("section",{className:"bw-findings",children:[e.jsxs("h4",{className:"bw-sec-head bw-head-row",children:["Findings",s.episodes.length>0&&e.jsxs("span",{className:"bw-feed-count",children:[s.episodes.length," ",s.episodes.length===1?"episode":"episodes"]})]}),e.jsx("div",{className:"bw-eps",children:l.length===0?e.jsxs("div",{className:"bw-empty",children:[e.jsx("p",{className:"bw-empty-head",children:"Nothing found yet"}),e.jsxs("p",{className:"bw-empty-note",children:["An episode lands here when a program opens pages in a browser nobody has touched for"," ",s.settings.quietMinutes," ",s.settings.quietMinutes===1?"minute":"minutes",". On most machines that is rare, so an empty list is the ordinary result rather than a sign something is wrong."]}),!s.settings.enabled&&e.jsx("p",{className:"bw-empty-note",children:"Watching is off, so this shows only what this deck has seen since it started. Anything an earlier run recorded comes back when you switch it on."})]}):l.map(o=>e.jsxs("div",{className:"bw-day",children:[e.jsx("h4",{className:"bw-sec-head",children:o.label}),o.episodes.map(i=>{const w=E(i),b=c===w;return e.jsxs("div",{className:`bw-ep${b?" open":""}`,children:[e.jsxs("div",{className:"bw-ep-row",children:[e.jsxs("button",{className:"bw-ep-head",ref:p=>{p?u.current.set(w,p):u.current.delete(w)},onClick:()=>d(b?null:w),"aria-expanded":b,children:[e.jsx("span",{className:"bw-chev","aria-hidden":!0,children:b?"▾":"▸"}),e.jsx("span",{className:"bw-ep-host",children:i.host}),e.jsxs("span",{className:"bw-ep-meta",children:[le(i),U(i)&&e.jsxs(e.Fragment,{children:[" · ",U(i)]}),i.provisional&&e.jsxs(e.Fragment,{children:[" · ",e.jsx("span",{title:"Its quiet window has not closed yet. If somebody used the browser in the minutes after it, it is withdrawn; until then it is not written down and no reaction runs.",children:"provisional"})]})]}),e.jsxs("span",{className:"bw-ep-count",children:[i.count," ",i.count===1?"page":"pages"]})]}),e.jsx("button",{className:"glyph-btn bw-ep-x",onClick:()=>n(i),"aria-label":`Dismiss ${i.host}`,title:"Dismiss — it leaves this list for good, and stays in the log file",children:"×"})]}),b&&e.jsx("ul",{className:"bw-urls",children:i.urls.map((p,y)=>e.jsxs("li",{children:[e.jsx("span",{className:"bw-url-time",children:new Date(p.timeMs).toLocaleTimeString("en-GB",{hour:"2-digit",minute:"2-digit",hour12:!1})}),e.jsx("span",{className:"bw-url",children:p.url})]},`${p.url}-${p.timeMs}-${y}`))})]},w)})]},o.label))})]})}function he(s){const a=/^#?([0-9a-f]{6})$/i.exec((s??"").trim());if(!a)return!1;const h=parseInt(a[1],16),c=[h>>16&255,h>>8&255,h&255].map(d=>{const u=d/255;return u<=.03928?u/12.92:Math.pow((u+.055)/1.055,2.4)});return .2126*c[0]+.7152*c[1]+.0722*c[2]>.5}const G=1e4,ue=12e3;function we({browsers:s,findings:a,watching:h,palette:c}){const d=r.useRef(null),u=r.useRef(null);return u.current={browsers:s,findings:a,watching:h},r.useEffect(()=>{var b;const t=d.current;if(!t)return;const n=t.getContext("2d");if(!n)return;const l=((b=window.matchMedia)==null?void 0:b.call(window,"(prefers-reduced-motion: reduce)").matches)??!1;let o=0,i=!1;const w=()=>{var q;if(i)return;const p=u.current;if(!p)return;const{browsers:y,findings:j,watching:P}=p,S=c["--accent"]||"#7dd3fc",g=c["--line"]||"#1f2229",v=c["--text-dim"]||"#6e727c",$=he(c["--bg"]),N=$?42:26,k=$?.85:.65,A=Date.now(),R=Math.min(window.devicePixelRatio||1,2),C=t.clientWidth,W=t.clientHeight;(t.width!==C*R||t.height!==W*R)&&(t.width=C*R,t.height=W*R),n.setTransform(R,0,0,R,0,0),n.clearRect(0,0,C,W);const m=C/2,x=W/2,M=Math.min(C,W)/2-6;n.strokeStyle=g,n.lineWidth=1,n.globalAlpha=$?1:.9;for(const f of[.4,.7,1])n.beginPath(),n.arc(m,x,M*f,0,Math.PI*2),n.stroke();n.beginPath(),n.moveTo(m-M,x),n.lineTo(m+M,x),n.moveTo(m,x-M),n.lineTo(m,x+M),n.stroke(),n.globalAlpha=1;const I=(P&&!l?A%G/G:0)*Math.PI*2-Math.PI/2;if(P){const f=(q=n.createConicGradient)==null?void 0:q.call(n,I-.85,m,x);f&&(f.addColorStop(0,"transparent"),f.addColorStop(.85/(Math.PI*2),`color-mix(in srgb, ${S} ${N}%, transparent)`),f.addColorStop(1,"transparent"),n.fillStyle=f,n.beginPath(),n.moveTo(m,x),n.arc(m,x,M,I-.85,I),n.closePath(),n.fill()),n.strokeStyle=S,n.globalAlpha=k,n.beginPath(),n.moveTo(m,x),n.lineTo(m+Math.cos(I)*M,x+Math.sin(I)*M),n.stroke(),n.globalAlpha=1}y.forEach((f,T)=>{const B=T/Math.max(y.length,1)*Math.PI*2-Math.PI/2,D=f.lastReadMs===null?1:Math.min(1,(A-f.lastReadMs)/12e4),O=M*(.42+D*.5),F=m+Math.cos(B)*O,L=x+Math.sin(B)*O,J=Math.abs((I-B+Math.PI*3)%(Math.PI*2)-Math.PI),K=P?Math.max(0,1-J/.55):0;n.beginPath(),n.arc(F,L,3+K*1.6,0,Math.PI*2),n.fillStyle=f.running?S:v,n.globalAlpha=f.running?.55+K*.45:.35,n.fill(),n.globalAlpha=1});for(const f of j){const T=(A-f.atMs)/ue;if(T<0||T>1)continue;const D=Math.max(0,y.findIndex(L=>L.key===f.browser))/Math.max(y.length,1)*Math.PI*2-Math.PI/2,O=m+Math.cos(D)*M*.6,F=x+Math.sin(D)*M*.6;n.beginPath(),n.arc(O,F,4+T*22,0,Math.PI*2),n.strokeStyle=S,n.globalAlpha=(1-T)*.8,n.lineWidth=1.5,n.stroke(),n.globalAlpha=1}o=requestAnimationFrame(w)};return w(),()=>{i=!0,cancelAnimationFrame(o)}},[c]),e.jsx("canvas",{ref:d,className:"bw-radar",role:"img","aria-label":h?`Watching ${s.length} browser${s.length===1?"":"s"}`:"Not watching"})}function me({snap:s,watching:a,palette:h}){const[c,d]=r.useState(!1),[u,t]=r.useState(()=>Date.now());r.useEffect(()=>{const l=setInterval(()=>t(Date.now()),1e3);return()=>clearInterval(l)},[]);const n=Y(s.coverage.lastHumanMs,s.coverage.quietMs,u);return e.jsxs("section",{className:"bw-sec",children:[e.jsxs("h4",{className:"bw-sec-head",children:["Activity overview",e.jsx("button",{className:"bw-help",onClick:()=>d(l=>!l),"aria-expanded":c,"aria-label":"What the sweep and the dots mean",children:"?"})]}),e.jsxs("div",{className:"bw-glance",children:[e.jsx(we,{browsers:a.map(l=>{var o;return{key:l.key,name:l.name,running:l.running,lastReadMs:((o=s.profiles.find(i=>i.browser===l.key))==null?void 0:o.lastWrittenMs)??null}}),findings:s.episodes.slice(0,6).map(l=>({browser:l.browser??null,atMs:l.endMs})),watching:s.settings.enabled,palette:h}),e.jsx("ul",{className:"bw-counts",children:z(a,s.profiles).map(l=>e.jsxs("li",{children:[e.jsx("span",{className:"bw-count-n",children:l.visits.toLocaleString("en-US")}),e.jsx("span",{className:"bw-count-of",children:l.name})]},l.key))})]}),e.jsx("p",{className:"bw-since",children:"Pages a program opened, since this deck started"}),s.coverage.checkedMs>0&&e.jsxs("p",{className:"bw-since",children:["Last checked ",V(s.coverage.checkedMs,u),s.coverage.checks>0&&e.jsxs(e.Fragment,{children:[" · ",s.coverage.checks.toLocaleString("en-US")," ",s.coverage.checks===1?"check":"checks"]})]}),n!==null&&e.jsxs("p",{className:"bw-gate",children:["You are browsing — a program page would count in ",X(n)]}),c&&e.jsxs("dl",{className:"bw-key",children:[e.jsx("dt",{children:"Sweep"}),e.jsx("dd",{children:"one poll of every watched profile"}),e.jsx("dt",{children:"Dot"}),e.jsx("dd",{children:"a browser, further out the longer since it last wrote history"}),e.jsx("dt",{children:"Ring"}),e.jsx("dd",{children:"a finding, leaving the browser it came from"})]})]})}function be({watching:s,rest:a}){const[h,c]=r.useState(!1),[d,u]=r.useState(!1);return e.jsxs("section",{className:"bw-sec",children:[e.jsxs("h4",{className:"bw-sec-head",children:["Watched profiles",e.jsx("button",{className:"bw-help",onClick:()=>c(t=>!t),"aria-expanded":h,"aria-label":"What running, idle and connected to Anthropic mean",children:"?"})]}),h&&e.jsxs("dl",{className:"bw-key",children:[e.jsx("dt",{children:"Running"}),e.jsx("dd",{children:"the browser has a process on this machine right now"}),e.jsx("dt",{children:"Connected"}),e.jsx("dd",{children:"an open connection to an address Anthropic's relay uses — which it shares with claude.ai, so an open tab looks the same as an agent channel"})]}),e.jsx("ul",{className:"bw-profiles",children:s.map(t=>e.jsxs("li",{children:[e.jsx("span",{className:`bw-prof-dot${t.running?" on":""}`,"aria-hidden":!0}),e.jsx("span",{className:"bw-prof-name",children:t.name}),e.jsx("span",{className:"bw-prof-state",children:t.running?"running":"idle"}),t.running&&t.relay.state==="live"&&e.jsx("span",{className:"bw-relay",children:"connected to Anthropic"})]},t.key))}),a.length>0&&e.jsxs(e.Fragment,{children:[e.jsxs("button",{className:"bw-why bw-rest-head",onClick:()=>u(t=>!t),"aria-expanded":d,children:[e.jsx("span",{className:"bw-chev","aria-hidden":!0,children:d?"▾":"▸"})," ",a.length," not watched"]}),d&&e.jsxs("p",{className:"bw-rest",children:[a.map(t=>t.name).join(", ")," — not installed, or installed and never opened, so there is no history to read."]})]})]})}function pe({snap:s,quiet:a,setQuiet:h,save:c,access:d,setAccess:u}){return e.jsxs("div",{className:"bw-settings",children:[e.jsxs("label",{title:"A program opening a page counts as a finding only if nobody had touched the browser for this long. Shorter catches more and reports more of your own work; longer is quieter.",children:[e.jsx("span",{children:"Nobody browsing for"}),e.jsxs("select",{value:a??s.settings.quietMinutes,onChange:t=>{h(Number(t.target.value)),c({quietMinutes:Number(t.target.value)})},children:[e.jsx("option",{value:1,children:"1 min"}),e.jsx("option",{value:5,children:"5 min"}),e.jsx("option",{value:15,children:"15 min"}),e.jsx("option",{value:30,children:"30 min"}),e.jsx("option",{value:60,children:"60 min"})]})]}),e.jsxs("label",{children:[e.jsx("span",{children:"When it finds one"}),e.jsx("select",{value:s.settings.reaction,onChange:t=>void c({reaction:t.target.value}),disabled:!s.settings.enabled,title:s.settings.enabled?"What to do besides writing it down.":"Turn watching on to arm a reaction.",children:(s.reactions??["notify"]).map(t=>e.jsx("option",{value:t,children:t==="notify"?"notify me":t==="close-tab"?"close the tab":"quit the browser"},t))})]}),e.jsxs("div",{className:"bw-settings-note",children:[e.jsxs("p",{children:["Chrome marks a navigation that came from an extension or a command rather than from a click. These are the ones that happened while nobody had touched the browser for the time above. ",e.jsx("strong",{children:"Usually that is your own agent doing what you asked."})]}),e.jsxs("button",{className:"bw-why",onClick:()=>u(t=>!t),"aria-expanded":d,children:[e.jsx("span",{className:"bw-chev","aria-hidden":!0,children:d?"▾":"▸"})," What Browser Watch can access"]}),d&&e.jsxs("dl",{className:"bw-access",children:[e.jsx("dt",{children:"Reads"}),e.jsxs("dd",{children:["A copy of each browser's own history database — the live file is locked while the browser holds it. Only rows newer than the moment this deck started:"," ",new Date(s.coverage.startedMs).toLocaleString(),"."]}),e.jsx("dt",{children:"Keeps"}),e.jsxs("dd",{children:["Only while the switch is on, and only the episodes it flagged — never your ordinary browsing. In ",e.jsx("code",{className:"bw-path",children:s.coverage.logPath}),typeof s.coverage.logBytes=="number"?` (${Z(s.coverage.logBytes)})`:"",", with every address written in full so you can check it yourself."]}),e.jsx("dt",{children:"Sends"}),e.jsx("dd",{children:"Nothing. No part of this reads or writes over the network; the deck serves on 127.0.0.1 and this panel talks only to it."}),e.jsx("dt",{children:"Never reads"}),e.jsx("dd",{children:"Anything from before this deck started, cookies, saved passwords, page contents, or any browser profile with no history file."})]})]})]})}function H(s,a){return a?{kind:"saving",word:"Saving"}:s.settings.enabled?{kind:"on",word:"Watching"}:{kind:"off",word:"Paused"}}function ge({snap:s,saving:a,save:h,why:c,setWhy:d}){return e.jsxs("footer",{className:"bw-status",children:[e.jsx("span",{className:`bw-mode-dot${s.settings.enabled?" on":""}`,"aria-hidden":!0}),e.jsx("span",{className:"bw-mode-word",children:H(s,a).word},H(s,a).kind),e.jsx("span",{className:"bw-status-text",children:s.coverage.archived===0?s.settings.enabled?"No episodes recorded yet":"No episodes on disk":`${s.coverage.archived} ${s.coverage.archived===1?"episode":"episodes"} on disk`}),e.jsx("button",{className:`glyph-btn bw-gear${c?" on":""}`,onClick:()=>d(u=>!u),"aria-expanded":c,"aria-label":"Settings",title:"Settings",children:e.jsxs("svg",{width:"13",height:"13",viewBox:"0 0 14 14",fill:"none",stroke:"currentColor",strokeWidth:"1.3","aria-hidden":!0,children:[e.jsx("circle",{cx:"7",cy:"7",r:"2.1"}),e.jsx("path",{d:"M7 1.2v1.6M7 11.2v1.6M1.2 7h1.6M11.2 7h1.6M2.9 2.9l1.1 1.1M10 10l1.1 1.1M11.1 2.9L10 4M4 10l-1.1 1.1"})]})}),e.jsxs("label",{className:"bw-switch",htmlFor:"bw-enabled",children:[e.jsx("span",{className:"bw-switch-label",id:"bw-enabled-label",children:"Watch browser activity"}),e.jsx("button",{id:"bw-enabled",type:"button",role:"switch","aria-checked":s.settings.enabled,"aria-labelledby":"bw-enabled-label",className:"switch",onClick:()=>void h({enabled:!s.settings.enabled}),title:s.settings.enabled?"On — every episode it finds is written down, so the list outlives the browsing history being cleared":"Off — showing only what this deck has seen since it started. Anything an earlier run archived is hidden until you switch back on, and nothing new is kept",children:e.jsx("span",{className:"switch-knob"})})]})]})}function fe({relay:s}){const[a,h]=r.useState(!1),[c,d]=r.useState(!1),u=s.killswitch.blocked?"unblock":"block",t=s.command[u],n=r.useCallback(async()=>{await ee(t.command)&&(d(!0),window.setTimeout(()=>d(!1),1600))},[t.command]),l=s.profiles.filter(i=>{var w;return(w=i.report)==null?void 0:w.present}),o=s.profiles.filter(i=>i.report===null);return e.jsxs("section",{className:"bw-sec",children:[e.jsx("h4",{className:"bw-sec-head",children:"Remote control"}),e.jsx("p",{className:`bw-rc-verdict ${s.verdict}`,role:"status",children:s.verdict==="exposed"?"Claude in Chrome is live here, so any Claude Code session signed in to your Anthropic account can drive this browser — from any machine, with no prompt on this one.":s.verdict==="protected"?`The relay is black-holed in ${s.hostsPath}, so no new connection to it can be made from this machine.`:"Claude in Chrome is not installed and enabled in any profile here, so there is nothing for the relay to drive."}),s.killswitch.foreign.length>0&&e.jsxs("p",{className:"bw-rc-foreign",children:["Another entry for this host is already in ",s.hostsPath,", and ccdeck will not touch it:",s.killswitch.foreign.map(i=>e.jsx("code",{className:"bw-path",children:i},i))]}),e.jsxs("button",{className:"bw-why",onClick:()=>h(i=>!i),"aria-expanded":a,children:[e.jsx("span",{className:"bw-chev","aria-hidden":!0,children:a?"▾":"▸"})," ",s.verdict==="protected"?"How to undo the block":"What it can reach, and how to stop it"]}),a&&e.jsxs("div",{className:"bw-rc-body",children:[l.length>0&&e.jsx("dl",{className:"bw-key",children:l.map(i=>e.jsxs(r.Fragment,{children:[e.jsxs("dt",{children:[i.name,i.profile==="Default"?"":` · ${i.profile}`]}),e.jsxs("dd",{children:[i.report.enabled?"enabled":"installed but switched off",i.report.allUrls?", can act on every site":", limited to the sites it was granted",i.report.sensitiveApis.length>0&&e.jsxs(e.Fragment,{children:[" — ",i.report.sensitiveApis.join(", ")]})]})]},`${i.browser}/${i.profile}`))}),o.length>0&&e.jsxs("p",{className:"bw-rc-note",children:[o.map(i=>i.name).join(", ")," — its settings file could not be read, so nothing is claimed about it either way."]}),!s.hostsRead&&e.jsxs("p",{className:"bw-rc-note",children:[s.hostsPath," could not be read, so whether the relay is already blocked is unknown."]}),l.some(i=>i.report.allUrls)&&e.jsxs("p",{className:"bw-rc-note",children:["Narrowing the sites in ",e.jsx("code",{className:"bw-path",children:"chrome://extensions"})," does not change this: the grant is held at the browser level and the per-site list is enforced inside the extension, by the extension."]}),e.jsxs("div",{className:"bw-rc-cmd",children:[e.jsx("code",{children:t.command}),e.jsx("button",{className:"btn bw-rc-copy",onClick:n,children:c?"Copied":"Copy"})]}),e.jsx("p",{className:"bw-rc-note",children:t.note})]})]})}function xe(s){const[a,h]=r.useState(null),[c,d]=r.useState(null),[u,t]=r.useState(null),[n,l]=r.useState(!1),o=r.useRef(!1),[i,w]=r.useState(null),[b,p]=r.useState(!1),y=r.useRef(!1),j=r.useCallback(async g=>{var v,$;if(!(g&&!se(o.current))){g&&(o.current=!0,l(!0));try{const N=await fetch(`/api/browser-watch${g?"?refresh=1":""}`);if(!N.ok)throw new Error(`the deck answered ${N.status}`);const k=await N.json();h(k),y.current||w(((v=k==null?void 0:k.settings)==null?void 0:v.quietMinutes)??15),s((($=k==null?void 0:k.settings)==null?void 0:$.enabled)===!0),d(null)}catch(N){d(N instanceof Error?N.message:String(N))}finally{g&&(o.current=!1,l(!1))}}},[s]);r.useEffect(()=>{j(!1)},[j]),r.useEffect(()=>{const g=setInterval(()=>{j(!1)},1e4);return()=>clearInterval(g)},[j]);const P=r.useCallback(async g=>{try{await fetch("/api/browser-watch/dismiss",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({host:g.host,startMs:g.startMs})})}finally{await j(!1)}},[j]),S=r.useCallback(async g=>{y.current=!0,p(!0);try{const v=await fetch("/api/browser-watch",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(g)});if(!v.ok)throw new Error(`the deck answered ${v.status}`);await j(!1),t(null)}catch(v){t(v instanceof Error?v.message:String(v))}finally{y.current=!1,p(!1)}},[j]);return{snap:a,error:c,writeError:u,setWriteError:t,busy:n,quiet:i,setQuiet:w,saving:b,load:j,dismiss:P,save:S}}function ye({onClose:s,onSeen:a,onWatching:h,palette:c}){const d=te(s),u=ne(s),{snap:t,error:n,writeError:l,setWriteError:o,busy:i,quiet:w,setQuiet:b,saving:p,load:y,dismiss:j,save:P}=xe(h),[S,g]=r.useState(!1),[v,$]=r.useState(!1),N=r.useRef(null),k=r.useRef(a);k.current=a,r.useEffect(()=>()=>k.current(Date.now()),[]);const A=ae(t==null?void 0:t.browsers),R=((t==null?void 0:t.browsers)??[]).filter(m=>!(m.installed&&m.profiles>0)),C=t?ie(t):null,W=A.reduce((m,x)=>m+x.profiles,0);return e.jsx("div",{className:"modal-backdrop",...u,role:"presentation",children:e.jsxs("div",{ref:d,className:"modal bw-modal",onClick:m=>m.stopPropagation(),role:"dialog","aria-modal":"true","aria-labelledby":"bw-title",children:[e.jsxs("header",{className:"modal-head",children:[e.jsxs("div",{className:"modal-title",children:[e.jsx("span",{className:"modal-tool-name",id:"bw-title",children:"Browser watch"}),e.jsx("span",{className:"modal-tool-id",children:t?`${W} ${W===1?"profile":"profiles"} · local only`:"local only"})]}),e.jsxs("div",{className:"modal-actions",children:[e.jsx("button",{ref:N,className:"glyph-btn",onClick:()=>void y(!0),...re(i),"aria-label":"Re-read every profile now",title:"Re-read every profile now",children:i?"…":"↻"}),e.jsx("button",{className:"glyph-btn",onClick:s,"aria-label":"Close (Esc)",title:"Close (Esc)",children:"×"})]})]}),e.jsxs("div",{className:"modal-body bw-body",children:[!t&&!n&&e.jsxs("div",{className:"bw-loading",children:[e.jsx("span",{className:"bw-loading-bar","aria-hidden":!0}),e.jsx("p",{children:"Reading each browser's history. The first look copies the file, which takes a moment."})]}),n&&e.jsx("div",{className:"bw-state",children:e.jsxs("div",{className:"bw-row err",children:[e.jsx("span",{className:"bw-dot","aria-hidden":!0}),e.jsx("span",{className:"bw-row-label",children:"Could not read"}),e.jsx("span",{className:"bw-row-detail",children:n})]})}),t&&C&&e.jsx("p",{className:`bw-trouble ${C.kind}`,role:"status",children:C.text}),t&&e.jsxs("div",{className:"bw-work",children:[e.jsxs("aside",{className:"bw-side",children:[e.jsx(me,{snap:t,watching:A,palette:c}),e.jsx(be,{watching:A,rest:R}),t.relay&&e.jsx(fe,{relay:t.relay})]}),e.jsxs("div",{className:"bw-main",children:[e.jsx(de,{snap:t,dismiss:j,refreshRef:N}),e.jsx(oe,{snap:t})]})]}),t&&S&&e.jsx(pe,{snap:t,quiet:w,setQuiet:b,save:P,access:v,setAccess:$}),l&&e.jsx("div",{className:"bw-state bw-write-err",role:"alert",children:e.jsxs("div",{className:"bw-row err",children:[e.jsx("span",{className:"bw-dot","aria-hidden":!0}),e.jsx("span",{className:"bw-row-label",children:"Could not save that setting"}),e.jsxs("span",{className:"bw-row-detail",children:[l," — the switches below show what is stored, not what you pressed."]}),e.jsx("button",{className:"glyph-btn bw-write-err-x",onClick:()=>o(null),"aria-label":"Dismiss",title:"Dismiss",children:"×"})]})}),t&&e.jsx(ge,{snap:t,saving:p,save:P,why:S,setWhy:g})]})]})})}export{ye as default};
@@ -1 +0,0 @@
1
- import{a as O,f as p,r as i,s as Z,p as _,j as a,b as g,c as I,u as ee,d as se,e as ae,g as te,h as E,i as ne,k as oe}from"./index-B9pQ4U7M.js";function le(s){return!s.answer||s.answer.range!==s.want?{phase:"busy",stale:!1}:s.answer.ok?{phase:s.answer.days>0?"chart":"empty",stale:s.loading}:{phase:"error",stale:s.loading}}function q(s){const e=new Map;for(const n of s)for(const o of n.agents??[]){if(!o||typeof o.agent!="string"||o.agent==="")continue;const r=e.get(o.agent)??{id:o.agent,cost:0,tokens:0};r.cost+=Number.isFinite(o.totalCost)?o.totalCost:0,r.tokens+=Number.isFinite(o.totalTokens)?o.totalTokens:0,e.set(o.agent,r)}return[...e.values()].sort((n,o)=>o.cost-n.cost||n.id.localeCompare(o.id))}function re(s){const e=q([{agents:s}]);return e.length<2?null:e.map(n=>`${O(n.id)} ${p(n.cost)}`).join(" · ")}function F(s){switch(s.toLowerCase()){case"claude":return"var(--model-opus)";case"codex":return"var(--model-codex)";case"gemini":return"var(--model-gemini)";case"copilot":return"var(--model-haiku)";default:return"var(--model-other)"}}function ce(s,e){if(!(e>0)||!(s>0))return"0%";const n=s/e*100;return n<.1?"<0.1%":`${n.toFixed(1)}%`}const u=s=>typeof s=="number"&&Number.isFinite(s)?s:0,$=s=>typeof s=="string"?s:"",U=s=>Array.isArray(s)?s.filter(e=>typeof e=="string"):[];function ie(s){const e=s??{};return{modelName:$(e.modelName),cost:u(e.cost),inputTokens:u(e.inputTokens),outputTokens:u(e.outputTokens),cacheCreationTokens:u(e.cacheCreationTokens),cacheReadTokens:u(e.cacheReadTokens)}}function ue(s){const e=s??{},n=Array.isArray(e.agents)?e.agents.map(o=>{const r=o??{};return{agent:$(r.agent),totalCost:u(r.totalCost),totalTokens:u(r.totalTokens)}}):void 0;return{period:$(e.period),totalCost:u(e.totalCost),totalTokens:u(e.totalTokens),inputTokens:u(e.inputTokens),outputTokens:u(e.outputTokens),cacheCreationTokens:u(e.cacheCreationTokens),cacheReadTokens:u(e.cacheReadTokens),modelsUsed:U(e.modelsUsed),modelBreakdowns:Array.isArray(e.modelBreakdowns)?e.modelBreakdowns.map(ie):[],...n?{agents:n}:{},...e.metadata&&typeof e.metadata=="object"?{metadata:{agents:U(e.metadata.agents)}}:{}}}function de(s){const e=s??{};return{...e,ok:e.ok===!0,days:Array.isArray(e.days)?e.days.map(ue):void 0}}function C(s){const e=s.toLowerCase();return e.includes("opus")?"var(--model-opus)":e.includes("sonnet")?"var(--model-sonnet)":e.includes("haiku")?"var(--model-haiku)":e.includes("gpt-5")||e.includes("gpt5")?"var(--model-gpt5)":e.includes("gpt")?"var(--model-gpt)":e.includes("gemini")?"var(--model-gemini)":e.includes("codex")?"var(--model-codex)":e.includes("fable")||e.includes("mythos")?"var(--model-fable)":"var(--model-other)"}function he(s){let e=0,n=0,o=0,r=0;const m=new Map;for(const l of s){e+=l.totalCost,n+=l.totalTokens,o+=l.inputTokens+l.outputTokens,r+=l.cacheReadTokens;for(const d of l.modelBreakdowns)m.set(d.modelName,(m.get(d.modelName)??0)+d.cost)}return{totalCost:e,totalTok:n,inOut:o,cacheRead:r,modelCosts:m}}function me(s){return Array.from(s.entries()).sort((e,n)=>n[1]-e[1])}function G(s){return s.slice().sort((e,n)=>n.cost-e.cost)}function v(s,e){return e>0?s/e*100:0}function pe(s){var n,o;return re(s.agents)??((o=(n=s.metadata)==null?void 0:n.agents)!=null&&o.length?s.metadata.agents.join(" · "):null)}function ge(){let s=0;return{begin(){const e=++s;return()=>e===s},cancel(){s++}}}function fe(s){const[e,n]=i.useState(null),[o,r]=i.useState(!1),m=i.useState(ge)[0],l=i.useRef(!1),d=(f=!1)=>{if(f&&!Z(l.current))return;const x=m.begin();l.current=f;const j=s;r(!0);const h=`/api/ccusage?since=${_(j)}${f?"&refresh=1":""}`;fetch(h).then(c=>c.json()).then(c=>{x()&&n({range:j,resp:de(c)})}).catch(()=>{x()&&n({range:j,resp:{ok:!1,reason:"unreachable"}})}).finally(()=>{x()&&(l.current=!1,r(!1))})};return i.useEffect(()=>(d(!1),()=>m.cancel()),[s]),{landed:e,loading:o,reload:()=>d(!0)}}function xe({selectedDay:s}){const e=pe(s);return a.jsxs("div",{className:"uh-detail",children:[a.jsxs("div",{className:"uh-detail-head",children:[a.jsx("span",{className:"uh-detail-date",children:s.period}),a.jsx("span",{className:"uh-detail-cost",children:p(s.totalCost)}),e&&a.jsx("span",{className:"uh-detail-agents",title:e,children:e})]}),a.jsxs("div",{className:"uh-detail-mini",children:[a.jsx(b,{label:"input",val:g(s.inputTokens)}),a.jsx(b,{label:"output",val:g(s.outputTokens)}),a.jsx(b,{label:"cache write",val:g(s.cacheCreationTokens)}),a.jsx(b,{label:"cache read",val:g(s.cacheReadTokens)})]}),a.jsx("div",{className:"uh-detail-models",children:G(s.modelBreakdowns).map(n=>{const o=v(n.cost,s.totalCost);return a.jsxs("div",{className:"uh-model-row",title:n.modelName,children:[a.jsxs("span",{className:"uh-model-name",children:[a.jsx("span",{className:"uh-legend-dot",style:{background:C(n.modelName)}}),a.jsx("span",{className:"uh-model-label",children:I(n.modelName)})]}),a.jsx("span",{className:"uh-model-bar",children:a.jsx("span",{className:"uh-model-bar-fill",style:{width:`${o}%`,background:C(n.modelName)}})}),a.jsx("span",{className:"uh-model-cost",children:p(n.cost)})]},n.modelName)})})]})}function b({label:s,val:e}){return a.jsxs("div",{className:"uh-ministat",children:[a.jsx("span",{className:"uh-ministat-val",children:e}),a.jsx("span",{className:"uh-ministat-label",children:s})]})}const je=[7,14,30,90];function Ne({onClose:s,providers:e}){var P;const[n,o]=i.useState(30),[r,m]=i.useState(null),{landed:l,loading:d,reload:f}=fe(n),x=i.useRef(null),j=ee(s,{focusRef:x}),S=se(s),h=le({loading:d,want:n,answer:l&&{range:l.range,ok:l.resp.ok===!0,days:((P=l.resp.days)==null?void 0:P.length)??0}}),c=h.phase==="chart"?l.resp.days??[]:[],M=i.useRef(null),H=ae(h.phase!=="error",M),V=i.useMemo(()=>c.reduce((t,k)=>Math.max(t,k.totalCost),0),[c]),A=i.useRef(null);i.useLayoutEffect(()=>{const t=A.current;t&&(t.scrollLeft=t.scrollWidth)},[c]);const{totalCost:T,totalTok:W,inOut:z,cacheRead:D,modelCosts:L}=i.useMemo(()=>he(c),[c]),J=i.useMemo(()=>me(L),[L]),N=i.useMemo(()=>q(c),[c]),K=N.length>1,B=r?c.find(t=>t.period===r)??null:null;return a.jsx("div",{className:"modal-backdrop",...S,role:"presentation",children:a.jsxs("div",{ref:j,className:"modal uh-modal",onClick:t=>t.stopPropagation(),role:"dialog","aria-modal":"true","aria-label":"Usage history",children:[a.jsxs("header",{className:"uh-head",children:[a.jsxs("div",{className:"uh-titlewrap",children:[a.jsx("div",{className:"uh-title",children:"Usage history"}),a.jsx("div",{className:"uh-sub",children:h.stale?"running ccusage…":te(e,N.map(t=>t.id))})]}),a.jsx("div",{className:"uh-range",role:"group","aria-label":"Range",children:je.map(t=>a.jsxs("button",{type:"button","aria-pressed":n===t,className:"uh-range-btn",onClick:()=>{o(t),m(null)},children:[t,"d"]},t))}),a.jsx("button",{ref:M,className:"glyph-btn uh-reload",onClick:f,...E(d),title:"Re-run ccusage","aria-label":"Reload",children:d?"…":"↻"}),a.jsx("button",{ref:x,className:"glyph-btn uh-close",onClick:s,"aria-label":"Close",children:"×"})]}),h.phase==="busy"?a.jsxs("div",{className:"uh-status uh-status-wait","aria-busy":"true",children:[a.jsx("span",{className:"uh-wait-word",children:l?"running ccusage…":"running ccusage… (first run downloads the package)"}),a.jsx("span",{className:"uh-wait-axis","aria-hidden":!0})]}):h.phase==="error"?a.jsxs("div",{className:"uh-status uh-err",title:oe(l.resp)||void 0,children:[ne(l.resp,"ccusage did not report usage"),a.jsx("div",{children:a.jsx("button",{className:"btn uh-retry",onClick:()=>{H(),f()},...E(d),children:d?"trying…":"Try again"})})]}):h.phase==="empty"?a.jsx("div",{className:`uh-status${h.stale?" uh-stale":""}`,"aria-busy":h.stale||void 0,children:"no usage in this range"}):a.jsxs("div",{className:h.stale?"uh-stale":void 0,"aria-busy":h.stale||void 0,children:[a.jsxs("div",{className:"uh-totals",children:[a.jsx(y,{label:"total cost",val:p(T),accent:!0}),a.jsx(y,{label:"tokens",val:g(W)}),a.jsx(y,{label:"input+output",val:g(z)}),a.jsx(y,{label:"cache reads",val:g(D)})]}),K&&a.jsxs("div",{className:"uh-agents",role:"group","aria-label":"Cost by CLI",children:[a.jsx("div",{className:"uh-agent-bar","aria-hidden":"true",children:N.map(t=>a.jsx("span",{className:"uh-agent-seg",style:{width:`${v(t.cost,T)}%`,background:F(t.id)}},t.id))}),a.jsx("div",{className:"uh-agent-keys",children:N.map(t=>a.jsxs("span",{className:"uh-agent-key",title:`${t.id} · ${g(t.tokens)} tokens`,children:[a.jsx("span",{className:"uh-legend-dot",style:{background:F(t.id)}}),O(t.id),a.jsx("span",{className:"uh-agent-cost",children:p(t.cost)}),a.jsx("span",{className:"uh-agent-share",children:ce(t.cost,T)})]},t.id))})]}),a.jsx("div",{ref:A,className:"uh-chart",role:"group","aria-label":"Daily cost by model",children:c.map((t,k)=>{const Q=v(t.totalCost,V),w=t.period===r,X=`${t.period}, ${p(t.totalCost)}`;return a.jsxs("button",{className:`uh-bar-col${w?" sel":""}`,onClick:()=>m(w?null:t.period),"aria-pressed":w,"aria-label":X,title:`${t.period} · ${p(t.totalCost)}`,style:{flexBasis:`${100/c.length}%`,"--uh-i":k,"--uh-step":`${Math.min(10,200/Math.max(c.length,1))}ms`},children:[a.jsx("div",{className:"uh-bar",style:{height:`${Math.max(Q,t.totalCost>0?2:0)}%`},children:G(t.modelBreakdowns).map(R=>{const Y=v(R.cost,t.totalCost);return a.jsx("div",{className:"uh-bar-seg",style:{height:`${Y}%`,background:C(R.modelName)}},R.modelName)})}),a.jsx("span",{className:"uh-bar-label",children:t.period.slice(5)})]},t.period)})}),a.jsx("div",{className:"uh-legend",children:J.map(([t,k])=>a.jsxs("span",{className:"uh-legend-item",title:t,children:[a.jsx("span",{className:"uh-legend-dot",style:{background:C(t)}}),I(t)," ",a.jsx("span",{className:"uh-legend-cost",children:p(k)})]},t))}),B&&a.jsx(xe,{selectedDay:B})]})]})})}function y({label:s,val:e,accent:n}){return a.jsxs("div",{className:"uh-stat",children:[a.jsx("span",{className:`uh-stat-val${n?" accent":""}`,children:e}),a.jsx("span",{className:"uh-stat-label",children:s})]})}export{Ne as default};