dsh-grok-subscription 1.0.2 → 1.0.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.en.md CHANGED
@@ -25,7 +25,7 @@ Models, reasoning effort, and weekly quota all stay inside DSH.
25
25
 
26
26
  ## Three-step start
27
27
 
28
- 1. **Install the plugin.** Run the command below; to select a candidate version, enter the full `package@version`, for example `dsh-grok-subscription@1.0.2`.
28
+ 1. **Install the plugin.** Run the command below; to select a candidate version, enter the full `package@version`, for example `dsh-grok-subscription@1.0.3`.
29
29
 
30
30
  ```sh
31
31
  dsh plugin --profile web add BaronCyrus/dsh-grok-subscription
@@ -57,13 +57,13 @@ These capabilities reuse the same local Grok Build sign-in.
57
57
  <img src="docs/assets/settings-account.webp" width="820" alt="The Grok Subscription page in DSH Settings: sign-in status, account, login buttons and credential notes">
58
58
  </p>
59
59
 
60
- The **Settings → Grok Subscription** screen shows sign-in status and a masked account, offers CLI login, device-code login, pull from the Grok CLI, and logout, and explains how credentials are read. Account and time values in the screenshot are demo data.
60
+ The **Settings → Grok Subscription** screen uses a status chip for the sign-in state, and an account card offering CLI login, device-code login, pull from the Grok CLI, and logout; how credentials are read lives in a collapsible "How signing in works" disclosure. It is styled with DSH's own design tokens, so it follows the light and dark theme. Account and time values in the screenshot are demo data.
61
61
 
62
62
  <p align="center">
63
63
  <img src="docs/assets/settings-usage.webp" width="820" alt="The experimental usage panel in Grok Subscription settings: 6% used, about 94% remaining">
64
64
  </p>
65
65
 
66
- The experimental usage panel comes from an undocumented subscription billing endpoint (`/v1/billing?format=credits`). It is informational: the endpoint may change or disappear, a failed read never shows an invented percentage, and chat is unaffected.
66
+ The experimental usage panel comes from an undocumented subscription billing endpoint (`/v1/billing?format=credits`): the remaining share is drawn as a progress bar and per-product rows are listed separately. It is informational: the endpoint may change or disappear, a failed read never shows an invented percentage, and chat is unaffected.
67
67
 
68
68
  ## Prepare DSH
69
69
 
@@ -89,7 +89,7 @@ dsh plugin --profile web add BaronCyrus/dsh-grok-subscription
89
89
  You can also install the version published on npm:
90
90
 
91
91
  ```sh
92
- dsh plugin --profile web add dsh-grok-subscription@1.0.2
92
+ dsh plugin --profile web add dsh-grok-subscription@1.0.3
93
93
  ```
94
94
 
95
95
  DSH handles target selection, the profile lock, dependency resolution, and bundle activation.
package/README.md CHANGED
@@ -25,7 +25,7 @@
25
25
 
26
26
  ## 三步开始
27
27
 
28
- 1. **安装插件**:在终端运行下面的命令;指定候选版本时填写完整的 `包名@版本`,例如 `dsh-grok-subscription@1.0.2`。
28
+ 1. **安装插件**:在终端运行下面的命令;指定候选版本时填写完整的 `包名@版本`,例如 `dsh-grok-subscription@1.0.3`。
29
29
 
30
30
  ```sh
31
31
  dsh plugin --profile web add BaronCyrus/dsh-grok-subscription
@@ -57,13 +57,13 @@
57
57
  <img src="docs/assets/settings-account.webp" width="820" alt="DSH 设置中的 Grok 订阅页面:登录状态、账号、登录按钮与凭据说明">
58
58
  </p>
59
59
 
60
- 上图为 **设置 → Grok 订阅** 主界面:显示登录状态与脱敏账号,提供 CLI 登录、设备码登录、从 Grok CLI 拉取和登出,并说明凭据的读取方式。截图中的账号与时间均为演示数据。
60
+ 上图为 **设置 → Grok 订阅** 主界面:顶部状态胶囊显示登录状态,账户卡片提供 CLI 登录、设备码登录、从 Grok CLI 拉取和登出,凭据读取方式收在可展开的「登录方式说明」里。样式沿用 DSH 自身的设计变量,因此跟随主题明暗切换。截图中的账号与时间均为演示数据。
61
61
 
