dsh-ds-balance 2.1.0 → 2.1.1

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 CHANGED
@@ -40,7 +40,7 @@
40
40
  | 界面 | 一句话 | 你会用它来 |
41
41
  |---|---|---|
42
42
  | 侧栏圆环 | 常驻左边栏底部的状态环 + 名称,**展开态悬浮条目就报出余额金额**,**环里填了多少 = 余额离该币种预警线还有多远** | 扫一眼就知道还剩多少、离预警线多远 |
43
- | 余额浮层 | 点条目展开:总额、赠送 / 充值拆分、数据新鲜度、手动刷新(带冷却);**右上角图标一键跳到插件页** | 核对具体数字,以及数据是几分钟前的 |
43
+ | 余额浮层 | 点条目展开:总额、赠送 / 充值拆分、数据新鲜度、手动刷新(带冷却);**右上角图标跳到本插件的配置格**(宿主没有那条跨插件深链服务时退回 Plugins 列表,悬浮文字同步改口径) | 核对具体数字,以及数据是几分钟前的 |
44
44
  | 设置卡片 | 连接 / 展示 / 阈值 / 刷新四组,**默认四组全收起**,点组头逐组展开 | 换端点、换币种、调预警线、调节奏 |
45
45
 
46
46
  三种界面的分工是死的:**圆环回答「大概还剩多少」,浮层回答「具体是多少」,卡片回答「怎么算」。**
package/README_en.md CHANGED
@@ -40,7 +40,7 @@ The balance needs somewhere to live that does not take up room. A permanent stat
40
40
  | Surface | One line | Use it for |
41
41
  |---|---|---|
42
42
  | Sidebar ring | A status ring plus label at the bottom of the sidebar; **in the expanded state, hovering the entry reports the balance amount**, and **how full it is = how far the balance is from that currency's warning line** | Seeing at a glance how much is left and how close it is to the warning line |
43
- | Balance popover | Click the entry: total, granted / topped-up split, data freshness, manual refresh (with cooldown); **an icon at its top right jumps straight to the Plugins page** | Checking the exact figures, and how many minutes old they are |
43
+ | Balance popover | Click the entry: total, granted / topped-up split, data freshness, manual refresh (with cooldown); **an icon at its top right opens this plugin's configuration page** (on a host without that cross-plugin deep link it falls back to the Plugins list, and its tooltip says so) | Checking the exact figures, and how many minutes old they are |
44
44
  | Settings card | Connection / Display / Thresholds / Refresh, **all collapsed by default**, expand a group from its header | Changing the endpoint, the currency, the warning lines, the cadence |
45
45
 
46
46
  The division of labour is fixed: **the ring answers "roughly how much is left", the popover answers "exactly how much", and the card answers "how is that computed".**
