dsh-background-by-model 0.4.1 → 0.4.3
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/README.md +34 -1
- package/README.zh.md +34 -1
- package/lib/client.js +1949 -224
- package/lib/client.js.map +1 -1
- package/lib/host-scan-WjK2VGBe.js +1182 -0
- package/lib/host-scan.js +2 -0
- package/lib/index.js +24 -0
- package/lib/testing.js +611 -0
- package/package.json +128 -122
- package/scripts/host-contract-scan.mjs +182 -0
- package/test/host-check.test.mjs +385 -0
package/lib/client.js
CHANGED
|
@@ -25,9 +25,11 @@ window.__ModuleLoader__.load({
|
|
|
25
25
|
pageInterface: "界面",
|
|
26
26
|
pageModelBg: "模型背景",
|
|
27
27
|
pageProfile: "配置",
|
|
28
|
+
pageHostCheck: "宿主自检",
|
|
28
29
|
descInterface: "为主界面的各个区域单独调节透明度与模糊,营造空间层次感。这里的设置对所有模型生效",
|
|
29
30
|
descModelBg: "每条规则 = 一个匹配串 + 一套背景外观。切换模型时,从上往下找第一条匹配串出现在模型名里的规则;都没中就使用第 1 条",
|
|
30
31
|
descProfile: "把当前全部规则连同图片导出备份,或从文件一键恢复",
|
|
32
|
+
descHostCheck: "逐条检查本插件依赖的宿主接口在这个 dsh 版本上是否还在:会话与模型服务、它着色的界面元素、以及被重新发出的设计令牌。红色那一行就是坏掉的地方",
|
|
31
33
|
uiTitle: "主界面",
|
|
32
34
|
uiOpacity: "透明度",
|
|
33
35
|
uiBlur: "模糊度",
|
|
@@ -121,7 +123,32 @@ window.__ModuleLoader__.load({
|
|
|
121
123
|
toastExportDone: "配置文件已开始下载",
|
|
122
124
|
importDone: "已导入配置",
|
|
123
125
|
importFail: "导入失败,文件格式不正确",
|
|
124
|
-
footerTag: "按模型换背景插件"
|
|
126
|
+
footerTag: "按模型换背景插件",
|
|
127
|
+
hostAllOk: "这个 dsh 版本上一切正常",
|
|
128
|
+
hostPartial: "有东西坏掉了",
|
|
129
|
+
hostBroken: "插件依赖的接口全都不见了",
|
|
130
|
+
hostUnknown: "还读不到结果",
|
|
131
|
+
hostSummary: "{pass} 项正常 · {fail} 项失效 · {skip} 项当前无法观察",
|
|
132
|
+
hostRunning: "检查中…",
|
|
133
|
+
hostRerun: "重新检查",
|
|
134
|
+
hostCopy: "复制诊断报告",
|
|
135
|
+
hostCopied: "诊断报告已复制",
|
|
136
|
+
hostCopyFail: "复制失败,浏览器拒绝了剪贴板访问",
|
|
137
|
+
hostPass: "正常",
|
|
138
|
+
hostFail: "失效",
|
|
139
|
+
hostNa: "未观察",
|
|
140
|
+
hostInfo: "仅记录",
|
|
141
|
+
hostPlugin: "插件",
|
|
142
|
+
hostPluginFloor: "要求 dsh",
|
|
143
|
+
hostDiskScan: "安装文件",
|
|
144
|
+
hostUnreadable: "未读到",
|
|
145
|
+
hostModel: "当前模型",
|
|
146
|
+
hostModelNone: "未检测到",
|
|
147
|
+
hostExpectedIn: "应该还在",
|
|
148
|
+
hostUsedBy: "本插件读取处",
|
|
149
|
+
hostBelowFloor: "当前 dsh 版本低于本插件声明的下限,宿主可能已经拒绝加载它。升级 dsh,或用 dsh plugin allow-version 明确放行。",
|
|
150
|
+
hostDiskFailures: "有 {n} 条契约在安装目录里已经找不到对应的宿主代码 —— 说明它们被改名或删掉了,而不是这次没观察到。",
|
|
151
|
+
hostHint: "灰色的「未观察」不是故障:设置弹窗打开时看不到对话页,没开过文件时右侧栏也不在 DOM 里。想让它变绿,把对应界面打开再看一次。"
|
|
125
152
|
};
|
|
126
153
|
const en = {
|
|
127
154
|
nav: "Model background",
|
|
@@ -130,9 +157,11 @@ window.__ModuleLoader__.load({
|
|
|
130
157
|
pageInterface: "Interface",
|
|
131
158
|
pageModelBg: "Model background",
|
|
132
159
|
pageProfile: "Profile",
|
|
160
|
+
pageHostCheck: "Host check",
|
|
133
161
|
descInterface: "Tune opacity and blur per surface to build depth. These settings are global and apply to every model",
|
|
134
162
|
descModelBg: "Each rule is a match string plus one look. On a model switch the list is scanned top-down for the first match string contained in the model name; if nothing hits, rule 1 is used",
|
|
135
163
|
descProfile: "Back up every rule together with its image, or restore one from a file",
|
|
164
|
+
descHostCheck: "Checks every host interface this plugin depends on against the dsh build you are running: the session and model services, the elements it paints, and the design tokens it re-emits. The red line is what broke",
|
|
136
165
|
uiTitle: "Interface",
|
|
137
166
|
uiOpacity: "Opacity",
|
|
138
167
|
uiBlur: "Blur",
|
|
@@ -226,7 +255,32 @@ window.__ModuleLoader__.load({
|
|
|
226
255
|
toastExportDone: "Export started",
|
|
227
256
|
importDone: "Profile imported",
|
|
228
257
|
importFail: "Import failed — invalid file",
|
|
229
|
-
footerTag: "Per-model wallpaper plugin"
|
|
258
|
+
footerTag: "Per-model wallpaper plugin",
|
|
259
|
+
hostAllOk: "Everything holds on this dsh build",
|
|
260
|
+
hostPartial: "Something broke",
|
|
261
|
+
hostBroken: "None of the interfaces this plugin needs are there",
|
|
262
|
+
hostUnknown: "No result yet",
|
|
263
|
+
hostSummary: "{pass} ok · {fail} broken · {skip} not observable right now",
|
|
264
|
+
hostRunning: "Checking…",
|
|
265
|
+
hostRerun: "Check again",
|
|
266
|
+
hostCopy: "Copy diagnostics",
|
|
267
|
+
hostCopied: "Diagnostics copied",
|
|
268
|
+
hostCopyFail: "Copy failed — the browser denied clipboard access",
|
|
269
|
+
hostPass: "ok",
|
|
270
|
+
hostFail: "broken",
|
|
271
|
+
hostNa: "n/a",
|
|
272
|
+
hostInfo: "note",
|
|
273
|
+
hostPlugin: "Plugin",
|
|
274
|
+
hostPluginFloor: "needs dsh",
|
|
275
|
+
hostDiskScan: "Installed files",
|
|
276
|
+
hostUnreadable: "unreadable",
|
|
277
|
+
hostModel: "Current model",
|
|
278
|
+
hostModelNone: "not detected",
|
|
279
|
+
hostExpectedIn: "Should still be in",
|
|
280
|
+
hostUsedBy: "Read by",
|
|
281
|
+
hostBelowFloor: "This dsh is older than the floor this plugin declares, so the host may already refuse to load it. Update dsh, or grant it explicitly with dsh plugin allow-version.",
|
|
282
|
+
hostDiskFailures: "{n} contract(s) can no longer be found in the installed host code — they were renamed or removed, this is not just \"not observable right now\".",
|
|
283
|
+
hostHint: "A grey \"n/a\" is not a failure: the conversation view is off screen while the settings dialog is open, and the right panel is not in the DOM until a file has been opened. Open that surface and check again to turn it green."
|
|
230
284
|
};
|
|
231
285
|
//#endregion
|
|
232
286
|
//#region src/schema.ts
|
|
@@ -687,6 +741,21 @@ window.__ModuleLoader__.load({
|
|
|
687
741
|
const parts = [d.provider, d.model].filter((s) => typeof s === "string" && s !== "");
|
|
688
742
|
return parts.length > 0 ? parts.join(" ") : null;
|
|
689
743
|
}
|
|
744
|
+
/**
|
|
745
|
+
* The installed-host half of the self-check: the host version and whether the
|
|
746
|
+
* packages on disk still contain every literal the plugin depends on.
|
|
747
|
+
*
|
|
748
|
+
* Returns null when the node half does not answer (an older build of this plugin
|
|
749
|
+
* still mounted, a host without the RPC channel); the panel then shows the live
|
|
750
|
+
* probe alone rather than claiming the disk scan passed.
|
|
751
|
+
*/
|
|
752
|
+
async function readHostScan(lang) {
|
|
753
|
+
const data = await rpcCall("hostCheck", { lang });
|
|
754
|
+
if (data === null || typeof data !== "object") return null;
|
|
755
|
+
const d = data;
|
|
756
|
+
if (!Array.isArray(d.results)) return null;
|
|
757
|
+
return data;
|
|
758
|
+
}
|
|
690
759
|
//#endregion
|
|
691
760
|
//#region src/client/utils/color.ts
|
|
692
761
|
function hsvToHsl(h, s, v) {
|
|
@@ -1054,6 +1123,291 @@ window.__ModuleLoader__.load({
|
|
|
1054
1123
|
Math.round((b + m) * 255)
|
|
1055
1124
|
];
|
|
1056
1125
|
}
|
|
1126
|
+
const UI_CSS = `
|
|
1127
|
+
/* The section is rendered INLINE inside the host settings dialog's content
|
|
1128
|
+
* column: the host provides the modal chrome (backdrop, centering, closing).
|
|
1129
|
+
* These classes style only the embedded shell; transient fixed layers (toast,
|
|
1130
|
+
* color picker, background editor) escape through Portals on <html>. */
|
|
1131
|
+
.dab-root{position:relative;color:var(--dsw-alias-label-primary);animation:dab-fade-in .35s ease both;container-type:inline-size;display:flex;flex-direction:column;align-items:center;width:100%;min-width:0;--dab-mono:ui-monospace,"Cascadia Mono","SF Mono",Consolas,"Courier New",monospace}
|
|
1132
|
+
.dab-root *,.dab-root *::before,.dab-root *::after{box-sizing:border-box}
|
|
1133
|
+
.dab-root button{font-family:inherit}
|
|
1134
|
+
|
|
1135
|
+
/* ── shell: nav rail + page body ─────────────────────────────────────────── */
|
|
1136
|
+
.dab-shell{display:grid;grid-template-columns:158px minmax(0,1fr);gap:26px;align-items:start;padding-bottom:8px;width:100%;max-width:980px;margin:0 auto}
|
|
1137
|
+
.dab-nav{position:sticky;top:0;display:flex;flex-direction:column;gap:18px}
|
|
1138
|
+
.dab-brand{display:flex;align-items:center;gap:10px;padding:2px 6px}
|
|
1139
|
+
.dab-brand-tile{width:30px;height:30px;flex:none;border-radius:9px;display:grid;place-items:center;color:var(--dsw-alias-brand-text);background:var(--dsw-alias-brand-primary);box-shadow:0 4px 14px -4px var(--dsw-alias-brand-primary)}
|
|
1140
|
+
.dab-brand-name{font-size:13px;font-weight:650;letter-spacing:.01em;line-height:1.25}
|
|
1141
|
+
.dab-brand-tag{font-size:9px;letter-spacing:.16em;font-weight:600;color:var(--dsw-alias-label-quaternary,var(--dsw-alias-label-tertiary));text-transform:uppercase}
|
|
1142
|
+
.dab-nav-list{position:relative;display:flex;flex-direction:column;gap:4px}
|
|
1143
|
+
.dab-nav-ind{position:absolute;left:0;right:0;top:0;height:38px;border-radius:11px;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);background:color-mix(in srgb,var(--dsw-alias-brand-primary) 13%,transparent);border-color:color-mix(in srgb,var(--dsw-alias-brand-primary) 25%,transparent);transition:transform .38s cubic-bezier(.22,1,.36,1)}
|
|
1144
|
+
.dab-nav-item{position:relative;z-index:1;display:flex;align-items:center;gap:10px;height:38px;padding:0 12px;border:0;background:none;border-radius:11px;color:var(--dsw-alias-label-tertiary);font-size:13px;cursor:pointer;text-align:left;transition:color .22s ease}
|
|
1145
|
+
.dab-nav-item:hover{color:var(--dsw-alias-label-primary)}
|
|
1146
|
+
.dab-nav-item.is-active{color:var(--dsw-alias-brand-primary);font-weight:600}
|
|
1147
|
+
.dab-nav-item svg{flex:none;transition:transform .3s cubic-bezier(.34,1.56,.64,1)}
|
|
1148
|
+
.dab-nav-item:hover svg{transform:scale(1.14) rotate(-5deg)}
|
|
1149
|
+
|
|
1150
|
+
/* ── page chrome ─────────────────────────────────────────────────────────── */
|
|
1151
|
+
.dab-page{animation:dab-page-in .4s cubic-bezier(.22,1,.36,1) both;min-width:0;display:flex;flex-direction:column;gap:13px}
|
|
1152
|
+
.dab-head{margin:2px 0 5px}
|
|
1153
|
+
.dab-overline{font-size:10.5px;font-weight:700;letter-spacing:.18em;text-transform:uppercase;color:var(--dsw-alias-brand-primary);opacity:.9}
|
|
1154
|
+
.dab-h1{margin:4px 0 0;font-size:21px;font-weight:700;letter-spacing:-.01em}
|
|
1155
|
+
.dab-desc{margin:6px 0 0;font-size:12.5px;line-height:1.55;color:var(--dsw-alias-label-tertiary);max-width:56ch}
|
|
1156
|
+
.dab-card{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);border-radius:16px;padding:18px}
|
|
1157
|
+
.dab-card-hover{transition:transform .28s ease,box-shadow .28s ease,border-color .28s ease}
|
|
1158
|
+
.dab-card-hover:hover{transform:translateY(-2px);box-shadow:0 10px 28px -12px rgba(0,0,0,.28)}
|
|
1159
|
+
.dab-rise{animation:dab-rise-in .55s cubic-bezier(.22,1,.36,1) both;animation-delay:calc(var(--d,0) * 62ms)}
|
|
1160
|
+
|
|
1161
|
+
/* ── accent hero (color orb) ─────────────────────────────────────────────── */
|
|
1162
|
+
.dab-hero-accent{display:flex;align-items:center;gap:24px;flex-wrap:wrap}
|
|
1163
|
+
.dab-orb-wrap{position:relative;width:118px;height:118px;flex:none}
|
|
1164
|
+
.dab-orb{position:absolute;inset:11px;border-radius:50%;background:radial-gradient(circle at 32% 28%,rgba(255,255,255,.5),rgba(255,255,255,0) 44%),var(--c,#888);box-shadow:0 16px 36px -10px var(--c-soft,transparent),inset 0 -10px 20px rgba(0,0,0,.16);animation:dab-orb-in .7s cubic-bezier(.22,1,.36,1) both}
|
|
1165
|
+
.dab-orb-ring{position:absolute;inset:0;border-radius:50%;background:conic-gradient(from 0deg,transparent 0 30%,var(--c,#888) 46%,transparent 62%,transparent 76%,var(--c,#888) 90%,transparent 100%);-webkit-mask:radial-gradient(farthest-side,transparent calc(100% - 3.5px),#000 calc(100% - 2.5px));mask:radial-gradient(farthest-side,transparent calc(100% - 3.5px),#000 calc(100% - 2.5px));animation:dab-spin 7s linear infinite;opacity:.9}
|
|
1166
|
+
.dab-hex-caption{font-size:11px;color:var(--dsw-alias-label-tertiary);letter-spacing:.04em}
|
|
1167
|
+
.dab-hex{font-family:var(--dab-mono);font-size:24px;font-weight:600;letter-spacing:.02em;line-height:1.2;margin-top:2px}
|
|
1168
|
+
.dab-hsl-row{display:flex;gap:16px;margin-top:7px;font-family:var(--dab-mono);font-size:11px;color:var(--dsw-alias-label-tertiary)}
|
|
1169
|
+
.dab-hsl-row b{font-weight:600;color:var(--dsw-alias-label-secondary,var(--dsw-alias-label-tertiary))}
|
|
1170
|
+
|
|
1171
|
+
/* ── swatches ────────────────────────────────────────────────────────────── */
|
|
1172
|
+
.dab-swatch-title{font-size:12px;font-weight:600;margin-bottom:10px;color:var(--dsw-alias-label-secondary,var(--dsw-alias-label-tertiary))}
|
|
1173
|
+
.dab-swatches{display:flex;flex-wrap:wrap;gap:9px}
|
|
1174
|
+
.dab-swatch{width:25px;height:25px;border-radius:50%;border:0;padding:0;cursor:pointer;box-shadow:inset 0 0 0 1px rgba(0,0,0,.1);transition:transform .22s cubic-bezier(.34,1.56,.64,1),box-shadow .22s ease}
|
|
1175
|
+
.dab-swatch:hover{transform:scale(1.2)}
|
|
1176
|
+
.dab-swatch.is-on{box-shadow:0 0 0 2px var(--dsw-alias-bg-layer-1),0 0 0 4px var(--dsw-alias-brand-primary)}
|
|
1177
|
+
|
|
1178
|
+
/* ── wheel card ──────────────────────────────────────────────────────────── */
|
|
1179
|
+
.dab-wheel-card{position:relative;display:flex;align-items:center;justify-content:center;gap:28px;flex-wrap:wrap;padding:24px 18px}
|
|
1180
|
+
.dab-wheel-glow{position:absolute;width:230px;height:230px;border-radius:50%;filter:blur(48px);opacity:.2;background:var(--c,#888);pointer-events:none;transition:background .4s ease}
|
|
1181
|
+
.dab-wheel{position:relative;cursor:crosshair;border-radius:50%;box-shadow:0 12px 32px -14px rgba(0,0,0,.4)}
|
|
1182
|
+
.dab-hint{font-size:11.5px;line-height:1.55;color:var(--dsw-alias-label-tertiary);padding:0 4px}
|
|
1183
|
+
|
|
1184
|
+
/* ── precise color inputs ────────────────────────────────────────────────── */
|
|
1185
|
+
.dab-inputs{display:flex;flex-direction:column;gap:10px;min-width:172px}
|
|
1186
|
+
.dab-field{display:flex;align-items:center;gap:8px}
|
|
1187
|
+
.dab-field-label{width:14px;text-align:center;font-family:var(--dab-mono);font-size:11px;font-weight:600;color:var(--dsw-alias-label-tertiary)}
|
|
1188
|
+
.dab-num{flex:1;min-width:0;height:30px;padding:0 10px;border-radius:9px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font-family:var(--dab-mono);font-size:12px;outline:none;transition:border-color .2s,box-shadow .2s}
|
|
1189
|
+
.dab-num:focus{border-color:var(--dsw-alias-brand-primary);box-shadow:0 0 0 3px color-mix(in srgb,var(--dsw-alias-brand-primary) 18%,transparent)}
|
|
1190
|
+
.dab-num::-webkit-outer-spin-button,.dab-num::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
|
|
1191
|
+
.dab-num{-moz-appearance:textfield;appearance:textfield}
|
|
1192
|
+
.dab-urlinput{flex:1;min-width:180px;height:34px;padding:0 12px;border-radius:10px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font-size:12.5px;outline:none;transition:border-color .2s}
|
|
1193
|
+
.dab-urlinput::placeholder{color:var(--dsw-alias-label-quaternary)}
|
|
1194
|
+
.dab-urlinput:focus{border-color:var(--dsw-alias-brand-primary);box-shadow:0 0 0 3px color-mix(in srgb,var(--dsw-alias-brand-primary) 18%,transparent)}
|
|
1195
|
+
.dab-swatch-lg{height:38px;border-radius:10px;border:1px solid var(--dsw-alias-border-l2);box-shadow:inset 0 0 14px rgba(0,0,0,.1);transition:transform .3s ease}
|
|
1196
|
+
.dab-swatch-lg:hover{transform:scale(1.02)}
|
|
1197
|
+
|
|
1198
|
+
/* ── buttons & chips ─────────────────────────────────────────────────────── */
|
|
1199
|
+
.dab-btn{display:inline-flex;align-items:center;justify-content:center;gap:7px;height:34px;padding:0 14px;border-radius:10px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-button-elevated-fill);color:var(--dsw-alias-label-primary);font-size:12.5px;font-weight:550;cursor:pointer;transition:transform .18s ease,box-shadow .18s ease,border-color .18s ease,opacity .18s ease,background .18s ease}
|
|
1200
|
+
.dab-btn:hover:not(:disabled){transform:translateY(-1px);box-shadow:0 5px 14px -6px rgba(0,0,0,.32)}
|
|
1201
|
+
.dab-btn:active:not(:disabled){transform:translateY(0) scale(.97);box-shadow:none}
|
|
1202
|
+
.dab-btn:disabled{opacity:.5;cursor:not-allowed}
|
|
1203
|
+
.dab-btn-primary{background:var(--dsw-alias-brand-primary);color:var(--dsw-alias-brand-text);border-color:transparent}
|
|
1204
|
+
.dab-btn-danger{color:var(--dsw-alias-state-error-primary)}
|
|
1205
|
+
.dab-btn-ghost{background:transparent;border-color:transparent;color:var(--dsw-alias-label-secondary,var(--dsw-alias-label-tertiary))}
|
|
1206
|
+
.dab-btn-ghost:hover:not(:disabled){background:var(--dsw-alias-bg-layer-2);box-shadow:none}
|
|
1207
|
+
.dab-btn:focus-visible,.dab-nav-item:focus-visible,.dab-seg-item:focus-visible,.dab-swatch:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}
|
|
1208
|
+
.dab-chip-row{display:flex;flex-wrap:wrap;gap:8px}
|
|
1209
|
+
.dab-chip{height:30px;padding:0 14px;border-radius:99px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary,var(--dsw-alias-label-tertiary));font-size:12px;cursor:pointer;display:inline-flex;align-items:center;gap:6px;transition:all .22s ease}
|
|
1210
|
+
.dab-chip:hover{border-color:var(--dsw-alias-brand-primary);color:var(--dsw-alias-label-primary)}
|
|
1211
|
+
.dab-chip.is-active{background:var(--dsw-alias-brand-primary);color:var(--dsw-alias-brand-text);border-color:transparent}
|
|
1212
|
+
|
|
1213
|
+
/* ── sliders ─────────────────────────────────────────────────────────────── */
|
|
1214
|
+
.dab-slider-block{display:flex;flex-direction:column;gap:6px}
|
|
1215
|
+
.dab-slider-block + .dab-slider-block{margin-top:13px}
|
|
1216
|
+
.dab-slider-label{font-size:12px;font-weight:550;color:var(--dsw-alias-label-secondary,var(--dsw-alias-label-tertiary))}
|
|
1217
|
+
.dab-slider-val{font-family:var(--dab-mono);font-size:11px;color:var(--dsw-alias-label-tertiary);min-width:44px;text-align:right}
|
|
1218
|
+
.dab-slider{-webkit-appearance:none;appearance:none;flex:1;min-width:0;height:4px;border-radius:99px;outline:none;cursor:pointer;margin:5px 0;background:linear-gradient(to right,var(--dsw-alias-brand-primary) calc(var(--pct,50) * 1%),var(--dsw-alias-border-l2) calc(var(--pct,50) * 1%));transition:height .15s ease}
|
|
1219
|
+
.dab-slider:hover{height:5px}
|
|
1220
|
+
.dab-slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:15px;height:15px;border-radius:50%;background:var(--dsw-alias-button-elevated-fill,#fff);border:2px solid var(--dsw-alias-brand-primary);box-shadow:0 1px 5px rgba(0,0,0,.28);transition:transform .16s cubic-bezier(.34,1.56,.64,1)}
|
|
1221
|
+
.dab-slider:hover::-webkit-slider-thumb,.dab-slider:focus-visible::-webkit-slider-thumb{transform:scale(1.22)}
|
|
1222
|
+
.dab-slider:active::-webkit-slider-thumb{transform:scale(1.34)}
|
|
1223
|
+
.dab-slider::-moz-range-thumb{width:13px;height:13px;border-radius:50%;background:var(--dsw-alias-button-elevated-fill,#fff);border:2px solid var(--dsw-alias-brand-primary)}
|
|
1224
|
+
|
|
1225
|
+
/* ── interface part cards ────────────────────────────────────────────────── */
|
|
1226
|
+
.dab-grid-parts{display:grid;grid-template-columns:repeat(auto-fill,minmax(256px,1fr));gap:13px}
|
|
1227
|
+
.dab-part-head{display:flex;align-items:center;gap:11px;margin-bottom:14px}
|
|
1228
|
+
.dab-part-ico{width:32px;height:32px;flex:none;border-radius:10px;display:grid;place-items:center;color:var(--dsw-alias-brand-primary);background:var(--dsw-alias-bg-layer-2);background:color-mix(in srgb,var(--dsw-alias-brand-primary) 12%,transparent)}
|
|
1229
|
+
.dab-part-name{font-size:13.5px;font-weight:600}
|
|
1230
|
+
.dab-part-badge{margin-left:auto;font-family:var(--dab-mono);font-size:11px;color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-bg-layer-2);border-radius:99px;padding:3px 9px}
|
|
1231
|
+
|
|
1232
|
+
/* ── segmented control ───────────────────────────────────────────────────── */
|
|
1233
|
+
.dab-seg{position:relative;display:inline-flex;padding:3px;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:11px}
|
|
1234
|
+
.dab-seg-thumb{position:absolute;top:3px;bottom:3px;left:3px;width:var(--w,96px);border-radius:8px;background:var(--dsw-alias-button-elevated-fill);box-shadow:0 2px 8px -2px rgba(0,0,0,.28);transition:transform .32s cubic-bezier(.22,1,.36,1)}
|
|
1235
|
+
.dab-seg-item{position:relative;z-index:1;display:inline-flex;align-items:center;justify-content:center;gap:6px;width:var(--w,96px);height:30px;border:0;background:none;border-radius:8px;color:var(--dsw-alias-label-tertiary);font-size:12.5px;cursor:pointer;transition:color .25s ease}
|
|
1236
|
+
.dab-seg-item.is-active{color:var(--dsw-alias-label-primary);font-weight:600}
|
|
1237
|
+
|
|
1238
|
+
/* ── toggle switch ───────────────────────────────────────────────────────── */
|
|
1239
|
+
.dab-toggle{position:relative;width:42px;height:24px;flex:none;border-radius:99px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);cursor:pointer;padding:0;transition:background .28s ease,border-color .28s ease}
|
|
1240
|
+
.dab-toggle-knob{position:absolute;top:2.5px;left:2.5px;width:17px;height:17px;border-radius:50%;background:var(--dsw-alias-label-secondary,#999);box-shadow:0 1px 3px rgba(0,0,0,.3);transition:transform .28s cubic-bezier(.22,1,.36,1),background .28s ease}
|
|
1241
|
+
.dab-toggle.is-on{background:var(--dsw-alias-brand-primary);border-color:var(--dsw-alias-brand-primary)}
|
|
1242
|
+
.dab-toggle.is-on .dab-toggle-knob{transform:translateX(18px);background:#fff}
|
|
1243
|
+
|
|
1244
|
+
/* ── background preview hero ─────────────────────────────────────────────── */
|
|
1245
|
+
.dab-hero{position:relative;border-radius:16px;overflow:hidden;aspect-ratio:16/9;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2)}
|
|
1246
|
+
.dab-hero-img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;transition:transform .5s cubic-bezier(.22,1,.36,1)}
|
|
1247
|
+
.dab-hero:hover .dab-hero-img{transform:scale(1.03)}
|
|
1248
|
+
.dab-hero-empty{position:absolute;inset:6px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;color:var(--dsw-alias-label-tertiary);font-size:12.5px;border:1.5px dashed var(--dsw-alias-border-l2);border-radius:12px;cursor:pointer;background:transparent;transition:border-color .25s,color .25s,background .25s;width:auto;height:auto}
|
|
1249
|
+
.dab-hero-empty:hover{border-color:var(--dsw-alias-brand-primary);color:var(--dsw-alias-brand-primary)}
|
|
1250
|
+
.dab-hero-empty.is-over{border-color:var(--dsw-alias-brand-primary);color:var(--dsw-alias-brand-primary);background:color-mix(in srgb,var(--dsw-alias-brand-primary) 8%,transparent)}
|
|
1251
|
+
.dab-hero-badge{position:absolute;top:10px;left:10px;display:inline-flex;align-items:center;gap:5px;height:24px;padding:0 11px;border-radius:99px;background:rgba(0,0,0,.45);color:#fff;font-size:11px;backdrop-filter:blur(6px);pointer-events:none}
|
|
1252
|
+
.dab-hero-veil{position:absolute;left:0;right:0;bottom:0;padding:34px 12px 12px;display:flex;align-items:flex-end;justify-content:flex-end;gap:8px;background:linear-gradient(to top,rgba(0,0,0,.55),rgba(0,0,0,0));opacity:0;transform:translateY(6px);transition:opacity .3s ease,transform .3s ease}
|
|
1253
|
+
.dab-hero:hover .dab-hero-veil{opacity:1;transform:none}
|
|
1254
|
+
.dab-hero-veil .dab-btn{background:rgba(255,255,255,.94);color:#14161a;border-color:transparent;height:30px;font-size:12px}
|
|
1255
|
+
.dab-hero-veil .dab-btn-danger{color:#dc2626}
|
|
1256
|
+
|
|
1257
|
+
/* ── profile page ────────────────────────────────────────────────────────── */
|
|
1258
|
+
.dab-profile-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(238px,1fr));gap:13px}
|
|
1259
|
+
.dab-profile-ico{width:38px;height:38px;border-radius:11px;display:grid;place-items:center;margin-bottom:13px;color:var(--dsw-alias-brand-primary);background:var(--dsw-alias-bg-layer-2);background:color-mix(in srgb,var(--dsw-alias-brand-primary) 12%,transparent)}
|
|
1260
|
+
.dab-profile-title{font-size:14px;font-weight:650}
|
|
1261
|
+
.dab-profile-desc{font-size:12px;color:var(--dsw-alias-label-tertiary);line-height:1.55;margin:5px 0 15px}
|
|
1262
|
+
.dab-footer{margin-top:6px;padding:14px 4px 0;border-top:1px solid var(--dsw-alias-border-l2);display:flex;align-items:center;justify-content:space-between;font-size:11px;color:var(--dsw-alias-label-tertiary)}
|
|
1263
|
+
.dab-footer-mono{font-family:var(--dab-mono);letter-spacing:.02em}
|
|
1264
|
+
|
|
1265
|
+
/* ── toast ───────────────────────────────────────────────────────────────── */
|
|
1266
|
+
.dab-toast{position:fixed;left:50%;bottom:30px;transform:translateX(-50%);display:flex;align-items:center;gap:8px;height:38px;padding:0 16px;border-radius:99px;background:var(--dsw-alias-bg-layer-3,var(--dsw-alias-bg-layer-2));border:1px solid var(--dsw-alias-border-l2);box-shadow:0 10px 30px -8px rgba(0,0,0,.38);font-size:12.5px;z-index:10001;animation:dab-toast-in .32s cubic-bezier(.22,1,.36,1) both}
|
|
1267
|
+
.dab-toast-ok{color:var(--dsw-alias-brand-primary);display:grid;place-items:center}
|
|
1268
|
+
.dab-toast-err{color:var(--dsw-alias-state-error-primary);display:grid;place-items:center}
|
|
1269
|
+
|
|
1270
|
+
/* ── modals (editor / eyedropper / crash) ────────────────────────────────── */
|
|
1271
|
+
/* Pin to the viewport explicitly with vw/vh so ancestor padding/margins on
|
|
1272
|
+
* body cannot shift or clip the overlay; keep it above host sidebar chrome.
|
|
1273
|
+
* pointer-events:auto re-enables interaction: these overlays render inside a
|
|
1274
|
+
* Portal root that is pointer-events:none so it never blocks the page alone. */
|
|
1275
|
+
.dab-overlay{position:fixed;left:0;top:0;width:100vw;height:100vh;z-index:999999;background:rgba(8,10,14,.62);backdrop-filter:blur(8px);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:14px;animation:dab-fade-in .25s ease both;pointer-events:auto}
|
|
1276
|
+
.dab-overlay-title{color:#fff;font-size:15px;font-weight:600}
|
|
1277
|
+
.dab-overlay-hint{color:rgba(255,255,255,.62);font-size:12px}
|
|
1278
|
+
.dab-modal-card{animation:dab-zoom-in .3s cubic-bezier(.22,1,.36,1) both;max-width:calc(100vw - 40px);max-height:calc(100vh - 120px);overflow:auto}
|
|
1279
|
+
.dab-overlay .dab-btn{background:rgba(255,255,255,.94);color:#14161a;border-color:transparent}
|
|
1280
|
+
.dab-overlay .dab-btn-primary{background:var(--dsw-alias-brand-primary);color:var(--dsw-alias-brand-text)}
|
|
1281
|
+
.dab-crash{display:flex;flex-direction:column;gap:10px;align-items:center;padding:28px 18px;border:1px solid var(--dsw-alias-border-l2);border-radius:16px}
|
|
1282
|
+
.dab-crash-title{font-size:15px;font-weight:650}
|
|
1283
|
+
.dab-crash-desc{font-size:12px;color:var(--dsw-alias-label-tertiary);text-align:center;line-height:1.5}
|
|
1284
|
+
|
|
1285
|
+
/* ── model rules ─────────────────────────────────────────────────────────── */
|
|
1286
|
+
.dab-rules{display:flex;flex-direction:column;gap:12px}
|
|
1287
|
+
.dab-rule{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);border-radius:16px;padding:14px;transition:border-color .25s,box-shadow .25s,opacity .25s}
|
|
1288
|
+
.dab-rule.is-active{border-color:var(--dsw-alias-brand-primary);box-shadow:0 0 0 3px color-mix(in srgb,var(--dsw-alias-brand-primary) 16%,transparent)}
|
|
1289
|
+
.dab-rule.is-off{opacity:.55}
|
|
1290
|
+
.dab-rule-head{display:flex;align-items:center;gap:9px;flex-wrap:wrap}
|
|
1291
|
+
.dab-rule-num{font-family:var(--dab-mono);font-size:11px;font-weight:700;color:var(--dsw-alias-label-tertiary);min-width:16px}
|
|
1292
|
+
.dab-rule-badge{font-size:10px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--dsw-alias-brand-primary);background:color-mix(in srgb,var(--dsw-alias-brand-primary) 14%,transparent);border-radius:99px;padding:2px 8px}
|
|
1293
|
+
.dab-rule-match{flex:1;min-width:150px;height:32px;padding:0 11px;border-radius:9px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font-family:var(--dab-mono);font-size:12.5px;outline:none;transition:border-color .2s,box-shadow .2s}
|
|
1294
|
+
.dab-rule-match::placeholder{color:var(--dsw-alias-label-quaternary)}
|
|
1295
|
+
.dab-rule-match:focus{border-color:var(--dsw-alias-brand-primary);box-shadow:0 0 0 3px color-mix(in srgb,var(--dsw-alias-brand-primary) 18%,transparent)}
|
|
1296
|
+
.dab-icon-btn{width:28px;height:28px;flex:none;display:grid;place-items:center;border-radius:8px;border:1px solid var(--dsw-alias-border-l2);background:transparent;color:var(--dsw-alias-label-tertiary);cursor:pointer;transition:color .2s,border-color .2s,background .2s}
|
|
1297
|
+
.dab-icon-btn:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-brand-primary)}
|
|
1298
|
+
.dab-icon-btn:disabled{opacity:.35;cursor:not-allowed}
|
|
1299
|
+
.dab-icon-btn-danger:hover:not(:disabled){color:var(--dsw-alias-state-error-primary);border-color:var(--dsw-alias-state-error-primary)}
|
|
1300
|
+
.dab-rule-body{margin-top:13px;display:flex;flex-direction:column;gap:13px}
|
|
1301
|
+
.dab-rule-thumb{position:relative;border-radius:12px;overflow:hidden;aspect-ratio:16/9;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);cursor:pointer}
|
|
1302
|
+
.dab-rule-thumb img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
|
|
1303
|
+
.dab-rule-thumb-empty{position:absolute;inset:5px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;border:1.5px dashed var(--dsw-alias-border-l2);border-radius:9px;color:var(--dsw-alias-label-tertiary);font-size:12px;text-align:center;padding:0 10px}
|
|
1304
|
+
.dab-rule-thumb.is-over .dab-rule-thumb-empty{border-color:var(--dsw-alias-brand-primary);color:var(--dsw-alias-brand-primary)}
|
|
1305
|
+
.dab-rule-cols{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,1fr);gap:18px;align-items:start}
|
|
1306
|
+
.dab-rule-section-title{font-size:12px;font-weight:600;margin-bottom:9px;color:var(--dsw-alias-label-secondary,var(--dsw-alias-label-tertiary))}
|
|
1307
|
+
@container (max-width:760px){.dab-rule-cols{grid-template-columns:1fr}}
|
|
1308
|
+
|
|
1309
|
+
/* ── host self-check ─────────────────────────────────────────────────────── */
|
|
1310
|
+
.dab-verdict{display:flex;flex-direction:column;gap:14px;position:relative;overflow:hidden}
|
|
1311
|
+
.dab-verdict::after{content:'';position:absolute;inset:0;pointer-events:none;opacity:.5;background:linear-gradient(120deg,transparent 55%,color-mix(in srgb,var(--dab-verdict-tone,transparent) 26%,transparent))}
|
|
1312
|
+
.dab-verdict-ok{--dab-verdict-tone:var(--dsw-alias-state-success-primary,var(--dsw-alias-brand-primary));border-color:color-mix(in srgb,var(--dsw-alias-state-success-primary,var(--dsw-alias-brand-primary)) 38%,var(--dsw-alias-border-l2))}
|
|
1313
|
+
.dab-verdict-partial,.dab-verdict-broken{--dab-verdict-tone:var(--dsw-alias-state-error-primary);border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 40%,var(--dsw-alias-border-l2))}
|
|
1314
|
+
.dab-verdict-unknown{--dab-verdict-tone:var(--dsw-alias-label-tertiary)}
|
|
1315
|
+
.dab-verdict-head{display:flex;align-items:center;gap:13px;flex-wrap:wrap}
|
|
1316
|
+
.dab-verdict-ico{width:34px;height:34px;flex:none;border-radius:11px;display:grid;place-items:center;color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-bg-layer-2)}
|
|
1317
|
+
.dab-verdict-ico-ok{color:var(--dsw-alias-state-success-primary,var(--dsw-alias-brand-primary));background:color-mix(in srgb,var(--dsw-alias-state-success-primary,var(--dsw-alias-brand-primary)) 14%,transparent)}
|
|
1318
|
+
.dab-verdict-ico-partial,.dab-verdict-ico-broken{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 14%,transparent)}
|
|
1319
|
+
.dab-verdict-text{flex:1;min-width:170px}
|
|
1320
|
+
.dab-verdict-title{font-size:14.5px;font-weight:650}
|
|
1321
|
+
.dab-verdict-sub{margin-top:4px;font-size:12px;color:var(--dsw-alias-label-tertiary)}
|
|
1322
|
+
.dab-verdict-actions{display:flex;gap:8px;flex-wrap:wrap;margin-left:auto}
|
|
1323
|
+
.dab-facts{display:flex;flex-wrap:wrap;gap:8px;position:relative;z-index:1}
|
|
1324
|
+
.dab-fact{display:inline-flex;align-items:center;gap:7px;height:27px;padding:0 11px;border-radius:99px;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);max-width:100%}
|
|
1325
|
+
.dab-fact.is-bad{border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 45%,transparent);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 10%,transparent)}
|
|
1326
|
+
.dab-fact-k{font-size:10.5px;font-weight:650;letter-spacing:.06em;text-transform:uppercase;color:var(--dsw-alias-label-tertiary);white-space:nowrap}
|
|
1327
|
+
.dab-fact-v{font-family:var(--dab-mono);font-size:11.5px;color:var(--dsw-alias-label-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:32ch}
|
|
1328
|
+
.dab-check-group-title{display:flex;align-items:center;gap:9px;font-size:12.5px;font-weight:650;margin-bottom:11px}
|
|
1329
|
+
.dab-check-group-count{margin-left:auto;font-family:var(--dab-mono);font-size:11px;font-weight:500;color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-bg-layer-2);border-radius:99px;padding:3px 9px}
|
|
1330
|
+
.dab-checks{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:2px}
|
|
1331
|
+
.dab-check{display:flex;gap:10px;padding:9px 10px;border-radius:11px;transition:background .2s ease}
|
|
1332
|
+
.dab-check:hover{background:var(--dsw-alias-bg-layer-2)}
|
|
1333
|
+
.dab-check-ico{width:19px;height:19px;flex:none;margin-top:1px;border-radius:50%;display:grid;place-items:center;color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-bg-layer-2)}
|
|
1334
|
+
.dab-check-ico-pass{color:var(--dsw-alias-state-success-primary,var(--dsw-alias-brand-primary));background:color-mix(in srgb,var(--dsw-alias-state-success-primary,var(--dsw-alias-brand-primary)) 15%,transparent)}
|
|
1335
|
+
.dab-check-ico-fail{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 15%,transparent)}
|
|
1336
|
+
.dab-check-dot{width:5px;height:5px;border-radius:50%;background:currentColor;opacity:.65}
|
|
1337
|
+
.dab-check-body{min-width:0;flex:1;display:flex;flex-direction:column;gap:3px}
|
|
1338
|
+
.dab-check-head{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap}
|
|
1339
|
+
.dab-check-label{font-size:12.5px;font-weight:600}
|
|
1340
|
+
.dab-check-status{font-size:10px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;padding:1px 7px;border-radius:99px;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-tertiary)}
|
|
1341
|
+
.dab-check-status-pass{color:var(--dsw-alias-state-success-primary,var(--dsw-alias-brand-primary));background:color-mix(in srgb,var(--dsw-alias-state-success-primary,var(--dsw-alias-brand-primary)) 14%,transparent)}
|
|
1342
|
+
.dab-check-status-fail{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 14%,transparent)}
|
|
1343
|
+
.dab-check-target{font-family:var(--dab-mono);font-size:10.5px;color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-bg-layer-2);border-radius:6px;padding:1px 6px;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
1344
|
+
.dab-check-detail{font-family:var(--dab-mono);font-size:11px;color:var(--dsw-alias-label-tertiary);line-height:1.5;overflow-wrap:anywhere}
|
|
1345
|
+
.dab-check-reason{display:flex;flex-direction:column;gap:5px;font-size:12px;line-height:1.55;color:var(--dsw-alias-label-secondary,var(--dsw-alias-label-tertiary))}
|
|
1346
|
+
.dab-check-symptom{color:var(--dsw-alias-state-error-primary)}
|
|
1347
|
+
.dab-check-where{display:flex;gap:7px;align-items:baseline;flex-wrap:wrap;font-size:11px;color:var(--dsw-alias-label-tertiary)}
|
|
1348
|
+
.dab-check-where code{font-family:var(--dab-mono);font-size:10.5px;overflow-wrap:anywhere}
|
|
1349
|
+
.dab-check-note{margin:12px 0 0;font-size:11.5px;line-height:1.55;color:var(--dsw-alias-label-tertiary);position:relative;z-index:1}
|
|
1350
|
+
|
|
1351
|
+
/* ── keyframes ───────────────────────────────────────────────────────────── */
|
|
1352
|
+
@keyframes dab-fade-in{from{opacity:0}to{opacity:1}}
|
|
1353
|
+
@keyframes dab-page-in{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:none}}
|
|
1354
|
+
@keyframes dab-rise-in{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}
|
|
1355
|
+
@keyframes dab-orb-in{from{opacity:0;transform:scale(.82)}to{opacity:1;transform:none}}
|
|
1356
|
+
@keyframes dab-spin{to{transform:rotate(360deg)}}
|
|
1357
|
+
@keyframes dab-rotate{to{transform:rotate(360deg)}}
|
|
1358
|
+
@keyframes dab-toast-in{from{opacity:0;transform:translate(-50%,10px)}to{opacity:1;transform:translate(-50%,0)}}
|
|
1359
|
+
@keyframes dab-zoom-in{from{opacity:0;transform:scale(.94) translateY(8px)}to{opacity:1;transform:none}}
|
|
1360
|
+
@keyframes dab-flow{to{background-position:300% 50%}}
|
|
1361
|
+
|
|
1362
|
+
/* ── responsive & motion preferences ─────────────────────────────────────── */
|
|
1363
|
+
@container (max-width:620px){
|
|
1364
|
+
.dab-shell{grid-template-columns:1fr;gap:14px}
|
|
1365
|
+
.dab-nav{position:static;flex-direction:row;align-items:center;justify-content:space-between;gap:10px}
|
|
1366
|
+
.dab-nav-list{flex-direction:row;overflow-x:auto;scrollbar-width:none}
|
|
1367
|
+
.dab-nav-list::-webkit-scrollbar{display:none}
|
|
1368
|
+
.dab-nav-ind{display:none}
|
|
1369
|
+
.dab-nav-item{flex:none;padding:0 10px}
|
|
1370
|
+
.dab-nav-item.is-active{background:var(--dsw-alias-bg-layer-2)}
|
|
1371
|
+
.dab-types{grid-template-columns:1fr}
|
|
1372
|
+
.dab-verdict-actions{margin-left:0;width:100%}
|
|
1373
|
+
.dab-verdict-actions .dab-btn{flex:1}
|
|
1374
|
+
}
|
|
1375
|
+
@container (min-width:621px){
|
|
1376
|
+
.dab-shell{grid-template-columns:158px minmax(0,1fr);gap:26px}
|
|
1377
|
+
}
|
|
1378
|
+
@media (prefers-reduced-motion:reduce){
|
|
1379
|
+
.dab-root *,.dab-root *::before,.dab-root *::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}
|
|
1380
|
+
}
|
|
1381
|
+
`;
|
|
1382
|
+
const CSS_ID = "dab-ui-css";
|
|
1383
|
+
/**
|
|
1384
|
+
* The attribute that marks a `<style>` element as OURS.
|
|
1385
|
+
*
|
|
1386
|
+
* The host's own CSS modules also set `data-plugin` on their `<style>` elements
|
|
1387
|
+
* (every one of them), so `data-plugin` cannot distinguish this plugin's sheets
|
|
1388
|
+
* from the host's — and the host-contract probe has to make exactly that
|
|
1389
|
+
* distinction: a token this plugin re-emits must never be allowed to make a
|
|
1390
|
+
* *host* check pass. This attribute is the honest marker, and it is set on every
|
|
1391
|
+
* stylesheet this plugin injects.
|
|
1392
|
+
*/
|
|
1393
|
+
const OWN_SHEET_ATTR = "data-dab-side";
|
|
1394
|
+
/** Mark a stylesheet this plugin injected, so the host probe can tell it apart. */
|
|
1395
|
+
function markOwnSheet(el) {
|
|
1396
|
+
el.setAttribute(OWN_SHEET_ATTR, "own");
|
|
1397
|
+
return el;
|
|
1398
|
+
}
|
|
1399
|
+
/** Inject the design-system stylesheet once per document (HMR-safe). */
|
|
1400
|
+
function ensureUiCss() {
|
|
1401
|
+
if (typeof document === "undefined") return;
|
|
1402
|
+
let el = document.getElementById(CSS_ID);
|
|
1403
|
+
if (!el) {
|
|
1404
|
+
el = document.createElement("style");
|
|
1405
|
+
el.id = CSS_ID;
|
|
1406
|
+
markOwnSheet(el);
|
|
1407
|
+
document.head.appendChild(el);
|
|
1408
|
+
}
|
|
1409
|
+
if (el.textContent !== UI_CSS) el.textContent = UI_CSS;
|
|
1410
|
+
}
|
|
1057
1411
|
//#endregion
|
|
1058
1412
|
//#region src/client/wallpaper.ts
|
|
1059
1413
|
let wpEl = null;
|
|
@@ -1063,6 +1417,7 @@ window.__ModuleLoader__.load({
|
|
|
1063
1417
|
if (tokenStyleEl?.isConnected) return tokenStyleEl;
|
|
1064
1418
|
tokenStyleEl = document.createElement("style");
|
|
1065
1419
|
tokenStyleEl.dataset.plugin = "dsh-background-by-model-tokens";
|
|
1420
|
+
markOwnSheet(tokenStyleEl);
|
|
1066
1421
|
document.head.appendChild(tokenStyleEl);
|
|
1067
1422
|
return tokenStyleEl;
|
|
1068
1423
|
}
|
|
@@ -1206,6 +1561,7 @@ window.__ModuleLoader__.load({
|
|
|
1206
1561
|
if (partBlurStyleEl?.isConnected) return;
|
|
1207
1562
|
partBlurStyleEl = document.createElement("style");
|
|
1208
1563
|
partBlurStyleEl.dataset.plugin = "dsh-background-by-model-parts";
|
|
1564
|
+
markOwnSheet(partBlurStyleEl);
|
|
1209
1565
|
partBlurStyleEl.textContent = PART_BLUR_RULE;
|
|
1210
1566
|
document.head.appendChild(partBlurStyleEl);
|
|
1211
1567
|
}
|
|
@@ -1466,6 +1822,7 @@ window.__ModuleLoader__.load({
|
|
|
1466
1822
|
if (tableFixStyleEl === null) {
|
|
1467
1823
|
tableFixStyleEl = document.createElement("style");
|
|
1468
1824
|
tableFixStyleEl.dataset.plugin = "dsh-background-by-model-table-fix";
|
|
1825
|
+
markOwnSheet(tableFixStyleEl);
|
|
1469
1826
|
tableFixStyleEl.textContent = TABLE_FIX_RULE;
|
|
1470
1827
|
}
|
|
1471
1828
|
if (!tableFixStyleEl.isConnected) document.head.appendChild(tableFixStyleEl);
|
|
@@ -2098,231 +2455,1248 @@ window.__ModuleLoader__.load({
|
|
|
2098
2455
|
release();
|
|
2099
2456
|
};
|
|
2100
2457
|
}
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
.
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
.
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
.
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
.
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
.
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
.
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
.
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
.
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
.
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2458
|
+
//#endregion
|
|
2459
|
+
//#region src/host-contracts.ts
|
|
2460
|
+
const SIDEBAR_RIGHT = "dsh-client-ui-sidebar-right";
|
|
2461
|
+
/**
|
|
2462
|
+
* The contract table.
|
|
2463
|
+
*
|
|
2464
|
+
* Ordered by group and then by how visible a break is: the model hop first (the
|
|
2465
|
+
* plugin's whole purpose), then the surfaces it paints, then the tokens it
|
|
2466
|
+
* re-emits, then the regions it decorates.
|
|
2467
|
+
*/
|
|
2468
|
+
const HOST_CONTRACTS = [
|
|
2469
|
+
{
|
|
2470
|
+
id: "sessions.list",
|
|
2471
|
+
group: "model",
|
|
2472
|
+
kind: "host-service",
|
|
2473
|
+
criticality: "core",
|
|
2474
|
+
since: "0.1.7",
|
|
2475
|
+
label: {
|
|
2476
|
+
en: "Session list service",
|
|
2477
|
+
zh: "会话列表服务"
|
|
2478
|
+
},
|
|
2479
|
+
symptom: {
|
|
2480
|
+
en: "no session id can be read, so the background is stuck on rule 1 and never follows the model",
|
|
2481
|
+
zh: "读不到会话 id,背景会一直停在规则 1,不再跟着模型切换"
|
|
2482
|
+
},
|
|
2483
|
+
target: "sessions.list",
|
|
2484
|
+
checks: [
|
|
2485
|
+
{
|
|
2486
|
+
kind: "service",
|
|
2487
|
+
id: "sessions"
|
|
2488
|
+
},
|
|
2489
|
+
{
|
|
2490
|
+
kind: "path",
|
|
2491
|
+
service: "sessions",
|
|
2492
|
+
path: "list",
|
|
2493
|
+
expect: "observable"
|
|
2494
|
+
},
|
|
2495
|
+
{
|
|
2496
|
+
kind: "path",
|
|
2497
|
+
service: "sessions",
|
|
2498
|
+
path: "list.getSnapshot",
|
|
2499
|
+
expect: "function"
|
|
2500
|
+
}
|
|
2501
|
+
],
|
|
2502
|
+
sources: [{
|
|
2503
|
+
path: "dsh-api-session-controller/lib/client.js",
|
|
2504
|
+
literal: "retainedBy"
|
|
2505
|
+
}],
|
|
2506
|
+
usedBy: "src/client/modelbg.ts"
|
|
2507
|
+
},
|
|
2508
|
+
{
|
|
2509
|
+
id: "sessions.list.current",
|
|
2510
|
+
group: "model",
|
|
2511
|
+
kind: "host-service",
|
|
2512
|
+
criticality: "label",
|
|
2513
|
+
since: "<=0.1.6",
|
|
2514
|
+
label: {
|
|
2515
|
+
en: "Session list: legacy current id",
|
|
2516
|
+
zh: "会话列表:旧版 current 字段"
|
|
2517
|
+
},
|
|
2518
|
+
symptom: {
|
|
2519
|
+
en: "harmless on its own — the 0.1.7 hops below take over",
|
|
2520
|
+
zh: "本身无害 —— 0.1.7 上由下面的兜底路径接管"
|
|
2521
|
+
},
|
|
2522
|
+
target: "list.current",
|
|
2523
|
+
checks: [],
|
|
2524
|
+
sources: [{
|
|
2525
|
+
path: "dsh-api-session-controller/lib/client.js",
|
|
2526
|
+
literal: "retainedBy"
|
|
2527
|
+
}],
|
|
2528
|
+
usedBy: "src/client/modelbg.ts"
|
|
2529
|
+
},
|
|
2530
|
+
{
|
|
2531
|
+
id: "sessions.retainedBy.mainView",
|
|
2532
|
+
group: "model",
|
|
2533
|
+
kind: "host-service",
|
|
2534
|
+
criticality: "label",
|
|
2535
|
+
since: "0.1.7",
|
|
2536
|
+
label: {
|
|
2537
|
+
en: "Main-view retention on the session row",
|
|
2538
|
+
zh: "会话行的主视图占用标记"
|
|
2539
|
+
},
|
|
2540
|
+
symptom: {
|
|
2541
|
+
en: "while uiSession is not mounted yet, no session id can be derived",
|
|
2542
|
+
zh: "uiSession 尚未挂载时推导不出会话 id"
|
|
2543
|
+
},
|
|
2544
|
+
target: "byId[].retainedBy.mainView",
|
|
2545
|
+
checks: [],
|
|
2546
|
+
sources: [{
|
|
2547
|
+
path: "dsh-api-session-controller/lib/client.js",
|
|
2548
|
+
literal: "retainedBy"
|
|
2549
|
+
}],
|
|
2550
|
+
usedBy: "src/client/modelbg.ts"
|
|
2551
|
+
},
|
|
2552
|
+
{
|
|
2553
|
+
id: "uiSession.current",
|
|
2554
|
+
group: "model",
|
|
2555
|
+
kind: "host-service",
|
|
2556
|
+
criticality: "core",
|
|
2557
|
+
since: "0.1.7",
|
|
2558
|
+
label: {
|
|
2559
|
+
en: "uiSession service (main view session)",
|
|
2560
|
+
zh: "uiSession 服务(主视图会话)"
|
|
2561
|
+
},
|
|
2562
|
+
symptom: {
|
|
2563
|
+
en: "the session id can only be guessed from the list retention",
|
|
2564
|
+
zh: "会话 id 只能退回用列表占用标记去猜"
|
|
2565
|
+
},
|
|
2566
|
+
target: "uiSession.current",
|
|
2567
|
+
checks: [{
|
|
2568
|
+
kind: "path",
|
|
2569
|
+
service: "uiSession",
|
|
2570
|
+
path: "current",
|
|
2571
|
+
expect: "observable"
|
|
2572
|
+
}],
|
|
2573
|
+
sources: [{
|
|
2574
|
+
path: "dsh-client-ui-session/lib/client.js",
|
|
2575
|
+
literal: "uiSession"
|
|
2576
|
+
}],
|
|
2577
|
+
usedBy: "src/client/modelbg.ts"
|
|
2578
|
+
},
|
|
2579
|
+
{
|
|
2580
|
+
id: "sessions.binding.projections",
|
|
2581
|
+
group: "model",
|
|
2582
|
+
kind: "host-service",
|
|
2583
|
+
criticality: "core",
|
|
2584
|
+
since: "<=0.1.6",
|
|
2585
|
+
label: {
|
|
2586
|
+
en: "Session binding + projections",
|
|
2587
|
+
zh: "会话 binding 与 projections"
|
|
2588
|
+
},
|
|
2589
|
+
symptom: {
|
|
2590
|
+
en: "this session's own model selection is unreadable — only the host default is left",
|
|
2591
|
+
zh: "读不到本会话自己的模型选择,只剩宿主默认模型可用"
|
|
2592
|
+
},
|
|
2593
|
+
target: "binding(id).session.projections.faceOf(\"modelSelection\")",
|
|
2594
|
+
checks: [{
|
|
2595
|
+
kind: "path",
|
|
2596
|
+
service: "sessions",
|
|
2597
|
+
path: "binding",
|
|
2598
|
+
expect: "function"
|
|
2599
|
+
}],
|
|
2600
|
+
sources: [{
|
|
2601
|
+
path: "dsh-api-session-controller/lib/client.js",
|
|
2602
|
+
literal: "faceOf"
|
|
2603
|
+
}],
|
|
2604
|
+
usedBy: "src/client/modelbg.ts"
|
|
2605
|
+
},
|
|
2606
|
+
{
|
|
2607
|
+
id: "modelDirectories.directoryFor",
|
|
2608
|
+
group: "model",
|
|
2609
|
+
kind: "host-service",
|
|
2610
|
+
criticality: "label",
|
|
2611
|
+
since: "<=0.1.6",
|
|
2612
|
+
label: {
|
|
2613
|
+
en: "Model directory (display names)",
|
|
2614
|
+
zh: "模型目录(显示名)"
|
|
2615
|
+
},
|
|
2616
|
+
symptom: {
|
|
2617
|
+
en: "the model is shown as provider/model id instead of its display name",
|
|
2618
|
+
zh: "模型只会显示 provider/模型 id,读不到显示名"
|
|
2619
|
+
},
|
|
2620
|
+
target: "modelDirectories.directoryFor(id)",
|
|
2621
|
+
checks: [{
|
|
2622
|
+
kind: "path",
|
|
2623
|
+
service: "modelDirectories",
|
|
2624
|
+
path: "directoryFor",
|
|
2625
|
+
expect: "function"
|
|
2626
|
+
}],
|
|
2627
|
+
sources: [{
|
|
2628
|
+
path: "dsh-client-ui-model-selection/lib/client.js",
|
|
2629
|
+
literal: "modelSelection"
|
|
2630
|
+
}],
|
|
2631
|
+
usedBy: "src/client/modelbg.ts"
|
|
2632
|
+
},
|
|
2633
|
+
{
|
|
2634
|
+
id: "shell.overlay",
|
|
2635
|
+
group: "surface",
|
|
2636
|
+
kind: "host-attr",
|
|
2637
|
+
criticality: "looks",
|
|
2638
|
+
since: "<=0.1.6",
|
|
2639
|
+
label: {
|
|
2640
|
+
en: "AppFrame overlay + its three columns",
|
|
2641
|
+
zh: "主框架浮层与三列"
|
|
2642
|
+
},
|
|
2643
|
+
symptom: {
|
|
2644
|
+
en: "per-part blur and the main-background opacity cannot find the columns at all",
|
|
2645
|
+
zh: "分区域模糊与主背景透明度完全找不到那几列"
|
|
2646
|
+
},
|
|
2647
|
+
target: "[data-shell-overlay]",
|
|
2648
|
+
checks: [{
|
|
2649
|
+
kind: "attr",
|
|
2650
|
+
attr: "data-shell-overlay"
|
|
2651
|
+
}],
|
|
2652
|
+
sources: [{
|
|
2653
|
+
path: "dsh-client-ui-layout/lib/client.js",
|
|
2654
|
+
literal: "data-shell-overlay"
|
|
2655
|
+
}],
|
|
2656
|
+
usedBy: "src/client/wallpaper.ts"
|
|
2657
|
+
},
|
|
2658
|
+
{
|
|
2659
|
+
id: "settings.dialog",
|
|
2660
|
+
group: "surface",
|
|
2661
|
+
kind: "dom-selector",
|
|
2662
|
+
criticality: "looks",
|
|
2663
|
+
since: "<=0.1.6",
|
|
2664
|
+
label: {
|
|
2665
|
+
en: "Settings dialog (aria-modal)",
|
|
2666
|
+
zh: "设置弹窗(aria-modal)"
|
|
2667
|
+
},
|
|
2668
|
+
symptom: {
|
|
2669
|
+
en: "the settings panel opacity and blur sliders stop having any effect",
|
|
2670
|
+
zh: "设置界面透明度与模糊失效"
|
|
2671
|
+
},
|
|
2672
|
+
target: "[role=\"dialog\"][aria-modal=\"true\"][aria-labelledby]",
|
|
2673
|
+
checks: [{
|
|
2674
|
+
kind: "selector",
|
|
2675
|
+
selector: "div[role=\"dialog\"][aria-modal=\"true\"][aria-labelledby]",
|
|
2676
|
+
optionalWhen: "settingsClosed"
|
|
2677
|
+
}],
|
|
2678
|
+
sources: [{
|
|
2679
|
+
path: "dsh-client-ui-primitives/lib/index.js",
|
|
2680
|
+
literal: "aria-modal"
|
|
2681
|
+
}, {
|
|
2682
|
+
path: "src/client/components/ui.css.ts",
|
|
2683
|
+
literal: ".dab-card"
|
|
2684
|
+
}],
|
|
2685
|
+
usedBy: "src/client/wallpaper.ts"
|
|
2686
|
+
},
|
|
2687
|
+
{
|
|
2688
|
+
id: "settings.card",
|
|
2689
|
+
group: "surface",
|
|
2690
|
+
kind: "host-style",
|
|
2691
|
+
criticality: "looks",
|
|
2692
|
+
since: "<=0.1.6",
|
|
2693
|
+
label: {
|
|
2694
|
+
en: "Option card inside the dialog (.dab-card)",
|
|
2695
|
+
zh: "弹窗内的选项卡(.dab-card)"
|
|
2696
|
+
},
|
|
2697
|
+
symptom: {
|
|
2698
|
+
en: "the dialog option-panel blur has nothing to attach to",
|
|
2699
|
+
zh: "弹窗内选项卡的模糊没有附着对象"
|
|
2700
|
+
},
|
|
2701
|
+
target: ".dab-card",
|
|
2702
|
+
checks: [{
|
|
2703
|
+
kind: "rule",
|
|
2704
|
+
match: ".dab-card",
|
|
2705
|
+
side: "own",
|
|
2706
|
+
hint: "settings dialog card class"
|
|
2707
|
+
}],
|
|
2708
|
+
sources: [{
|
|
2709
|
+
path: "src/client/components/ui.css.ts",
|
|
2710
|
+
literal: ".dab-card"
|
|
2711
|
+
}],
|
|
2712
|
+
usedBy: "src/client/wallpaper.ts"
|
|
2713
|
+
},
|
|
2714
|
+
{
|
|
2715
|
+
id: "composer.card",
|
|
2716
|
+
group: "surface",
|
|
2717
|
+
kind: "host-attr",
|
|
2718
|
+
criticality: "looks",
|
|
2719
|
+
since: "<=0.1.6",
|
|
2720
|
+
label: {
|
|
2721
|
+
en: "Composer card",
|
|
2722
|
+
zh: "输入框卡片"
|
|
2723
|
+
},
|
|
2724
|
+
symptom: {
|
|
2725
|
+
en: "the composer loses its backdrop — the input blur slider does nothing",
|
|
2726
|
+
zh: "输入框失去毛玻璃背板,输入区模糊滑块失效"
|
|
2727
|
+
},
|
|
2728
|
+
target: "[data-composer-card]",
|
|
2729
|
+
checks: [{
|
|
2730
|
+
kind: "attr",
|
|
2731
|
+
attr: "data-composer-card",
|
|
2732
|
+
optionalWhen: "chatNotMounted"
|
|
2733
|
+
}],
|
|
2734
|
+
sources: [{
|
|
2735
|
+
path: "dsh-client-ui-conversation/lib/client.js",
|
|
2736
|
+
literal: "data-composer-card"
|
|
2737
|
+
}],
|
|
2738
|
+
usedBy: "src/client/wallpaper.ts"
|
|
2739
|
+
},
|
|
2740
|
+
{
|
|
2741
|
+
id: "cordis.panel",
|
|
2742
|
+
group: "surface",
|
|
2743
|
+
kind: "host-attr",
|
|
2744
|
+
criticality: "label",
|
|
2745
|
+
since: "<=0.1.6",
|
|
2746
|
+
label: {
|
|
2747
|
+
en: "Cordis panel",
|
|
2748
|
+
zh: "Cordis 面板"
|
|
2749
|
+
},
|
|
2750
|
+
symptom: {
|
|
2751
|
+
en: "the Cordis panel keeps the host surface instead of the input slider's alpha",
|
|
2752
|
+
zh: "Cordis 面板拿不到输入区滑块的透明度,仍是宿主表面"
|
|
2753
|
+
},
|
|
2754
|
+
target: "[data-cordis-panel]",
|
|
2755
|
+
checks: [{
|
|
2756
|
+
kind: "attr",
|
|
2757
|
+
attr: "data-cordis-panel",
|
|
2758
|
+
optionalWhen: "cordisClosed"
|
|
2759
|
+
}],
|
|
2760
|
+
sources: [{
|
|
2761
|
+
path: "dsh-client-ui-cordis/lib/client.js",
|
|
2762
|
+
literal: "data-cordis-panel"
|
|
2763
|
+
}],
|
|
2764
|
+
usedBy: "src/client/wallpaper.ts"
|
|
2765
|
+
},
|
|
2766
|
+
{
|
|
2767
|
+
id: "rightbar.panel",
|
|
2768
|
+
group: "surface",
|
|
2769
|
+
kind: "host-attr",
|
|
2770
|
+
criticality: "looks",
|
|
2771
|
+
since: "0.1.7",
|
|
2772
|
+
label: {
|
|
2773
|
+
en: "File-preview panel host",
|
|
2774
|
+
zh: "文件预览面板宿主"
|
|
2775
|
+
},
|
|
2776
|
+
symptom: {
|
|
2777
|
+
en: "the right panel loses its own opacity/blur card and falls back to the main background",
|
|
2778
|
+
zh: "右侧栏失去自己的透明度/模糊控制,退回跟随主背景"
|
|
2779
|
+
},
|
|
2780
|
+
target: "[data-sidebar-right-panel]",
|
|
2781
|
+
checks: [{
|
|
2782
|
+
kind: "selector",
|
|
2783
|
+
selector: "div[data-sidebar-right-panel]",
|
|
2784
|
+
optionalWhen: "noRightPanel"
|
|
2785
|
+
}],
|
|
2786
|
+
sources: [{
|
|
2787
|
+
path: `${SIDEBAR_RIGHT}/lib/client.js`,
|
|
2788
|
+
literal: "data-sidebar-right-panel"
|
|
2789
|
+
}],
|
|
2790
|
+
usedBy: "src/client/wallpaper.ts"
|
|
2791
|
+
},
|
|
2792
|
+
{
|
|
2793
|
+
id: "rightbar.openFlag",
|
|
2794
|
+
group: "surface",
|
|
2795
|
+
kind: "host-attr",
|
|
2796
|
+
criticality: "looks",
|
|
2797
|
+
since: "0.1.7",
|
|
2798
|
+
label: {
|
|
2799
|
+
en: "Right panel open/closed flag",
|
|
2800
|
+
zh: "右侧栏开合标记"
|
|
2801
|
+
},
|
|
2802
|
+
symptom: {
|
|
2803
|
+
en: "the panel's backdrop cannot tell open from closed — a frosted plate covers the empty half",
|
|
2804
|
+
zh: "分不清右侧栏开着还是关着,空白的右半屏会出现磨砂板"
|
|
2805
|
+
},
|
|
2806
|
+
target: "[data-sidebar-right-open]",
|
|
2807
|
+
checks: [{
|
|
2808
|
+
kind: "rule",
|
|
2809
|
+
match: "[data-sidebar-right-open]",
|
|
2810
|
+
hint: "right panel open flag"
|
|
2811
|
+
}],
|
|
2812
|
+
sources: [{
|
|
2813
|
+
path: `${SIDEBAR_RIGHT}/lib/client.js`,
|
|
2814
|
+
literal: "data-sidebar-right-open"
|
|
2815
|
+
}],
|
|
2816
|
+
usedBy: "src/client/wallpaper.ts"
|
|
2817
|
+
},
|
|
2818
|
+
{
|
|
2819
|
+
id: "dockkit.content",
|
|
2820
|
+
group: "surface",
|
|
2821
|
+
kind: "host-attr",
|
|
2822
|
+
criticality: "core",
|
|
2823
|
+
since: "0.1.7",
|
|
2824
|
+
label: {
|
|
2825
|
+
en: "Dock tab host (the panel's painted surface)",
|
|
2826
|
+
zh: "停靠页宿主(面板真正的表面)"
|
|
2827
|
+
},
|
|
2828
|
+
symptom: {
|
|
2829
|
+
en: "nothing carries the panel surface any more — the right-panel card is painted on a container that is always full width",
|
|
2830
|
+
zh: "面板表面不再落在任何元素上,右面板卡片会被画在一个恒定满宽的容器上"
|
|
2831
|
+
},
|
|
2832
|
+
target: "[data-dockkit-content]",
|
|
2833
|
+
checks: [{
|
|
2834
|
+
kind: "rule",
|
|
2835
|
+
match: "[data-dockkit-content]",
|
|
2836
|
+
hint: "dock tab host"
|
|
2837
|
+
}],
|
|
2838
|
+
sources: [{
|
|
2839
|
+
path: ".",
|
|
2840
|
+
literal: "dockkit-content",
|
|
2841
|
+
root: "frontend"
|
|
2842
|
+
}],
|
|
2843
|
+
usedBy: "src/client/wallpaper.ts"
|
|
2844
|
+
},
|
|
2845
|
+
{
|
|
2846
|
+
id: "dockkit.empty",
|
|
2847
|
+
group: "surface",
|
|
2848
|
+
kind: "host-attr",
|
|
2849
|
+
criticality: "looks",
|
|
2850
|
+
since: "0.1.7",
|
|
2851
|
+
label: {
|
|
2852
|
+
en: "Dock empty seat",
|
|
2853
|
+
zh: "停靠空位"
|
|
2854
|
+
},
|
|
2855
|
+
symptom: {
|
|
2856
|
+
en: "an empty preview pane paints the host surface over the wallpaper",
|
|
2857
|
+
zh: "空的预览格会用宿主表面盖住壁纸"
|
|
2858
|
+
},
|
|
2859
|
+
target: "[data-dockkit-empty]",
|
|
2860
|
+
checks: [{
|
|
2861
|
+
kind: "rule",
|
|
2862
|
+
match: "[data-dockkit-empty]",
|
|
2863
|
+
hint: "dock empty seat"
|
|
2864
|
+
}],
|
|
2865
|
+
sources: [{
|
|
2866
|
+
path: `${SIDEBAR_RIGHT}/lib/client.js`,
|
|
2867
|
+
literal: "data-dockkit-empty"
|
|
2868
|
+
}],
|
|
2869
|
+
usedBy: "src/client/wallpaper.ts"
|
|
2870
|
+
},
|
|
2871
|
+
{
|
|
2872
|
+
id: "dockkit.float",
|
|
2873
|
+
group: "surface",
|
|
2874
|
+
kind: "host-attr",
|
|
2875
|
+
criticality: "label",
|
|
2876
|
+
since: "0.1.7",
|
|
2877
|
+
label: {
|
|
2878
|
+
en: "Floating dock pane flag",
|
|
2879
|
+
zh: "浮动停靠面板标记"
|
|
2880
|
+
},
|
|
2881
|
+
symptom: {
|
|
2882
|
+
en: "floating panes would be painted like docked ones, frosting a panel that should stay the host's",
|
|
2883
|
+
zh: "浮动面板会被当作停靠面板上色,本该保持宿主样式的面板被磨砂"
|
|
2884
|
+
},
|
|
2885
|
+
target: "[data-dockkit-float]",
|
|
2886
|
+
checks: [{
|
|
2887
|
+
kind: "rule",
|
|
2888
|
+
match: "[data-dockkit-float]",
|
|
2889
|
+
hint: "floating pane flag"
|
|
2890
|
+
}],
|
|
2891
|
+
sources: [{
|
|
2892
|
+
path: `${SIDEBAR_RIGHT}/lib/client.js`,
|
|
2893
|
+
literal: "data-dockkit-float"
|
|
2894
|
+
}],
|
|
2895
|
+
usedBy: "src/client/wallpaper.ts"
|
|
2896
|
+
},
|
|
2897
|
+
{
|
|
2898
|
+
id: "chat.flow",
|
|
2899
|
+
group: "region",
|
|
2900
|
+
kind: "dom-selector",
|
|
2901
|
+
criticality: "looks",
|
|
2902
|
+
since: "<=0.1.6",
|
|
2903
|
+
label: {
|
|
2904
|
+
en: "Chat message column",
|
|
2905
|
+
zh: "对话消息列"
|
|
2906
|
+
},
|
|
2907
|
+
symptom: {
|
|
2908
|
+
en: "the conversation card (and its opacity/blur) cannot find the message column",
|
|
2909
|
+
zh: "对话卡片(及其透明度/模糊)找不到消息列"
|
|
2910
|
+
},
|
|
2911
|
+
target: "[data-chat-flow]",
|
|
2912
|
+
checks: [{
|
|
2913
|
+
kind: "selector",
|
|
2914
|
+
selector: "[data-chat-flow]",
|
|
2915
|
+
optionalWhen: "chatNotMounted"
|
|
2916
|
+
}],
|
|
2917
|
+
sources: [{
|
|
2918
|
+
path: "dsh-client-ui-chat/lib/client.js",
|
|
2919
|
+
literal: "data-chat-flow"
|
|
2920
|
+
}],
|
|
2921
|
+
usedBy: "src/client/wallpaper.ts"
|
|
2922
|
+
},
|
|
2923
|
+
{
|
|
2924
|
+
id: "conversation.scroll",
|
|
2925
|
+
group: "region",
|
|
2926
|
+
kind: "dom-selector",
|
|
2927
|
+
criticality: "looks",
|
|
2928
|
+
since: "<=0.1.6",
|
|
2929
|
+
label: {
|
|
2930
|
+
en: "Conversation scrollport",
|
|
2931
|
+
zh: "对话滚动区"
|
|
2932
|
+
},
|
|
2933
|
+
symptom: {
|
|
2934
|
+
en: "the marker-less fallback would wrap the whole scrollport in the conversation card",
|
|
2935
|
+
zh: "标记缺失时的兜底会把整个滚动区套上对话卡片"
|
|
2936
|
+
},
|
|
2937
|
+
target: "[data-conversation-scroll]",
|
|
2938
|
+
checks: [{
|
|
2939
|
+
kind: "selector",
|
|
2940
|
+
selector: "[data-conversation-scroll]",
|
|
2941
|
+
optionalWhen: "chatNotMounted"
|
|
2942
|
+
}],
|
|
2943
|
+
sources: [{
|
|
2944
|
+
path: "dsh-client-ui-conversation/lib/client.js",
|
|
2945
|
+
literal: "data-conversation-scroll"
|
|
2946
|
+
}],
|
|
2947
|
+
usedBy: "src/client/wallpaper.ts"
|
|
2948
|
+
},
|
|
2949
|
+
{
|
|
2950
|
+
id: "trajectory.root",
|
|
2951
|
+
group: "region",
|
|
2952
|
+
kind: "dom-selector",
|
|
2953
|
+
criticality: "looks",
|
|
2954
|
+
since: "<=0.1.6",
|
|
2955
|
+
label: {
|
|
2956
|
+
en: "Trajectory view root",
|
|
2957
|
+
zh: "轨迹页根节点"
|
|
2958
|
+
},
|
|
2959
|
+
symptom: {
|
|
2960
|
+
en: "the trajectory opacity/blur sliders stop reaching the trajectory page",
|
|
2961
|
+
zh: "轨迹页的透明度与模糊滑块失效"
|
|
2962
|
+
},
|
|
2963
|
+
target: "[data-conversation-composer-overlay]",
|
|
2964
|
+
checks: [{
|
|
2965
|
+
kind: "selector",
|
|
2966
|
+
selector: "[data-conversation-composer-overlay]",
|
|
2967
|
+
optionalWhen: "trajectoryNotMounted"
|
|
2968
|
+
}],
|
|
2969
|
+
sources: [{
|
|
2970
|
+
path: "dsh-client-ui-trajectory/lib/client.js",
|
|
2971
|
+
literal: "data-conversation-composer-overlay"
|
|
2972
|
+
}],
|
|
2973
|
+
usedBy: "src/client/wallpaper.ts"
|
|
2974
|
+
},
|
|
2975
|
+
{
|
|
2976
|
+
id: "markdown.wideTable",
|
|
2977
|
+
group: "region",
|
|
2978
|
+
kind: "host-style",
|
|
2979
|
+
criticality: "label",
|
|
2980
|
+
since: "<=0.1.6",
|
|
2981
|
+
label: {
|
|
2982
|
+
en: "Wide markdown table clamp",
|
|
2983
|
+
zh: "宽表格收拢样式"
|
|
2984
|
+
},
|
|
2985
|
+
symptom: {
|
|
2986
|
+
en: "a wide table bleeds outside the conversation card instead of scrolling inside it",
|
|
2987
|
+
zh: "宽表格会溢出到对话卡片外,而不是在卡片内横向滚动"
|
|
2988
|
+
},
|
|
2989
|
+
target: ".md-table-wide",
|
|
2990
|
+
checks: [{
|
|
2991
|
+
kind: "rule",
|
|
2992
|
+
match: ".md-table-wide",
|
|
2993
|
+
hint: "markdown table class"
|
|
2994
|
+
}],
|
|
2995
|
+
sources: [{
|
|
2996
|
+
path: "dsh-client-ui-chat/lib/client.js",
|
|
2997
|
+
literal: "md-table-wide"
|
|
2998
|
+
}],
|
|
2999
|
+
usedBy: "src/client/wallpaper.ts"
|
|
3000
|
+
},
|
|
3001
|
+
{
|
|
3002
|
+
id: "token.bgBase",
|
|
3003
|
+
group: "token",
|
|
3004
|
+
kind: "host-token",
|
|
3005
|
+
criticality: "looks",
|
|
3006
|
+
since: "<=0.1.6",
|
|
3007
|
+
label: {
|
|
3008
|
+
en: "Main background surface",
|
|
3009
|
+
zh: "主背景表面"
|
|
3010
|
+
},
|
|
3011
|
+
symptom: {
|
|
3012
|
+
en: "the main-background slider and the right-panel card lose their surface",
|
|
3013
|
+
zh: "主背景透明度与右侧栏卡片失去表面色"
|
|
3014
|
+
},
|
|
3015
|
+
target: "--dsw-alias-bg-base",
|
|
3016
|
+
checks: [{
|
|
3017
|
+
kind: "rule",
|
|
3018
|
+
match: "--dsw-alias-bg-base",
|
|
3019
|
+
hint: "platform palette"
|
|
3020
|
+
}, {
|
|
3021
|
+
kind: "computed",
|
|
3022
|
+
token: "--dsw-alias-bg-base"
|
|
3023
|
+
}],
|
|
3024
|
+
sources: [{
|
|
3025
|
+
path: "dsh-client-ui-theme/lib/client.js",
|
|
3026
|
+
literal: "--dsw-alias-bg-base"
|
|
3027
|
+
}],
|
|
3028
|
+
usedBy: "src/client/wallpaper.ts"
|
|
3029
|
+
},
|
|
3030
|
+
{
|
|
3031
|
+
id: "token.layers",
|
|
3032
|
+
group: "token",
|
|
3033
|
+
kind: "host-token",
|
|
3034
|
+
criticality: "looks",
|
|
3035
|
+
since: "<=0.1.6",
|
|
3036
|
+
label: {
|
|
3037
|
+
en: "Layered surfaces (cards)",
|
|
3038
|
+
zh: "层次表面(卡片)"
|
|
3039
|
+
},
|
|
3040
|
+
symptom: {
|
|
3041
|
+
en: "the card slider, the conversation card and the trajectory slider go blank",
|
|
3042
|
+
zh: "卡片滑块、对话卡片与轨迹滑块失去颜色"
|
|
3043
|
+
},
|
|
3044
|
+
target: "--dsw-alias-bg-layer-1/2/3",
|
|
3045
|
+
checks: [
|
|
3046
|
+
{
|
|
3047
|
+
kind: "rule",
|
|
3048
|
+
match: "--dsw-alias-bg-layer-1",
|
|
3049
|
+
hint: "platform palette"
|
|
3050
|
+
},
|
|
3051
|
+
{
|
|
3052
|
+
kind: "computed",
|
|
3053
|
+
token: "--dsw-alias-bg-layer-1"
|
|
3054
|
+
},
|
|
3055
|
+
{
|
|
3056
|
+
kind: "computed",
|
|
3057
|
+
token: "--dsw-alias-bg-layer-2"
|
|
3058
|
+
},
|
|
3059
|
+
{
|
|
3060
|
+
kind: "computed",
|
|
3061
|
+
token: "--dsw-alias-bg-layer-3"
|
|
3062
|
+
}
|
|
3063
|
+
],
|
|
3064
|
+
sources: [{
|
|
3065
|
+
path: "dsh-client-ui-theme/lib/client.js",
|
|
3066
|
+
literal: "--dsw-alias-bg-layer-1"
|
|
3067
|
+
}],
|
|
3068
|
+
usedBy: "src/client/wallpaper.ts"
|
|
3069
|
+
},
|
|
3070
|
+
{
|
|
3071
|
+
id: "token.menuSurface",
|
|
3072
|
+
group: "token",
|
|
3073
|
+
kind: "host-token",
|
|
3074
|
+
criticality: "core",
|
|
3075
|
+
since: "0.1.7",
|
|
3076
|
+
label: {
|
|
3077
|
+
en: "Menu material fill (0.1.7 split)",
|
|
3078
|
+
zh: "菜单材质填充(0.1.7 拆分)"
|
|
3079
|
+
},
|
|
3080
|
+
symptom: {
|
|
3081
|
+
en: "the menu panel and its sticky group headings take two different fills — the solid band across the model picker",
|
|
3082
|
+
zh: "菜单面板与吸顶分组标题用了两种填充 —— 模型选择器上那道白道"
|
|
3083
|
+
},
|
|
3084
|
+
target: "--dsw-menu-surface-fill",
|
|
3085
|
+
checks: [{
|
|
3086
|
+
kind: "rule",
|
|
3087
|
+
match: "--dsw-menu-surface-fill",
|
|
3088
|
+
hint: "MenuSurface material fill"
|
|
3089
|
+
}, {
|
|
3090
|
+
kind: "computed",
|
|
3091
|
+
token: "--dsw-menu-surface-fill",
|
|
3092
|
+
on: ".dab-root"
|
|
3093
|
+
}],
|
|
3094
|
+
sources: [{
|
|
3095
|
+
path: "dsh-client-ui-theme/lib/client.js",
|
|
3096
|
+
literal: "--dsw-menu-surface-fill"
|
|
3097
|
+
}, {
|
|
3098
|
+
path: "dsh-client-ui-primitives/lib/MenuSurface.module.css",
|
|
3099
|
+
literal: "var(--dsw-menu-surface-fill)"
|
|
3100
|
+
}],
|
|
3101
|
+
usedBy: "src/client/utils/color.ts, src/client/wallpaper.ts"
|
|
3102
|
+
},
|
|
3103
|
+
{
|
|
3104
|
+
id: "token.menuAlias",
|
|
3105
|
+
group: "token",
|
|
3106
|
+
kind: "host-token",
|
|
3107
|
+
criticality: "looks",
|
|
3108
|
+
since: "<=0.1.6",
|
|
3109
|
+
label: {
|
|
3110
|
+
en: "Menu overlay fill",
|
|
3111
|
+
zh: "菜单浮层填充"
|
|
3112
|
+
},
|
|
3113
|
+
symptom: {
|
|
3114
|
+
en: "dropdowns, the slash menu and popovers keep the host fill while the panel follows the plugin",
|
|
3115
|
+
zh: "下拉、斜杠菜单与浮层保持宿主填充,而面板跟随插件,两边不一致"
|
|
3116
|
+
},
|
|
3117
|
+
target: "--dsw-specific-menu",
|
|
3118
|
+
checks: [{
|
|
3119
|
+
kind: "rule",
|
|
3120
|
+
match: "--dsw-specific-menu",
|
|
3121
|
+
hint: "menu overlay fill"
|
|
3122
|
+
}, {
|
|
3123
|
+
kind: "computed",
|
|
3124
|
+
token: "--dsw-specific-menu",
|
|
3125
|
+
on: ".dab-root"
|
|
3126
|
+
}],
|
|
3127
|
+
sources: [{
|
|
3128
|
+
path: "dsh-client-ui-theme/lib/client.js",
|
|
3129
|
+
literal: "--dsw-specific-menu"
|
|
3130
|
+
}, {
|
|
3131
|
+
path: "dsh-client-ui-chat/lib/client.js",
|
|
3132
|
+
literal: "--dsw-specific-menu"
|
|
3133
|
+
}],
|
|
3134
|
+
usedBy: "src/client/utils/color.ts, src/client/wallpaper.ts"
|
|
3135
|
+
},
|
|
3136
|
+
{
|
|
3137
|
+
id: "token.sidebarFill",
|
|
3138
|
+
group: "token",
|
|
3139
|
+
kind: "host-token",
|
|
3140
|
+
criticality: "looks",
|
|
3141
|
+
since: "<=0.1.6",
|
|
3142
|
+
label: {
|
|
3143
|
+
en: "Sidebar fill",
|
|
3144
|
+
zh: "左侧栏填充"
|
|
3145
|
+
},
|
|
3146
|
+
symptom: {
|
|
3147
|
+
en: "the sidebar opacity slider stops tinting the sidebar",
|
|
3148
|
+
zh: "左侧栏透明度滑块失效"
|
|
3149
|
+
},
|
|
3150
|
+
target: "--dsw-specific-sidebar-fill",
|
|
3151
|
+
checks: [{
|
|
3152
|
+
kind: "rule",
|
|
3153
|
+
match: "--dsw-specific-sidebar-fill",
|
|
3154
|
+
hint: "sidebar fill"
|
|
3155
|
+
}, {
|
|
3156
|
+
kind: "computed",
|
|
3157
|
+
token: "--dsw-specific-sidebar-fill"
|
|
3158
|
+
}],
|
|
3159
|
+
sources: [{
|
|
3160
|
+
path: "dsh-client-ui-theme/lib/client.js",
|
|
3161
|
+
literal: "--dsw-specific-sidebar-fill"
|
|
3162
|
+
}],
|
|
3163
|
+
usedBy: "src/client/utils/color.ts, src/client/wallpaper.ts"
|
|
3164
|
+
},
|
|
3165
|
+
{
|
|
3166
|
+
id: "token.inputFill",
|
|
3167
|
+
group: "token",
|
|
3168
|
+
kind: "host-token",
|
|
3169
|
+
criticality: "looks",
|
|
3170
|
+
since: "<=0.1.6",
|
|
3171
|
+
label: {
|
|
3172
|
+
en: "Input/control surface",
|
|
3173
|
+
zh: "输入与控件表面"
|
|
3174
|
+
},
|
|
3175
|
+
symptom: {
|
|
3176
|
+
en: "the input slider stops tinting the composer and the controls",
|
|
3177
|
+
zh: "输入区透明度滑块失效"
|
|
3178
|
+
},
|
|
3179
|
+
target: "--dsw-specific-input-major",
|
|
3180
|
+
checks: [{
|
|
3181
|
+
kind: "rule",
|
|
3182
|
+
match: "--dsw-specific-input-major",
|
|
3183
|
+
hint: "input/control fill"
|
|
3184
|
+
}, {
|
|
3185
|
+
kind: "computed",
|
|
3186
|
+
token: "--dsw-specific-input-major"
|
|
3187
|
+
}],
|
|
3188
|
+
sources: [{
|
|
3189
|
+
path: "dsh-client-ui-theme/lib/client.js",
|
|
3190
|
+
literal: "--dsw-specific-input-major"
|
|
3191
|
+
}],
|
|
3192
|
+
usedBy: "src/client/utils/color.ts, src/client/wallpaper.ts"
|
|
3193
|
+
},
|
|
3194
|
+
{
|
|
3195
|
+
id: "token.schemeFlag",
|
|
3196
|
+
group: "token",
|
|
3197
|
+
kind: "host-attr",
|
|
3198
|
+
criticality: "core",
|
|
3199
|
+
since: "<=0.1.6",
|
|
3200
|
+
label: {
|
|
3201
|
+
en: "Dark-scheme flag on <body>",
|
|
3202
|
+
zh: "<body> 上的深色标记"
|
|
3203
|
+
},
|
|
3204
|
+
symptom: {
|
|
3205
|
+
en: "a dark rule can no longer force the dark palette — light surfaces flash through",
|
|
3206
|
+
zh: "深色规则无法强制深色调色板,会闪出浅色表面"
|
|
3207
|
+
},
|
|
3208
|
+
target: "body[data-ds-dark-theme]",
|
|
3209
|
+
checks: [{
|
|
3210
|
+
kind: "rule",
|
|
3211
|
+
match: "data-ds-dark-theme",
|
|
3212
|
+
hint: "dark scheme attribute"
|
|
3213
|
+
}],
|
|
3214
|
+
sources: [{
|
|
3215
|
+
path: "dsh-client-ui-theme/lib/client.js",
|
|
3216
|
+
literal: "data-ds-dark-theme"
|
|
3217
|
+
}, {
|
|
3218
|
+
path: ".",
|
|
3219
|
+
literal: "data-ds-dark-theme",
|
|
3220
|
+
root: "frontend"
|
|
3221
|
+
}],
|
|
3222
|
+
usedBy: "src/client/wallpaper.ts"
|
|
3223
|
+
},
|
|
3224
|
+
{
|
|
3225
|
+
id: "token.labelCaption",
|
|
3226
|
+
group: "token",
|
|
3227
|
+
kind: "host-token",
|
|
3228
|
+
criticality: "label",
|
|
3229
|
+
since: "<=0.1.6",
|
|
3230
|
+
label: {
|
|
3231
|
+
en: "Caption label token",
|
|
3232
|
+
zh: "弱化文字令牌"
|
|
3233
|
+
},
|
|
3234
|
+
symptom: {
|
|
3235
|
+
en: "placeholder text falls back to a hard-coded grey instead of the theme's caption colour",
|
|
3236
|
+
zh: "占位文字退回固定灰色,不再跟随主题的弱化色"
|
|
3237
|
+
},
|
|
3238
|
+
target: "--dsw-alias-label-caption",
|
|
3239
|
+
checks: [{
|
|
3240
|
+
kind: "rule",
|
|
3241
|
+
match: "--dsw-alias-label-caption",
|
|
3242
|
+
hint: "label palette"
|
|
3243
|
+
}, {
|
|
3244
|
+
kind: "computed",
|
|
3245
|
+
token: "--dsw-alias-label-caption"
|
|
3246
|
+
}],
|
|
3247
|
+
sources: [{
|
|
3248
|
+
path: "dsh-client-ui-theme/lib/client.js",
|
|
3249
|
+
literal: "--dsw-alias-label-caption"
|
|
3250
|
+
}],
|
|
3251
|
+
usedBy: "src/client/wallpaper.ts"
|
|
2322
3252
|
}
|
|
2323
|
-
|
|
3253
|
+
];
|
|
3254
|
+
/** Localized label lookup for a contract. */
|
|
3255
|
+
function labelOf(c, lang) {
|
|
3256
|
+
return lang === "zh" ? c.label.zh : c.label.en;
|
|
3257
|
+
}
|
|
3258
|
+
/** Localized symptom lookup for a contract. */
|
|
3259
|
+
function symptomOf(c, lang) {
|
|
3260
|
+
return lang === "zh" ? c.symptom.zh : c.symptom.en;
|
|
3261
|
+
}
|
|
3262
|
+
/** Groups the panel renders, in order. */
|
|
3263
|
+
const CONTRACT_GROUPS = [
|
|
3264
|
+
{
|
|
3265
|
+
id: "model",
|
|
3266
|
+
label: {
|
|
3267
|
+
en: "Model → rule resolution",
|
|
3268
|
+
zh: "模型 → 规则匹配"
|
|
3269
|
+
}
|
|
3270
|
+
},
|
|
3271
|
+
{
|
|
3272
|
+
id: "surface",
|
|
3273
|
+
label: {
|
|
3274
|
+
en: "Surfaces the plugin paints",
|
|
3275
|
+
zh: "插件着色的界面区域"
|
|
3276
|
+
}
|
|
3277
|
+
},
|
|
3278
|
+
{
|
|
3279
|
+
id: "token",
|
|
3280
|
+
label: {
|
|
3281
|
+
en: "Design tokens re-emitted",
|
|
3282
|
+
zh: "被重新发出的设计令牌"
|
|
3283
|
+
}
|
|
3284
|
+
},
|
|
3285
|
+
{
|
|
3286
|
+
id: "region",
|
|
3287
|
+
label: {
|
|
3288
|
+
en: "Conversation views",
|
|
3289
|
+
zh: "对话视图"
|
|
3290
|
+
}
|
|
3291
|
+
}
|
|
3292
|
+
];
|
|
3293
|
+
/** Overall verdict of a report, for the panel's headline. */
|
|
3294
|
+
function verdictOf(report) {
|
|
3295
|
+
if (report.results.length === 0) return "unknown";
|
|
3296
|
+
if (report.summary.fail > 0) return report.summary.pass > 0 ? "partial" : "broken";
|
|
3297
|
+
return report.summary.pass > 0 ? "ok" : "unknown";
|
|
3298
|
+
}
|
|
3299
|
+
/** Markdown rendering of a report — the thing a user pastes into an issue. */
|
|
3300
|
+
function reportToMarkdown(report, lang) {
|
|
3301
|
+
const zh = lang === "zh";
|
|
3302
|
+
const lines = [];
|
|
3303
|
+
lines.push(zh ? "## dsh-background-by-model 宿主自检" : "## dsh-background-by-model host check");
|
|
3304
|
+
lines.push("");
|
|
3305
|
+
lines.push(`- ${zh ? "阶段" : "phase"}: ${report.phase}`);
|
|
3306
|
+
if (report.plugin.version !== "") lines.push(`- ${zh ? "插件" : "plugin"}: ${report.plugin.version} (dsh >=${report.plugin.floor})`);
|
|
3307
|
+
lines.push(`- dsh: ${report.host.version !== "" ? report.host.version : zh ? "未读到" : "unreadable"}${report.host.compatible === false ? zh ? "(低于插件下限)" : " (below the plugin floor)" : ""}`);
|
|
3308
|
+
if (report.host.node !== "") lines.push(`- node: ${report.host.node}`);
|
|
3309
|
+
if (report.host.root !== "") lines.push(`- host root: ${report.host.root}`);
|
|
3310
|
+
if (report.host.note !== "") lines.push(`- note: ${report.host.note}`);
|
|
3311
|
+
if (report.host.model !== void 0) {
|
|
3312
|
+
const m = report.host.model;
|
|
3313
|
+
lines.push(`- ${zh ? "模型" : "model"}: \`${m.text !== "" ? m.text : "(none)"}\` · ${m.source}${m.note !== "" ? ` · ${m.note}` : ""} · ${zh ? "规则" : "rule"} ${m.rule !== "" ? m.rule : "-"}${m.matched ? "" : zh ? "(兜底)" : " (fallback)"}`);
|
|
3314
|
+
}
|
|
3315
|
+
lines.push("");
|
|
3316
|
+
lines.push(`### ${zh ? "结论" : "Summary"}: ${report.summary.pass} pass / ${report.summary.fail} fail / ${report.summary.skip} n/a`);
|
|
3317
|
+
lines.push("");
|
|
3318
|
+
for (const r of report.results) {
|
|
3319
|
+
const mark = r.status === "pass" ? "PASS" : r.status === "fail" ? "**FAIL**" : r.status === "skip" ? "n/a" : "info";
|
|
3320
|
+
lines.push(`- ${mark} \`${r.id}\` — ${r.label} · \`${r.target}\``);
|
|
3321
|
+
if (r.status === "fail") {
|
|
3322
|
+
lines.push(` - ${zh ? "现象" : "symptom"}: ${r.symptom}`);
|
|
3323
|
+
lines.push(` - ${zh ? "原因" : "reason"}: ${r.reason ?? r.detail}`);
|
|
3324
|
+
lines.push(` - ${zh ? "期望位置" : "expected in"}: ${r.sources.map((s) => s.path).join(", ")}`);
|
|
3325
|
+
} else if (r.detail !== "") lines.push(` - ${r.detail}`);
|
|
3326
|
+
}
|
|
3327
|
+
return lines.join("\n");
|
|
3328
|
+
}
|
|
3329
|
+
//#endregion
|
|
3330
|
+
//#region src/client/judge.ts
|
|
3331
|
+
/** The real browser environment. */
|
|
3332
|
+
function browserEnv(ctx) {
|
|
3333
|
+
const tokenRoot = () => {
|
|
3334
|
+
if (typeof document === "undefined") return null;
|
|
3335
|
+
return document.body ?? document.documentElement ?? null;
|
|
3336
|
+
};
|
|
3337
|
+
return {
|
|
3338
|
+
querySelector: (selector) => document.querySelector(selector),
|
|
3339
|
+
tokenRoot,
|
|
3340
|
+
computedValue: (token, on) => {
|
|
3341
|
+
if (typeof document === "undefined") return "";
|
|
3342
|
+
const host = on ?? tokenRoot();
|
|
3343
|
+
if (host === null) return "";
|
|
3344
|
+
return getComputedStyle(host).getPropertyValue(token).trim();
|
|
3345
|
+
},
|
|
3346
|
+
styleSheets: () => typeof document === "undefined" ? [] : Array.from(document.styleSheets),
|
|
3347
|
+
ctx
|
|
3348
|
+
};
|
|
3349
|
+
}
|
|
3350
|
+
const ANCHORS = {
|
|
3351
|
+
settingsClosed: "div[role=\"dialog\"][aria-modal=\"true\"][aria-labelledby]",
|
|
3352
|
+
chatNotMounted: "[data-chat-flow],[data-conversation-scroll]",
|
|
3353
|
+
trajectoryNotMounted: "[data-conversation-composer-overlay]",
|
|
3354
|
+
noRightPanel: "div[data-sidebar-right-panel],[data-dockkit-strip],[data-dockkit-pane]",
|
|
3355
|
+
cordisClosed: "[data-cordis-panel]"
|
|
3356
|
+
};
|
|
3357
|
+
function mounted(env, anchorId) {
|
|
3358
|
+
const sel = ANCHORS[anchorId];
|
|
3359
|
+
if (sel === void 0) return true;
|
|
3360
|
+
try {
|
|
3361
|
+
return env.querySelector(sel) !== null;
|
|
3362
|
+
} catch {
|
|
3363
|
+
return false;
|
|
3364
|
+
}
|
|
3365
|
+
}
|
|
3366
|
+
/** Walk a dotted path off a root object, tolerating a missing first hop. */
|
|
3367
|
+
function walkPath(root, path) {
|
|
3368
|
+
let cur = root;
|
|
3369
|
+
for (const key of path.split(".")) {
|
|
3370
|
+
if (cur === null || cur === void 0) return void 0;
|
|
3371
|
+
cur = cur[key];
|
|
3372
|
+
}
|
|
3373
|
+
return cur;
|
|
3374
|
+
}
|
|
3375
|
+
function looksObservable(v) {
|
|
3376
|
+
return typeof v === "object" && v !== null && typeof v.subscribe === "function" && typeof v.getSnapshot === "function";
|
|
3377
|
+
}
|
|
3378
|
+
/**
|
|
3379
|
+
* Whether a stylesheet belongs to THIS plugin.
|
|
3380
|
+
*
|
|
3381
|
+
* `data-plugin` cannot answer this: the host's own CSS modules set it on every
|
|
3382
|
+
* `<style>` they inject (`@deepseek-ai/dsh-client-ui-theme` and its peers). Only
|
|
3383
|
+
* the marker written by this plugin's `markOwnSheet` can, and getting this wrong
|
|
3384
|
+
* in the "skip our own sheets" direction skips the host's whole theme.
|
|
3385
|
+
*
|
|
3386
|
+
* Read via `getAttribute` (the browser's own spelling) and fall back to the
|
|
3387
|
+
* camel-cased `dataset` key, which is what a plain-object test fixture exposes.
|
|
3388
|
+
*/
|
|
3389
|
+
function isOwnSheet(sheet) {
|
|
3390
|
+
const owner = sheet.ownerNode;
|
|
3391
|
+
if (owner === null || owner === void 0) return false;
|
|
3392
|
+
if (typeof owner.getAttribute === "function") return owner.getAttribute(OWN_SHEET_ATTR) !== null;
|
|
3393
|
+
const ds = owner.dataset;
|
|
3394
|
+
return ds !== void 0 && (ds["data-dab-side"] !== void 0 || ds["dabSide"] !== void 0);
|
|
3395
|
+
}
|
|
3396
|
+
/**
|
|
3397
|
+
* A stylesheet mention of a literal, on the requested side.
|
|
3398
|
+
*
|
|
3399
|
+
* The host's rules are the only place a renamed token still answers honestly:
|
|
3400
|
+
* a token can be *declared* by this plugin while the host's rule that gave it a
|
|
3401
|
+
* value is gone. Cross-origin sheets (none on a local dsh, but the probe must
|
|
3402
|
+
* not throw if one ever appears) are skipped.
|
|
3403
|
+
*/
|
|
3404
|
+
function ruleMentioned(env, literal, side) {
|
|
3405
|
+
for (const sheet of env.styleSheets()) {
|
|
3406
|
+
if (isOwnSheet(sheet) !== (side === "own")) continue;
|
|
3407
|
+
let rules = null;
|
|
3408
|
+
try {
|
|
3409
|
+
rules = sheet.cssRules;
|
|
3410
|
+
} catch {
|
|
3411
|
+
continue;
|
|
3412
|
+
}
|
|
3413
|
+
if (rules === null) continue;
|
|
3414
|
+
for (const rule of Array.from(rules)) {
|
|
3415
|
+
const text = rule.cssText;
|
|
3416
|
+
if (typeof text === "string" && text.includes(literal)) return true;
|
|
3417
|
+
const inner = rule.cssRules;
|
|
3418
|
+
if (inner === void 0 || inner === null) continue;
|
|
3419
|
+
for (const child of Array.from(inner)) {
|
|
3420
|
+
const childText = child.cssText;
|
|
3421
|
+
if (typeof childText === "string" && childText.includes(literal)) return true;
|
|
3422
|
+
}
|
|
3423
|
+
}
|
|
3424
|
+
}
|
|
3425
|
+
return false;
|
|
3426
|
+
}
|
|
3427
|
+
/** Which sides of stylesheet the probe can actually read (a blind check must not fail). */
|
|
3428
|
+
function readability(env) {
|
|
3429
|
+
const out = {
|
|
3430
|
+
host: false,
|
|
3431
|
+
own: false
|
|
3432
|
+
};
|
|
3433
|
+
for (const sheet of env.styleSheets()) try {
|
|
3434
|
+
if (sheet.cssRules !== null) {
|
|
3435
|
+
if (isOwnSheet(sheet)) out.own = true;
|
|
3436
|
+
else out.host = true;
|
|
3437
|
+
}
|
|
3438
|
+
} catch {}
|
|
3439
|
+
return out;
|
|
3440
|
+
}
|
|
3441
|
+
function evaluate(env, check) {
|
|
3442
|
+
switch (check.kind) {
|
|
3443
|
+
case "service": {
|
|
3444
|
+
let svc;
|
|
3445
|
+
try {
|
|
3446
|
+
svc = env.ctx.get(check.id);
|
|
3447
|
+
} catch {
|
|
3448
|
+
svc = void 0;
|
|
3449
|
+
}
|
|
3450
|
+
return svc === void 0 || svc === null ? {
|
|
3451
|
+
status: "fail",
|
|
3452
|
+
name: `ctx.get('${check.id}')`,
|
|
3453
|
+
detail: "service not published",
|
|
3454
|
+
reason: `the host does not publish the "${check.id}" service`
|
|
3455
|
+
} : {
|
|
3456
|
+
status: "pass",
|
|
3457
|
+
name: `ctx.get('${check.id}')`,
|
|
3458
|
+
detail: "service present"
|
|
3459
|
+
};
|
|
3460
|
+
}
|
|
3461
|
+
case "path": {
|
|
3462
|
+
let svc;
|
|
3463
|
+
try {
|
|
3464
|
+
svc = env.ctx.get(check.service);
|
|
3465
|
+
} catch {
|
|
3466
|
+
svc = void 0;
|
|
3467
|
+
}
|
|
3468
|
+
if (svc === void 0 || svc === null) return {
|
|
3469
|
+
status: "skip",
|
|
3470
|
+
name: `${check.service}.${check.path}`,
|
|
3471
|
+
detail: "service not published yet"
|
|
3472
|
+
};
|
|
3473
|
+
const value = walkPath(svc, check.path);
|
|
3474
|
+
const name = `${check.service}.${check.path}`;
|
|
3475
|
+
if (check.expect === "function") return typeof value === "function" ? {
|
|
3476
|
+
status: "pass",
|
|
3477
|
+
name,
|
|
3478
|
+
detail: "is a function"
|
|
3479
|
+
} : {
|
|
3480
|
+
status: "fail",
|
|
3481
|
+
name,
|
|
3482
|
+
detail: `expected a function, got ${typeof value}`,
|
|
3483
|
+
reason: `"${name}" is not a function on this host`
|
|
3484
|
+
};
|
|
3485
|
+
if (check.expect === "observable") return looksObservable(value) ? {
|
|
3486
|
+
status: "pass",
|
|
3487
|
+
name,
|
|
3488
|
+
detail: "is an observable face"
|
|
3489
|
+
} : {
|
|
3490
|
+
status: "fail",
|
|
3491
|
+
name,
|
|
3492
|
+
detail: "not an observable face",
|
|
3493
|
+
reason: `"${name}" no longer exposes subscribe()/getSnapshot()`
|
|
3494
|
+
};
|
|
3495
|
+
return value === void 0 ? {
|
|
3496
|
+
status: "fail",
|
|
3497
|
+
name,
|
|
3498
|
+
detail: "undefined",
|
|
3499
|
+
reason: `"${name}" is gone`
|
|
3500
|
+
} : {
|
|
3501
|
+
status: "pass",
|
|
3502
|
+
name,
|
|
3503
|
+
detail: typeof value
|
|
3504
|
+
};
|
|
3505
|
+
}
|
|
3506
|
+
case "selector": {
|
|
3507
|
+
const present = env.querySelector(check.selector) !== null;
|
|
3508
|
+
const name = `querySelector(${JSON.stringify(check.selector)})`;
|
|
3509
|
+
if (present) return {
|
|
3510
|
+
status: "pass",
|
|
3511
|
+
name,
|
|
3512
|
+
detail: "element found"
|
|
3513
|
+
};
|
|
3514
|
+
if (check.optionalWhen !== void 0 && !mounted(env, check.optionalWhen)) return {
|
|
3515
|
+
status: "skip",
|
|
3516
|
+
name,
|
|
3517
|
+
detail: "not on screen right now"
|
|
3518
|
+
};
|
|
3519
|
+
return {
|
|
3520
|
+
status: "fail",
|
|
3521
|
+
name,
|
|
3522
|
+
detail: "no element matches",
|
|
3523
|
+
reason: `no element matches ${check.selector} while its surface is on screen`
|
|
3524
|
+
};
|
|
3525
|
+
}
|
|
3526
|
+
case "attr": {
|
|
3527
|
+
const present = env.querySelector(`[${check.attr}]`) !== null;
|
|
3528
|
+
const name = `querySelector([${check.attr}])`;
|
|
3529
|
+
if (present) return {
|
|
3530
|
+
status: "pass",
|
|
3531
|
+
name,
|
|
3532
|
+
detail: "attribute found"
|
|
3533
|
+
};
|
|
3534
|
+
if (check.optionalWhen !== void 0 && !mounted(env, check.optionalWhen)) return {
|
|
3535
|
+
status: "skip",
|
|
3536
|
+
name,
|
|
3537
|
+
detail: "not on screen right now"
|
|
3538
|
+
};
|
|
3539
|
+
return {
|
|
3540
|
+
status: "fail",
|
|
3541
|
+
name,
|
|
3542
|
+
detail: "no element carries it",
|
|
3543
|
+
reason: `nothing in the DOM carries ${check.attr} while its surface is on screen`
|
|
3544
|
+
};
|
|
3545
|
+
}
|
|
3546
|
+
case "computed": {
|
|
3547
|
+
const host = check.on !== void 0 ? env.querySelector(check.on) : null;
|
|
3548
|
+
const name = `computed(${check.token}${check.on !== void 0 ? ` on ${check.on}` : ""})`;
|
|
3549
|
+
if (check.on !== void 0 && host === null) return {
|
|
3550
|
+
status: "skip",
|
|
3551
|
+
name,
|
|
3552
|
+
detail: `${check.on} not mounted`
|
|
3553
|
+
};
|
|
3554
|
+
const value = env.computedValue(check.token, host);
|
|
3555
|
+
return value !== "" ? {
|
|
3556
|
+
status: "pass",
|
|
3557
|
+
name,
|
|
3558
|
+
detail: value
|
|
3559
|
+
} : {
|
|
3560
|
+
status: "fail",
|
|
3561
|
+
name,
|
|
3562
|
+
detail: "resolves to nothing",
|
|
3563
|
+
reason: `${check.token} resolves to an empty value`
|
|
3564
|
+
};
|
|
3565
|
+
}
|
|
3566
|
+
case "rule": {
|
|
3567
|
+
const side = check.side ?? "host";
|
|
3568
|
+
const name = `${side === "host" ? "host" : "plugin"} stylesheet includes ${JSON.stringify(check.match)}`;
|
|
3569
|
+
if (!readability(env)[side]) return {
|
|
3570
|
+
status: "skip",
|
|
3571
|
+
name,
|
|
3572
|
+
detail: side === "host" ? "no host stylesheet is readable" : "this plugin's sheet is not mounted"
|
|
3573
|
+
};
|
|
3574
|
+
return ruleMentioned(env, check.match, side) ? {
|
|
3575
|
+
status: "pass",
|
|
3576
|
+
name,
|
|
3577
|
+
detail: side === "host" ? "declared by a host rule" : "declared by this plugin's own sheet"
|
|
3578
|
+
} : {
|
|
3579
|
+
status: "fail",
|
|
3580
|
+
name,
|
|
3581
|
+
detail: "no rule mentions it",
|
|
3582
|
+
reason: side === "host" ? `no host stylesheet declares ${check.match}` : `this plugin's own stylesheet does not declare ${check.match} — it was not injected`
|
|
3583
|
+
};
|
|
3584
|
+
}
|
|
3585
|
+
default: return {
|
|
3586
|
+
status: "fail",
|
|
3587
|
+
name: "unknown check",
|
|
3588
|
+
detail: String(check)
|
|
3589
|
+
};
|
|
3590
|
+
}
|
|
3591
|
+
}
|
|
3592
|
+
/** A contract with no checks is documentation only (a legacy alias we still accept). */
|
|
3593
|
+
function noChecks(c, lang) {
|
|
3594
|
+
return {
|
|
3595
|
+
id: c.id,
|
|
3596
|
+
label: labelOf(c, lang),
|
|
3597
|
+
symptom: symptomOf(c, lang),
|
|
3598
|
+
status: "info",
|
|
3599
|
+
detail: lang === "zh" ? "无需运行时检查 —— 仅记录兼容用法" : "no runtime check — accepted for compatibility",
|
|
3600
|
+
target: c.target,
|
|
3601
|
+
sources: c.sources,
|
|
3602
|
+
usedBy: c.usedBy,
|
|
3603
|
+
checks: []
|
|
3604
|
+
};
|
|
3605
|
+
}
|
|
3606
|
+
/**
|
|
3607
|
+
* Run every contract against a probe environment.
|
|
3608
|
+
*
|
|
3609
|
+
* Only the FIRST failing check of a contract is reported: the later ones usually
|
|
3610
|
+
* depend on it (no service ⇒ no methods on it), so listing them all would turn
|
|
3611
|
+
* one rename into five red lines.
|
|
3612
|
+
*/
|
|
3613
|
+
function probeContracts(env, lang) {
|
|
3614
|
+
return HOST_CONTRACTS.map((c) => {
|
|
3615
|
+
const result = {
|
|
3616
|
+
id: c.id,
|
|
3617
|
+
label: labelOf(c, lang),
|
|
3618
|
+
symptom: symptomOf(c, lang),
|
|
3619
|
+
status: "pass",
|
|
3620
|
+
detail: "",
|
|
3621
|
+
target: c.target,
|
|
3622
|
+
sources: c.sources,
|
|
3623
|
+
usedBy: c.usedBy,
|
|
3624
|
+
checks: []
|
|
3625
|
+
};
|
|
3626
|
+
if (c.checks.length === 0) return noChecks(c, lang);
|
|
3627
|
+
const outcomes = [];
|
|
3628
|
+
let firstFail;
|
|
3629
|
+
for (const check of c.checks) {
|
|
3630
|
+
let outcome;
|
|
3631
|
+
try {
|
|
3632
|
+
outcome = evaluate(env, check);
|
|
3633
|
+
} catch (e) {
|
|
3634
|
+
outcome = {
|
|
3635
|
+
status: "fail",
|
|
3636
|
+
name: check.kind,
|
|
3637
|
+
detail: String(e),
|
|
3638
|
+
reason: `${check.kind} check threw`
|
|
3639
|
+
};
|
|
3640
|
+
}
|
|
3641
|
+
outcomes.push(outcome);
|
|
3642
|
+
if (outcome.status === "fail" && firstFail === void 0) firstFail = outcome;
|
|
3643
|
+
}
|
|
3644
|
+
result.checks = outcomes.map((o) => `${o.status} · ${o.name} — ${o.detail}`);
|
|
3645
|
+
if (firstFail !== void 0) {
|
|
3646
|
+
result.status = "fail";
|
|
3647
|
+
result.detail = `${firstFail.name} — ${firstFail.detail}`;
|
|
3648
|
+
result.reason = firstFail.reason;
|
|
3649
|
+
return result;
|
|
3650
|
+
}
|
|
3651
|
+
const ran = outcomes.filter((o) => o.status !== "skip");
|
|
3652
|
+
if (ran.length === 0) {
|
|
3653
|
+
result.status = "skip";
|
|
3654
|
+
result.detail = outcomes.map((o) => `${o.name} — ${o.detail}`).join("; ");
|
|
3655
|
+
return result;
|
|
3656
|
+
}
|
|
3657
|
+
result.status = ran.some((o) => o.status === "info") && ran.every((o) => o.status === "info") ? "info" : "pass";
|
|
3658
|
+
const last = ran[ran.length - 1];
|
|
3659
|
+
result.detail = `${last.name} — ${last.detail}`;
|
|
3660
|
+
return result;
|
|
3661
|
+
});
|
|
3662
|
+
}
|
|
3663
|
+
/** Assemble the panel's report around a set of probe results. */
|
|
3664
|
+
function buildClientReport(input, host) {
|
|
3665
|
+
const results = probeContracts(browserEnv(input.ctx), input.lang);
|
|
3666
|
+
const pass = results.filter((r) => r.status === "pass").length;
|
|
3667
|
+
const fail = results.filter((r) => r.status === "fail").length;
|
|
3668
|
+
const skip = results.filter((r) => r.status === "skip" || r.status === "info").length;
|
|
3669
|
+
return {
|
|
3670
|
+
phase: "client",
|
|
3671
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3672
|
+
plugin: {
|
|
3673
|
+
version: input.pluginVersion,
|
|
3674
|
+
floor: input.floor
|
|
3675
|
+
},
|
|
3676
|
+
host: {
|
|
3677
|
+
version: host.version,
|
|
3678
|
+
compatible: host.compatible,
|
|
3679
|
+
node: "",
|
|
3680
|
+
root: "",
|
|
3681
|
+
note: host.note,
|
|
3682
|
+
model: input.model
|
|
3683
|
+
},
|
|
3684
|
+
results,
|
|
3685
|
+
summary: {
|
|
3686
|
+
pass,
|
|
3687
|
+
fail,
|
|
3688
|
+
skip,
|
|
3689
|
+
total: results.length
|
|
3690
|
+
}
|
|
3691
|
+
};
|
|
2324
3692
|
}
|
|
2325
3693
|
//#endregion
|
|
3694
|
+
//#region src/client/build-info.ts
|
|
3695
|
+
/** This plugin's version at build time ('0.0.0' when built unbundled). */
|
|
3696
|
+
const PLUGIN_VERSION = "0.4.3";
|
|
3697
|
+
/** The dsh floor this build declares, straight from `engines.dsh`. */
|
|
3698
|
+
const DSH_FLOOR = "0.1.7-rc.2";
|
|
3699
|
+
//#endregion
|
|
2326
3700
|
//#region src/client/components/icons.tsx
|
|
2327
3701
|
/** Sun glyph, migrated from @deepseek-ai/dsh-client-ui-primitives IconLightOutline16. */
|
|
2328
3702
|
const SUN_PATHS = "<path d=\"M11.3496 8C11.3496 6.14985 9.85015 4.65039 8 4.65039C6.14985 4.65039 4.65039 6.14985 4.65039 8C4.65039 9.85015 6.14985 11.3496 8 11.3496C9.85015 11.3496 11.3496 9.85015 11.3496 8ZM12.6504 8C12.6504 10.5681 10.5681 12.6504 8 12.6504C5.43188 12.6504 3.34961 10.5681 3.34961 8C3.34961 5.43188 5.43188 3.34961 8 3.34961C10.5681 3.34961 12.6504 5.43188 12.6504 8Z\" fill=\"currentColor\"/><path d=\"M8.65039 0.5V2.5H7.34961V0.5H8.65039Z\" fill=\"currentColor\"/><path d=\"M8.65039 13.5V15.5H7.34961V13.5H8.65039Z\" fill=\"currentColor\"/><path d=\"M3.15808 2.24035L4.57229 3.65456L3.6525 4.57435L2.23829 3.16014L3.15808 2.24035Z\" fill=\"currentColor\"/><path d=\"M12.3505 11.4327L13.7647 12.8469L12.8449 13.7667L11.4307 12.3525L12.3505 11.4327Z\" fill=\"currentColor\"/><path d=\"M2.24537 12.8469L3.65958 11.4327L4.57937 12.3525L3.16516 13.7667L2.24537 12.8469Z\" fill=\"currentColor\"/><path d=\"M11.4377 3.65455L12.852 2.24033L13.7718 3.16012L12.3575 4.57434L11.4377 3.65455Z\" fill=\"currentColor\"/><path d=\"M0.5 7.35461H2.5V8.6554H0.5L0.5 7.35461Z\" fill=\"currentColor\"/><path d=\"M13.5 7.35461H15.5V8.6554H13.5V8.6554Z\" fill=\"currentColor\"/>";
|
|
@@ -2434,6 +3808,11 @@ window.__ModuleLoader__.load({
|
|
|
2434
3808
|
className,
|
|
2435
3809
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M7.2 1.8l1.2 3.1 3.1 1.2-3.1 1.2-1.2 3.1-1.2-3.1L2.9 6.1 6 4.9l1.2-3.1z" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M12 10.5l.5 1.2 1.2.5-1.2.5-.5 1.2-.5-1.2-1.2-.5 1.2-.5.5-1.2z" })]
|
|
2436
3810
|
});
|
|
3811
|
+
const RefreshIcon = ({ size, className }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Glyph, {
|
|
3812
|
+
size,
|
|
3813
|
+
className,
|
|
3814
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M13.2 8A5.2 5.2 0 1 1 11.5 4.2" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M13.4 1.8v2.9h-2.9" })]
|
|
3815
|
+
});
|
|
2437
3816
|
const DownloadIcon = ({ size, className }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Glyph, {
|
|
2438
3817
|
size,
|
|
2439
3818
|
className,
|
|
@@ -2494,6 +3873,17 @@ window.__ModuleLoader__.load({
|
|
|
2494
3873
|
className,
|
|
2495
3874
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M8 2.6l5.4 9.8H2.6L8 2.6z" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M8 6.8v2.4M8 11.4v.01" })]
|
|
2496
3875
|
});
|
|
3876
|
+
const CopyIcon = ({ size, className }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Glyph, {
|
|
3877
|
+
size,
|
|
3878
|
+
className,
|
|
3879
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
3880
|
+
x: "5.4",
|
|
3881
|
+
y: "5.4",
|
|
3882
|
+
width: "7.6",
|
|
3883
|
+
height: "7.6",
|
|
3884
|
+
rx: "1.6"
|
|
3885
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M10.6 5.4V4.2a1.6 1.6 0 0 0-1.6-1.6H4.2a1.6 1.6 0 0 0-1.6 1.6v4.8a1.6 1.6 0 0 0 1.6 1.6h1.2" })]
|
|
3886
|
+
});
|
|
2497
3887
|
const CanvasIcon = ({ size, className }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Glyph, {
|
|
2498
3888
|
size,
|
|
2499
3889
|
className,
|
|
@@ -4311,6 +5701,290 @@ window.__ModuleLoader__.load({
|
|
|
4311
5701
|
] });
|
|
4312
5702
|
}
|
|
4313
5703
|
//#endregion
|
|
5704
|
+
//#region src/client/components/pages/HostPage.tsx
|
|
5705
|
+
/**
|
|
5706
|
+
* The "Host check" page.
|
|
5707
|
+
*
|
|
5708
|
+
* Two answers side by side, because a plugin that styles someone else's interface
|
|
5709
|
+
* fails in two different ways:
|
|
5710
|
+
*
|
|
5711
|
+
* this build, live the probe in ../../judge.ts runs against the DOM, the
|
|
5712
|
+
* host stylesheets and the Cordis services — what the
|
|
5713
|
+
* plugin can actually see right now.
|
|
5714
|
+
* the installed files the node half's scan (src/host-scan.ts) reports whether
|
|
5715
|
+
* the packages on disk still contain every literal the
|
|
5716
|
+
* plugin depends on, and names the host's own version.
|
|
5717
|
+
*
|
|
5718
|
+
* The page exists so a failure stops being "背景不动了": it names the hop, the
|
|
5719
|
+
* literal, the file that should define it and the file of this plugin that reads
|
|
5720
|
+
* it — and copies all of that as one markdown block for an issue.
|
|
5721
|
+
*/
|
|
5722
|
+
/** Contract id → its group, so the panel's order is the table's order. */
|
|
5723
|
+
const GROUP_OF = new Map(HOST_CONTRACTS.map((c) => [c.id, c.group]));
|
|
5724
|
+
function HostPage({ p, notify }) {
|
|
5725
|
+
const { t, lang, readModelFacts } = p;
|
|
5726
|
+
const [client, setClient] = (0, react.useState)(null);
|
|
5727
|
+
const [scanned, setScanned] = (0, react.useState)(null);
|
|
5728
|
+
const [busy, setBusy] = (0, react.useState)(false);
|
|
5729
|
+
const run = (0, react.useCallback)(async () => {
|
|
5730
|
+
setBusy(true);
|
|
5731
|
+
try {
|
|
5732
|
+
setClient(buildClientReport({
|
|
5733
|
+
ctx: p.ctx,
|
|
5734
|
+
lang,
|
|
5735
|
+
pluginVersion: PLUGIN_VERSION,
|
|
5736
|
+
floor: DSH_FLOOR,
|
|
5737
|
+
model: readModelFacts?.()
|
|
5738
|
+
}, {
|
|
5739
|
+
version: "",
|
|
5740
|
+
compatible: null,
|
|
5741
|
+
note: ""
|
|
5742
|
+
}));
|
|
5743
|
+
setScanned(await readHostScan(lang));
|
|
5744
|
+
} finally {
|
|
5745
|
+
setBusy(false);
|
|
5746
|
+
}
|
|
5747
|
+
}, [
|
|
5748
|
+
p,
|
|
5749
|
+
lang,
|
|
5750
|
+
readModelFacts
|
|
5751
|
+
]);
|
|
5752
|
+
(0, react.useEffect)(() => {
|
|
5753
|
+
run();
|
|
5754
|
+
}, [run]);
|
|
5755
|
+
const hostVersion = scanned?.host.version ?? "";
|
|
5756
|
+
const compatible = scanned?.host.compatible ?? null;
|
|
5757
|
+
const verdict = client === null ? "unknown" : verdictOf(client);
|
|
5758
|
+
const diskVerdict = scanned === null ? "unknown" : verdictOf(scanned);
|
|
5759
|
+
const rows = client?.results ?? [];
|
|
5760
|
+
const copyReport = async () => {
|
|
5761
|
+
const parts = [];
|
|
5762
|
+
if (client !== null) parts.push(reportToMarkdown(client, lang));
|
|
5763
|
+
if (scanned !== null) parts.push(reportToMarkdown(scanned, lang));
|
|
5764
|
+
try {
|
|
5765
|
+
await navigator.clipboard.writeText(parts.join("\n\n"));
|
|
5766
|
+
notify(t("hostCopied"));
|
|
5767
|
+
} catch {
|
|
5768
|
+
notify(t("hostCopyFail"), false);
|
|
5769
|
+
}
|
|
5770
|
+
};
|
|
5771
|
+
const statusWord = (s) => s === "pass" ? t("hostPass") : s === "fail" ? t("hostFail") : s === "skip" ? t("hostNa") : t("hostInfo");
|
|
5772
|
+
const row = (r) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
5773
|
+
className: `dab-check dab-check-${r.status}`,
|
|
5774
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5775
|
+
className: `dab-check-ico dab-check-ico-${r.status}`,
|
|
5776
|
+
"aria-hidden": "true",
|
|
5777
|
+
children: r.status === "pass" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CheckIcon, { size: 12 }) : r.status === "fail" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AlertIcon, { size: 12 }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "dab-check-dot" })
|
|
5778
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5779
|
+
className: "dab-check-body",
|
|
5780
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5781
|
+
className: "dab-check-head",
|
|
5782
|
+
children: [
|
|
5783
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5784
|
+
className: "dab-check-label",
|
|
5785
|
+
children: r.label
|
|
5786
|
+
}),
|
|
5787
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5788
|
+
className: `dab-check-status dab-check-status-${r.status}`,
|
|
5789
|
+
children: statusWord(r.status)
|
|
5790
|
+
}),
|
|
5791
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
|
|
5792
|
+
className: "dab-check-target",
|
|
5793
|
+
children: r.target
|
|
5794
|
+
})
|
|
5795
|
+
]
|
|
5796
|
+
}), r.status === "fail" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5797
|
+
className: "dab-check-reason",
|
|
5798
|
+
children: [
|
|
5799
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: r.reason ?? r.detail }),
|
|
5800
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5801
|
+
className: "dab-check-symptom",
|
|
5802
|
+
children: r.symptom
|
|
5803
|
+
}),
|
|
5804
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5805
|
+
className: "dab-check-where",
|
|
5806
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("hostExpectedIn") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: r.sources.map((s) => s.path).join(" · ") })]
|
|
5807
|
+
}),
|
|
5808
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5809
|
+
className: "dab-check-where",
|
|
5810
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("hostUsedBy") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: r.usedBy })]
|
|
5811
|
+
})
|
|
5812
|
+
]
|
|
5813
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5814
|
+
className: "dab-check-detail",
|
|
5815
|
+
title: r.checks.join("\n"),
|
|
5816
|
+
children: r.detail
|
|
5817
|
+
})]
|
|
5818
|
+
})]
|
|
5819
|
+
}, r.id);
|
|
5820
|
+
let rise = 1;
|
|
5821
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
5822
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
|
|
5823
|
+
className: "dab-head dab-rise",
|
|
5824
|
+
style: { "--d": 0 },
|
|
5825
|
+
children: [
|
|
5826
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5827
|
+
className: "dab-overline",
|
|
5828
|
+
children: "Host check"
|
|
5829
|
+
}),
|
|
5830
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
5831
|
+
className: "dab-h1",
|
|
5832
|
+
children: t("pageHostCheck")
|
|
5833
|
+
}),
|
|
5834
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5835
|
+
className: "dab-desc",
|
|
5836
|
+
children: t("descHostCheck")
|
|
5837
|
+
})
|
|
5838
|
+
]
|
|
5839
|
+
}),
|
|
5840
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
5841
|
+
className: `dab-card dab-verdict dab-verdict-${verdict} dab-rise`,
|
|
5842
|
+
style: { "--d": rise++ },
|
|
5843
|
+
children: [
|
|
5844
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5845
|
+
className: "dab-verdict-head",
|
|
5846
|
+
children: [
|
|
5847
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5848
|
+
className: `dab-verdict-ico dab-verdict-ico-${verdict}`,
|
|
5849
|
+
"aria-hidden": "true",
|
|
5850
|
+
children: verdict === "ok" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CheckIcon, { size: 16 }) : verdict === "unknown" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LayersIcon, { size: 16 }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AlertIcon, { size: 16 })
|
|
5851
|
+
}),
|
|
5852
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5853
|
+
className: "dab-verdict-text",
|
|
5854
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5855
|
+
className: "dab-verdict-title",
|
|
5856
|
+
children: verdict === "ok" ? t("hostAllOk") : verdict === "unknown" ? t("hostUnknown") : verdict === "broken" ? t("hostBroken") : t("hostPartial")
|
|
5857
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5858
|
+
className: "dab-verdict-sub",
|
|
5859
|
+
children: t("hostSummary").replace("{pass}", String(client?.summary.pass ?? 0)).replace("{fail}", String(client?.summary.fail ?? 0)).replace("{skip}", String(client?.summary.skip ?? 0))
|
|
5860
|
+
})]
|
|
5861
|
+
}),
|
|
5862
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5863
|
+
className: "dab-verdict-actions",
|
|
5864
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
5865
|
+
type: "button",
|
|
5866
|
+
className: "dab-btn",
|
|
5867
|
+
disabled: busy,
|
|
5868
|
+
onClick: () => void run(),
|
|
5869
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(RefreshIcon, { size: 13 }), busy ? t("hostRunning") : t("hostRerun")]
|
|
5870
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
5871
|
+
type: "button",
|
|
5872
|
+
className: "dab-btn dab-btn-primary",
|
|
5873
|
+
onClick: () => void copyReport(),
|
|
5874
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopyIcon, { size: 13 }), t("hostCopy")]
|
|
5875
|
+
})]
|
|
5876
|
+
})
|
|
5877
|
+
]
|
|
5878
|
+
}),
|
|
5879
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5880
|
+
className: "dab-facts",
|
|
5881
|
+
children: [
|
|
5882
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Fact, {
|
|
5883
|
+
label: "dsh",
|
|
5884
|
+
value: hostVersion !== "" ? hostVersion : t("hostUnreadable"),
|
|
5885
|
+
bad: compatible === false
|
|
5886
|
+
}),
|
|
5887
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Fact, {
|
|
5888
|
+
label: t("hostPluginFloor"),
|
|
5889
|
+
value: `>=${DSH_FLOOR}`
|
|
5890
|
+
}),
|
|
5891
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Fact, {
|
|
5892
|
+
label: t("hostPlugin"),
|
|
5893
|
+
value: PLUGIN_VERSION
|
|
5894
|
+
}),
|
|
5895
|
+
scanned !== null && scanned.host.node !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Fact, {
|
|
5896
|
+
label: "node",
|
|
5897
|
+
value: scanned.host.node
|
|
5898
|
+
}) : null,
|
|
5899
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Fact, {
|
|
5900
|
+
label: t("hostDiskScan"),
|
|
5901
|
+
value: diskVerdict === "ok" ? `${scanned?.summary.pass ?? 0}/${scanned?.summary.total ?? 0}` : diskVerdict === "unknown" ? t("hostNa") : `${scanned?.summary.fail ?? 0} ${t("hostFail")}`,
|
|
5902
|
+
bad: diskVerdict === "broken" || diskVerdict === "partial"
|
|
5903
|
+
}),
|
|
5904
|
+
client?.host.model !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Fact, {
|
|
5905
|
+
label: t("hostModel"),
|
|
5906
|
+
value: client.host.model.text !== "" ? client.host.model.text : t("hostModelNone")
|
|
5907
|
+
}) : null
|
|
5908
|
+
]
|
|
5909
|
+
}),
|
|
5910
|
+
compatible === false ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5911
|
+
className: "dab-check-note",
|
|
5912
|
+
children: t("hostBelowFloor")
|
|
5913
|
+
}) : null,
|
|
5914
|
+
scanned !== null && scanned.host.note !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5915
|
+
className: "dab-check-note",
|
|
5916
|
+
children: scanned.host.note
|
|
5917
|
+
}) : null,
|
|
5918
|
+
diskFailures(scanned) > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5919
|
+
className: "dab-check-note",
|
|
5920
|
+
children: t("hostDiskFailures").replace("{n}", String(diskFailures(scanned)))
|
|
5921
|
+
}) : null
|
|
5922
|
+
]
|
|
5923
|
+
}),
|
|
5924
|
+
client === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
|
|
5925
|
+
className: "dab-card dab-rise",
|
|
5926
|
+
style: { "--d": rise },
|
|
5927
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5928
|
+
className: "dab-check-detail",
|
|
5929
|
+
children: t("hostRunning")
|
|
5930
|
+
})
|
|
5931
|
+
}) : null,
|
|
5932
|
+
CONTRACT_GROUPS.map((group) => {
|
|
5933
|
+
const groupRows = rows.filter((r) => GROUP_OF.get(r.id) === group.id);
|
|
5934
|
+
if (groupRows.length === 0) return null;
|
|
5935
|
+
const ok = groupRows.filter((r) => r.status === "pass").length;
|
|
5936
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
5937
|
+
className: "dab-card dab-rise",
|
|
5938
|
+
style: { "--d": rise++ },
|
|
5939
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5940
|
+
className: "dab-check-group-title",
|
|
5941
|
+
children: [lang === "zh" ? group.label.zh : group.label.en, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
5942
|
+
className: "dab-check-group-count",
|
|
5943
|
+
children: [
|
|
5944
|
+
ok,
|
|
5945
|
+
"/",
|
|
5946
|
+
groupRows.length
|
|
5947
|
+
]
|
|
5948
|
+
})]
|
|
5949
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
5950
|
+
className: "dab-checks",
|
|
5951
|
+
children: groupRows.map(row)
|
|
5952
|
+
})]
|
|
5953
|
+
}, group.id);
|
|
5954
|
+
}),
|
|
5955
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5956
|
+
className: "dab-hint dab-rise",
|
|
5957
|
+
style: { "--d": rise++ },
|
|
5958
|
+
children: t("hostHint")
|
|
5959
|
+
}),
|
|
5960
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("footer", {
|
|
5961
|
+
className: "dab-footer dab-rise",
|
|
5962
|
+
style: { "--d": rise },
|
|
5963
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5964
|
+
className: "dab-footer-mono",
|
|
5965
|
+
children: "dsh-background-by-model"
|
|
5966
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("footerTag") })]
|
|
5967
|
+
})
|
|
5968
|
+
] });
|
|
5969
|
+
}
|
|
5970
|
+
/** How many contracts the on-disk scan could not find (0 when it did not answer). */
|
|
5971
|
+
function diskFailures(report) {
|
|
5972
|
+
return report === null ? 0 : report.summary.fail;
|
|
5973
|
+
}
|
|
5974
|
+
function Fact({ label, value, bad = false }) {
|
|
5975
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5976
|
+
className: `dab-fact${bad ? " is-bad" : ""}`,
|
|
5977
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5978
|
+
className: "dab-fact-k",
|
|
5979
|
+
children: label
|
|
5980
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
|
|
5981
|
+
className: "dab-fact-v",
|
|
5982
|
+
title: value,
|
|
5983
|
+
children: value
|
|
5984
|
+
})]
|
|
5985
|
+
});
|
|
5986
|
+
}
|
|
5987
|
+
//#endregion
|
|
4314
5988
|
//#region src/client/components/ThemeSection.tsx
|
|
4315
5989
|
/**
|
|
4316
5990
|
* dsh-background-by-model — settings section shell.
|
|
@@ -4358,6 +6032,14 @@ window.__ModuleLoader__.load({
|
|
|
4358
6032
|
p: props,
|
|
4359
6033
|
notify
|
|
4360
6034
|
})
|
|
6035
|
+
},
|
|
6036
|
+
{
|
|
6037
|
+
label: t("pageHostCheck"),
|
|
6038
|
+
Icon: GearIcon,
|
|
6039
|
+
node: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HostPage, {
|
|
6040
|
+
p: props,
|
|
6041
|
+
notify
|
|
6042
|
+
})
|
|
4361
6043
|
}
|
|
4362
6044
|
];
|
|
4363
6045
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ErrorBoundary, {
|
|
@@ -4457,6 +6139,7 @@ window.__ModuleLoader__.load({
|
|
|
4457
6139
|
}, "dsh-background-by-model: skin dispose");
|
|
4458
6140
|
const styleEl = document.createElement("style");
|
|
4459
6141
|
styleEl.dataset.plugin = "dsh-background-by-model";
|
|
6142
|
+
markOwnSheet(styleEl);
|
|
4460
6143
|
styleEl.textContent = `body[data-ds-dark-theme="dsh-background-by-model"]::before{content:'';position:fixed;inset:0;z-index:-1;pointer-events:none;background:radial-gradient(ellipse 80% 60% at 50% 0%,rgba(255,255,255,0.03) 0%,transparent 60%)}${SETTINGS_STYLE_RULE}${TRAJECTORY_STYLE_RULE}${RIGHTBAR_STYLE_RULE}${INPUT_BLUR_RULE}[data-composer-card] textarea::placeholder,[data-composer-card] input::placeholder,[data-composer-card] [contenteditable]::placeholder,[data-cordis-panel] input::placeholder,[data-cordis-panel] textarea::placeholder,.dab-input::placeholder,.dab-input textarea::placeholder,.dab-input input::placeholder{color:var(--dsh-any-placeholder,var(--dsw-alias-label-caption,#8a8f98))!important;font-style:italic;opacity:.85}`;
|
|
4461
6144
|
document.head.appendChild(styleEl);
|
|
4462
6145
|
ctx.effect(() => () => {
|
|
@@ -4620,11 +6303,33 @@ window.__ModuleLoader__.load({
|
|
|
4620
6303
|
if (id === activeRuleId) applyActive();
|
|
4621
6304
|
else sync();
|
|
4622
6305
|
};
|
|
6306
|
+
const activeLang = () => {
|
|
6307
|
+
try {
|
|
6308
|
+
const snapshot = ctx.locale.getSnapshot();
|
|
6309
|
+
return typeof snapshot?.active === "string" && snapshot.active.toLowerCase().startsWith("zh") ? "zh" : "en";
|
|
6310
|
+
} catch {
|
|
6311
|
+
return "en";
|
|
6312
|
+
}
|
|
6313
|
+
};
|
|
6314
|
+
/** The model-resolution facts the host self-check reports, read on demand. */
|
|
6315
|
+
const readModelFacts = () => {
|
|
6316
|
+
const rule = activeRuleId === null ? null : ruleById(activeRuleId);
|
|
6317
|
+
return {
|
|
6318
|
+
text: modelText,
|
|
6319
|
+
source: modelSource,
|
|
6320
|
+
note: modelNote,
|
|
6321
|
+
rule: rule === null ? "" : rule.match !== "" ? rule.match : rule.id,
|
|
6322
|
+
matched: activeMatched
|
|
6323
|
+
};
|
|
6324
|
+
};
|
|
4623
6325
|
const sectionInject = (actions) => {
|
|
4624
6326
|
bound = actions;
|
|
4625
6327
|
sync();
|
|
4626
6328
|
return {
|
|
4627
6329
|
t: ctx.locale.bind(NS),
|
|
6330
|
+
ctx,
|
|
6331
|
+
lang: activeLang(),
|
|
6332
|
+
readModelFacts,
|
|
4628
6333
|
imageOf: (slot) => displayImageOf(slot),
|
|
4629
6334
|
addRule: () => {
|
|
4630
6335
|
const rule = newRule(nextRuleId(), nextSlot());
|
|
@@ -4876,6 +6581,26 @@ window.__ModuleLoader__.load({
|
|
|
4876
6581
|
const onPageHide = () => flushSave();
|
|
4877
6582
|
window.addEventListener("pagehide", onPageHide);
|
|
4878
6583
|
ctx.effect(() => () => window.removeEventListener("pagehide", onPageHide), "dsh-background-by-model: pagehide flush");
|
|
6584
|
+
const hostCheckTimer = window.setTimeout(() => {
|
|
6585
|
+
try {
|
|
6586
|
+
const report = buildClientReport({
|
|
6587
|
+
ctx,
|
|
6588
|
+
lang: activeLang(),
|
|
6589
|
+
pluginVersion: PLUGIN_VERSION,
|
|
6590
|
+
floor: DSH_FLOOR,
|
|
6591
|
+
model: readModelFacts()
|
|
6592
|
+
}, {
|
|
6593
|
+
version: "",
|
|
6594
|
+
compatible: null,
|
|
6595
|
+
note: ""
|
|
6596
|
+
});
|
|
6597
|
+
const failed = report.results.filter((r) => r.status === "fail");
|
|
6598
|
+
if (failed.length === 0) return;
|
|
6599
|
+
console.warn(`dsh-background-by-model: ${failed.length} of ${report.results.length} host contract(s) are broken on this dsh build. Open Settings → ${ctx.locale.bind(NS)("pageHostCheck")} for the full report.`);
|
|
6600
|
+
for (const r of failed) console.warn(`dsh-background-by-model: [${r.id}] ${r.reason ?? r.detail} — read by ${r.usedBy}; expected in ${r.sources.map((s) => s.path).join(", ")}`);
|
|
6601
|
+
} catch {}
|
|
6602
|
+
}, 2500);
|
|
6603
|
+
ctx.effect(() => () => window.clearTimeout(hostCheckTimer), "dsh-background-by-model: host check");
|
|
4879
6604
|
}
|
|
4880
6605
|
/** Color of the currently active rule, or null when it uses the system theme. */
|
|
4881
6606
|
function activeRuleColor() {
|