62
62
  <p align="center">
63
63
  <img src="docs/assets/settings-usage.webp" width="820" alt="Grok 订阅设置页中的实验性用量面板:已用 6%、剩余约 94%">
64
64
  </p>
65
65
 
66
- 实验性用量面板来自未公开的订阅计费接口(`/v1/billing?format=credits`)。它仅供参考:接口可能随时变化或消失,读取失败时不会显示编造的百分比,也不影响聊天。
66
+ 实验性用量面板来自未公开的订阅计费接口(`/v1/billing?format=credits`):剩余比例以进度条呈现,产品明细逐行列出。它仅供参考:接口可能随时变化或消失,读取失败时不会显示编造的百分比,也不影响聊天。
67
67
 
68
68
  ## 准备 DSH
69
69
 
@@ -89,7 +89,7 @@ dsh plugin --profile web add BaronCyrus/dsh-grok-subscription
89
89
  也可以按 npm 上的已发布版本安装:
90
90
 
91
91
  ```sh
92
- dsh plugin --profile web add dsh-grok-subscription@1.0.2
92
+ dsh plugin --profile web add dsh-grok-subscription@1.0.3
93
93
  ```
94
94
 
95
95
  目标选择、profile 锁、依赖解析和 bundle 激活均由 DSH 负责。
