diff --git a/plugins/jsc-statusbar/.claude-plugin/plugin.json b/plugins/jsc-statusbar/.claude-plugin/plugin.json index 0782a61..2317b5f 100644 --- a/plugins/jsc-statusbar/.claude-plugin/plugin.json +++ b/plugins/jsc-statusbar/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-statusbar", - "version": "0.0.3", + "version": "0.0.4", "description": "輸入框上方的單行狀態列:模型、目錄、分支、5h/7d 額度與重置、context、CPU/記憶體/磁碟", "license": "MIT", "types": "./types/index.d.ts", diff --git a/plugins/jsc-statusbar/hooks/register.tsx b/plugins/jsc-statusbar/hooks/register.tsx index 4788157..c76d676 100644 --- a/plugins/jsc-statusbar/hooks/register.tsx +++ b/plugins/jsc-statusbar/hooks/register.tsx @@ -1,7 +1,7 @@ import { atom, read, update } from 'claude-code' import type { EngineInterface, Register, SessionContextUsage, SessionRateLimit } from 'claude-code' -import type { Mode, Sys, Usage } from '../types' +import type { Mode, Sys, Usage, Window } from '../types' import { fit, cpuPercent, parseCpu, parseDf, parseGit, parseMem, parseOffset } from './format' import type { CpuSample } from './format' @@ -19,6 +19,7 @@ const ALERT_AT = 90 const GIT_TOOLS = new Set(['Bash', 'Edit', 'Write', 'NotebookEdit']) const LABEL: Record = { five_hour: '5 小時', seven_day: '7 天' } +// 引擎要等第一次 API 回應才有額度與 ctx;先前的額度存在 $.store,開新對話時先顯示,ctx 先當作 0 function toUsage(context: SessionContextUsage, rateLimits: SessionRateLimit[]): Usage { const win = (kind: string) => { const r = rateLimits.find(x => x.kind === kind) @@ -27,9 +28,25 @@ function toUsage(context: SessionContextUsage, rateLimits: SessionRateLimit[]): return { fiveHour: win('five_hour'), sevenDay: win('seven_day'), - ctxTokens: context.tokens, + ctxTokens: context.tokens ?? 0, ctxWindow: context.window, - ctxPercent: context.percent, + ctxPercent: context.percent ?? 0, + } +} + +// 已過重置時間的視窗用量已歸零,不再沿用舊值 +const live = (w: Window | undefined, now: number) => + w && (!w.resetsAt || Date.parse(w.resetsAt) > now) ? w : undefined + +async function restoreRates($: EngineInterface, usage: Usage): Promise { + const saved = (await $.store.get('rates')) as Pick | undefined + if (!saved) return usage + const now = await $.clock.now() + + return { + ...usage, + fiveHour: usage.fiveHour ?? live(saved.fiveHour, now), + sevenDay: usage.sevenDay ?? live(saved.sevenDay, now), } } @@ -104,9 +121,11 @@ export const register: Register = on => { tzOffsetMin = parseOffset(r.stdout) } catch {} const u = await $.session.usage() - await update($, usageAtom, () => toUsage(u.context, u.rateLimits)) + const usage = await restoreRates($, toUsage(u.context, u.rateLimits)) + await update($, usageAtom, () => usage) void refreshGit($) - void refreshSys($) + // CPU 要兩次取樣才算得出來,開場先隔 0.5 秒補一次,免得等到 3 秒後才出現 + void refreshSys($).then(() => $.clock.after(500, () => void refreshSys($))) $.clock.every(SYS_MS, () => void refreshSys($)) $.clock.every(GIT_MS, () => void refreshGit($)) @@ -115,6 +134,11 @@ export const register: Register = on => { on('session.measure', async ($, e, next) => { await update($, usageAtom, () => toUsage(e.context, e.rateLimits)) + if (e.rateLimits.length) { + const w = (kind: string) => e.rateLimits.find(x => x.kind === kind) + const pick = (r: SessionRateLimit | undefined) => r && { percent: r.percentUsed, resetsAt: r.resetsAt } + await $.store.set('rates', { fiveHour: pick(w('five_hour')), sevenDay: pick(w('seven_day')) }) + } if (e.changed.includes('rateLimits')) await alert($, e.rateLimits) return next(e)