dsh-coding-subscription-oauth 0.5.6 → 0.5.8

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-coding-subscription-oauth",
3
3
  "description": "DeepSeek Harness coding-subscription OAuth: SuperGrok/Grok Build, ChatGPT Plus Codex, Kimi Code, Claude Code. Fixes AUTH API key is invalid, INVALID_REPLAY_STATE, grok-4.6 xhigh, Kimi Bearer vs x-api-key.",
4
- "version": "0.5.6",
4
+ "version": "0.5.8",
5
5
  "publishConfig": {
6
6
  "access": "public",
7
7
  "registry": "https://registry.npmjs.org/"
@@ -73,6 +73,10 @@ if (releaseVersions[0] !== manifest.version) {
73
73
  fail(`top CHANGELOG release (${releaseVersions[0] ?? "missing"}) does not match package version ${manifest.version}`);
74
74
  }
75
75
  if (!readme.includes(manifest.version)) fail(`README.md does not mention ${manifest.version}`);
76
+ const installDoc = await readFile(join(root, "INSTALL.md"), "utf8");
77
+ if (!installDoc.includes(manifest.version)) fail(`INSTALL.md does not mention ${manifest.version}`);
78
+ const readmeZh = await readFile(join(root, "README.zh-CN.md"), "utf8");
79
+ if (!readmeZh.includes(manifest.version)) fail(`README.zh-CN.md does not mention ${manifest.version}`);
76
80
 
77
81
  if (mode === "pack") run("pnpm", ["run", "release:build"]);
78
82
  else run("pnpm", ["run", "release:verify"]);
@@ -1,9 +1,11 @@
1
1
  /** About tab: terms, remote help, and plugin version. */
2
2
 
3
3
  import { PLUGIN_VERSION } from "../constants.ts";
4
- import { bodyStyle, cardStyle, hintStyle, warningStyle } from "../styles.ts";
4
+ import { bodyStyle, cardStyle, hintStyle, linkStyle, titleStyle, warningStyle } from "../styles.ts";
5
5
  import type { GrokBuildSettingsInjected } from "../types.ts";
6
6
 
7
+ const README_URL = "https://github.com/lninghaha/dsh-coding-subscription-oauth#readme";
8
+
7
9
  export interface AboutTabProps {
8
10
  t: GrokBuildSettingsInjected["t"];
9
11
  }
@@ -11,10 +13,18 @@ export interface AboutTabProps {
11
13
  export function AboutTab({ t }: AboutTabProps) {
12
14
  return (
13
15
  <section style={cardStyle} aria-labelledby="coding-oauth-about-title">
14
- <p style={warningStyle}>{t("termsWarning")}</p>
16
+ <h3 id="coding-oauth-about-title" style={{ ...titleStyle, fontSize: 16 }}>
17
+ {t("aboutTitle")}
18
+ </h3>
19
+ <p style={{ ...warningStyle, marginTop: 12 }}>{t("termsWarning")}</p>
15
20
  <p style={{ ...bodyStyle, marginTop: 12 }}>{t("remoteLoginHelp")}</p>
16
21
  <p style={{ ...hintStyle, marginTop: 12 }}>{t("pluginVersion", { version: PLUGIN_VERSION })}</p>
17
22
  <p style={{ ...hintStyle, marginTop: 8 }}>{t("aboutDocsHint")}</p>
23
+ <p style={{ marginTop: 8 }}>
24
+ <a href={README_URL} target="_blank" rel="noreferrer" style={linkStyle}>
25
+ {t("aboutDocsLink")}
26
+ </a>
27
+ </p>
18
28
  </section>
19
29
  );
20
30
  }
@@ -1,6 +1,6 @@
1
1
  /** Local API gateway settings tab. */
2
2
 
3
- import { useMemo, useState } from "react";
3
+ import { type KeyboardEvent, useMemo, useState } from "react";
4
4
  import { GATEWAY_PORT_MAX, GATEWAY_PORT_MIN } from "../constants.ts";
5
5
  import { buildGatewaySnippets, type GatewaySnippetId } from "../gatewaySnippets.ts";
6
6
  import { formatGatewayBaseUrl, parseGatewayPort, randomGatewayPort } from "../parsers.ts";
@@ -8,7 +8,6 @@ import {
8
8
  bodyStyle,
9
9
  buttonStyle,
10
10
  cardStyle,
11
- checkRowStyle,
12
11
  copyRowStyle,
13
12
  dotStyle,
14
13
  errorStyle,
@@ -29,6 +28,7 @@ import {
29
28
  import type { CopyField, GatewayView, GrokBuildSettingsInjected } from "../types.ts";
30
29
  import { Badge } from "./Badge.tsx";
31
30
  import { CopyButton } from "./CopyButton.tsx";
31
+ import { ToggleSwitch } from "./ToggleSwitch.tsx";
32
32
 
33
33
  export interface GatewayTabProps {
34
34
  t: GrokBuildSettingsInjected["t"];
@@ -79,7 +79,6 @@ export function GatewayTab({
79
79
  onPortDraftChange,
80
80
  onApplyPort,
81
81
  onRandomPort,
82
- onCopy,
83
82
  onCopyKey,
84
83
  onToggleKeyVisible,
85
84
  onRotateConfirm,
@@ -111,6 +110,34 @@ export function GatewayTab({
111
110
  return idle ?? t("copy");
112
111
  };
113
112
 
113
+ const focusSnippetTab = (index: number): void => {
114
+ const tab = SNIPPET_TABS[index];
115
+ if (tab === undefined) return;
116
+ setActiveSnippet(tab.id);
117
+ document.getElementById(`coding-oauth-snippet-tab-${tab.id}`)?.focus();
118
+ };
119
+
120
+ const onSnippetKeyDown = (event: KeyboardEvent<HTMLDivElement>): void => {
121
+ const current = SNIPPET_TABS.findIndex((tab) => tab.id === activeSnippet);
122
+ if (current < 0) return;
123
+ if (event.key === "ArrowRight" || event.key === "ArrowDown") {
124
+ event.preventDefault();
125
+ focusSnippetTab((current + 1) % SNIPPET_TABS.length);
126
+ } else if (event.key === "ArrowLeft" || event.key === "ArrowUp") {
127
+ event.preventDefault();
128
+ focusSnippetTab((current - 1 + SNIPPET_TABS.length) % SNIPPET_TABS.length);
129
+ } else if (event.key === "Home") {
130
+ event.preventDefault();
131
+ focusSnippetTab(0);
132
+ } else if (event.key === "End") {
133
+ event.preventDefault();
134
+ focusSnippetTab(SNIPPET_TABS.length - 1);
135
+ }
136
+ };
137
+
138
+ const openAiUrl = gateway === undefined ? "" : `${formatGatewayBaseUrl(gateway.bind, gateway.port)}/v1`;
139
+ const anthropicUrl = gateway === undefined ? "" : formatGatewayBaseUrl(gateway.bind, gateway.port);
140
+
114
141
  return (
115
142
  <section style={cardStyle} aria-labelledby="coding-oauth-gateway-title">
116
143
  <div>
@@ -138,7 +165,11 @@ export function GatewayTab({
138
165
  {t("gatewayLoading")}
139
166
  </div>
140
167
  </div>
141
- ) : gateway === undefined ? null : (
168
+ ) : gateway === undefined ? (
169
+ <p style={hintStyle} role="status">
170
+ {t("gatewayLoadFailed")}
171
+ </p>
172
+ ) : (
142
173
  <div style={nestedStyle}>
143
174
  <Badge
144
175
  label={gateway.running ? t("gatewayRunning") : t("gatewayStopped")}
@@ -172,13 +203,23 @@ export function GatewayTab({
172
203
  </div>
173
204
  </div>
174
205
  ) : (
175
- <label style={checkRowStyle}>
176
- <input
177
- type="checkbox"
206
+ <label
207
+ htmlFor="coding-oauth-gateway-enabled"
208
+ style={{
209
+ display: "flex",
210
+ alignItems: "center",
211
+ justifyContent: "space-between",
212
+ gap: 12,
213
+ fontSize: 14,
214
+ color: "var(--dsw-alias-label-primary)",
215
+ }}
216
+ >
217
+ <span>{t("gatewayEnabled")}</span>
218
+ <ToggleSwitch
219
+ id="coding-oauth-gateway-enabled"
178
220
  checked={gateway.enabled}
179
221
  disabled={gatewayBusy}
180
- onChange={(event) => {
181
- const enabled = event.target.checked;
222
+ onChange={(enabled) => {
182
223
  if (enabled) {
183
224
  setEnableConfirm(true);
184
225
  return;
@@ -186,7 +227,6 @@ export function GatewayTab({
186
227
  onEnabledChange(false);
187
228
  }}
188
229
  />
189
- <span>{t("gatewayEnabled")}</span>
190
230
  </label>
191
231
  )}
192
232
  <div>
@@ -261,34 +301,26 @@ export function GatewayTab({
261
301
  <p style={copyRowStyle}>
262
302
  <span style={hintStyle}>
263
303
  {t("gatewayOpenAiUrl")}
264
- <span style={{ display: "block", ...monoStyle }}>
265
- {`${formatGatewayBaseUrl(gateway.bind, gateway.port)}/v1`}
266
- </span>
304
+ <span style={{ display: "block", ...monoStyle }}>{openAiUrl}</span>
267
305
  </span>
268
- <button
269
- type="button"
270
- style={primaryButtonStyle}
271
- onClick={() => {
272
- onCopy("openai", `${formatGatewayBaseUrl(gateway.bind, gateway.port)}/v1`);
273
- }}
274
- >
275
- {copyLabel("openai")}
276
- </button>
306
+ <CopyButton
307
+ text={openAiUrl}
308
+ idleLabel={t("copy")}
309
+ copiedLabel={t("copied")}
310
+ failedLabel={t("copyFailed")}
311
+ />
277
312
  </p>
278
313
  <p style={copyRowStyle}>
279
314
  <span style={hintStyle}>
280
315
  {t("gatewayAnthropicUrl")}
281
- <span style={{ display: "block", ...monoStyle }}>{formatGatewayBaseUrl(gateway.bind, gateway.port)}</span>
316
+ <span style={{ display: "block", ...monoStyle }}>{anthropicUrl}</span>
282
317
  </span>
283
- <button
284
- type="button"
285
- style={buttonStyle}
286
- onClick={() => {
287
- onCopy("anthropic", formatGatewayBaseUrl(gateway.bind, gateway.port));
288
- }}
289
- >
290
- {copyLabel("anthropic")}
291
- </button>
318
+ <CopyButton
319
+ text={anthropicUrl}
320
+ idleLabel={t("copy")}
321
+ copiedLabel={t("copied")}
322
+ failedLabel={t("copyFailed")}
323
+ />
292
324
  </p>
293
325
  <p style={copyRowStyle}>
294
326
  <span style={hintStyle}>
@@ -310,15 +342,23 @@ export function GatewayTab({
310
342
  {gateway.enabled && snippets !== undefined ? (
311
343
  <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
312
344
  <h4 style={{ ...titleStyle, fontSize: 14 }}>{t("gatewaySnippetsTitle")}</h4>
313
- <div role="tablist" aria-label={t("gatewaySnippetsTitle")} style={segmentedNavStyle}>
345
+ <div
346
+ role="tablist"
347
+ aria-label={t("gatewaySnippetsTitle")}
348
+ style={segmentedNavStyle}
349
+ onKeyDown={onSnippetKeyDown}
350
+ >
314
351
  {SNIPPET_TABS.map((tab) => {
315
352
  const selected = activeSnippet === tab.id;
316
353
  return (
317
354
  <button
318
355
  key={tab.id}
356
+ id={`coding-oauth-snippet-tab-${tab.id}`}
319
357
  type="button"
320
358
  role="tab"
321
359
  aria-selected={selected}
360
+ aria-controls="coding-oauth-snippet-panel"
361
+ tabIndex={selected ? 0 : -1}
322
362
  style={selected ? segmentedTabActiveStyle : segmentedTabStyle}
323
363
  onClick={() => {
324
364
  setActiveSnippet(tab.id);
@@ -329,13 +369,18 @@ export function GatewayTab({
329
369
  );
330
370
  })}
331
371
  </div>
332
- <code style={snippetStyle}>{snippets[activeSnippet]}</code>
372
+ <div
373
+ id="coding-oauth-snippet-panel"
374
+ role="tabpanel"
375
+ aria-labelledby={`coding-oauth-snippet-tab-${activeSnippet}`}
376
+ >
377
+ <code style={snippetStyle}>{snippets[activeSnippet]}</code>
378
+ </div>
333
379
  <CopyButton
334
380
  text={snippets[activeSnippet]}
335
381
  idleLabel={t("copy")}
336
382
  copiedLabel={t("copied")}
337
383
  failedLabel={t("copyFailed")}
338
- primary
339
384
  />
340
385
  </div>
341
386
  ) : null}
@@ -344,7 +389,7 @@ export function GatewayTab({
344
389
  <p style={bodyStyle}>{t("gatewayRotateConfirm")}</p>
345
390
  <p style={hintStyle}>{t("gatewayRotateConfirmHint")}</p>
346
391
  <div style={{ display: "flex", flexWrap: "wrap", gap: 8 }}>
347
- <button type="button" style={buttonStyle} disabled={gatewayBusy} onClick={onRotate}>
392
+ <button type="button" style={primaryButtonStyle} disabled={gatewayBusy} onClick={onRotate}>
348
393
  {t("gatewayRotateConfirmAction")}
349
394
  </button>
350
395
  <button type="button" style={buttonStyle} disabled={gatewayBusy} onClick={onRotateCancel}>
@@ -11,8 +11,9 @@ export interface ProgressBarProps {
11
11
 
12
12
  function barColor(percent: number): string {
13
13
  if (percent >= 90) return "var(--dsw-alias-state-error-primary, #d92d20)";
14
- if (percent >= 75) return "var(--dsw-alias-state-warning-primary, #e06c00)";
15
- return "var(--dsw-alias-brand-primary, #1677ff)";
14
+ if (percent >= 75) return "var(--dsw-alias-state-warn-primary, #e06c00)";
15
+ // Prefer info fill over brand-primary: dark theme inverts brand-primary to near-white.
16
+ return "var(--dsw-alias-button-info-fill, var(--dsw-static-deepseek-500, #4f6ef7))";
16
17
  }
17
18
 
18
19
  export function ProgressBar({ value, label, meta }: ProgressBarProps) {
@@ -19,7 +19,7 @@ const trackStyle = (checked: boolean, disabled: boolean): CSSProperties => ({
19
19
  borderRadius: 11,
20
20
  flex: "0 0 auto",
21
21
  background: checked
22
- ? "var(--dsw-alias-brand-primary, #315fc7)"
22
+ ? "var(--dsw-alias-button-primary-fill)"
23
23
  : "var(--dsw-alias-border-l4, rgba(127, 127, 127, 0.45))",
24
24
  opacity: disabled ? 0.5 : 1,
25
25
  cursor: disabled ? "not-allowed" : "pointer",
@@ -35,7 +35,9 @@ const thumbStyle = (checked: boolean): CSSProperties => ({
35
35
  width: 18,
36
36
  height: 18,
37
37
  borderRadius: "50%",
38
- background: "#ffffff",
38
+ // Match DSH primary fill/foreground pairing so the thumb stays visible when
39
+ // dark theme inverts brand-primary to near-white.
40
+ background: checked ? "var(--dsw-alias-label-primary-foreground)" : "var(--dsw-alias-button-elevated-fill)",
39
41
  boxShadow: "0 1px 3px rgba(0, 0, 0, 0.25)",
40
42
  transition: TRANSITION,
41
43
  });
@@ -203,4 +203,4 @@ export const CONSUMED_PREVIEW_CODES = new Set([
203
203
  "unsafe_destination",
204
204
  ]);
205
205
 
206
- export const PLUGIN_VERSION = "0.5.6";
206
+ export const PLUGIN_VERSION = "0.5.8";
@@ -226,8 +226,10 @@ export const en = {
226
226
  remoteLoginHelp:
227
227
  "On a remote DSH host, open Settings here and use device-code sign-in for each provider. Complete the code on any browser that can reach the provider, then return to chat and pick the provider route model.",
228
228
  pluginVersion: "Plugin version {version}",
229
+ aboutTitle: "About",
229
230
  aboutDocsHint:
230
231
  "See the plugin README and INSTALL docs in the repository for setup, supported providers, and gateway safety notes.",
232
+ aboutDocsLink: "Open README on GitHub",
231
233
  otherLoginMethods: "Other sign-in methods",
232
234
  hideOtherLoginMethods: "Hide other methods",
233
235
  signInStepOpen: "Open the provider authorization page",
@@ -466,7 +468,9 @@ export const zh: { [Key in GrokBuildSettingsKey]: string } = {
466
468
  remoteLoginHelp:
467
469
  "在远程 DSH 主机上,打开此处 Settings,对每个供应商使用设备码登录。在能访问供应商的任意浏览器完成验证码,再回到聊天选择对应路由模型。",
468
470
  pluginVersion: "插件版本 {version}",
471
+ aboutTitle: "关于",
469
472
  aboutDocsHint: "仓库中的 README 与 INSTALL 文档介绍了安装步骤、支持的供应商以及网关安全注意事项。",
473
+ aboutDocsLink: "在 GitHub 打开 README",
470
474
  otherLoginMethods: "其他登录方式",
471
475
  hideOtherLoginMethods: "收起其他方式",
472
476
  signInStepOpen: "打开供应商授权页面",
@@ -61,9 +61,11 @@ export const buttonStyle: CSSProperties = {
61
61
  };
62
62
  export const primaryButtonStyle: CSSProperties = {
63
63
  ...buttonStyle,
64
- borderColor: "var(--dsw-alias-brand-primary, #315fc7)",
65
- background: "var(--dsw-alias-brand-primary, #315fc7)",
66
- color: "#ffffff",
64
+ // DSH dark theme flips brand-primary to near-white; use the button/foreground
65
+ // pair so primary CTAs stay readable in both light and dark mode.
66
+ border: "none",
67
+ background: "var(--dsw-alias-button-primary-fill)",
68
+ color: "var(--dsw-alias-label-primary-foreground)",
67
69
  boxShadow: "0 1px 3px rgba(0, 0, 0, 0.28)",
68
70
  fontWeight: 600,
69
71
  };
@@ -80,7 +82,9 @@ export const warningStyle: CSSProperties = {
80
82
  ...bodyStyle,
81
83
  padding: "10px 12px",
82
84
  borderRadius: 8,
83
- background: "var(--dsw-alias-bg-layer-1)",
85
+ border: "1px solid color-mix(in srgb, var(--dsw-alias-state-warn-primary, #e06c00) 35%, transparent)",
86
+ background: "color-mix(in srgb, var(--dsw-alias-state-warn-primary, #e06c00) 10%, transparent)",
87
+ color: "var(--dsw-alias-label-primary)",
84
88
  };
85
89
  export const tipStyle: CSSProperties = {
86
90
  ...bodyStyle,
@@ -234,9 +238,9 @@ export const stepNumberStyle: CSSProperties = {
234
238
  };
235
239
  export const stepNumberActiveStyle: CSSProperties = {
236
240
  ...stepNumberStyle,
237
- background: "var(--dsw-alias-brand-primary, #315fc7)",
238
- borderColor: "var(--dsw-alias-brand-primary, #315fc7)",
239
- color: "#ffffff",
241
+ background: "var(--dsw-alias-button-primary-fill)",
242
+ borderColor: "var(--dsw-alias-button-primary-fill)",
243
+ color: "var(--dsw-alias-label-primary-foreground)",
240
244
  };
241
245
 
242
246
  export type StatusTone = "success" | "error" | "warning" | "info" | "neutral";
@@ -248,11 +252,15 @@ export function statusToneColor(tone: StatusTone): string {
248
252
  case "error":
249
253
  return "var(--dsw-alias-state-error-primary, #d92d20)";
250
254
  case "warning":
251
- return "var(--dsw-alias-state-warning-primary, #e06c00)";
255
+ // DSH token is `state-warn-*` (not `state-warning-*`).
256
+ return "var(--dsw-alias-state-warn-primary, #e06c00)";
252
257
  case "info":
258
+ // Accent/text color (not a solid fill + white text pair).
253
259
  return "var(--dsw-alias-brand-primary, #1677ff)";
254
260
  default:
255
- return "var(--dsw-alias-label-dimmed, #9aa0a6)";
261
+ // Prefer tertiary over dimmed: dimmed is near-invisible on light cards
262
+ // and too dark on dark cards.
263
+ return "var(--dsw-alias-label-tertiary, #81858c)";
256
264
  }
257
265
  }
258
266
 
@@ -279,14 +287,14 @@ export function dotStyle(
279
287
  installed = true,
280
288
  ): CSSProperties {
281
289
  const color = !installed
282
- ? "var(--dsw-alias-label-dimmed, #9aa0a6)"
290
+ ? "var(--dsw-alias-label-tertiary, #81858c)"
283
291
  : status === "signed-in" || status === "available"
284
292
  ? "var(--dsw-alias-state-success-primary, #22a06b)"
285
293
  : status === "error"
286
294
  ? "var(--dsw-alias-state-error-primary, #d92d20)"
287
295
  : status === "signing-in" || status === "loading"
288
296
  ? "var(--dsw-alias-brand-primary, #1677ff)"
289
- : "var(--dsw-alias-label-dimmed, #9aa0a6)";
297
+ : "var(--dsw-alias-label-tertiary, #81858c)";
290
298
  return { width: 9, height: 9, borderRadius: "50%", flex: "0 0 auto", background: color };
291
299
  }
292
300
 
@@ -297,10 +305,3 @@ export function providerStatusTone(status: ProviderStatus["status"], installed =
297
305
  if (status === "signing-in") return "info";
298
306
  return "neutral";
299
307
  }
300
-
301
- /** @deprecated Use segmentedTabStyle / segmentedTabActiveStyle */
302
- export const tabNavStyle: CSSProperties = segmentedNavStyle;
303
- /** @deprecated Use segmentedTabStyle */
304
- export const tabButtonStyle: CSSProperties = segmentedTabStyle;
305
- /** @deprecated Use segmentedTabActiveStyle */
306
- export const tabButtonActiveStyle: CSSProperties = segmentedTabActiveStyle;