Binary file
Binary file
package/lib/client.js CHANGED
@@ -34,7 +34,7 @@ var import_react2 = require("react");
34
34
  var zh = {
35
35
  nav: "Grok \u8BA2\u9605",
36
36
  title: "Grok \u8BA2\u9605",
37
- subtitle: "\u7528 SuperGrok / X Premium\uFF08Grok Build\uFF09\u4F1A\u8BDD\uFF0C\u800C\u4E0D\u662F XAI_API_KEY\u3002\xB7 1.0.2",
37
+ subtitle: "\u7528 SuperGrok / X Premium\uFF08Grok Build\uFF09\u4F1A\u8BDD\uFF0C\u800C\u4E0D\u662F XAI_API_KEY\u3002\xB7 1.0.3",
38
38
  signedIn: "\u5DF2\u767B\u5F55",
39
39
  signedOut: "\u672A\u767B\u5F55",
40
40
  account: "\u8D26\u6237",
@@ -52,6 +52,8 @@ var zh = {
52
52
  loginHint: "\u4F1A\u5728\u542F\u52A8 DSH \u7684\u7EC8\u7AEF\u4E2D\u8FD0\u884C grok login\u3002\u8BF7\u5728\u7EC8\u7AEF\u6216\u6D4F\u89C8\u5668\u4E2D\u5B8C\u6210\u6388\u6743\uFF0C\u7136\u540E\u7B49\u5F85\u540C\u6B65\u3002",
53
53
  deviceHint: "\u8BBE\u5907\u7801\u63D0\u793A\u4F1A\u51FA\u73B0\u5728 DSH \u7EC8\u7AEF\u3002\u4E5F\u53EF\u624B\u52A8\u8FD0\u884C grok login --device-auth\uFF0C\u5B8C\u6210\u540E\u70B9\u201C\u4ECE Grok CLI \u62C9\u53D6\u201D\u3002",
54
54
  pullHint: "\u5B89\u5168\u8BFB\u53D6 ~/.grok/auth.json\uFF08\u62D2\u7EDD\u7B26\u53F7\u94FE\u63A5\u4E0E\u7EC4/\u5176\u4ED6\u4EBA\u53EF\u8BFB\uFF09\uFF0C\u53EA\u628A\u77ED\u671F access token \u5199\u5165 DSH\u3002",
55
+ loginHelp: "\u767B\u5F55\u65B9\u5F0F\u8BF4\u660E",
56
+ refreshCatalog: "\u5237\u65B0\u6A21\u578B\u76EE\u5F55",
55
57
  busy: "\u5904\u7406\u4E2D\u2026",
56
58
  busyCatalog: "\u6B63\u5728\u5237\u65B0\u6A21\u578B\u76EE\u5F55\u2026",
57
59
  busyUsage: "\u6B63\u5728\u5237\u65B0\u7528\u91CF\u2026",
@@ -81,7 +83,7 @@ var zh = {
81
83
  var en = {
82
84
  nav: "Grok Subscription",
83
85
  title: "Grok Subscription",
84
- subtitle: "Use a SuperGrok / X Premium (Grok Build) session, not XAI_API_KEY. \xB7 1.0.2",
86
+ subtitle: "Use a SuperGrok / X Premium (Grok Build) session, not XAI_API_KEY. \xB7 1.0.3",
85
87
  signedIn: "Signed in",
86
88
  signedOut: "Signed out",
87
89
  account: "Account",
@@ -99,6 +101,8 @@ var en = {
99
101
  loginHint: "Runs grok login in the terminal that started DSH. Finish the browser or CLI prompt there, then wait for sync.",
100
102
  deviceHint: "The device-code prompt appears in the DSH terminal. You can also run grok login --device-auth yourself, then Pull from Grok CLI.",
101
103
  pullHint: "Reads ~/.grok/auth.json securely (refuses symlinks and group/other-readable files) and stores only the short-lived access token in DSH.",
104
+ loginHelp: "How signing in works",
105
+ refreshCatalog: "Refresh model catalog",
102
106
  busy: "Working\u2026",
103
107
  busyCatalog: "Refreshing model catalog\u2026",
104
108
  busyUsage: "Refreshing usage\u2026",
@@ -165,6 +169,83 @@ var REQUEST_IMAGE_PIXEL_BUDGET = 2048 * 2048;
165
169
  var REQUEST_IMAGE_MAX_BYTES = 1024 * 1024;
166
170
  var FALLBACK_MODEL_IDS = Object.freeze(["grok-4.7", "grok-4.6", "grok-4.5"]);
167
171
 
172
+ // src/client-settings-style.js
173
+ var SETTINGS_STYLE = `
174
+ .grokSubscription{display:flex;flex-direction:column;gap:10px;max-width:720px;color:var(--dsw-alias-label-primary);container-type:inline-size}
175
+ .grokSubscription h2,.grokSubscription h3,.grokSubscription p{margin:0}
176
+ .grokSubscription h2{font-size:16px;line-height:24px;font-weight:500}
177
+ .grokSubscription h3{font-size:15px;line-height:22px;font-weight:600}
178
+
179
+ .gsHead{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
180
+ .gsLead{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:20px}
181
+
182
+ .gsCard{border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-layer-3);border-radius:16px;padding:14px 16px;display:flex;flex-direction:column;gap:12px}
183
+ .gsCardHead{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
184
+ .gsCardHead .gsSpacer{flex:1;min-width:0}
185
+
186
+ .gsChip{display:inline-flex;align-items:center;gap:6px;border-radius:999px;padding:2px 10px;font-size:12px;line-height:18px;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-secondary);white-space:nowrap}
187
+ .gsChip--ok{color:var(--dsw-alias-state-success-primary)}
188
+ .gsChip--off{color:var(--dsw-alias-label-tertiary)}
189
+ .gsChip--warn{color:var(--dsw-alias-state-warn-label)}
190
+ .gsChip--error{color:var(--dsw-alias-label-error)}
191
+ .gsDot{width:6px;height:6px;border-radius:50%;background:currentColor;flex:none}
192
+
193
+ .gsAccount{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%}
194
+
195
+ .gsActions{display:flex;flex-wrap:wrap;gap:8px;align-items:center}
196
+ .gsBtn{box-sizing:border-box;height:36px;font:inherit;cursor:pointer;border:.5px solid var(--dsw-alias-border-l3);border-radius:18px;display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:0 14px;font-size:14px;line-height:22px;color:var(--dsw-alias-label-primary);background:0 0;transition:background .16s,border-color .16s,color .16s}
197
+ .gsBtn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}
198
+ .gsBtn:focus-visible{outline:2px solid var(--dsw-alias-border-l3);outline-offset:2px}
199
+ .gsBtn:disabled{opacity:.45;cursor:not-allowed}
200
+ .gsBtn--primary{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground);border-color:transparent}
201
+ .gsBtn--primary:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover)}
202
+ .gsBtn--danger:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger);color:var(--dsw-alias-label-error);border-color:var(--dsw-alias-label-error)}
203
+
204
+ .gsStatus{display:flex;align-items:flex-start;gap:8px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary)}
205
+ .gsStatus--ok{color:var(--dsw-alias-state-success-primary)}
206
+ .gsStatus--error{color:var(--dsw-alias-label-error)}
207
+ .gsStatus--warn{color:var(--dsw-alias-state-warn-label)}
208
+ .gsStatus--busy{color:var(--dsw-alias-label-tertiary)}
209
+
210
+ .gsDisclosure{margin:0}
211
+ .gsDisclosure>summary{display:flex;align-items:center;gap:8px;min-height:28px;cursor:pointer;list-style:none;font-size:13px;line-height:20px;color:var(--dsw-alias-label-secondary)}
212
+ .gsDisclosure>summary::-webkit-details-marker{display:none}
213
+ .gsDisclosure>summary:hover{color:var(--dsw-alias-label-primary)}
214
+ .gsDisclosure>summary:focus-visible{outline:2px solid var(--dsw-alias-border-l3);outline-offset:4px;border-radius:4px}
215
+ .gsChevron{flex:none;transition:transform .16s}
216
+ .gsDisclosure[open] .gsChevron{transform:rotate(180deg)}
217
+ .gsDisclosureBody{display:flex;flex-direction:column;gap:6px;padding-top:8px;margin-top:8px;border-top:.5px solid var(--dsw-alias-border-l2)}
218
+ .gsDisclosureBody p{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}
219
+
220
+ .gsGauge{display:flex;flex-direction:column;gap:8px}
221
+ .gsGaugeTop{display:flex;align-items:baseline;gap:8px}
222
+ .gsGaugeValue{font-size:24px;line-height:30px;font-weight:600;color:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums}
223
+ .gsGaugeLabel{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary)}
224
+ .gsBar{height:6px;border-radius:999px;background:color-mix(in srgb, var(--dsw-alias-label-primary) 14%, transparent);overflow:hidden}
225
+ .gsBar>span{display:block;height:100%;border-radius:999px;background:var(--dsw-alias-brand-primary);transition:width .2s}
226
+ .gsGaugeMeta{display:flex;flex-wrap:wrap;gap:4px 10px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary)}
227
+ .gsGaugeMeta code{font-size:11px;color:var(--dsw-alias-label-tertiary)}
228
+
229
+ .gsRows{display:flex;flex-direction:column;gap:0}
230
+ .gsRow{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:7px 0;font-size:13px;line-height:20px;border-top:.5px solid var(--dsw-alias-border-l2)}
231
+ .gsRow:first-child{border-top:none}
232
+ .gsRowValue{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;white-space:nowrap}
233
+
234
+ .gsModels{display:flex;flex-wrap:wrap;gap:6px}
235
+ .gsModel{display:inline-flex;align-items:baseline;gap:6px;border:.5px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);border-radius:10px;padding:4px 10px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-primary)}
236
+ .gsModel code{font-size:11px;color:var(--dsw-alias-label-tertiary)}
237
+
238
+ .gsHint,.gsEmpty,.gsCaveat{margin:0;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}
239
+ .gsCaveat{font-size:11px;line-height:17px;color:var(--dsw-alias-label-tertiary)}
240
+ .gsLink{color:var(--dsw-alias-link);font-size:13px;line-height:20px;text-decoration:none;align-self:center}
241
+ .gsLink:hover{text-decoration:underline}
242
+
243
+ @container (max-width: 420px){
244
+ .grokSubscription .gsGaugeValue{font-size:20px;line-height:26px}
245
+ .grokSubscription .gsBtn{flex:1 1 auto}
246
+ }
247
+ `;
248
+
168
249
  // src/client-composer-quota.jsx
169
250
  var import_react = require("react");
170
251
  var import_react_dom = require("react-dom");
@@ -533,60 +614,66 @@ async function callRpc(rpc, endpoint, payload = {}) {
533
614
  if (timer) clearTimeout(timer);
534
615
  }
535
616
  }
617
+ function Chevron() {
618
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { className: "gsChevron", width: "12", height: "12", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M4 6l4 4 4-4", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round" }) });
619
+ }
620
+ function Chip({ tone = "off", children }) {
621
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: `gsChip gsChip--${tone}`, children: [
622
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "gsDot" }),
623
+ children
624
+ ] });
625
+ }
536
626
  function UsagePanel({ usage, t, usageBusy, onRefresh, signedIn }) {
537
627
  const ok = usage?.status === "ok";
538
628
  const used = ok ? formatPercent(usage.usedPercent) : void 0;
539
629
  const remaining = ok ? formatPercent(usage.remainingPercent) : void 0;
540
630
  const resetLabel = usage?.periodEndLocal || usage?.periodEnd;
541
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "usageBlock", children: [
542
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("strong", { children: t("usageTitle") }),
543
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "muted", children: t("usageSubtitle") }),
544
- ok && used !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
545
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { children: [
546
- t("usageUsed"),
547
- ": ",
548
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("strong", { children: [
549
- used,
631
+ const gaugeValue = remaining ?? (used !== void 0 ? formatPercent(100 - Number(used)) : void 0);
632
+ const gaugeLabel = remaining !== void 0 ? t("usageRemaining") : t("usageUsed");
633
+ const fill = typeof gaugeValue === "number" && Number.isFinite(gaugeValue) ? Math.min(100, Math.max(0, gaugeValue)) : 0;
634
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gsCard", children: [
635
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "gsCardHead", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { children: t("usageTitle") }) }),
636
+ ok && (used !== void 0 || remaining !== void 0) ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gsGauge", children: [
637
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gsGaugeTop", children: [
638
+ gaugeValue !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "gsGaugeValue", children: [
639
+ gaugeValue,
550
640
  "%"
551
- ] }),
552
- remaining !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
553
- " \xB7 ",
554
- t("usageRemaining"),
641
+ ] }) : null,
642
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "gsGaugeLabel", children: gaugeLabel })
643
+ ] }),
644
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "gsBar", role: "img", "aria-label": `${gaugeLabel} ${gaugeValue ?? 0}%`, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { width: `${fill}%` } }) }),
645
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gsGaugeMeta", children: [
646
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
647
+ t("usageUsed"),
555
648
  " ",
