@mars-sea/dsh-commandcode-provider 0.13.2 → 0.13.4
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 +50 -0
- package/README.md +23 -7
- package/README.zh-CN.md +18 -5
- package/lib/client.js +570 -64
- package/lib/client.js.map +1 -1
- package/lib/index.d.ts +64 -17
- package/lib/index.js +237 -59
- package/lib/index.js.map +1 -1
- package/package.json +38 -38
package/lib/client.js
CHANGED
|
@@ -482,6 +482,28 @@ window.__ModuleLoader__.load({
|
|
|
482
482
|
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return {};
|
|
483
483
|
return Object.fromEntries(Object.entries(raw).filter(([id, value]) => id !== "" && typeof value === "boolean"));
|
|
484
484
|
}
|
|
485
|
+
/** 自定义上下文长度接受的上界(token);覆盖任何已知模型窗口仍留有余量。 */
|
|
486
|
+
const CONTEXT_WINDOW_MAX = 1e7;
|
|
487
|
+
/**
|
|
488
|
+
* 判定一个值能否作为自定义上下文长度:边界内的安全整数,符合宿主模型元数据约束。
|
|
489
|
+
*
|
|
490
|
+
* 程序化构造的连接可能绕过 schemastery 归一化,所以 `resolveAdapterOptions`
|
|
491
|
+
* 也要重新判定——越界或非数值直接丢弃,回落到目录公布值,而不是让请求带着
|
|
492
|
+
* 一个网关会拒绝的窗口上线。
|
|
493
|
+
*/
|
|
494
|
+
function isValidContextWindow(value) {
|
|
495
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 1 && value <= 1e7;
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* 只接收合法条目的按模型覆盖表;空表、非对象与非法数值一律视为未设置。
|
|
499
|
+
* 配置校验会拒绝非整数或越界值;本函数额外保护绕过配置校验的程序化调用,
|
|
500
|
+
* 丢弃坏条目,不把非法窗口交给宿主。返回新对象;未设置时为 `undefined`。
|
|
501
|
+
*/
|
|
502
|
+
function readContextWindowOverrides(raw) {
|
|
503
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
504
|
+
const entries = Object.entries(raw).filter((entry) => entry[0] !== "" && isValidContextWindow(entry[1]));
|
|
505
|
+
return entries.length === 0 ? void 0 : Object.fromEntries(entries);
|
|
506
|
+
}
|
|
485
507
|
//#endregion
|
|
486
508
|
//#region src/client/settings-write.ts
|
|
487
509
|
/** 设置写入过程:同一控制器的意图按顺序执行,配置确认与凭据确认分别保留。
|
|
@@ -579,6 +601,11 @@ window.__ModuleLoader__.load({
|
|
|
579
601
|
value: Object.fromEntries([...keys].map((id) => [id, list.length === 0 || list.includes(id)]))
|
|
580
602
|
});
|
|
581
603
|
}
|
|
604
|
+
if (owned.kind === "save" && owned.contextWindowOverrides !== void 0) ops.push({
|
|
605
|
+
op: "set",
|
|
606
|
+
path: ["contextWindowOverrides"],
|
|
607
|
+
value: owned.contextWindowOverrides
|
|
608
|
+
});
|
|
582
609
|
const baseline = new Map(ops.map((op) => [op.path[0], structuredClone(fieldBaseline(snapshot, op.path[0]))]));
|
|
583
610
|
const defaultRef = owned.kind === "save" && owned.key !== void 0 || owned.kind === "key" && owned.target === "default" ? this.defaultRef() : void 0;
|
|
584
611
|
return new Promise((resolve) => {
|
|
@@ -929,6 +956,50 @@ window.__ModuleLoader__.load({
|
|
|
929
956
|
};
|
|
930
957
|
}
|
|
931
958
|
/**
|
|
959
|
+
* The compact token-count shorthand (`32K`, `1M`) in BINARY units, the way
|
|
960
|
+
* context windows are quoted: 32K → 32 768 (32 × 1024), 128K → 131 072,
|
|
961
|
+
* 1M → 1 048 576 (1024 × 1024), so the common power-of-two windows land on
|
|
962
|
+
* their exact values instead of 1000-based rounding.
|
|
963
|
+
*
|
|
964
|
+
* Plain text uses the {@link numberField} parse plus an integer check;
|
|
965
|
+
* shorthand converts to whole tokens first. Empty-clears and bounds
|
|
966
|
+
* errors stay identical, and the converted value is bounds-checked against the same
|
|
967
|
+
* 1 – 10,000,000 range (the Host schema would reject an overshoot at save
|
|
968
|
+
* time with only a generic failure; naming `tooLarge` here says which).
|
|
969
|
+
* `Math.round` keeps fractional shorthand like `1.5K` on whole tokens.
|
|
970
|
+
*/
|
|
971
|
+
const COMPACT_TOKENS = /^(\d+(?:\.\d+)?)\s*([kKmM])$/;
|
|
972
|
+
function contextWindowField(field, bounds) {
|
|
973
|
+
const plain = numberField$1(field, bounds);
|
|
974
|
+
return {
|
|
975
|
+
field,
|
|
976
|
+
format: plain.format,
|
|
977
|
+
parse: (text) => {
|
|
978
|
+
const compact = COMPACT_TOKENS.exec(text.trim());
|
|
979
|
+
if (compact === null) {
|
|
980
|
+
const parsed = plain.parse(text);
|
|
981
|
+
return parsed.kind === "set" && !isValidContextWindow(parsed.value) ? {
|
|
982
|
+
kind: "invalid",
|
|
983
|
+
reason: "format"
|
|
984
|
+
} : parsed;
|
|
985
|
+
}
|
|
986
|
+
const value = Math.round(Number(compact[1]) * (compact[2].toLowerCase() === "k" ? 1024 : 1048576));
|
|
987
|
+
if (value < bounds.min) return {
|
|
988
|
+
kind: "invalid",
|
|
989
|
+
reason: "tooSmall"
|
|
990
|
+
};
|
|
991
|
+
if (value > bounds.max) return {
|
|
992
|
+
kind: "invalid",
|
|
993
|
+
reason: "tooLarge"
|
|
994
|
+
};
|
|
995
|
+
return {
|
|
996
|
+
kind: "set",
|
|
997
|
+
value
|
|
998
|
+
};
|
|
999
|
+
}
|
|
1000
|
+
};
|
|
1001
|
+
}
|
|
1002
|
+
/**
|
|
932
1003
|
* A boolean field, staged as the strings `'true'`/`'false'` (an empty draft
|
|
933
1004
|
* clears it). The component renders a toggle and only ever stages these two
|
|
934
1005
|
* strings; anything else blocks save.
|
|
@@ -1007,6 +1078,10 @@ window.__ModuleLoader__.load({
|
|
|
1007
1078
|
min: 0,
|
|
1008
1079
|
max: 50
|
|
1009
1080
|
}),
|
|
1081
|
+
contextWindowField("contextWindow", {
|
|
1082
|
+
min: 1,
|
|
1083
|
+
max: CONTEXT_WINDOW_MAX
|
|
1084
|
+
}),
|
|
1010
1085
|
booleanField$1("offloadSeenImagesForCache"),
|
|
1011
1086
|
booleanField$1("filterModelsByPlan"),
|
|
1012
1087
|
booleanField$1("webSearch"),
|
|
@@ -1034,6 +1109,8 @@ window.__ModuleLoader__.load({
|
|
|
1034
1109
|
accountResult;
|
|
1035
1110
|
/** Staged visible-model allowlist (undefined = no draft). */
|
|
1036
1111
|
visibleModelsDraft = void 0;
|
|
1112
|
+
/** Staged per-model context-length overrides as raw text (undefined = no draft, issue #86). */
|
|
1113
|
+
contextWindowOverridesDraft = void 0;
|
|
1037
1114
|
catalogModels = [];
|
|
1038
1115
|
catalogFailed = false;
|
|
1039
1116
|
saving = false;
|
|
@@ -1061,6 +1138,7 @@ window.__ModuleLoader__.load({
|
|
|
1061
1138
|
this.writer.dispose();
|
|
1062
1139
|
this.staged.clear();
|
|
1063
1140
|
this.visibleModelsDraft = void 0;
|
|
1141
|
+
this.contextWindowOverridesDraft = void 0;
|
|
1064
1142
|
for (const dispose of this.disposers) dispose();
|
|
1065
1143
|
this.disposers.length = 0;
|
|
1066
1144
|
this.listeners.clear();
|
|
@@ -1122,10 +1200,12 @@ window.__ModuleLoader__.load({
|
|
|
1122
1200
|
accountEnrollmentRefs: this.enrollmentRefs(false),
|
|
1123
1201
|
visibleModels: this.effectiveVisibleModels(),
|
|
1124
1202
|
visibleModelsAll: this.visibleModelsDraft === void 0 ? this.storedShowsAll() : this.visibleModelsDraft.length === 0,
|
|
1203
|
+
contextWindow: this.field("contextWindow"),
|
|
1204
|
+
contextWindowOverrides: this.effectiveContextWindowOverrides(),
|
|
1125
1205
|
catalogModels: this.catalogModels,
|
|
1126
1206
|
catalogFailed: this.catalogFailed,
|
|
1127
|
-
dirty: plan.length > 0 || this.visibleModelsDirty(),
|
|
1128
|
-
invalid: plan.some((item) => item.change === void 0),
|
|
1207
|
+
dirty: plan.length > 0 || this.visibleModelsDirty() || this.contextWindowOverridesDirty(),
|
|
1208
|
+
invalid: plan.some((item) => item.change === void 0) || this.effectiveContextWindowOverrides().some((row) => row.invalid),
|
|
1129
1209
|
saving: this.saving,
|
|
1130
1210
|
failed: this.failed,
|
|
1131
1211
|
savedCount: this.savedCount,
|
|
@@ -1173,9 +1253,10 @@ window.__ModuleLoader__.load({
|
|
|
1173
1253
|
}
|
|
1174
1254
|
/** Discard every staged edit. */
|
|
1175
1255
|
discard() {
|
|
1176
|
-
if (this.staged.size === 0 && this.visibleModelsDraft === void 0 && !this.failed) return;
|
|
1256
|
+
if (this.staged.size === 0 && this.visibleModelsDraft === void 0 && this.contextWindowOverridesDraft === void 0 && !this.failed) return;
|
|
1177
1257
|
this.staged.clear();
|
|
1178
1258
|
this.visibleModelsDraft = void 0;
|
|
1259
|
+
this.contextWindowOverridesDraft = void 0;
|
|
1179
1260
|
this.failed = false;
|
|
1180
1261
|
this.saveResult = void 0;
|
|
1181
1262
|
this.publish();
|
|
@@ -1202,9 +1283,12 @@ window.__ModuleLoader__.load({
|
|
|
1202
1283
|
if (this.disposed || this.saving) return;
|
|
1203
1284
|
const submitted = new Map(this.staged);
|
|
1204
1285
|
const submittedVisible = this.visibleModelsDraft;
|
|
1286
|
+
const submittedContextOverrides = this.contextWindowOverridesDraft;
|
|
1205
1287
|
const plan = this.plan();
|
|
1206
1288
|
const visibleDirty = this.visibleModelsDirty();
|
|
1207
|
-
|
|
1289
|
+
const contextOverrides = this.parsedContextWindowOverrides();
|
|
1290
|
+
const contextOverridesDirty = this.contextWindowOverridesDirty();
|
|
1291
|
+
if (plan.length === 0 && !visibleDirty && !contextOverridesDirty || plan.some((item) => item.change === void 0) || contextOverrides.invalid) return;
|
|
1208
1292
|
const ops = plan.flatMap((item) => typeof item.change === "object" ? [item.change] : []);
|
|
1209
1293
|
const key = plan.find((item) => item.field === "apiKey")?.change;
|
|
1210
1294
|
this.saving = true;
|
|
@@ -1214,6 +1298,7 @@ window.__ModuleLoader__.load({
|
|
|
1214
1298
|
kind: "save",
|
|
1215
1299
|
ops,
|
|
1216
1300
|
...visibleDirty ? { visibleModels: submittedVisible ?? [] } : {},
|
|
1301
|
+
...contextOverridesDirty ? { contextWindowOverrides: contextOverrides.values } : {},
|
|
1217
1302
|
...typeof key === "string" ? { key } : {}
|
|
1218
1303
|
});
|
|
1219
1304
|
this.publish();
|
|
@@ -1225,6 +1310,7 @@ window.__ModuleLoader__.load({
|
|
|
1225
1310
|
if (!this.failed) this.savedCount += 1;
|
|
1226
1311
|
for (const [field, draft] of submitted) if ((field === "apiKey" ? outcome.credential === "confirmed" && outcome.issue === void 0 : outcome.config === "confirmed" || writeCompleted(outcome)) && this.staged.get(field) === draft) this.staged.delete(field);
|
|
1227
1312
|
if ((outcome.config === "confirmed" || writeCompleted(outcome)) && this.visibleModelsDraft === submittedVisible) this.visibleModelsDraft = void 0;
|
|
1313
|
+
if ((outcome.config === "confirmed" || writeCompleted(outcome)) && this.contextWindowOverridesDraft === submittedContextOverrides) this.contextWindowOverridesDraft = void 0;
|
|
1228
1314
|
this.publish();
|
|
1229
1315
|
return outcome;
|
|
1230
1316
|
}
|
|
@@ -1242,6 +1328,63 @@ window.__ModuleLoader__.load({
|
|
|
1242
1328
|
this.saveResult = void 0;
|
|
1243
1329
|
this.publish();
|
|
1244
1330
|
}
|
|
1331
|
+
/**
|
|
1332
|
+
* Stage the whole per-model context-length map (issue #86): one entry per
|
|
1333
|
+
* row, the value the row's input shows verbatim (blank = drop the override
|
|
1334
|
+
* on save). The component rebuilds the map from its projected rows, so a
|
|
1335
|
+
* model reselect moves the value to its new key in the same edit.
|
|
1336
|
+
*/
|
|
1337
|
+
editContextWindowOverrides(next) {
|
|
1338
|
+
this.contextWindowOverridesDraft = { ...next };
|
|
1339
|
+
this.failed = false;
|
|
1340
|
+
this.saveResult = void 0;
|
|
1341
|
+
this.publish();
|
|
1342
|
+
}
|
|
1343
|
+
/** The stored per-model overrides, already cleaned by the shared validity rule. */
|
|
1344
|
+
storedContextWindowOverrides() {
|
|
1345
|
+
return { ...readContextWindowOverrides(this.sectionValue("contextWindowOverrides")) };
|
|
1346
|
+
}
|
|
1347
|
+
/** Effective rows: the staged draft verbatim, or the stored values formatted. */
|
|
1348
|
+
effectiveContextWindowOverrides() {
|
|
1349
|
+
const draft = this.contextWindowOverridesDraft;
|
|
1350
|
+
const source = draft !== void 0 ? draft : Object.fromEntries(Object.entries(this.storedContextWindowOverrides()).map(([model, value]) => [model, String(value)]));
|
|
1351
|
+
return Object.entries(source).filter(([model]) => model !== "").map(([model, text]) => {
|
|
1352
|
+
const parsed = this.spec("contextWindow").parse(text);
|
|
1353
|
+
return {
|
|
1354
|
+
model,
|
|
1355
|
+
text,
|
|
1356
|
+
invalid: parsed.kind === "invalid",
|
|
1357
|
+
invalidReason: parsed.kind === "invalid" ? parsed.reason : void 0
|
|
1358
|
+
};
|
|
1359
|
+
});
|
|
1360
|
+
}
|
|
1361
|
+
/**
|
|
1362
|
+
* The draft parsed for the write: blank rows and unparseable rows drop out
|
|
1363
|
+
* (an invalid row blocks save on its own), everything else is the value the
|
|
1364
|
+
* writer puts at `contextWindowOverrides`.
|
|
1365
|
+
*/
|
|
1366
|
+
parsedContextWindowOverrides() {
|
|
1367
|
+
const values = {};
|
|
1368
|
+
let invalid = false;
|
|
1369
|
+
for (const row of this.effectiveContextWindowOverrides()) {
|
|
1370
|
+
if (row.invalid) {
|
|
1371
|
+
invalid = true;
|
|
1372
|
+
continue;
|
|
1373
|
+
}
|
|
1374
|
+
const parsed = this.spec("contextWindow").parse(row.text);
|
|
1375
|
+
if (parsed.kind === "set" && typeof parsed.value === "number") values[row.model] = parsed.value;
|
|
1376
|
+
}
|
|
1377
|
+
return {
|
|
1378
|
+
values,
|
|
1379
|
+
invalid
|
|
1380
|
+
};
|
|
1381
|
+
}
|
|
1382
|
+
contextWindowOverridesDirty() {
|
|
1383
|
+
if (this.contextWindowOverridesDraft === void 0) return false;
|
|
1384
|
+
const values = this.parsedContextWindowOverrides().values;
|
|
1385
|
+
const stored = this.storedContextWindowOverrides();
|
|
1386
|
+
return [.../* @__PURE__ */ new Set([...Object.keys(values), ...Object.keys(stored)])].some((model) => values[model] !== stored[model]);
|
|
1387
|
+
}
|
|
1245
1388
|
renameAccount(ref, label) {
|
|
1246
1389
|
return this.runAccountOp("rename", {
|
|
1247
1390
|
kind: "rename",
|
|
@@ -1519,12 +1662,51 @@ window.__ModuleLoader__.load({
|
|
|
1519
1662
|
function formatMoneyExact(value) {
|
|
1520
1663
|
return `$${value.toFixed(4)}`;
|
|
1521
1664
|
}
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1665
|
+
/**
|
|
1666
|
+
* 紧凑数字:K/M/B 三级,一切档位决策的唯一实现。
|
|
1667
|
+
*
|
|
1668
|
+
* 档位必须**回头判断**:先除以档位再四舍五入会算出 `999_999 → "1000.0K"`、
|
|
1669
|
+
* `999_999_999 → "1000.0M"` 这类既超长、又落在并不存在的档位上的结果,
|
|
1670
|
+
* 因此每个档位算完后若已经够到下一档,就改用下一档重算一次。
|
|
1671
|
+
*
|
|
1672
|
+
* @param value - 非负计数或词元数。
|
|
1673
|
+
* @param keepZeroDecimal - 整数值是否保留一位小数。词元显示沿用 `3.0B` 的旧样式;
|
|
1674
|
+
* 计数显示去掉多余的 `.0`,见 {@link formatCompactCount}。
|
|
1675
|
+
* @returns 紧凑文本;不足 1000 时原样返回该整数。
|
|
1676
|
+
*/
|
|
1677
|
+
function compact(value, keepZeroDecimal) {
|
|
1678
|
+
const rounded = (scaled) => Math.round(scaled * 10) / 10;
|
|
1679
|
+
const text = (scaled) => {
|
|
1680
|
+
const fixed = rounded(scaled);
|
|
1681
|
+
return keepZeroDecimal ? fixed.toFixed(1) : String(fixed);
|
|
1682
|
+
};
|
|
1683
|
+
if (value >= 1e9) return `${text(value / 1e9)}B`;
|
|
1684
|
+
if (value >= 1e6) return rounded(value / 1e6) >= 1e3 ? `${text(value / 1e9)}B` : `${text(value / 1e6)}M`;
|
|
1685
|
+
if (value >= 1e3) return rounded(value / 1e3) >= 1e3 ? `${text(value / 1e6)}M` : `${text(value / 1e3)}K`;
|
|
1526
1686
|
return String(value);
|
|
1527
1687
|
}
|
|
1688
|
+
/**
|
|
1689
|
+
* 紧凑计数:请求数、成功/失败数这类会被窄卡片截断的整数。
|
|
1690
|
+
* `1234` → `1.2K`、`12000` → `12K`、`1500000` → `1.5M`,整数不带多余的 `.0`。
|
|
1691
|
+
*/
|
|
1692
|
+
function formatCompactCount(value) {
|
|
1693
|
+
return compact(value, false);
|
|
1694
|
+
}
|
|
1695
|
+
/** 紧凑词元数:与 {@link formatCompactCount} 共用档位,但整数值保留一位小数(`3.0B`)。 */
|
|
1696
|
+
function formatTokensCompact(value) {
|
|
1697
|
+
return compact(value, true);
|
|
1698
|
+
}
|
|
1699
|
+
/**
|
|
1700
|
+
* 精确值悬停提示:只在紧凑显示确实丢了信息时才返回文本。
|
|
1701
|
+
* 未换算时悬停弹出的文字与卡片上写的一模一样,没有意义,因此返回 `undefined` 让调用方
|
|
1702
|
+
* 整个省略 `title`,而不是挂一个内容重复的提示。
|
|
1703
|
+
*
|
|
1704
|
+
* @param parts - 标签与精确数值,按卡片上的显示顺序。
|
|
1705
|
+
* @returns 形如 `请求数 1234567 · 成功 1234567 · 失败 123` 的提示,或 `undefined`。
|
|
1706
|
+
*/
|
|
1707
|
+
function compactCountTitle(parts) {
|
|
1708
|
+
return parts.some(([, value]) => formatCompactCount(value) !== String(value)) ? parts.map(([label, value]) => `${label} ${value}`).join(" · ") : void 0;
|
|
1709
|
+
}
|
|
1528
1710
|
/** The input is already in percent units; callers add the percent sign. */
|
|
1529
1711
|
function formatSuccessRate(value) {
|
|
1530
1712
|
return String(Number(value.toFixed(2)));
|
|
@@ -2824,6 +3006,18 @@ window.__ModuleLoader__.load({
|
|
|
2824
3006
|
visibleModelsShowAll: "显示全部",
|
|
2825
3007
|
visibleModelsStaleHint: "有 {count} 个已选模型在目录中找不到了(可能已下架),不影响其他模型;可清理或保留。",
|
|
2826
3008
|
visibleModelsCleanStale: "清理失效({count})",
|
|
3009
|
+
contextWindow: "自定义上下文长度",
|
|
3010
|
+
contextWindowHint: "覆盖模型目录公布的上下文窗口(token):上下文计量、自动压缩时机与模型选择器的上下文标注都按此值计算,对未单独设置的模型生效。留空则跟随目录;支持 32K、1M 这样的简写(二进制换算:32K = 32 768,1M = 1 048 576)。",
|
|
3011
|
+
contextOverridesTitle: "按模型覆盖",
|
|
3012
|
+
contextOverridesHint: "为单个模型单独设置上下文长度,优先于上面的全局值;模型下拉只列出模型白名单中的可见模型(显示全部时即整个目录)。",
|
|
3013
|
+
contextOverrideAdd: "添加模型",
|
|
3014
|
+
contextOverrideRemove: "移除该模型的上下文覆盖",
|
|
3015
|
+
contextOverrideModel: "模型",
|
|
3016
|
+
contextOverrideValue: "上下文长度",
|
|
3017
|
+
contextOverrideNoCatalog: "模型目录尚未加载,暂时无法添加按模型覆盖。",
|
|
3018
|
+
contextWindowTooSmall: "上下文长度不能小于 1",
|
|
3019
|
+
contextWindowTooLarge: "上下文长度不能大于 10,000,000",
|
|
3020
|
+
contextWindowInvalid: "请输入有效的整数 token 数",
|
|
2827
3021
|
overridden: "已覆盖",
|
|
2828
3022
|
reset: "重置",
|
|
2829
3023
|
invalidNumber: "无效数字",
|
|
@@ -2949,7 +3143,7 @@ window.__ModuleLoader__.load({
|
|
|
2949
3143
|
modelPick: "选择模型…",
|
|
2950
3144
|
modelCount: "已选 {count} 个模型",
|
|
2951
3145
|
modelCatalogFailed: "模型目录获取失败,暂时无法选择模型;已保存的设置仍会生效。",
|
|
2952
|
-
modelsTitle: "
|
|
3146
|
+
modelsTitle: "模型与上下文",
|
|
2953
3147
|
privacyTitle: "隐私与安全",
|
|
2954
3148
|
integrationsTitle: "集成与显示"
|
|
2955
3149
|
};
|
|
@@ -3002,6 +3196,18 @@ window.__ModuleLoader__.load({
|
|
|
3002
3196
|
visibleModelsShowAll: "Show all",
|
|
3003
3197
|
visibleModelsStaleHint: "{count} selected model(s) are no longer in the catalog (possibly retired); other models are unaffected. Clean them up or keep them.",
|
|
3004
3198
|
visibleModelsCleanStale: "Clean stale ({count})",
|
|
3199
|
+
contextWindow: "Custom context length",
|
|
3200
|
+
contextWindowHint: "Overrides the context window published by the model catalog (in tokens): the context meter, auto-compaction timing and the model-picker annotation all follow it, for every model without a per-model override below. Empty follows the catalog; shorthand like 32K or 1M is accepted (binary units: 32K is 32,768, 1M is 1,048,576).",
|
|
3201
|
+
contextOverridesTitle: "Per-model overrides",
|
|
3202
|
+
contextOverridesHint: "Set a context length for one model; it wins over the global value above. The picker lists only the models kept by the visible-models allowlist (the whole catalog while showing all).",
|
|
3203
|
+
contextOverrideAdd: "Add model",
|
|
3204
|
+
contextOverrideRemove: "Remove this model’s context override",
|
|
3205
|
+
contextOverrideModel: "Model",
|
|
3206
|
+
contextOverrideValue: "Context length",
|
|
3207
|
+
contextOverrideNoCatalog: "The model catalog has not loaded yet; per-model overrides cannot be added right now.",
|
|
3208
|
+
contextWindowTooSmall: "Context length must be at least 1",
|
|
3209
|
+
contextWindowTooLarge: "Context length must not exceed 10,000,000",
|
|
3210
|
+
contextWindowInvalid: "Enter a valid whole-token count",
|
|
3005
3211
|
overridden: "Overridden",
|
|
3006
3212
|
reset: "Reset",
|
|
3007
3213
|
invalidNumber: "Invalid number",
|
|
@@ -3127,7 +3333,7 @@ window.__ModuleLoader__.load({
|
|
|
3127
3333
|
modelPick: "Choose models…",
|
|
3128
3334
|
modelCount: "{count} models selected",
|
|
3129
3335
|
modelCatalogFailed: "The model catalog could not be loaded; saved settings still apply.",
|
|
3130
|
-
modelsTitle: "Models",
|
|
3336
|
+
modelsTitle: "Models & context",
|
|
3131
3337
|
privacyTitle: "Privacy & security",
|
|
3132
3338
|
integrationsTitle: "Integrations & display"
|
|
3133
3339
|
};
|
|
@@ -3270,6 +3476,19 @@ window.__ModuleLoader__.load({
|
|
|
3270
3476
|
return selected.includes(modelId) ? selected.filter((value) => value !== modelId) : [...selected, modelId];
|
|
3271
3477
|
}
|
|
3272
3478
|
/**
|
|
3479
|
+
* The catalog rows a picker may offer under the visible-models allowlist, in
|
|
3480
|
+
* catalog order: the whole catalog while "show all" is active (the empty
|
|
3481
|
+
* allowlist means exactly that), otherwise only the allowlisted ids. The
|
|
3482
|
+
* per-model context override editor uses this so its dropdown agrees with the
|
|
3483
|
+
* whitelist on the same card stack — a staged whitelist edit narrows it
|
|
3484
|
+
* immediately, since both read the projected page state.
|
|
3485
|
+
*/
|
|
3486
|
+
function allowlistedModels(catalog, allowlist, showAll) {
|
|
3487
|
+
if (showAll) return [...catalog];
|
|
3488
|
+
const keep = new Set(allowlist);
|
|
3489
|
+
return catalog.filter((model) => keep.has(model.id));
|
|
3490
|
+
}
|
|
3491
|
+
/**
|
|
3273
3492
|
* Whether the catalog is trustworthy enough to call an unlisted selection
|
|
3274
3493
|
* "retired". FALSE while the first fetch is in flight, after a failure, and
|
|
3275
3494
|
* for a loaded-but-empty catalog — an empty list makes every selected id look
|
|
@@ -3291,7 +3510,7 @@ window.__ModuleLoader__.load({
|
|
|
3291
3510
|
}
|
|
3292
3511
|
//#endregion
|
|
3293
3512
|
//#region package.json
|
|
3294
|
-
var version = "0.13.
|
|
3513
|
+
var version = "0.13.4";
|
|
3295
3514
|
var repository = {
|
|
3296
3515
|
"type": "git",
|
|
3297
3516
|
"url": "git+https://github.com/Mars-Sea/dsh-commandcode-provider.git"
|
|
@@ -3474,11 +3693,12 @@ window.__ModuleLoader__.load({
|
|
|
3474
3693
|
*
|
|
3475
3694
|
* Layout, top to bottom: Accounts (one card per account, status, quota,
|
|
3476
3695
|
* actions and dedicated models — every operation commits immediately), Models
|
|
3477
|
-
* (plan filter + visible-model allowlist
|
|
3478
|
-
*
|
|
3479
|
-
*
|
|
3480
|
-
*
|
|
3481
|
-
*
|
|
3696
|
+
* & context (plan filter + visible-model allowlist, then the context-length
|
|
3697
|
+
* rows: the global custom window and the per-model overrides, issue #86),
|
|
3698
|
+
* Privacy & security (ZDR), Integrations & display (web search, sidebar quota
|
|
3699
|
+
* card), and a collapsed Advanced card (API base + network limits). Everything
|
|
3700
|
+
* below Accounts is a flat group of rows (title/description left, control
|
|
3701
|
+
* right) inside the staged form the floating save bar writes.
|
|
3482
3702
|
*
|
|
3483
3703
|
* Styles are injected once by the client entry (see src/client/index.ts) and
|
|
3484
3704
|
* class-prefixed `cc-` to stay local.
|
|
@@ -3549,7 +3769,7 @@ window.__ModuleLoader__.load({
|
|
|
3549
3769
|
}) : null;
|
|
3550
3770
|
}
|
|
3551
3771
|
/** One text/number field row. */
|
|
3552
|
-
function Field({ id, label, hint, state, disabled, numeric, decimal, wide, placeholder, onEdit, onReset, t }) {
|
|
3772
|
+
function Field({ id, label, hint, state, disabled, numeric, decimal, context, wide, placeholder, onEdit, onReset, t }) {
|
|
3553
3773
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingRow, {
|
|
3554
3774
|
title: label,
|
|
3555
3775
|
titleFor: id,
|
|
@@ -3558,7 +3778,7 @@ window.__ModuleLoader__.load({
|
|
|
3558
3778
|
t
|
|
3559
3779
|
}),
|
|
3560
3780
|
description: hint,
|
|
3561
|
-
error: state.invalid ? invalidCopy(state.invalidReason, t, decimal) : void 0,
|
|
3781
|
+
error: state.invalid ? invalidCopy(state.invalidReason, t, context === true ? "context" : decimal === true ? "duration" : "number") : void 0,
|
|
3562
3782
|
control: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(FieldOverride, {
|
|
3563
3783
|
label,
|
|
3564
3784
|
state,
|
|
@@ -3579,12 +3799,17 @@ window.__ModuleLoader__.load({
|
|
|
3579
3799
|
});
|
|
3580
3800
|
}
|
|
3581
3801
|
/** The per-field error copy for a staged draft's failure reason. */
|
|
3582
|
-
function invalidCopy(reason, t,
|
|
3583
|
-
if (duration) {
|
|
3802
|
+
function invalidCopy(reason, t, mode = "number") {
|
|
3803
|
+
if (mode === "duration") {
|
|
3584
3804
|
if (reason === "tooSmall") return t("durationTooSmall");
|
|
3585
3805
|
if (reason === "tooLarge") return t("durationTooLarge");
|
|
3586
3806
|
return t("durationInvalid");
|
|
3587
3807
|
}
|
|
3808
|
+
if (mode === "context") {
|
|
3809
|
+
if (reason === "tooSmall") return t("contextWindowTooSmall");
|
|
3810
|
+
if (reason === "tooLarge") return t("contextWindowTooLarge");
|
|
3811
|
+
return t("contextWindowInvalid");
|
|
3812
|
+
}
|
|
3588
3813
|
if (reason === "tooSmall") return t("numberTooSmall");
|
|
3589
3814
|
if (reason === "tooLarge") return t("numberTooLarge");
|
|
3590
3815
|
return t("invalidNumber");
|
|
@@ -3622,9 +3847,10 @@ window.__ModuleLoader__.load({
|
|
|
3622
3847
|
});
|
|
3623
3848
|
}
|
|
3624
3849
|
/** One stat tile in the account card's summary grid. */
|
|
3625
|
-
function UsageStat({ label, value, sub }) {
|
|
3850
|
+
function UsageStat({ label, value, sub, title }) {
|
|
3626
3851
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3627
3852
|
className: "cc-usageStat",
|
|
3853
|
+
title,
|
|
3628
3854
|
children: [
|
|
3629
3855
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3630
3856
|
className: "cc-usageStatLabel",
|
|
@@ -3757,8 +3983,13 @@ window.__ModuleLoader__.load({
|
|
|
3757
3983
|
children: [
|
|
3758
3984
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(UsageStat, {
|
|
3759
3985
|
label: t("usageRequests"),
|
|
3760
|
-
value:
|
|
3761
|
-
sub: `${t("usageCompleted")} ${report.usage.completedCount} / ${t("usageFailed")} ${report.usage.failedCount}
|
|
3986
|
+
value: formatCompactCount(report.usage.totalCount),
|
|
3987
|
+
sub: `${t("usageCompleted")} ${formatCompactCount(report.usage.completedCount)} / ${t("usageFailed")} ${formatCompactCount(report.usage.failedCount)}`,
|
|
3988
|
+
title: compactCountTitle([
|
|
3989
|
+
[t("usageRequests"), report.usage.totalCount],
|
|
3990
|
+
[t("usageCompleted"), report.usage.completedCount],
|
|
3991
|
+
[t("usageFailed"), report.usage.failedCount]
|
|
3992
|
+
])
|
|
3762
3993
|
}),
|
|
3763
3994
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(UsageStat, {
|
|
3764
3995
|
label: t("usageSuccessRate"),
|
|
@@ -4025,31 +4256,189 @@ window.__ModuleLoader__.load({
|
|
|
4025
4256
|
});
|
|
4026
4257
|
}
|
|
4027
4258
|
/**
|
|
4028
|
-
* Models: the plan filter and the allowlist sit together because both
|
|
4029
|
-
* what the model picker lists (the allowlist applies after the plan
|
|
4259
|
+
* Models & context: the plan filter and the allowlist sit together because both
|
|
4260
|
+
* decide what the model picker lists (the allowlist applies after the plan
|
|
4261
|
+
* filter), and the context-length fields follow them because they are the same
|
|
4262
|
+
* subject seen from the other side — the window each listed model advertises.
|
|
4263
|
+
* Those fields carry no heading or divider of their own: they are rows of this
|
|
4264
|
+
* group, not a section inside it.
|
|
4030
4265
|
*/
|
|
4031
|
-
function ModelsCard({ state, disabled, t, onEdit, onReset, onSelect, onClear }) {
|
|
4266
|
+
function ModelsCard({ state, disabled, t, onEdit, onReset, onSelect, onClear, onOverrides }) {
|
|
4032
4267
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(SettingsGroup, {
|
|
4033
4268
|
title: t("modelsTitle"),
|
|
4034
4269
|
divided: true,
|
|
4035
|
-
children: [
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4270
|
+
children: [
|
|
4271
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleField, {
|
|
4272
|
+
id: "cc-filter-models-by-plan",
|
|
4273
|
+
label: t("filterModelsByPlan"),
|
|
4274
|
+
hint: t("filterModelsByPlanHint"),
|
|
4275
|
+
state: state.filterModelsByPlan,
|
|
4276
|
+
disabled,
|
|
4277
|
+
defaultChecked: true,
|
|
4278
|
+
onEdit: (text) => onEdit("filterModelsByPlan", text),
|
|
4279
|
+
onReset: () => onReset("filterModelsByPlan"),
|
|
4280
|
+
t
|
|
4281
|
+
}),
|
|
4282
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(VisibleModelsRow, {
|
|
4283
|
+
t,
|
|
4284
|
+
state,
|
|
4285
|
+
disabled,
|
|
4286
|
+
onSelect,
|
|
4287
|
+
onClear
|
|
4288
|
+
}),
|
|
4289
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContextCard, {
|
|
4290
|
+
state,
|
|
4291
|
+
disabled,
|
|
4292
|
+
t,
|
|
4293
|
+
onEdit,
|
|
4294
|
+
onReset,
|
|
4295
|
+
onOverrides
|
|
4296
|
+
})
|
|
4297
|
+
]
|
|
4298
|
+
});
|
|
4299
|
+
}
|
|
4300
|
+
/**
|
|
4301
|
+
* Context length (issue #86): the global custom window plus one row per model
|
|
4302
|
+
* override, both staged into the same save as the other form fields. Rendered
|
|
4303
|
+
* as plain rows of the Models & context group — no heading, no divider of its
|
|
4304
|
+
* own: the setting belongs to the models that group already governs, and on the
|
|
4305
|
+
* real page a section inside the group read as visual noise. The advertised
|
|
4306
|
+
* window is what the harness meters context against and when it compacts, so a
|
|
4307
|
+
* lowered value compacts earlier and a raised one follows the model's real
|
|
4308
|
+
* capacity when the catalog under-reports it. Precedence: a per-model row, then
|
|
4309
|
+
* the global value, then the catalog.
|
|
4310
|
+
*
|
|
4311
|
+
* The rows live as raw text drafts: blank drops the override on save, an
|
|
4312
|
+
* unparseable one blocks the save with its own bounds copy, and the value
|
|
4313
|
+
* accepts the compact shorthand the picker prints (`256K`, `1M`). The model
|
|
4314
|
+
* select offers only allowlisted models the list does not already carry —
|
|
4315
|
+
* one override per model, matching what the Models card lets through — and
|
|
4316
|
+
* keeps a stored id the catalog no longer carries (or the whitelist just
|
|
4317
|
+
* dropped) as itself so an existing override never silently disappears.
|
|
4318
|
+
*/
|
|
4319
|
+
function ContextCard({ state, disabled, t, onEdit, onReset, onOverrides }) {
|
|
4320
|
+
const rows = state.contextWindowOverrides;
|
|
4321
|
+
const used = new Set(rows.map((row) => row.model));
|
|
4322
|
+
const available = allowlistedModels(state.catalogModels, state.visibleModels, state.visibleModelsAll).filter((model) => !used.has(model.id));
|
|
4323
|
+
const asMap = () => Object.fromEntries(rows.map((row) => [row.model, row.text]));
|
|
4324
|
+
const setText = (model, text) => onOverrides({
|
|
4325
|
+
...asMap(),
|
|
4326
|
+
[model]: text
|
|
4327
|
+
});
|
|
4328
|
+
const setModel = (from, to) => {
|
|
4329
|
+
if (to === "" || to === from || used.has(to)) return;
|
|
4330
|
+
const next = {};
|
|
4331
|
+
for (const row of rows) next[row.model === from ? to : row.model] = row.text;
|
|
4332
|
+
onOverrides(next);
|
|
4333
|
+
};
|
|
4334
|
+
const removeRow = (model) => {
|
|
4335
|
+
const next = asMap();
|
|
4336
|
+
delete next[model];
|
|
4337
|
+
onOverrides(next);
|
|
4338
|
+
};
|
|
4339
|
+
const addRow = () => {
|
|
4340
|
+
const model = available[0];
|
|
4341
|
+
if (model === void 0) return;
|
|
4342
|
+
onOverrides({
|
|
4343
|
+
...asMap(),
|
|
4344
|
+
[model.id]: ""
|
|
4345
|
+
});
|
|
4346
|
+
};
|
|
4347
|
+
const labelOf = (model) => {
|
|
4348
|
+
const entry = state.catalogModels.find((candidate) => candidate.id === model);
|
|
4349
|
+
return entry === void 0 ? `${model} · ${t("modelStale")}` : `${entry.name} (${model})`;
|
|
4350
|
+
};
|
|
4351
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
4352
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
4353
|
+
id: "cc-context-window",
|
|
4354
|
+
label: t("contextWindow"),
|
|
4355
|
+
hint: t("contextWindowHint"),
|
|
4356
|
+
state: state.contextWindow,
|
|
4040
4357
|
disabled,
|
|
4041
|
-
|
|
4042
|
-
onEdit: (text) => onEdit("
|
|
4043
|
-
onReset: () => onReset("
|
|
4358
|
+
context: true,
|
|
4359
|
+
onEdit: (text) => onEdit("contextWindow", text),
|
|
4360
|
+
onReset: () => onReset("contextWindow"),
|
|
4044
4361
|
t
|
|
4045
|
-
}),
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4362
|
+
}),
|
|
4363
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingRow, {
|
|
4364
|
+
title: t("contextOverridesTitle"),
|
|
4365
|
+
description: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("contextOverridesHint") }),
|
|
4366
|
+
control: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4367
|
+
type: "button",
|
|
4368
|
+
className: "cc-linkButton",
|
|
4369
|
+
disabled: disabled || available.length === 0,
|
|
4370
|
+
onClick: addRow,
|
|
4371
|
+
children: t("contextOverrideAdd")
|
|
4372
|
+
})
|
|
4373
|
+
}),
|
|
4374
|
+
state.catalogFailed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4375
|
+
className: "cc-groupDesc",
|
|
4376
|
+
children: t("contextOverrideNoCatalog")
|
|
4377
|
+
}) : null,
|
|
4378
|
+
rows.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4379
|
+
className: "cc-contextHead",
|
|
4380
|
+
children: [
|
|
4381
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4382
|
+
className: "cc-contextHeadModel",
|
|
4383
|
+
children: t("contextOverrideModel")
|
|
4384
|
+
}),
|
|
4385
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4386
|
+
className: "cc-contextHeadValue",
|
|
4387
|
+
children: t("contextOverrideValue")
|
|
4388
|
+
}),
|
|
4389
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4390
|
+
className: "cc-contextHeadGap",
|
|
4391
|
+
"aria-hidden": "true"
|
|
4392
|
+
})
|
|
4393
|
+
]
|
|
4394
|
+
}) : null,
|
|
4395
|
+
rows.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4396
|
+
className: "cc-contextItem",
|
|
4397
|
+
children: [
|
|
4398
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4399
|
+
className: "cc-contextSelect",
|
|
4400
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
4401
|
+
className: "cc-input cc-contextModel",
|
|
4402
|
+
value: row.model,
|
|
4403
|
+
"aria-label": t("contextOverrideModel"),
|
|
4404
|
+
disabled,
|
|
4405
|
+
onChange: (event) => setModel(row.model, event.target.value),
|
|
4406
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4407
|
+
value: row.model,
|
|
4408
|
+
children: labelOf(row.model)
|
|
4409
|
+
}), available.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4410
|
+
value: model.id,
|
|
4411
|
+
children: labelOf(model.id)
|
|
4412
|
+
}, model.id))]
|
|
4413
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4414
|
+
className: "cc-contextCaret",
|
|
4415
|
+
"aria-hidden": "true"
|
|
4416
|
+
})]
|
|
4417
|
+
}),
|
|
4418
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
4419
|
+
className: `cc-input cc-contextValue${row.invalid ? " cc-inputInvalid" : ""}`,
|
|
4420
|
+
type: "text",
|
|
4421
|
+
"aria-label": t("contextOverrideValue"),
|
|
4422
|
+
"aria-invalid": row.invalid || void 0,
|
|
4423
|
+
value: row.text,
|
|
4424
|
+
disabled,
|
|
4425
|
+
onChange: (event) => setText(row.model, event.target.value)
|
|
4426
|
+
}),
|
|
4427
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4428
|
+
type: "button",
|
|
4429
|
+
className: "cc-iconButton",
|
|
4430
|
+
"aria-label": t("contextOverrideRemove"),
|
|
4431
|
+
disabled,
|
|
4432
|
+
onClick: () => removeRow(row.model),
|
|
4433
|
+
children: "×"
|
|
4434
|
+
}),
|
|
4435
|
+
row.invalid ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4436
|
+
className: "cc-rowError cc-contextError",
|
|
4437
|
+
children: invalidCopy(row.invalidReason, t, "context")
|
|
4438
|
+
}) : null
|
|
4439
|
+
]
|
|
4440
|
+
}, row.model))
|
|
4441
|
+
] });
|
|
4053
4442
|
}
|
|
4054
4443
|
/**
|
|
4055
4444
|
* Privacy & security: the opt-in that changes where data goes. ZDR restricts
|
|
@@ -5279,7 +5668,8 @@ window.__ModuleLoader__.load({
|
|
|
5279
5668
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelsCard, {
|
|
5280
5669
|
...form,
|
|
5281
5670
|
onSelect: props.editVisibleModels,
|
|
5282
|
-
onClear: props.clearVisibleModels
|
|
5671
|
+
onClear: props.clearVisibleModels,
|
|
5672
|
+
onOverrides: props.editContextWindowOverrides
|
|
5283
5673
|
}),
|
|
5284
5674
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PrivacyCard, { ...form }),
|
|
5285
5675
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IntegrationsCard, { ...form }),
|
|
@@ -5608,11 +5998,22 @@ window.__ModuleLoader__.load({
|
|
|
5608
5998
|
analysisNoShare: "无占比",
|
|
5609
5999
|
analysisShareHint: "占全部已知成本小计",
|
|
5610
6000
|
analysisSimulationHeading: "相同工作量价格试算",
|
|
5611
|
-
analysisSimulationHint: "
|
|
6001
|
+
analysisSimulationHint: "差额以所选历史全部请求的当前价估算为基准;负数表示成本更低,仅比较完整覆盖的相同工作量。",
|
|
5612
6002
|
analysisSimulationCost: "试算成本",
|
|
5613
6003
|
analysisCoverage: "计价覆盖",
|
|
5614
6004
|
analysisUsd: "美元",
|
|
5615
6005
|
analysisTokenUnit: "词元",
|
|
6006
|
+
analysisScenarioMonthly: "折算 30 天",
|
|
6007
|
+
analysisDifference: "较历史基准",
|
|
6008
|
+
analysisBaseline: "历史成本基准",
|
|
6009
|
+
analysisZeroBaseline: "基准为零,不计算比例",
|
|
6010
|
+
analysisNeedRange: "请选择日期范围",
|
|
6011
|
+
analysisShortRange: "观察范围不足一天",
|
|
6012
|
+
analysisIncomplete: "历史读取不完整",
|
|
6013
|
+
analysisNoRequests: "没有请求记录",
|
|
6014
|
+
analysisScenarioExcluded: "存在超上下文请求",
|
|
6015
|
+
analysisScenarioUnpriced: "存在未知用量或价格",
|
|
6016
|
+
analysisBaselineUnpriced: "历史基准未全部计价",
|
|
5616
6017
|
analysisCost: "已知成本 · 美元",
|
|
5617
6018
|
analysisRequests: "模型请求",
|
|
5618
6019
|
analysisSessionCount: "会话数",
|
|
@@ -5650,6 +6051,7 @@ window.__ModuleLoader__.load({
|
|
|
5650
6051
|
analysisAssumption: "沿用原请求的词元与时段,对比不同模型的价格。换模型后的生成长度、缓存命中、重试和质量可能变化;不会调用模型或自动推荐。",
|
|
5651
6052
|
analysisContext: "上下文词元",
|
|
5652
6053
|
analysisCapabilities: "模型能力",
|
|
6054
|
+
analysisInputType: "输入类型",
|
|
5653
6055
|
analysisEfforts: "推理力度",
|
|
5654
6056
|
analysisExcluded: "超出上下文",
|
|
5655
6057
|
analysisUnknownPrice: "未知用量或价格",
|
|
@@ -5701,11 +6103,22 @@ window.__ModuleLoader__.load({
|
|
|
5701
6103
|
analysisNoShare: ANALYTICS_COPY_ZH.analysisNoShare,
|
|
5702
6104
|
analysisShareHint: "Share of the known cost subtotal",
|
|
5703
6105
|
analysisSimulationHeading: "Price simulation for the same workload",
|
|
5704
|
-
analysisSimulationHint: "
|
|
6106
|
+
analysisSimulationHint: "Differences use all selected recorded requests repriced at current rates as the baseline. Negative means lower cost; only complete coverage is compared.",
|
|
5705
6107
|
analysisSimulationCost: "Simulated cost",
|
|
5706
6108
|
analysisCoverage: "Priced coverage",
|
|
5707
6109
|
analysisUsd: "USD",
|
|
5708
6110
|
analysisTokenUnit: "tokens",
|
|
6111
|
+
analysisScenarioMonthly: "30-day estimate",
|
|
6112
|
+
analysisDifference: "vs. recorded baseline",
|
|
6113
|
+
analysisBaseline: "Recorded cost baseline",
|
|
6114
|
+
analysisZeroBaseline: ANALYTICS_COPY_ZH.analysisZeroBaseline,
|
|
6115
|
+
analysisNeedRange: ANALYTICS_COPY_ZH.analysisNeedRange,
|
|
6116
|
+
analysisShortRange: ANALYTICS_COPY_ZH.analysisShortRange,
|
|
6117
|
+
analysisIncomplete: ANALYTICS_COPY_ZH.analysisIncomplete,
|
|
6118
|
+
analysisNoRequests: ANALYTICS_COPY_ZH.analysisNoRequests,
|
|
6119
|
+
analysisScenarioExcluded: ANALYTICS_COPY_ZH.analysisScenarioExcluded,
|
|
6120
|
+
analysisScenarioUnpriced: ANALYTICS_COPY_ZH.analysisScenarioUnpriced,
|
|
6121
|
+
analysisBaselineUnpriced: ANALYTICS_COPY_ZH.analysisBaselineUnpriced,
|
|
5709
6122
|
analysisCost: "Known cost · USD",
|
|
5710
6123
|
analysisRequests: "Model requests",
|
|
5711
6124
|
analysisSessionCount: "Sessions",
|
|
@@ -5743,6 +6156,7 @@ window.__ModuleLoader__.load({
|
|
|
5743
6156
|
analysisAssumption: "Compares model prices using original tokens and request times. Output length, cache hits, retries and quality may change with another model. Makes no model calls or automatic recommendations.",
|
|
5744
6157
|
analysisContext: "Context tokens",
|
|
5745
6158
|
analysisCapabilities: "Model capabilities",
|
|
6159
|
+
analysisInputType: "Input type",
|
|
5746
6160
|
analysisEfforts: "Reasoning efforts",
|
|
5747
6161
|
analysisExcluded: "Over context limit",
|
|
5748
6162
|
analysisUnknownPrice: "Unknown usage or price",
|
|
@@ -6010,8 +6424,13 @@ window.__ModuleLoader__.load({
|
|
|
6010
6424
|
if (usage !== void 0) {
|
|
6011
6425
|
stats.push({
|
|
6012
6426
|
label: "requests",
|
|
6013
|
-
value:
|
|
6014
|
-
sub: `${usage.completedCount} ${t("completed")} / ${usage.failedCount} ${t("failed")}
|
|
6427
|
+
value: formatCompactCount(usage.totalCount),
|
|
6428
|
+
sub: `${formatCompactCount(usage.completedCount)} ${t("completed")} / ${formatCompactCount(usage.failedCount)} ${t("failed")}`,
|
|
6429
|
+
title: compactCountTitle([
|
|
6430
|
+
[t("requests"), usage.totalCount],
|
|
6431
|
+
[t("completed"), usage.completedCount],
|
|
6432
|
+
[t("failed"), usage.failedCount]
|
|
6433
|
+
])
|
|
6015
6434
|
});
|
|
6016
6435
|
stats.push({
|
|
6017
6436
|
label: "successRate",
|
|
@@ -6337,6 +6756,44 @@ window.__ModuleLoader__.load({
|
|
|
6337
6756
|
}]
|
|
6338
6757
|
};
|
|
6339
6758
|
//#endregion
|
|
6759
|
+
//#region src/client/analytics-comparison.ts
|
|
6760
|
+
function coverageReason(report, scenario) {
|
|
6761
|
+
if (report.status !== "ready") return "unavailable";
|
|
6762
|
+
if (report.truncated || report.unreadableSessions > 0 || report.skippedSessions > 0) return "partial";
|
|
6763
|
+
if (report.totals.requests === 0) return "empty";
|
|
6764
|
+
if (scenario.excludedRequests > 0) return "excluded";
|
|
6765
|
+
if (scenario.unknownRequests > 0 || scenario.pricedRequests !== report.totals.requests) return "coverage";
|
|
6766
|
+
}
|
|
6767
|
+
function compareScenario(report, scenario) {
|
|
6768
|
+
const reason = coverageReason(report, scenario);
|
|
6769
|
+
let monthly;
|
|
6770
|
+
if (reason) monthly = {
|
|
6771
|
+
value: null,
|
|
6772
|
+
reason
|
|
6773
|
+
};
|
|
6774
|
+
else if (report.query.mode !== "range") monthly = {
|
|
6775
|
+
value: null,
|
|
6776
|
+
reason: "recent"
|
|
6777
|
+
};
|
|
6778
|
+
else {
|
|
6779
|
+
const elapsed = Math.min(report.query.to, report.generatedAt) - report.query.from;
|
|
6780
|
+
monthly = elapsed >= 864e5 ? { value: scenario.cost * (2592e6 / elapsed) } : {
|
|
6781
|
+
value: null,
|
|
6782
|
+
reason: "short-range"
|
|
6783
|
+
};
|
|
6784
|
+
}
|
|
6785
|
+
const comparisonReason = reason ?? (report.totals.pricedRequests !== report.totals.requests ? "baseline" : void 0);
|
|
6786
|
+
const difference = comparisonReason ? {
|
|
6787
|
+
value: null,
|
|
6788
|
+
reason: comparisonReason
|
|
6789
|
+
} : { value: scenario.cost - report.totals.cost };
|
|
6790
|
+
return {
|
|
6791
|
+
monthly,
|
|
6792
|
+
difference,
|
|
6793
|
+
differencePercent: difference.value !== null && report.totals.cost > 0 ? difference.value / report.totals.cost * 100 : null
|
|
6794
|
+
};
|
|
6795
|
+
}
|
|
6796
|
+
//#endregion
|
|
6340
6797
|
//#region src/client/analytics-view.tsx
|
|
6341
6798
|
/** 只展示数字快照;筛选提交后才查询,页签、展开与模型筛选不触发历史读取。 */
|
|
6342
6799
|
function localDate(time) {
|
|
@@ -6587,7 +7044,19 @@ window.__ModuleLoader__.load({
|
|
|
6587
7044
|
})]
|
|
6588
7045
|
});
|
|
6589
7046
|
}
|
|
6590
|
-
|
|
7047
|
+
const estimateReasonKeys = {
|
|
7048
|
+
unavailable: "analysisUnavailable",
|
|
7049
|
+
empty: "analysisNoRequests",
|
|
7050
|
+
partial: "analysisIncomplete",
|
|
7051
|
+
excluded: "analysisScenarioExcluded",
|
|
7052
|
+
coverage: "analysisScenarioUnpriced",
|
|
7053
|
+
baseline: "analysisBaselineUnpriced",
|
|
7054
|
+
recent: "analysisNeedRange",
|
|
7055
|
+
"short-range": "analysisShortRange"
|
|
7056
|
+
};
|
|
7057
|
+
const signedMoney = (n) => `${n > 0 ? "+" : n < 0 ? "-" : ""}${money(Math.abs(n))}`;
|
|
7058
|
+
function ScenarioRow({ m, report, text }) {
|
|
7059
|
+
const { monthly, difference, differencePercent } = compareScenario(report, m);
|
|
6591
7060
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
|
|
6592
7061
|
className: "ccp-analysisModelRow",
|
|
6593
7062
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("summary", {
|
|
@@ -6606,17 +7075,17 @@ window.__ModuleLoader__.load({
|
|
|
6606
7075
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Metric, {
|
|
6607
7076
|
label: text("analysisSimulationCost"),
|
|
6608
7077
|
value: m.pricedRequests ? money(m.cost) : text("analysisMissing"),
|
|
6609
|
-
sub: text("
|
|
7078
|
+
sub: `${text("analysisCoverage")} ${m.pricedRequests}/${report.totals.requests}`
|
|
6610
7079
|
}),
|
|
6611
7080
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Metric, {
|
|
6612
|
-
label: text("
|
|
6613
|
-
value:
|
|
6614
|
-
sub:
|
|
7081
|
+
label: text("analysisScenarioMonthly"),
|
|
7082
|
+
value: monthly.value === null ? text("analysisMissing") : money(monthly.value),
|
|
7083
|
+
sub: monthly.value === null ? text(estimateReasonKeys[monthly.reason]) : text("analysisUsd")
|
|
6615
7084
|
}),
|
|
6616
7085
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Metric, {
|
|
6617
|
-
label: text("
|
|
6618
|
-
value:
|
|
6619
|
-
sub: text(
|
|
7086
|
+
label: text("analysisDifference"),
|
|
7087
|
+
value: difference.value === null ? text("analysisMissing") : signedMoney(difference.value),
|
|
7088
|
+
sub: difference.value === null ? text(estimateReasonKeys[difference.reason]) : differencePercent === null ? text("analysisZeroBaseline") : `${differencePercent > 0 ? "+" : ""}${differencePercent.toFixed(1)}%`
|
|
6620
7089
|
}),
|
|
6621
7090
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chevron, {})
|
|
6622
7091
|
]
|
|
@@ -6633,7 +7102,11 @@ window.__ModuleLoader__.load({
|
|
|
6633
7102
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DetailGroup, {
|
|
6634
7103
|
title: "analysisCapabilities",
|
|
6635
7104
|
text,
|
|
6636
|
-
values: [
|
|
7105
|
+
values: [
|
|
7106
|
+
["analysisContext", m.contextWindow === null ? text("analysisMissing") : `${digits(m.contextWindow)} ${text("analysisTokenUnit")}`],
|
|
7107
|
+
["analysisInputType", text(m.images ? "analysisImages" : "analysisTextOnly")],
|
|
7108
|
+
["analysisEfforts", m.efforts.length ? m.efforts.map((e) => effortName(e, text)).join(", ") : text("analysisNoEfforts")]
|
|
7109
|
+
]
|
|
6637
7110
|
})]
|
|
6638
7111
|
})]
|
|
6639
7112
|
});
|
|
@@ -6935,6 +7408,16 @@ window.__ModuleLoader__.load({
|
|
|
6935
7408
|
className: "ccp-analysisAssumption",
|
|
6936
7409
|
children: text("analysisAssumption")
|
|
6937
7410
|
}),
|
|
7411
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
7412
|
+
className: "ccp-analysisNote ccp-analysisBaseline",
|
|
7413
|
+
children: [
|
|
7414
|
+
text("analysisBaseline"),
|
|
7415
|
+
": ",
|
|
7416
|
+
m.pricedRequests === m.requests && m.requests > 0 && !report.truncated && !report.unreadableSessions && !report.skippedSessions ? money(m.cost) : text("analysisMissing"),
|
|
7417
|
+
" · ",
|
|
7418
|
+
text("analysisMonthlyHint")
|
|
7419
|
+
]
|
|
7420
|
+
}),
|
|
6938
7421
|
report.catalogUnavailable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6939
7422
|
className: "ccp-analysisMessage ccp-analysisWarning",
|
|
6940
7423
|
role: "status",
|
|
@@ -6976,12 +7459,13 @@ window.__ModuleLoader__.load({
|
|
|
6976
7459
|
children: [
|
|
6977
7460
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: text("analysisModel") }),
|
|
6978
7461
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: text("analysisSimulationCost") }),
|
|
6979
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: text("
|
|
6980
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: text("
|
|
7462
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: text("analysisScenarioMonthly") }),
|
|
7463
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: text("analysisDifference") }),
|
|
6981
7464
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {})
|
|
6982
7465
|
]
|
|
6983
7466
|
}), scenarios.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ScenarioRow, {
|
|
6984
7467
|
m: model,
|
|
7468
|
+
report,
|
|
6985
7469
|
text
|
|
6986
7470
|
}, model.id))]
|
|
6987
7471
|
}) : !report.catalogUnavailable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
@@ -7125,9 +7609,10 @@ window.__ModuleLoader__.load({
|
|
|
7125
7609
|
});
|
|
7126
7610
|
}
|
|
7127
7611
|
/** A labelled figure. */
|
|
7128
|
-
function Tile({ label, value, sub }) {
|
|
7612
|
+
function Tile({ label, value, sub, title }) {
|
|
7129
7613
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7130
7614
|
className: "ccp-tile",
|
|
7615
|
+
title,
|
|
7131
7616
|
children: [
|
|
7132
7617
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7133
7618
|
className: "ccp-tileLabel",
|
|
@@ -7299,7 +7784,8 @@ window.__ModuleLoader__.load({
|
|
|
7299
7784
|
children: account.stats.map((stat) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Tile, {
|
|
7300
7785
|
label: text(stat.label),
|
|
7301
7786
|
value: stat.value,
|
|
7302
|
-
sub: stat.sub
|
|
7787
|
+
sub: stat.sub,
|
|
7788
|
+
title: stat.title
|
|
7303
7789
|
}, stat.label))
|
|
7304
7790
|
})
|
|
7305
7791
|
]
|
|
@@ -8430,7 +8916,8 @@ window.__ModuleLoader__.load({
|
|
|
8430
8916
|
* 12/18 label-tertiary, the control on the right. The hairline rhythm is
|
|
8431
8917
|
* section-level: a 0.5px border-l2 rule separates the GROUPS (see
|
|
8432
8918
|
* `cc-groupDivided`), never the rows inside one, so a group's rows read as one
|
|
8433
|
-
* list. Selectors and buttons are
|
|
8919
|
+
* list. Selectors and buttons are
|
|
8920
|
+
* capsules (36px / 28px compact), inputs are
|
|
8434
8921
|
* 32px with an 8px radius and a 0.5px border-l4 edge, a card is a 0.5px
|
|
8435
8922
|
* border-l4 outline with a 16px radius, and a nested panel is a
|
|
8436
8923
|
* bg-module-platform fill with a 12px radius. Errors use state-error-primary.
|
|
@@ -8537,6 +9024,25 @@ window.__ModuleLoader__.load({
|
|
|
8537
9024
|
.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:6px;color:var(--dsw-alias-label-tertiary);cursor:pointer}
|
|
8538
9025
|
.cc-iconButton:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}
|
|
8539
9026
|
.cc-iconButton:focus-visible{outline:none;box-shadow:0 0 0 2px var(--dsw-alias-border-l3)}
|
|
9027
|
+
/* Per-model context-length override rows (issue #86): the error wraps onto
|
|
9028
|
+
its own line beneath the select/input/remove trio. The three cells form a
|
|
9029
|
+
fixed column contract — flexible model, 112px value, 28px remove — and the
|
|
9030
|
+
cc-contextHead labels repeat the first two (plus the remove cell's width
|
|
9031
|
+
as a spacer) so they sit exactly over their columns. */
|
|
9032
|
+
.cc-contextItem{align-items:center;gap:8px;display:flex;flex-wrap:wrap;padding:8px 0}
|
|
9033
|
+
.cc-contextHead{align-items:center;gap:8px;display:flex;flex-wrap:wrap;padding:4px 0 0;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}
|
|
9034
|
+
.cc-contextHeadModel{flex:1;min-width:200px}
|
|
9035
|
+
.cc-contextHeadValue{flex:none;width:112px}
|
|
9036
|
+
.cc-contextHeadGap{flex:none;width:28px}
|
|
9037
|
+
.cc-contextSelect{position:relative;display:block;flex:1;min-width:200px}
|
|
9038
|
+
/* appearance:none swaps the platform arrow for the cc-contextCaret, shaped
|
|
9039
|
+
like cc-selectorCaret so every dropdown in the plugin opens with the same
|
|
9040
|
+
hand-drawn chevron. */
|
|
9041
|
+
.cc-contextModel{box-sizing:border-box;appearance:none;-webkit-appearance:none;width:100%;padding-inline-end:26px;text-overflow:ellipsis;cursor:pointer}
|
|
9042
|
+
.cc-contextCaret{position:absolute;inset-inline-end:12px;top:50%;width:6px;height:6px;border-right:1.5px solid var(--dsw-alias-label-tertiary);border-bottom:1.5px solid var(--dsw-alias-label-tertiary);transform:translateY(-50%) rotate(45deg);pointer-events:none}
|
|
9043
|
+
.cc-contextSelect:has(.cc-contextModel:disabled) .cc-contextCaret{opacity:.6}
|
|
9044
|
+
.cc-contextValue{flex:none;width:112px}
|
|
9045
|
+
.cc-contextError{flex-basis:100%}
|
|
8540
9046
|
/* Three dots from one element: the dot plus two box-shadow copies. */
|
|
8541
9047
|
.cc-kebab{width:3px;height:3px;border-radius:50%;corner-shape:round;background:currentColor;box-shadow:0 -5px 0 currentColor,0 5px 0 currentColor}
|
|
8542
9048
|
.cc-tabDot{flex-shrink:0;width:8px;height:8px;border-radius:50%;corner-shape:round}
|
|
@@ -8848,7 +9354,6 @@ window.__ModuleLoader__.load({
|
|
|
8848
9354
|
.ccp-analysisTableHead{padding-top:11px;padding-bottom:11px;background:var(--dsw-alias-interactive-bg-hover);border-bottom:.5px solid var(--dsw-alias-border-l2);font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary)}
|
|
8849
9355
|
.ccp-analysisTableHead > span:not(:first-child){text-align:right}
|
|
8850
9356
|
.ccp-analysisScenarioHead,.ccp-analysisScenarioSummary{grid-template-columns:minmax(190px,2fr) minmax(118px,1.1fr) minmax(155px,1.35fr) minmax(142px,1.15fr) 16px}
|
|
8851
|
-
.ccp-analysisScenarioHead > span:nth-child(3),.ccp-analysisScenarioHead > span:nth-child(4){text-align:left}
|
|
8852
9357
|
.ccp-analysisModelSummary,.ccp-analysisScenarioSummary{cursor:pointer;list-style:none;min-height:60px}
|
|
8853
9358
|
.ccp-analysisModelSummary::-webkit-details-marker,.ccp-analysisScenarioSummary::-webkit-details-marker,.ccp-analysisDisclosure::-webkit-details-marker{display:none}
|
|
8854
9359
|
.ccp-analysisModelSummary:hover,.ccp-analysisScenarioSummary:hover{background:var(--dsw-alias-interactive-bg-hover)}
|
|
@@ -8856,7 +9361,6 @@ window.__ModuleLoader__.load({
|
|
|
8856
9361
|
.ccp-analysisModelName{font-size:14px;line-height:22px;font-weight:500;overflow-wrap:anywhere}
|
|
8857
9362
|
.ccp-analysisModelCount{margin-top:3px}
|
|
8858
9363
|
.ccp-analysisModelSummary > .ccp-analysisMetric,.ccp-analysisScenarioSummary > .ccp-analysisMetric{align-items:flex-end}
|
|
8859
|
-
.ccp-analysisScenarioSummary > .ccp-analysisMetric:nth-child(3),.ccp-analysisScenarioSummary > .ccp-analysisMetric:nth-child(4){align-items:flex-start}
|
|
8860
9364
|
/* 宽表使用集中表头,但保留每个指标的无障碍标签;窄面板恢复可见标签。 */
|
|
8861
9365
|
.ccp-analysisModelSummary .ccp-analysisMetricLabel,.ccp-analysisScenarioSummary .ccp-analysisMetricLabel,.ccp-analysisSearchLabel{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
|
|
8862
9366
|
.ccp-analysisShare{display:flex;align-items:center;gap:8px;width:100%;max-width:148px;margin-top:3px}
|
|
@@ -8880,6 +9384,7 @@ window.__ModuleLoader__.load({
|
|
|
8880
9384
|
.ccp-analysisMonthlyValue{font-size:14px;line-height:22px;font-weight:500;color:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums}
|
|
8881
9385
|
.ccp-analysisMonthly .ccp-analysisNote{width:100%;margin:0}
|
|
8882
9386
|
.ccp-analysisAssumption{font-size:14px;line-height:22px;color:var(--dsw-alias-label-tertiary);margin:0 0 18px;max-width:76ch}
|
|
9387
|
+
.ccp-analysisBaseline{margin:0 0 16px}
|
|
8883
9388
|
.ccp-analysisSearch{flex:0 1 270px}
|
|
8884
9389
|
.ccp-analysisSearchShell{position:relative;display:block}
|
|
8885
9390
|
.ccp-analysisSearchShell > .ccp-analysisIcon{position:absolute;top:10px;left:11px;color:var(--dsw-alias-label-tertiary);pointer-events:none}
|
|
@@ -9265,7 +9770,8 @@ window.__ModuleLoader__.load({
|
|
|
9265
9770
|
setActiveAccount: (id) => controller.setActiveAccount(id).then(refreshUsageOn),
|
|
9266
9771
|
setAccountModels: (target, ids) => controller.setAccountModels(target, ids),
|
|
9267
9772
|
editVisibleModels: (ids) => controller.editVisibleModels(ids),
|
|
9268
|
-
clearVisibleModels: () => controller.clearVisibleModels()
|
|
9773
|
+
clearVisibleModels: () => controller.clearVisibleModels(),
|
|
9774
|
+
editContextWindowOverrides: (next) => controller.editContextWindowOverrides(next)
|
|
9269
9775
|
});
|
|
9270
9776
|
ctx.slots.inject("settings.section", () => ctx.slots.register({
|
|
9271
9777
|
name: "settings.section",
|