@eddyskywalker/dsh-chatgpt-subscription 0.1.37 → 0.1.39

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.
Files changed (56) hide show
  1. package/bin/antigravity-login.mjs +36 -0
  2. package/lib/client.js +1306 -53
  3. package/lib/client.js.map +1 -1
  4. package/lib/index.js +1921 -23
  5. package/lib/types/client/CodexSubscriptionSection.d.ts.map +1 -1
  6. package/lib/types/client/antigravity/AntigravityComposerQuota.d.ts +11 -0
  7. package/lib/types/client/antigravity/AntigravityComposerQuota.d.ts.map +1 -0
  8. package/lib/types/client/antigravity/AntigravitySection.d.ts +10 -0
  9. package/lib/types/client/antigravity/AntigravitySection.d.ts.map +1 -0
  10. package/lib/types/client/antigravity/locales.d.ts +298 -0
  11. package/lib/types/client/antigravity/locales.d.ts.map +1 -0
  12. package/lib/types/client/antigravity/styles.d.ts +3 -0
  13. package/lib/types/client/antigravity/styles.d.ts.map +1 -0
  14. package/lib/types/client/index.d.ts +1 -0
  15. package/lib/types/client/index.d.ts.map +1 -1
  16. package/lib/types/client/locales.d.ts +19 -7
  17. package/lib/types/client/locales.d.ts.map +1 -1
  18. package/lib/types/client/mermaid/renderer.d.ts +3 -0
  19. package/lib/types/client/mermaid/renderer.d.ts.map +1 -0
  20. package/lib/types/client/mermaid/sanitize.d.ts +2 -0
  21. package/lib/types/client/mermaid/sanitize.d.ts.map +1 -0
  22. package/lib/types/client/mermaid/styles.d.ts +3 -0
  23. package/lib/types/client/mermaid/styles.d.ts.map +1 -0
  24. package/lib/types/host/antigravity/adapter.d.ts +15 -0
  25. package/lib/types/host/antigravity/adapter.d.ts.map +1 -0
  26. package/lib/types/host/antigravity/client.d.ts +21 -0
  27. package/lib/types/host/antigravity/client.d.ts.map +1 -0
  28. package/lib/types/host/antigravity/mapper.d.ts +28 -0
  29. package/lib/types/host/antigravity/mapper.d.ts.map +1 -0
  30. package/lib/types/host/antigravity/oauth.d.ts +38 -0
  31. package/lib/types/host/antigravity/oauth.d.ts.map +1 -0
  32. package/lib/types/host/antigravity/routes.d.ts +6 -0
  33. package/lib/types/host/antigravity/routes.d.ts.map +1 -0
  34. package/lib/types/host/antigravity/token-store.d.ts +60 -0
  35. package/lib/types/host/antigravity/token-store.d.ts.map +1 -0
  36. package/lib/types/host/antigravity/types.d.ts +46 -0
  37. package/lib/types/host/antigravity/types.d.ts.map +1 -0
  38. package/lib/types/host/common/idle-watchdog.d.ts +8 -0
  39. package/lib/types/host/common/idle-watchdog.d.ts.map +1 -0
  40. package/lib/types/host/preferences.d.ts.map +1 -1
  41. package/lib/types/host/responses-client.d.ts +3 -1
  42. package/lib/types/host/responses-client.d.ts.map +1 -1
  43. package/lib/types/host/responses-mapper.d.ts +2 -2
  44. package/lib/types/host/responses-mapper.d.ts.map +1 -1
  45. package/lib/types/host/routes.d.ts.map +1 -1
  46. package/lib/types/index.d.ts +4 -0
  47. package/lib/types/index.d.ts.map +1 -1
  48. package/lib/types/shared/antigravity-contracts.d.ts +72 -0
  49. package/lib/types/shared/antigravity-contracts.d.ts.map +1 -0
  50. package/lib/types/shared/contracts.d.ts +3 -0
  51. package/lib/types/shared/contracts.d.ts.map +1 -1
  52. package/lib/types/shared/model-catalog.d.ts +7 -0
  53. package/lib/types/shared/model-catalog.d.ts.map +1 -1
  54. package/lib/types/shared/preferences.d.ts +2 -1
  55. package/lib/types/shared/preferences.d.ts.map +1 -1
  56. package/package.json +34 -29
package/lib/client.js CHANGED
@@ -4,7 +4,30 @@ window.__ModuleLoader__.load({
4
4
  var module = { exports: {} };
5
5
  var exports = module.exports;
6
6
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ //#region \0rolldown/runtime.js
8
+ var __create = Object.create;
9
+ var __defProp = Object.defineProperty;
10
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
11
+ var __getOwnPropNames = Object.getOwnPropertyNames;
12
+ var __getProtoOf = Object.getPrototypeOf;
13
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
14
+ var __copyProps = (to, from, except, desc) => {
15
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
16
+ key = keys[i];
17
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
18
+ get: ((k) => from[k]).bind(null, key),
19
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
20
+ });
21
+ }
22
+ return to;
23
+ };
24
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
25
+ value: mod,
26
+ enumerable: true
27
+ }) : target, mod));
28
+ //#endregion
7
29
  let react = require("react");
30
+ react = __toESM(react, 1);
8
31
  let react_jsx_runtime = require("react/jsx-runtime");
9
32
  const ROUTE_PREFIX = "/api/dsh-chatgpt-subscription";
10
33
  const CODEX_CHATGPT_PROVIDER_ID = "codex-chatgpt";
@@ -33,7 +56,7 @@ window.__ModuleLoader__.load({
33
56
  //#endregion
34
57
  //#region src/client/CodexComposerQuota.tsx
35
58
  function CodexComposerQuota({ api, directory, loadModelDirectory, t }) {
36
- const modelState = useStore(directory);
59
+ const modelState = useStore$1(directory);
37
60
  const [status, setStatus] = (0, react.useState)(null);
38
61
  const [loading, setLoading] = (0, react.useState)(false);
39
62
  const mountedRef = (0, react.useRef)(false);
@@ -80,7 +103,7 @@ window.__ModuleLoader__.load({
80
103
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("quickQuotaLabel") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: quota === null ? loading ? t("quickQuotaLoading") : "—" : formatPercent$1(quota.remainingPercent) })]
81
104
  });
82
105
  }
83
- function useStore(store) {
106
+ function useStore$1(store) {
84
107
  return (0, react.useSyncExternalStore)((listener) => store.subscribe(listener), () => store.getSnapshot(), () => store.getSnapshot());
85
108
  }
86
109
  function formatPercent$1(value) {
@@ -185,7 +208,8 @@ window.__ModuleLoader__.load({
185
208
  inputModalities: ["text", "image"],
186
209
  defaultReasoningEffort: "medium",
187
210
  reasoningProfile: "gpt-5.6",
188
- supportsReasoningSummary: true
211
+ supportsReasoningSummary: true,
212
+ fallbackModelId: "gpt-5.6-terra"
189
213
  },
190
214
  {
191
215
  id: "gpt-5.6-terra",
@@ -194,7 +218,8 @@ window.__ModuleLoader__.load({
194
218
  inputModalities: ["text", "image"],
195
219
  defaultReasoningEffort: "medium",
196
220
  reasoningProfile: "gpt-5.6",
197
- supportsReasoningSummary: true
221
+ supportsReasoningSummary: true,
222
+ fallbackModelId: "gpt-5.5"
198
223
  },
199
224
  {
200
225
  id: "gpt-5.6-luna",
@@ -203,7 +228,8 @@ window.__ModuleLoader__.load({
203
228
  inputModalities: ["text", "image"],
204
229
  defaultReasoningEffort: "medium",
205
230
  reasoningProfile: "gpt-5.6",
206
- supportsReasoningSummary: true
231
+ supportsReasoningSummary: true,
232
+ fallbackModelId: "gpt-5.5"
207
233
  },
208
234
  {
209
235
  id: "gpt-5.5",
@@ -221,7 +247,8 @@ window.__ModuleLoader__.load({
221
247
  inputModalities: ["text", "image"],
222
248
  defaultReasoningEffort: "none",
223
249
  reasoningProfile: "standard",
224
- supportsReasoningSummary: true
250
+ supportsReasoningSummary: true,
251
+ fallbackModelId: "gpt-5.4-mini"
225
252
  },
226
253
  {
227
254
  id: "gpt-5.4-mini",
@@ -486,7 +513,7 @@ window.__ModuleLoader__.load({
486
513
  setSubagentContextDraft(null);
487
514
  return;
488
515
  }
489
- const parsed = parsePositiveCapacity(draft);
516
+ const parsed = parsePositiveCapacity$1(draft);
490
517
  if (parsed === null) {
491
518
  setError(t("contextWindowInvalid"));
492
519
  return;
@@ -565,7 +592,7 @@ window.__ModuleLoader__.load({
565
592
  }),
566
593
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoRow, {
567
594
  label: t("expires"),
568
- value: formatDate(account?.tokenExpiresAt)
595
+ value: formatDate$1(account?.tokenExpiresAt)
569
596
  })
570
597
  ] }) : null,
571
598
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoRow, {
@@ -628,7 +655,7 @@ window.__ModuleLoader__.load({
628
655
  }),
629
656
  connection !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoRow, {
630
657
  label: t("latency"),
631
- value: `${connection.latencyMs} ms · ${formatDate(connection.checkedAt)}`
658
+ value: `${connection.latencyMs} ms · ${formatDate$1(connection.checkedAt)}`
632
659
  }) : null,
633
660
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
634
661
  className: "dsh-codex-muted dsh-codex-models-hint",
@@ -823,6 +850,44 @@ window.__ModuleLoader__.load({
823
850
  ]
824
851
  })]
825
852
  }),
853
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
854
+ className: "dsh-codex-pref-row",
855
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("reasoningSummary") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
856
+ className: "dsh-codex-muted",
857
+ children: t("reasoningSummaryHint")
858
+ })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
859
+ className: "dsh-codex-select",
860
+ "aria-label": t("reasoningSummary"),
861
+ value: preferences?.reasoningSummary ?? "",
862
+ disabled: busy !== null,
863
+ onChange: (event) => {
864
+ const value = event.currentTarget.value;
865
+ updatePreferences({ reasoningSummary: value === "" ? null : value });
866
+ },
867
+ children: [
868
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
869
+ value: "",
870
+ children: t("providerDefault")
871
+ }),
872
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
873
+ value: "auto",
874
+ children: t("summaryAuto")
875
+ }),
876
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
877
+ value: "concise",
878
+ children: t("summaryConcise")
879
+ }),
880
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
881
+ value: "detailed",
882
+ children: t("summaryDetailed")
883
+ }),
884
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
885
+ value: "none",
886
+ children: t("summaryNone")
887
+ })
888
+ ]
889
+ })]
890
+ }),
826
891
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
827
892
  className: "dsh-codex-pref-row",
