@mars-sea/dsh-commandcode-provider 0.15.4 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +53 -0
- package/README.md +16 -3
- package/README.zh-CN.md +16 -3
- package/lib/client.js +409 -144
- package/lib/index.d.ts +73 -6
- package/lib/index.js +344 -78
- package/package.json +50 -43
package/lib/client.js
CHANGED
|
@@ -1856,6 +1856,8 @@ window.__ModuleLoader__.load({
|
|
|
1856
1856
|
generation = 0;
|
|
1857
1857
|
inFlight = false;
|
|
1858
1858
|
disposed = false;
|
|
1859
|
+
/** When the last fetch SETTLED successfully, for {@link USAGE_AUTO_REFRESH_COALESCE_MS}. */
|
|
1860
|
+
settledAt;
|
|
1859
1861
|
constructor(remote) {
|
|
1860
1862
|
this.remote = remote;
|
|
1861
1863
|
}
|
|
@@ -1877,9 +1879,16 @@ window.__ModuleLoader__.load({
|
|
|
1877
1879
|
/**
|
|
1878
1880
|
* Fetch (or refetch) the report. Concurrent refreshes collapse onto one
|
|
1879
1881
|
* request; a superseded fetch's late result is dropped, never published.
|
|
1882
|
+
*
|
|
1883
|
+
* `auto` marks a refresh nobody asked for — a surface appearing, or the
|
|
1884
|
+
* background tick. Those also reuse a result fetched within
|
|
1885
|
+
* {@link USAGE_AUTO_REFRESH_COALESCE_MS}, because two surfaces mounting a
|
|
1886
|
+
* second apart are asking the same question. Every other caller is a user
|
|
1887
|
+
* action and always goes to the wire.
|
|
1880
1888
|
*/
|
|
1881
|
-
async refresh() {
|
|
1889
|
+
async refresh(options) {
|
|
1882
1890
|
if (this.disposed || this.inFlight) return;
|
|
1891
|
+
if (options?.auto === true && this.current.status === "ready" && this.settledAt !== void 0 && Date.now() - this.settledAt < 5e3) return;
|
|
1883
1892
|
const generation = ++this.generation;
|
|
1884
1893
|
this.inFlight = true;
|
|
1885
1894
|
this.current = {
|
|
@@ -1891,13 +1900,16 @@ window.__ModuleLoader__.load({
|
|
|
1891
1900
|
try {
|
|
1892
1901
|
const response = await this.remote.report();
|
|
1893
1902
|
if (this.disposed || generation !== this.generation) return;
|
|
1894
|
-
if (response.ok)
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1903
|
+
if (response.ok) {
|
|
1904
|
+
const fetchedAt = Date.now();
|
|
1905
|
+
this.settledAt = fetchedAt;
|
|
1906
|
+
this.current = {
|
|
1907
|
+
status: "ready",
|
|
1908
|
+
report: response.value,
|
|
1909
|
+
error: void 0,
|
|
1910
|
+
fetchedAt
|
|
1911
|
+
};
|
|
1912
|
+
} else this.current = {
|
|
1901
1913
|
...this.current,
|
|
1902
1914
|
status: "error",
|
|
1903
1915
|
error: response.error.message
|
|
@@ -2887,6 +2899,23 @@ window.__ModuleLoader__.load({
|
|
|
2887
2899
|
]
|
|
2888
2900
|
};
|
|
2889
2901
|
//#endregion
|
|
2902
|
+
//#region src/client/random-id.ts
|
|
2903
|
+
/**
|
|
2904
|
+
* 页面身份(issue #102)。`crypto.randomUUID` 是安全上下文专属:局域网里用
|
|
2905
|
+
* `http://<ip>:3080` 打开设置页时它是 `undefined`,而这里在控制器构造期就调用,
|
|
2906
|
+
* 异常会落到设置页插槽的错误边界上,整块内容变成空白。`crypto.getRandomValues`
|
|
2907
|
+
* 在非安全上下文同样可用,因此只在 `randomUUID` 不可用时按 RFC 4122 自行拼一个 v4。
|
|
2908
|
+
*/
|
|
2909
|
+
function randomId() {
|
|
2910
|
+
const source = globalThis.crypto;
|
|
2911
|
+
if (typeof source.randomUUID === "function") return source.randomUUID();
|
|
2912
|
+
const bytes = source.getRandomValues(/* @__PURE__ */ new Uint8Array(16));
|
|
2913
|
+
bytes[6] = bytes[6] & 15 | 64;
|
|
2914
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
2915
|
+
const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
2916
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
2917
|
+
}
|
|
2918
|
+
//#endregion
|
|
2890
2919
|
//#region src/client/enrollment.ts
|
|
2891
2920
|
const live = (state) => [
|
|
2892
2921
|
"creating",
|
|
@@ -2912,7 +2941,7 @@ window.__ModuleLoader__.load({
|
|
|
2912
2941
|
watch;
|
|
2913
2942
|
ready;
|
|
2914
2943
|
openedPage = false;
|
|
2915
|
-
constructor(remote, changed, pollMs = 750, newId =
|
|
2944
|
+
constructor(remote, changed, pollMs = 750, newId = randomId, openPage = openLoginPage) {
|
|
2916
2945
|
this.remote = remote;
|
|
2917
2946
|
this.changed = changed;
|
|
2918
2947
|
this.pollMs = pollMs;
|
|
@@ -3243,7 +3272,6 @@ window.__ModuleLoader__.load({
|
|
|
3243
3272
|
usageCost: "花费",
|
|
3244
3273
|
usageCompleted: "成功",
|
|
3245
3274
|
usageBillingPeriod: "官方账期用量",
|
|
3246
|
-
usagePeriodUnknown: "官方用量汇总,统计周期未确认",
|
|
3247
3275
|
usageTokens: "Token",
|
|
3248
3276
|
usageTokensIn: "入",
|
|
3249
3277
|
usageTokensOut: "出",
|
|
@@ -3330,7 +3358,7 @@ window.__ModuleLoader__.load({
|
|
|
3330
3358
|
accountEnrollmentCleanupRetry: "重试清理",
|
|
3331
3359
|
accountRefSeparator: ":",
|
|
3332
3360
|
accountModels: "专用模型",
|
|
3333
|
-
accountModelsHint: "
|
|
3361
|
+
accountModelsHint: "选中即专属:优先此账户处理,不可用时自动切换",
|
|
3334
3362
|
accountModelOwner: "属于 {name}",
|
|
3335
3363
|
modelPick: "选择模型…",
|
|
3336
3364
|
modelCount: "已选 {count} 个模型",
|
|
@@ -3515,7 +3543,6 @@ window.__ModuleLoader__.load({
|
|
|
3515
3543
|
usageCost: "Spend",
|
|
3516
3544
|
usageCompleted: "Completed",
|
|
3517
3545
|
usageBillingPeriod: "Official billing-period usage",
|
|
3518
|
-
usagePeriodUnknown: "Official usage summary; the billing period is unconfirmed",
|
|
3519
3546
|
usageTokens: "Tokens",
|
|
3520
3547
|
usageTokensIn: "in",
|
|
3521
3548
|
usageTokensOut: "out",
|
|
@@ -3602,7 +3629,7 @@ window.__ModuleLoader__.load({
|
|
|
3602
3629
|
accountEnrollmentCleanupRetry: "Retry cleanup",
|
|
3603
3630
|
accountRefSeparator: ": ",
|
|
3604
3631
|
accountModels: "Dedicated models",
|
|
3605
|
-
accountModelsHint: "
|
|
3632
|
+
accountModelsHint: "Selected models are dedicated to this account and still switch when unavailable",
|
|
3606
3633
|
accountModelOwner: "on {name}",
|
|
3607
3634
|
modelPick: "Choose models…",
|
|
3608
3635
|
modelCount: "{count} models selected",
|
|
@@ -3837,7 +3864,7 @@ window.__ModuleLoader__.load({
|
|
|
3837
3864
|
}
|
|
3838
3865
|
//#endregion
|
|
3839
3866
|
//#region package.json
|
|
3840
|
-
var version = "0.
|
|
3867
|
+
var version = "0.16.0";
|
|
3841
3868
|
var repository = {
|
|
3842
3869
|
"type": "git",
|
|
3843
3870
|
"url": "git+https://github.com/Mars-Sea/dsh-commandcode-provider.git"
|
|
@@ -4863,7 +4890,7 @@ window.__ModuleLoader__.load({
|
|
|
4863
4890
|
"aria-valuenow": Math.round(ratio * 100),
|
|
4864
4891
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4865
4892
|
className: exceeded ? "cc-usageBarFill cc-usageBarFillWarn" : "cc-usageBarFill",
|
|
4866
|
-
style: {
|
|
4893
|
+
style: { transform: `translateX(${(ratio - 1) * 100}%)` }
|
|
4867
4894
|
})
|
|
4868
4895
|
}),
|
|
4869
4896
|
reset !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
@@ -4877,6 +4904,19 @@ window.__ModuleLoader__.load({
|
|
|
4877
4904
|
]
|
|
4878
4905
|
});
|
|
4879
4906
|
}
|
|
4907
|
+
/**
|
|
4908
|
+
* Left text of the balance label row: the billing period, stated once per card.
|
|
4909
|
+
* 用量汇总与订阅起点对齐时给完整区间;对不上(或用量汇总没取到)时退回订阅
|
|
4910
|
+
* 自身的截止日,它不依赖对齐也可靠。余额没取到时没有「月额度剩余」可挂,完整
|
|
4911
|
+
* 区间前补回「官方账期用量」,避免一串裸日期没有上下文。
|
|
4912
|
+
*/
|
|
4913
|
+
function balanceLabelText(report, t) {
|
|
4914
|
+
const plan = report.plan;
|
|
4915
|
+
const usagePeriod = formatUsageBillingPeriod(report.usage?.periodStart, plan?.currentPeriodStart, plan?.currentPeriodEnd);
|
|
4916
|
+
const periodText = usagePeriod !== "" ? usagePeriod : plan !== void 0 && plan.currentPeriodEnd > 0 ? `${t("usagePeriodEnd")} ${new Date(plan.currentPeriodEnd).toLocaleDateString()}` : "";
|
|
4917
|
+
if (report.credits !== void 0) return periodText !== "" ? `${t("usageMonthlyLeft")} · ${periodText}` : t("usageMonthlyLeft");
|
|
4918
|
+
return usagePeriod !== "" ? `${t("usageBillingPeriod")} ${usagePeriod}` : periodText;
|
|
4919
|
+
}
|
|
4880
4920
|
/** One pool account's facts (identity, totals, credits, window limits). */
|
|
4881
4921
|
function AccountReport({ entry, fetchedAt, t, headless = false }) {
|
|
4882
4922
|
const report = entry.report;
|
|
@@ -4886,9 +4926,12 @@ window.__ModuleLoader__.load({
|
|
|
4886
4926
|
const plan = report.plan;
|
|
4887
4927
|
const planName = plan?.name ?? "";
|
|
4888
4928
|
const planStatus = plan !== void 0 && plan.status !== "" && plan.status !== "active" ? plan.status : "";
|
|
4889
|
-
const showPeriod = plan !== void 0 && plan.currentPeriodEnd > 0;
|
|
4890
4929
|
const showPartial = report.failures.length > 0 && report.blocked === void 0;
|
|
4891
|
-
const
|
|
4930
|
+
const label = balanceLabelText(report, t);
|
|
4931
|
+
const updatedText = fetchedAt === void 0 ? "" : `${t("usageUpdated")} ${new Date(fetchedAt).toLocaleTimeString(void 0, {
|
|
4932
|
+
hour: "2-digit",
|
|
4933
|
+
minute: "2-digit"
|
|
4934
|
+
})}`;
|
|
4892
4935
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4893
4936
|
className: "cc-accountReport",
|
|
4894
4937
|
children: [
|
|
@@ -4927,26 +4970,33 @@ window.__ModuleLoader__.load({
|
|
|
4927
4970
|
children: report.failures.join(" · ")
|
|
4928
4971
|
}) : null]
|
|
4929
4972
|
}) : null,
|
|
4930
|
-
|
|
4931
|
-
const monthlyKnown = credits.monthlyReported !== false;
|
|
4932
|
-
const depleted = monthlyKnown && credits.monthlyCredits <= 0;
|
|
4973
|
+
(() => {
|
|
4974
|
+
const monthlyKnown = credits !== void 0 && credits.monthlyReported !== false;
|
|
4975
|
+
const depleted = credits !== void 0 && monthlyKnown && credits.monthlyCredits <= 0;
|
|
4976
|
+
if (label === "" && updatedText === "") return null;
|
|
4933
4977
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4934
4978
|
className: depleted ? "cc-balance cc-balanceEmpty" : "cc-balance",
|
|
4935
4979
|
children: [
|
|
4936
4980
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4937
|
-
className: "cc-
|
|
4981
|
+
className: "cc-balanceRow",
|
|
4938
4982
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4939
4983
|
className: "cc-balanceLabel",
|
|
4940
|
-
children:
|
|
4941
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4984
|
+
children: label
|
|
4985
|
+
}), updatedText !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4986
|
+
className: "cc-balanceLabel",
|
|
4987
|
+
children: updatedText
|
|
4988
|
+
}) : null]
|
|
4989
|
+
}),
|
|
4990
|
+
credits !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4991
|
+
className: "cc-balanceRow",
|
|
4992
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4942
4993
|
className: "cc-balanceValue",
|
|
4943
4994
|
children: monthlyKnown ? formatMoney(Math.max(0, credits.monthlyCredits)) : "—"
|
|
4995
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4996
|
+
className: "cc-balanceSub",
|
|
4997
|
+
children: `${t("usagePurchased")} ${credits.purchasedReported === false ? "—" : formatMoney(credits.purchasedCredits)} · ${t("usageFree")} ${credits.freeReported === false ? "—" : formatMoney(credits.freeCredits)}`
|
|
4944
4998
|
})]
|
|
4945
|
-
}),
|
|
4946
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4947
|
-
className: "cc-balanceSub",
|
|
4948
|
-
children: `${t("usagePurchased")} ${credits.purchasedReported === false ? "—" : formatMoney(credits.purchasedCredits)} · ${t("usageFree")} ${credits.freeReported === false ? "—" : formatMoney(credits.freeCredits)}`
|
|
4949
|
-
}),
|
|
4999
|
+
}) : null,
|
|
4950
5000
|
depleted ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4951
5001
|
className: "cc-balanceNote",
|
|
4952
5002
|
role: "status",
|
|
@@ -4954,11 +5004,7 @@ window.__ModuleLoader__.load({
|
|
|
4954
5004
|
}) : null
|
|
4955
5005
|
]
|
|
4956
5006
|
});
|
|
4957
|
-
})()
|
|
4958
|
-
report.usage !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4959
|
-
className: "cc-usageUpdated",
|
|
4960
|
-
children: usagePeriod ? `${t("usageBillingPeriod")} ${usagePeriod}` : t("usagePeriodUnknown")
|
|
4961
|
-
}) : null,
|
|
5007
|
+
})(),
|
|
4962
5008
|
report.usage !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4963
5009
|
className: "cc-usageStats cc-usageStatsActivity",
|
|
4964
5010
|
children: [
|
|
@@ -5000,35 +5046,10 @@ window.__ModuleLoader__.load({
|
|
|
5000
5046
|
t
|
|
5001
5047
|
}) : null]
|
|
5002
5048
|
}) : null,
|
|
5003
|
-
|
|
5004
|
-
className: "cc-
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
className: "cc-usageUpdated",
|
|
5008
|
-
children: [
|
|
5009
|
-
t("usagePeriodEnd"),
|
|
5010
|
-
" ",
|
|
5011
|
-
new Date(plan.currentPeriodEnd).toLocaleDateString()
|
|
5012
|
-
]
|
|
5013
|
-
}) : null,
|
|
5014
|
-
showPartial ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5015
|
-
className: "cc-usagePartial",
|
|
5016
|
-
title: report.failures.join("; "),
|
|
5017
|
-
children: t("usagePartial")
|
|
5018
|
-
}) : null,
|
|
5019
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cc-usageMetaSpacer" }),
|
|
5020
|
-
fetchedAt !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
5021
|
-
className: "cc-usageUpdated",
|
|
5022
|
-
children: [
|
|
5023
|
-
t("usageUpdated"),
|
|
5024
|
-
" ",
|
|
5025
|
-
new Date(fetchedAt).toLocaleTimeString(void 0, {
|
|
5026
|
-
hour: "2-digit",
|
|
5027
|
-
minute: "2-digit"
|
|
5028
|
-
})
|
|
5029
|
-
]
|
|
5030
|
-
}) : null
|
|
5031
|
-
]
|
|
5049
|
+
showPartial ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5050
|
+
className: "cc-usagePartial",
|
|
5051
|
+
title: report.failures.join("; "),
|
|
5052
|
+
children: t("usagePartial")
|
|
5032
5053
|
}) : null
|
|
5033
5054
|
]
|
|
5034
5055
|
});
|
|
@@ -5201,6 +5222,128 @@ window.__ModuleLoader__.load({
|
|
|
5201
5222
|
});
|
|
5202
5223
|
}
|
|
5203
5224
|
/**
|
|
5225
|
+
* The glanceable header strip: account health, monthly remainder, service
|
|
5226
|
+
* status, model scope. Every string reuses one the groups already compute
|
|
5227
|
+
* (group summaries, balance labels, the service badge), so the strip adds no
|
|
5228
|
+
* locale keys and cannot disagree with the card below it. Cells are plain
|
|
5229
|
+
* text, not links — the groups beneath are the place to act.
|
|
5230
|
+
*/
|
|
5231
|
+
function StatusStrip({ t, state, usage, snapshot, probe }) {
|
|
5232
|
+
const rows = accountRows(state, usage, t);
|
|
5233
|
+
const issues = accountIssues(rows.map((row) => row.usage));
|
|
5234
|
+
const defaultRow = rows.find((row) => row.id === "default");
|
|
5235
|
+
const credits = defaultRow?.usage?.report?.credits;
|
|
5236
|
+
const monthly = credits !== void 0 && credits.monthlyReported !== false ? credits : void 0;
|
|
5237
|
+
const depleted = monthly !== void 0 && monthly.monthlyCredits <= 0;
|
|
5238
|
+
const planName = defaultRow?.usage?.report?.plan?.name ?? "";
|
|
5239
|
+
const overridden = modelsOverriddenCount(state);
|
|
5240
|
+
const view = serviceStatusView(snapshot, probe, t, Date.now());
|
|
5241
|
+
const serviceDot = view.badge.tone === "ok" ? "cc-tabDot cc-tabDotOk" : view.badge.tone === "warn" ? "cc-tabDot cc-tabDotWarn" : view.badge.tone === "error" ? "cc-tabDot cc-tabDotError" : "cc-tabDot cc-tabDotBlank";
|
|
5242
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5243
|
+
className: "cc-statusStrip",
|
|
5244
|
+
children: [
|
|
5245
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5246
|
+
className: "cc-statusCell",
|
|
5247
|
+
children: [
|
|
5248
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
5249
|
+
className: "cc-statusHead",
|
|
5250
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5251
|
+
className: issues.severe ? "cc-tabDot cc-tabDotError" : issues.count > 0 ? "cc-tabDot cc-tabDotWarn" : "cc-tabDot cc-tabDotOk",
|
|
5252
|
+
"aria-hidden": "true"
|
|
5253
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5254
|
+
className: "cc-statusLabel",
|
|
5255
|
+
children: t("accountsTitle")
|
|
5256
|
+
})]
|
|
5257
|
+
}),
|
|
5258
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5259
|
+
className: "cc-statusStripValue",
|
|
5260
|
+
children: rows.length === 1 ? t("accountsSummaryOne") : t("accountsSummaryMany", { count: rows.length })
|
|
5261
|
+
}),
|
|
5262
|
+
issues.count > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5263
|
+
className: "cc-statusSub",
|
|
5264
|
+
children: t("accountsSummaryIssues", { count: issues.count })
|
|
5265
|
+
}) : planName !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5266
|
+
className: "cc-statusSub",
|
|
5267
|
+
children: planName
|
|
5268
|
+
}) : null
|
|
5269
|
+
]
|
|
5270
|
+
}),
|
|
5271
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5272
|
+
className: "cc-statusCell",
|
|
5273
|
+
children: [
|
|
5274
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
5275
|
+
className: "cc-statusHead",
|
|
5276
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5277
|
+
className: depleted ? "cc-tabDot cc-tabDotError" : monthly !== void 0 ? "cc-tabDot cc-tabDotOk" : "cc-tabDot cc-tabDotBlank",
|
|
5278
|
+
"aria-hidden": "true"
|
|
5279
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5280
|
+
className: "cc-statusLabel",
|
|
5281
|
+
children: t("usageMonthlyLeft")
|
|
5282
|
+
})]
|
|
5283
|
+
}),
|
|
5284
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5285
|
+
className: "cc-statusStripValue",
|
|
5286
|
+
children: monthly !== void 0 ? formatMoney(Math.max(0, monthly.monthlyCredits)) : "—"
|
|
5287
|
+
}),
|
|
5288
|
+
credits !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5289
|
+
className: "cc-statusSub",
|
|
5290
|
+
children: `${t("usagePurchased")} ${credits.purchasedReported === false ? "—" : formatMoney(credits.purchasedCredits)} · ${t("usageFree")} ${credits.freeReported === false ? "—" : formatMoney(credits.freeCredits)}`
|
|
5291
|
+
}) : null
|
|
5292
|
+
]
|
|
5293
|
+
}),
|
|
5294
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5295
|
+
className: "cc-statusCell",
|
|
5296
|
+
children: [
|
|
5297
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
5298
|
+
className: "cc-statusHead",
|
|
5299
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5300
|
+
className: serviceDot,
|
|
5301
|
+
"aria-hidden": "true"
|
|
5302
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5303
|
+
className: "cc-statusLabel",
|
|
5304
|
+
children: t("serviceStatusTitle")
|
|
5305
|
+
})]
|
|
5306
|
+
}),
|
|
5307
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5308
|
+
className: "cc-statusStripValue",
|
|
5309
|
+
children: view.badge.label
|
|
5310
|
+
}),
|
|
5311
|
+
view.updated !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5312
|
+
className: "cc-statusSub",
|
|
5313
|
+
children: view.updated
|
|
5314
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5315
|
+
className: "cc-statusSub",
|
|
5316
|
+
children: view.local
|
|
5317
|
+
})
|
|
5318
|
+
]
|
|
5319
|
+
}),
|
|
5320
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5321
|
+
className: "cc-statusCell",
|
|
5322
|
+
children: [
|
|
5323
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
5324
|
+
className: "cc-statusHead",
|
|
5325
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5326
|
+
className: "cc-tabDot",
|
|
5327
|
+
"aria-hidden": "true"
|
|
5328
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5329
|
+
className: "cc-statusLabel",
|
|
5330
|
+
children: t("modelsTitle")
|
|
5331
|
+
})]
|
|
5332
|
+
}),
|
|
5333
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5334
|
+
className: "cc-statusStripValue",
|
|
5335
|
+
children: state.visibleModelsAll ? t("visibleModelsSummaryAll") : t("visibleModelsSummary", { count: state.visibleModels.length })
|
|
5336
|
+
}),
|
|
5337
|
+
overridden > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5338
|
+
className: "cc-statusSub",
|
|
5339
|
+
children: overriddenText(overridden, t)
|
|
5340
|
+
}) : null
|
|
5341
|
+
]
|
|
5342
|
+
})
|
|
5343
|
+
]
|
|
5344
|
+
});
|
|
5345
|
+
}
|
|
5346
|
+
/**
|
|
5204
5347
|
* One top-level group of the page (决策记录第十章第4条). All five groups share
|
|
5205
5348
|
* this one shape so they cannot drift apart: the heading is an `h3` wrapping a
|
|
5206
5349
|
* single full-width button — chevron first, then the title and the summary
|
|
@@ -5211,14 +5354,15 @@ window.__ModuleLoader__.load({
|
|
|
5211
5354
|
* The body stays mounted and is hidden with the `hidden` attribute rather than
|
|
5212
5355
|
* unrendered: `aria-controls` then always points at a real element, and the
|
|
5213
5356
|
* save bar's locate action can focus an invalid field right after opening its
|
|
5214
|
-
* group. Folding is presentation only
|
|
5215
|
-
*
|
|
5357
|
+
* group. Folding is presentation only and instant toggling is deliberate (it
|
|
5358
|
+
* happens too often to be worth a wait); staged drafts, validation and the
|
|
5359
|
+
* save path are untouched (第六章第1、2条).
|
|
5216
5360
|
*/
|
|
5217
|
-
function FoldableGroup({ id, title,
|
|
5361
|
+
function FoldableGroup({ id, title, expanded, onToggle, badges, action, children }) {
|
|
5218
5362
|
const titleId = `cc-group-${id}-title`;
|
|
5219
5363
|
const bodyId = `cc-group-${id}-body`;
|
|
5220
5364
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
5221
|
-
className:
|
|
5365
|
+
className: "cc-group",
|
|
5222
5366
|
"aria-labelledby": titleId,
|
|
5223
5367
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5224
5368
|
className: "cc-groupHead",
|
|
@@ -5850,7 +5994,7 @@ window.__ModuleLoader__.load({
|
|
|
5850
5994
|
"aria-hidden": "true",
|
|
5851
5995
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5852
5996
|
className: limit.exceeded ? "cc-miniMeterFill cc-usageBarFillWarn" : "cc-miniMeterFill",
|
|
5853
|
-
style: {
|
|
5997
|
+
style: { transform: `translateX(${(ratio - 1) * 100}%)` }
|
|
5854
5998
|
})
|
|
5855
5999
|
}),
|
|
5856
6000
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -6459,8 +6603,13 @@ window.__ModuleLoader__.load({
|
|
|
6459
6603
|
* The unified account card: every account the pool rotates through, one row
|
|
6460
6604
|
* each, with its status and quota inline — so one account's facts no longer
|
|
6461
6605
|
* have to be cross-read across separate surfaces.
|
|
6606
|
+
*
|
|
6607
|
+
* Exported for the render test (`tests/accounts-card-render.test.ts`) that pins
|
|
6608
|
+
* the first-paint auto-refresh wiring against a real React tree: the page mounts
|
|
6609
|
+
* this card, and a test that only checks the injected function exists cannot see
|
|
6610
|
+
* the card reaching for it.
|
|
6462
6611
|
*/
|
|
6463
|
-
function AccountsCard({ t, state, usage, login, disabled, actions, onRefresh, openAnalytics, createEnrollment, expanded, onToggle }) {
|
|
6612
|
+
function AccountsCard({ t, state, usage, login, disabled, actions, onRefresh, onRefreshAuto, openAnalytics, createEnrollment, expanded, onToggle }) {
|
|
6464
6613
|
const [adding, setAdding] = (0, react.useState)(false);
|
|
6465
6614
|
const [enrollment] = (0, react.useState)(createEnrollment);
|
|
6466
6615
|
const snapshot = (0, react.useSyncExternalStore)(enrollment.store.subscribe, enrollment.store.getSnapshot);
|
|
@@ -6471,8 +6620,8 @@ window.__ModuleLoader__.load({
|
|
|
6471
6620
|
const closeAdd = (0, react.useCallback)(() => setAdding(false), []);
|
|
6472
6621
|
const { loading, shouldRefresh } = usageCardState(usage);
|
|
6473
6622
|
(0, react.useEffect)(() => {
|
|
6474
|
-
if (shouldRefresh)
|
|
6475
|
-
}, [shouldRefresh,
|
|
6623
|
+
if (shouldRefresh) onRefreshAuto();
|
|
6624
|
+
}, [shouldRefresh, onRefreshAuto]);
|
|
6476
6625
|
const rows = accountRows(state, usage, t).filter((row) => row.ref === void 0 || !state.accountEnrollmentRefs.includes(row.ref));
|
|
6477
6626
|
const multi = rows.length > 1;
|
|
6478
6627
|
const pinnedRow = rows.find((row) => row.id === state.activeAccount);
|
|
@@ -6480,7 +6629,6 @@ window.__ModuleLoader__.load({
|
|
|
6480
6629
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(FoldableGroup, {
|
|
6481
6630
|
id: "accounts",
|
|
6482
6631
|
title: t("accountsTitle"),
|
|
6483
|
-
divided: false,
|
|
6484
6632
|
expanded,
|
|
6485
6633
|
onToggle,
|
|
6486
6634
|
badges: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(GroupSummary, { parts: [rows.length === 1 ? t("accountsSummaryOne") : t("accountsSummaryMany", { count: rows.length })] }), issues.count > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -6948,6 +7096,13 @@ window.__ModuleLoader__.load({
|
|
|
6948
7096
|
className: "cc-intro",
|
|
6949
7097
|
children: t("intro")
|
|
6950
7098
|
}),
|
|
7099
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatusStrip, {
|
|
7100
|
+
t,
|
|
7101
|
+
state,
|
|
7102
|
+
usage,
|
|
7103
|
+
snapshot: status.snapshot,
|
|
7104
|
+
probe: status.probe
|
|
7105
|
+
}),
|
|
6951
7106
|
!state.writable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Notice, {
|
|
6952
7107
|
icon: "lock",
|
|
6953
7108
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("readOnly") })
|
|
@@ -6960,6 +7115,7 @@ window.__ModuleLoader__.load({
|
|
|
6960
7115
|
disabled,
|
|
6961
7116
|
actions,
|
|
6962
7117
|
onRefresh: props.refreshUsage,
|
|
7118
|
+
onRefreshAuto: props.refreshUsageAuto,
|
|
6963
7119
|
openAnalytics: props.openAnalytics,
|
|
6964
7120
|
createEnrollment: props.createEnrollment,
|
|
6965
7121
|
...foldProps("accounts")
|
|
@@ -7899,6 +8055,14 @@ window.__ModuleLoader__.load({
|
|
|
7899
8055
|
set: (callback, ms) => setTimeout(callback, ms),
|
|
7900
8056
|
clear: (handle) => clearTimeout(handle)
|
|
7901
8057
|
};
|
|
8058
|
+
const DOCUMENT_VISIBILITY = {
|
|
8059
|
+
isVisible: () => typeof document === "undefined" ? true : document.visibilityState !== "hidden",
|
|
8060
|
+
subscribe: (onChange) => {
|
|
8061
|
+
if (typeof document === "undefined") return () => void 0;
|
|
8062
|
+
document.addEventListener("visibilitychange", onChange);
|
|
8063
|
+
return () => document.removeEventListener("visibilitychange", onChange);
|
|
8064
|
+
}
|
|
8065
|
+
};
|
|
7902
8066
|
/** Live mount count of {@link startPanelAutoRefresh}. */
|
|
7903
8067
|
let references = 0;
|
|
7904
8068
|
/** Owner ticket of the running loop, if any. */
|
|
@@ -7908,6 +8072,16 @@ window.__ModuleLoader__.load({
|
|
|
7908
8072
|
/** Pending timer handle of the running loop. */
|
|
7909
8073
|
let handle;
|
|
7910
8074
|
/**
|
|
8075
|
+
* Unsubscribe of the visibility listener owned by the RUNNING loop.
|
|
8076
|
+
*
|
|
8077
|
+
* Module-level on purpose: only the surface that started the loop receives the
|
|
8078
|
+
* subscribe handle, but that surface is not necessarily the last to unmount.
|
|
8079
|
+
* Keeping the handle on the same layer as `activeTicket`/`handle` lets any
|
|
8080
|
+
* teardown order release it; a function-local handle leaked the listener
|
|
8081
|
+
* whenever the starter unmounted first.
|
|
8082
|
+
*/
|
|
8083
|
+
let visibilityUnsubscribe;
|
|
8084
|
+
/**
|
|
7911
8085
|
* Start the shared auto refresh. One fetch when the surface appears (the sidebar
|
|
7912
8086
|
* row is the point of the panel — it must be current, not wait for a click), then
|
|
7913
8087
|
* a tick every {@link PANEL_AUTO_REFRESH_MS} while a surface stays mounted.
|
|
@@ -7917,22 +8091,37 @@ window.__ModuleLoader__.load({
|
|
|
7917
8091
|
* tick goes through `usage.refresh()`, which collapses a concurrent fetch onto
|
|
7918
8092
|
* the in-flight one. Returns the disposer that drops this surface's reference.
|
|
7919
8093
|
*
|
|
8094
|
+
* A hidden document is still mounted, so its ticks are SKIPPED: a background
|
|
8095
|
+
* tab must not keep paying for a four-call report nobody reads. The tick is
|
|
8096
|
+
* rescheduled either way, so the loop survives, and becoming visible again
|
|
8097
|
+
* fetches at once — the data on screen is by then older than a tick would have
|
|
8098
|
+
* left it. The first paint still fetches unconditionally: it is the moment the
|
|
8099
|
+
* row appears, and there is nothing to show without it.
|
|
8100
|
+
*
|
|
7920
8101
|
* @param usage - the shared usage controller.
|
|
7921
8102
|
* @param isConfigured - whether a credential exists right now (re-read per tick).
|
|
7922
8103
|
* @param timer - timer seam for tests.
|
|
8104
|
+
* @param visibility - visibility seam; see {@link AutoRefreshVisibility}.
|
|
7923
8105
|
*/
|
|
7924
|
-
function startPanelAutoRefresh(usage, isConfigured, timer = REAL_TIMER) {
|
|
8106
|
+
function startPanelAutoRefresh(usage, isConfigured, timer = REAL_TIMER, visibility = DOCUMENT_VISIBILITY) {
|
|
7925
8107
|
references += 1;
|
|
7926
8108
|
const ticket = ++ticketSeq;
|
|
8109
|
+
const fetch = () => {
|
|
8110
|
+
if (isConfigured()) usage.refresh({ auto: true });
|
|
8111
|
+
};
|
|
7927
8112
|
const tick = () => {
|
|
7928
8113
|
if (activeTicket !== ticket) return;
|
|
7929
|
-
if (
|
|
8114
|
+
if (visibility.isVisible()) fetch();
|
|
7930
8115
|
handle = timer.set(tick, PANEL_AUTO_REFRESH_MS);
|
|
7931
8116
|
};
|
|
7932
8117
|
if (activeTicket === void 0) {
|
|
7933
8118
|
activeTicket = ticket;
|
|
7934
8119
|
handle = timer.set(tick, PANEL_AUTO_REFRESH_MS);
|
|
7935
|
-
|
|
8120
|
+
fetch();
|
|
8121
|
+
visibilityUnsubscribe = visibility.subscribe?.(() => {
|
|
8122
|
+
if (activeTicket !== ticket || !visibility.isVisible()) return;
|
|
8123
|
+
fetch();
|
|
8124
|
+
});
|
|
7936
8125
|
}
|
|
7937
8126
|
let stopped = false;
|
|
7938
8127
|
return () => {
|
|
@@ -7941,6 +8130,8 @@ window.__ModuleLoader__.load({
|
|
|
7941
8130
|
references -= 1;
|
|
7942
8131
|
if (references > 0) return;
|
|
7943
8132
|
activeTicket = void 0;
|
|
8133
|
+
visibilityUnsubscribe?.();
|
|
8134
|
+
visibilityUnsubscribe = void 0;
|
|
7944
8135
|
if (handle !== void 0) {
|
|
7945
8136
|
timer.clear(handle);
|
|
7946
8137
|
handle = void 0;
|
|
@@ -10229,13 +10420,26 @@ window.__ModuleLoader__.load({
|
|
|
10229
10420
|
* The settings-page stylesheet.
|
|
10230
10421
|
*
|
|
10231
10422
|
* Every size is the design canvas's (the cc-kit.css of the "CommandCode 设置页
|
|
10232
|
-
* 重设计" artboards, 2026-10-21
|
|
10233
|
-
*
|
|
10234
|
-
*
|
|
10235
|
-
*
|
|
10236
|
-
*
|
|
10237
|
-
*
|
|
10238
|
-
*
|
|
10423
|
+
* 重设计" artboards, 2026-10-21, as revised by the 2026-10-29 card redesign):
|
|
10424
|
+
* a row is 14px of vertical padding with a .5px hairline between rows, title
|
|
10425
|
+
* 14/22, description 12/18, a 24px gap to the control; a group is a 16px-radius
|
|
10426
|
+
* card on the raised surface with a 1px border-l2 stroke, its heading 52px
|
|
10427
|
+
* high; the page title is 22/28 and group titles 15/22, both 600. The page
|
|
10428
|
+
* renders with grayscale antialiasing and descriptive text in label-secondary.
|
|
10429
|
+
* A status strip under the header (account health, monthly remainder, service
|
|
10430
|
+
* status, model scope) reuses the groups' own summary strings, so it adds no
|
|
10431
|
+
* locale keys and cannot disagree with the card below it.
|
|
10432
|
+
*
|
|
10433
|
+
* Motion (2026-10-29): anything pressable scales on :active (0.97, the 20–28px
|
|
10434
|
+
* icon buttons 0.95) over 160ms of one strong ease-out,
|
|
10435
|
+
* cubic-bezier(.23,1,.32,1), which the fold chevron, save bar, usage bar,
|
|
10436
|
+
* card entrance and notice entrance share; colour and border changes fade over
|
|
10437
|
+
* 150ms `ease`. Only transform, opacity and colours animate — never layout.
|
|
10438
|
+
* Every :hover rule sits in the one `(hover:hover) and (pointer:fine)` block at
|
|
10439
|
+
* the end, so a tap on a touch screen does not leave a stuck hover colour;
|
|
10440
|
+
* :active stays outside it, since a press needs feedback on touch too. Folding
|
|
10441
|
+
* a group or an account card stays instant: it happens too often to be worth a
|
|
10442
|
+
* wait, and a hidden body keeps its `aria-controls` target mounted.
|
|
10239
10443
|
*
|
|
10240
10444
|
* Shapes follow one rule — clickable things are pills, fillable things are
|
|
10241
10445
|
* 8px. Buttons are drawn here (PageButton → cc-btn: md 32px, sm 28px, lg 36px
|
|
@@ -10249,27 +10453,61 @@ window.__ModuleLoader__.load({
|
|
|
10249
10453
|
* shares cc-hint / cc-badgeMuted / cc-input and keeps their colours.
|
|
10250
10454
|
*/
|
|
10251
10455
|
const PAGE_CSS = `
|
|
10252
|
-
.cc-section{max-width:720px;color:var(--dsw-alias-label-primary);flex-direction:column;display:flex;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}
|
|
10253
|
-
.cc-title{margin:0;color:var(--dsw-alias-label-primary);font-size:
|
|
10456
|
+
.cc-section{max-width:720px;color:var(--dsw-alias-label-primary);flex-direction:column;display:flex;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;--cc-ease-out:cubic-bezier(.23,1,.32,1)}
|
|
10457
|
+
.cc-title{margin:0;color:var(--dsw-alias-label-primary);font-size:22px;font-weight:700;line-height:28px}
|
|
10254
10458
|
.cc-intro{margin:4px 0 0;color:var(--dsw-alias-label-secondary);font-size:14px;line-height:22px}
|
|
10459
|
+
/* The glanceable header strip: four cells (account health, monthly remainder,
|
|
10460
|
+
* service status, model scope) on one card. Labels reuse cc-statusLabel and
|
|
10461
|
+
* dots reuse cc-tabDot, so no new colour language is introduced; the strip
|
|
10462
|
+
* folds to 2 × 2 in a narrow panel. */
|
|
10463
|
+
.cc-statusStrip{grid-template-columns:repeat(4,minmax(0,1fr));gap:0;display:grid;margin-top:16px;border:1px solid var(--dsw-alias-border-l2);border-radius:16px;background:var(--dsw-alias-bg-layer-1);box-shadow:0 1px 2px rgba(0,0,0,.04)}
|
|
10464
|
+
.cc-statusCell{flex-direction:column;gap:2px;min-width:0;display:flex;padding:14px 16px}
|
|
10465
|
+
.cc-statusCell+.cc-statusCell{border-left:1px solid var(--dsw-alias-border-l2)}
|
|
10466
|
+
.cc-statusHead{align-items:center;gap:6px;display:flex}
|
|
10467
|
+
.cc-statusStripValue{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:22px}
|
|
10468
|
+
.cc-statusSub{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px}
|
|
10469
|
+
@media (max-width:560px){.cc-statusStrip{grid-template-columns:repeat(2,minmax(0,1fr))}.cc-statusCell:nth-child(3){border-left:0}.cc-statusCell:nth-child(n+3){border-top:1px solid var(--dsw-alias-border-l2)}}
|
|
10255
10470
|
.cc-spacer{flex:1}
|
|
10256
|
-
|
|
10257
|
-
|
|
10258
|
-
|
|
10259
|
-
.
|
|
10260
|
-
.cc-
|
|
10471
|
+
/* Groups are cards now (2026-10-29 redesign): a 16px-radius raised surface
|
|
10472
|
+
* with a border-l2 stroke, 16px between cards, no divider rules. The entrance
|
|
10473
|
+
* is a @starting-style rise with a 40ms stagger per card — transitions, not
|
|
10474
|
+
* keyframes, so a re-render never replays it. */
|
|
10475
|
+
.cc-group{flex-direction:column;display:flex;margin-top:16px;border:1px solid var(--dsw-alias-border-l2);border-radius:16px;background:var(--dsw-alias-bg-layer-1);box-shadow:0 1px 2px rgba(0,0,0,.04);transition:opacity 320ms var(--cc-ease-out),transform 320ms var(--cc-ease-out)}
|
|
10476
|
+
@starting-style{.cc-group{opacity:0;transform:translateY(8px)}}
|
|
10477
|
+
.cc-section>section:nth-of-type(1){transition-delay:0ms}
|
|
10478
|
+
.cc-section>section:nth-of-type(2){transition-delay:40ms}
|
|
10479
|
+
.cc-section>section:nth-of-type(3){transition-delay:80ms}
|
|
10480
|
+
.cc-section>section:nth-of-type(4){transition-delay:120ms}
|
|
10481
|
+
.cc-section>section:nth-of-type(5){transition-delay:160ms}
|
|
10482
|
+
.cc-groupHead{align-items:center;gap:8px;display:flex;min-height:52px;padding:0 16px}
|
|
10483
|
+
.cc-groupTitle{margin:0;color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:22px}
|
|
10261
10484
|
.cc-groupDesc{margin:2px 0 0;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px}
|
|
10262
|
-
.cc-rows>.cc-groupDesc{margin:0}
|
|
10485
|
+
.cc-rows>.cc-groupDesc{margin:0 16px}
|
|
10486
|
+
/* A block straight under a group's description (the account list, its
|
|
10487
|
+
* notices, the override table) keeps the same 12px as the account-mode line
|
|
10488
|
+
* and the actions row inside the card; a setting row brings its own 12px
|
|
10489
|
+
* padding instead. */
|
|
10490
|
+
.cc-rows>.cc-groupDesc+.cc-notices,.cc-rows>.cc-groupDesc+.cc-accountList,.cc-rows>.cc-groupDesc+.cc-contextTable{margin-top:12px}
|
|
10263
10491
|
.cc-groupHeading{flex:1;min-width:0;margin:0;font:inherit;display:flex}
|
|
10264
10492
|
/* The whole heading row is the fold toggle: chevron, title, summary badges. */
|
|
10265
|
-
.cc-disclosure{box-sizing:border-box;flex-wrap:wrap;flex:1;align-items:center;gap:6px 8px;min-width:0;min-height:
|
|
10266
|
-
.cc-foldChevron{color:var(--dsw-alias-label-tertiary);transition:transform .15s ease}
|
|
10493
|
+
.cc-disclosure{box-sizing:border-box;flex-wrap:wrap;flex:1;align-items:center;gap:6px 8px;min-width:0;min-height:52px;padding:0;border:0;background:0 0;color:inherit;font:inherit;text-align:left;cursor:pointer;border-radius:8px;display:flex}
|
|
10494
|
+
.cc-foldChevron{color:var(--dsw-alias-label-tertiary);transition:transform .16s var(--cc-ease-out),color .15s ease}
|
|
10267
10495
|
.cc-foldChevronClosed{transform:rotate(-90deg)}
|
|
10496
|
+
/* Folding stays instant (2026-10-29): the body stays mounted and is hidden with
|
|
10497
|
+
* the hidden attribute, so aria-controls always points at a real element
|
|
10498
|
+
* and the save bar's locate action can focus an invalid field right after
|
|
10499
|
+
* opening its group. */
|
|
10268
10500
|
.cc-groupBody{flex-direction:column;display:flex;padding-top:4px}
|
|
10269
10501
|
.cc-groupBody[hidden]{display:none}
|
|
10270
10502
|
.cc-disclosure:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}
|
|
10271
|
-
.cc-rows{flex-direction:column;display:flex}
|
|
10272
|
-
|
|
10503
|
+
.cc-rows{flex-direction:column;display:flex;padding-top:4px}
|
|
10504
|
+
/* Card inset by default: every direct body child sits 16px off the card edge,
|
|
10505
|
+
* and only rows opt out — they carry their own 16px side padding so their
|
|
10506
|
+
* hairlines run the card's full width. */
|
|
10507
|
+
.cc-rows>*{margin-left:16px;margin-right:16px}
|
|
10508
|
+
.cc-rows>.cc-row{margin-left:0;margin-right:0}
|
|
10509
|
+
.cc-row{flex-wrap:wrap;align-items:center;gap:8px 24px;display:flex;padding:14px 16px}
|
|
10510
|
+
.cc-rows>.cc-row+.cc-row{border-top:.5px solid var(--dsw-alias-border-l2)}
|
|
10273
10511
|
.cc-rowFlush{padding:0}
|
|
10274
10512
|
/* The text column starts at 200px (not 300px) so the widest controls — the
|
|
10275
10513
|
* 280px API base input, the allowlist's button + 200px selector — still sit
|
|
@@ -10288,16 +10526,14 @@ window.__ModuleLoader__.load({
|
|
|
10288
10526
|
/* The row-level field, drawn to the design canvas: 32px, 8px corners, a 1px
|
|
10289
10527
|
* border-l3 stroke on the raised surface. A number field draws its unit inside
|
|
10290
10528
|
* the same box (cc-unitBox), right of the value. */
|
|
10291
|
-
.cc-fieldInput{box-sizing:border-box;flex:none;height:32px;min-width:0;padding:0 10px;border:1px solid var(--dsw-alias-border-l3);border-radius:8px;background:var(--dsw-alias-bg-layer-1);font:inherit;color:var(--dsw-alias-label-primary);font-size:14px;line-height:22px}
|
|
10529
|
+
.cc-fieldInput{box-sizing:border-box;flex:none;height:32px;min-width:0;padding:0 10px;border:1px solid var(--dsw-alias-border-l3);border-radius:8px;background:var(--dsw-alias-bg-layer-1);font:inherit;color:var(--dsw-alias-label-primary);font-size:14px;line-height:22px;transition:border-color .15s ease}
|
|
10292
10530
|
.cc-fieldInput::placeholder{color:var(--dsw-alias-label-dimmed)}
|
|
10293
|
-
.cc-fieldInput:hover:not(:disabled){border-color:var(--dsw-alias-label-tertiary)}
|
|
10294
10531
|
.cc-fieldInput:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}
|
|
10295
10532
|
.cc-fieldInput:disabled{opacity:.5;cursor:default}
|
|
10296
|
-
.cc-fieldInputInvalid
|
|
10297
|
-
.cc-unitBox{box-sizing:border-box;flex:none;align-items:center;display:inline-flex;width:160px;height:32px;border:1px solid var(--dsw-alias-border-l3);border-radius:8px;background:var(--dsw-alias-bg-layer-1);overflow:hidden}
|
|
10298
|
-
.cc-unitBox:hover{border-color:var(--dsw-alias-label-tertiary)}
|
|
10533
|
+
.cc-fieldInputInvalid{border-color:var(--dsw-alias-state-error-primary)}
|
|
10534
|
+
.cc-unitBox{box-sizing:border-box;flex:none;align-items:center;display:inline-flex;width:160px;height:32px;border:1px solid var(--dsw-alias-border-l3);border-radius:8px;background:var(--dsw-alias-bg-layer-1);overflow:hidden;transition:border-color .15s ease}
|
|
10299
10535
|
.cc-unitBox:focus-within{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}
|
|
10300
|
-
.cc-unitBoxInvalid
|
|
10536
|
+
.cc-unitBoxInvalid{border-color:var(--dsw-alias-state-error-primary)}
|
|
10301
10537
|
.cc-unitBox:has(>.cc-fieldInput:disabled){opacity:.5}
|
|
10302
10538
|
.cc-unitBox>.cc-fieldInput{flex:1;width:auto;height:30px;border:0;border-radius:0;background:0 0;outline:none;text-align:right;font-variant-numeric:tabular-nums}
|
|
10303
10539
|
.cc-unit{flex:none;padding:0 10px 0 2px;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;white-space:nowrap}
|
|
@@ -10305,8 +10541,8 @@ window.__ModuleLoader__.load({
|
|
|
10305
10541
|
* round reset button. Reset lives here rather than after the control, so no
|
|
10306
10542
|
* row reserves a reset column and the page keeps one right edge. */
|
|
10307
10543
|
.cc-overrideTag{align-items:center;gap:4px;display:inline-flex}
|
|
10308
|
-
.cc-resetInline{flex:none;align-items:center;justify-content:center;display:inline-flex;width:20px;height:20px;padding:0;border:0;border-radius:999px;corner-shape:round;background:0 0;color:var(--dsw-alias-label-tertiary);cursor:pointer}
|
|
10309
|
-
.cc-resetInline:
|
|
10544
|
+
.cc-resetInline{flex:none;align-items:center;justify-content:center;display:inline-flex;width:20px;height:20px;padding:0;border:0;border-radius:999px;corner-shape:round;background:0 0;color:var(--dsw-alias-label-tertiary);cursor:pointer;transition:transform .16s var(--cc-ease-out),background-color .15s ease,color .15s ease}
|
|
10545
|
+
.cc-resetInline:active:not(:disabled){transform:scale(.95)}
|
|
10310
10546
|
.cc-resetInline:disabled{cursor:default;opacity:.4}
|
|
10311
10547
|
.cc-resetInline:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}
|
|
10312
10548
|
.cc-icon{flex:none;width:16px;height:16px}
|
|
@@ -10314,18 +10550,15 @@ window.__ModuleLoader__.load({
|
|
|
10314
10550
|
* shape, md 32px / 14px text / 14px icon, sm 28px / 12px text, lg 36px for
|
|
10315
10551
|
* the save bar; primary is the brand fill, secondary the quiet grey fill,
|
|
10316
10552
|
* ghost label-secondary text, danger the error fill. */
|
|
10317
|
-
.cc-btn{box-sizing:border-box;flex:none;align-items:center;justify-content:center;gap:6px;display:inline-flex;height:32px;padding:0 14px;border:0;border-radius:999px;corner-shape:round;font:inherit;font-size:14px;line-height:22px;white-space:nowrap;cursor:pointer;text-decoration:none}
|
|
10553
|
+
.cc-btn{box-sizing:border-box;flex:none;align-items:center;justify-content:center;gap:6px;display:inline-flex;height:32px;padding:0 14px;border:0;border-radius:999px;corner-shape:round;font:inherit;font-size:14px;line-height:22px;white-space:nowrap;cursor:pointer;text-decoration:none;transition:transform .16s var(--cc-ease-out),background-color .15s ease,color .15s ease}
|
|
10554
|
+
.cc-btn:active:not(:disabled){transform:scale(.97)}
|
|
10318
10555
|
.cc-btnSm{height:28px;padding:0 10px;gap:4px;font-size:12px;line-height:18px}
|
|
10319
10556
|
.cc-btnLg{height:36px;padding:0 16px}
|
|
10320
10557
|
.cc-btn .cc-btnIcon{width:14px;height:14px}
|
|
10321
10558
|
.cc-btnPrimary{background:var(--dsw-alias-button-primary-fill,var(--dsw-alias-brand-primary));color:var(--dsw-alias-label-primary-foreground,#fff)}
|
|
10322
|
-
.cc-btnPrimary:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover,var(--dsw-alias-button-primary-fill,var(--dsw-alias-brand-primary)))}
|
|
10323
10559
|
.cc-btnSecondary{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-primary)}
|
|
10324
|
-
.cc-btnSecondary:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}
|
|
10325
10560
|
.cc-btnGhost{background:0 0;color:var(--dsw-alias-label-secondary)}
|
|
10326
|
-
.cc-btnGhost:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}
|
|
10327
10561
|
.cc-btnDanger{background:var(--dsw-alias-state-error-primary);color:#fff}
|
|
10328
|
-
.cc-btnDanger:hover:not(:disabled){background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 88%,#000)}
|
|
10329
10562
|
.cc-btn:disabled{cursor:default;opacity:.4}
|
|
10330
10563
|
.cc-btn:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}
|
|
10331
10564
|
.cc-groupSummary{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:400;line-height:18px;white-space:nowrap}
|
|
@@ -10342,7 +10575,6 @@ window.__ModuleLoader__.load({
|
|
|
10342
10575
|
.cc-fieldHead{align-items:center;gap:8px;display:flex}
|
|
10343
10576
|
.cc-label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}
|
|
10344
10577
|
.cc-reset{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;line-height:18px}
|
|
10345
|
-
.cc-reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}
|
|
10346
10578
|
.cc-reset:disabled{cursor:default;opacity:.4}
|
|
10347
10579
|
.cc-input{box-sizing:border-box;height:32px;min-width:0;padding:0 10px;border:.5px solid var(--dsw-alias-border-l4);border-radius:8px;background:var(--dsw-alias-bg-layer-1);font:inherit;color:var(--dsw-alias-label-primary);font-size:14px;line-height:22px}
|
|
10348
10580
|
.cc-input::placeholder{color:var(--dsw-alias-label-dimmed)}
|
|
@@ -10351,8 +10583,7 @@ window.__ModuleLoader__.load({
|
|
|
10351
10583
|
.cc-inputInvalid{border-color:var(--dsw-alias-state-error-primary)}
|
|
10352
10584
|
.cc-hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}
|
|
10353
10585
|
.cc-modelSelectAnchor{flex-direction:column;align-items:stretch;gap:6px;display:inline-flex;width:200px}
|
|
10354
|
-
.cc-selector{box-sizing:border-box;justify-content:space-between;align-items:center;gap:8px;display:inline-flex;height:32px;padding:0 10px;border:1px solid var(--dsw-alias-border-l3);border-radius:8px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;font-size:14px;line-height:22px;text-align:left;cursor:pointer}
|
|
10355
|
-
.cc-selector:hover:not(:disabled){border-color:var(--dsw-alias-label-tertiary)}
|
|
10586
|
+
.cc-selector{box-sizing:border-box;justify-content:space-between;align-items:center;gap:8px;display:inline-flex;height:32px;padding:0 10px;border:1px solid var(--dsw-alias-border-l3);border-radius:8px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;font-size:14px;line-height:22px;text-align:left;cursor:pointer;transition:border-color .15s ease}
|
|
10356
10587
|
.cc-selector:disabled{cursor:default;opacity:.4}
|
|
10357
10588
|
.cc-selector:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}
|
|
10358
10589
|
.cc-selectorText{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
@@ -10375,27 +10606,28 @@ window.__ModuleLoader__.load({
|
|
|
10375
10606
|
.cc-check:checked::after{content:'';position:absolute;top:3px;left:5px;width:3px;height:7px;border:solid var(--dsw-alias-label-primary-foreground,#fff);border-width:0 1.5px 1.5px 0;transform:rotate(45deg)}
|
|
10376
10607
|
.cc-checkName{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
10377
10608
|
/* Modelled on the platform's own Switch primitive (ui-primitives
|
|
10378
|
-
* Switch.module.css): a
|
|
10379
|
-
*
|
|
10609
|
+
* Switch.module.css): a 40x24 capsule padding its track by 2px and sliding a
|
|
10610
|
+
* 20px thumb across the 36px content box. Three things are load-bearing.
|
|
10380
10611
|
* (1) corner-shape:round on BOTH track and thumb — the theme sets a global
|
|
10381
10612
|
* superellipse corner shape, which squares a capsule's ends off around a round
|
|
10382
10613
|
* thumb. (2) No literal colours: brand-primary INVERTS between the modes, so a
|
|
10383
10614
|
* hardcoded white thumb vanishes on the checked track in dark mode; the brand
|
|
10384
10615
|
* foreground is what the platform paints on that fill. (3) border-l3 as the
|
|
10385
|
-
* off track — border-l2 leaves it indistinguishable from the page.
|
|
10386
|
-
.
|
|
10616
|
+
* off track — border-l2 leaves it indistinguishable from the page. The track
|
|
10617
|
+
* and thumb share one duration so the switch reads as one object. */
|
|
10618
|
+
.cc-toggle{box-sizing:border-box;appearance:none;flex-shrink:0;width:40px;height:24px;margin:0;padding:2px;border:0;border-radius:12px;corner-shape:round;background:var(--dsw-alias-border-l3);cursor:pointer;position:relative;transition:background-color .12s ease}
|
|
10387
10619
|
.cc-toggle:checked{background:var(--dsw-alias-brand-primary)}
|
|
10388
|
-
.cc-toggle::after{content:'';display:block;width:
|
|
10620
|
+
.cc-toggle::after{content:'';display:block;width:20px;height:20px;border-radius:50%;corner-shape:round;background:var(--dsw-alias-label-primary-foreground);transition:transform .12s ease}
|
|
10389
10621
|
.cc-toggle:checked::after{transform:translateX(16px)}
|
|
10390
10622
|
.cc-toggle:not(:checked)::after{background:var(--dsw-alias-switch-thumb,var(--dsw-alias-label-primary-foreground))}
|
|
10391
10623
|
.cc-toggle:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}
|
|
10392
10624
|
.cc-toggle:disabled{cursor:default;opacity:.5}
|
|
10393
|
-
.cc-accountMode{align-items:center;gap:8px;display:flex;margin:12px 0;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px}
|
|
10625
|
+
.cc-accountMode{align-items:center;gap:8px;display:flex;margin:12px 16px 0;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px}
|
|
10394
10626
|
.cc-accountList{flex-direction:column;gap:8px;display:flex}
|
|
10395
|
-
.cc-accountList+.cc-panel{margin
|
|
10627
|
+
.cc-accountList+.cc-panel{margin:8px 16px 16px}
|
|
10396
10628
|
/* One row under the list: Add account (outline) leads, the analytics exit
|
|
10397
10629
|
* (ghost) trails; the exit's long label clamps instead of wrapping the row. */
|
|
10398
|
-
.cc-accountActions{flex-wrap:wrap;align-items:center;gap:8px;display:flex;margin
|
|
10630
|
+
.cc-accountActions{flex-wrap:wrap;align-items:center;gap:8px;display:flex;margin:12px 16px 16px}
|
|
10399
10631
|
.cc-analyticsEntry{max-width:100%;min-width:0}
|
|
10400
10632
|
.cc-analyticsEntryText{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
10401
10633
|
.cc-chevronOpen{transform:rotate(180deg)}
|
|
@@ -10406,14 +10638,14 @@ window.__ModuleLoader__.load({
|
|
|
10406
10638
|
.cc-accountToggle{flex-wrap:wrap;align-items:center;gap:6px 8px;display:flex;flex:1;min-width:0;min-height:44px;padding:0 0 0 16px;background:0 0;border:0;border-radius:12px;color:inherit;cursor:pointer;font:inherit;text-align:left}
|
|
10407
10639
|
.cc-accountToggle:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px;border-radius:4px}
|
|
10408
10640
|
.cc-accountName{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}
|
|
10409
|
-
.cc-iconButton{flex-shrink:0;align-items:center;justify-content:center;display:inline-flex;width:28px;height:28px;padding:0;background:0 0;border:0;border-radius:8px;color:var(--dsw-alias-label-tertiary);cursor:pointer}
|
|
10410
|
-
.cc-iconButton:
|
|
10641
|
+
.cc-iconButton{flex-shrink:0;align-items:center;justify-content:center;display:inline-flex;width:28px;height:28px;padding:0;background:0 0;border:0;border-radius:8px;color:var(--dsw-alias-label-tertiary);cursor:pointer;transition:transform .16s var(--cc-ease-out),background-color .15s ease,color .15s ease}
|
|
10642
|
+
.cc-iconButton:active:not(:disabled){transform:scale(.95)}
|
|
10411
10643
|
.cc-iconButton:disabled{cursor:default;opacity:.4}
|
|
10412
|
-
.cc-iconButton:focus-visible{outline:
|
|
10644
|
+
.cc-iconButton:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}
|
|
10413
10645
|
/* Per-model context-length overrides (issue #86) as the design's small table:
|
|
10414
10646
|
* a card holding a head row over a model / value / remove grid, a line
|
|
10415
10647
|
* between rows; a row's error wraps onto its own line across the grid. */
|
|
10416
|
-
.cc-contextTable{flex-direction:column;display:flex;margin:0
|
|
10648
|
+
.cc-contextTable{flex-direction:column;display:flex;margin:0 16px 16px;border:1px solid var(--dsw-alias-border-l2);border-radius:12px}
|
|
10417
10649
|
.cc-contextItem{grid-template-columns:minmax(0,1fr) 144px 28px;align-items:center;gap:8px;display:grid;padding:8px 8px 8px 12px}
|
|
10418
10650
|
.cc-contextItem+.cc-contextItem{border-top:1px solid var(--dsw-alias-border-l2)}
|
|
10419
10651
|
.cc-contextHead{grid-template-columns:minmax(0,1fr) 144px 28px;align-items:center;gap:8px;display:grid;height:32px;padding:0 8px 0 12px;border-bottom:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}
|
|
@@ -10433,7 +10665,10 @@ window.__ModuleLoader__.load({
|
|
|
10433
10665
|
.cc-miniMeter{align-items:center;gap:8px;display:inline-flex;font-size:12px;line-height:18px}
|
|
10434
10666
|
.cc-miniMeterLabel{color:var(--dsw-alias-label-secondary)}
|
|
10435
10667
|
.cc-miniMeterTrack{overflow:hidden;background:var(--dsw-alias-bg-module-platform);border-radius:999px;width:64px;height:4px}
|
|
10436
|
-
|
|
10668
|
+
/* The fill is always full width and slides in from the left under the track's
|
|
10669
|
+
* overflow clip: animating transform skips layout, and unlike scaleX it keeps
|
|
10670
|
+
* the fill's rounded ends. */
|
|
10671
|
+
.cc-miniMeterFill{display:block;width:100%;background:var(--dsw-alias-brand-primary);border-radius:999px;height:100%;transition:transform .24s var(--cc-ease-out)}
|
|
10437
10672
|
.cc-miniMeterValue{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums}
|
|
10438
10673
|
.cc-accountDetails{container-type:inline-size;border-top:1px solid var(--dsw-alias-border-l2);flex-direction:column;gap:16px;display:flex;padding:16px}
|
|
10439
10674
|
.cc-inlineForm{flex-direction:column;gap:8px;display:flex}
|
|
@@ -10445,8 +10680,8 @@ window.__ModuleLoader__.load({
|
|
|
10445
10680
|
/* The add-account method switch, drawn as the design's segmented control: a
|
|
10446
10681
|
* pill track on the raised surface, the chosen segment in the brand fill. */
|
|
10447
10682
|
.cc-seg{align-self:flex-start;display:inline-flex;padding:2px;border-radius:999px;corner-shape:round;background:var(--dsw-alias-bg-layer-1);box-shadow:inset 0 0 0 1px var(--dsw-alias-border-l2)}
|
|
10448
|
-
.cc-segItem{height:28px;padding:0 12px;border:0;border-radius:999px;corner-shape:round;background:0 0;color:var(--dsw-alias-label-secondary);font:inherit;font-size:12px;line-height:18px;cursor:pointer}
|
|
10449
|
-
.cc-segItem:
|
|
10683
|
+
.cc-segItem{height:28px;padding:0 12px;border:0;border-radius:999px;corner-shape:round;background:0 0;color:var(--dsw-alias-label-secondary);font:inherit;font-size:12px;line-height:18px;cursor:pointer;transition:transform .16s var(--cc-ease-out),background-color .15s ease,color .15s ease}
|
|
10684
|
+
.cc-segItem:active:not(:disabled):not(.cc-segOn){transform:scale(.97)}
|
|
10450
10685
|
.cc-segItem:disabled{cursor:default;opacity:.4}
|
|
10451
10686
|
.cc-segItem:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}
|
|
10452
10687
|
.cc-segOn{background:var(--dsw-alias-button-primary-fill,var(--dsw-alias-brand-primary));color:var(--dsw-alias-label-primary-foreground,#fff)}
|
|
@@ -10463,13 +10698,14 @@ window.__ModuleLoader__.load({
|
|
|
10463
10698
|
.cc-usageStats{grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:8px;display:grid}
|
|
10464
10699
|
.cc-usageStatsActivity{grid-template-columns:repeat(4,minmax(0,1fr))}
|
|
10465
10700
|
@container (max-width:460px){.cc-usageStatsActivity{grid-template-columns:repeat(2,minmax(0,1fr))}}
|
|
10466
|
-
.cc-balance{flex-
|
|
10467
|
-
|
|
10701
|
+
.cc-balance{flex-direction:column;gap:2px;display:flex}
|
|
10702
|
+
/* 两行左右分栏:标签行左账期、右更新时间;数值行左剩余、右已购与赠送。 */
|
|
10703
|
+
.cc-balanceRow{flex-wrap:wrap;justify-content:space-between;align-items:baseline;gap:4px 16px;display:flex}
|
|
10468
10704
|
.cc-balanceLabel{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px}
|
|
10469
10705
|
.cc-balanceValue{color:var(--dsw-alias-label-primary);font-size:20px;font-weight:600;line-height:28px;font-variant-numeric:tabular-nums}
|
|
10470
10706
|
.cc-balanceSub{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px;font-variant-numeric:tabular-nums}
|
|
10471
10707
|
.cc-balanceEmpty .cc-balanceValue{color:var(--dsw-alias-state-error-primary)}
|
|
10472
|
-
.cc-balanceNote{
|
|
10708
|
+
.cc-balanceNote{margin:0;color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px}
|
|
10473
10709
|
.cc-usageStat{min-width:0;flex-direction:column;gap:2px;display:flex;padding:10px 12px;border-radius:8px;background:var(--dsw-alias-bg-module-platform)}
|
|
10474
10710
|
.cc-usageStatLabel{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px}
|
|
10475
10711
|
.cc-usageStatValue{color:var(--dsw-alias-label-primary);font-size:16px;font-weight:500;line-height:24px;font-variant-numeric:tabular-nums}
|
|
@@ -10481,19 +10717,22 @@ window.__ModuleLoader__.load({
|
|
|
10481
10717
|
.cc-usageWindowValue{color:var(--dsw-alias-label-primary);font-size:12px;line-height:18px;font-variant-numeric:tabular-nums}
|
|
10482
10718
|
.cc-usageExceeded{color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px}
|
|
10483
10719
|
.cc-usageBar{overflow:hidden;background:var(--dsw-alias-bg-module-platform);border-radius:999px;height:4px}
|
|
10484
|
-
|
|
10720
|
+
/* The fill is always full width and slides in from the left under the track's
|
|
10721
|
+
* overflow clip: animating transform skips layout, and unlike scaleX it keeps
|
|
10722
|
+
* the fill's rounded ends. */
|
|
10723
|
+
.cc-usageBarFill{width:100%;background:var(--dsw-alias-brand-primary);border-radius:999px;height:100%;transition:transform .24s var(--cc-ease-out)}
|
|
10485
10724
|
.cc-usageBarFillWarn{background:var(--dsw-alias-state-error-primary)}
|
|
10486
10725
|
.cc-usageWindowReset{color:var(--dsw-alias-label-secondary);margin:0;font-size:12px;line-height:18px}
|
|
10487
|
-
.cc-usageMeta{align-items:center;gap:8px;display:flex}
|
|
10488
|
-
.cc-usageMetaSpacer{flex:1}
|
|
10489
|
-
.cc-usageUpdated{color:var(--dsw-alias-label-secondary);margin:0;font-size:12px;line-height:18px}
|
|
10490
10726
|
.cc-usagePartial{color:var(--dsw-alias-state-error-primary);margin:0;font-size:12px;line-height:18px}
|
|
10491
10727
|
.cc-notices{flex-direction:column;gap:8px;display:flex;margin-bottom:12px}
|
|
10492
10728
|
.cc-section>.cc-notice{margin-top:12px}
|
|
10493
10729
|
/* One block for every result, warning and blocked state: neutral (read-only,
|
|
10494
10730
|
* in progress), warn (partly done, needs a retry) and error (failed). The
|
|
10495
|
-
* icon and title take the tone; the body stays label-secondary for contrast.
|
|
10496
|
-
|
|
10731
|
+
* icon and title take the tone; the body stays label-secondary for contrast.
|
|
10732
|
+
* A notice settles in from 4px above as it first renders, so a result never
|
|
10733
|
+
* jolts the content below it; browsers without @starting-style just show it. */
|
|
10734
|
+
.cc-notice{box-sizing:border-box;align-items:flex-start;gap:10px;display:flex;padding:10px 12px;border-radius:12px;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px;transition:opacity .2s var(--cc-ease-out),transform .2s var(--cc-ease-out)}
|
|
10735
|
+
@starting-style{.cc-notice{opacity:0;transform:translateY(-4px)}}
|
|
10497
10736
|
.cc-noticeIcon{flex:none;width:16px;height:16px;margin-top:1px;color:var(--dsw-alias-label-tertiary)}
|
|
10498
10737
|
.cc-noticeBody{flex-direction:column;flex:1;gap:2px;min-width:0;display:flex;word-break:break-word}
|
|
10499
10738
|
.cc-noticeBody>p{margin:0}
|
|
@@ -10508,21 +10747,17 @@ window.__ModuleLoader__.load({
|
|
|
10508
10747
|
.cc-noticeError>.cc-noticeIcon,.cc-noticeError .cc-noticeTitle{color:var(--dsw-alias-state-error-primary)}
|
|
10509
10748
|
.cc-version{margin:32px 0 0;text-align:center;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}
|
|
10510
10749
|
.cc-versionLink{color:var(--dsw-alias-state-warn-label,var(--dsw-alias-label-secondary));text-decoration:none}
|
|
10511
|
-
.cc-versionLink:hover{color:var(--dsw-alias-label-primary);text-decoration:underline;text-underline-position:under}
|
|
10512
10750
|
.cc-providerCard{flex-direction:column;display:flex}
|
|
10513
10751
|
.cc-footer{justify-content:flex-end;align-items:center;gap:8px;display:flex}
|
|
10514
10752
|
.cc-failed{display:inline-block;max-width:260px;min-width:0;color:var(--dsw-alias-state-error-primary);margin:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px;line-height:18px}
|
|
10515
10753
|
.cc-loginLink{align-items:center;gap:4px;display:inline-flex;color:var(--dsw-alias-link,var(--dsw-alias-brand-primary));text-decoration:none;font-size:12px;line-height:18px}
|
|
10516
10754
|
.cc-loginLink>.cc-iconInline{width:12px;height:12px}
|
|
10517
|
-
.cc-loginLink:hover{text-decoration:underline;text-underline-position:under}
|
|
10518
10755
|
/* The status row's dot + verdict sit beside the row title; the primitive's own
|
|
10519
10756
|
* state tokens colour the dot, so only the neutral label and the failure note
|
|
10520
10757
|
* need styling here. */
|
|
10521
10758
|
.cc-statusBadge{align-items:center;gap:6px;display:inline-flex;white-space:nowrap}
|
|
10522
10759
|
.cc-statusLabel{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px}
|
|
10523
10760
|
.cc-statusNotice{color:var(--dsw-alias-state-warn-label,var(--dsw-alias-label-tertiary));margin:0;font-size:12px;line-height:18px}
|
|
10524
|
-
/* One muted line in the status description: the local-connectivity probe and
|
|
10525
|
-
* the publication-lag note both use it, so the dot and the text stay aligned. */
|
|
10526
10761
|
/* The service-status row's two sources: a key column (Official / This device)
|
|
10527
10762
|
* beside a value line that wraps under itself, never under the key. */
|
|
10528
10763
|
.cc-statusLines{grid-template-columns:auto minmax(0,1fr);align-items:baseline;gap:4px 12px;display:grid;margin:2px 0 0;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px}
|
|
@@ -10537,7 +10772,6 @@ window.__ModuleLoader__.load({
|
|
|
10537
10772
|
.cc-statusTitleLink{align-items:center;gap:4px;display:inline-flex;margin-left:auto}
|
|
10538
10773
|
.cc-statusBad{color:var(--dsw-alias-state-warn-label,var(--dsw-alias-state-warn-primary,#b45309))}
|
|
10539
10774
|
.cc-statusLink{color:var(--dsw-alias-link,var(--dsw-alias-brand-primary));text-decoration:none;font-size:12px;line-height:18px;white-space:nowrap}
|
|
10540
|
-
.cc-statusLink:hover{text-decoration:underline;text-underline-position:under}
|
|
10541
10775
|
.cc-loginDone{color:var(--dsw-alias-state-success-primary,var(--dsw-alias-label-secondary))}
|
|
10542
10776
|
.cc-loginError{color:var(--dsw-alias-state-error-primary)}
|
|
10543
10777
|
/* The floating save bar: a centered bar pinned to the bottom of the
|
|
@@ -10545,15 +10779,16 @@ window.__ModuleLoader__.load({
|
|
|
10545
10779
|
* page, so the bar overlays the scrolling content instead of taking a row of
|
|
10546
10780
|
* its own; while shown, the section reserves room at its end so the last line
|
|
10547
10781
|
* can scroll clear. Hidden, it sinks and fades, and visibility (switched after
|
|
10548
|
-
* the transition) takes its buttons out of the tab order.
|
|
10549
|
-
*
|
|
10782
|
+
* the transition) takes its buttons out of the tab order. It leaves faster than
|
|
10783
|
+
* it arrives and never overshoots: the bar shows on every edit. Its surface is
|
|
10784
|
+
* an elevated layer (bg-layer-1 over the settings panel's layer-2) with the
|
|
10550
10785
|
* platform's prominent elevation and no stroke, and its corners are concentric
|
|
10551
|
-
* with the capsule buttons inside it (36px pills inset by
|
|
10786
|
+
* with the capsule buttons inside it (36px pills inset by 6px in a 48px pill)
|
|
10552
10787
|
* so the gap around each button is even. */
|
|
10553
10788
|
.cc-sectionWithBar{padding-bottom:80px}
|
|
10554
10789
|
.cc-saveBarDock{position:sticky;bottom:0;z-index:2;height:0;pointer-events:none}
|
|
10555
|
-
.cc-saveBar{--cc-saveBar-tone:var(--dsw-alias-state-warn-primary,#d97706);position:absolute;left:50%;bottom:20px;box-sizing:border-box;width:max-content;max-width:calc(100% - 24px);align-items:center;gap:10px;display:flex;height:
|
|
10556
|
-
.cc-saveBarShown{opacity:1;visibility:visible;transform:translate(-50%,0);pointer-events:auto;transition:opacity .
|
|
10790
|
+
.cc-saveBar{--cc-saveBar-tone:var(--dsw-alias-state-warn-primary,#d97706);position:absolute;left:50%;bottom:20px;box-sizing:border-box;width:max-content;max-width:calc(100% - 24px);align-items:center;gap:10px;display:flex;height:48px;padding:6px 6px 6px 16px;border:0;border-radius:24px;corner-shape:round;background:var(--dsw-alias-bg-layer-1);box-shadow:var(--dsw-elevation-prominent,0 12px 32px -8px rgba(0,0,0,.24),0 2px 8px rgba(0,0,0,.08));opacity:0;visibility:hidden;transform:translate(-50%,12px);transition:opacity .16s var(--cc-ease-out),transform .16s var(--cc-ease-out),visibility 0s linear .16s}
|
|
10791
|
+
.cc-saveBarShown{opacity:1;visibility:visible;transform:translate(-50%,0);pointer-events:auto;transition:opacity .22s var(--cc-ease-out),transform .22s var(--cc-ease-out),visibility 0s}
|
|
10557
10792
|
.cc-saveBar-error{--cc-saveBar-tone:var(--dsw-alias-state-error-primary)}
|
|
10558
10793
|
.cc-saveBar-success{--cc-saveBar-tone:var(--dsw-alias-state-success-primary,#16a34a);padding-right:18px}
|
|
10559
10794
|
.cc-saveBarIcon{flex-shrink:0;align-items:center;justify-content:center;display:inline-flex;width:16px;height:16px;color:var(--cc-saveBar-tone)}
|
|
@@ -10561,7 +10796,36 @@ window.__ModuleLoader__.load({
|
|
|
10561
10796
|
.cc-saveBarText{min-width:0;margin:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-primary);font-size:14px;line-height:22px}
|
|
10562
10797
|
.cc-saveBar-error .cc-saveBarText{color:var(--dsw-alias-state-error-primary)}
|
|
10563
10798
|
.cc-saveBarActions{flex-shrink:0;align-items:center;gap:4px;display:flex;margin-left:8px}
|
|
10564
|
-
|
|
10799
|
+
/* Hover only where a real pointer hovers. Order matters inside: the invalid
|
|
10800
|
+
* borders come after the plain hover borders at equal specificity, so an
|
|
10801
|
+
* invalid field keeps its error border under the pointer. */
|
|
10802
|
+
@media (hover:hover) and (pointer:fine){
|
|
10803
|
+
.cc-fieldInput:hover:not(:disabled),.cc-unitBox:hover,.cc-selector:hover:not(:disabled){border-color:var(--dsw-alias-label-tertiary)}
|
|
10804
|
+
.cc-fieldInputInvalid:hover:not(:disabled),.cc-unitBoxInvalid:hover{border-color:var(--dsw-alias-state-error-primary)}
|
|
10805
|
+
.cc-resetInline:hover:not(:disabled),.cc-iconButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}
|
|
10806
|
+
.cc-btnPrimary:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover,var(--dsw-alias-button-primary-fill,var(--dsw-alias-brand-primary)))}
|
|
10807
|
+
.cc-btnSecondary:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}
|
|
10808
|
+
.cc-btnGhost:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}
|
|
10809
|
+
.cc-btnDanger:hover:not(:disabled){background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 88%,#000)}
|
|
10810
|
+
.cc-segItem:hover:not(:disabled):not(.cc-segOn),.cc-reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}
|
|
10811
|
+
/* A whole-row toggle (group heading, account card head) shows it is one
|
|
10812
|
+
* target by lifting its chevron; a fill would weigh too much across the row. */
|
|
10813
|
+
.cc-disclosure:hover .cc-foldChevron,.cc-accountToggle:hover .cc-foldChevron{color:var(--dsw-alias-label-primary)}
|
|
10814
|
+
.cc-versionLink:hover{color:var(--dsw-alias-label-primary);text-decoration:underline;text-underline-position:under}
|
|
10815
|
+
.cc-loginLink:hover,.cc-statusLink:hover{text-decoration:underline;text-underline-position:under}
|
|
10816
|
+
}
|
|
10817
|
+
/* Reduced motion drops movement and scale but keeps the fades and colour
|
|
10818
|
+
* changes, which carry meaning without motion. Card entrance keeps its fade
|
|
10819
|
+
* (the transform is neutralised), and the fold is already instant. */
|
|
10820
|
+
@media (prefers-reduced-motion:reduce){
|
|
10821
|
+
.cc-foldChevron,.cc-toggle::after,.cc-usageBarFill,.cc-miniMeterFill{transition:none}
|
|
10822
|
+
.cc-btn,.cc-iconButton,.cc-resetInline,.cc-segItem{transition:background-color .15s ease,color .15s ease}
|
|
10823
|
+
.cc-btn:active:not(:disabled),.cc-iconButton:active:not(:disabled),.cc-resetInline:active:not(:disabled),.cc-segItem:active:not(:disabled):not(.cc-segOn){transform:none}
|
|
10824
|
+
.cc-notice{transition:opacity .2s ease}
|
|
10825
|
+
.cc-group{transition:opacity .2s ease;transform:none!important}
|
|
10826
|
+
.cc-saveBar{transition:opacity .16s ease,visibility 0s linear .16s}
|
|
10827
|
+
.cc-saveBarShown{transition:opacity .2s ease,visibility 0s}
|
|
10828
|
+
}
|
|
10565
10829
|
`;
|
|
10566
10830
|
//#endregion
|
|
10567
10831
|
//#region src/client/panel-styles.ts
|
|
@@ -11176,6 +11440,7 @@ window.__ModuleLoader__.load({
|
|
|
11176
11440
|
discard: () => controller.discard(),
|
|
11177
11441
|
refreshCredentials: () => void controller.refreshCredentials(),
|
|
11178
11442
|
refreshUsage: () => void usageController.refresh(),
|
|
11443
|
+
refreshUsageAuto: () => void usageController.refresh({ auto: true }),
|
|
11179
11444
|
...typeof ctx.get("layout")?.selectPanel === "function" ? { openAnalytics } : {},
|
|
11180
11445
|
beginLogin: (targetRef) => void loginController.begin(targetRef),
|
|
11181
11446
|
cancelLogin: () => void loginController.cancel(),
|