@eddyskywalker/dsh-chatgpt-subscription 0.1.37 → 0.1.38

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