dsh-provider-usage 0.3.2 → 0.3.3
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/README.md +7 -5
- package/README_ZH.md +7 -5
- package/lib/client.js +54 -62
- package/lib/client.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
# dsh-provider-usage
|
|
12
12
|
|
|
13
|
-
**Every provider's balance, one glance away.** A [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) plugin that floats a draggable
|
|
13
|
+
**Every provider's balance, one glance away.** A [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) plugin that floats a small draggable usage ball over the Web GUI — no more logging into each provider console to check whether you are about to run out of credit mid-session.
|
|
14
14
|
|
|
15
15
|
## Features
|
|
16
16
|
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
- **Kimi Code subscription** (`kimi-coding`) → `GET {baseURL}/v1/usages` (weekly quota and rate-limit windows, with reset countdown)
|
|
21
21
|
- **Moonshot open platform** (`moonshotai-cn` / `moonshotai`) → `GET {baseURL}/users/me/balance`
|
|
22
22
|
- **Credentials stay safe** — API keys are resolved per request through the harness credentials service (environment variables / `~/.dsh/.credentials.yaml`); never cached, never written to disk.
|
|
23
|
-
- **Floating ball widget** — a draggable floating ball opens the
|
|
23
|
+
- **Floating ball widget** — a draggable floating ball opens the usage panel. Drop it anywhere in the viewport (position persisted); it docks by default at the bottom-left of the chat area with equal margins, and the panel-header home button sends it back. The halo around the ball encodes provider health: green all good, amber some quota below 30% left, red on query failure / missing key / usage ≥90%.
|
|
24
24
|
- **Version badge** — the panel header shows the running plugin version next to the title, so it is obvious which release is loaded.
|
|
25
25
|
- **Bilingual panel** — built-in Chinese/English UI; follows the harness language by default, with a one-click toggle in the panel header (persisted in localStorage).
|
|
26
26
|
- **Configurable refresh** — adjustable in the panel (15s–30min, persisted in localStorage); the default comes from the plugin config.
|
|
@@ -28,7 +28,9 @@
|
|
|
28
28
|
|
|
29
29
|
## Screenshots
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
The floating ball (bottom-left, with the green healthy halo) and the open usage panel:
|
|
32
|
+
|
|
33
|
+

