dsh-token-gauge 0.1.8 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client.mjs +124 -25
- package/package.json +6 -2
- package/src/client.ts +117 -33
package/lib/client.mjs
CHANGED
|
@@ -32,7 +32,6 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
32
32
|
var client_exports = {};
|
|
33
33
|
__export(client_exports, {
|
|
34
34
|
apply: () => apply,
|
|
35
|
-
inject: () => inject,
|
|
36
35
|
name: () => name
|
|
37
36
|
});
|
|
38
37
|
module.exports = __toCommonJS(client_exports);
|
|
@@ -966,34 +965,134 @@ function GaugePanel() {
|
|
|
966
965
|
}
|
|
967
966
|
});
|
|
968
967
|
}
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
968
|
+
function GaugeTab() {
|
|
969
|
+
return import_react.default.createElement("div", {
|
|
970
|
+
ref: gaugeRef,
|
|
971
|
+
className: "tg-panel-root",
|
|
972
|
+
style: {
|
|
973
|
+
width: "100%",
|
|
974
|
+
height: "100%",
|
|
975
|
+
boxSizing: "border-box",
|
|
976
|
+
overflow: "hidden",
|
|
977
|
+
background: "var(--dsw-alias-bg-base)",
|
|
978
|
+
color: "var(--dsw-alias-label-primary)"
|
|
979
|
+
}
|
|
980
|
+
});
|
|
981
|
+
}
|
|
982
|
+
var GaugeIcon = (size) => {
|
|
983
|
+
const s = typeof size === "number" && size > 0 ? size : 16;
|
|
984
|
+
const svgProps = {
|
|
985
|
+
width: s,
|
|
986
|
+
height: s,
|
|
987
|
+
viewBox: "0 0 16 16",
|
|
988
|
+
fill: "none",
|
|
989
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
990
|
+
"aria-hidden": "true"
|
|
983
991
|
};
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
992
|
+
const arc = import_react.default.createElement("path", {
|
|
993
|
+
d: "M2.6 11.6a5.6 5.6 0 1 1 10.8 0",
|
|
994
|
+
stroke: "currentColor",
|
|
995
|
+
strokeWidth: 1.5,
|
|
996
|
+
strokeLinecap: "round"
|
|
997
|
+
});
|
|
998
|
+
const needle = import_react.default.createElement("path", {
|
|
999
|
+
d: "M8 11.6 10.4 7.2",
|
|
1000
|
+
stroke: "currentColor",
|
|
1001
|
+
strokeWidth: 1.5,
|
|
1002
|
+
strokeLinecap: "round"
|
|
1003
|
+
});
|
|
1004
|
+
return import_react.default.createElement("svg", svgProps, arc, needle);
|
|
1005
|
+
};
|
|
1006
|
+
var TAB_ORDER = 90;
|
|
1007
|
+
var name = PLUGIN_ID;
|
|
1008
|
+
function findOurTab(snap) {
|
|
1009
|
+
if (!snap) return null;
|
|
1010
|
+
const trees = [snap.splits, snap.bottomSplits];
|
|
1011
|
+
for (const tree of trees) {
|
|
1012
|
+
if (!tree) continue;
|
|
1013
|
+
const stack = [tree];
|
|
1014
|
+
while (stack.length) {
|
|
1015
|
+
const node = stack.pop();
|
|
1016
|
+
if (!node) continue;
|
|
1017
|
+
if (Array.isArray(node.tabs)) {
|
|
1018
|
+
for (const t2 of node.tabs) if (t2 && t2.type === PLUGIN_ID) return t2;
|
|
1019
|
+
}
|
|
1020
|
+
if (Array.isArray(node.children)) stack.push(...node.children);
|
|
1021
|
+
if (node.splits) stack.push(node.splits);
|
|
1022
|
+
if (node.bottomSplits) stack.push(node.bottomSplits);
|
|
990
1023
|
}
|
|
991
|
-
} catch (e) {
|
|
992
|
-
console.error("[dsh-token-gauge] \u6CE8\u518C shell.overlay \u5931\u8D25", e);
|
|
993
1024
|
}
|
|
1025
|
+
return null;
|
|
1026
|
+
}
|
|
1027
|
+
function apply(ctx) {
|
|
1028
|
+
let unregisterTab = null;
|
|
1029
|
+
let mountedOverlay = false;
|
|
1030
|
+
let slotsService = null;
|
|
1031
|
+
let fallbackTimer = void 0;
|
|
1032
|
+
let openTimer = null;
|
|
1033
|
+
const stopSlots = ctx.inject(["slots"], (slotsCtx) => {
|
|
1034
|
+
slotsService = slotsCtx.slots;
|
|
1035
|
+
});
|
|
1036
|
+
const mountOverlay = (reason) => {
|
|
1037
|
+
if (mountedOverlay || unregisterTab) return;
|
|
1038
|
+
if (!slotsService || typeof slotsService.register !== "function") return;
|
|
1039
|
+
console.warn(`[dsh-token-gauge] ${reason}\uFF0C\u4EEA\u8868\u76D8\u6539\u4E3A\u53F3\u4E0B\u89D2\u6D6E\u5C42\u663E\u793A\uFF08\u4ECD\u53EF\u7528\uFF09\u3002`);
|
|
1040
|
+
try {
|
|
1041
|
+
const d = slotsService.register(
|
|
1042
|
+
{ name: "shell.overlay", id: PLUGIN_ID, order: 100, label: "Token \u4EEA\u8868\u76D8" },
|
|
1043
|
+
() => import_react.default.createElement(GaugePanel)
|
|
1044
|
+
);
|
|
1045
|
+
if (typeof d === "function") mountedOverlay = true;
|
|
1046
|
+
} catch (e) {
|
|
1047
|
+
console.error("[dsh-token-gauge] \u6D6E\u5C42\u515C\u5E95\u4E5F\u5931\u8D25", e);
|
|
1048
|
+
}
|
|
1049
|
+
};
|
|
1050
|
+
const stopSidebar = ctx.inject(["betterSidebar"], (sidebarCtx) => {
|
|
1051
|
+
const sidebar = sidebarCtx.betterSidebar;
|
|
1052
|
+
if (!sidebar || typeof sidebar.registerTab !== "function") return;
|
|
1053
|
+
unregisterTab = sidebar.registerTab({
|
|
1054
|
+
id: PLUGIN_ID,
|
|
1055
|
+
title: "Token \u4EEA\u8868\u76D8",
|
|
1056
|
+
single: true,
|
|
1057
|
+
order: TAB_ORDER,
|
|
1058
|
+
icon: GaugeIcon,
|
|
1059
|
+
component: () => import_react.default.createElement(GaugeTab)
|
|
1060
|
+
});
|
|
1061
|
+
if (typeof fallbackTimer !== "undefined") clearTimeout(fallbackTimer);
|
|
1062
|
+
let attempts = 0;
|
|
1063
|
+
openTimer = setInterval(() => {
|
|
1064
|
+
attempts++;
|
|
1065
|
+
const snap = sidebar.getSnapshot?.();
|
|
1066
|
+
const sid = snap?.sessionId;
|
|
1067
|
+
if (sid !== void 0 && typeof sidebar.openTab === "function") {
|
|
1068
|
+
try {
|
|
1069
|
+
sidebar.openTab({ type: PLUGIN_ID });
|
|
1070
|
+
} catch {
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
if (sid !== void 0 && findOurTab(snap)) {
|
|
1074
|
+
clearInterval(openTimer);
|
|
1075
|
+
openTimer = null;
|
|
1076
|
+
return;
|
|
1077
|
+
}
|
|
1078
|
+
if (attempts >= 45) {
|
|
1079
|
+
clearInterval(openTimer);
|
|
1080
|
+
openTimer = null;
|
|
1081
|
+
mountOverlay("better-sidebar \u5DF2\u6CE8\u518C\u4F46\u4F1A\u8BDD\u59CB\u7EC8\u672A\u5C31\u7EEA");
|
|
1082
|
+
}
|
|
1083
|
+
}, 800);
|
|
1084
|
+
return () => {
|
|
1085
|
+
if (unregisterTab) unregisterTab();
|
|
1086
|
+
};
|
|
1087
|
+
});
|
|
1088
|
+
fallbackTimer = setTimeout(() => {
|
|
1089
|
+
if (!unregisterTab) mountOverlay("\u672A\u53D6\u5F97 dsh-better-sidebar \u670D\u52A1\uFF08\u8BF7\u786E\u8BA4\u5176\u5DF2\u5B89\u88C5\u5E76\u542F\u7528\uFF09");
|
|
1090
|
+
}, 8e3);
|
|
994
1091
|
return () => {
|
|
995
|
-
if (typeof
|
|
996
|
-
if (
|
|
1092
|
+
if (typeof fallbackTimer !== "undefined") clearTimeout(fallbackTimer);
|
|
1093
|
+
if (openTimer) clearInterval(openTimer);
|
|
1094
|
+
if (typeof stopSidebar === "function") stopSidebar();
|
|
1095
|
+
if (typeof stopSlots === "function") stopSlots();
|
|
997
1096
|
};
|
|
998
1097
|
}
|
|
999
1098
|
; return module.exports; } });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-token-gauge",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "DSH 机车风格 Token 用量/费用仪表盘侧边栏插件(稳定版 0.1.1-rc.x 兼容)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.mjs",
|
|
@@ -60,7 +60,8 @@
|
|
|
60
60
|
},
|
|
61
61
|
"peerDependencies": {
|
|
62
62
|
"@deepseek-ai/cordis": "*",
|
|
63
|
-
"@deepseek-ai/dsh-host-webserver": "*"
|
|
63
|
+
"@deepseek-ai/dsh-host-webserver": "*",
|
|
64
|
+
"dsh-better-sidebar": ">=0.15.2"
|
|
64
65
|
},
|
|
65
66
|
"peerDependenciesMeta": {
|
|
66
67
|
"@deepseek-ai/cordis": {
|
|
@@ -68,6 +69,9 @@
|
|
|
68
69
|
},
|
|
69
70
|
"@deepseek-ai/dsh-host-webserver": {
|
|
70
71
|
"optional": true
|
|
72
|
+
},
|
|
73
|
+
"dsh-better-sidebar": {
|
|
74
|
+
"optional": true
|
|
71
75
|
}
|
|
72
76
|
},
|
|
73
77
|
"devDependencies": {
|
package/src/client.ts
CHANGED
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
// 通过同域 HTTP 与 Host 半边通信:
|
|
5
5
|
// GET /plugins/dsh-token-gauge/state -> 完整 State(JSON)
|
|
6
6
|
// POST /plugins/dsh-token-gauge/cmd -> {type,payload} -> 更新后 State
|
|
7
|
-
//
|
|
7
|
+
// 优先把仪表盘注册到 dsh-better-sidebar 的右侧工作台 Tab(停靠侧边栏),注册后轮询
|
|
8
|
+
// openTab 直到会话就绪并自动打开激活(解决“已挂载却未显示”:session 未就绪时 openTab
|
|
9
|
+
// 会静默 no-op);若 better-sidebar 始终不可用,回退到官方 shell.overlay 浮层兜底。
|
|
8
10
|
// 为避免与运行时 React 产生双实例 hook 冲突,本组件不调用任何 hook,
|
|
9
11
|
// 仅在 ref 回调里挂载一次原生 DOM 仪表盘,并自行轮询刷新。
|
|
10
12
|
// ============================================================================
|
|
@@ -223,6 +225,22 @@ function GaugePanel(): React.ReactElement {
|
|
|
223
225
|
})
|
|
224
226
|
}
|
|
225
227
|
|
|
228
|
+
// 停靠在 better-sidebar 工作台 Tab 内的面板:填满容器(非固定定位),同样零 hook、ref 挂原生 DOM。
|
|
229
|
+
function GaugeTab(): React.ReactElement {
|
|
230
|
+
return React.createElement('div', {
|
|
231
|
+
ref: gaugeRef,
|
|
232
|
+
className: 'tg-panel-root',
|
|
233
|
+
style: {
|
|
234
|
+
width: '100%',
|
|
235
|
+
height: '100%',
|
|
236
|
+
boxSizing: 'border-box',
|
|
237
|
+
overflow: 'hidden',
|
|
238
|
+
background: 'var(--dsw-alias-bg-base)',
|
|
239
|
+
color: 'var(--dsw-alias-label-primary)',
|
|
240
|
+
} as any,
|
|
241
|
+
})
|
|
242
|
+
}
|
|
243
|
+
|
|
226
244
|
// 侧边栏图标:better-sidebar 以 `icon(size)` 形式调用(见其内置 tab:
|
|
227
245
|
// `icon: (size) => jsx(Icon, { size })`),故这里接收数字尺寸。用 currentColor
|
|
228
246
|
// 着色以自适应明暗主题,纯 SVG 零依赖。
|
|
@@ -258,45 +276,111 @@ const TAB_ORDER = 90
|
|
|
258
276
|
|
|
259
277
|
export const name = PLUGIN_ID
|
|
260
278
|
|
|
261
|
-
//
|
|
262
|
-
//
|
|
263
|
-
|
|
264
|
-
//
|
|
265
|
-
|
|
279
|
+
// 不声明顶层 inject(避免 pending 导致插件加载失败)。better-sidebar(兄弟 bundle 服务)
|
|
280
|
+
// 与 slots(框架级浮层服务)都用 apply 内的 ctx.inject 软注入回调按需获取。
|
|
281
|
+
|
|
282
|
+
// 在 better-sidebar 快照里查找我们的 Tab 是否已打开(按 type 判定,single 单例)。
|
|
283
|
+
function findOurTab(snap: any): any {
|
|
284
|
+
if (!snap) return null
|
|
285
|
+
const trees = [snap.splits, snap.bottomSplits]
|
|
286
|
+
for (const tree of trees) {
|
|
287
|
+
if (!tree) continue
|
|
288
|
+
const stack: any[] = [tree]
|
|
289
|
+
while (stack.length) {
|
|
290
|
+
const node = stack.pop()
|
|
291
|
+
if (!node) continue
|
|
292
|
+
if (Array.isArray(node.tabs)) {
|
|
293
|
+
for (const t of node.tabs) if (t && t.type === PLUGIN_ID) return t
|
|
294
|
+
}
|
|
295
|
+
if (Array.isArray(node.children)) stack.push(...node.children)
|
|
296
|
+
if (node.splits) stack.push(node.splits)
|
|
297
|
+
if (node.bottomSplits) stack.push(node.bottomSplits)
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return null
|
|
301
|
+
}
|
|
266
302
|
|
|
267
303
|
export function apply(ctx: any): (() => void) | void {
|
|
268
|
-
let
|
|
304
|
+
let unregisterTab: (() => void) | null = null
|
|
305
|
+
let mountedOverlay = false
|
|
306
|
+
let slotsService: any = null
|
|
307
|
+
let fallbackTimer: any = void 0
|
|
308
|
+
let openTimer: any = null
|
|
269
309
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
disposeRegistration = ctx.slots.register(
|
|
275
|
-
{
|
|
276
|
-
name: 'shell.overlay',
|
|
277
|
-
id: PLUGIN_ID,
|
|
278
|
-
order: 100,
|
|
279
|
-
label: 'Token 仪表盘',
|
|
280
|
-
},
|
|
281
|
-
() => React.createElement(GaugePanel),
|
|
282
|
-
)
|
|
283
|
-
}
|
|
310
|
+
// 取得框架级 slots(仅用于浮层兜底)。若平台不提供 slots,浮层兜底不可用,但侧边栏主路径不受影响。
|
|
311
|
+
const stopSlots = ctx.inject(['slots'], (slotsCtx: any) => {
|
|
312
|
+
slotsService = slotsCtx.slots
|
|
313
|
+
})
|
|
284
314
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
315
|
+
// 浮层兜底:把仪表盘挂到官方 shell.overlay(右下角 33vh),仅当 better-sidebar 不可用或会话始终未就绪时。
|
|
316
|
+
const mountOverlay = (reason: string): void => {
|
|
317
|
+
if (mountedOverlay || unregisterTab) return // 已注册到侧边栏则不再叠加浮层
|
|
318
|
+
if (!slotsService || typeof slotsService.register !== 'function') return
|
|
319
|
+
console.warn(`[dsh-token-gauge] ${reason},仪表盘改为右下角浮层显示(仍可用)。`)
|
|
320
|
+
try {
|
|
321
|
+
const d = slotsService.register(
|
|
322
|
+
{ name: 'shell.overlay', id: PLUGIN_ID, order: 100, label: 'Token 仪表盘' },
|
|
323
|
+
() => React.createElement(GaugePanel),
|
|
324
|
+
)
|
|
325
|
+
if (typeof d === 'function') mountedOverlay = true
|
|
326
|
+
} catch (e) {
|
|
327
|
+
console.error('[dsh-token-gauge] 浮层兜底也失败', e)
|
|
293
328
|
}
|
|
294
|
-
} catch (e) {
|
|
295
|
-
console.error('[dsh-token-gauge] 注册 shell.overlay 失败', e)
|
|
296
329
|
}
|
|
297
330
|
|
|
331
|
+
// 主路径:注册到 better-sidebar 右侧工作台 Tab(停靠侧边栏),并轮询 openTab 直到会话就绪,
|
|
332
|
+
// 让仪表盘自动打开并成为当前可见页(“已挂载但未显示”的根因是 session 未就绪时 openTab 静默 no-op)。
|
|
333
|
+
const stopSidebar = ctx.inject(['betterSidebar'], (sidebarCtx: any) => {
|
|
334
|
+
const sidebar = sidebarCtx.betterSidebar
|
|
335
|
+
if (!sidebar || typeof sidebar.registerTab !== 'function') return
|
|
336
|
+
unregisterTab = sidebar.registerTab({
|
|
337
|
+
id: PLUGIN_ID,
|
|
338
|
+
title: 'Token 仪表盘',
|
|
339
|
+
single: true,
|
|
340
|
+
order: TAB_ORDER,
|
|
341
|
+
icon: GaugeIcon,
|
|
342
|
+
component: () => React.createElement(GaugeTab),
|
|
343
|
+
})
|
|
344
|
+
if (typeof fallbackTimer !== 'undefined') clearTimeout(fallbackTimer)
|
|
345
|
+
let attempts = 0
|
|
346
|
+
openTimer = setInterval(() => {
|
|
347
|
+
attempts++
|
|
348
|
+
const snap = sidebar.getSnapshot?.()
|
|
349
|
+
const sid = snap?.sessionId
|
|
350
|
+
// session 已就绪才尝试打开(未就绪时 openTab 会静默 no-op)
|
|
351
|
+
if (sid !== undefined && typeof sidebar.openTab === 'function') {
|
|
352
|
+
try {
|
|
353
|
+
sidebar.openTab({ type: PLUGIN_ID })
|
|
354
|
+
} catch {
|
|
355
|
+
/* ignore */
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
if (sid !== undefined && findOurTab(snap)) {
|
|
359
|
+
clearInterval(openTimer)
|
|
360
|
+
openTimer = null
|
|
361
|
+
return
|
|
362
|
+
}
|
|
363
|
+
if (attempts >= 45) {
|
|
364
|
+
// 约 36s 仍未打开(极端:会话始终未建立)→ 浮层兜底,保证可见
|
|
365
|
+
clearInterval(openTimer)
|
|
366
|
+
openTimer = null
|
|
367
|
+
mountOverlay('better-sidebar 已注册但会话始终未就绪')
|
|
368
|
+
}
|
|
369
|
+
}, 800)
|
|
370
|
+
return () => {
|
|
371
|
+
if (unregisterTab) unregisterTab()
|
|
372
|
+
}
|
|
373
|
+
})
|
|
374
|
+
|
|
375
|
+
// 兜底:8s 内 better-sidebar 仍未就绪 → 浮层
|
|
376
|
+
fallbackTimer = setTimeout(() => {
|
|
377
|
+
if (!unregisterTab) mountOverlay('未取得 dsh-better-sidebar 服务(请确认其已安装并启用)')
|
|
378
|
+
}, 8000)
|
|
379
|
+
|
|
298
380
|
return () => {
|
|
299
|
-
if (typeof
|
|
300
|
-
if (
|
|
381
|
+
if (typeof fallbackTimer !== 'undefined') clearTimeout(fallbackTimer)
|
|
382
|
+
if (openTimer) clearInterval(openTimer)
|
|
383
|
+
if (typeof stopSidebar === 'function') stopSidebar()
|
|
384
|
+
if (typeof stopSlots === 'function') stopSlots()
|
|
301
385
|
}
|
|
302
386
|
}
|