828
893
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("subagentMaxDepth") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
@@ -883,7 +948,7 @@ window.__ModuleLoader__.load({
883
948
  type: "text",
884
949
  inputMode: "numeric",
885
950
  placeholder: t("providerDefault"),
886
- value: subagentContextDraft ?? (preferences?.subagentContextWindow ? formatCapacity(preferences.subagentContextWindow) : ""),
951
+ value: subagentContextDraft ?? (preferences?.subagentContextWindow ? formatCapacity$1(preferences.subagentContextWindow) : ""),
887
952
  disabled: busy !== null,
888
953
  "aria-label": t("subagentContextWindow"),
889
954
  onChange: (event) => {
@@ -932,7 +997,7 @@ window.__ModuleLoader__.load({
932
997
  id: inputId,
933
998
  type: "text",
934
999
  inputMode: "numeric",
935
- value: draft ?? formatCapacity(fallback),
1000
+ value: draft ?? formatCapacity$1(fallback),
936
1001
  disabled: busy !== null,
937
1002
  "aria-label": entry.name + " " + t("contextWindow"),
938
1003
  onChange: (event) => {
@@ -1046,7 +1111,7 @@ window.__ModuleLoader__.load({
1046
1111
  children: [
1047
1112
  t("updated"),
1048
1113
  ": ",
1049
- formatDate(quota.fetchedAt)
1114
+ formatDate$1(quota.fetchedAt)
1050
1115
  ]
1051
1116
  }) : null
1052
1117
  ]
@@ -1197,7 +1262,7 @@ window.__ModuleLoader__.load({
1197
1262
  return `${value} ${t("limitWindow")}`;
1198
1263
  }
1199
1264
  }
1200
- function parsePositiveCapacity(value) {
1265
+ function parsePositiveCapacity$1(value) {
1201
1266
  const matched = value.trim().toLowerCase().replace(/[,_\s]/g, "").match(/^(\d+(?:\.\d+)?)(k|m)?$/);
1202
1267
  if (matched === null) return null;
1203
1268
  const multiplier = matched[2] === "m" ? 1e6 : matched[2] === "k" ? 1e3 : 1;
@@ -1205,10 +1270,10 @@ window.__ModuleLoader__.load({
1205
1270
  return Number.isSafeInteger(parsed) && parsed >= 1 ? parsed : null;
1206
1271
  }
1207
1272
  function parseCapacity(value) {
1208
- const parsed = parsePositiveCapacity(value);
1273
+ const parsed = parsePositiveCapacity$1(value);
1209
1274
  return parsed !== null && parsed <= 1e6 ? parsed : null;
1210
1275
  }
1211
- function formatCapacity(value) {
1276
+ function formatCapacity$1(value) {
1212
1277
  if (value % 1e3 === 0) return `${value / 1e3}K`;
1213
1278
  return String(value);
1214
1279
  }
@@ -1229,7 +1294,7 @@ window.__ModuleLoader__.load({
1229
1294
  ].filter((part) => part !== null);
1230
1295
  return parts.length > 0 ? parts.join(" · ") : t("unknown");
1231
1296
  }
1232
- function formatDate(seconds) {
1297
+ function formatDate$1(seconds) {
1233
1298
  if (seconds === void 0 || seconds === null || typeof seconds !== "number" || !Number.isFinite(seconds) || seconds <= 0) return "—";
1234
1299
  const ms = seconds > 1e10 ? seconds : seconds * 1e3;
1235
1300
  try {
@@ -1244,7 +1309,7 @@ window.__ModuleLoader__.load({
1244
1309
  function formatReset(seconds) {
1245
1310
  if (seconds === void 0 || seconds === null || typeof seconds !== "number" || !Number.isFinite(seconds) || seconds <= 0) return "—";
1246
1311
  const ms = seconds > 1e10 ? seconds : seconds * 1e3;
1247
- const absolute = formatDate(seconds);
1312
+ const absolute = formatDate$1(seconds);
1248
1313
  const diff = ms - Date.now();
1249
1314
  const abs = Math.abs(diff);
1250
1315
  const [amount, unit] = abs >= 864e5 ? [Math.round(diff / 864e5), "day"] : abs >= 36e5 ? [Math.round(diff / 36e5), "hour"] : [Math.round(diff / 6e4), "minute"];
@@ -1260,10 +1325,10 @@ window.__ModuleLoader__.load({
1260
1325
  //#endregion
1261
1326
  //#region src/client/locales.ts
1262
1327
  const NS = "dsh-chatgpt-subscription";
1263
- const dictionaries = {
1328
+ const dictionaries$1 = {
1264
1329
  zh: {
1265
- title: "Codex 订阅",
1266
- intro: "使用 ChatGPT 账号登录,在 DSH 中使用订阅可用的 Codex 模型。",
1330
+ title: "ChatGPT",
1331
+ intro: "使用 ChatGPT 账号登录,在 DSH 中使用订阅可用的模型。",
1267
1332
  account: "账号",
1268
1333
  signedOut: "尚未登录",
1269
1334
  signedIn: "已登录",
@@ -1322,10 +1387,16 @@ window.__ModuleLoader__.load({
1322
1387
  verbosityLow: "低(保持回答简洁)",
1323
1388
  verbosityMedium: "中(兼顾细节与简洁)",
1324
1389
  verbosityHigh: "高(在回答中包含更多细节)",
1390
+ reasoningSummary: "推理摘要",
1391
+ reasoningSummaryHint: "选择 ChatGPT 总结其推理的方式;不影响思考深度。",
1392
+ summaryAuto: "自动(让模型选择摘要详细程度)",
1393
+ summaryConcise: "简洁(显示简要推理摘要)",
1394
+ summaryDetailed: "详细(显示更详细的推理摘要)",
1395
+ summaryNone: "无(不显示推理摘要)",
1325
1396
  searchProvider: "搜索与抓取来源",
1326
1397
  searchProviderHint: "选择 DSH 联网工具使用默认来源,或改用当前 ChatGPT 订阅的 Codex 来源(自动走代理隧道)。",
1327
1398
  searchProviderDsh: "DSH 默认",
1328
- searchProviderCodex: "Codex 订阅(代理隧道)",
1399
+ searchProviderCodex: "ChatGPT(代理隧道)",
1329
1400
  providerDefault: "使用 Provider 默认值",
1330
1401
  subagentEnhancements: "子代理设置",
1331
1402
  subagentContextWindow: "子代理上下文预算",
@@ -1387,8 +1458,8 @@ window.__ModuleLoader__.load({
1387
1458
  unknown: "未知"
1388
1459
  },
1389
1460
  en: {
1390
- title: "Codex subscription",
1391
- intro: "Sign in with ChatGPT to use Codex models available to your subscription in DSH.",
1461
+ title: "ChatGPT",
1462
+ intro: "Sign in with ChatGPT to use models available to your subscription in DSH.",
1392
1463
  account: "Account",
1393
1464
  signedOut: "Not signed in",
1394
1465
  signedIn: "Signed in",
@@ -1447,10 +1518,16 @@ window.__ModuleLoader__.load({
1447
1518
  verbosityLow: "Low (keep answers concise)",
1448
1519
  verbosityMedium: "Medium (balance detail and brevity)",
1449
1520
  verbosityHigh: "High (include more detail)",
1521
+ reasoningSummary: "Reasoning summary",
1522
+ reasoningSummaryHint: "Controls how ChatGPT summarizes its chain of thought; does not change reasoning depth.",
1523
+ summaryAuto: "Auto (let model determine detail)",
1524
+ summaryConcise: "Concise (brief summary)",
1525
+ summaryDetailed: "Detailed (in-depth summary)",
1526
+ summaryNone: "None (hide reasoning summary)",
1450
1527
  searchProvider: "Search & Fetch provider",
1451
1528
  searchProviderHint: "Choose whether DSH web tools use the default provider or the Codex proxy-backed source from this ChatGPT subscription.",
1452
1529
  searchProviderDsh: "DSH default",
1453
- searchProviderCodex: "Codex subscription (Proxy tunnel)",
1530
+ searchProviderCodex: "ChatGPT (Proxy tunnel)",
1454
1531
  providerDefault: "Use provider default",
1455
1532
  subagentEnhancements: "Subagent settings",
1456
1533
  subagentContextWindow: "Subagent context window budget",
@@ -1514,77 +1591,77 @@ window.__ModuleLoader__.load({
1514
1591
  };
1515
1592
  //#endregion
1516
1593
  //#region src/client/styles.ts
1517
- const STYLE_ID = "@eddyskywalker/dsh-chatgpt-subscription/main";
1594
+ const STYLE_ID$1 = "@eddyskywalker/dsh-chatgpt-subscription/main";
1518
1595
  const CSS = `
1519
1596
  .dsh-codex-page{box-sizing:border-box;color:var(--dsw-alias-label-primary);display:flex;flex-direction:column;gap:20px;max-width:780px;min-width:0;padding:2px 0 30px}
1520
1597
  .dsh-codex-page *{box-sizing:border-box}
1521
1598
  .dsh-codex-title{font-size:15px;font-weight:650;line-height:1.4;margin:0 0 5px}
1522
1599
  .dsh-codex-intro,.dsh-codex-muted{color:var(--dsw-alias-label-secondary);font-size:13px;line-height:1.55;margin:0}
1523
- .dsh-codex-group{border-top:1px solid var(--dsw-alias-border-l2);min-width:0}
1600
+ .dsh-codex-group{border-top:0.5px solid var(--dsw-alias-border-l2);min-width:0}
1524
1601
  .dsh-codex-grouphead{align-items:center;display:flex;gap:12px;justify-content:space-between;min-height:48px}
1525
1602
  .dsh-codex-grouphead h3{font-size:14px;font-weight:650;margin:0}
1526
- .dsh-codex-row{align-items:center;border-bottom:1px solid var(--dsw-alias-border-l2);display:flex;gap:20px;justify-content:space-between;min-height:44px;padding:8px 0}
1603
+ .dsh-codex-row{align-items:center;border-bottom:0.5px solid var(--dsw-alias-border-l2);display:flex;gap:20px;justify-content:space-between;min-height:44px;padding:8px 0}
1527
1604
  .dsh-codex-label{color:var(--dsw-alias-label-secondary);font-size:13px;flex:0 0 auto}
1528
1605
  .dsh-codex-value{font-size:13px;min-width:0;overflow-wrap:anywhere;text-align:right}
1529
1606
  .dsh-codex-actions{display:flex;flex-wrap:wrap;gap:8px;justify-content:flex-end;padding-top:12px}
1530
- .dsh-codex-button{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:999px;color:var(--dsw-alias-label-primary);cursor:pointer;font:inherit;font-size:13px;line-height:1;padding:8px 13px;white-space:nowrap}
1607
+ .dsh-codex-button{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:999px;corner-shape:round;color:var(--dsw-alias-label-primary);cursor:pointer;font:inherit;font-size:13px;line-height:1;padding:8px 13px;white-space:nowrap}
1531
1608
  .dsh-codex-button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}
1532
1609
  .dsh-codex-button:focus-visible,.dsh-codex-link:focus-visible{outline:2px solid var(--dsw-alias-button-info-fill,#397ee8);outline-offset:2px}
1533
1610
  .dsh-codex-button:disabled{cursor:default;opacity:.5}
1534
1611
  .dsh-codex-button-primary{background:var(--dsw-alias-button-info-fill,#397ee8);border-color:transparent;color:var(--dsw-alias-button-info-label,#fff)}
1535
- .dsh-codex-notice{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.55;margin:12px 0 0;padding:10px 12px}
1612
+ .dsh-codex-notice{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.55;margin:12px 0 0;padding:10px 12px}
1536
1613
  .dsh-codex-error,.dsh-codex-warning,.dsh-codex-success{font-size:12px;line-height:1.5;margin:8px 0 0}
1537
1614
  .dsh-codex-error{color:var(--dsw-alias-label-danger,#d94b4b)}
1538
1615
  .dsh-codex-warning{color:var(--dsw-alias-label-warning,#c77a18)}
1539
1616
  .dsh-codex-success{color:var(--dsw-alias-label-success,#2e9b62)}
1540
- .dsh-codex-errorbar{align-items:center;background:color-mix(in srgb,var(--dsw-alias-label-danger,#d94b4b) 9%,transparent);border:1px solid color-mix(in srgb,var(--dsw-alias-label-danger,#d94b4b) 28%,transparent);border-radius:7px;color:var(--dsw-alias-label-danger,#d94b4b);display:flex;font-size:13px;gap:12px;justify-content:space-between;padding:10px 12px}
1617
+ .dsh-codex-errorbar{align-items:center;background:color-mix(in srgb,var(--dsw-alias-label-danger,#d94b4b) 9%,transparent);border:0.5px solid color-mix(in srgb,var(--dsw-alias-label-danger,#d94b4b) 28%,transparent);border-radius:7px;color:var(--dsw-alias-label-danger,#d94b4b);display:flex;font-size:13px;gap:12px;justify-content:space-between;padding:10px 12px}
1541
1618
  .dsh-codex-link{color:var(--dsw-alias-label-link,#3278d4);display:inline-block;font-size:13px;margin-top:8px}
1542
1619
  .dsh-codex-models-hint{padding-top:10px}
1543
1620
  .dsh-codex-models{display:flex;flex-wrap:wrap;gap:7px;padding-top:8px}
1544
1621
  .dsh-codex-models label{cursor:pointer;display:block;position:relative}
1545
1622
  .dsh-codex-models input{position:absolute;opacity:0;pointer-events:none}
1546
- .dsh-codex-models span{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:6px;color:var(--dsw-alias-label-secondary);display:block;font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:11px;padding:5px 8px}
1623
+ .dsh-codex-models span{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:6px;color:var(--dsw-alias-label-secondary);display:block;font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:11px;padding:5px 8px}
1547
1624
  .dsh-codex-models input:checked+span{background:color-mix(in srgb,var(--dsw-alias-button-info-fill,#397ee8) 14%,var(--dsw-alias-bg-layer-2));border-color:color-mix(in srgb,var(--dsw-alias-button-info-fill,#397ee8) 55%,var(--dsw-alias-border-l2));color:var(--dsw-alias-label-primary)}
1548
1625
  .dsh-codex-models input:focus-visible+span{outline:2px solid var(--dsw-alias-button-info-fill,#397ee8);outline-offset:2px}
1549
1626
  .dsh-codex-models input:disabled+span{cursor:default;opacity:.5}
1550
- .dsh-codex-pref-row{align-items:center;border-bottom:1px solid var(--dsw-alias-border-l2);display:flex;gap:16px;justify-content:space-between;min-height:58px;padding:10px 0}
1627
+ .dsh-codex-pref-row{align-items:center;border-bottom:0.5px solid var(--dsw-alias-border-l2);display:flex;gap:16px;justify-content:space-between;min-height:58px;padding:10px 0}
1551
1628
  .dsh-codex-pref-row strong,.dsh-codex-check strong{display:block;font-size:13px;font-weight:600;line-height:1.35}
1552
- .dsh-codex-segments{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;display:inline-flex;flex-wrap:nowrap;gap:3px;padding:3px}
1629
+ .dsh-codex-segments{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:8px;display:inline-flex;flex-wrap:nowrap;gap:3px;padding:3px}
1553
1630
  .dsh-codex-segments label{cursor:pointer;display:block;margin:0}
1554
1631
  .dsh-codex-segments input{position:absolute;opacity:0;pointer-events:none}
1555
- .dsh-codex-segments span,.dsh-codex-segments button{border:1px solid transparent;border-radius:6px;color:var(--dsw-alias-label-secondary);display:block;font:inherit;font-size:12px;font-weight:500;line-height:1.2;padding:6px 12px;transition:all .15s ease;user-select:none;white-space:nowrap}
1632
+ .dsh-codex-segments span,.dsh-codex-segments button{border:0.5px solid transparent;border-radius:6px;color:var(--dsw-alias-label-secondary);display:block;font:inherit;font-size:12px;font-weight:500;line-height:1.2;padding:6px 12px;transition:all .15s ease;user-select:none;white-space:nowrap}
1556
1633
  .dsh-codex-segments button{background:transparent;cursor:pointer}
1557
1634
  .dsh-codex-segments label:hover span,.dsh-codex-segments button:hover:not(:disabled){color:var(--dsw-alias-label-primary)}
1558
1635
  .dsh-codex-segments input:checked+span,.dsh-codex-segments button.active,.dsh-codex-segments button[aria-pressed=true]{background:color-mix(in srgb,var(--dsw-alias-button-info-fill,#397ee8) 22%,var(--dsw-alias-bg-layer-2));border-color:color-mix(in srgb,var(--dsw-alias-button-info-fill,#397ee8) 60%,var(--dsw-alias-border-l2));color:var(--dsw-alias-label-primary);font-weight:600;box-shadow:0 1px 3px rgba(0,0,0,.15)}
1559
1636
  .dsh-codex-segments input:focus-visible+span,.dsh-codex-segments button:focus-visible{outline:2px solid var(--dsw-alias-button-info-fill,#397ee8);outline-offset:2px}
1560
1637
  .dsh-codex-segments input:disabled+span,.dsh-codex-segments button:disabled{cursor:default;opacity:.5}
1561
- .dsh-codex-select,.dsh-codex-capacity-control input{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-primary);font:inherit;font-size:12px;padding:7px 9px}
1638
+ .dsh-codex-select,.dsh-codex-capacity-control input{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-primary);font:inherit;font-size:12px;padding:7px 9px}
1562
1639
  .dsh-codex-select{max-width:210px;min-width:150px}
1563
- .dsh-codex-context-settings{border-bottom:1px solid var(--dsw-alias-border-l2);display:grid;gap:10px;padding:12px 0}
1640
+ .dsh-codex-context-settings{border-bottom:0.5px solid var(--dsw-alias-border-l2);display:grid;gap:10px;padding:12px 0}
1564
1641
  .dsh-codex-context-settings>div>strong{font-size:13px;font-weight:600}
1565
1642
  .dsh-codex-context-row{align-items:center;display:flex;font-size:12px;gap:14px;justify-content:space-between}
1566
1643
  .dsh-codex-proxy-control{align-items:center;display:flex;gap:8px;width:100%}
1567
- .dsh-codex-proxy-control input{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-primary);flex:1;font:inherit;font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:12px;min-width:220px;padding:7px 10px;text-align:left}
1644
+ .dsh-codex-proxy-control input{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-primary);flex:1;font:inherit;font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:12px;min-width:220px;padding:7px 10px;text-align:left}
1568
1645
  .dsh-codex-proxy-status{align-items:center;color:var(--dsw-alias-label-secondary);display:flex;flex-wrap:wrap;font-size:12px;gap:6px;margin:6px 0 0}
1569
- .dsh-codex-proxy-tag{background:color-mix(in srgb,var(--dsw-alias-button-info-fill,#397ee8) 18%,transparent);border:1px solid color-mix(in srgb,var(--dsw-alias-button-info-fill,#397ee8) 45%,transparent);border-radius:4px;color:var(--dsw-alias-label-primary);font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:11px;padding:2px 6px}
1646
+ .dsh-codex-proxy-tag{background:color-mix(in srgb,var(--dsw-alias-button-info-fill,#397ee8) 18%,transparent);border:0.5px solid color-mix(in srgb,var(--dsw-alias-button-info-fill,#397ee8) 45%,transparent);border-radius:4px;color:var(--dsw-alias-label-primary);font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:11px;padding:2px 6px}
1570
1647
  .dsh-codex-capacity-control{align-items:center;display:flex;gap:7px}
1571
1648
  .dsh-codex-capacity-control input{font-family:ui-monospace,SFMono-Regular,Consolas,monospace;text-align:right;width:90px}
1572
1649
  .dsh-codex-capacity-control input[type=number]{width:110px}
1573
1650
  .dsh-codex-capacity-control small{color:var(--dsw-alias-label-tertiary);font-size:11px;width:42px}
1574
- .dsh-codex-context-save{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-primary);cursor:pointer;font:inherit;font-size:12px;line-height:1;padding:8px 10px;white-space:nowrap}
1651
+ .dsh-codex-context-save{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-primary);cursor:pointer;font:inherit;font-size:12px;line-height:1;padding:8px 10px;white-space:nowrap}
1575
1652
  .dsh-codex-context-save:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}
1576
1653
  .dsh-codex-context-save:focus-visible{outline:2px solid var(--dsw-alias-button-info-fill,#397ee8);outline-offset:2px}
1577
1654
  .dsh-codex-context-save:disabled{cursor:default;opacity:.45}
1578
- .dsh-codex-check{align-items:flex-start;border-bottom:1px solid var(--dsw-alias-border-l2);cursor:pointer;display:flex;gap:10px;padding:12px 0}
1655
+ .dsh-codex-check{align-items:flex-start;border-bottom:0.5px solid var(--dsw-alias-border-l2);cursor:pointer;display:flex;gap:10px;padding:12px 0}
1579
1656
  .dsh-codex-check input{flex:none;margin-top:2px}
1580
1657
  .dsh-codex-check small{color:var(--dsw-alias-label-secondary);display:block;font-size:12px;line-height:1.45;margin-top:2px}
1581
- .dsh-codex-quota-card{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;margin-top:12px;padding:12px}
1658
+ .dsh-codex-quota-card{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:8px;margin-top:12px;padding:12px}
1582
1659
  .dsh-codex-quota-title{align-items:center;display:flex;font-size:13px;gap:8px;justify-content:space-between}
1583
1660
  .dsh-codex-quota-title span{color:var(--dsw-alias-label-tertiary);font-size:11px;text-transform:uppercase}
1584
- .dsh-codex-quota-fact{align-items:flex-start;border-bottom:1px solid var(--dsw-alias-border-l2);display:flex;font-size:12px;gap:12px;justify-content:space-between;line-height:1.45;padding:10px 0}
1661
+ .dsh-codex-quota-fact{align-items:flex-start;border-bottom:0.5px solid var(--dsw-alias-border-l2);display:flex;font-size:12px;gap:12px;justify-content:space-between;line-height:1.45;padding:10px 0}
1585
1662
  .dsh-codex-quota-fact span{color:var(--dsw-alias-label-secondary);flex:none}
1586
1663
  .dsh-codex-quota-fact strong{font-weight:600;min-width:0;overflow-wrap:anywhere;text-align:right}
1587
- .dsh-codex-reset-credits{align-items:center;border-bottom:1px solid var(--dsw-alias-border-l2);display:flex;gap:16px;justify-content:space-between;padding:10px 0}
1664
+ .dsh-codex-reset-credits{align-items:center;border-bottom:0.5px solid var(--dsw-alias-border-l2);display:flex;gap:16px;justify-content:space-between;padding:10px 0}
1588
1665
  .dsh-codex-reset-credits-info{display:grid;font-size:12px;gap:2px;grid-template-columns:auto auto}
1589
1666
  .dsh-codex-reset-credits-info>span{color:var(--dsw-alias-label-secondary)}
1590
1667
  .dsh-codex-reset-credits-info>strong{font-weight:650;text-align:right}
@@ -1593,44 +1670,1182 @@ window.__ModuleLoader__.load({
1593
1670
  .dsh-codex-meter-label,.dsh-codex-meter-meta{display:flex;gap:10px;justify-content:space-between}
1594
1671
  .dsh-codex-meter-label{font-size:12px;margin-bottom:6px}
1595
1672
  .dsh-codex-meter-meta{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:1.45;margin-top:6px}
1596
- .dsh-codex-meter{background:var(--dsw-alias-bg-layer-1,rgba(127,127,127,.15));border-radius:999px;height:7px;overflow:hidden;width:100%}
1673
+ .dsh-codex-meter{background:var(--dsw-alias-bg-layer-1,rgba(127,127,127,.15));border-radius:999px;corner-shape:round;height:7px;overflow:hidden;width:100%}
1597
1674
  .dsh-codex-meter>span{background:var(--dsw-alias-button-info-fill,#397ee8);border-radius:inherit;display:block;height:100%;max-width:100%;min-width:0;transition:width .25s ease}
1598
1675
  .dsh-codex-meter-warning>span{background:var(--dsw-alias-label-warning,#d58a24)}
1599
1676
  .dsh-codex-meter-danger>span{background:var(--dsw-alias-label-danger,#d94b4b)}
1600
- .dsh-codex-empty{border:1px dashed var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-tertiary);font-size:12px;margin:12px 0 0;padding:16px;text-align:center}
1677
+ .dsh-codex-empty{border:0.5px dashed var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-tertiary);font-size:12px;margin:12px 0 0;padding:16px;text-align:center}
1601
1678
  .dsh-codex-timestamp{color:var(--dsw-alias-label-tertiary);font-size:11px;margin:10px 0 0;text-align:right}
1602
1679
  .dsh-codex-skeleton{display:grid;gap:9px;padding-top:10px}
1603
1680
  .dsh-codex-skeleton span{animation:dsh-codex-pulse 1.4s ease-in-out infinite;background:var(--dsw-alias-bg-layer-2);border-radius:5px;height:42px}
1604
1681
  .dsh-codex-skeleton span:nth-child(2){animation-delay:.12s}.dsh-codex-skeleton span:nth-child(3){animation-delay:.24s}
1605
1682
  .dsh-codex-sr{height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;clip:rect(0 0 0 0);white-space:nowrap}
1606
- .dsh-codex-composer-quota{align-items:center;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:999px;color:var(--dsw-alias-label-secondary);display:inline-flex;font-size:11px;gap:6px;height:28px;line-height:1;max-width:160px;padding:0 9px;white-space:nowrap}
1683
+ .dsh-codex-composer-quota{align-items:center;background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:999px;corner-shape:round;color:var(--dsw-alias-label-secondary);display:inline-flex;font-size:11px;gap:6px;height:28px;line-height:1;max-width:160px;padding:0 9px;white-space:nowrap}
1607
1684
  .dsh-codex-composer-quota strong{color:var(--dsw-alias-label-primary);font-size:11px;font-weight:650}
1608
1685
  .dsh-codex-composer-quota[data-level=warning] strong{color:var(--dsw-alias-label-warning,#d58a24)}
1609
1686
  .dsh-codex-composer-quota[data-level=danger] strong{color:var(--dsw-alias-label-danger,#d94b4b)}
1610
- .dsh-codex-image-tool{border:1px solid var(--dsw-alias-border-l2);border-radius:8px;display:flex;flex-direction:column;gap:8px;margin:4px 0 4px 4px;max-width:520px;padding:10px 12px}
1687
+ .dsh-codex-image-tool{border:0.5px solid var(--dsw-alias-border-l2);border-radius:8px;display:flex;flex-direction:column;gap:8px;margin:4px 0 4px 4px;max-width:520px;padding:10px 12px}
1611
1688
  .dsh-codex-image-tool-head{align-items:center;display:flex;font-size:13px;gap:8px;min-width:0}
1612
- .dsh-codex-image-dot{background:var(--dsw-alias-button-info-fill,#397ee8);border-radius:50%;display:inline-block;flex:none;height:8px;width:8px}
1689
+ .dsh-codex-image-dot{background:var(--dsw-alias-button-info-fill,#397ee8);border-radius:50%;corner-shape:round;display:inline-block;flex:none;height:8px;width:8px}
1613
1690
  .dsh-codex-image-tool[data-state=error] .dsh-codex-image-dot{background:var(--dsw-alias-label-danger,#d94b4b)}
1614
1691
  .dsh-codex-image-title{font-weight:600}
1615
1692
  .dsh-codex-image-summary{color:var(--dsw-alias-label-tertiary);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
1616
1693
  .dsh-codex-image-grid{display:flex;flex-wrap:wrap;gap:8px}
1617
- .dsh-codex-image-preview{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;display:block;max-height:260px;max-width:min(100%,360px);object-fit:contain}
1618
- .dsh-codex-image-loading,.dsh-codex-image-failed{border:1px dashed var(--dsw-alias-border-l2);border-radius:8px;color:var(--dsw-alias-label-tertiary);display:inline-flex;font-size:12px;line-height:1.4;padding:18px 20px}
1694
+ .dsh-codex-image-preview{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:8px;display:block;max-height:260px;max-width:min(100%,360px);object-fit:contain}
1695
+ .dsh-codex-image-loading,.dsh-codex-image-failed{border:0.5px dashed var(--dsw-alias-border-l2);border-radius:8px;color:var(--dsw-alias-label-tertiary);display:inline-flex;font-size:12px;line-height:1.4;padding:18px 20px}
1619
1696
  .dsh-codex-image-failed{color:var(--dsw-alias-label-danger,#d94b4b)}
1620
1697
  @keyframes dsh-codex-pulse{0%,100%{opacity:.55}50%{opacity:1}}
1621
1698
  @media(max-width:560px){.dsh-codex-row,.dsh-codex-pref-row,.dsh-codex-quota-fact,.dsh-codex-reset-credits{align-items:flex-start;flex-direction:column;gap:3px}.dsh-codex-value,.dsh-codex-quota-fact strong{text-align:left}.dsh-codex-actions{justify-content:flex-start}.dsh-codex-grouphead{align-items:flex-start;flex-direction:column;gap:0;padding:12px 0}.dsh-codex-meter-meta{align-items:flex-start;flex-direction:column;gap:2px}.dsh-codex-errorbar{align-items:flex-start;flex-direction:column}.dsh-codex-segments{width:100%}.dsh-codex-select{max-width:none;width:100%}.dsh-codex-context-row{align-items:flex-start;flex-direction:column;gap:5px}.dsh-codex-segments label{flex:1}.dsh-codex-segments span{text-align:center}.dsh-codex-image-tool{max-width:100%;margin-left:0}}
1622
1699
  @media(prefers-reduced-motion:reduce){.dsh-codex-meter>span{transition:none}.dsh-codex-skeleton span{animation:none}}
1623
1700
  `;
1624
1701
  function installStyles() {
1625
- if (document.querySelector(`style[data-plugin-css="${STYLE_ID}"]`) !== null) return () => void 0;
1702
+ if (document.querySelector(`style[data-plugin-css="${STYLE_ID$1}"]`) !== null) return () => void 0;
1626
1703
  const element = document.createElement("style");
1627
1704
  element.dataset.plugin = "@eddyskywalker/dsh-chatgpt-subscription";
1628
- element.dataset.pluginCss = STYLE_ID;
1705
+ element.dataset.pluginCss = STYLE_ID$1;
1629
1706
  element.textContent = CSS;
1630
1707
  document.head.appendChild(element);
1631
1708
  return () => element.remove();
1632
1709
  }
1633
1710
  //#endregion
1711
+ //#region src/client/antigravity/locales.ts
1712
+ const NS_ANTIGRAVITY = "dsh-antigravity";
1713
+ const zh = {
1714
+ title: "Antigravity",
1715
+ pageDesc: "登录 Google Antigravity / Cloud Code Assist,并查看当前账号的共享额度。",
1716
+ account: "已登录",
1717
+ signedIn: "已登录",
1718
+ signedOut: "未登录",
1719
+ plan: "套餐",
1720
+ accountId: "账号 ID",
1721
+ expires: "令牌到期",
1722
+ storage: "凭据存储",
1723
+ storageNotice: "令牌由 Host 保存于本地安全存储 ($DSH_HOME/storages/antigravity-oauth.json),不会进入浏览器。",
1724
+ signInAgain: "重新登录",
1725
+ refreshToken: "刷新凭据",
1726
+ signOut: "注销",
1727
+ signIn: "登录",
1728
+ signingIn: "登录中...",
1729
+ connection: "连接",
1730
+ provider: "Provider",
1731
+ providerValue: "Google Antigravity · antigravity",
1732
+ connectionState: "连接状态",
1733
+ connected: "已连接",
1734
+ untested: "未测试",
1735
+ modelsHint: "勾选后模型才会显示在对话页的模型选择列表中;至少保留一个。",
1736
+ selectAll: "全选",
1737
+ unselectAll: "全不选",
1738
+ enhanced: "增强功能",
1739
+ defaultReasoningEffort: "默认思考深度",
1740
+ defaultReasoningEffortHint: "控制支持推理的模型在未单独指定时的思考强度。",
1741
+ defaultEffortAuto: "使用 Provider 默认值",
1742
+ contextWindowSection: "模型上下文窗口",
1743
+ contextWindowHint: "默认按模型标准;可选应用最高容量,该值用于 DSH 的压缩与溢出判断,可输入 1M、512K、200K 等容量。",
1744
+ save: "保存",
1745
+ saved: "已保存",
1746
+ saving: "保存中...",
1747
+ quotaSection: "用量与配额",
1748
+ quotaDesc: "数据来自 Google Cloud Code Assist 配额服务,页面可见时最多每 60 秒刷新一次。",
1749
+ refreshQuota: "刷新用量",
1750
+ refreshingQuota: "刷新中...",
1751
+ resetPrefix: "重置: {time}",
1752
+ resetUnavailable: "重置: n/a",
1753
+ resetNow: "现在",
1754
+ timeDayHour: "{days}天 {hours}时",
1755
+ timeHourMin: "{hours}h {minutes}m",
1756
+ timeMin: "{minutes}m",
1757
+ updatedAt: "更新时间:{time}",
1758
+ loading: "加载中...",
1759
+ loginFailed: "登录失败",
1760
+ tokens: "tokens"
1761
+ };
1762
+ const en = {
1763
+ title: "Antigravity",
1764
+ pageDesc: "Sign in to Google Antigravity / Cloud Code Assist and view shared quotas.",
1765
+ account: "Signed in",
1766
+ signedIn: "Signed in",
1767
+ signedOut: "Not signed in",
1768
+ plan: "Plan",
1769
+ accountId: "Account ID",
1770
+ expires: "Token expires",
1771
+ storage: "Credential storage",
1772
+ storageNotice: "Tokens are stored locally by the Host ($DSH_HOME/storages/antigravity-oauth.json) and never sent to browser.",
1773
+ signInAgain: "Sign in again",
1774
+ refreshToken: "Refresh token",
1775
+ signOut: "Sign out",
1776
+ signIn: "Sign in",
1777
+ signingIn: "Signing in...",
1778
+ connection: "Connection",
1779
+ provider: "Provider",
1780
+ providerValue: "Google Antigravity · antigravity",
1781
+ connectionState: "Connection state",
1782
+ connected: "Connected",
1783
+ untested: "Untested",
1784
+ modelsHint: "Checked models will appear in DSH conversation model list; keep at least one.",
1785
+ selectAll: "Select all",
1786
+ unselectAll: "Deselect all",
1787
+ enhanced: "Enhanced Features",
1788
+ defaultReasoningEffort: "Default reasoning effort",
1789
+ defaultReasoningEffortHint: "Controls reasoning effort for reasoning models when not explicitly specified in conversation.",
1790
+ defaultEffortAuto: "Use Provider default",
1791
+ contextWindowSection: "Model Context Window",
1792
+ contextWindowHint: "Defaults to model specification; configure custom capacity used by DSH for compaction and overflow judgments (e.g., 1M, 512K, 200K).",
1793
+ save: "Save",
1794
+ saved: "Saved",
1795
+ saving: "Saving...",
1796
+ quotaSection: "Usage & Quota",
1797
+ quotaDesc: "Data from Google Cloud Code Assist quota service; refreshes up to once every 60 seconds when visible.",
1798
+ refreshQuota: "Refresh quota",
1799
+ refreshingQuota: "Refreshing...",
1800
+ resetPrefix: "Reset: {time}",
1801
+ resetUnavailable: "Reset: n/a",
1802
+ resetNow: "now",
1803
+ timeDayHour: "{days}d {hours}h",
1804
+ timeHourMin: "{hours}h {minutes}m",
1805
+ timeMin: "{minutes}m",
1806
+ updatedAt: "Updated at: {time}",
1807
+ loading: "Loading...",
1808
+ loginFailed: "Login failed",
1809
+ tokens: "tokens"
1810
+ };
1811
+ const dictionaries = {
1812
+ zh,
1813
+ "zh-CN": zh,
1814
+ en,
1815
+ "en-US": en
1816
+ };
1817
+ //#endregion
1818
+ //#region src/client/antigravity/AntigravitySection.tsx
1819
+ const API$1 = "/antigravity/api";
1820
+ async function fetchApi$1(path, options) {
1821
+ const res = await fetch(`${API$1}${path}`, {
1822
+ ...options,
1823
+ headers: {
1824
+ "Content-Type": "application/json",
1825
+ ...options?.headers
1826
+ }
1827
+ });
1828
+ const json = await res.json();
1829
+ if (!res.ok || !json.ok) throw new Error(json.error || `HTTP ${res.status}`);
1830
+ return json.value;
1831
+ }
1832
+ function parsePositiveCapacity(value) {
1833
+ const matched = value.trim().toLowerCase().replace(/[,_\s]/g, "").match(/^(\d+(?:\.\d+)?)(k|m)?$/);
1834
+ if (matched === null) return null;
1835
+ const multiplier = matched[2] === "m" ? 1e6 : matched[2] === "k" ? 1e3 : 1;
1836
+ const parsed = Number(matched[1]) * multiplier;
1837
+ return Number.isSafeInteger(parsed) && parsed >= 1 ? parsed : null;
1838
+ }
1839
+ function formatCapacity(value) {
1840
+ if (value >= 1e6 && value % 1e5 === 0) return `${value / 1e6}M`;
1841
+ if (value % 1e3 === 0) return `${value / 1e3}K`;
1842
+ return String(value);
1843
+ }
1844
+ function formatResetTime(resetTime) {
1845
+ if (!resetTime) return "";
1846
+ const diff = new Date(resetTime).getTime() - Date.now();
1847
+ if (diff <= 0) return "现在";
1848
+ const mins = Math.floor(diff / 6e4);
1849
+ const hours = Math.floor(mins / 60);
1850
+ const days = Math.floor(hours / 24);
1851
+ if (days > 0) return `${days}天 ${hours % 24}时`;
1852
+ if (hours > 0) return `${hours}h ${mins % 60}m`;
1853
+ return `${mins}m`;
1854
+ }
1855
+ function formatDate(ms) {
1856
+ if (!ms || ms <= 0) return "—";
1857
+ try {
1858
+ return new Intl.DateTimeFormat(void 0, {
1859
+ dateStyle: "medium",
1860
+ timeStyle: "short"
1861
+ }).format(ms);
1862
+ } catch {
1863
+ return "—";
1864
+ }
1865
+ }
1866
+ function AntigravitySection({ onModelChange, loadModelDirectory }) {
1867
+ const [status, setStatus] = (0, react.useState)(null);
1868
+ const [loading, setLoading] = (0, react.useState)(true);
1869
+ const [busy, setBusy] = (0, react.useState)(null);
1870
+ const [error, setError] = (0, react.useState)(null);
1871
+ const [contextDrafts, setContextDrafts] = (0, react.useState)({});
1872
+ const [savingModel, setSavingModel] = (0, react.useState)(null);
1873
+ const t = zh;
1874
+ const notifyChange = (0, react.useCallback)(() => {
1875
+ onModelChange?.();
1876
+ loadModelDirectory?.();
1877
+ }, [onModelChange, loadModelDirectory]);
1878
+ const loadStatus = (0, react.useCallback)(async (quiet = false) => {
1879
+ if (!quiet) setError(null);
1880
+ try {
1881
+ const data = await fetchApi$1("/status");
1882
+ setStatus(data);
1883
+ const drafts = {};
1884
+ for (const m of data.models) {
1885
+ const val = data.contextWindowOverrides[m.id] || m.defaultContextWindow;
1886
+ drafts[m.id] = formatCapacity(val);
1887
+ }
1888
+ setContextDrafts(drafts);
1889
+ } catch (err) {
1890
+ if (!quiet) setError(err instanceof Error ? err.message : String(err));
1891
+ } finally {
1892
+ setLoading(false);
1893
+ }
1894
+ }, []);
1895
+ (0, react.useEffect)(() => {
1896
+ loadStatus();
1897
+ const refreshWhenVisible = () => {
1898
+ if (document.visibilityState === "visible") loadStatus(true);
1899
+ };
1900
+ document.addEventListener("visibilitychange", refreshWhenVisible);
1901
+ const timer = window.setInterval(refreshWhenVisible, 6e4);
1902
+ return () => {
1903
+ window.clearInterval(timer);
1904
+ document.removeEventListener("visibilitychange", refreshWhenVisible);
1905
+ };
1906
+ }, [loadStatus]);
1907
+ const handleLogin = async () => {
1908
+ try {
1909
+ setBusy("login");
1910
+ setError(null);
1911
+ const flow = await fetchApi$1("/login", { method: "POST" });
1912
+ if (flow.authUrl) window.open(flow.authUrl, "_blank");
1913
+ const pollTimer = setInterval(async () => {
1914
+ try {
1915
+ const pollStatus = await fetchApi$1("/login/status");
1916
+ if (pollStatus.status === "complete") {
1917
+ clearInterval(pollTimer);
1918
+ setBusy(null);
1919
+ await loadStatus();
1920
+ notifyChange();
1921
+ } else if (pollStatus.status === "error") {
1922
+ clearInterval(pollTimer);
1923
+ setBusy(null);
1924
+ setError(t.loginFailed);
1925
+ }
1926
+ } catch {}
1927
+ }, 1500);
1928
+ setTimeout(() => {
1929
+ clearInterval(pollTimer);
1930
+ setBusy(null);
1931
+ }, 300 * 1e3);
1932
+ } catch (err) {
1933
+ setBusy(null);
1934
+ setError(err instanceof Error ? err.message : String(err));
1935
+ }
1936
+ };
1937
+ const handleRefreshQuota = async () => {
1938
+ try {
1939
+ setBusy("quota");
1940
+ setError(null);
1941
+ const updated = await fetchApi$1("/quota", { method: "POST" });
1942
+ setStatus(updated);
1943
+ } catch (err) {
1944
+ setError(err instanceof Error ? err.message : String(err));
1945
+ } finally {
1946
+ setBusy(null);
1947
+ }
1948
+ };
1949
+ const handleLogout = async () => {
1950
+ try {
1951
+ setBusy("logout");
1952
+ setError(null);
1953
+ const updated = await fetchApi$1("/logout", { method: "POST" });
1954
+ setStatus(updated);
1955
+ notifyChange();
1956
+ } catch (err) {
1957
+ setError(err instanceof Error ? err.message : String(err));
1958
+ } finally {
1959
+ setBusy(null);
1960
+ }
1961
+ };
1962
+ const toggleModel = async (modelId, checked) => {
1963
+ if (!status) return;
1964
+ const currentEnabled = status.models.filter((m) => m.enabled).map((m) => m.id);
1965
+ const nextEnabled = checked ? [.../* @__PURE__ */ new Set([...currentEnabled, modelId])] : currentEnabled.filter((id) => id !== modelId);
1966
+ if (nextEnabled.length === 0) return;
1967
+ try {
1968
+ const updated = await fetchApi$1("/models", {
1969
+ method: "POST",
1970
+ body: JSON.stringify({ enabledModelIds: nextEnabled })
1971
+ });
1972
+ setStatus(updated);
1973
+ notifyChange();
1974
+ } catch (err) {
1975
+ setError(err instanceof Error ? err.message : String(err));
1976
+ }
1977
+ };
1978
+ const setAllModels = async (selectAll) => {
1979
+ if (!status) return;
1980
+ const nextEnabled = selectAll ? status.models.map((m) => m.id) : [status.models[0].id];
1981
+ try {
1982
+ const updated = await fetchApi$1("/models", {
1983
+ method: "POST",
1984
+ body: JSON.stringify({ enabledModelIds: nextEnabled })
1985
+ });
1986
+ setStatus(updated);
1987
+ notifyChange();
1988
+ } catch (err) {
1989
+ setError(err instanceof Error ? err.message : String(err));
1990
+ }
1991
+ };
1992
+ const handleUpdateEffort = async (effort) => {
1993
+ try {
1994
+ const updated = await fetchApi$1("/settings", {
1995
+ method: "POST",
1996
+ body: JSON.stringify({ defaultReasoningEffort: effort })
1997
+ });
1998
+ setStatus(updated);
1999
+ notifyChange();
2000
+ } catch (err) {
2001
+ setError(err instanceof Error ? err.message : String(err));
2002
+ }
2003
+ };
2004
+ const handleSaveContextWindow = async (modelId) => {
2005
+ const raw = contextDrafts[modelId] || "";
2006
+ const parsed = parsePositiveCapacity(raw);
2007
+ if (parsed === null) {
2008
+ setError(`无效的上下文容量值: ${raw}`);
2009
+ return;
2010
+ }
2011
+ try {
2012
+ setSavingModel(modelId);
2013
+ setError(null);
2014
+ const updated = await fetchApi$1("/settings", {
2015
+ method: "POST",
2016
+ body: JSON.stringify({ contextWindowOverrides: { [modelId]: parsed } })
2017
+ });
2018
+ setStatus(updated);
2019
+ setContextDrafts((prev) => ({
2020
+ ...prev,
2021
+ [modelId]: formatCapacity(parsed)
2022
+ }));
2023
+ notifyChange();
2024
+ } catch (err) {
2025
+ setError(err instanceof Error ? err.message : String(err));
2026
+ } finally {
2027
+ setSavingModel(null);
2028
+ }
2029
+ };
2030
+ if (loading) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2031
+ className: "dsha-page",
2032
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2033
+ className: "dsha-empty",
2034
+ children: t.loading
2035
+ })
2036
+ });
2037
+ const quota = status?.quota;
2038
+ const groups = quota?.groups || [];
2039
+ const visibleCount = status?.models.filter((m) => m.enabled).length || 0;
2040
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2041
+ className: "dsha-page",
2042
+ children: [
2043
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2044
+ className: "dsha-group",
2045
+ children: [
2046
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2047
+ className: "dsha-grouphead",
2048
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: status?.authenticated ? t.account : t.signedOut })
2049
+ }),
2050
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2051
+ className: "dsha-row",
2052
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2053
+ className: "dsha-label",
2054
+ children: status?.authenticated ? t.signedIn : t.signedOut
2055
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2056
+ className: "dsha-value",
2057
+ children: status?.email || "—"
2058
+ })]
2059
+ }),
2060
+ status?.authenticated && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
2061
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2062
+ className: "dsha-row",
2063
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2064
+ className: "dsha-label",
2065
+ children: t.plan
2066
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2067
+ className: "dsha-value",
2068
+ children: quota?.planLabel || "Google AI Ultra"
2069
+ })]
2070
+ }),
2071
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2072
+ className: "dsha-row",
2073
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2074
+ className: "dsha-label",
2075
+ children: t.accountId
2076
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2077
+ className: "dsha-value",
2078
+ children: status.projectId || "antigravity-default"
2079
+ })]
2080
+ }),
2081
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2082
+ className: "dsha-row",
2083
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2084
+ className: "dsha-label",
2085
+ children: t.expires
2086
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2087
+ className: "dsha-value",
2088
+ children: formatDate(Date.now() + 864e5 * 30)
2089
+ })]
2090
+ })
2091
+ ] }),
2092
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2093
+ className: "dsha-row",
2094
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2095
+ className: "dsha-label",
2096
+ children: t.storage
2097
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2098
+ className: "dsha-value",
2099
+ children: "本地安全存储 (JSON)"
2100
+ })]
2101
+ }),
2102
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2103
+ className: "dsha-notice",
2104
+ children: t.storageNotice
2105
+ }),
2106
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2107
+ className: "dsha-actions",
2108
+ children: !status?.authenticated ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2109
+ className: "dsha-btn dsha-btn-primary",
2110
+ disabled: busy !== null,
2111
+ onClick: handleLogin,
2112
+ children: busy === "login" ? t.signingIn : t.signIn
2113
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
2114
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2115
+ className: "dsha-btn dsha-btn-primary",
2116
+ disabled: busy !== null,
2117
+ onClick: handleLogin,
2118
+ children: t.signInAgain
2119
+ }),
2120
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2121
+ className: "dsha-btn",
2122
+ disabled: busy !== null,
2123
+ onClick: handleRefreshQuota,
2124
+ children: busy === "quota" ? t.refreshingQuota : t.refreshToken
2125
+ }),
2126
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2127
+ className: "dsha-btn",
2128
+ disabled: busy !== null,
2129
+ onClick: handleLogout,
2130
+ children: t.signOut
2131
+ })
2132
+ ] })
2133
+ })
2134
+ ]
2135
+ }),
2136
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2137
+ className: "dsha-group",
2138
+ children: [
2139
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2140
+ className: "dsha-grouphead",
2141
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t.connection })
2142
+ }),
2143
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2144
+ className: "dsha-row",
2145
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2146
+ className: "dsha-label",
2147
+ children: t.provider
2148
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2149
+ className: "dsha-value",
2150
+ children: t.providerValue
2151
+ })]
2152
+ }),
2153
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2154
+ className: "dsha-row",
2155
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2156
+ className: "dsha-label",
2157
+ children: t.connectionState
2158
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2159
+ className: "dsha-value",
2160
+ children: status?.authenticated ? t.connected : t.untested
2161
+ })]
2162
+ }),
2163
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2164
+ className: "dsha-muted dsha-models-hint",
2165
+ children: t.modelsHint
2166
+ }),
2167
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2168
+ className: "dsha-models",
2169
+ "aria-label": "Antigravity Models",
2170
+ children: status?.models.map((model) => {
2171
+ const checked = model.enabled;
2172
+ const lastVisible = checked && visibleCount === 1;
2173
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2174
+ title: model.id,
2175
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2176
+ type: "checkbox",
2177
+ checked,
2178
+ disabled: busy !== null || lastVisible,
2179
+ onChange: (e) => void toggleModel(model.id, e.currentTarget.checked)
2180
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: model.name })]
2181
+ }, model.id);
2182
+ })
2183
+ }),
2184
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2185
+ className: "dsha-actions",
2186
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2187
+ className: "dsha-btn",
2188
+ disabled: busy !== null,
2189
+ onClick: () => setAllModels(true),
2190
+ children: t.selectAll
2191
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2192
+ className: "dsha-btn",
2193
+ disabled: busy !== null,
2194
+ onClick: () => setAllModels(false),
2195
+ children: t.unselectAll
2196
+ })]
2197
+ })
2198
+ ]
2199
+ }),
2200
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2201
+ className: "dsha-group",
2202
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2203
+ className: "dsha-grouphead",
2204
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t.enhanced })
2205
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2206
+ className: "dsha-pref-row",
2207
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t.defaultReasoningEffort }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2208
+ className: "dsha-muted",
2209
+ children: t.defaultReasoningEffortHint
2210
+ })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
2211
+ className: "dsha-select",
2212
+ "aria-label": t.defaultReasoningEffort,
2213
+ value: status?.defaultReasoningEffort ?? "",
2214
+ disabled: busy !== null,
2215
+ onChange: (e) => {
2216
+ const val = e.currentTarget.value;
2217
+ handleUpdateEffort(val === "" ? null : val);
2218
+ },
2219
+ children: [
2220
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2221
+ value: "",
2222
+ children: t.defaultEffortAuto
2223
+ }),
2224
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2225
+ value: "low",
2226
+ children: "Low"
2227
+ }),
2228
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2229
+ value: "medium",
2230
+ children: "Medium"
2231
+ }),
2232
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2233
+ value: "high",
2234
+ children: "High"
2235
+ })
2236
+ ]
2237
+ })]
2238
+ })]
2239
+ }),
2240
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2241
+ className: "dsha-group",
2242
+ children: [
2243
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2244
+ className: "dsha-grouphead",
2245
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t.contextWindowSection })
2246
+ }),
2247
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2248
+ className: "dsha-muted",
2249
+ children: t.contextWindowHint
2250
+ }),
2251
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2252
+ className: "dsha-context-settings",
2253
+ children: status?.models.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2254
+ className: "dsha-context-row",
2255
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: model.name }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2256
+ className: "dsha-capacity-control",
2257
+ children: [
2258
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2259
+ type: "text",
2260
+ value: contextDrafts[model.id] ?? "",
2261
+ onChange: (e) => setContextDrafts({
2262
+ ...contextDrafts,
2263
+ [model.id]: e.target.value
2264
+ }),
2265
+ onKeyDown: (e) => {
2266
+ if (e.key === "Enter") handleSaveContextWindow(model.id);
2267
+ }
2268
+ }),
2269
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: t.tokens }),
2270
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2271
+ type: "button",
2272
+ className: "dsha-context-save",
2273
+ disabled: savingModel === model.id,
2274
+ onClick: () => void handleSaveContextWindow(model.id),
2275
+ children: savingModel === model.id ? t.saving : t.save
2276
+ })
2277
+ ]
2278
+ })]
2279
+ }, model.id))
2280
+ })
2281
+ ]
2282
+ }),
2283
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2284
+ className: "dsha-group",
2285
+ children: [
2286
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2287
+ className: "dsha-grouphead",
2288
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t.quotaSection }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2289
+ className: "dsha-btn",
2290
+ disabled: busy !== null || !status?.authenticated,
2291
+ onClick: handleRefreshQuota,
2292
+ children: busy === "quota" ? t.refreshingQuota : t.refreshQuota
2293
+ })]
2294
+ }),
2295
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2296
+ className: "dsha-muted",
2297
+ children: t.quotaDesc
2298
+ }),
2299
+ !status?.authenticated ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2300
+ className: "dsha-empty",
2301
+ children: t.signedOut
2302
+ }) : groups.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2303
+ className: "dsha-empty",
2304
+ children: "暂无配额数据,点击右上角刷新用量。"
2305
+ }) : groups.map((group, gIdx) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2306
+ className: "dsha-quota-card",
2307
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2308
+ className: "dsha-quota-title",
2309
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: group.displayName }), group.description && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: group.description })]
2310
+ }), group.buckets.map((bucket, bIdx) => {
2311
+ const isCyan = /claude|gpt|3p/i.test(group.displayName);
2312
+ const pct = Math.round(bucket.remainingFraction * 100);
2313
+ const resetText = formatResetTime(bucket.resetTime);
2314
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2315
+ className: "dsha-meter-wrap",
2316
+ children: [
2317
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2318
+ className: "dsha-meter-label",
2319
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: bucket.displayName }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("strong", { children: [pct, "% 剩余"] })]
2320
+ }),
2321
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2322
+ className: `dsha-meter ${isCyan ? "dsha-meter-cyan" : "dsha-meter-green"}`,
2323
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: { width: `${pct}%` } })
2324
+ }),
2325
+ resetText && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2326
+ className: "dsha-meter-meta",
2327
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["重置: ", resetText] })
2328
+ })
2329
+ ]
2330
+ }, bIdx);
2331
+ })]
2332
+ }, gIdx)),
2333
+ quota?.fetchedAt && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2334
+ className: "dsha-timestamp",
2335
+ children: ["更新时间: ", new Date(quota.fetchedAt).toLocaleString()]
2336
+ })
2337
+ ]
2338
+ }),
2339
+ error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2340
+ className: "dsha-error",
2341
+ children: error
2342
+ })
2343
+ ]
2344
+ });
2345
+ }
2346
+ //#endregion
2347
+ //#region src/client/antigravity/styles.ts
2348
+ const STYLE_ID = "dsh-antigravity-settings-style";
2349
+ function installAntigravityStyles() {
2350
+ if (typeof document === "undefined" || document.getElementById("dsh-antigravity-settings-style")) return;
2351
+ const style = document.createElement("style");
2352
+ style.id = STYLE_ID;
2353
+ style.textContent = `
2354
+ .dsha-page{box-sizing:border-box;color:var(--dsw-alias-label-primary);display:flex;flex-direction:column;gap:20px;max-width:780px;min-width:0;padding:2px 0 30px}
2355
+ .dsha-page *{box-sizing:border-box}
2356
+ .dsha-group{border-top:0.5px solid var(--dsw-alias-border-l2);min-width:0}
2357
+ .dsha-group:first-of-type{border-top:0}
2358
+ .dsha-grouphead{align-items:center;display:flex;gap:12px;justify-content:space-between;min-height:48px}
2359
+ .dsha-grouphead h3{font-size:14px;font-weight:650;margin:0;color:var(--dsw-alias-label-primary)}
2360
+ .dsha-row{align-items:center;border-bottom:0.5px solid var(--dsw-alias-border-l2);display:flex;gap:20px;justify-content:space-between;min-height:44px;padding:8px 0}
2361
+ .dsha-label{color:var(--dsw-alias-label-secondary);font-size:13px;flex:0 0 auto}
2362
+ .dsha-value{font-size:13px;min-width:0;overflow-wrap:anywhere;text-align:right;color:var(--dsw-alias-label-primary)}
2363
+ .dsha-actions{display:flex;flex-wrap:wrap;gap:8px;justify-content:flex-end;padding-top:12px}
2364
+ .dsha-btn{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:999px;corner-shape:round;color:var(--dsw-alias-label-primary);cursor:pointer;font:inherit;font-size:13px;line-height:1;padding:8px 13px;white-space:nowrap}
2365
+ .dsha-btn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}
2366
+ .dsha-btn:disabled{cursor:default;opacity:.5}
2367
+ .dsha-btn-primary{background:var(--dsw-alias-button-info-fill,#397ee8);border-color:transparent;color:var(--dsw-alias-button-info-label,#fff)}
2368
+ .dsha-btn-primary:hover:not(:disabled){opacity:.9}
2369
+ .dsha-notice{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.55;margin:12px 0 0;padding:10px 12px}
2370
+ .dsha-muted{color:var(--dsw-alias-label-secondary);font-size:13px;line-height:1.55;margin:0}
2371
+ .dsha-models-hint{padding-top:10px}
2372
+ .dsha-models{display:flex;flex-wrap:wrap;gap:7px;padding-top:8px}
2373
+ .dsha-models label{cursor:pointer;display:block;position:relative}
2374
+ .dsha-models input{position:absolute;opacity:0;pointer-events:none}
2375
+ .dsha-models span{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:6px;color:var(--dsw-alias-label-secondary);display:block;font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:11px;padding:5px 8px;transition:all .15s ease}
2376
+ .dsha-models input:checked+span{background:color-mix(in srgb,var(--dsw-alias-button-info-fill,#397ee8) 14%,var(--dsw-alias-bg-layer-2));border-color:color-mix(in srgb,var(--dsw-alias-button-info-fill,#397ee8) 55%,var(--dsw-alias-border-l2));color:var(--dsw-alias-label-primary);font-weight:600}
2377
+ .dsha-models input:disabled+span{cursor:default;opacity:.5}
2378
+ .dsha-pref-row{align-items:center;border-bottom:0.5px solid var(--dsw-alias-border-l2);display:flex;gap:16px;justify-content:space-between;min-height:58px;padding:10px 0}
2379
+ .dsha-pref-row strong{display:block;font-size:13px;font-weight:600;line-height:1.35;color:var(--dsw-alias-label-primary)}
2380
+ .dsha-select{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-primary);font:inherit;font-size:12px;padding:7px 9px;max-width:220px;min-width:160px}
2381
+ .dsha-context-settings{border-bottom:0.5px solid var(--dsw-alias-border-l2);display:grid;gap:10px;padding:12px 0}
2382
+ .dsha-context-settings>div>strong{font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary)}
2383
+ .dsha-context-row{align-items:center;display:flex;font-size:12px;gap:14px;justify-content:space-between}
2384
+ .dsha-capacity-control{align-items:center;display:flex;gap:7px}
2385
+ .dsha-capacity-control input{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-primary);font:inherit;font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:12px;padding:7px 9px;text-align:right;width:90px}
2386
+ .dsha-capacity-control small{color:var(--dsw-alias-label-tertiary);font-size:11px;width:42px}
2387
+ .dsha-context-save{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-primary);cursor:pointer;font:inherit;font-size:12px;line-height:1;padding:8px 10px;white-space:nowrap}
2388
+ .dsha-context-save:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}
2389
+ .dsha-context-save:disabled{cursor:default;opacity:.45}
2390
+ .dsha-quota-card{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:8px;margin-top:12px;padding:12px}
2391
+ .dsha-quota-title{align-items:center;display:flex;font-size:13px;gap:8px;justify-content:space-between;color:var(--dsw-alias-label-primary);font-weight:650}
2392
+ .dsha-quota-title span{color:var(--dsw-alias-label-tertiary);font-size:11px;text-transform:uppercase}
2393
+ .dsha-meter-wrap{margin-top:13px}
2394
+ .dsha-meter-label,.dsha-meter-meta{display:flex;gap:10px;justify-content:space-between}
2395
+ .dsha-meter-label{font-size:12px;margin-bottom:6px;color:var(--dsw-alias-label-primary)}
2396
+ .dsha-meter-meta{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:1.45;margin-top:6px}
2397
+ .dsha-meter{background:var(--dsw-alias-bg-layer-1,rgba(127,127,127,.15));border-radius:999px;corner-shape:round;height:7px;overflow:hidden;width:100%}
2398
+ .dsha-meter>span{background:var(--dsw-alias-button-info-fill,#397ee8);border-radius:inherit;display:block;height:100%;max-width:100%;min-width:0;transition:width .25s ease}
2399
+ .dsha-meter-green>span{background:var(--dsw-alias-label-success,#10b981)}
2400
+ .dsha-meter-cyan>span{background:#06b6d4}
2401
+ .dsha-timestamp{color:var(--dsw-alias-label-tertiary);font-size:11px;margin:10px 0 0;text-align:right}
2402
+ .dsha-mini-btn{border:0;background:transparent;color:var(--dsw-alias-label-link,#397ee8);font-size:12px;line-height:18px;cursor:pointer;padding:0;margin-left:8px}
2403
+ .dsha-mini-btn:hover{text-decoration:underline}
2404
+ .dsha-empty{border:0.5px dashed var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-tertiary);font-size:12px;margin:12px 0 0;padding:16px;text-align:center}
2405
+ .dsha-error{color:var(--dsw-alias-label-danger,#d94b4b);font-size:12px;margin:8px 0 0}
2406
+ .dsha-composer-quota{align-items:center;background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:999px;corner-shape:round;color:var(--dsw-alias-label-secondary);cursor:pointer;display:inline-flex;font-size:11px;gap:6px;height:28px;line-height:1;max-width:160px;padding:0 9px;white-space:nowrap;user-select:none;transition:all .15s ease}
2407
+ .dsha-composer-quota:hover{background:var(--dsw-alias-interactive-bg-hover);border-color:var(--dsw-alias-border-l1,var(--dsw-alias-border-l2))}
2408
+ .dsha-composer-quota strong{color:var(--dsw-alias-label-primary);font-size:11px;font-weight:650}
2409
+ .dsha-composer-quota[data-level=warning] strong{color:var(--dsw-alias-label-warning,#d58a24)}
2410
+ .dsha-composer-quota[data-level=danger] strong{color:var(--dsw-alias-label-danger,#d94b4b)}
2411
+ `;
2412
+ document.head.append(style);
2413
+ }
2414
+ //#endregion
2415
+ //#region src/client/antigravity/AntigravityComposerQuota.tsx
2416
+ const API = "/antigravity/api";
2417
+ async function fetchApi(path, options) {
2418
+ const res = await fetch(`${API}${path}`, {
2419
+ ...options,
2420
+ headers: {
2421
+ "Content-Type": "application/json",
2422
+ ...options?.headers
2423
+ }
2424
+ });
2425
+ const json = await res.json();
2426
+ if (!res.ok || !json.ok) throw new Error(json.error || `HTTP ${res.status}`);
2427
+ return json.value;
2428
+ }
2429
+ function selectQuotaBucketForModel(groups, modelId) {
2430
+ if (!groups || groups.length === 0) return null;
2431
+ const isClaudeOrGpt = modelId ? /claude|gpt/i.test(modelId) : false;
2432
+ const targetGroup = groups.find((g) => {
2433
+ const match = /claude|gpt|3p/i.test(g.displayName);
2434
+ return isClaudeOrGpt ? match : !match;
2435
+ }) || groups[0];
2436
+ if (!targetGroup || !targetGroup.buckets || targetGroup.buckets.length === 0) return null;
2437
+ const shortBucket = targetGroup.buckets.find((b) => /5\s*小时|hour|5h/i.test(b.displayName)) || targetGroup.buckets[0];
2438
+ return {
2439
+ groupName: targetGroup.displayName,
2440
+ bucketName: shortBucket.displayName,
2441
+ remainingPercent: Math.round(shortBucket.remainingFraction * 100),
2442
+ resetTime: shortBucket.resetTime
2443
+ };
2444
+ }
2445
+ function formatResetCountdown(resetTime) {
2446
+ if (!resetTime) return "";
2447
+ try {
2448
+ const diff = new Date(resetTime).getTime() - Date.now();
2449
+ if (diff <= 0) return "即将重置";
2450
+ const h = Math.floor(diff / 36e5);
2451
+ const m = Math.floor(diff % 36e5 / 6e4);
2452
+ if (h > 0) return `${h}小时${m}分后重置`;
2453
+ return `${m}分钟后重置`;
2454
+ } catch {
2455
+ return "";
2456
+ }
2457
+ }
2458
+ function AntigravityComposerQuota({ directory, loadModelDirectory }) {
2459
+ const modelState = useStore(directory);
2460
+ const [status, setStatus] = (0, react.useState)(null);
2461
+ const [loading, setLoading] = (0, react.useState)(false);
2462
+ const mountedRef = (0, react.useRef)(false);
2463
+ const selected = modelState.current;
2464
+ const isAntigravity = selected?.provider === "antigravity";
2465
+ (0, react.useEffect)(() => {
2466
+ loadModelDirectory();
2467
+ }, [loadModelDirectory]);
2468
+ (0, react.useEffect)(() => {
2469
+ mountedRef.current = true;
2470
+ return () => {
2471
+ mountedRef.current = false;
2472
+ };
2473
+ }, []);
2474
+ const fetchStatus = (0, react.useCallback)(async (refresh = false) => {
2475
+ if (!mountedRef.current) return;
2476
+ setLoading(true);
2477
+ try {
2478
+ const data = refresh ? await fetchApi("/quota", { method: "POST" }) : await fetchApi("/status");
2479
+ if (mountedRef.current) setStatus(data);
2480
+ } catch {} finally {
2481
+ if (mountedRef.current) setLoading(false);
2482
+ }
2483
+ }, []);
2484
+ (0, react.useEffect)(() => {
2485
+ if (!isAntigravity) return;
2486
+ fetchStatus(false);
2487
+ const timer = window.setInterval(() => {
2488
+ if (document.visibilityState === "visible") fetchStatus(false);
2489
+ }, 6e4);
2490
+ return () => {
2491
+ window.clearInterval(timer);
2492
+ };
2493
+ }, [fetchStatus, isAntigravity]);
2494
+ const quotaInfo = (0, react.useMemo)(() => {
2495
+ return selectQuotaBucketForModel(status?.quota?.groups || [], selected?.model);
2496
+ }, [status?.quota?.groups, selected?.model]);
2497
+ if (!isAntigravity || !status?.authenticated) return null;
2498
+ const level = quotaInfo === null ? "normal" : quotaInfo.remainingPercent <= 5 ? "danger" : quotaInfo.remainingPercent <= 20 ? "warning" : "normal";
2499
+ const countdown = quotaInfo?.resetTime ? formatResetCountdown(quotaInfo.resetTime) : "";
2500
+ const tooltip = quotaInfo ? `[Antigravity] ${quotaInfo.groupName} - ${quotaInfo.bucketName}: 剩余 ${quotaInfo.remainingPercent}%${countdown ? " (" + countdown + ")" : ""},点击刷新` : "点击刷新 Antigravity 配额";
2501
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2502
+ className: "dsha-composer-quota",
2503
+ "data-level": level,
2504
+ title: tooltip,
2505
+ "aria-label": tooltip,
2506
+ onClick: () => void fetchStatus(true),
2507
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2508
+ className: "dsha-composer-quota-label",
2509
+ children: "额度"
2510
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
2511
+ className: "dsha-composer-quota-val",
2512
+ children: loading ? "…" : quotaInfo === null ? "—" : `${quotaInfo.remainingPercent}%`
2513
+ })]
2514
+ });
2515
+ }
2516
+ function useStore(store) {
2517
+ return (0, react.useSyncExternalStore)((listener) => store.subscribe(listener), () => store.getSnapshot(), () => store.getSnapshot());
2518
+ }
2519
+ //#endregion
2520
+ //#region src/client/mermaid/sanitize.ts
2521
+ const STRIP_ELEMENTS = /* @__PURE__ */ new Set([
2522
+ "foreignobject",
2523
+ "script",
2524
+ "img",
2525
+ "iframe",
2526
+ "object",
2527
+ "embed",
2528
+ "video",
2529
+ "audio",
2530
+ "input",
2531
+ "button",
2532
+ "form",
2533
+ "link",
2534
+ "meta",
2535
+ "base"
2536
+ ]);
2537
+ function sanitizeSvg(svg) {
2538
+ if (typeof DOMParser === "undefined" || typeof XMLSerializer === "undefined") return "";
2539
+ let doc;
2540
+ try {
2541
+ doc = new DOMParser().parseFromString(svg, "image/svg+xml");
2542
+ } catch {
2543
+ return "";
2544
+ }
2545
+ if (doc.querySelector("parsererror") !== null) return "";
2546
+ if (doc.documentElement === null || doc.documentElement.localName !== "svg") return "";
2547
+ doc.querySelectorAll("*").forEach((node) => {
2548
+ if (STRIP_ELEMENTS.has(node.localName.toLowerCase())) {
2549
+ node.remove();
2550
+ return;
2551
+ }
2552
+ for (const attribute of [...node.attributes]) {
2553
+ const name = attribute.name;
2554
+ const normalized = name.toLowerCase();
2555
+ if (normalized.startsWith("@") || normalized.startsWith("on")) {
2556
+ node.removeAttribute(name);
2557
+ continue;
2558
+ }
2559
+ if (normalized === "href" || normalized === "xlink:href") node.removeAttribute(name);
2560
+ }
2561
+ });
2562
+ return new XMLSerializer().serializeToString(doc.documentElement);
2563
+ }
2564
+ //#endregion
2565
+ //#region src/client/mermaid/styles.ts
2566
+ const MERMAID_STYLE_ID = "dsh-mermaid-renderer-style";
2567
+ function installMermaidStyles() {
2568
+ if (typeof document === "undefined" || document.getElementById("dsh-mermaid-renderer-style")) return;
2569
+ const style = document.createElement("style");
2570
+ style.id = MERMAID_STYLE_ID;
2571
+ style.textContent = `
2572
+ .dsh-mermaid-wrapper{margin:12px 0;border:1px solid var(--dsw-alias-border-l2,rgba(255,255,255,0.12));border-radius:8px;overflow:hidden;background:var(--dsw-alias-bg-layer-2,#1a1a1a);box-shadow:0 2px 8px rgba(0,0,0,0.15)}
2573
+ .dsh-mermaid-header{display:flex;align-items:center;justify-content:space-between;padding:6px 12px;background:var(--dsw-alias-bg-layer-1,rgba(0,0,0,0.25));border-bottom:1px solid var(--dsw-alias-border-l2,rgba(255,255,255,0.08));font-size:12px;color:var(--dsw-alias-label-secondary,#888)}
2574
+ .dsh-mermaid-title{font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:11px;text-transform:uppercase;letter-spacing:0.5px;color:var(--dsw-alias-label-tertiary,#aaa);font-weight:600}
2575
+ .dsh-mermaid-controls{display:flex;gap:8px;align-items:center}
2576
+ .dsh-mermaid-btn{background:transparent;border:1px solid var(--dsw-alias-border-l2,rgba(255,255,255,0.15));border-radius:4px;color:var(--dsw-alias-label-secondary,#ccc);padding:2px 8px;font-size:11px;cursor:pointer;line-height:18px;transition:all .15s}
2577
+ .dsh-mermaid-btn:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(255,255,255,0.08));color:var(--dsw-alias-label-primary,#fff);border-color:var(--dsw-alias-border-l1,rgba(255,255,255,0.3))}
2578
+ .dsh-mermaid-chart{padding:18px;overflow-x:auto;display:flex;justify-content:center;align-items:center;min-height:100px;background:var(--dsw-alias-bg-layer-2,#181818);cursor:zoom-in}
2579
+ .dsh-mermaid-chart svg{max-width:100%;height:auto;display:block;margin:0 auto}
2580
+ .dsh-mermaid-source{display:none;margin:0;padding:12px;background:var(--dsw-alias-bg-layer-1,#121212);border-top:1px solid var(--dsw-alias-border-l2,rgba(255,255,255,0.08));font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:12px;line-height:1.5;overflow-x:auto;white-space:pre-wrap;word-break:break-all}
2581
+ .dsh-mermaid-source.is-visible{display:block}
2582
+
2583
+ /* 全屏缩放预览模态框 */
2584
+ .dsh-mermaid-modal-overlay{position:fixed;inset:0;background:rgba(0,0,0,0.8);backdrop-filter:blur(4px);z-index:99999;display:flex;flex-direction:column;justify-content:center;align-items:center}
2585
+ .dsh-mermaid-modal-toolbar{position:absolute;top:20px;right:24px;display:flex;gap:8px;background:rgba(20,20,20,0.85);border:1px solid rgba(255,255,255,0.15);border-radius:6px;padding:4px;box-shadow:0 8px 24px rgba(0,0,0,0.5)}
2586
+ .dsh-mermaid-modal-btn{background:transparent;border:0;color:#fff;font-size:14px;padding:6px 12px;cursor:pointer;border-radius:4px}
2587
+ .dsh-mermaid-modal-btn:hover{background:rgba(255,255,255,0.15)}
2588
+ .dsh-mermaid-modal-stage{flex:1;width:100%;height:100%;overflow:auto;display:flex;justify-content:center;align-items:center;padding:40px}
2589
+ .dsh-mermaid-modal-stage svg{max-width:90vw;max-height:85vh;height:auto}
2590
+ `;
2591
+ document.head.append(style);
2592
+ }
2593
+ //#endregion
2594
+ //#region src/client/mermaid/renderer.ts
2595
+ let mermaidSeq = 0;
2596
+ let mermaidPromise = null;
2597
+ const processedHosts = /* @__PURE__ */ new WeakSet();
2598
+ function loadMermaid() {
2599
+ if (typeof window === "undefined") return Promise.resolve(null);
2600
+ const existing = window.mermaid;
2601
+ if (existing) return Promise.resolve(existing);
2602
+ if (mermaidPromise) return mermaidPromise;
2603
+ mermaidPromise = new Promise((resolve) => {
2604
+ const current = window.mermaid;
2605
+ if (current) {
2606
+ resolve(current);
2607
+ return;
2608
+ }
2609
+ const script = document.createElement("script");
2610
+ script.src = "/api/dsh-chatgpt-subscription/mermaid.min.js";
2611
+ script.async = true;
2612
+ script.onload = () => {
2613
+ resolve(window.mermaid || null);
2614
+ };
2615
+ script.onerror = () => {
2616
+ const cdn = document.createElement("script");
2617
+ cdn.src = "https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js";
2618
+ cdn.async = true;
2619
+ cdn.onload = () => resolve(window.mermaid || null);
2620
+ cdn.onerror = () => resolve(null);
2621
+ document.head.appendChild(cdn);
2622
+ };
2623
+ document.head.appendChild(script);
2624
+ });
2625
+ return mermaidPromise;
2626
+ }
2627
+ function isDarkTheme() {
2628
+ if (typeof document === "undefined") return false;
2629
+ const root = document.documentElement;
2630
+ return root.classList.contains("dark") || root.getAttribute("data-theme") === "dark" || root.getAttribute("data-color-mode") === "dark" || window.matchMedia("(prefers-color-scheme: dark)").matches;
2631
+ }
2632
+ async function initMermaidConfig() {
2633
+ const m = await loadMermaid();
2634
+ if (!m) return null;
2635
+ try {
2636
+ m.initialize({
2637
+ startOnLoad: false,
2638
+ securityLevel: "strict",
2639
+ htmlLabels: false,
2640
+ theme: isDarkTheme() ? "dark" : "default",
2641
+ fontFamily: "ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif"
2642
+ });
2643
+ } catch {}
2644
+ return m;
2645
+ }
2646
+ function openZoomModal(svgHtml) {
2647
+ const overlay = document.createElement("div");
2648
+ overlay.className = "dsh-mermaid-modal-overlay";
2649
+ let scale = 1;
2650
+ overlay.innerHTML = `
2651
+ <div class="dsh-mermaid-modal-toolbar">
2652
+ <button type="button" class="dsh-mermaid-modal-btn" data-action="zoom-out" title="缩小">− 缩小</button>
2653
+ <button type="button" class="dsh-mermaid-modal-btn" data-action="zoom-in" title="放大">+ 放大</button>
2654
+ <button type="button" class="dsh-mermaid-modal-btn" data-action="reset" title="重置">⟳ 重置</button>
2655
+ <button type="button" class="dsh-mermaid-modal-btn" data-action="close" title="关闭">✕ 关闭</button>
2656
+ </div>
2657
+ <div class="dsh-mermaid-modal-stage">
2658
+ <div class="dsh-mermaid-zoom-container">${svgHtml}</div>
2659
+ </div>
2660
+ `;
2661
+ const stage = overlay.querySelector(".dsh-mermaid-zoom-container");
2662
+ const updateTransform = () => {
2663
+ if (stage) stage.style.transform = `scale(${scale})`;
2664
+ };
2665
+ overlay.addEventListener("click", (e) => {
2666
+ const target = e.target;
2667
+ const action = target?.getAttribute("data-action");
2668
+ if (action === "close" || target === overlay) {
2669
+ overlay.remove();
2670
+ document.removeEventListener("keydown", onKeyDown);
2671
+ } else if (action === "zoom-in") {
2672
+ scale = Math.min(5, scale * 1.25);
2673
+ updateTransform();
2674
+ } else if (action === "zoom-out") {
2675
+ scale = Math.max(.2, scale / 1.25);
2676
+ updateTransform();
2677
+ } else if (action === "reset") {
2678
+ scale = 1;
2679
+ updateTransform();
2680
+ }
2681
+ });
2682
+ const onKeyDown = (e) => {
2683
+ if (e.key === "Escape") {
2684
+ overlay.remove();
2685
+ document.removeEventListener("keydown", onKeyDown);
2686
+ }
2687
+ };
2688
+ document.addEventListener("keydown", onKeyDown);
2689
+ document.body.appendChild(overlay);
2690
+ }
2691
+ function findMermaidCandidate(codeEl) {
2692
+ if (codeEl.closest(".dsh-mermaid-wrapper")) return null;
2693
+ const host = codeEl.closest(".md-code-block") || codeEl.closest("pre") || codeEl;
2694
+ if (processedHosts.has(host) || host.getAttribute("data-dsh-mermaid-status")) return null;
2695
+ if (host.previousElementSibling?.classList.contains("dsh-mermaid-wrapper")) return null;
2696
+ const codeText = (codeEl.textContent || "").trim();
2697
+ if (!codeText || codeText.length < 5) return null;
2698
+ const hasMermaidClass = [...codeEl.classList].some((c) => c.toLowerCase().includes("mermaid"));
2699
+ const topText = host.firstElementChild?.textContent?.trim().toLowerCase();
2700
+ const isHeaderMermaid = topText === "mermaid" || topText?.startsWith("mermaid ");
2701
+ const isGrammarStart = /^(flowchart|graph|sequenceDiagram|classDiagram|stateDiagram|erDiagram|gantt|pie|gitGraph|mindmap|timeline)\b/i.test(codeText);
2702
+ if (hasMermaidClass || isHeaderMermaid || isGrammarStart) return {
2703
+ host,
2704
+ code: codeText
2705
+ };
2706
+ return null;
2707
+ }
2708
+ async function renderMermaidHost(host, code) {
2709
+ processedHosts.add(host);
2710
+ host.setAttribute("data-dsh-mermaid-status", "processing");
2711
+ const m = await initMermaidConfig();
2712
+ if (!m) {
2713
+ processedHosts.delete(host);
2714
+ host.removeAttribute("data-dsh-mermaid-status");
2715
+ return;
2716
+ }
2717
+ const renderId = `dsh-mermaid-${++mermaidSeq}`;
2718
+ try {
2719
+ const { svg } = await m.render(renderId, code);
2720
+ const cleanSvg = sanitizeSvg(svg);
2721
+ if (!cleanSvg) {
2722
+ processedHosts.delete(host);
2723
+ host.removeAttribute("data-dsh-mermaid-status");
2724
+ return;
2725
+ }
2726
+ if (host.previousElementSibling?.classList.contains("dsh-mermaid-wrapper")) {
2727
+ host.setAttribute("data-dsh-mermaid-status", "done");
2728
+ host.style.display = "none";
2729
+ return;
2730
+ }
2731
+ const wrapper = document.createElement("div");
2732
+ wrapper.className = "dsh-mermaid-wrapper";
2733
+ let showSource = false;
2734
+ wrapper.innerHTML = `
2735
+ <div class="dsh-mermaid-header">
2736
+ <span class="dsh-mermaid-title">Mermaid 图表</span>
2737
+ <div class="dsh-mermaid-controls">
2738
+ <button type="button" class="dsh-mermaid-btn" data-btn="toggle-source">源码</button>
2739
+ <button type="button" class="dsh-mermaid-btn" data-btn="copy">复制</button>
2740
+ <button type="button" class="dsh-mermaid-btn" data-btn="zoom" title="放大查看">放大 ↗</button>
2741
+ </div>
2742
+ </div>
2743
+ <div class="dsh-mermaid-chart" title="点击放大">${cleanSvg}</div>
2744
+ <div class="dsh-mermaid-source"><code>${code.replace(/</g, "&lt;").replace(/>/g, "&gt;")}</code></div>
2745
+ `;
2746
+ const sourceEl = wrapper.querySelector(".dsh-mermaid-source");
2747
+ const toggleBtn = wrapper.querySelector("[data-btn=\"toggle-source\"]");
2748
+ const copyBtn = wrapper.querySelector("[data-btn=\"copy\"]");
2749
+ const chartEl = wrapper.querySelector(".dsh-mermaid-chart");
2750
+ toggleBtn?.addEventListener("click", (e) => {
2751
+ e.stopPropagation();
2752
+ showSource = !showSource;
2753
+ if (showSource) {
2754
+ sourceEl.classList.add("is-visible");
2755
+ toggleBtn.textContent = "图表";
2756
+ } else {
2757
+ sourceEl.classList.remove("is-visible");
2758
+ toggleBtn.textContent = "源码";
2759
+ }
2760
+ });
2761
+ copyBtn?.addEventListener("click", async (e) => {
2762
+ e.stopPropagation();
2763
+ try {
2764
+ await navigator.clipboard.writeText(code);
2765
+ copyBtn.textContent = "已复制";
2766
+ setTimeout(() => {
2767
+ copyBtn.textContent = "复制";
2768
+ }, 1500);
2769
+ } catch {}
2770
+ });
2771
+ const handleZoom = (e) => {
2772
+ e.stopPropagation();
2773
+ openZoomModal(cleanSvg);
2774
+ };
2775
+ wrapper.querySelector("[data-btn=\"zoom\"]")?.addEventListener("click", handleZoom);
2776
+ chartEl?.addEventListener("click", handleZoom);
2777
+ host.style.display = "none";
2778
+ host.parentNode?.insertBefore(wrapper, host);
2779
+ host.setAttribute("data-dsh-mermaid-status", "done");
2780
+ } catch {
2781
+ processedHosts.delete(host);
2782
+ host.removeAttribute("data-dsh-mermaid-status");
2783
+ const errContainer = document.getElementById(renderId);
2784
+ if (errContainer) errContainer.remove();
2785
+ }
2786
+ }
2787
+ function scanAndRenderMermaid() {
2788
+ if (typeof document === "undefined") return;
2789
+ const codeElements = document.querySelectorAll("pre code, .md-code-block code");
2790
+ for (const codeEl of codeElements) {
2791
+ const candidate = findMermaidCandidate(codeEl);
2792
+ if (candidate) renderMermaidHost(candidate.host, candidate.code);
2793
+ }
2794
+ }
2795
+ function setupMermaidObserver() {
2796
+ if (typeof document === "undefined") return () => {};
2797
+ installMermaidStyles();
2798
+ let timer;
2799
+ const debouncedScan = () => {
2800
+ window.clearTimeout(timer);
2801
+ timer = window.setTimeout(() => {
2802
+ scanAndRenderMermaid();
2803
+ }, 250);
2804
+ };
2805
+ debouncedScan();
2806
+ const observer = new MutationObserver((mutations) => {
2807
+ let shouldScan = false;
2808
+ for (const m of mutations) {
2809
+ for (const node of m.addedNodes) if (node.nodeType === 1) {
2810
+ const el = node;
2811
+ if (el.classList?.contains("dsh-mermaid-wrapper") || el.closest?.(".dsh-mermaid-wrapper")) continue;
2812
+ shouldScan = true;
2813
+ break;
2814
+ }
2815
+ if (shouldScan) break;
2816
+ }
2817
+ if (shouldScan) debouncedScan();
2818
+ });
2819
+ observer.observe(document.body, {
2820
+ childList: true,
2821
+ subtree: true
2822
+ });
2823
+ const themeObserver = new MutationObserver(() => {
2824
+ initMermaidConfig();
2825
+ for (const el of document.querySelectorAll("[data-dsh-mermaid-status=\"done\"]")) {
2826
+ processedHosts.delete(el);
2827
+ el.removeAttribute("data-dsh-mermaid-status");
2828
+ const prevWrapper = el.previousElementSibling;
2829
+ if (prevWrapper && prevWrapper.classList.contains("dsh-mermaid-wrapper")) prevWrapper.remove();
2830
+ el.style.display = "";
2831
+ }
2832
+ debouncedScan();
2833
+ });
2834
+ themeObserver.observe(document.documentElement, {
2835
+ attributes: true,
2836
+ attributeFilter: [
2837
+ "class",
2838
+ "data-theme",
2839
+ "data-color-mode"
2840
+ ]
2841
+ });
2842
+ return () => {
2843
+ observer.disconnect();
2844
+ themeObserver.disconnect();
2845
+ window.clearTimeout(timer);
2846
+ };
2847
+ }
2848
+ //#endregion
1634
2849
  //#region src/client/index.tsx