556
- remaining,
649
+ used ?? "\u2014",
557
650
  "%"
558
- ] }) : null
559
- ] }),
560
- resetLabel ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "muted", children: [
561
- t("usageReset"),
562
- ": ",
563
- resetLabel,
564
- usage.periodEnd && usage.periodEndLocal ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
565
- " (",
566
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { children: usage.periodEnd }),
567
- ")"
568
- ] }) : null
569
- ] }) : null,
570
- Array.isArray(usage.productUsage) && usage.productUsage.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
571
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "muted", children: t("usageProduct") }),
572
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ul", { children: usage.productUsage.map((row, index) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("li", { children: [
573
- row.name ?? "\u2014",
574
- typeof row.usedPercent === "number" ? ` \xB7 ${formatPercent(row.usedPercent)}%` : ""
575
- ] }, `${row.name ?? "row"}-${index}`)) })
576
- ] }) : null,
577
- usage.fetchedAt ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "muted", children: [
578
- t("usageFetchedAt"),
579
- ": ",
580
- usage.fetchedAt
581
- ] }) : null
582
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { children: [
651
+ ] }),
652
+ resetLabel ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
653
+ t("usageReset"),
654
+ " ",
655
+ resetLabel
656
+ ] }) : null,
657
+ usage.periodEnd && usage.periodEndLocal ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { children: usage.periodEnd }) : null
658
+ ] })
659
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "gsEmpty", children: [
583
660
  t("usageUnavailable"),
584
661
  usage?.reason ? `: ${usage.reason}` : ""
585
662
  ] }),
