@caius_kong/ccusage-dashboard 0.2.13 → 0.2.14

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.
package/lib/index.html CHANGED
@@ -55,7 +55,9 @@
55
55
  .range button{background:var(--panel-2);border:1px solid var(--border);color:var(--text);border-radius:6px;padding:5px 12px;cursor:pointer;font-size:12px}
56
56
  .range button:hover{border-color:var(--accent)}
57
57
  .range button.active{background:var(--accent);border-color:var(--accent);color:#0d1117;font-weight:600}
58
- .sessid{font-variant-numeric:tabular-nums;letter-spacing:.3px}
58
+ .sessid{font-variant-numeric:tabular-nums;letter-spacing:.3px;cursor:pointer;transition:background .15s,border-color .15s,color .15s}
59
+ .sessid:hover{background:var(--accent);border-color:var(--accent);color:#0d1117}
60
+ .sessid.copied{background:var(--green);border-color:var(--green);color:#0d1117}
59
61
  .budget-band{margin:2px 0 14px;background:var(--panel-2);border-radius:8px;padding:12px 16px;display:flex;flex-direction:column;gap:8px}
60
62
  .budget-band .bl{display:flex;justify-content:space-between;font-size:13px;color:var(--muted)}
61
63
  .budget-band .bl b{color:var(--text)}
@@ -133,6 +135,24 @@ function fmtTok(n){
133
135
  }
134
136
  function fmtPct(n){return n==null?"–":n.toFixed(1)+"%";}
135
137
  function esc(s){const d=document.createElement("div");d.textContent=s;return d.innerHTML;}
138
+ async function copySessionId(id, ev){
139
+ ev.stopPropagation();
140
+ const el=ev.currentTarget; // capture before any await; currentTarget is nulled after dispatch
141
+ try{
142
+ await navigator.clipboard.writeText(id);
143
+ }catch(e){
144
+ // fallback for non-secure contexts / older browsers
145
+ const ta=document.createElement("textarea");
146
+ ta.value=id;ta.style.position="fixed";ta.style.opacity="0";
147
+ document.body.appendChild(ta);ta.select();
148
+ try{document.execCommand("copy");}catch(_){}
149
+ document.body.removeChild(ta);
150
+ }
151
+ el.classList.add("copied");
152
+ const old=el.textContent;
153
+ el.textContent="✓ copied";
154
+ setTimeout(()=>{el.textContent=old;el.classList.remove("copied");},1200);
155
+ }
136
156
 
137
157
  function renderModels(data){
138
158
  const total=data.totalCost||0;
@@ -190,9 +210,11 @@ function renderSessions(data){
190
210
  $("sessRange").textContent=`${total} sessions`;
191
211
  if(!list.length){$("sessionsList").innerHTML='<div class="empty">no sessions in range</div>';return;}
192
212
  $("sessionsList").innerHTML=list.map(s=>{
213
+ const shortId=s.id.slice(0,8);
214
+ const idPill=`<span class="pill sessid" title="click to copy full session id" onclick="copySessionId('${s.id.replace(/[\\']/g,"\\$&")}',event)">${esc(shortId)}</span>`;
193
215
  const label = s.hasCwd
194
- ? `<span class="name" title="workdir: ${esc(s.cwd||s.projectKey)}">${esc(s.dirName||s.id.slice(0,8))} <span class="pill sessid">${esc(s.id.slice(0,8))}</span></span>`
195
- : `<span class="name muted" title="no workdir found for this ${esc(s.agent)} session">${esc(s.id.slice(0,8))}</span>`;
216
+ ? `<span class="name" title="workdir: ${esc(s.cwd||s.projectKey)}">${esc(s.dirName||shortId)} ${idPill}</span>`
217
+ : `<span class="name muted" title="no workdir found for this ${esc(s.agent)} session">${idPill}</span>`;
196
218
  const last=(s.lastActivity||"").slice(0,10);
197
219
  return `<div class="row">
198
220
  <div class="row-top"><span>${label} <span class="pill">${esc(s.agent)}</span></span><span class="cost">${fmtUsd(s.cost)}</span></div>
package/lib/server.py CHANGED
@@ -202,14 +202,13 @@ def sessions(since_days: int = 30, period: str = "", from_date: str = "", to_dat
202
202
  Filtering: pass `period` in ("today" | "week" | "month") OR an explicit
203
203
  date range via `from_date`/`to_date` (YYYY-MM-DD). `since_days` is kept for
204
204
  backward compatibility with the old 7d/30d/90d/all selector.
205
- """
206
- data = run_ccusage(
207
- ["session", "--by-agent", "--json", "--offline"],
208
- TTL["/api/sessions"],
209
- )
210
- rows = data.get("session") or []
211
205
 
212
- # ---- choose the cutoff window ----------------
206
+ The window is passed to ccusage as --since/--until so it filters by usage
207
+ EVENT date (entry.date) before summarising, exactly like the CLI session
208
+ report. Filtering rows ourselves by lastActivity would keep a whole session's
209
+ lifetime cost even when only a few events fall in the window, inflating the
210
+ total (observed: today $229 vs ccusage's true $0.09).
211
+ """
213
212
  today = date.today()
214
213
  lo = hi = None
215
214
  if from_date and to_date:
@@ -232,20 +231,17 @@ def sessions(since_days: int = 30, period: str = "", from_date: str = "", to_dat
232
231
  lo = today - timedelta(days=since_days)
233
232
  hi = today
234
233
 
234
+ ccusage_args = ["session", "--json", "--offline"]
235
+ if lo and hi:
236
+ ccusage_args += ["--since", lo.isoformat(), "--until", hi.isoformat()]
237
+ data = run_ccusage(ccusage_args, TTL["/api/sessions"])
238
+ rows = data.get("session") or []
239
+
235
240
  out = []
236
241
  for r in rows:
237
242
  meta = r.get("metadata") or {}
238
243
  sid = r.get("period")
239
244
  last_activity = meta.get("lastActivity") or ""
240
- # date filter by last activity against [lo, hi]
241
- if last_activity:
242
- try:
243
- act_date = date.fromisoformat(last_activity[:10])
244
- except ValueError:
245
- act_date = None
246
- if act_date is not None:
247
- if act_date < lo or act_date > hi:
248
- continue
249
245
  project_raw = meta.get("projectPath") or ""
250
246
  agent_name = r.get("agent", "?")
251
247
  cwd = ""
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caius_kong/ccusage-dashboard",
3
- "version": "0.2.13",
3
+ "version": "0.2.14",
4
4
  "description": "One-command local dashboard for ccusage: today/week/month/custom cost by model, 30-day trend, monthly budget alert. Numbers straight from ccusage.",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",