1635
2850
  const inject = [
1636
2851
  "slots",
@@ -1639,8 +2854,14 @@ window.__ModuleLoader__.load({
1639
2854
  "conversation"
1640
2855
  ];
1641
2856
  function apply(ctx) {
1642
- ctx.effect(() => ctx.locale.register(NS, dictionaries), "dsh-chatgpt-subscription: dictionaries");
2857
+ ctx.effect(() => ctx.locale.register(NS, dictionaries$1), "dsh-chatgpt-subscription: dictionaries");
1643
2858
  ctx.effect(() => installStyles(), "dsh-chatgpt-subscription: styles");
2859
+ ctx.effect(() => ctx.locale.register(NS_ANTIGRAVITY, dictionaries), "dsh-antigravity: dictionaries");
2860
+ ctx.effect(() => {
2861
+ installAntigravityStyles();
2862
+ return () => {};
2863
+ }, "dsh-antigravity: styles");
2864
+ ctx.effect(() => setupMermaidObserver(), "dsh-mermaid: observer");
1644
2865
  const t = ctx.locale.bind(NS);
1645
2866
  ctx.slots.inject("settings.section", () => ctx.slots.register({
1646
2867
  name: "settings.section",
@@ -1649,6 +2870,23 @@ window.__ModuleLoader__.load({
1649
2870
  label: () => t("title"),
1650
2871
  locale: NS
1651
2872
  }, CodexSubscriptionSection));
2873
+ const AntigravitySectionWrapper = () => {
2874
+ const handleModelChange = () => {
2875
+ try {
2876
+ const conv = ctx.conversation;
2877
+ const activeSessionId = conv?.activeSessionId || conv?.currentSessionId || conv?.activeId;
2878
+ if (activeSessionId && ctx.modelDirectories) ctx.modelDirectories.directoryFor(activeSessionId)?.load?.().catch?.(() => void 0);
2879
+ } catch {}
2880
+ };
2881
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AntigravitySection, { onModelChange: handleModelChange });
2882
+ };
2883
+ ctx.slots.inject("settings.section", () => ctx.slots.register({
2884
+ name: "settings.section",
2885
+ id: "antigravity",
2886
+ order: 46,
2887
+ label: () => "Antigravity",
2888
+ locale: NS_ANTIGRAVITY
2889
+ }, AntigravitySectionWrapper));
1652
2890
  ctx.slots.inject("conversation.input.right", () => ctx.slots.register({
1653
2891
  name: "conversation.input.right",
1654
2892
  id: "codex-subscription-quota",
@@ -1665,6 +2903,21 @@ window.__ModuleLoader__.load({
1665
2903
  };
1666
2904
  }
1667
2905
  }, CodexComposerQuota));
2906
+ ctx.slots.inject("conversation.input.right", () => ctx.slots.register({
2907
+ name: "conversation.input.right",
2908
+ id: "antigravity-quota",
2909
+ order: 36,
2910
+ locale: NS_ANTIGRAVITY,
2911
+ inject: (sessionId) => {
2912
+ const directory = ctx.modelDirectories.directoryFor(sessionId);
2913
+ return {
2914
+ directory: directory.store,
2915
+ loadModelDirectory: () => {
2916
+ directory.load().catch(() => void 0);
2917
+ }
2918
+ };
2919
+ }
2920
+ }, AntigravityComposerQuota));
1668
2921
  ctx.slots.inject("tool.call.toolview", () => ctx.slots.register({
1669
2922
  name: "tool.call.toolview",
1670
2923
  key: CODEX_IMAGE_TOOL_NAME,