From 7afbfcb025d2abc57b07107845a10115baa346a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=B3=BB=E7=B5=B1=E7=AE=A1=E7=90=86=E5=93=A1?= <1+admin@noreply.localhost> Date: Tue, 13 Oct 2026 14:16:51 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20statusbar=20=E5=9C=A8=E9=80=81=E5=87=BA?= =?UTF-8?q?=E7=AC=AC=E4=B8=80=E5=80=8B=E6=8F=90=E7=A4=BA=E8=A9=9E=E5=89=8D?= =?UTF-8?q?=E5=B0=B1=E9=A1=AF=E7=A4=BA=E9=A1=8D=E5=BA=A6=E3=80=81ctx=20?= =?UTF-8?q?=E8=88=87=E7=B3=BB=E7=B5=B1=E8=B3=87=E6=BA=90=EF=BC=8C=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E5=8D=87=E8=87=B3=200.0.4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 5h/7d 額度存進 $.store,新對話先顯示上次的數值(已過重置時間的視窗不沿用) - 尚無 API 回應時 ctx 先顯示 0% - 開場 0.5 秒後補一次系統取樣,CPU 不必等 3 秒 Co-Authored-By: Claude Sonnet 5.5 --- .../jsc-statusbar/.claude-plugin/plugin.json | 2 +- plugins/jsc-statusbar/hooks/register.tsx | 34 ++++++++++++++++--- 2 files changed, 30 insertions(+), 6 deletions(-) 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)