586
- usageBusy ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "muted", children: t("busyUsage") }) : null,
587
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "row", children: [
588
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", disabled: Boolean(usageBusy) || !signedIn, onClick: onRefresh, children: t("usageRefresh") }),
589
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("a", { href: USAGE_PAGE_URL, target: "_blank", rel: "noreferrer", children: t("usageOpenGrok") })
663
+ Array.isArray(usage.productUsage) && usage.productUsage.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "gsRows", children: usage.productUsage.map((row, index) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gsRow", children: [
664
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: row.name ?? "\u2014" }),
665
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "gsRowValue", children: typeof row.usedPercent === "number" ? `${formatPercent(row.usedPercent)}%` : "\u2014" })
666
+ ] }, `${row.name ?? "row"}-${index}`)) }) : null,
667
+ usage.fetchedAt ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "gsHint", children: [
668
+ t("usageFetchedAt"),
669
+ ": ",
670
+ usage.fetchedAt
671
+ ] }) : null,
672
+ usageBusy ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "gsStatus gsStatus--busy", children: t("busyUsage") }) : null,
673
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "gsHint", children: t("usageSubtitle") }),
674
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gsActions", children: [
675
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "gsBtn", type: "button", disabled: Boolean(usageBusy) || !signedIn, onClick: onRefresh, children: t("usageRefresh") }),
676
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("a", { className: "gsLink", href: USAGE_PAGE_URL, target: "_blank", rel: "noreferrer", children: t("usageOpenGrok") })
590
677
  ] })