|
|
32
34
|
|
|
33
35
|
## Install
|
|
34
36
|
|
|
@@ -62,7 +64,7 @@ Restart `dsh web` after changing the plugin set (a plugin add/remove requires a
|
|
|
62
64
|
dsh plugin --profile web add dsh-provider-usage@latest
|
|
63
65
|
```
|
|
64
66
|
|
|
65
|
-
Then restart `dsh web` and refresh the page. If the release you want was published very recently, your profile's supply-chain cooldown (`minimumReleaseAge`) may silently keep the older version — pin the exact version instead (`dsh plugin --profile web add dsh-provider-usage@0.1
|
|
67
|
+
Then restart `dsh web` and refresh the page. If the release you want was published very recently, your profile's supply-chain cooldown (`minimumReleaseAge`) may silently keep the older version — pin the exact version instead (`dsh plugin --profile web add dsh-provider-usage@0.3.1`) and dsh will exempt it automatically. The version badge in the panel header confirms which release is actually loaded.
|
|
66
68
|
|
|
67
69
|
## Configuration
|
|
68
70
|
|
|
@@ -101,7 +103,7 @@ config:
|
|
|
101
103
|
## How it works
|
|
102
104
|
|
|
103
105
|
- **Host half** (`src/index.ts`): `UsageService extends TypertRemoteService` exposes `usage/list` via `@Remote('list')` (SRC mode, no codegen). Config is declared with schemastery, and `installSettingsSection` enables hot updates from settings.
|
|
104
|
-
- **Client half** (`src/client/`): a `window.__ModuleLoader__.load({id, factory})` bundle (built by tsdown)
|
|
106
|
+
- **Client half** (`src/client/`): a `window.__ModuleLoader__.load({id, factory})` bundle (built by tsdown) mounts through the `sidebar.footer.action` slot (used purely as a mount point — the trigger itself is a floating ball portaled to `document.body`) and polls `usage/list` through `ctx.connection.rpc.call('/api', 'usage/list', {args:{}})` on its own interval. The service stays stateless — every poll fetches live values.
|
|
105
107
|
|
|
106
108
|
## License
|
|
107
109
|
|
package/README_ZH.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
# dsh-provider-usage
|
|
12
12
|
|
|
13
|
-
[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 插件:在 Web GUI
|
|
13
|
+
[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 插件:在 Web GUI 上悬浮一个可任意拖动的用量球,实时查看所有已配置 LLM provider 的账户余额与配额用量——不用再逐个登录 provider 控制台确认。
|
|
14
14
|
|
|
15
15
|
## 功能
|
|
16
16
|
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
- **Kimi Code 订阅**(`kimi-coding`)→ `GET {baseURL}/v1/usages`(每周配额及各限速窗口,含重置倒计时)
|
|
21
21
|
- **Moonshot 开放平台**(`moonshotai-cn` / `moonshotai`)→ `GET {baseURL}/users/me/balance`
|
|
22
22
|
- **密钥安全** —— 通过 harness 凭据服务按次解析(环境变量 / `~/.dsh/.credentials.yaml`),不缓存、不落地。
|
|
23
|
-
- **悬浮球入口** ——
|
|
23
|
+
- **悬浮球入口** —— 可任意拖动的悬浮球点击弹出用量面板,位置持久化;默认停靠在主对话区域左下角(左边距 = 底边距),面板头部的归位按钮一键回到默认位置;球体光晕表达健康度:绿色全部正常、黄色有配额剩余不足 30%、红色查询失败/缺密钥/用量 ≥90%。
|
|
24
24
|
- **版本徽章** —— 面板标题旁显示当前运行的插件版本,一眼确认加载的是哪个发布版。
|
|
25
25
|
- **中英双语** —— 面板内置中英文界面,默认跟随 harness 系统语言,标题栏按钮一键切换(localStorage 持久化)。
|
|
26
26
|
- **刷新周期可调** —— 面板内调整(15s–30min,localStorage 持久化),默认值由插件配置提供。
|
|
@@ -28,7 +28,9 @@
|
|
|
28
28
|
|
|
29
29
|
## 截图
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
悬浮球(左下角,绿色光晕表示全部正常)与打开的用量面板:
|
|
32
|
+
|
|
33
|
+

|
|
32
34
|
|
|
33
35
|
## 安装
|
|
34
36
|
|
|
@@ -62,7 +64,7 @@ dsh plugin --profile web add ./dsh-provider-usage-<version>.tgz
|
|
|
62
64
|
dsh plugin --profile web add dsh-provider-usage@latest
|
|
63
65
|
```
|
|
64
66
|
|
|
65
|
-
然后重启 `dsh web` 并刷新页面。如果目标版本刚发布不久,profile 的供应链冷静期(`minimumReleaseAge`)可能会静默停留在旧版——这时指定精确版本号(如 `dsh plugin --profile web add dsh-provider-usage@0.1
|
|
67
|
+
然后重启 `dsh web` 并刷新页面。如果目标版本刚发布不久,profile 的供应链冷静期(`minimumReleaseAge`)可能会静默停留在旧版——这时指定精确版本号(如 `dsh plugin --profile web add dsh-provider-usage@0.3.1`),dsh 会自动豁免该版本。面板标题旁的版本徽章可以确认实际加载的版本。
|
|
66
68
|
|
|
67
69
|
## 配置说明
|
|
68
70
|
|
|
@@ -101,7 +103,7 @@ config:
|
|
|
101
103
|
## 架构
|
|
102
104
|
|
|
103
105
|
- **Host 半**(`src/index.ts`):`UsageService extends TypertRemoteService`,`@Remote('list')` 暴露 `usage/list`(SRC 模式,无需代码生成);`Config` 用 schemastery 声明,`installSettingsSection` 支持 settings 热更新。
|
|
104
|
-
- **Client 半**(`src/client/`):`window.__ModuleLoader__.load({id, factory})` 格式 bundle(tsdown
|
|
106
|
+
- **Client 半**(`src/client/`):`window.__ModuleLoader__.load({id, factory})` 格式 bundle(tsdown 构建),通过 `sidebar.footer.action` slot 挂载(仅作为挂载点——触发器本体是 portal 到 `document.body` 的悬浮球),通过 `ctx.connection.rpc.call('/api', 'usage/list', {args:{}})` 轮询。服务本身无状态——每次轮询都取实时值。
|
|
105
107
|
|
|
106
108
|
## 许可证
|
|
107
109
|
|
package/lib/client.js
CHANGED
|
@@ -10,9 +10,9 @@ window.__ModuleLoader__.load({
|
|
|
10
10
|
//#region src/client/locales.ts
|
|
11
11
|
/** Simplified Chinese dictionary (the key-set source of truth). */
|
|
12
12
|
const zh = {
|
|
13
|
-
"action.label": "
|
|
14
|
-
"action.aria": "Provider
|
|
15
|
-
"panel.title": "Provider
|
|
13
|
+
"action.label": "用量",
|
|
14
|
+
"action.aria": "Provider 用量",
|
|
15
|
+
"panel.title": "Provider 用量",
|
|
16
16
|
"panel.refresh": "立即刷新",
|
|
17
17
|
"panel.refreshing": "刷新中…",
|
|
18
18
|
"lang.switch": "Switch to English",
|
|
@@ -145,6 +145,15 @@ window.__ModuleLoader__.load({
|
|
|
145
145
|
localStorage.setItem(STORAGE_KEY, String(value));
|
|
146
146
|
} catch {}
|
|
147
147
|
}
|
|
148
|
+
/** Equal margin from the chat area's left edge and the window's bottom edge. */
|
|
149
|
+
const DOCK_MARGIN = 24;
|
|
150
|
+
/**
|
|
151
|
+
* Default dock: bottom-left of the main chat area with equal margins on both
|
|
152
|
+
* axes. Returned as viewport fractions of the ball CENTER, like every other
|
|
153
|
+
* position. The chat area's left edge is the sidebar's right edge, derived
|
|
154
|
+
* from the full-height ancestor of the settings slot (stable data-slot hook;
|
|
155
|
+
* class names are build-hashed), so it adapts to sidebar collapse.
|
|
156
|
+
*/
|
|
148
157
|
function defaultFloatPos() {
|
|
149
158
|
let sidebarRight = 264;
|
|
150
159
|
let node = document.querySelector("[data-slot=\"sidebar.settings\"]")?.parentElement ?? null;
|
|
@@ -156,11 +165,9 @@ window.__ModuleLoader__.load({
|
|
|
156
165
|
}
|
|
157
166
|
node = node.parentElement;
|
|
158
167
|
}
|
|
159
|
-
const send = document.querySelector("button[aria-label=\"发送消息\"], button[aria-label=\"Send message\"], button[aria-label=\"Send\"]")?.getBoundingClientRect();
|
|
160
|
-
const y = send && send.width > 0 ? Math.round(send.top + send.height / 2 - BALL_SIZE / 2) : window.innerHeight - 100 - BALL_SIZE;
|
|
161
168
|
return {
|
|
162
|
-
|
|
163
|
-
|
|
169
|
+
fx: (sidebarRight + DOCK_MARGIN + BALL_SIZE / 2) / window.innerWidth,
|
|
170
|
+
fy: (window.innerHeight - DOCK_MARGIN - BALL_SIZE / 2) / window.innerHeight
|
|
164
171
|
};
|
|
165
172
|
}
|
|
166
173
|
function readStoredFloatPos() {
|
|
@@ -168,11 +175,11 @@ window.__ModuleLoader__.load({
|
|
|
168
175
|
const raw = localStorage.getItem(POS_KEY);
|
|
169
176
|
if (raw === null) return null;
|
|
170
177
|
const parsed = JSON.parse(raw);
|
|
171
|
-
if (parsed === null || typeof parsed.
|
|
172
|
-
if (typeof parsed.
|
|
178
|
+
if (parsed === null || typeof parsed.fx !== "number" || !Number.isFinite(parsed.fx)) return null;
|
|
179
|
+
if (typeof parsed.fy !== "number" || !Number.isFinite(parsed.fy)) return null;
|
|
173
180
|
return {
|
|
174
|
-
|
|
175
|
-
|
|
181
|
+
fx: Math.min(1, Math.max(0, parsed.fx)),
|
|
182
|
+
fy: Math.min(1, Math.max(0, parsed.fy))
|
|
176
183
|
};
|
|
177
184
|
} catch {
|
|
178
185
|
return null;
|
|
@@ -183,6 +190,12 @@ window.__ModuleLoader__.load({
|
|
|
183
190
|
localStorage.setItem(POS_KEY, JSON.stringify(pos));
|
|
184
191
|
} catch {}
|
|
185
192
|
}
|
|
193
|
+
/** 归位: forget the pinned spot; the ball follows the default dock again. */
|
|
194
|
+
function clearStoredFloatPos() {
|
|
195
|
+
try {
|
|
196
|
+
localStorage.removeItem(POS_KEY);
|
|
197
|
+
} catch {}
|
|
198
|
+
}
|
|
186
199
|
function formatCountdown(resetAt, now, t) {
|
|
187
200
|
const diff = new Date(resetAt).getTime() - now;
|
|
188
201
|
if (!Number.isFinite(diff) || diff <= 0) return null;
|
|
@@ -396,11 +409,13 @@ window.__ModuleLoader__.load({
|
|
|
396
409
|
const [intervalSec, setIntervalSec] = (0, react.useState)(() => readStoredInterval() ?? DEFAULT_INTERVAL);
|
|
397
410
|
const [langOverride, setLangOverride] = (0, react.useState)(() => readStoredLang());
|
|
398
411
|
const [now, setNow] = (0, react.useState)(() => Date.now());
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
412
|
+
/** Pinned spot (user-dragged) as viewport fractions; null = follow the default dock. */
|
|
413
|
+
const [pinnedPos, setPinnedPos] = (0, react.useState)(() => readStoredFloatPos());
|
|
414
|
+
/** Viewport size drives the fraction → px conversion and the default dock. */
|
|
415
|
+
const [viewport, setViewport] = (0, react.useState)({
|
|
416
|
+
w: window.innerWidth,
|
|
417
|
+
h: window.innerHeight
|
|
418
|
+
});
|
|
404
419
|
/** Transient ball position while dragging (cursor-centered); null when docked. */
|
|
405
420
|
const [dragPos, setDragPos] = (0, react.useState)(null);
|
|
406
421
|
const harnessLang = t("lang.switch") === zh["lang.switch"] ? "zh" : t("lang.switch") === en["lang.switch"] ? "en" : null;
|
|
@@ -413,12 +428,10 @@ window.__ModuleLoader__.load({
|
|
|
413
428
|
return next;
|
|
414
429
|
});
|
|
415
430
|
}, [harnessLang]);
|
|
416
|
-
/** Panel-head home button:
|
|
431
|
+
/** Panel-head home button: unpin the ball so it follows the default dock. */
|
|
417
432
|
const resetFloatPos = () => {
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
setFloatPos(next);
|
|
421
|
-
posPinned.current = true;
|
|
433
|
+
clearStoredFloatPos();
|
|
434
|
+
setPinnedPos(null);
|
|
422
435
|
};
|
|
423
436
|
const [panelPos, setPanelPos] = (0, react.useState)(null);
|
|
424
437
|
const rootRef = (0, react.useRef)(null);
|
|
@@ -491,38 +504,18 @@ window.__ModuleLoader__.load({
|
|
|
491
504
|
update();
|
|
492
505
|
window.addEventListener("resize", update);
|
|
493
506
|
return () => window.removeEventListener("resize", update);
|
|
494
|
-
}, [
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
const y = Math.min(Math.max(8, current.y), window.innerHeight - BALL_SIZE - 8);
|
|
500
|
-
if (x === current.x && y === current.y) return current;
|
|
501
|
-
const next = {
|
|
502
|
-
x,
|
|
503
|
-
y
|
|
504
|
-
};
|
|
505
|
-
storeFloatPos(next);
|
|
506
|
-
return next;
|
|
507
|
-
});
|
|
508
|
-
};
|
|
509
|
-
clampPos();
|
|
510
|
-
window.addEventListener("resize", clampPos);
|
|
511
|
-
return () => window.removeEventListener("resize", clampPos);
|
|
512
|
-
}, []);
|
|
507
|
+
}, [
|
|
508
|
+
open,
|
|
509
|
+
pinnedPos,
|
|
510
|
+
viewport
|
|
511
|
+
]);
|
|
513
512
|
(0, react.useEffect)(() => {
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
return;
|
|
521
|
-
}
|
|
522
|
-
const next = defaultFloatPos();
|
|
523
|
-
setFloatPos((current) => current.x === next.x && current.y === next.y ? current : next);
|
|
524
|
-
}, 400);
|
|
525
|
-
return () => clearInterval(timer);
|
|
513
|
+
const onResize = () => setViewport({
|
|
514
|
+
w: window.innerWidth,
|
|
515
|
+
h: window.innerHeight
|
|
516
|
+
});
|
|
517
|
+
window.addEventListener("resize", onResize);
|
|
518
|
+
return () => window.removeEventListener("resize", onResize);
|
|
526
519
|
}, []);
|
|
527
520
|
const tone = healthTone(data, error);
|
|
528
521
|
const onKeyDown = (event) => {
|
|
@@ -561,13 +554,12 @@ window.__ModuleLoader__.load({
|
|
|
561
554
|
const x = Math.min(Math.max(half, event.clientX), window.innerWidth - half);
|
|
562
555
|
const y = Math.min(Math.max(half, event.clientY), window.innerHeight - half);
|
|
563
556
|
const next = {
|
|
564
|
-
|
|
565
|
-
|
|
557
|
+
fx: x / window.innerWidth,
|
|
558
|
+
fy: y / window.innerHeight
|
|
566
559
|
};
|
|
567
|
-
|
|
560
|
+
setPinnedPos(next);
|
|
568
561
|
storeFloatPos(next);
|
|
569
562
|
setDragPos(null);
|
|
570
|
-
posPinned.current = true;
|
|
571
563
|
suppressClickRef.current = true;
|
|
572
564
|
};
|
|
573
565
|
const cancelBallDrag = (event) => {
|
|
@@ -584,6 +576,9 @@ window.__ModuleLoader__.load({
|
|
|
584
576
|
setOpen((current) => !current);
|
|
585
577
|
if (!open) refresh();
|
|
586
578
|
};
|
|
579
|
+
const docked = pinnedPos ?? defaultFloatPos();
|
|
580
|
+
const centerX = dragPos !== null ? dragPos.x : docked.fx * viewport.w;
|
|
581
|
+
const centerY = dragPos !== null ? dragPos.y : docked.fy * viewport.h;
|
|
587
582
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
588
583
|
ref: rootRef,
|
|
589
584
|
className: "dsh-usage-root",
|
|
@@ -592,12 +587,9 @@ window.__ModuleLoader__.load({
|
|
|
592
587
|
ref: ballRef,
|
|
593
588
|
type: "button",
|
|
594
589
|
className: `dsh-usage-ball dsh-usage-tone-${tone}`,
|
|
595
|
-
style:
|
|
596
|
-
left:
|
|
597
|
-
top:
|
|
598
|
-
} : {
|
|
599
|
-
left: floatPos.x,
|
|
600
|
-
top: floatPos.y
|
|
590
|
+
style: {
|
|
591
|
+
left: centerX - BALL_SIZE / 2,
|
|
592
|
+
top: centerY - BALL_SIZE / 2
|
|
601
593
|
},
|
|
602
594
|
"aria-expanded": open,
|
|
603
595
|
"aria-label": tt("action.aria"),
|
package/lib/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":["useState","useRef","useMemo","useCallback","createPortal"],"sources":["../src/client/locales.ts","../src/client/Panel.tsx","../src/client/styles.ts","../src/client/index.ts"],"sourcesContent":["/** Simplified Chinese dictionary (the key-set source of truth). */\nexport const zh = {\n 'action.label': '额度',\n 'action.aria': 'Provider 额度',\n 'panel.title': 'Provider 额度',\n 'panel.refresh': '立即刷新',\n 'panel.refreshing': '刷新中…',\n 'lang.switch': 'Switch to English',\n 'panel.resetPos': '悬浮球归位',\n 'panel.interval': '刷新周期',\n 'panel.updated': '更新于 {time}',\n 'panel.never': '尚未获取',\n 'panel.empty': '未检测到已配置的 Provider',\n 'interval.15s': '15 秒',\n 'interval.30s': '30 秒',\n 'interval.60s': '1 分钟',\n 'interval.300s': '5 分钟',\n 'interval.900s': '15 分钟',\n 'interval.1800s': '30 分钟',\n 'balance.total': '总额',\n 'balance.granted': '赠送 {amount}',\n 'balance.toppedUp': '充值 {amount}',\n 'usage.weekly': '每周配额',\n 'usage.used': '已用 {used}',\n 'usage.remaining': '剩余 {remaining}',\n 'usage.unlimited': '不限量',\n 'usage.resetAt': '重置于 {time}',\n 'usage.resetIn': '{countdown} 后重置',\n 'status.unsupported': '该 Provider 暂不支持额度查询',\n 'status.missingCredential': '未配置密钥({ref})',\n 'status.error': '查询失败',\n 'status.tone.ok': '全部正常',\n 'status.tone.warn': '有用量接近上限',\n 'status.tone.danger': '有 Provider 异常或用量将尽',\n 'countdown.days': '{days}天{hours}小时',\n 'countdown.hours': '{hours}小时{minutes}分',\n 'countdown.minutes': '{minutes}分钟',\n}\n\n/** English dictionary, key-identical to the Chinese source of truth. */\nexport const en = {\n 'action.label': 'Usage',\n 'action.aria': 'Provider usage',\n 'panel.title': 'Provider Usage',\n 'panel.refresh': 'Refresh now',\n 'panel.refreshing': 'Refreshing…',\n 'lang.switch': '切换到中文',\n 'panel.resetPos': 'Reset ball position',\n 'panel.interval': 'Refresh interval',\n 'panel.updated': 'Updated {time}',\n 'panel.never': 'Not fetched yet',\n 'panel.empty': 'No configured provider detected',\n 'interval.15s': '15s',\n 'interval.30s': '30s',\n 'interval.60s': '1 min',\n 'interval.300s': '5 min',\n 'interval.900s': '15 min',\n 'interval.1800s': '30 min',\n 'balance.total': 'Total',\n 'balance.granted': 'granted {amount}',\n 'balance.toppedUp': 'topped up {amount}',\n 'usage.weekly': 'Weekly quota',\n 'usage.used': 'used {used}',\n 'usage.remaining': '{remaining} left',\n 'usage.unlimited': 'Unlimited',\n 'usage.resetAt': 'Resets at {time}',\n 'usage.resetIn': 'Resets in {countdown}',\n 'status.unsupported': 'Usage query is not supported for this provider',\n 'status.missingCredential': 'No API key configured ({ref})',\n 'status.error': 'Query failed',\n 'status.tone.ok': 'All providers healthy',\n 'status.tone.warn': 'Some usage nearing its limit',\n 'status.tone.danger': 'Provider error or usage almost exhausted',\n 'countdown.days': '{days}d {hours}h',\n 'countdown.hours': '{hours}h {minutes}m',\n 'countdown.minutes': '{minutes}m',\n}\n","/**\n * Floating-ball quota widget: a draggable ball portaled to document.body\n * (the sidebar slot only mounts the component; the button itself no longer\n * lives in the sidebar, avoiding slot layout contention with other plugins).\n * The popover panel lists every configured provider's live balance/quota.\n * Polling interval is user-selectable (persisted in localStorage) and falls\n * back to the deployment-suggested value from the host plugin config.\n */\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { createPortal } from 'react-dom'\nimport { en, zh } from './locales'\n\n/* ------------------------------------------------------------------ *\n * Wire views (mirror of the host half)\n * ------------------------------------------------------------------ */\n\ninterface BalanceRow {\n currency: string\n total: string\n granted: string\n toppedUp: string\n}\n\ninterface UsageRow {\n label: string\n used: number | null\n limit: number | null\n remaining: number | null\n percent: number | null\n resetAt: string | null\n}\n\ninterface ProviderUsageView {\n id: string\n displayName: string\n kind: 'balance' | 'usage' | null\n status: 'ok' | 'error' | 'missing-credential' | 'unsupported'\n message: string | null\n balances: BalanceRow[] | null\n usages: UsageRow[] | null\n}\n\ninterface UsageListResult {\n fetchedAt: string\n refreshSeconds: number\n /** Plugin package version (absent on older hosts). */\n version?: string\n providers: ProviderUsageView[]\n}\n\nexport interface UsageActionProps {\n /** Namespace translator injected by the locale seat. */\n t: (key: string, params?: Record<string, unknown>) => string\n /** Business face: call the host `usage/list` remote. */\n fetchUsage: () => Promise<UsageListResult>\n}\n\n/* ------------------------------------------------------------------ *\n * Refresh interval\n * ------------------------------------------------------------------ */\n\nconst INTERVAL_OPTIONS = [15, 30, 60, 300, 900, 1800] as const\nconst STORAGE_KEY = 'dsh.provider-usage.refreshSeconds'\nconst LANG_KEY = 'dsh.provider-usage.lang'\nconst POS_KEY = 'dsh.provider-usage.floatPos'\nconst DEFAULT_INTERVAL = 60\n\n/** Floating ball diameter in px; drag math derives from it. */\nconst BALL_SIZE = 32\n\ntype Lang = 'zh' | 'en'\n/** Docked ball position: absolute viewport coordinates of the ball's top-left. */\ninterface FloatPos {\n x: number\n y: number\n}\n\n/** Panel-level override stored in localStorage; null means follow the harness language. */\nfunction readStoredLang(): Lang | null {\n try {\n const raw = localStorage.getItem(LANG_KEY)\n return raw === 'zh' || raw === 'en' ? raw : null\n } catch {\n return null\n }\n}\n\nfunction storeLang(lang: Lang): void {\n try {\n localStorage.setItem(LANG_KEY, lang)\n } catch {\n /* storage unavailable: keep the in-memory value only */\n }\n}\n\n/** Local translator over the bundled dictionaries, mirroring the shell's `{param}` interpolation. */\nfunction makeT(dict: Record<string, string>): UsageActionProps['t'] {\n return (key, params) => {\n const template = dict[key] ?? key\n if (params === undefined) return template\n return template.replace(/\\{(\\w+)\\}/g, (raw, name: string) => (name in params ? String(params[name]) : raw))\n }\n}\n\nfunction readStoredInterval(): number | null {\n try {\n const raw = localStorage.getItem(STORAGE_KEY)\n if (raw === null) return null\n const value = Number(raw)\n return Number.isFinite(value) && value >= 5 ? value : null\n } catch {\n return null\n }\n}\n\nfunction storeInterval(value: number): void {\n try {\n localStorage.setItem(STORAGE_KEY, String(value))\n } catch {\n /* storage unavailable: keep the in-memory value only */\n }\n}\n\n/* ------------------------------------------------------------------ *\n * Floating ball position\n * ------------------------------------------------------------------ */\n\nfunction defaultFloatPos(): FloatPos {\n // Bottom-left of the chat area: just right of the sidebar edge, level with\n // the composer send row. Recomputed on every reset, so 归位 follows both\n // the composer (welcome screen vs conversation view) and sidebar collapse.\n // The sidebar edge comes from the full-height ancestor of the settings slot\n // (stable data-slot hook; class names are build-hashed).\n let sidebarRight = 264\n let node = document.querySelector('[data-slot=\"sidebar.settings\"]')?.parentElement ?? null\n while (node) {\n const rect = node.getBoundingClientRect()\n if (rect.x <= 1 && rect.height >= window.innerHeight * 0.9) {\n sidebarRight = rect.right\n break\n }\n node = node.parentElement\n }\n const send = document\n .querySelector('button[aria-label=\"发送消息\"], button[aria-label=\"Send message\"], button[aria-label=\"Send\"]')\n ?.getBoundingClientRect()\n const y = send && send.width > 0\n ? Math.round(send.top + send.height / 2 - BALL_SIZE / 2)\n : window.innerHeight - 100 - BALL_SIZE\n return { x: Math.round(sidebarRight + 16), y }\n}\n\nfunction readStoredFloatPos(): FloatPos | null {\n try {\n const raw = localStorage.getItem(POS_KEY)\n if (raw === null) return null\n const parsed = JSON.parse(raw) as Partial<FloatPos> | null\n if (parsed === null || typeof parsed.x !== 'number' || !Number.isFinite(parsed.x)) return null\n if (typeof parsed.y !== 'number' || !Number.isFinite(parsed.y)) return null\n return { x: parsed.x, y: parsed.y }\n } catch {\n return null\n }\n}\n\nfunction storeFloatPos(pos: FloatPos): void {\n try {\n localStorage.setItem(POS_KEY, JSON.stringify(pos))\n } catch {\n /* storage unavailable: keep the in-memory value only */\n }\n}\n\n/* ------------------------------------------------------------------ *\n * Formatting helpers\n * ------------------------------------------------------------------ */\n\nfunction formatCountdown(resetAt: string, now: number, t: UsageActionProps['t']): string | null {\n const diff = new Date(resetAt).getTime() - now\n if (!Number.isFinite(diff) || diff <= 0) return null\n const minutes = Math.floor(diff / 60000)\n if (minutes >= 60 * 24) return t('countdown.days', { days: Math.floor(minutes / (60 * 24)), hours: Math.floor((minutes % (60 * 24)) / 60) })\n if (minutes >= 60) return t('countdown.hours', { hours: Math.floor(minutes / 60), minutes: minutes % 60 })\n return t('countdown.minutes', { minutes: Math.max(1, minutes) })\n}\n\nfunction barTone(percent: number | null): 'ok' | 'warn' | 'danger' {\n if (percent === null) return 'ok'\n if (percent >= 90) return 'danger'\n if (percent >= 70) return 'warn'\n return 'ok'\n}\n\n/** Worst health across the fetch + every provider: drives the trigger's status dot. */\nfunction healthTone(data: UsageListResult | null, error: string | null): 'ok' | 'warn' | 'danger' {\n if (error !== null) return 'danger'\n let tone: 'ok' | 'warn' | 'danger' = 'ok'\n for (const provider of data?.providers ?? []) {\n if (provider.status === 'error' || provider.status === 'missing-credential') return 'danger'\n for (const row of provider.usages ?? []) {\n const rowTone = barTone(row.percent)\n if (rowTone === 'danger') return 'danger'\n if (rowTone === 'warn') tone = 'warn'\n }\n }\n return tone\n}\n\nfunction formatAmount(value: number | null): string {\n if (value === null) return '—'\n return Number.isInteger(value) ? String(value) : value.toFixed(2)\n}\n\n/* ------------------------------------------------------------------ *\n * Provider card\n * ------------------------------------------------------------------ */\n\nfunction UsageRows({ provider, now, t }: { provider: ProviderUsageView; now: number; t: UsageActionProps['t'] }) {\n const rows = provider.usages ?? []\n if (rows.length === 0) return null\n return (\n <>\n {rows.map((row, index) => {\n const percent = row.percent\n const reset = row.resetAt !== null ? formatCountdown(row.resetAt, now, t) : null\n return (\n <div className=\"dsh-usage-row\" key={`${row.label}-${index}`}>\n <div className=\"dsh-usage-rowHead\">\n <span className=\"dsh-usage-rowLabel\">{row.label === 'weekly' ? t('usage.weekly') : row.label}</span>\n <span className=\"dsh-usage-rowValue\">\n {row.limit === null\n ? t('usage.unlimited')\n : percent !== null\n ? `${Math.round(percent)}%`\n : t('usage.used', { used: formatAmount(row.used) })}\n </span>\n </div>\n {percent !== null ? (\n <div className=\"dsh-usage-barTrack\">\n <div\n className={`dsh-usage-barFill dsh-usage-barFill--${barTone(percent)}`}\n style={{ width: `${Math.min(100, Math.max(0, percent))}%` }}\n />\n </div>\n ) : null}\n {reset !== null ? <span className=\"dsh-usage-rowReset\">{t('usage.resetIn', { countdown: reset })}</span> : null}\n </div>\n )\n })}\n </>\n )\n}\n\nfunction ProviderCard({ provider, now, t }: { provider: ProviderUsageView; now: number; t: UsageActionProps['t'] }) {\n return (\n <div className=\"dsh-usage-card\">\n <div className=\"dsh-usage-cardHead\">\n <span className=\"dsh-usage-providerName\">{provider.displayName}</span>\n </div>\n {provider.status === 'unsupported' ? (\n <span className=\"dsh-usage-message\">{t('status.unsupported')}</span>\n ) : provider.status === 'missing-credential' ? (\n <span className=\"dsh-usage-message\">{t('status.missingCredential', { ref: provider.message })}</span>\n ) : provider.status === 'error' ? (\n <span className=\"dsh-usage-message\">{t('status.error')}: {provider.message}</span>\n ) : provider.kind === 'balance' ? (\n (provider.balances ?? []).map((row, index) => (\n <div key={`${row.currency}-${index}`}>\n <div className=\"dsh-usage-balanceRow\">\n <span className=\"dsh-usage-balanceTotal\">{row.total}</span>\n <span className=\"dsh-usage-balanceCurrency\">{row.currency}</span>\n </div>\n <span className=\"dsh-usage-balanceParts\">\n {[row.granted !== '0' && row.granted !== '0.00' ? t('balance.granted', { amount: row.granted }) : null,\n row.toppedUp !== '0' && row.toppedUp !== '0.00' ? t('balance.toppedUp', { amount: row.toppedUp }) : null]\n .filter(Boolean)\n .join(' · ')}\n </span>\n </div>\n ))\n ) : (\n <UsageRows provider={provider} now={now} t={t} />\n )}\n </div>\n )\n}\n\n/* ------------------------------------------------------------------ *\n * Trigger + popover\n * ------------------------------------------------------------------ */\n\n/** Battery level: remaining charge reads as remaining quota. */\nfunction BatteryIcon() {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <rect width=\"16\" height=\"10\" x=\"2\" y=\"7\" rx=\"2\" ry=\"2\" />\n <path d=\"M22 11v2\" />\n <path d=\"M6 11v2\" />\n <path d=\"M10 11v2\" />\n </svg>\n )\n}\n\nfunction RefreshIcon() {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M20 12a8 8 0 1 1-2.34-5.66\" />\n <path d=\"M20 4v4h-4\" />\n </svg>\n )\n}\n\nfunction GlobeIcon() {\n return (\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"9\" />\n <path d=\"M3 12h18\" />\n <path d=\"M12 3c2.5 2.6 3.9 5.7 3.9 9s-1.4 6.4-3.9 9c-2.5-2.6-3.9-5.7-3.9-9S9.5 5.6 12 3z\" />\n </svg>\n )\n}\n\n/** Crosshair for the \"reset ball to its default spot\" head button. */\nfunction HomeIcon() {\n return (\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"7\" />\n <path d=\"M12 2v3M12 19v3M2 12h3M19 12h3\" />\n <circle cx=\"12\" cy=\"12\" r=\"1.5\" fill=\"currentColor\" stroke=\"none\" />\n </svg>\n )\n}\n\nexport default function UsageAction({ t, fetchUsage }: UsageActionProps) {\n const [open, setOpen] = useState(false)\n const [data, setData] = useState<UsageListResult | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [loading, setLoading] = useState(false)\n const [intervalSec, setIntervalSec] = useState(() => readStoredInterval() ?? DEFAULT_INTERVAL)\n const [langOverride, setLangOverride] = useState<Lang | null>(() => readStoredLang())\n const [now, setNow] = useState(() => Date.now())\n const [floatPos, setFloatPos] = useState<FloatPos>(() => readStoredFloatPos() ?? defaultFloatPos())\n /** True once the position comes from storage/drag/reset — the default\n anchors to the composer send button, which mounts after this plugin,\n so an unpinned ball re-anchors as soon as the composer appears. */\n const posPinned = useRef(readStoredFloatPos() !== null)\n /** Transient ball position while dragging (cursor-centered); null when docked. */\n const [dragPos, setDragPos] = useState<{ x: number; y: number } | null>(null)\n\n // Follow the harness language by default: probe the shell-injected\n // translator with a known key, then let a panel-level override win.\n const harnessLang: Lang | null =\n t('lang.switch') === zh['lang.switch'] ? 'zh' : t('lang.switch') === en['lang.switch'] ? 'en' : null\n const lang: Lang = langOverride ?? harnessLang ?? 'en'\n const tt = useMemo(() => makeT(lang === 'zh' ? (zh as Record<string, string>) : (en as Record<string, string>)), [lang])\n const toggleLang = useCallback(() => {\n setLangOverride((current) => {\n const next: Lang = (current ?? harnessLang ?? 'en') === 'zh' ? 'en' : 'zh'\n storeLang(next)\n return next\n })\n }, [harnessLang])\n /** Panel-head home button: send the ball back to its default spot. */\n const resetFloatPos = () => {\n const next = defaultFloatPos()\n storeFloatPos(next)\n setFloatPos(next)\n posPinned.current = true\n }\n const [panelPos, setPanelPos] = useState<{ left: number; top?: number; bottom?: number } | null>(null)\n const rootRef = useRef<HTMLDivElement>(null)\n const ballRef = useRef<HTMLButtonElement>(null)\n const panelRef = useRef<HTMLDivElement>(null)\n const inFlight = useRef(false)\n /** Active ball-drag session; cleared on pointerup/pointercancel. */\n const dragRef = useRef<{ pointerId: number; startX: number; startY: number; dragging: boolean } | null>(null)\n /** Set when a drag ends so the trailing click does not toggle the panel. */\n const suppressClickRef = useRef(false)\n\n const refresh = useCallback(async () => {\n if (inFlight.current) return\n inFlight.current = true\n setLoading(true)\n try {\n const result = await fetchUsage()\n setData(result)\n setError(null)\n } catch (reason) {\n setError(reason instanceof Error ? reason.message : String(reason))\n } finally {\n inFlight.current = false\n setLoading(false)\n }\n }, [fetchUsage])\n\n // Initial fetch + fixed-interval polling; skip ticks while the tab is hidden.\n useEffect(() => {\n refresh()\n }, [refresh])\n useEffect(() => {\n const timer = setInterval(() => {\n if (document.visibilityState === 'visible') refresh()\n }, intervalSec * 1000)\n return () => clearInterval(timer)\n }, [intervalSec, refresh])\n\n // Adopt the deployment-suggested interval until the user picks one locally.\n useEffect(() => {\n if (data !== null && readStoredInterval() === null && data.refreshSeconds !== intervalSec) {\n setIntervalSec(data.refreshSeconds)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [data])\n\n // Slow tick so reset countdowns advance while the panel is open.\n useEffect(() => {\n if (!open) return\n setNow(Date.now())\n const timer = setInterval(() => setNow(Date.now()), 30000)\n return () => clearInterval(timer)\n }, [open])\n\n // Close on outside press / Escape, mirroring the shipped popover behavior.\n // The panel and the ball are portaled to document.body, so all trees count\n // as inside.\n useEffect(() => {\n if (!open) return\n const closeOutside = (event: PointerEvent) => {\n if (!(event.target instanceof Node)) return\n if (rootRef.current?.contains(event.target)) return\n if (ballRef.current?.contains(event.target)) return\n if (panelRef.current?.contains(event.target)) return\n setOpen(false)\n }\n document.addEventListener('pointerdown', closeOutside)\n return () => document.removeEventListener('pointerdown', closeOutside)\n }, [open])\n\n // Anchor the fixed-position panel to the ball; recompute while open so a\n // position reset or window resize keeps it attached. A ball in the lower\n // half of the viewport opens the panel above it, upper half below.\n useEffect(() => {\n if (!open) return\n const update = () => {\n const rect = ballRef.current?.getBoundingClientRect()\n if (!rect) return\n const left = Math.min(Math.max(8, rect.left), Math.max(8, window.innerWidth - 336))\n setPanelPos(\n rect.top > window.innerHeight / 2\n ? { left, bottom: window.innerHeight - rect.top + 6 }\n : { left, top: rect.bottom + 6 },\n )\n }\n update()\n window.addEventListener('resize', update)\n return () => window.removeEventListener('resize', update)\n }, [open, floatPos])\n\n // Keep the docked ball inside the viewport across window resizes.\n useEffect(() => {\n const clampPos = () => {\n setFloatPos((current) => {\n const x = Math.min(Math.max(8, current.x), window.innerWidth - BALL_SIZE - 8)\n const y = Math.min(Math.max(8, current.y), window.innerHeight - BALL_SIZE - 8)\n if (x === current.x && y === current.y) return current\n const next = { x, y }\n storeFloatPos(next)\n return next\n })\n }\n clampPos()\n window.addEventListener('resize', clampPos)\n return () => window.removeEventListener('resize', clampPos)\n }, [])\n\n // First run (no stored position): the default anchors to the composer send\n // button, which mounts after this plugin — poll briefly until it appears.\n useEffect(() => {\n if (posPinned.current) return\n let tries = 0\n const timer = setInterval(() => {\n tries += 1\n if (posPinned.current || tries > 20) {\n clearInterval(timer)\n return\n }\n const next = defaultFloatPos()\n setFloatPos((current) => (current.x === next.x && current.y === next.y ? current : next))\n }, 400)\n return () => clearInterval(timer)\n }, [])\n\n const tone = healthTone(data, error)\n\n const onKeyDown = (event: React.KeyboardEvent) => {\n if (event.key !== 'Escape' || !open) return\n event.preventDefault()\n setOpen(false)\n }\n\n /* Ball dragging: pointer events only (touch included via touch-action:\n none). A press becomes a drag past a 5px dead zone; while dragging the\n ball follows the cursor centered on it, and on release it stays exactly\n where dropped (no edge snapping). A plain press falls through to the\n click handler. */\n const onBallPointerDown = (event: React.PointerEvent<HTMLButtonElement>) => {\n event.currentTarget.setPointerCapture(event.pointerId)\n dragRef.current = { pointerId: event.pointerId, startX: event.clientX, startY: event.clientY, dragging: false }\n }\n\n const onBallPointerMove = (event: React.PointerEvent<HTMLButtonElement>) => {\n const drag = dragRef.current\n if (drag === null || drag.pointerId !== event.pointerId) return\n if (!drag.dragging) {\n if (Math.abs(event.clientX - drag.startX) < 5 && Math.abs(event.clientY - drag.startY) < 5) return\n drag.dragging = true\n // The panel would detach from a moving anchor: close it on drag start.\n if (open) setOpen(false)\n }\n const half = BALL_SIZE / 2\n setDragPos({\n x: Math.min(Math.max(half, event.clientX), window.innerWidth - half),\n y: Math.min(Math.max(half, event.clientY), window.innerHeight - half),\n })\n }\n\n const endBallDrag = (event: React.PointerEvent<HTMLButtonElement>) => {\n const drag = dragRef.current\n dragRef.current = null\n if (drag === null || drag.pointerId !== event.pointerId || !drag.dragging) return\n const half = BALL_SIZE / 2\n const x = Math.min(Math.max(half, event.clientX), window.innerWidth - half)\n const y = Math.min(Math.max(half, event.clientY), window.innerHeight - half)\n const next: FloatPos = { x: x - half, y: y - half }\n setFloatPos(next)\n storeFloatPos(next)\n setDragPos(null)\n posPinned.current = true\n suppressClickRef.current = true\n }\n\n const cancelBallDrag = (event: React.PointerEvent<HTMLButtonElement>) => {\n if (dragRef.current?.pointerId !== event.pointerId) return\n dragRef.current = null\n setDragPos(null)\n }\n\n const onBallClick = () => {\n if (suppressClickRef.current) {\n suppressClickRef.current = false\n return\n }\n setNow(Date.now())\n setOpen((current) => !current)\n if (!open) refresh()\n }\n\n return (\n <div ref={rootRef} className=\"dsh-usage-root\" onKeyDown={onKeyDown}>\n {createPortal(\n <button\n ref={ballRef}\n type=\"button\"\n className={`dsh-usage-ball dsh-usage-tone-${tone}`}\n style={\n dragPos !== null\n ? { left: dragPos.x - BALL_SIZE / 2, top: dragPos.y - BALL_SIZE / 2 }\n : { left: floatPos.x, top: floatPos.y }\n }\n aria-expanded={open}\n aria-label={tt('action.aria')}\n title={`${tt('action.aria')} · ${tt(`status.tone.${tone}`)}`}\n onPointerDown={onBallPointerDown}\n onPointerMove={onBallPointerMove}\n onPointerUp={endBallDrag}\n onPointerCancel={cancelBallDrag}\n onClick={onBallClick}\n onKeyDown={onKeyDown}\n >\n <BatteryIcon />\n </button>,\n document.body,\n )}\n {open && panelPos !== null\n ? createPortal(\n <div\n ref={panelRef}\n className=\"dsh-usage-panel\"\n role=\"dialog\"\n aria-label={tt('panel.title')}\n style={{ left: panelPos.left, top: panelPos.top, bottom: panelPos.bottom }}\n onKeyDown={onKeyDown}\n >\n <div className=\"dsh-usage-head\">\n <span className=\"dsh-usage-title\">{tt('panel.title')}</span>\n {data?.version ? <span className=\"dsh-usage-version\">v{data.version}</span> : null}\n <button\n type=\"button\"\n className=\"dsh-usage-home\"\n onClick={resetFloatPos}\n title={tt('panel.resetPos')}\n aria-label={tt('panel.resetPos')}\n >\n <HomeIcon />\n </button>\n <button\n type=\"button\"\n className=\"dsh-usage-lang\"\n onClick={toggleLang}\n title={tt('lang.switch')}\n aria-label={tt('lang.switch')}\n >\n <GlobeIcon />\n <span>{lang === 'zh' ? '中' : 'EN'}</span>\n </button>\n <button\n type=\"button\"\n className={`dsh-usage-refresh${loading ? ' dsh-usage-refresh--loading' : ''}`}\n disabled={loading}\n onClick={refresh}\n title={tt('panel.refresh')}\n aria-label={tt('panel.refresh')}\n >\n <RefreshIcon />\n </button>\n </div>\n {data === null && error !== null ? (\n <span className=\"dsh-usage-message\">{tt('status.error')}: {error}</span>\n ) : null}\n {data !== null && data.providers.length === 0 ? (\n <span className=\"dsh-usage-message\">{tt('panel.empty')}</span>\n ) : null}\n {(data?.providers ?? []).map((provider) => (\n <ProviderCard key={provider.id} provider={provider} now={now} t={tt} />\n ))}\n <div className=\"dsh-usage-foot\">\n <span className=\"dsh-usage-footLabel\">{tt('panel.interval')}</span>\n <select\n className=\"dsh-usage-select\"\n value={intervalSec}\n onChange={(event) => {\n const value = Number(event.target.value)\n setIntervalSec(value)\n storeInterval(value)\n }}\n >\n {INTERVAL_OPTIONS.map((seconds) => (\n <option key={seconds} value={seconds}>{tt(`interval.${seconds}s`)}</option>\n ))}\n </select>\n <span className=\"dsh-usage-updated\">\n {data === null\n ? tt('panel.never')\n : tt('panel.updated', { time: new Date(data.fetchedAt).toLocaleTimeString(lang === 'zh' ? 'zh-CN' : 'en-US') })}\n </span>\n </div>\n </div>,\n document.body,\n )\n : null}\n </div>\n )\n}\n","/**\n * Panel stylesheet, injected once per page. Follows the shipped plugins'\n * convention of a `<style data-plugin-css>` tag and design-token colors.\n */\nconst CSS = `\n.dsh-usage-root { position: relative; display: inline-flex; }\n/* Tri-tone health halo: only the shadow carries the worst provider state\n (green ok / amber remaining<30% / red error or usage>=90%) — the button\n face itself stays neutral. A 1px tone ring plus a soft halo keeps the\n state readable on both light and dark skins. */\n.dsh-usage-tone-ok { --dsh-usage-tone: #22a06b; }\n.dsh-usage-tone-warn { --dsh-usage-tone: #e2b93b; }\n.dsh-usage-tone-danger { --dsh-usage-tone: #d94f4f; }\n/* Floating ball trigger: a draggable fixed circle portaled to document.body,\n one layer below the panel (1000) so the popover always covers it. Position\n comes from inline style: docked it sits at its stored coordinates, while\n dragging it follows the cursor. Keeps the shell's lv3 lift shadow; the\n tone halo stacks on top. */\n.dsh-usage-ball {\n position: fixed; z-index: 999; width: 32px; height: 32px; border-radius: 50%;\n cursor: pointer; touch-action: none;\n display: inline-flex; align-items: center; justify-content: center;\n border: 1px solid var(--dsw-alias-border-l2);\n background-color: var(--dsw-specific-menu);\n color: var(--dsw-alias-label-tertiary);\n transition: box-shadow 0.15s;\n}\n.dsh-usage-ball:hover, .dsh-usage-ball:focus-visible { color: var(--dsw-alias-label-secondary); }\n.dsh-usage-ball svg { width: 15px; height: 15px; }\n.dsh-usage-ball.dsh-usage-tone-ok,\n.dsh-usage-ball.dsh-usage-tone-warn,\n.dsh-usage-ball.dsh-usage-tone-danger {\n box-shadow:\n var(--dsw-shadow-lv3),\n 0 0 0 1px color-mix(in srgb, var(--dsh-usage-tone) 40%, transparent),\n 0 0 14px color-mix(in srgb, var(--dsh-usage-tone) 55%, transparent);\n}\n.dsh-usage-ball.dsh-usage-tone-ok:hover,\n.dsh-usage-ball.dsh-usage-tone-warn:hover,\n.dsh-usage-ball.dsh-usage-tone-danger:hover,\n.dsh-usage-ball.dsh-usage-tone-ok:focus-visible,\n.dsh-usage-ball.dsh-usage-tone-warn:focus-visible,\n.dsh-usage-ball.dsh-usage-tone-danger:focus-visible {\n box-shadow:\n var(--dsw-shadow-lv3),\n 0 0 0 1px color-mix(in srgb, var(--dsh-usage-tone) 55%, transparent),\n 0 0 18px color-mix(in srgb, var(--dsh-usage-tone) 70%, transparent);\n}\n.dsh-usage-panel {\n z-index: 1000; box-sizing: border-box;\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-specific-menu);\n box-shadow: var(--dsw-shadow-lv3);\n border-radius: 12px; margin: 0; padding: 8px;\n width: 320px; max-width: min(360px, 100vw - 16px);\n max-height: min(460px, 100vh - 60px); overflow: auto;\n display: flex; flex-direction: column; gap: 6px;\n /* position: fixed with viewport coordinates computed inline from the\n trigger rect — absolute positioning inside the sidebar gets clipped by\n the column's overflow. */\n position: fixed;\n --dsh-scrollbar-thumb: var(--dsw-alias-scrollbar-bg-l2);\n --dsh-scrollbar-thumb-hover: var(--dsw-alias-scrollbar-hover-l2);\n}\n.dsh-usage-head {\n display: flex; align-items: center; gap: 8px; padding: 2px 4px 6px;\n border-bottom: 1px solid var(--dsw-alias-border-l2);\n}\n.dsh-usage-title { color: var(--dsw-alias-label-primary); font-size: 13px; font-weight: 600; }\n.dsh-usage-version {\n color: var(--dsw-alias-label-tertiary); font-size: 11px; line-height: 16px;\n padding: 0 5px; border-radius: 4px; background: var(--dsw-alias-fill-l2);\n font-variant-numeric: tabular-nums; user-select: none; margin-right: auto;\n}\n.dsh-usage-lang {\n cursor: pointer; display: inline-flex; align-items: center; gap: 4px;\n border: 0; border-radius: 6px; background: 0; padding: 3px 5px;\n color: var(--dsw-alias-label-tertiary); font-size: 11px; line-height: 16px;\n}\n.dsh-usage-lang:hover { color: var(--dsw-alias-label-secondary); }\n.dsh-usage-refresh {\n cursor: pointer; display: inline-flex; align-items: center; justify-content: center;\n border: 0; border-radius: 6px; background: 0; padding: 4px;\n color: var(--dsw-alias-label-tertiary); line-height: 0;\n}\n.dsh-usage-refresh:hover { color: var(--dsw-alias-label-secondary); }\n.dsh-usage-refresh:disabled { opacity: 0.5; cursor: default; }\n/* Home button in the panel head: sends the ball back to its default spot. */\n.dsh-usage-home {\n cursor: pointer; display: inline-flex; align-items: center; justify-content: center;\n border: 0; border-radius: 6px; background: 0; padding: 4px;\n color: var(--dsw-alias-label-tertiary); line-height: 0;\n}\n.dsh-usage-home:hover { color: var(--dsw-alias-label-secondary); }\n@keyframes dsh-usage-spin { to { transform: rotate(360deg); } }\n.dsh-usage-refresh--loading svg { animation: dsh-usage-spin 0.9s linear infinite; }\n.dsh-usage-card {\n border-radius: 8px; padding: 8px 10px; display: flex; flex-direction: column; gap: 6px;\n background: var(--dsw-alias-fill-l2);\n}\n.dsh-usage-cardHead { display: flex; align-items: center; gap: 8px; }\n.dsh-usage-providerName {\n color: var(--dsw-alias-label-primary); font-size: 13px; font-weight: 600;\n min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1;\n}\n.dsh-usage-message { color: var(--dsw-alias-label-tertiary); font-size: 12px; line-height: 18px; word-break: break-all; }\n.dsh-usage-balanceRow { display: flex; align-items: baseline; gap: 8px; }\n.dsh-usage-balanceTotal {\n color: var(--dsw-alias-label-primary); font-size: 18px; font-weight: 600;\n font-variant-numeric: tabular-nums;\n}\n.dsh-usage-balanceCurrency { color: var(--dsw-alias-label-secondary); font-size: 12px; }\n.dsh-usage-balanceParts { color: var(--dsw-alias-label-tertiary); font-size: 11px; line-height: 16px; }\n.dsh-usage-row { display: flex; flex-direction: column; gap: 3px; }\n.dsh-usage-rowHead { display: flex; align-items: center; gap: 8px; font-size: 12px; line-height: 18px; }\n.dsh-usage-rowLabel { color: var(--dsw-alias-label-secondary); flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.dsh-usage-rowValue { color: var(--dsw-alias-label-tertiary); font-variant-numeric: tabular-nums; flex: none; }\n.dsh-usage-barTrack {\n height: 4px; border-radius: 2px; overflow: hidden;\n background: var(--dsw-alias-border-l2);\n}\n.dsh-usage-barFill { height: 100%; border-radius: 2px; transition: width 0.3s; }\n.dsh-usage-barFill--ok { background: #22a06b; }\n.dsh-usage-barFill--warn { background: #e2b93b; }\n.dsh-usage-barFill--danger { background: #d94f4f; }\n.dsh-usage-rowReset { color: var(--dsw-alias-label-tertiary); font-size: 11px; line-height: 16px; }\n.dsh-usage-foot {\n display: flex; align-items: center; gap: 8px; padding: 6px 4px 2px;\n border-top: 1px solid var(--dsw-alias-border-l2);\n}\n.dsh-usage-footLabel { color: var(--dsw-alias-label-tertiary); font-size: 11px; }\n.dsh-usage-select {\n border: 1px solid var(--dsw-alias-border-l2); border-radius: 6px; background: 0;\n color: var(--dsw-alias-label-secondary); font-size: 11px; padding: 2px 4px; cursor: pointer;\n}\n.dsh-usage-updated { color: var(--dsw-alias-label-tertiary); font-size: 11px; margin-left: auto; }\n`\n\nconst TAG_ID = 'dsh-provider-usage/panel.css'\n\n/** Inject the panel stylesheet once; on HMR swap, refresh the existing tag's payload in place. */\nexport function ensureStyles(): void {\n if (typeof document === 'undefined') return\n const existing = document.querySelector('style[data-plugin-css=' + JSON.stringify(TAG_ID) + ']')\n if (existing !== null) {\n // A hot-swapped bundle re-runs this module while the old <style> tag is\n // still in the DOM — sync its content so the new CSS actually applies.\n if (existing.textContent !== CSS) existing.textContent = CSS\n return\n }\n const tag = document.createElement('style')\n tag.dataset.plugin = 'dsh-provider-usage'\n tag.dataset.pluginCss = TAG_ID\n tag.textContent = CSS\n document.head.appendChild(tag)\n}\n","/**\n * provider-usage — browser half.\n *\n * Registers the locale dictionaries and contributes the usage widget into the\n * sidebar footer's additive action slot. Data comes from the host `usage`\n * Typert Remote (SRC mode — no generated descriptors) through the raw RPC\n * caller on the connection service.\n */\nimport UsageAction, { type UsageActionProps } from './Panel'\nimport { en, zh } from './locales'\nimport { ensureStyles } from './styles'\n\n/** Required client services: slot registry, locale seats, and the connection RPC caller. */\nconst inject = ['slots', 'locale', 'connection']\n\ninterface ClientContext {\n effect: (fn: () => unknown, label: string) => void\n locale: { register: (ns: string, dicts: { zh: unknown; en: unknown }) => unknown }\n slots: {\n inject: (name: string, callback: () => unknown) => void\n register: (entry: Record<string, unknown>, component: unknown) => unknown\n }\n connection: {\n rpc: {\n call: (\n channel: string,\n endpoint: string,\n payload: { args: Record<string, unknown> },\n signal?: AbortSignal,\n ) => Promise<{ ok: true; value: unknown } | { ok: false; error: { code: string; message: string } }>\n }\n }\n}\n\nfunction apply(ctx: ClientContext) {\n ensureStyles()\n ctx.effect(() => ctx.locale.register('provider-usage', { zh, en }), 'provider-usage: dictionaries')\n\n const fetchUsage: UsageActionProps['fetchUsage'] = async () => {\n const result = await ctx.connection.rpc.call('/api', 'usage/list', { args: {} })\n if (!result.ok) throw new Error(`${result.error.code}: ${result.error.message}`)\n return result.value as Awaited<ReturnType<UsageActionProps['fetchUsage']>>\n }\n\n ctx.slots.inject('sidebar.footer.action', () =>\n ctx.slots.register(\n {\n name: 'sidebar.footer.action',\n id: 'provider-usage',\n order: 10,\n locale: 'provider-usage',\n inject: () => ({ fetchUsage }),\n },\n UsageAction,\n ),\n )\n}\n\nexport { apply, inject }\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.js","names":["useState","useMemo","useCallback","useRef","createPortal"],"sources":["../src/client/locales.ts","../src/client/Panel.tsx","../src/client/styles.ts","../src/client/index.ts"],"sourcesContent":["/** Simplified Chinese dictionary (the key-set source of truth). */\nexport const zh = {\n 'action.label': '用量',\n 'action.aria': 'Provider 用量',\n 'panel.title': 'Provider 用量',\n 'panel.refresh': '立即刷新',\n 'panel.refreshing': '刷新中…',\n 'lang.switch': 'Switch to English',\n 'panel.resetPos': '悬浮球归位',\n 'panel.interval': '刷新周期',\n 'panel.updated': '更新于 {time}',\n 'panel.never': '尚未获取',\n 'panel.empty': '未检测到已配置的 Provider',\n 'interval.15s': '15 秒',\n 'interval.30s': '30 秒',\n 'interval.60s': '1 分钟',\n 'interval.300s': '5 分钟',\n 'interval.900s': '15 分钟',\n 'interval.1800s': '30 分钟',\n 'balance.total': '总额',\n 'balance.granted': '赠送 {amount}',\n 'balance.toppedUp': '充值 {amount}',\n 'usage.weekly': '每周配额',\n 'usage.used': '已用 {used}',\n 'usage.remaining': '剩余 {remaining}',\n 'usage.unlimited': '不限量',\n 'usage.resetAt': '重置于 {time}',\n 'usage.resetIn': '{countdown} 后重置',\n 'status.unsupported': '该 Provider 暂不支持额度查询',\n 'status.missingCredential': '未配置密钥({ref})',\n 'status.error': '查询失败',\n 'status.tone.ok': '全部正常',\n 'status.tone.warn': '有用量接近上限',\n 'status.tone.danger': '有 Provider 异常或用量将尽',\n 'countdown.days': '{days}天{hours}小时',\n 'countdown.hours': '{hours}小时{minutes}分',\n 'countdown.minutes': '{minutes}分钟',\n}\n\n/** English dictionary, key-identical to the Chinese source of truth. */\nexport const en = {\n 'action.label': 'Usage',\n 'action.aria': 'Provider usage',\n 'panel.title': 'Provider Usage',\n 'panel.refresh': 'Refresh now',\n 'panel.refreshing': 'Refreshing…',\n 'lang.switch': '切换到中文',\n 'panel.resetPos': 'Reset ball position',\n 'panel.interval': 'Refresh interval',\n 'panel.updated': 'Updated {time}',\n 'panel.never': 'Not fetched yet',\n 'panel.empty': 'No configured provider detected',\n 'interval.15s': '15s',\n 'interval.30s': '30s',\n 'interval.60s': '1 min',\n 'interval.300s': '5 min',\n 'interval.900s': '15 min',\n 'interval.1800s': '30 min',\n 'balance.total': 'Total',\n 'balance.granted': 'granted {amount}',\n 'balance.toppedUp': 'topped up {amount}',\n 'usage.weekly': 'Weekly quota',\n 'usage.used': 'used {used}',\n 'usage.remaining': '{remaining} left',\n 'usage.unlimited': 'Unlimited',\n 'usage.resetAt': 'Resets at {time}',\n 'usage.resetIn': 'Resets in {countdown}',\n 'status.unsupported': 'Usage query is not supported for this provider',\n 'status.missingCredential': 'No API key configured ({ref})',\n 'status.error': 'Query failed',\n 'status.tone.ok': 'All providers healthy',\n 'status.tone.warn': 'Some usage nearing its limit',\n 'status.tone.danger': 'Provider error or usage almost exhausted',\n 'countdown.days': '{days}d {hours}h',\n 'countdown.hours': '{hours}h {minutes}m',\n 'countdown.minutes': '{minutes}m',\n}\n","/**\n * Floating-ball quota widget: a draggable ball portaled to document.body\n * (the sidebar slot only mounts the component; the button itself no longer\n * lives in the sidebar, avoiding slot layout contention with other plugins).\n * The popover panel lists every configured provider's live balance/quota.\n * Polling interval is user-selectable (persisted in localStorage) and falls\n * back to the deployment-suggested value from the host plugin config.\n */\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { createPortal } from 'react-dom'\nimport { en, zh } from './locales'\n\n/* ------------------------------------------------------------------ *\n * Wire views (mirror of the host half)\n * ------------------------------------------------------------------ */\n\ninterface BalanceRow {\n currency: string\n total: string\n granted: string\n toppedUp: string\n}\n\ninterface UsageRow {\n label: string\n used: number | null\n limit: number | null\n remaining: number | null\n percent: number | null\n resetAt: string | null\n}\n\ninterface ProviderUsageView {\n id: string\n displayName: string\n kind: 'balance' | 'usage' | null\n status: 'ok' | 'error' | 'missing-credential' | 'unsupported'\n message: string | null\n balances: BalanceRow[] | null\n usages: UsageRow[] | null\n}\n\ninterface UsageListResult {\n fetchedAt: string\n refreshSeconds: number\n /** Plugin package version (absent on older hosts). */\n version?: string\n providers: ProviderUsageView[]\n}\n\nexport interface UsageActionProps {\n /** Sidebar expansion state, owned by the sidebar footer slot. Not read\n directly — a change re-renders us, which re-anchors the default dock. */\n wide?: boolean\n /** Namespace translator injected by the locale seat. */\n t: (key: string, params?: Record<string, unknown>) => string\n /** Business face: call the host `usage/list` remote. */\n fetchUsage: () => Promise<UsageListResult>\n}\n\n/* ------------------------------------------------------------------ *\n * Refresh interval\n * ------------------------------------------------------------------ */\n\nconst INTERVAL_OPTIONS = [15, 30, 60, 300, 900, 1800] as const\nconst STORAGE_KEY = 'dsh.provider-usage.refreshSeconds'\nconst LANG_KEY = 'dsh.provider-usage.lang'\nconst POS_KEY = 'dsh.provider-usage.floatPos'\nconst DEFAULT_INTERVAL = 60\n\n/** Floating ball diameter in px; drag math derives from it. */\nconst BALL_SIZE = 32\n\ntype Lang = 'zh' | 'en'\n/** Docked ball position: the ball CENTER as fractions of the viewport, so\n both the default spot and dragged spots follow window resizes. */\ninterface FloatPos {\n fx: number\n fy: number\n}\n\n/** Panel-level override stored in localStorage; null means follow the harness language. */\nfunction readStoredLang(): Lang | null {\n try {\n const raw = localStorage.getItem(LANG_KEY)\n return raw === 'zh' || raw === 'en' ? raw : null\n } catch {\n return null\n }\n}\n\nfunction storeLang(lang: Lang): void {\n try {\n localStorage.setItem(LANG_KEY, lang)\n } catch {\n /* storage unavailable: keep the in-memory value only */\n }\n}\n\n/** Local translator over the bundled dictionaries, mirroring the shell's `{param}` interpolation. */\nfunction makeT(dict: Record<string, string>): UsageActionProps['t'] {\n return (key, params) => {\n const template = dict[key] ?? key\n if (params === undefined) return template\n return template.replace(/\\{(\\w+)\\}/g, (raw, name: string) => (name in params ? String(params[name]) : raw))\n }\n}\n\nfunction readStoredInterval(): number | null {\n try {\n const raw = localStorage.getItem(STORAGE_KEY)\n if (raw === null) return null\n const value = Number(raw)\n return Number.isFinite(value) && value >= 5 ? value : null\n } catch {\n return null\n }\n}\n\nfunction storeInterval(value: number): void {\n try {\n localStorage.setItem(STORAGE_KEY, String(value))\n } catch {\n /* storage unavailable: keep the in-memory value only */\n }\n}\n\n/* ------------------------------------------------------------------ *\n * Floating ball position\n * ------------------------------------------------------------------ */\n\n/** Equal margin from the chat area's left edge and the window's bottom edge. */\nconst DOCK_MARGIN = 24\n\n/**\n * Default dock: bottom-left of the main chat area with equal margins on both\n * axes. Returned as viewport fractions of the ball CENTER, like every other\n * position. The chat area's left edge is the sidebar's right edge, derived\n * from the full-height ancestor of the settings slot (stable data-slot hook;\n * class names are build-hashed), so it adapts to sidebar collapse.\n */\nfunction defaultFloatPos(): FloatPos {\n let sidebarRight = 264\n let node = document.querySelector('[data-slot=\"sidebar.settings\"]')?.parentElement ?? null\n while (node) {\n const rect = node.getBoundingClientRect()\n if (rect.x <= 1 && rect.height >= window.innerHeight * 0.9) {\n sidebarRight = rect.right\n break\n }\n node = node.parentElement\n }\n return {\n fx: (sidebarRight + DOCK_MARGIN + BALL_SIZE / 2) / window.innerWidth,\n fy: (window.innerHeight - DOCK_MARGIN - BALL_SIZE / 2) / window.innerHeight,\n }\n}\n\nfunction readStoredFloatPos(): FloatPos | null {\n try {\n const raw = localStorage.getItem(POS_KEY)\n if (raw === null) return null\n const parsed = JSON.parse(raw) as Partial<FloatPos> | null\n if (parsed === null || typeof parsed.fx !== 'number' || !Number.isFinite(parsed.fx)) return null\n if (typeof parsed.fy !== 'number' || !Number.isFinite(parsed.fy)) return null\n return { fx: Math.min(1, Math.max(0, parsed.fx)), fy: Math.min(1, Math.max(0, parsed.fy)) }\n } catch {\n return null\n }\n}\n\nfunction storeFloatPos(pos: FloatPos): void {\n try {\n localStorage.setItem(POS_KEY, JSON.stringify(pos))\n } catch {\n /* storage unavailable: keep the in-memory value only */\n }\n}\n\n/** 归位: forget the pinned spot; the ball follows the default dock again. */\nfunction clearStoredFloatPos(): void {\n try {\n localStorage.removeItem(POS_KEY)\n } catch {\n /* storage unavailable */\n }\n}\n\n/* ------------------------------------------------------------------ *\n * Formatting helpers\n * ------------------------------------------------------------------ */\n\nfunction formatCountdown(resetAt: string, now: number, t: UsageActionProps['t']): string | null {\n const diff = new Date(resetAt).getTime() - now\n if (!Number.isFinite(diff) || diff <= 0) return null\n const minutes = Math.floor(diff / 60000)\n if (minutes >= 60 * 24) return t('countdown.days', { days: Math.floor(minutes / (60 * 24)), hours: Math.floor((minutes % (60 * 24)) / 60) })\n if (minutes >= 60) return t('countdown.hours', { hours: Math.floor(minutes / 60), minutes: minutes % 60 })\n return t('countdown.minutes', { minutes: Math.max(1, minutes) })\n}\n\nfunction barTone(percent: number | null): 'ok' | 'warn' | 'danger' {\n if (percent === null) return 'ok'\n if (percent >= 90) return 'danger'\n if (percent >= 70) return 'warn'\n return 'ok'\n}\n\n/** Worst health across the fetch + every provider: drives the trigger's status dot. */\nfunction healthTone(data: UsageListResult | null, error: string | null): 'ok' | 'warn' | 'danger' {\n if (error !== null) return 'danger'\n let tone: 'ok' | 'warn' | 'danger' = 'ok'\n for (const provider of data?.providers ?? []) {\n if (provider.status === 'error' || provider.status === 'missing-credential') return 'danger'\n for (const row of provider.usages ?? []) {\n const rowTone = barTone(row.percent)\n if (rowTone === 'danger') return 'danger'\n if (rowTone === 'warn') tone = 'warn'\n }\n }\n return tone\n}\n\nfunction formatAmount(value: number | null): string {\n if (value === null) return '—'\n return Number.isInteger(value) ? String(value) : value.toFixed(2)\n}\n\n/* ------------------------------------------------------------------ *\n * Provider card\n * ------------------------------------------------------------------ */\n\nfunction UsageRows({ provider, now, t }: { provider: ProviderUsageView; now: number; t: UsageActionProps['t'] }) {\n const rows = provider.usages ?? []\n if (rows.length === 0) return null\n return (\n <>\n {rows.map((row, index) => {\n const percent = row.percent\n const reset = row.resetAt !== null ? formatCountdown(row.resetAt, now, t) : null\n return (\n <div className=\"dsh-usage-row\" key={`${row.label}-${index}`}>\n <div className=\"dsh-usage-rowHead\">\n <span className=\"dsh-usage-rowLabel\">{row.label === 'weekly' ? t('usage.weekly') : row.label}</span>\n <span className=\"dsh-usage-rowValue\">\n {row.limit === null\n ? t('usage.unlimited')\n : percent !== null\n ? `${Math.round(percent)}%`\n : t('usage.used', { used: formatAmount(row.used) })}\n </span>\n </div>\n {percent !== null ? (\n <div className=\"dsh-usage-barTrack\">\n <div\n className={`dsh-usage-barFill dsh-usage-barFill--${barTone(percent)}`}\n style={{ width: `${Math.min(100, Math.max(0, percent))}%` }}\n />\n </div>\n ) : null}\n {reset !== null ? <span className=\"dsh-usage-rowReset\">{t('usage.resetIn', { countdown: reset })}</span> : null}\n </div>\n )\n })}\n </>\n )\n}\n\nfunction ProviderCard({ provider, now, t }: { provider: ProviderUsageView; now: number; t: UsageActionProps['t'] }) {\n return (\n <div className=\"dsh-usage-card\">\n <div className=\"dsh-usage-cardHead\">\n <span className=\"dsh-usage-providerName\">{provider.displayName}</span>\n </div>\n {provider.status === 'unsupported' ? (\n <span className=\"dsh-usage-message\">{t('status.unsupported')}</span>\n ) : provider.status === 'missing-credential' ? (\n <span className=\"dsh-usage-message\">{t('status.missingCredential', { ref: provider.message })}</span>\n ) : provider.status === 'error' ? (\n <span className=\"dsh-usage-message\">{t('status.error')}: {provider.message}</span>\n ) : provider.kind === 'balance' ? (\n (provider.balances ?? []).map((row, index) => (\n <div key={`${row.currency}-${index}`}>\n <div className=\"dsh-usage-balanceRow\">\n <span className=\"dsh-usage-balanceTotal\">{row.total}</span>\n <span className=\"dsh-usage-balanceCurrency\">{row.currency}</span>\n </div>\n <span className=\"dsh-usage-balanceParts\">\n {[row.granted !== '0' && row.granted !== '0.00' ? t('balance.granted', { amount: row.granted }) : null,\n row.toppedUp !== '0' && row.toppedUp !== '0.00' ? t('balance.toppedUp', { amount: row.toppedUp }) : null]\n .filter(Boolean)\n .join(' · ')}\n </span>\n </div>\n ))\n ) : (\n <UsageRows provider={provider} now={now} t={t} />\n )}\n </div>\n )\n}\n\n/* ------------------------------------------------------------------ *\n * Trigger + popover\n * ------------------------------------------------------------------ */\n\n/** Battery level: remaining charge reads as remaining quota. */\nfunction BatteryIcon() {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <rect width=\"16\" height=\"10\" x=\"2\" y=\"7\" rx=\"2\" ry=\"2\" />\n <path d=\"M22 11v2\" />\n <path d=\"M6 11v2\" />\n <path d=\"M10 11v2\" />\n </svg>\n )\n}\n\nfunction RefreshIcon() {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M20 12a8 8 0 1 1-2.34-5.66\" />\n <path d=\"M20 4v4h-4\" />\n </svg>\n )\n}\n\nfunction GlobeIcon() {\n return (\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"9\" />\n <path d=\"M3 12h18\" />\n <path d=\"M12 3c2.5 2.6 3.9 5.7 3.9 9s-1.4 6.4-3.9 9c-2.5-2.6-3.9-5.7-3.9-9S9.5 5.6 12 3z\" />\n </svg>\n )\n}\n\n/** Crosshair for the \"reset ball to its default spot\" head button. */\nfunction HomeIcon() {\n return (\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"7\" />\n <path d=\"M12 2v3M12 19v3M2 12h3M19 12h3\" />\n <circle cx=\"12\" cy=\"12\" r=\"1.5\" fill=\"currentColor\" stroke=\"none\" />\n </svg>\n )\n}\n\nexport default function UsageAction({ t, fetchUsage }: UsageActionProps) {\n const [open, setOpen] = useState(false)\n const [data, setData] = useState<UsageListResult | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [loading, setLoading] = useState(false)\n const [intervalSec, setIntervalSec] = useState(() => readStoredInterval() ?? DEFAULT_INTERVAL)\n const [langOverride, setLangOverride] = useState<Lang | null>(() => readStoredLang())\n const [now, setNow] = useState(() => Date.now())\n /** Pinned spot (user-dragged) as viewport fractions; null = follow the default dock. */\n const [pinnedPos, setPinnedPos] = useState<FloatPos | null>(() => readStoredFloatPos())\n /** Viewport size drives the fraction → px conversion and the default dock. */\n const [viewport, setViewport] = useState({ w: window.innerWidth, h: window.innerHeight })\n /** Transient ball position while dragging (cursor-centered); null when docked. */\n const [dragPos, setDragPos] = useState<{ x: number; y: number } | null>(null)\n\n // Follow the harness language by default: probe the shell-injected\n // translator with a known key, then let a panel-level override win.\n const harnessLang: Lang | null =\n t('lang.switch') === zh['lang.switch'] ? 'zh' : t('lang.switch') === en['lang.switch'] ? 'en' : null\n const lang: Lang = langOverride ?? harnessLang ?? 'en'\n const tt = useMemo(() => makeT(lang === 'zh' ? (zh as Record<string, string>) : (en as Record<string, string>)), [lang])\n const toggleLang = useCallback(() => {\n setLangOverride((current) => {\n const next: Lang = (current ?? harnessLang ?? 'en') === 'zh' ? 'en' : 'zh'\n storeLang(next)\n return next\n })\n }, [harnessLang])\n /** Panel-head home button: unpin the ball so it follows the default dock. */\n const resetFloatPos = () => {\n clearStoredFloatPos()\n setPinnedPos(null)\n }\n const [panelPos, setPanelPos] = useState<{ left: number; top?: number; bottom?: number } | null>(null)\n const rootRef = useRef<HTMLDivElement>(null)\n const ballRef = useRef<HTMLButtonElement>(null)\n const panelRef = useRef<HTMLDivElement>(null)\n const inFlight = useRef(false)\n /** Active ball-drag session; cleared on pointerup/pointercancel. */\n const dragRef = useRef<{ pointerId: number; startX: number; startY: number; dragging: boolean } | null>(null)\n /** Set when a drag ends so the trailing click does not toggle the panel. */\n const suppressClickRef = useRef(false)\n\n const refresh = useCallback(async () => {\n if (inFlight.current) return\n inFlight.current = true\n setLoading(true)\n try {\n const result = await fetchUsage()\n setData(result)\n setError(null)\n } catch (reason) {\n setError(reason instanceof Error ? reason.message : String(reason))\n } finally {\n inFlight.current = false\n setLoading(false)\n }\n }, [fetchUsage])\n\n // Initial fetch + fixed-interval polling; skip ticks while the tab is hidden.\n useEffect(() => {\n refresh()\n }, [refresh])\n useEffect(() => {\n const timer = setInterval(() => {\n if (document.visibilityState === 'visible') refresh()\n }, intervalSec * 1000)\n return () => clearInterval(timer)\n }, [intervalSec, refresh])\n\n // Adopt the deployment-suggested interval until the user picks one locally.\n useEffect(() => {\n if (data !== null && readStoredInterval() === null && data.refreshSeconds !== intervalSec) {\n setIntervalSec(data.refreshSeconds)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [data])\n\n // Slow tick so reset countdowns advance while the panel is open.\n useEffect(() => {\n if (!open) return\n setNow(Date.now())\n const timer = setInterval(() => setNow(Date.now()), 30000)\n return () => clearInterval(timer)\n }, [open])\n\n // Close on outside press / Escape, mirroring the shipped popover behavior.\n // The panel and the ball are portaled to document.body, so all trees count\n // as inside.\n useEffect(() => {\n if (!open) return\n const closeOutside = (event: PointerEvent) => {\n if (!(event.target instanceof Node)) return\n if (rootRef.current?.contains(event.target)) return\n if (ballRef.current?.contains(event.target)) return\n if (panelRef.current?.contains(event.target)) return\n setOpen(false)\n }\n document.addEventListener('pointerdown', closeOutside)\n return () => document.removeEventListener('pointerdown', closeOutside)\n }, [open])\n\n // Anchor the fixed-position panel to the ball; recompute while open so a\n // position reset or window resize keeps it attached. A ball in the lower\n // half of the viewport opens the panel above it, upper half below.\n useEffect(() => {\n if (!open) return\n const update = () => {\n const rect = ballRef.current?.getBoundingClientRect()\n if (!rect) return\n const left = Math.min(Math.max(8, rect.left), Math.max(8, window.innerWidth - 336))\n setPanelPos(\n rect.top > window.innerHeight / 2\n ? { left, bottom: window.innerHeight - rect.top + 6 }\n : { left, top: rect.bottom + 6 },\n )\n }\n update()\n window.addEventListener('resize', update)\n return () => window.removeEventListener('resize', update)\n }, [open, pinnedPos, viewport])\n\n // Track the viewport: fraction-based positions and the default dock follow\n // window resizes automatically.\n useEffect(() => {\n const onResize = () => setViewport({ w: window.innerWidth, h: window.innerHeight })\n window.addEventListener('resize', onResize)\n return () => window.removeEventListener('resize', onResize)\n }, [])\n\n const tone = healthTone(data, error)\n\n const onKeyDown = (event: React.KeyboardEvent) => {\n if (event.key !== 'Escape' || !open) return\n event.preventDefault()\n setOpen(false)\n }\n\n /* Ball dragging: pointer events only (touch included via touch-action:\n none). A press becomes a drag past a 5px dead zone; while dragging the\n ball follows the cursor centered on it, and on release it stays exactly\n where dropped (no edge snapping). A plain press falls through to the\n click handler. */\n const onBallPointerDown = (event: React.PointerEvent<HTMLButtonElement>) => {\n event.currentTarget.setPointerCapture(event.pointerId)\n dragRef.current = { pointerId: event.pointerId, startX: event.clientX, startY: event.clientY, dragging: false }\n }\n\n const onBallPointerMove = (event: React.PointerEvent<HTMLButtonElement>) => {\n const drag = dragRef.current\n if (drag === null || drag.pointerId !== event.pointerId) return\n if (!drag.dragging) {\n if (Math.abs(event.clientX - drag.startX) < 5 && Math.abs(event.clientY - drag.startY) < 5) return\n drag.dragging = true\n // The panel would detach from a moving anchor: close it on drag start.\n if (open) setOpen(false)\n }\n const half = BALL_SIZE / 2\n setDragPos({\n x: Math.min(Math.max(half, event.clientX), window.innerWidth - half),\n y: Math.min(Math.max(half, event.clientY), window.innerHeight - half),\n })\n }\n\n const endBallDrag = (event: React.PointerEvent<HTMLButtonElement>) => {\n const drag = dragRef.current\n dragRef.current = null\n if (drag === null || drag.pointerId !== event.pointerId || !drag.dragging) return\n const half = BALL_SIZE / 2\n const x = Math.min(Math.max(half, event.clientX), window.innerWidth - half)\n const y = Math.min(Math.max(half, event.clientY), window.innerHeight - half)\n // Pin the drop point as viewport fractions so it follows window resizes.\n const next: FloatPos = { fx: x / window.innerWidth, fy: y / window.innerHeight }\n setPinnedPos(next)\n storeFloatPos(next)\n setDragPos(null)\n suppressClickRef.current = true\n }\n\n const cancelBallDrag = (event: React.PointerEvent<HTMLButtonElement>) => {\n if (dragRef.current?.pointerId !== event.pointerId) return\n dragRef.current = null\n setDragPos(null)\n }\n\n const onBallClick = () => {\n if (suppressClickRef.current) {\n suppressClickRef.current = false\n return\n }\n setNow(Date.now())\n setOpen((current) => !current)\n if (!open) refresh()\n }\n\n // Ball center in px: transient drag point > pinned fractions > default dock.\n // (The default reads the sidebar edge from the DOM at render time; cheap.)\n const docked = pinnedPos ?? defaultFloatPos()\n const centerX = dragPos !== null ? dragPos.x : docked.fx * viewport.w\n const centerY = dragPos !== null ? dragPos.y : docked.fy * viewport.h\n\n return (\n <div ref={rootRef} className=\"dsh-usage-root\" onKeyDown={onKeyDown}>\n {createPortal(\n <button\n ref={ballRef}\n type=\"button\"\n className={`dsh-usage-ball dsh-usage-tone-${tone}`}\n style={{ left: centerX - BALL_SIZE / 2, top: centerY - BALL_SIZE / 2 }}\n aria-expanded={open}\n aria-label={tt('action.aria')}\n title={`${tt('action.aria')} · ${tt(`status.tone.${tone}`)}`}\n onPointerDown={onBallPointerDown}\n onPointerMove={onBallPointerMove}\n onPointerUp={endBallDrag}\n onPointerCancel={cancelBallDrag}\n onClick={onBallClick}\n onKeyDown={onKeyDown}\n >\n <BatteryIcon />\n </button>,\n document.body,\n )}\n {open && panelPos !== null\n ? createPortal(\n <div\n ref={panelRef}\n className=\"dsh-usage-panel\"\n role=\"dialog\"\n aria-label={tt('panel.title')}\n style={{ left: panelPos.left, top: panelPos.top, bottom: panelPos.bottom }}\n onKeyDown={onKeyDown}\n >\n <div className=\"dsh-usage-head\">\n <span className=\"dsh-usage-title\">{tt('panel.title')}</span>\n {data?.version ? <span className=\"dsh-usage-version\">v{data.version}</span> : null}\n <button\n type=\"button\"\n className=\"dsh-usage-home\"\n onClick={resetFloatPos}\n title={tt('panel.resetPos')}\n aria-label={tt('panel.resetPos')}\n >\n <HomeIcon />\n </button>\n <button\n type=\"button\"\n className=\"dsh-usage-lang\"\n onClick={toggleLang}\n title={tt('lang.switch')}\n aria-label={tt('lang.switch')}\n >\n <GlobeIcon />\n <span>{lang === 'zh' ? '中' : 'EN'}</span>\n </button>\n <button\n type=\"button\"\n className={`dsh-usage-refresh${loading ? ' dsh-usage-refresh--loading' : ''}`}\n disabled={loading}\n onClick={refresh}\n title={tt('panel.refresh')}\n aria-label={tt('panel.refresh')}\n >\n <RefreshIcon />\n </button>\n </div>\n {data === null && error !== null ? (\n <span className=\"dsh-usage-message\">{tt('status.error')}: {error}</span>\n ) : null}\n {data !== null && data.providers.length === 0 ? (\n <span className=\"dsh-usage-message\">{tt('panel.empty')}</span>\n ) : null}\n {(data?.providers ?? []).map((provider) => (\n <ProviderCard key={provider.id} provider={provider} now={now} t={tt} />\n ))}\n <div className=\"dsh-usage-foot\">\n <span className=\"dsh-usage-footLabel\">{tt('panel.interval')}</span>\n <select\n className=\"dsh-usage-select\"\n value={intervalSec}\n onChange={(event) => {\n const value = Number(event.target.value)\n setIntervalSec(value)\n storeInterval(value)\n }}\n >\n {INTERVAL_OPTIONS.map((seconds) => (\n <option key={seconds} value={seconds}>{tt(`interval.${seconds}s`)}</option>\n ))}\n </select>\n <span className=\"dsh-usage-updated\">\n {data === null\n ? tt('panel.never')\n : tt('panel.updated', { time: new Date(data.fetchedAt).toLocaleTimeString(lang === 'zh' ? 'zh-CN' : 'en-US') })}\n </span>\n </div>\n </div>,\n document.body,\n )\n : null}\n </div>\n )\n}\n","/**\n * Panel stylesheet, injected once per page. Follows the shipped plugins'\n * convention of a `<style data-plugin-css>` tag and design-token colors.\n */\nconst CSS = `\n.dsh-usage-root { position: relative; display: inline-flex; }\n/* Tri-tone health halo: only the shadow carries the worst provider state\n (green ok / amber remaining<30% / red error or usage>=90%) — the button\n face itself stays neutral. A 1px tone ring plus a soft halo keeps the\n state readable on both light and dark skins. */\n.dsh-usage-tone-ok { --dsh-usage-tone: #22a06b; }\n.dsh-usage-tone-warn { --dsh-usage-tone: #e2b93b; }\n.dsh-usage-tone-danger { --dsh-usage-tone: #d94f4f; }\n/* Floating ball trigger: a draggable fixed circle portaled to document.body,\n one layer below the panel (1000) so the popover always covers it. Position\n comes from inline style: docked it sits at its stored coordinates, while\n dragging it follows the cursor. Keeps the shell's lv3 lift shadow; the\n tone halo stacks on top. */\n.dsh-usage-ball {\n position: fixed; z-index: 999; width: 32px; height: 32px; border-radius: 50%;\n cursor: pointer; touch-action: none;\n display: inline-flex; align-items: center; justify-content: center;\n border: 1px solid var(--dsw-alias-border-l2);\n background-color: var(--dsw-specific-menu);\n color: var(--dsw-alias-label-tertiary);\n transition: box-shadow 0.15s;\n}\n.dsh-usage-ball:hover, .dsh-usage-ball:focus-visible { color: var(--dsw-alias-label-secondary); }\n.dsh-usage-ball svg { width: 15px; height: 15px; }\n.dsh-usage-ball.dsh-usage-tone-ok,\n.dsh-usage-ball.dsh-usage-tone-warn,\n.dsh-usage-ball.dsh-usage-tone-danger {\n box-shadow:\n var(--dsw-shadow-lv3),\n 0 0 0 1px color-mix(in srgb, var(--dsh-usage-tone) 40%, transparent),\n 0 0 14px color-mix(in srgb, var(--dsh-usage-tone) 55%, transparent);\n}\n.dsh-usage-ball.dsh-usage-tone-ok:hover,\n.dsh-usage-ball.dsh-usage-tone-warn:hover,\n.dsh-usage-ball.dsh-usage-tone-danger:hover,\n.dsh-usage-ball.dsh-usage-tone-ok:focus-visible,\n.dsh-usage-ball.dsh-usage-tone-warn:focus-visible,\n.dsh-usage-ball.dsh-usage-tone-danger:focus-visible {\n box-shadow:\n var(--dsw-shadow-lv3),\n 0 0 0 1px color-mix(in srgb, var(--dsh-usage-tone) 55%, transparent),\n 0 0 18px color-mix(in srgb, var(--dsh-usage-tone) 70%, transparent);\n}\n.dsh-usage-panel {\n z-index: 1000; box-sizing: border-box;\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-specific-menu);\n box-shadow: var(--dsw-shadow-lv3);\n border-radius: 12px; margin: 0; padding: 8px;\n width: 320px; max-width: min(360px, 100vw - 16px);\n max-height: min(460px, 100vh - 60px); overflow: auto;\n display: flex; flex-direction: column; gap: 6px;\n /* position: fixed with viewport coordinates computed inline from the\n trigger rect — absolute positioning inside the sidebar gets clipped by\n the column's overflow. */\n position: fixed;\n --dsh-scrollbar-thumb: var(--dsw-alias-scrollbar-bg-l2);\n --dsh-scrollbar-thumb-hover: var(--dsw-alias-scrollbar-hover-l2);\n}\n.dsh-usage-head {\n display: flex; align-items: center; gap: 8px; padding: 2px 4px 6px;\n border-bottom: 1px solid var(--dsw-alias-border-l2);\n}\n.dsh-usage-title { color: var(--dsw-alias-label-primary); font-size: 13px; font-weight: 600; }\n.dsh-usage-version {\n color: var(--dsw-alias-label-tertiary); font-size: 11px; line-height: 16px;\n padding: 0 5px; border-radius: 4px; background: var(--dsw-alias-fill-l2);\n font-variant-numeric: tabular-nums; user-select: none; margin-right: auto;\n}\n.dsh-usage-lang {\n cursor: pointer; display: inline-flex; align-items: center; gap: 4px;\n border: 0; border-radius: 6px; background: 0; padding: 3px 5px;\n color: var(--dsw-alias-label-tertiary); font-size: 11px; line-height: 16px;\n}\n.dsh-usage-lang:hover { color: var(--dsw-alias-label-secondary); }\n.dsh-usage-refresh {\n cursor: pointer; display: inline-flex; align-items: center; justify-content: center;\n border: 0; border-radius: 6px; background: 0; padding: 4px;\n color: var(--dsw-alias-label-tertiary); line-height: 0;\n}\n.dsh-usage-refresh:hover { color: var(--dsw-alias-label-secondary); }\n.dsh-usage-refresh:disabled { opacity: 0.5; cursor: default; }\n/* Home button in the panel head: sends the ball back to its default spot. */\n.dsh-usage-home {\n cursor: pointer; display: inline-flex; align-items: center; justify-content: center;\n border: 0; border-radius: 6px; background: 0; padding: 4px;\n color: var(--dsw-alias-label-tertiary); line-height: 0;\n}\n.dsh-usage-home:hover { color: var(--dsw-alias-label-secondary); }\n@keyframes dsh-usage-spin { to { transform: rotate(360deg); } }\n.dsh-usage-refresh--loading svg { animation: dsh-usage-spin 0.9s linear infinite; }\n.dsh-usage-card {\n border-radius: 8px; padding: 8px 10px; display: flex; flex-direction: column; gap: 6px;\n background: var(--dsw-alias-fill-l2);\n}\n.dsh-usage-cardHead { display: flex; align-items: center; gap: 8px; }\n.dsh-usage-providerName {\n color: var(--dsw-alias-label-primary); font-size: 13px; font-weight: 600;\n min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1;\n}\n.dsh-usage-message { color: var(--dsw-alias-label-tertiary); font-size: 12px; line-height: 18px; word-break: break-all; }\n.dsh-usage-balanceRow { display: flex; align-items: baseline; gap: 8px; }\n.dsh-usage-balanceTotal {\n color: var(--dsw-alias-label-primary); font-size: 18px; font-weight: 600;\n font-variant-numeric: tabular-nums;\n}\n.dsh-usage-balanceCurrency { color: var(--dsw-alias-label-secondary); font-size: 12px; }\n.dsh-usage-balanceParts { color: var(--dsw-alias-label-tertiary); font-size: 11px; line-height: 16px; }\n.dsh-usage-row { display: flex; flex-direction: column; gap: 3px; }\n.dsh-usage-rowHead { display: flex; align-items: center; gap: 8px; font-size: 12px; line-height: 18px; }\n.dsh-usage-rowLabel { color: var(--dsw-alias-label-secondary); flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.dsh-usage-rowValue { color: var(--dsw-alias-label-tertiary); font-variant-numeric: tabular-nums; flex: none; }\n.dsh-usage-barTrack {\n height: 4px; border-radius: 2px; overflow: hidden;\n background: var(--dsw-alias-border-l2);\n}\n.dsh-usage-barFill { height: 100%; border-radius: 2px; transition: width 0.3s; }\n.dsh-usage-barFill--ok { background: #22a06b; }\n.dsh-usage-barFill--warn { background: #e2b93b; }\n.dsh-usage-barFill--danger { background: #d94f4f; }\n.dsh-usage-rowReset { color: var(--dsw-alias-label-tertiary); font-size: 11px; line-height: 16px; }\n.dsh-usage-foot {\n display: flex; align-items: center; gap: 8px; padding: 6px 4px 2px;\n border-top: 1px solid var(--dsw-alias-border-l2);\n}\n.dsh-usage-footLabel { color: var(--dsw-alias-label-tertiary); font-size: 11px; }\n.dsh-usage-select {\n border: 1px solid var(--dsw-alias-border-l2); border-radius: 6px; background: 0;\n color: var(--dsw-alias-label-secondary); font-size: 11px; padding: 2px 4px; cursor: pointer;\n}\n.dsh-usage-updated { color: var(--dsw-alias-label-tertiary); font-size: 11px; margin-left: auto; }\n`\n\nconst TAG_ID = 'dsh-provider-usage/panel.css'\n\n/** Inject the panel stylesheet once; on HMR swap, refresh the existing tag's payload in place. */\nexport function ensureStyles(): void {\n if (typeof document === 'undefined') return\n const existing = document.querySelector('style[data-plugin-css=' + JSON.stringify(TAG_ID) + ']')\n if (existing !== null) {\n // A hot-swapped bundle re-runs this module while the old <style> tag is\n // still in the DOM — sync its content so the new CSS actually applies.\n if (existing.textContent !== CSS) existing.textContent = CSS\n return\n }\n const tag = document.createElement('style')\n tag.dataset.plugin = 'dsh-provider-usage'\n tag.dataset.pluginCss = TAG_ID\n tag.textContent = CSS\n document.head.appendChild(tag)\n}\n","/**\n * provider-usage — browser half.\n *\n * Registers the locale dictionaries and contributes the usage widget into the\n * sidebar footer's additive action slot. Data comes from the host `usage`\n * Typert Remote (SRC mode — no generated descriptors) through the raw RPC\n * caller on the connection service.\n */\nimport UsageAction, { type UsageActionProps } from './Panel'\nimport { en, zh } from './locales'\nimport { ensureStyles } from './styles'\n\n/** Required client services: slot registry, locale seats, and the connection RPC caller. */\nconst inject = ['slots', 'locale', 'connection']\n\ninterface ClientContext {\n effect: (fn: () => unknown, label: string) => void\n locale: { register: (ns: string, dicts: { zh: unknown; en: unknown }) => unknown }\n slots: {\n inject: (name: string, callback: () => unknown) => void\n register: (entry: Record<string, unknown>, component: unknown) => unknown\n }\n connection: {\n rpc: {\n call: (\n channel: string,\n endpoint: string,\n payload: { args: Record<string, unknown> },\n signal?: AbortSignal,\n ) => Promise<{ ok: true; value: unknown } | { ok: false; error: { code: string; message: string } }>\n }\n }\n}\n\nfunction apply(ctx: ClientContext) {\n ensureStyles()\n ctx.effect(() => ctx.locale.register('provider-usage', { zh, en }), 'provider-usage: dictionaries')\n\n const fetchUsage: UsageActionProps['fetchUsage'] = async () => {\n const result = await ctx.connection.rpc.call('/api', 'usage/list', { args: {} })\n if (!result.ok) throw new Error(`${result.error.code}: ${result.error.message}`)\n return result.value as Awaited<ReturnType<UsageActionProps['fetchUsage']>>\n }\n\n ctx.slots.inject('sidebar.footer.action', () =>\n ctx.slots.register(\n {\n name: 'sidebar.footer.action',\n id: 'provider-usage',\n order: 10,\n locale: 'provider-usage',\n inject: () => ({ fetchUsage }),\n },\n UsageAction,\n ),\n )\n}\n\nexport { apply, inject }\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-provider-usage",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"description": "DeepSeek Harness plugin: live account balance/usage panel for every configured LLM provider (draggable floating-ball widget, configurable refresh interval)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|