dsh-token-gauge 0.1.9 → 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 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);
@@ -1006,25 +1005,49 @@ var GaugeIcon = (size) => {
1006
1005
  };
1007
1006
  var TAB_ORDER = 90;
1008
1007
  var name = PLUGIN_ID;
1009
- var inject = ["slots"];
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);
1023
+ }
1024
+ }
1025
+ return null;
1026
+ }
1010
1027
  function apply(ctx) {
1011
1028
  let unregisterTab = null;
1012
- let overlayDispose = void 0;
1013
- let registeredViaBetter = false;
1029
+ let mountedOverlay = false;
1030
+ let slotsService = null;
1014
1031
  let fallbackTimer = void 0;
1015
- const tryMountOverlay = (reason) => {
1016
- if (registeredViaBetter || overlayDispose !== void 0) return;
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;
1017
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`);
1018
1040
  try {
1019
- overlayDispose = ctx.slots.register(
1041
+ const d = slotsService.register(
1020
1042
  { name: "shell.overlay", id: PLUGIN_ID, order: 100, label: "Token \u4EEA\u8868\u76D8" },
1021
1043
  () => import_react.default.createElement(GaugePanel)
1022
1044
  );
1045
+ if (typeof d === "function") mountedOverlay = true;
1023
1046
  } catch (e) {
1024
1047
  console.error("[dsh-token-gauge] \u6D6E\u5C42\u515C\u5E95\u4E5F\u5931\u8D25", e);
1025
1048
  }
1026
1049
  };
1027
- const disposeSidebar = ctx.inject(["betterSidebar"], (sidebarCtx) => {
1050
+ const stopSidebar = ctx.inject(["betterSidebar"], (sidebarCtx) => {
1028
1051
  const sidebar = sidebarCtx.betterSidebar;
1029
1052
  if (!sidebar || typeof sidebar.registerTab !== "function") return;
1030
1053
  unregisterTab = sidebar.registerTab({
@@ -1035,25 +1058,41 @@ function apply(ctx) {
1035
1058
  icon: GaugeIcon,
1036
1059
  component: () => import_react.default.createElement(GaugeTab)
1037
1060
  });
1038
- registeredViaBetter = true;
1039
1061
  if (typeof fallbackTimer !== "undefined") clearTimeout(fallbackTimer);
1040
- try {
1041
- if (typeof sidebar.openTab === "function") sidebar.openTab({ type: PLUGIN_ID, single: true });
1042
- } catch (e) {
1043
- }
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);
1044
1084
  return () => {
1045
1085
  if (unregisterTab) unregisterTab();
1046
1086
  };
1047
1087
  });
1048
1088
  fallbackTimer = setTimeout(() => {
1049
- if (!registeredViaBetter) {
1050
- tryMountOverlay("\u672A\u53D6\u5F97 dsh-better-sidebar \u670D\u52A1\uFF08\u8BF7\u786E\u8BA4\u5176\u5DF2\u5B89\u88C5\u5E76\u542F\u7528\uFF09");
1051
- }
1052
- }, 3e4);
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);
1053
1091
  return () => {
1054
1092
  if (typeof fallbackTimer !== "undefined") clearTimeout(fallbackTimer);
1055
- if (typeof disposeSidebar === "function") disposeSidebar();
1056
- if (typeof overlayDispose === "function") overlayDispose();
1093
+ if (openTimer) clearInterval(openTimer);
1094
+ if (typeof stopSidebar === "function") stopSidebar();
1095
+ if (typeof stopSlots === "function") stopSlots();
1057
1096
  };
1058
1097
  }
1059
1098
  ; return module.exports; } });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-token-gauge",
3
- "version": "0.1.9",
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",
package/src/client.ts CHANGED
@@ -4,8 +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
- // 优先把仪表盘注册到 dsh-better-sidebar 的右侧工作台 Tab(停靠侧边栏);若 better-sidebar
8
- // 未就绪,回退到官方 shell.overlay 浮层(双模式,保证仪表盘永远可见)。
7
+ // 优先把仪表盘注册到 dsh-better-sidebar 的右侧工作台 Tab(停靠侧边栏),注册后轮询
8
+ // openTab 直到会话就绪并自动打开激活(解决“已挂载却未显示”:session 未就绪时 openTab
9
+ // 会静默 no-op);若 better-sidebar 始终不可用,回退到官方 shell.overlay 浮层兜底。
9
10
  // 为避免与运行时 React 产生双实例 hook 冲突,本组件不调用任何 hook,
10
11
  // 仅在 ref 回调里挂载一次原生 DOM 仪表盘,并自行轮询刷新。
11
12
  // ============================================================================
@@ -275,34 +276,61 @@ const TAB_ORDER = 90
275
276
 
276
277
  export const name = PLUGIN_ID
277
278
 
278
- // slots 是 layout 提供的框架级服务(root 早期即就绪),用于浮层兜底,可安全声明顶层 inject。
279
- // 注意:betterSidebar 是兄弟 bundle 提供的服务,不用顶层 inject(会触发 pending),改用
280
- // apply 内的 ctx.inject 软注入回调。
281
- export const inject = ['slots']
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
+ }
282
302
 
283
303
  export function apply(ctx: any): (() => void) | void {
284
304
  let unregisterTab: (() => void) | null = null
285
- let overlayDispose: unknown = void 0
286
- let registeredViaBetter = false
305
+ let mountedOverlay = false
306
+ let slotsService: any = null
287
307
  let fallbackTimer: any = void 0
308
+ let openTimer: any = null
309
+
310
+ // 取得框架级 slots(仅用于浮层兜底)。若平台不提供 slots,浮层兜底不可用,但侧边栏主路径不受影响。
311
+ const stopSlots = ctx.inject(['slots'], (slotsCtx: any) => {
312
+ slotsService = slotsCtx.slots
313
+ })
288
314
 
289
- // 浮层兜底:把仪表盘挂到官方 shell.overlay(全程右下角 33vh),保证即使 better-sidebar
290
- // 缺失/未启用,仪表盘也始终可见。
291
- const tryMountOverlay = (reason: string): void => {
292
- if (registeredViaBetter || overlayDispose !== void 0) return
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
293
319
  console.warn(`[dsh-token-gauge] ${reason},仪表盘改为右下角浮层显示(仍可用)。`)
294
320
  try {
295
- overlayDispose = ctx.slots.register(
321
+ const d = slotsService.register(
296
322
  { name: 'shell.overlay', id: PLUGIN_ID, order: 100, label: 'Token 仪表盘' },
297
323
  () => React.createElement(GaugePanel),
298
324
  )
325
+ if (typeof d === 'function') mountedOverlay = true
299
326
  } catch (e) {
300
327
  console.error('[dsh-token-gauge] 浮层兜底也失败', e)
301
328
  }
302
329
  }
303
330
 
304
- // 主路径:注册到 better-sidebar 右侧工作台 Tab(停靠侧边栏,带图标、靠前排序)
305
- const disposeSidebar = ctx.inject(['betterSidebar'], (sidebarCtx: any) => {
331
+ // 主路径:注册到 better-sidebar 右侧工作台 Tab(停靠侧边栏),并轮询 openTab 直到会话就绪,
332
+ // 让仪表盘自动打开并成为当前可见页(“已挂载但未显示”的根因是 session 未就绪时 openTab 静默 no-op)。
333
+ const stopSidebar = ctx.inject(['betterSidebar'], (sidebarCtx: any) => {
306
334
  const sidebar = sidebarCtx.betterSidebar
307
335
  if (!sidebar || typeof sidebar.registerTab !== 'function') return
308
336
  unregisterTab = sidebar.registerTab({
@@ -313,29 +341,46 @@ export function apply(ctx: any): (() => void) | void {
313
341
  icon: GaugeIcon,
314
342
  component: () => React.createElement(GaugeTab),
315
343
  })
316
- registeredViaBetter = true
317
344
  if (typeof fallbackTimer !== 'undefined') clearTimeout(fallbackTimer)
318
- // 会话就绪时尝试自动打开 Tab(未就绪会静默 no-op,用户也可从「+ 新建 Tab」菜单手动打开)
319
- try {
320
- if (typeof sidebar.openTab === 'function') sidebar.openTab({ type: PLUGIN_ID, single: true })
321
- } catch (e) {
322
- /* ignore */
323
- }
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)
324
370
  return () => {
325
371
  if (unregisterTab) unregisterTab()
326
372
  }
327
373
  })
328
374
 
329
- // 兜底:30s 内 better-sidebar 仍未就绪 → 浮层
375
+ // 兜底:8s 内 better-sidebar 仍未就绪 → 浮层
330
376
  fallbackTimer = setTimeout(() => {
331
- if (!registeredViaBetter) {
332
- tryMountOverlay('未取得 dsh-better-sidebar 服务(请确认其已安装并启用)')
333
- }
334
- }, 30000)
377
+ if (!unregisterTab) mountOverlay('未取得 dsh-better-sidebar 服务(请确认其已安装并启用)')
378
+ }, 8000)
335
379
 
336
380
  return () => {
337
381
  if (typeof fallbackTimer !== 'undefined') clearTimeout(fallbackTimer)
338
- if (typeof disposeSidebar === 'function') (disposeSidebar as () => void)()
339
- if (typeof overlayDispose === 'function') (overlayDispose as () => void)()
382
+ if (openTimer) clearInterval(openTimer)
383
+ if (typeof stopSidebar === 'function') stopSidebar()
384
+ if (typeof stopSlots === 'function') stopSlots()
340
385
  }
341
386
  }