package/lib/client.js CHANGED
@@ -56,7 +56,10 @@ var zh = {
56
56
  "popover.cooldown": "{value} \u79D2\u540E\u53EF\u518D\u6B21\u5237\u65B0",
57
57
  "popover.mismatch": "\u672A\u627E\u5230 {wanted} \u4F59\u989D\uFF0C\u5F53\u524D\u663E\u793A {shown}",
58
58
  "popover.action.useShown": "\u6539\u7528 {shown}",
59
+ // 右上角图标的两档措辞。落点由**宿主能力**决定(跨插件深链服务在不在),
60
+ // 措辞就跟着落点分档:一句含糊的话盖不住两种落点。见 BalancePopover 的 PluginsAction。
59
61
  "popover.action.openPlugins": "\u6253\u5F00\u63D2\u4EF6\u9875",
62
+ "popover.action.openPluginConfig": "\u6253\u5F00\u63D2\u4EF6\u914D\u7F6E\u9875",
60
63
  "state.unavailable": "\u8D26\u6237\u4E0D\u53EF\u7528",
61
64
  "state.stale": "\u6570\u636E\u5DF2\u8FC7\u671F",
62
65
  "state.error": "\u8BFB\u53D6\u5931\u8D25",
@@ -137,6 +140,7 @@ var en = {
137
140
  "popover.mismatch": "No {wanted} balance found; showing {shown}",
138
141
  "popover.action.useShown": "Use {shown}",
139
142
  "popover.action.openPlugins": "Open the Plugins page",
143
+ "popover.action.openPluginConfig": "Open the plugin configuration page",
140
144
  "state.unavailable": "Account unavailable",
141
145
  "state.stale": "Data is stale",
142
146
  "state.error": "Failed to load",
@@ -1544,7 +1548,7 @@ function BalancePopover(props) {
1544
1548
  onRefresh,
1545
1549
  onUseShown,
1546
1550
  useShownDisabled,
1547
- onOpenPlugins
1551
+ pluginsAction
1548
1552
  } = props;
1549
1553
  const shown = selection.shown;
1550
1554
  const totalText = shown === null ? BALANCE_PLACEHOLDER : formatMoney(shown.total, shown.currency);
@@ -1559,6 +1563,7 @@ function BalancePopover(props) {
1559
1563
  if (refreshing) statusText = t("popover.refreshing");
1560
1564
  else if (cooling) statusText = interpolate(t("popover.cooldown"), { value: String(cooldownSeconds) });
1561
1565
  const refreshLabel = t("sidebar.aria.refresh");
1566
+ const pluginsLabel = t(pluginsAction?.reachesConfig === true ? "popover.action.openPluginConfig" : "popover.action.openPlugins");
1562
1567
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1563
1568
  "section",
1564
1569
  {
@@ -1582,13 +1587,13 @@ function BalancePopover(props) {
1582
1587
  ]
1583
1588
  }
1584
1589
  ),
1585
- onOpenPlugins === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives3.Tooltip, { label: t("popover.action.openPlugins"), side: "top", delayMs: 500, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1590
+ pluginsAction === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives3.Tooltip, { label: pluginsLabel, side: "top", delayMs: 500, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1586
1591
  "button",
1587
1592
  {
1588
1593
  type: "button",
1589
1594
  className: BalancePopover_default.iconButton,
1590
- "aria-label": t("popover.action.openPlugins"),
1591
- onClick: onOpenPlugins,
1595
+ "aria-label": pluginsLabel,
1596
+ onClick: pluginsAction.open,
1592
1597
  children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives3.IconPluginPinwheelOutlineRegular, { size: 16 })
1593
1598
  }
1594
1599
  ) })
@@ -1728,18 +1733,18 @@ function useConfigSlotState(probe) {
1728
1733
  return state;
1729
1734
  }
1730
1735
  function usePluginsNavigation(navigation) {
1731
- const [openPlugins, setOpenPlugins] = (0, import_react5.useState)(() => navigation?.getSnapshot());
1736
+ const [action, setAction] = (0, import_react5.useState)(() => navigation?.getSnapshot());
1732
1737
  (0, import_react5.useEffect)(() => {
1733
1738
  if (navigation === void 0) {
1734
- setOpenPlugins(void 0);
1739
+ setAction(void 0);
1735
1740
  return;
1736
1741
  }
1737
- setOpenPlugins(() => navigation.getSnapshot());
1742
+ setAction(navigation.getSnapshot());
1738
1743
  return navigation.subscribe(() => {
1739
- setOpenPlugins(() => navigation.getSnapshot());
1744
+ setAction(navigation.getSnapshot());
1740
1745
  });
1741
1746
  }, [navigation]);
1742
- return openPlugins;
1747
+ return action;
1743
1748
  }