591
678
  ] });
592
679
  }
@@ -660,7 +747,8 @@ function GrokSubscriptionSection({ rpc, t }) {
660
747
  "login/cli": "loginOk",
661
748
  "login/device": "loginOk",
662
749
  logout: "logoutOk",
663
- "usage/refresh": "usageRefreshOk"
750
+ "usage/refresh": "usageRefreshOk",
751
+ "catalog/refresh": "catalogRefreshOk"
664
752
  }[endpoint];
665
753
  if (successKey) setNotice(t(successKey));
666
754
  if (endpoint === "usage/refresh" && value?.ok !== false && !value?.error) {
@@ -682,47 +770,43 @@ function GrokSubscriptionSection({ rpc, t }) {
682
770
  const models = catalog?.models ?? [];
683
771
  const signedIn = account?.signedIn === true;
684
772
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("section", { className: "grokSubscription", children: [
685
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", { children: `
686
- .grokSubscription { display: grid; gap: 12px; max-width: 42rem; }
687
- .grokSubscription h2 { margin: 0 0 4px; font-size: 1.15rem; }
688
- .grokSubscription p, .grokSubscription li { line-height: 1.5; }
689
- .grokSubscription .muted { opacity: 0.78; font-size: 0.92rem; }
690
- .grokSubscription .row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
691
- .grokSubscription button { cursor: pointer; }
692
- .grokSubscription .error { color: #b42318; }
693
- .grokSubscription .notice { color: #067647; }
694
- .grokSubscription ul { margin: 0; padding-left: 1.2rem; }
695
- .grokSubscription .usageBlock { display: grid; gap: 8px; padding: 10px 0; border-top: 1px solid color-mix(in srgb, currentColor 18%, transparent); }
696
- .grokSubscription a { color: inherit; }
697
- ` }),
698
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
773
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", { children: SETTINGS_STYLE }),
774
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gsHead", children: [
699
775
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h2", { children: t("title") }),
700
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "muted", children: t("subtitle") })
776
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Chip, { tone: signedIn ? "ok" : "off", children: signedIn ? t("signedIn") : t("signedOut") })
701
777
  ] }),
702
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
703
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("strong", { children: [
704
- t("account"),
705
- ": "
778
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "gsLead", children: t("subtitle") }),
779
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gsCard", children: [
780
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gsCardHead", children: [
781
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { children: t("account") }),
782
+ signedIn && account?.maskedAccount ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "gsAccount", children: account.maskedAccount }) : null
706
783
  ] }),
707
- signedIn ? `${t("signedIn")} \xB7 ${account.maskedAccount ?? t("unknownAccount")}` : t("signedOut")
708
- ] }),
709
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "row", children: [
710
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", disabled: Boolean(busy), onClick: () => void run("login/cli"), children: t("loginCli") }),
711
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", disabled: Boolean(busy), onClick: () => void run("login/device"), children: t("loginDevice") }),
712
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", disabled: Boolean(busy), onClick: () => void run("pull"), children: t("pull") }),
713
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", disabled: Boolean(busy) || !signedIn, onClick: () => void run("logout"), children: t("logout") })
784
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gsActions", children: [
785
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "gsBtn gsBtn--primary", type: "button", disabled: Boolean(busy), onClick: () => void run("login/cli"), children: t("loginCli") }),
786
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "gsBtn", type: "button", disabled: Boolean(busy), onClick: () => void run("login/device"), children: t("loginDevice") }),
787
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "gsBtn", type: "button", disabled: Boolean(busy), onClick: () => void run("pull"), children: t("pull") }),
788
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "gsBtn gsBtn--danger", type: "button", disabled: Boolean(busy) || !signedIn, onClick: () => void run("logout"), children: t("logout") })
789
+ ] }),
790
+ busy ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "gsStatus gsStatus--busy", children: t("busy") }) : null,
791
+ notice ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "gsStatus gsStatus--ok", children: notice }) : null,
792
+ error ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "gsStatus gsStatus--error", children: [
793
+ t("error"),
794
+ ": ",
795
+ error
796
+ ] }) : null,
797
+ status?.cliAvailable === false ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "gsStatus gsStatus--warn", children: t("cliMissing") }) : null,
798
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("details", { className: "gsDisclosure", children: [
799
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("summary", { children: [
800
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Chevron, {}),
801
+ t("loginHelp")
802
+ ] }),
803
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gsDisclosureBody", children: [
804
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: t("loginHint") }),
805
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: t("deviceHint") }),
806
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: t("pullHint") })
807
+ ] })
808
+ ] })
714
809
  ] }),
715
- busy ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "muted", children: t("busy") }) : null,
716
- notice ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "notice", children: notice }) : null,
717
- error ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "error", children: [
718
- t("error"),
719
- ": ",
720
- error
721
- ] }) : null,
722
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "muted", children: t("loginHint") }),
723
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "muted", children: t("deviceHint") }),
724
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "muted", children: t("pullHint") }),
725
- status?.cliAvailable === false ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "muted", children: t("cliMissing") }) : null,
726
810
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
727
811
  UsagePanel,
728
812
  {
@@ -733,21 +817,25 @@ function GrokSubscriptionSection({ rpc, t }) {
733
817
  onRefresh: () => void run("usage/refresh")
734
818
  }
735
819
  ),
736
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
737
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("strong", { children: t("models") }),
738
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "muted", children: sourceLabel(catalog?.source, t) }),
739
- models.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: t("noModels") }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ul", { children: models.map((model) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("li", { children: [
820
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gsCard", children: [
821
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gsCardHead", children: [
822
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { children: t("models") }),
823
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "gsSpacer" }),
824
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Chip, { tone: catalog?.source === "live" ? "ok" : catalog?.source === "fallback" ? "warn" : "off", children: sourceLabel(catalog?.source, t) })
825
+ ] }),
826
+ models.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "gsEmpty", children: t("noModels") }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "gsModels", children: models.map((model) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "gsModel", children: [
740
827
  model.name,
741
828
  " ",
742
829
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { children: model.id })
743
830
  ] }, model.id)) }),
744
- catalog?.error ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "error", children: [
831
+ catalog?.error ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "gsStatus gsStatus--error", children: [
745
832
  t("catalogError"),
746
833
  ": ",
747
834
  catalog.error
748
- ] }) : null
835
+ ] }) : null,
836
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "gsActions", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "gsBtn", type: "button", disabled: !signedIn, onClick: () => void run("catalog/refresh"), children: t("refreshCatalog") }) })
749
837
  ] }),
750
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "muted", children: t("caveats") })
838
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "gsCaveat", children: t("caveats") })
751
839
  ] });
752
840
  }
753
841
  function apply(ctx) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-grok-subscription",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "Use a SuperGrok or X Premium Grok Build subscription in DeepSeek Harness",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",