1744
1749
  function stateLabelKey(response, selection) {
1745
1750
  if (response.state === "error") return response.error?.code === "NO_KEY" ? "state.noKey" : "state.error";
@@ -1893,11 +1898,17 @@ function SidebarBalance({
1893
1898
  if (current === null) return;
1894
1899
  void onSelectCurrency(current.currency);
1895
1900
  }, [selection, onSelectCurrency]);
1896
- const openPlugins = usePluginsNavigation(pluginsNavigation);
1897
- const handleOpenPlugins = (0, import_react5.useCallback)(() => {
1898
- closeNow();
1899
- openPlugins?.();
1900
- }, [closeNow, openPlugins]);
1901
+ const pluginsAction = usePluginsNavigation(pluginsNavigation);
1902
+ const popoverPluginsAction = (0, import_react5.useMemo)(
1903
+ () => pluginsAction === void 0 ? void 0 : {
1904
+ reachesConfig: pluginsAction.reachesConfig,
1905
+ open: () => {
1906
+ closeNow();
1907
+ pluginsAction.open();
1908
+ }
1909
+ },
1910
+ [closeNow, pluginsAction]
1911
+ );
1901
1912
  const configSlotState = useConfigSlotState(configSlotProbe);
1902
1913
  const shown = selection.shown;
1903
1914
  const ring = ringSpecFor(response.severity);
@@ -1966,7 +1977,7 @@ function SidebarBalance({
1966
1977
  onRefresh: handleRefresh,
1967
1978
  onUseShown: handleUseShown,
1968
1979
  useShownDisabled: !config.writable,
1969
- onOpenPlugins: openPlugins === void 0 ? void 0 : handleOpenPlugins
1980
+ pluginsAction: popoverPluginsAction
1970
1981
  }
1971
1982
  ),
1972
1983
  document.body
@@ -2063,25 +2074,59 @@ function SettingsSeatComponent(props) {
2063
2074
  }
2064
2075
  var PLUGINS_PANEL_ID = "plugins";
2065
2076
  function createPluginsNavigation() {
2066
- let open;
2077
+ let selectPanel;
2078
+ let openBundle;
2079
+ let snapshot;
2067
2080
  const listeners2 = /* @__PURE__ */ new Set();
2068
- const publish = (next) => {
2069
- if (next === open) return;
2070
- open = next;
2081
+ const open = () => {
2082
+ if (openBundle !== void 0) {
2083
+ try {
2084
+ openBundle(BUNDLE_CONFIG_KEY);
2085
+ return;
2086
+ } catch (error) {
2087
+ console.warn("[WARN] ds-balance: cannot open the bundle config page", error);
2088
+ }
2089
+ }
2090
+ try {
2091
+ selectPanel?.();
2092
+ } catch (error) {
2093
+ console.warn("[WARN] ds-balance: cannot switch to the plugins panel", error);
2094
+ }
2095
+ };
2096
+ const publish = () => {
2097
+ const reachesConfig = openBundle !== void 0;
2098
+ const next = selectPanel === void 0 ? void 0 : { open, reachesConfig };
2099
+ const unchanged = next === void 0 ? snapshot === void 0 : snapshot !== void 0 && snapshot.reachesConfig === reachesConfig;
2100
+ if (unchanged) return;
2101
+ snapshot = next;
2071
2102
  for (const listener of [...listeners2]) listener();
2072
2103
  };
2073
2104
  return {
2074
- getSnapshot: () => open,
2105
+ getSnapshot: () => snapshot,
2075
2106
  subscribe: (listener) => {
2076
2107
  listeners2.add(listener);
2077
2108
  return () => {
2078
2109
  listeners2.delete(listener);
2079
2110
  };
2080
2111
  },
2081
- attach: (callback) => {
2082
- publish(callback);
2112
+ attachPanel: (callback) => {
2113
+ selectPanel = callback;
2114
+ publish();
2083
2115
  return () => {
2084
- if (open === callback) publish(void 0);
2116
+ if (selectPanel === callback) {
2117
+ selectPanel = void 0;
2118
+ publish();
2119
+ }
2120
+ };
2121
+ },
2122
+ attachDeepLink: (callback) => {
2123
+ openBundle = callback;
2124
+ publish();
2125
+ return () => {
2126
+ if (openBundle === callback) {
2127
+ openBundle = void 0;
2128
+ publish();
2129
+ }
2085
2130
  };
2086
2131
  }
2087
2132
  };
@@ -2091,21 +2136,13 @@ function apply(ctx) {
2091
2136
  const configSlotProbe = createConfigSlotProbe();
2092
2137
  ctx.effect(() => () => configSlotProbe.dispose(), "ds-balance: config slot probe");
2093
2138
  const pluginsNavigation = createPluginsNavigation();
2094
- let openBundlePanel;
2095
2139
  ctx.inject(["pluginNavigation"], (navCtx) => {
2096
2140
  const face = navCtx["pluginNavigation"];
2097
2141
  if (typeof face !== "object" || face === null) return;
2098
2142
  const candidate = face["openBundle"];
2099
2143
  if (typeof candidate !== "function") return;
2100
2144
  const openBundle = candidate.bind(face);
2101
- navCtx.effect(() => {
2102
- openBundlePanel = (packageName) => {
2103
- openBundle(packageName);
2104
- };
2105
- return () => {
2106
- openBundlePanel = void 0;
2107
- };
2108
- }, "ds-balance: bundle config deep link");
2145
+ navCtx.effect(() => pluginsNavigation.attachDeepLink(openBundle), "ds-balance: bundle config deep link");
2109
2146
  });
2110
2147
  ctx.inject(["layout"], (layoutCtx) => {
2111
2148
  if (!("layout" in layoutCtx)) return;
@@ -2113,21 +2150,12 @@ function apply(ctx) {
2113
2150
  if (typeof face !== "object" || face === null) return;
2114
2151
  if (!("selectPanel" in face) || typeof face.selectPanel !== "function") return;
2115
2152
  const selectPanel = face.selectPanel.bind(face);
2116
- layoutCtx.effect(() => pluginsNavigation.attach(() => {
2117
- if (openBundlePanel !== void 0) {
2118
- try {
2119
- openBundlePanel(BUNDLE_CONFIG_KEY);
2120
- return;
2121
- } catch (error) {
2122
- console.warn("[WARN] ds-balance: cannot open the bundle config page", error);
2123
- }
2124
- }
2125
- try {
2153
+ layoutCtx.effect(
2154
+ () => pluginsNavigation.attachPanel(() => {
2126
2155
  selectPanel(PLUGINS_PANEL_ID);
2127
- } catch (error) {
2128
- console.warn("[WARN] ds-balance: cannot switch to the plugins panel", error);
2129
- }
2130
- }), "ds-balance: plugins panel navigation");
2156
+ }),
2157
+ "ds-balance: plugins panel navigation"
2158
+ );
2131
2159
  });
2132
2160
  ctx.inject(["configForms"], (configCtx) => {
2133
2161
  configSlotProbe.markDeclared();
@@ -22,6 +22,7 @@ export declare const zh: {
22
22
  readonly 'popover.mismatch': "未找到 {wanted} 余额,当前显示 {shown}";
23
23
  readonly 'popover.action.useShown': "改用 {shown}";
24
24
  readonly 'popover.action.openPlugins': "打开插件页";
25
+ readonly 'popover.action.openPluginConfig': "打开插件配置页";
25
26
  readonly 'state.unavailable': "账户不可用";
26
27
  readonly 'state.stale': "数据已过期";
27
28
  readonly 'state.error': "读取失败";
@@ -46,10 +46,23 @@ export interface BalancePopoverProps {
46
46
  */
47
47
  useShownDisabled?: boolean;
48
48
  /**
49
- * 「切到 Plugins 页」回调。宿主没提供 layout 服务时给 undefined ——
50
- * 这时右上角那个图标按钮**不渲染**:不留按不动的死按钮。
49
+ * 右上角图标:点下去做什么、落到哪里。宿主没提供 layout 服务时给 undefined ——
50
+ * 这时那个按钮**不渲染**:不留按不动的死按钮。
51
51
  */
52
- onOpenPlugins?: () => void;
52
+ pluginsAction?: PluginsAction;
53
+ }
54
+ /**
55
+ * 浮层右上角图标点下去会发生什么。
56
+ *
57
+ * 落点由**宿主能力**决定:宿主提供跨插件深链服务时直达本插件的配置格,
58
+ * 服务缺席时只能到 Plugins 列表。所以措辞跟着落点分档 ——
59
+ * 「打开插件配置页」与「打开插件页」各说自己那一件事,不许用一句含糊的话盖住两种落点。
60
+ */
61
+ export interface PluginsAction {
62
+ /** 执行跳转;「先关浮层」由父组件包在里面。 */
63
+ open: () => void;
64
+ /** 落点是不是本插件的配置格。 */
65
+ reachesConfig: boolean;
53
66
  }
54
67
  /**
55
68
  * 渲染余额浮层。
@@ -13,14 +13,16 @@
13
13
  */
14
14
  import { type LocaleKey } from '../locales.ts';
15
15
  import { type ConfigSlotProbe } from '../config-slot.ts';
16
+ import { type PluginsAction } from './BalancePopover.tsx';
16
17
  /**
17
- * 「切到 Plugins 页」这个**可选**入口。宿主没提供 layout 服务时整条链不存在,
18
+ * 「打开本插件配置」这个**可选**入口。宿主没提供 layout 服务时整条链不存在,
18
19
  * 浮层右上角的图标因此不渲染 —— 不留按不动的死按钮。
19
20
  * 形态与 `configSlotProbe` 一样是个可订阅的小对象:服务可能晚到,图标要能自己出现、也能自己消失。
21
+ * 快照里带**落点**(见 BalancePopover 的 PluginsAction):深链服务在不在,措辞跟着分档。
20
22
  */
21
23
  export interface PluginsNavigation {
22
- /** 当前可用的导航回调;服务还没到位时是 undefined。 */
23
- getSnapshot: () => (() => void) | undefined;
24
+ /** 当前可用的动作与落点;宿主没有 layout 服务时是 undefined。 */
25
+ getSnapshot: () => PluginsAction | undefined;
24
26
  /**
25
27
  * 订阅它的出现与消失。
26
28
  * @param listener - 变化回调。
@@ -41,7 +43,7 @@ export interface SidebarBalanceProps {
41
43
  */
42
44
  configSlotProbe: ConfigSlotProbe;
43
45
  /**
44
- * 「切到 Plugins 页」的可选入口:有它才渲染浮层右上角那个图标按钮。
46
+ * 「打开本插件配置」的可选入口:有它才渲染浮层右上角那个图标按钮。
45
47
  * 宿主缺 layout 服务时整条链不存在,图标就不出现。
46
48
  */
47
49
  pluginsNavigation?: PluginsNavigation;
package/lib/version.d.ts CHANGED
@@ -8,5 +8,5 @@
8
8
  /** 线上 schema 版本。响应里逐条回传,消费方据此判断兼容性。 */
9
9
  export declare const SCHEMA_VERSION = 1;
10
10
  /** 插件版本。与 `package.json` 的 `version` 一致(有测试兜底)。 */
11
- export declare const PLUGIN_VERSION = "2.1.0";
11
+ export declare const PLUGIN_VERSION = "2.1.1";
12
12
  //# sourceMappingURL=version.d.ts.map
package/lib/version.js CHANGED
@@ -8,5 +8,5 @@
8
8
  /** 线上 schema 版本。响应里逐条回传,消费方据此判断兼容性。 */
9
9
  export const SCHEMA_VERSION = 1;
10
10
  /** 插件版本。与 `package.json` 的 `version` 一致(有测试兜底)。 */
11
- export const PLUGIN_VERSION = '2.1.0';
11
+ export const PLUGIN_VERSION = '2.1.1';
12
12
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-ds-balance",
3
- "version": "2.1.0",
3
+ "version": "2.1.1",
4
4
  "icon": "./icon.svg",
5
5
  "description": "DSH plugin: shows the DeepSeek account balance at the bottom of the sidebar, with a configuration card on the Plugins page.",
6
6
  "keywords": [