@y9931666/dsh-plugin-crossfire 0.1.1 → 0.1.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 +2 -0
- package/lib/client.js +160 -19
- package/lib/index.js +1 -1
- package/package.json +1 -1
- package/release-notes/v0.1.2.md +44 -0
- package/release-notes/v0.1.3.md +21 -0
- package/lib/types/budget.d.ts +0 -86
- package/lib/types/client/ReviewCard.d.ts +0 -41
- package/lib/types/client/SettingsView.d.ts +0 -34
- package/lib/types/client/brand.d.ts +0 -55
- package/lib/types/client/defectTitle.d.ts +0 -23
- package/lib/types/client/index.d.ts +0 -21
- package/lib/types/client/locales.d.ts +0 -19
- package/lib/types/client/merge.d.ts +0 -27
- package/lib/types/client/promptView.d.ts +0 -63
- package/lib/types/client/session-cache.d.ts +0 -49
- package/lib/types/client/slot-boundary.d.ts +0 -46
- package/lib/types/client/wire.d.ts +0 -192
- package/lib/types/dimensions.d.ts +0 -47
- package/lib/types/engine.d.ts +0 -172
- package/lib/types/etag.d.ts +0 -40
- package/lib/types/expert-events.d.ts +0 -110
- package/lib/types/export-md.d.ts +0 -21
- package/lib/types/harness-compat.d.ts +0 -104
- package/lib/types/index.d.ts +0 -89
- package/lib/types/members.d.ts +0 -143
- package/lib/types/noise.d.ts +0 -29
- package/lib/types/preferences.d.ts +0 -134
- package/lib/types/prompt.d.ts +0 -16
- package/lib/types/redteam.d.ts +0 -115
- package/lib/types/review.d.ts +0 -152
- package/lib/types/rpc.d.ts +0 -176
- package/lib/types/split.d.ts +0 -156
- package/lib/types/state.d.ts +0 -84
- package/lib/types/tools.d.ts +0 -59
- package/lib/types/types.d.ts +0 -308
- package/lib/types/version.d.ts +0 -18
- package/lib/types/web-guard.d.ts +0 -55
package/README.md
CHANGED
|
@@ -63,6 +63,8 @@ draft ──送红队上场──▶ attacking ──收集观点──▶ judgi
|
|
|
63
63
|
|
|
64
64
|
顶部影响条显示 `认定 / 驳回 / 待定` 与"打穿立场"的条数。
|
|
65
65
|
|
|
66
|
+
**视觉(v0.1.3)**:每条缺陷左侧有一条 3px 状态色条(认定=绿 / 驳回=红 / 待定=灰),十几条缺陷的分布一眼可见;折叠标题不再把长定位串塞进标题,改为「观点 N · 核心句」(完整路径留在展开后的正文);状态区文案从「待定 9 / 表态 9」改为「待定 9 / 共 9 条」。设置页改为卡片分组、控件统一 32px 高,并用内联 SVG 图标做区块目录。
|
|
67
|
+
|
|
66
68
|
## 数据落在哪
|
|
67
69
|
|
|
68
70
|
```
|
package/lib/client.js
CHANGED
|
@@ -237,13 +237,24 @@ function routeText(provider, model) {
|
|
|
237
237
|
* 一旦退化,肉眼看到的是"整张卡片塌成一团",而不是一条错误文案。它是纯字符串函数,
|
|
238
238
|
* 因此抽出来直接单测 —— 不需要 jsdom / react 测试环境(本仓库只有 `node:test`)。
|
|
239
239
|
*
|
|
240
|
-
*
|
|
241
|
-
*
|
|
240
|
+
* ## 标题从哪里来(三层,逐层兜底)
|
|
241
|
+
*
|
|
242
|
+
* 1. **压缩信封**:`观点 N(指向 dsh-plugin-crossfire/src/client/ReviewCard.module.css:106-176,维度 交互)`
|
|
243
|
+
* → `观点 N · ReviewCard.module.css:106-176 · 交互`
|
|
244
|
+
* 红队写**完整路径**是对的(审计要能定位到文件),但它不该占满标题 —— 用户实测反馈:
|
|
245
|
+
* "标题过长,导致后面无法显示;且有很多无用信息,比如路径的位置,这个可以放在内容里"。
|
|
246
|
+
* 所以完整路径仍留在缺陷正文(可追溯),标题只留**文件名 + 行号 + 维度**。
|
|
247
|
+
* 2. **原始信封**(压缩不出来时,例如括号里没有 `指向`/`维度` 的旧格式)→ 原样返回前缀。
|
|
248
|
+
* 3. **首句兜底**:未按格式提交时取首句(按换行、再按中英文句末标点切,限长)。
|
|
249
|
+
*
|
|
250
|
+
* **绝不**返回空串 —— 标题位空了比不显示更糟。
|
|
242
251
|
*/
|
|
243
252
|
/** 标题硬上限(字符)。超过即截断并加省略号。 */
|
|
244
253
|
const TITLE_MAX = 80;
|
|
254
|
+
/** 压缩后的单个片段上限(文件名或维度名过长时截断,避免任一片段独吞整行)。 */
|
|
255
|
+
const PART_MAX = 40;
|
|
245
256
|
/**
|
|
246
|
-
*
|
|
257
|
+
* 信封:`观点 N(…)` / `观点N(…)`。
|
|
247
258
|
*
|
|
248
259
|
* 括号内必须**至少有一个既非空白、也非右括号的字符**:`观点 1()`、`观点 1( )`、
|
|
249
260
|
* `观点 1(\u3000)` 是"格式没填",不是"合法信封"。旧写法 `[^))]*` 会把它们当成命中
|
|
@@ -256,24 +267,76 @@ const TITLE_MAX = 80;
|
|
|
256
267
|
*/
|
|
257
268
|
const ENVELOPE = /^(观点\s*\d+\s*[((]\s*[^))\s]\s*[^))]*[))])\s*[::]?/;
|
|
258
269
|
/**
|
|
259
|
-
*
|
|
270
|
+
* 信封内部:`观点 N(指向 X,维度 Y)` 的三段。
|
|
271
|
+
*
|
|
272
|
+
* `指向` 后取到**逗号或右括号**为止(中英文逗号、顿号都认);`维度` 后取到右括号为止。
|
|
273
|
+
* 用 `[\s\S]` 而不是 `.`:定位串里可能出现换行(红队偶尔换行排版)。
|
|
274
|
+
*/
|
|
275
|
+
const ENVELOPE_PARTS = /^观点\s*(\d+)\s*[((]\s*指向\s*([\s\S]*?)\s*[,,、]\s*维度\s*([\s\S]*?)\s*[))]/;
|
|
276
|
+
/**
|
|
277
|
+
* 从一条缺陷正文里导出标题。见模块注释的三层兜底。
|
|
260
278
|
*
|
|
261
279
|
* @param content 缺陷原文(可以是任意多行文本)。
|
|
262
|
-
* @returns
|
|
280
|
+
* @returns 非空标题,最长 {@link TITLE_MAX} 字符。
|
|
263
281
|
*/
|
|
264
282
|
function defectTitle(content) {
|
|
265
283
|
const text = content.trim();
|
|
266
284
|
const envelope = ENVELOPE.exec(text);
|
|
267
|
-
if (envelope !== null && envelope[1] !== void 0)
|
|
285
|
+
if (envelope !== null && envelope[1] !== void 0) {
|
|
286
|
+
const raw = envelope[1].trim();
|
|
287
|
+
const compact = compactEnvelope(raw);
|
|
288
|
+
if (compact !== void 0) return take(compact, TITLE_MAX);
|
|
289
|
+
const index = /观点\s*(\d+)/.exec(raw)?.[1];
|
|
290
|
+
const tail = text.slice(envelope[0].length).replace(/^[\s::]+/, "");
|
|
291
|
+
const firstLine$1 = (tail.split(/\r?\n/)[0] ?? tail).trim();
|
|
292
|
+
const firstSentence = (firstLine$1.split(/(?<=[。..;;!!??])/)[0] ?? firstLine$1).trim();
|
|
293
|
+
if (index !== void 0 && firstSentence !== "") return take(`观点 ${index} · ${firstSentence}`, TITLE_MAX);
|
|
294
|
+
if (index !== void 0) {
|
|
295
|
+
const dimension = /维度\s*([^))]*)/.exec(raw)?.[1]?.trim();
|
|
296
|
+
if (dimension !== void 0 && dimension !== "") return take(`观点 ${index} · ${dimension}`, TITLE_MAX);
|
|
297
|
+
return `观点 ${index}`;
|
|
298
|
+
}
|
|
299
|
+
return take(raw, TITLE_MAX);
|
|
300
|
+
}
|
|
268
301
|
const firstLine = text.split(/\r?\n/)[0] ?? text;
|
|
269
302
|
const title = (firstLine.split(/(?<=[。..;;!!??])/)[0] ?? firstLine).trim();
|
|
270
303
|
if (title === "") return take(text, TITLE_MAX);
|
|
271
304
|
return take(title, TITLE_MAX);
|
|
272
305
|
}
|
|
273
306
|
/**
|
|
307
|
+
* 把信封压缩成 `观点 N · 文件名:行号 · 维度`。
|
|
308
|
+
*
|
|
309
|
+
* @returns 压缩结果;**形状不认识时返回 `undefined`**(调用方退回原始前缀),
|
|
310
|
+
* 而不是硬拼一个残缺标题。
|
|
311
|
+
*/
|
|
312
|
+
function compactEnvelope(envelope) {
|
|
313
|
+
const parts = ENVELOPE_PARTS.exec(envelope);
|
|
314
|
+
if (parts === null) return void 0;
|
|
315
|
+
const index = parts[1] ?? "";
|
|
316
|
+
const pointer = shortPointer(parts[2] ?? "");
|
|
317
|
+
const dimension = (parts[3] ?? "").trim().replace(/\s+/g, " ");
|
|
318
|
+
if (index === "" || pointer === "" || dimension === "") return void 0;
|
|
319
|
+
return `观点 ${index} · ${take(pointer, PART_MAX)} · ${take(dimension, PART_MAX)}`;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* 把"指向"里的定位串压成**文件名 + 行号**:
|
|
323
|
+
*
|
|
324
|
+
* `dsh-plugin-crossfire/src/client/ReviewCard.module.css:106-176` → `ReviewCard.module.css:106-176`
|
|
325
|
+
* `src/a.ts` → `a.ts`
|
|
326
|
+
* `README.md` → `README.md`
|
|
327
|
+
*
|
|
328
|
+
* 只认 `/` 与 `\` 两种分隔符(Windows 路径也要能压)。文件名与行号之间的冒号保留 ——
|
|
329
|
+
* 行号是"跳到哪一行"的关键信息,不能丢。
|
|
330
|
+
*/
|
|
331
|
+
function shortPointer(pointer) {
|
|
332
|
+
const compact = pointer.trim().replace(/\s+/g, " ");
|
|
333
|
+
if (compact === "") return "";
|
|
334
|
+
return (compact.split(/[/\\]/).pop() ?? compact).trim();
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
274
337
|
* 截断到 `max` 个字符;被截断时补省略号。
|
|
275
338
|
*
|
|
276
|
-
* 省略号只在**真的截掉了内容**时加:`text` 恰好
|
|
339
|
+
* 省略号只在**真的截掉了内容**时加:`text` 恰好 `max` 字时不该显示一个假的"还有更多"。
|
|
277
340
|
*/
|
|
278
341
|
function take(text, max) {
|
|
279
342
|
return text.length > max ? `${text.slice(0, max)}…` : text;
|
|
@@ -395,7 +458,7 @@ function applyPromptResult(table, key, result) {
|
|
|
395
458
|
|
|
396
459
|
//#endregion
|
|
397
460
|
//#region \0dsh-css:F:\AI\DSH\1changyong\dsh-plugin-crossfire\src\client\ReviewCard.module.css.mjs
|
|
398
|
-
const css$1 = "/*\n * Crossfire Chat card styles.\n *\n * 硬约束:**每个颜色都来自主题令牌**(`Theme.dsefkao_listTokens` 的 `--dsw-alias-*` 一族),\n * 所以浅色与深色主题都无需自己写 override。圆桌的设置页 CSS 用了硬编码\n * `#ffffff` / `rgba(0,0,0,.8)`,在深色主题下是坏的 —— 这里不重复那个错误。\n */\n\n.dsefkao_root {\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 12px 14px;\n border: 1px solid var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.3));\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-1, transparent);\n color: var(--dsw-alias-label-primary, inherit);\n font-size: 13px;\n line-height: 1.6;\n}\n\n.dsefkao_card {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.dsefkao_card + .dsefkao_card {\n margin-top: 10px;\n padding-top: 10px;\n border-top: 1px dashed var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.3));\n}\n\n/* 折叠的评审:一行摘要,点一下切换到它。卡片会列出本会话的全部评审,全展开\n 就是一堵墙(验收期间真实攒到 5 场、12 条缺陷)。 */\n.dsefkao_collapsed {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n margin-top: 6px;\n padding: 6px 10px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.3));\n background: var(--dsw-alias-bg-layer-2, transparent);\n color: var(--dsw-alias-label-primary, inherit);\n font: inherit;\n font-size: 12px;\n text-align: left;\n cursor: pointer;\n}\n\n.dsefkao_collapsed:hover {\n border-color: var(--dsw-alias-brand-primary, #4a6cf7);\n}\n\n.dsefkao_header {\n display: flex;\n align-items: baseline;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n.dsefkao_title {\n font-weight: 700;\n font-size: 13px;\n letter-spacing: 0.02em;\n}\n\n.dsefkao_subjectTitle {\n font-weight: 600;\n}\n\n.dsefkao_badge {\n font-size: 11px;\n padding: 1px 8px;\n border-radius: 999px;\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.4));\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n.dsefkao_badge_draft {\n border-color: var(--dsw-alias-state-idle-primary, rgba(128, 128, 128, 0.5));\n}\n\n.dsefkao_badge_attacking {\n border-color: var(--dsw-alias-state-warn-primary, rgba(200, 150, 0, 0.6));\n color: var(--dsw-alias-state-warn-primary, inherit);\n}\n\n.dsefkao_badge_judging {\n border-color: var(--dsw-alias-brand-primary, #4a6cf7);\n color: var(--dsw-alias-brand-primary, inherit);\n}\n\n.dsefkao_badge_settled {\n border-color: var(--dsw-alias-state-success-primary, #2e7d32);\n color: var(--dsw-alias-state-success-primary, inherit);\n}\n\n/* ---- 状态区:当前处于哪一态 + 下一步该做什么(唯一主行动) ----------------\n *\n * 这是卡片上**唯一**的强对比区域,其余段落一律保持低对比(标题灰、正文常规)。\n * 主行动是这一区里唯一的主色实心按钮,所以\"现在该干什么\"不需要读完全卡去找。\n */\n.dsefkao_statusStrip {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n flex-wrap: wrap;\n padding: 10px 12px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.3));\n background: var(--dsw-alias-bg-layer-2, transparent);\n}\n\n/* 左侧信息块:态名 + 轮次 + 一句话说明;吃掉剩余宽度,主行动因此被推到右侧。 */\n.dsefkao_statusStripInfo {\n display: flex;\n flex-direction: column;\n gap: 2px;\n flex: 1 1 240px;\n min-width: 0;\n}\n\n.dsefkao_statusStripHead {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n/* 态名用反色实心药丸:它与主行动按钮同色,一眼就能配对(\"现在在这里 → 下一步在那里\")。\n *\n * 底色**按状态取色**,与抬头 badge 同源(draft=idle 灰 / attacking=warn 黄 /\n * judging=brand 蓝 / settled=success 绿)—— 此前这里恒为 brand 蓝,于是同一句\n * 「攻击中」在抬头是黄、在状态区是蓝,药丸对四态零区分,\"与主行动同色即可配对\"\n * 自然不成立。\n *\n * 缺省值就是 judging(品牌色,与主行动按钮同色);其余三态由下面的修饰类覆盖背景,\n * 文字色四态共用一个反色。四个状态名都来自调用点同一张\n * `styles[`statusState_${review.dsefkao_status}`]` 查表,颜色一律走 `--dsw-alias-*` 令牌。 */\n.dsefkao_statusState {\n font-size: 12px;\n font-weight: 600;\n padding: 2px 10px;\n border-radius: 999px;\n background: var(--dsw-alias-brand-primary, #4a6cf7);\n color: var(--dsw-alias-bg-base, #ffffff);\n}\n\n/* draft:与 .dsefkao_badge_draft 同源(idle 灰)。 */\n.dsefkao_statusState_draft {\n background: var(--dsw-alias-state-idle-primary, rgba(128, 128, 128, 0.85));\n}\n\n/* attacking:与 .dsefkao_badge_attacking 同源(warn 黄)。 */\n.dsefkao_statusState_attacking {\n background: var(--dsw-alias-state-warn-primary, rgba(200, 150, 0, 0.9));\n}\n\n/* settled:与 .dsefkao_badge_settled 同源(success 绿)。 */\n.dsefkao_statusState_settled {\n background: var(--dsw-alias-state-success-primary, rgba(46, 125, 50, 0.9));\n}\n\n.dsefkao_statusHint {\n font-size: 12px;\n line-height: 1.6;\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n/* 右侧行动块:一个主按钮 + 一行\"下一步\"小标题。 */\n.dsefkao_statusStripAction {\n display: flex;\n flex-direction: column;\n align-items: flex-end;\n gap: 4px;\n margin-left: auto;\n}\n\n.dsefkao_statusNextLabel {\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0.04em;\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n/* 主行动:唯一的主色实心按钮。字号与内边距都比次要按钮大一档,视觉权重随之提高。 */\n.dsefkao_cta {\n font: inherit;\n font-size: 13px;\n font-weight: 600;\n padding: 7px 18px;\n border-radius: 8px;\n cursor: pointer;\n border: 1px solid var(--dsw-alias-brand-primary, #4a6cf7);\n background: var(--dsw-alias-brand-primary, #4a6cf7);\n color: var(--dsw-alias-bg-base, #ffffff);\n}\n\n.dsefkao_cta:disabled {\n cursor: progress;\n opacity: 0.6;\n}\n\n.dsefkao_statusError {\n font-size: 12px;\n line-height: 1.6;\n color: var(--dsw-alias-state-error-primary, #d9534f);\n word-break: break-word;\n}\n\n.dsefkao_statusOk {\n font-size: 12px;\n line-height: 1.6;\n color: var(--dsw-alias-state-success-primary, #2e7d32);\n word-break: break-word;\n}\n\n.dsefkao_section {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n/* ---- 品牌块(provider 缩写 + 品牌色) ----\n *\n * 底色是**第三方商标色**(深色主题下也一样),文字固定白色 —— 这是本插件里唯一\n * 不使用主题令牌的颜色。面积被刻意压到 18px 见方:它是身份标记,不是信息主体,\n * 主题(浅色/深色)不影响其可读性。\n */\n.dsefkao_brandBlock {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: none;\n width: 18px;\n height: 18px;\n border-radius: 5px;\n font-size: 9px;\n font-weight: 700;\n letter-spacing: -0.02em;\n color: #ffffff;\n line-height: 1;\n user-select: none;\n}\n\n.dsefkao_sectionTitle {\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0.04em;\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n.dsefkao_subject,\n.dsefkao_stance {\n white-space: pre-wrap;\n word-break: break-word;\n padding: 8px 10px;\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-2, transparent);\n}\n\n.dsefkao_stance {\n border-left: 2px solid var(--dsw-alias-brand-primary, #4a6cf7);\n}\n\n/* 立场编辑框:左侧那根品牌色竖线是刻意的 —— 它是\"锋\"的视觉标记,提醒用户\n 这段文字会被红队逐条对攻。 */\n.dsefkao_stanceInput {\n font: inherit;\n font-size: 12px;\n line-height: 1.6;\n padding: 8px 10px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.4));\n border-left: 2px solid var(--dsw-alias-brand-primary, #4a6cf7);\n background: var(--dsw-alias-bg-layer-2, transparent);\n color: var(--dsw-alias-label-primary, inherit);\n resize: vertical;\n}\n\n.dsefkao_stanceInput:disabled {\n opacity: 0.7;\n}\n\n.dsefkao_chips {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n align-items: center;\n}\n\n.dsefkao_chip {\n font-size: 11px;\n padding: 1px 8px;\n border-radius: 999px;\n border: 1px solid var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.3));\n background: var(--dsw-alias-bg-layer-2, transparent);\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n.dsefkao_chipWarn {\n font-size: 11px;\n padding: 1px 8px;\n border-radius: 999px;\n border: 1px solid var(--dsw-alias-state-warn-primary, rgba(200, 150, 0, 0.6));\n color: var(--dsw-alias-state-warn-primary, inherit);\n}\n\n.dsefkao_chipStrike {\n font-size: 11px;\n padding: 1px 8px;\n border-radius: 999px;\n border: 1px solid var(--dsw-alias-state-error-primary, rgba(217, 83, 79, 0.6));\n color: var(--dsw-alias-state-error-primary, inherit);\n}\n\n.dsefkao_expertRow {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n padding: 2px 0;\n}\n\n.dsefkao_expertName {\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 11px;\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n.dsefkao_impactBar {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-wrap: wrap;\n padding: 6px 10px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.3));\n background: var(--dsw-alias-bg-layer-2, transparent);\n font-size: 12px;\n}\n\n.dsefkao_countEndorsed {\n color: var(--dsw-alias-state-success-primary, #2e7d32);\n font-weight: 600;\n}\n\n.dsefkao_countRejected {\n color: var(--dsw-alias-state-error-primary, #d9534f);\n font-weight: 600;\n}\n\n.dsefkao_countPending {\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n.dsefkao_countStrike {\n color: var(--dsw-alias-state-error-primary, #d9534f);\n}\n\n.dsefkao_defect {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 10px 12px;\n border: 1px solid var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.3));\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-2, transparent);\n}\n\n.dsefkao_defectHead {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n font-size: 11px;\n}\n\n.dsefkao_quote {\n font-size: 12px;\n padding-left: 8px;\n border-left: 2px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.4));\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n.dsefkao_defectContent {\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n/* ---- 观点标题 + 折叠 ----\n *\n * 用户诉求:①\"每条观点都要在开头立一个标题,这样目的明确\";②\"每条观点都可以折叠,\n * 折叠后只显示标题和对应专家\"。\n *\n * 标题做成**按钮**而不是纯文字:整行可点即展开/收起,省掉一个额外的折叠控件;视觉上\n * 它是这一条的抬头(比正文重一档),折叠态下也就是唯一可见的内容。\n */\n.dsefkao_defectTitle {\n font: inherit;\n font-size: 13px;\n font-weight: 600;\n line-height: 1.5;\n text-align: left;\n width: 100%;\n padding: 2px 0;\n border: none;\n background: transparent;\n color: var(--dsw-alias-label-primary, inherit);\n cursor: pointer;\n /* 折叠态下标题是这一条**唯一可见**的内容,所以它必须永远是单行:标题由\n `defectTitle()` 生成、硬上限 80 字符,13px 下 80 个汉字约 1040px,远超卡片列宽,\n 不约束就会折成 2–4 行,折叠体随之长回大块。 */\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n /* 长英文 token(文件名 / URL)没有可断点:nowrap 下它只会被省略号吃掉,但补上\n `anywhere` 后即使将来放宽 nowrap 也不会横向溢出。 */\n overflow-wrap: anywhere;\n}\n\n.dsefkao_defectTitle:hover {\n color: var(--dsw-alias-brand-primary, #4a6cf7);\n}\n\n.dsefkao_evidence {\n font-size: 12px;\n color: var(--dsw-alias-label-secondary, inherit);\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n.dsefkao_evidenceLabel {\n font-weight: 600;\n color: var(--dsw-alias-label-primary, inherit);\n}\n\n/* 复审核对区:用虚线框与\"待裁决\"的实线框区分开 —— 它问的是另一个问题\n (修没修),而不是\"这条缺陷成不成立\"。 */\n.dsefkao_recheckBox {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 8px 10px;\n border-radius: 8px;\n border: 1px dashed var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.45));\n}\n\n.dsefkao_recheckHead {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n font-size: 11px;\n}\n\n.dsefkao_rejectReason {\n font-size: 12px;\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n.dsefkao_actions {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n.dsefkao_btnPrimary,\n.dsefkao_btnPrimaryOn,\n.dsefkao_btnDanger,\n.dsefkao_btnDangerOn,\n.dsefkao_btnGhost {\n font: inherit;\n font-size: 12px;\n padding: 4px 12px;\n border-radius: 8px;\n cursor: pointer;\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.4));\n background: var(--dsw-alias-bg-layer-1, transparent);\n color: var(--dsw-alias-label-primary, inherit);\n}\n\n.dsefkao_btnPrimaryOn {\n border-color: var(--dsw-alias-state-success-primary, #2e7d32);\n color: var(--dsw-alias-state-success-primary, inherit);\n font-weight: 600;\n}\n\n.dsefkao_btnDangerOn {\n border-color: var(--dsw-alias-state-error-primary, #d9534f);\n color: var(--dsw-alias-state-error-primary, inherit);\n font-weight: 600;\n}\n\n.dsefkao_btnGhost {\n border-color: transparent;\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n.dsefkao_btnPrimary:disabled,\n.dsefkao_btnPrimaryOn:disabled,\n.dsefkao_btnDanger:disabled,\n.dsefkao_btnDangerOn:disabled,\n.dsefkao_btnGhost:disabled {\n cursor: progress;\n opacity: 0.6;\n}\n\n.dsefkao_rejectBox {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 8px 10px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-state-error-primary, rgba(217, 83, 79, 0.4));\n}\n\n.dsefkao_rejectPrompt {\n font-size: 12px;\n font-weight: 600;\n}\n\n.dsefkao_rejectInput {\n font: inherit;\n font-size: 12px;\n padding: 6px 8px;\n border-radius: 6px;\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.4));\n background: var(--dsw-alias-bg-base, transparent);\n color: var(--dsw-alias-label-primary, inherit);\n resize: vertical;\n}\n\n.dsefkao_link {\n align-self: flex-start;\n border: none;\n background: none;\n padding: 0;\n font: inherit;\n font-size: 12px;\n color: var(--dsw-alias-brand-primary, #4a6cf7);\n cursor: pointer;\n}\n\n.dsefkao_footer {\n display: flex;\n align-items: center;\n gap: 10px;\n flex-wrap: wrap;\n padding-top: 4px;\n border-top: 1px solid var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.25));\n}\n\n.dsefkao_footerNote {\n font-size: 11px;\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n.dsefkao_muted {\n font-size: 12px;\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n.dsefkao_warn {\n font-size: 11px;\n color: var(--dsw-alias-state-warn-primary, inherit);\n}\n\n.dsefkao_notice {\n font-size: 12px;\n padding: 6px 10px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-state-error-primary, rgba(217, 83, 79, 0.5));\n color: var(--dsw-alias-state-error-primary, inherit);\n}\n\n.dsefkao_empty {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n/* ==========================================================================\n * 用户反馈驱动的视觉改造(分区 / 三色表态 / 专家身份 / 可关闭说明 / 折叠按钮)\n *\n * 核心目标:**次要信息让位,主要信息抓得住**。手段只有三种 —— 分隔线、颜色语义、\n * 按钮形状。刻意不引入背景色块堆叠:那只会让\"一堵墙\"变成\"一堵彩色的墙\"。\n * 颜色仍然全部来自主题令牌(品牌色块是唯一例外,见 brand.dsefkao_ts)。\n * ========================================================================== */\n\n/* ---- 分区:带竖线的小标题 + 硬分隔线 ---- */\n\n.dsefkao_sectionDivider {\n border-top: 1px solid var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.28));\n padding-top: 10px;\n margin-top: 2px;\n}\n\n.dsefkao_sectionHeadRow {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n/* 分区标题:左侧品牌色竖线 + 加粗,成为扫读时的视觉锚点。 */\n.dsefkao_sectionTitleStrong {\n font-size: 12px;\n font-weight: 700;\n letter-spacing: 0.04em;\n padding-left: 8px;\n border-left: 3px solid var(--dsw-alias-brand-primary, #4a6cf7);\n color: var(--dsw-alias-label-primary, inherit);\n}\n\n.dsefkao_sectionCount {\n font-size: 11px;\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n/* ---- 展开 / 折叠:做成按钮,不要看起来像正文链接 ---- */\n\n.dsefkao_foldButton {\n font: inherit;\n font-size: 11px;\n padding: 2px 10px;\n border-radius: 999px;\n cursor: pointer;\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.45));\n background: var(--dsw-alias-bg-layer-2, transparent);\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n.dsefkao_foldButton:hover {\n border-color: var(--dsw-alias-brand-primary, #4a6cf7);\n color: var(--dsw-alias-label-primary, inherit);\n}\n\n/* ---- 可关闭的解释性说明 ---- */\n\n.dsefkao_explain {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n font-size: 12px;\n line-height: 1.6;\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n.dsefkao_explainText {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n.dsefkao_explainClose {\n flex: none;\n font: inherit;\n font-size: 11px;\n line-height: 1;\n padding: 3px 7px;\n border-radius: 6px;\n cursor: pointer;\n border: 1px solid transparent;\n background: transparent;\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n.dsefkao_explainClose:hover {\n border-color: var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.45));\n color: var(--dsw-alias-label-primary, inherit);\n}\n\n/* ---- 专家:头像 + 名称 + 维度 + 状态 ---- */\n\n.dsefkao_expertCard {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 8px 10px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.3));\n background: var(--dsw-alias-bg-layer-2, transparent);\n}\n\n.dsefkao_expertHead {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n/* 专家名用正文色与加粗:它是\"这条观点是谁提的\"的答案,必须比元数据更显眼。 */\n.dsefkao_expertNameStrong {\n font-size: 12px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary, inherit);\n}\n\n/* 缺陷子卡抬头里的作者标识(头像 + 名称)。 */\n.dsefkao_defectAuthor {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-size: 11px;\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n/* ---- 三态表态:绿 / 红 / 灰 -----------------------------------------------\n *\n * 用户要求:三个都做成按钮形状并各对应颜色;点中之后整块填充该颜色、文字变白。\n * 未点中 = 该颜色的描边 + 同色文字(颜色语义仍在,但不抢主行动的视觉权重)。\n */\n.dsefkao_verdictButton {\n font: inherit;\n font-size: 12px;\n font-weight: 600;\n padding: 4px 14px;\n border-radius: 8px;\n cursor: pointer;\n background: transparent;\n}\n\n.dsefkao_verdictEndorse {\n border: 1px solid var(--dsw-alias-state-success-primary, #2e7d32);\n color: var(--dsw-alias-state-success-primary, #2e7d32);\n}\n\n.dsefkao_verdictEndorseOn {\n border: 1px solid var(--dsw-alias-state-success-primary, #2e7d32);\n background: var(--dsw-alias-state-success-primary, #2e7d32);\n color: var(--dsw-alias-bg-base, #ffffff);\n}\n\n.dsefkao_verdictReject {\n border: 1px solid var(--dsw-alias-state-error-primary, #d9534f);\n color: var(--dsw-alias-state-error-primary, #d9534f);\n}\n\n.dsefkao_verdictRejectOn {\n border: 1px solid var(--dsw-alias-state-error-primary, #d9534f);\n background: var(--dsw-alias-state-error-primary, #d9534f);\n color: var(--dsw-alias-bg-base, #ffffff);\n}\n\n.dsefkao_verdictPending {\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.5));\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n.dsefkao_verdictPendingOn {\n border: 1px solid var(--dsw-alias-state-idle-primary, rgba(128, 128, 128, 0.85));\n background: var(--dsw-alias-state-idle-primary, rgba(128, 128, 128, 0.85));\n color: var(--dsw-alias-bg-base, #ffffff);\n}\n\n.dsefkao_verdictButton:disabled {\n cursor: progress;\n opacity: 0.6;\n}\n\n/* ---- 立场全貌(可滚动) ---- */\n\n.dsefkao_stanceFull {\n white-space: pre-wrap;\n word-break: break-word;\n max-height: 320px;\n overflow: auto;\n padding: 8px 10px;\n border-radius: 8px;\n border-left: 2px solid var(--dsw-alias-brand-primary, #4a6cf7);\n background: var(--dsw-alias-bg-layer-2, transparent);\n}\n\n/* ---- 角色定位编辑器 ---- */\n\n.dsefkao_roleEditor {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.dsefkao_roleInput {\n font: inherit;\n font-size: 12px;\n line-height: 1.6;\n padding: 6px 8px;\n border-radius: 6px;\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.4));\n background: var(--dsw-alias-bg-base, transparent);\n color: var(--dsw-alias-label-primary, inherit);\n resize: vertical;\n}\n\n";
|
|
461
|
+
const css$1 = "/*\n * Crossfire Chat card styles.\n *\n * 硬约束:**每个颜色都来自主题令牌**(`Theme.dsefkao_listTokens` 的 `--dsw-alias-*` 一族),\n * 所以浅色与深色主题都无需自己写 override。圆桌的设置页 CSS 用了硬编码\n * `#ffffff` / `rgba(0,0,0,.8)`,在深色主题下是坏的 —— 这里不重复那个错误。\n */\n\n.dsefkao_root {\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 12px 14px;\n border: 1px solid var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.3));\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-1, transparent);\n color: var(--dsw-alias-label-primary, inherit);\n font-size: 13px;\n line-height: 1.6;\n}\n\n.dsefkao_card {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.dsefkao_card + .dsefkao_card {\n margin-top: 10px;\n padding-top: 10px;\n border-top: 1px dashed var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.3));\n}\n\n/* 折叠的评审:一行摘要,点一下切换到它。卡片会列出本会话的全部评审,全展开\n 就是一堵墙(验收期间真实攒到 5 场、12 条缺陷)。 */\n.dsefkao_collapsed {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n margin-top: 6px;\n padding: 6px 10px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.3));\n background: var(--dsw-alias-bg-layer-2, transparent);\n color: var(--dsw-alias-label-primary, inherit);\n font: inherit;\n font-size: 12px;\n text-align: left;\n cursor: pointer;\n}\n\n.dsefkao_collapsed:hover {\n border-color: var(--dsw-alias-brand-primary, #4a6cf7);\n}\n\n.dsefkao_header {\n display: flex;\n align-items: baseline;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n.dsefkao_title {\n font-weight: 700;\n font-size: 13px;\n letter-spacing: 0.02em;\n}\n\n.dsefkao_subjectTitle {\n font-weight: 600;\n}\n\n.dsefkao_badge {\n font-size: 11px;\n padding: 1px 8px;\n border-radius: 999px;\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.4));\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n.dsefkao_badge_draft {\n border-color: var(--dsw-alias-state-idle-primary, rgba(128, 128, 128, 0.5));\n}\n\n.dsefkao_badge_attacking {\n border-color: var(--dsw-alias-state-warn-primary, rgba(200, 150, 0, 0.6));\n color: var(--dsw-alias-state-warn-primary, inherit);\n}\n\n.dsefkao_badge_judging {\n border-color: var(--dsw-alias-brand-primary, #4a6cf7);\n color: var(--dsw-alias-brand-primary, inherit);\n}\n\n.dsefkao_badge_settled {\n border-color: var(--dsw-alias-state-success-primary, #2e7d32);\n color: var(--dsw-alias-state-success-primary, inherit);\n}\n\n/* ---- 状态区:当前处于哪一态 + 下一步该做什么(唯一主行动) ----------------\n *\n * 这是卡片上**唯一**的强对比区域,其余段落一律保持低对比(标题灰、正文常规)。\n * 主行动是这一区里唯一的主色实心按钮,所以\"现在该干什么\"不需要读完全卡去找。\n */\n.dsefkao_statusStrip {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n flex-wrap: wrap;\n padding: 12px 14px;\n border-radius: 10px;\n border: 1px solid var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.3));\n background: var(--dsw-alias-bg-layer-2, transparent);\n}\n\n/* 左侧信息块:态名 + 轮次 + 一句话说明;吃掉剩余宽度,主行动因此被推到右侧。 */\n.dsefkao_statusStripInfo {\n display: flex;\n flex-direction: column;\n gap: 2px;\n flex: 1 1 240px;\n min-width: 0;\n}\n\n.dsefkao_statusStripHead {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n/* 态名用反色实心药丸:它与主行动按钮同色,一眼就能配对(\"现在在这里 → 下一步在那里\")。\n *\n * 底色**按状态取色**,与抬头 badge 同源(draft=idle 灰 / attacking=warn 黄 /\n * judging=brand 蓝 / settled=success 绿)—— 此前这里恒为 brand 蓝,于是同一句\n * 「攻击中」在抬头是黄、在状态区是蓝,药丸对四态零区分,\"与主行动同色即可配对\"\n * 自然不成立。\n *\n * 缺省值就是 judging(品牌色,与主行动按钮同色);其余三态由下面的修饰类覆盖背景,\n * 文字色四态共用一个反色。四个状态名都来自调用点同一张\n * `styles[`statusState_${review.dsefkao_status}`]` 查表,颜色一律走 `--dsw-alias-*` 令牌。 */\n.dsefkao_statusState {\n font-size: 12px;\n font-weight: 600;\n padding: 2px 10px;\n border-radius: 999px;\n background: var(--dsw-alias-brand-primary, #4a6cf7);\n color: var(--dsw-alias-bg-base, #ffffff);\n}\n\n/* draft:与 .dsefkao_badge_draft 同源(idle 灰)。 */\n.dsefkao_statusState_draft {\n background: var(--dsw-alias-state-idle-primary, rgba(128, 128, 128, 0.85));\n}\n\n/* attacking:与 .dsefkao_badge_attacking 同源(warn 黄)。 */\n.dsefkao_statusState_attacking {\n background: var(--dsw-alias-state-warn-primary, rgba(200, 150, 0, 0.9));\n}\n\n/* settled:与 .dsefkao_badge_settled 同源(success 绿)。 */\n.dsefkao_statusState_settled {\n background: var(--dsw-alias-state-success-primary, rgba(46, 125, 50, 0.9));\n}\n\n.dsefkao_statusHint {\n font-size: 12px;\n line-height: 1.6;\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n/* 右侧行动块:一个主按钮 + 一行\"下一步\"小标题。 */\n.dsefkao_statusStripAction {\n display: flex;\n flex-direction: column;\n align-items: flex-end;\n gap: 4px;\n margin-left: auto;\n}\n\n.dsefkao_statusNextLabel {\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0.04em;\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n/* 主行动:唯一的主色实心按钮。字号与内边距都比次要按钮大一档,视觉权重随之提高。 */\n.dsefkao_cta {\n font: inherit;\n font-size: 13px;\n font-weight: 600;\n padding: 7px 18px;\n border-radius: 8px;\n cursor: pointer;\n border: 1px solid var(--dsw-alias-brand-primary, #4a6cf7);\n background: var(--dsw-alias-brand-primary, #4a6cf7);\n color: var(--dsw-alias-bg-base, #ffffff);\n}\n\n.dsefkao_cta:disabled {\n cursor: progress;\n opacity: 0.6;\n}\n\n.dsefkao_statusError {\n font-size: 12px;\n line-height: 1.6;\n color: var(--dsw-alias-state-error-primary, #d9534f);\n word-break: break-word;\n}\n\n.dsefkao_statusOk {\n font-size: 12px;\n line-height: 1.6;\n color: var(--dsw-alias-state-success-primary, #2e7d32);\n word-break: break-word;\n}\n\n.dsefkao_section {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n/* ---- 品牌块(provider 缩写 + 品牌色) ----\n *\n * 底色是**第三方商标色**(深色主题下也一样),文字固定白色 —— 这是本插件里唯一\n * 不使用主题令牌的颜色。面积被刻意压到 18px 见方:它是身份标记,不是信息主体,\n * 主题(浅色/深色)不影响其可读性。\n */\n.dsefkao_brandBlock {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: none;\n width: 18px;\n height: 18px;\n border-radius: 5px;\n font-size: 9px;\n font-weight: 700;\n letter-spacing: -0.02em;\n color: #ffffff;\n line-height: 1;\n user-select: none;\n}\n\n.dsefkao_sectionTitle {\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0.04em;\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n.dsefkao_subject,\n.dsefkao_stance {\n white-space: pre-wrap;\n word-break: break-word;\n padding: 8px 10px;\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-2, transparent);\n}\n\n.dsefkao_stance {\n border-left: 2px solid var(--dsw-alias-brand-primary, #4a6cf7);\n}\n\n/* 立场编辑框:左侧那根品牌色竖线是刻意的 —— 它是\"锋\"的视觉标记,提醒用户\n 这段文字会被红队逐条对攻。 */\n.dsefkao_stanceInput {\n font: inherit;\n font-size: 12px;\n line-height: 1.6;\n padding: 8px 10px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.4));\n border-left: 2px solid var(--dsw-alias-brand-primary, #4a6cf7);\n background: var(--dsw-alias-bg-layer-2, transparent);\n color: var(--dsw-alias-label-primary, inherit);\n resize: vertical;\n}\n\n.dsefkao_stanceInput:disabled {\n opacity: 0.7;\n}\n\n.dsefkao_chips {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n align-items: center;\n}\n\n.dsefkao_chip {\n font-size: 11px;\n padding: 1px 8px;\n border-radius: 999px;\n border: 1px solid var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.3));\n background: var(--dsw-alias-bg-layer-2, transparent);\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n.dsefkao_chipWarn {\n font-size: 11px;\n padding: 1px 8px;\n border-radius: 999px;\n border: 1px solid var(--dsw-alias-state-warn-primary, rgba(200, 150, 0, 0.6));\n color: var(--dsw-alias-state-warn-primary, inherit);\n}\n\n.dsefkao_chipStrike {\n font-size: 11px;\n padding: 1px 8px;\n border-radius: 999px;\n border: 1px solid var(--dsw-alias-state-error-primary, rgba(217, 83, 79, 0.6));\n color: var(--dsw-alias-state-error-primary, inherit);\n}\n\n.dsefkao_expertRow {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n padding: 2px 0;\n}\n\n.dsefkao_expertName {\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 11px;\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n.dsefkao_impactBar {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-wrap: wrap;\n padding: 6px 10px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.3));\n background: var(--dsw-alias-bg-layer-2, transparent);\n font-size: 12px;\n}\n\n.dsefkao_countEndorsed {\n color: var(--dsw-alias-state-success-primary, #2e7d32);\n font-weight: 600;\n}\n\n.dsefkao_countRejected {\n color: var(--dsw-alias-state-error-primary, #d9534f);\n font-weight: 600;\n}\n\n.dsefkao_countPending {\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n.dsefkao_countStrike {\n color: var(--dsw-alias-state-error-primary, #d9534f);\n}\n\n/* 一条待裁决缺陷。\n *\n * 左侧 3px 竖条按表态状态着色(认定=绿 / 驳回=红 / 待定=灰):一条评审里常有\n * 十几条缺陷,用户最需要先看清\"哪些还没表态\",而这个信息此前只体现在底部按钮\n * 描边上 —— 必须把每条都读一遍才能得到全局分布。竖条让分布在一眼内可见。 */\n.dsefkao_defect {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 12px;\n border: 1px solid var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.3));\n border-left-width: 3px;\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-2, transparent);\n}\n\n.dsefkao_defect_pending {\n border-left-color: var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.4));\n}\n\n.dsefkao_defect_endorsed {\n border-left-color: var(--dsw-alias-state-success-primary, #2e7d32);\n}\n\n.dsefkao_defect_rejected {\n border-left-color: var(--dsw-alias-state-error-primary, #d9534f);\n}\n\n/* 缺陷之间留出可辨认的呼吸:此前它们只隔 4px(父级 .dsefkao_section 的 gap),\n * 一条条读下来像一整块连续文本。 */\n.dsefkao_defect + .dsefkao_defect {\n margin-top: 8px;\n}\n\n.dsefkao_defectHead {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n font-size: 12px;\n}\n\n.dsefkao_quote {\n font-size: 12px;\n padding-left: 8px;\n border-left: 2px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.4));\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n.dsefkao_defectContent {\n white-space: pre-wrap;\n word-break: break-word;\n line-height: 1.65;\n}\n\n/* ---- 观点标题 + 折叠 ----\n *\n * 用户诉求:①\"每条观点都要在开头立一个标题,这样目的明确\";②\"每条观点都可以折叠,\n * 折叠后只显示标题和对应专家\"。\n *\n * 标题做成**按钮**而不是纯文字:整行可点即展开/收起,省掉一个额外的折叠控件;视觉上\n * 它是这一条的抬头(比正文重一档),折叠态下也就是唯一可见的内容。\n */\n.dsefkao_defectTitle {\n font: inherit;\n font-size: 13px;\n font-weight: 600;\n line-height: 1.5;\n text-align: left;\n width: 100%;\n padding: 4px 0;\n border: none;\n background: transparent;\n color: var(--dsw-alias-label-primary, inherit);\n cursor: pointer;\n /* 折叠态下标题是这一条**唯一可见**的内容,所以它必须永远是单行:标题由\n `defectTitle()` 生成、硬上限 80 字符,13px 下 80 个汉字约 1040px,远超卡片列宽,\n 不约束就会折成 2–4 行,折叠体随之长回大块。 */\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n /* 长英文 token(文件名 / URL)没有可断点:nowrap 下它只会被省略号吃掉,但补上\n `anywhere` 后即使将来放宽 nowrap 也不会横向溢出。 */\n overflow-wrap: anywhere;\n}\n\n.dsefkao_defectTitle:hover {\n color: var(--dsw-alias-brand-primary, #4a6cf7);\n}\n\n.dsefkao_evidence {\n font-size: 12px;\n color: var(--dsw-alias-label-secondary, inherit);\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n.dsefkao_evidenceLabel {\n font-weight: 600;\n color: var(--dsw-alias-label-primary, inherit);\n}\n\n/* 复审核对区:用虚线框与\"待裁决\"的实线框区分开 —— 它问的是另一个问题\n (修没修),而不是\"这条缺陷成不成立\"。 */\n.dsefkao_recheckBox {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 8px 10px;\n border-radius: 8px;\n border: 1px dashed var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.45));\n}\n\n.dsefkao_recheckHead {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n font-size: 11px;\n}\n\n.dsefkao_rejectReason {\n font-size: 12px;\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n.dsefkao_actions {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n.dsefkao_btnPrimary,\n.dsefkao_btnPrimaryOn,\n.dsefkao_btnDanger,\n.dsefkao_btnDangerOn,\n.dsefkao_btnGhost {\n font: inherit;\n font-size: 12px;\n padding: 4px 12px;\n border-radius: 8px;\n cursor: pointer;\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.4));\n background: var(--dsw-alias-bg-layer-1, transparent);\n color: var(--dsw-alias-label-primary, inherit);\n}\n\n.dsefkao_btnPrimaryOn {\n border-color: var(--dsw-alias-state-success-primary, #2e7d32);\n color: var(--dsw-alias-state-success-primary, inherit);\n font-weight: 600;\n}\n\n.dsefkao_btnDangerOn {\n border-color: var(--dsw-alias-state-error-primary, #d9534f);\n color: var(--dsw-alias-state-error-primary, inherit);\n font-weight: 600;\n}\n\n.dsefkao_btnGhost {\n border-color: transparent;\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n.dsefkao_btnPrimary:disabled,\n.dsefkao_btnPrimaryOn:disabled,\n.dsefkao_btnDanger:disabled,\n.dsefkao_btnDangerOn:disabled,\n.dsefkao_btnGhost:disabled {\n cursor: progress;\n opacity: 0.6;\n}\n\n.dsefkao_rejectBox {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 8px 10px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-state-error-primary, rgba(217, 83, 79, 0.4));\n}\n\n.dsefkao_rejectPrompt {\n font-size: 12px;\n font-weight: 600;\n}\n\n.dsefkao_rejectInput {\n font: inherit;\n font-size: 12px;\n padding: 6px 8px;\n border-radius: 6px;\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.4));\n background: var(--dsw-alias-bg-base, transparent);\n color: var(--dsw-alias-label-primary, inherit);\n resize: vertical;\n}\n\n.dsefkao_link {\n align-self: flex-start;\n border: none;\n background: none;\n padding: 0;\n font: inherit;\n font-size: 12px;\n color: var(--dsw-alias-brand-primary, #4a6cf7);\n cursor: pointer;\n}\n\n.dsefkao_footer {\n display: flex;\n align-items: center;\n gap: 10px;\n flex-wrap: wrap;\n padding-top: 4px;\n border-top: 1px solid var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.25));\n}\n\n.dsefkao_footerNote {\n font-size: 11px;\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n.dsefkao_muted {\n font-size: 12px;\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n.dsefkao_warn {\n font-size: 11px;\n color: var(--dsw-alias-state-warn-primary, inherit);\n}\n\n.dsefkao_notice {\n font-size: 12px;\n padding: 6px 10px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-state-error-primary, rgba(217, 83, 79, 0.5));\n color: var(--dsw-alias-state-error-primary, inherit);\n}\n\n/* 空态:没有评审时整卡只剩一行字,看起来像\"坏了\"。\n * 用一圈虚线 + 内边距把它读成\"这里还没有东西\",而不是\"渲染失败\"。 */\n.dsefkao_empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 6px;\n padding: 28px 16px;\n border: 1px dashed var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.4));\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-2, transparent);\n text-align: center;\n}\n\n/* ==========================================================================\n * 用户反馈驱动的视觉改造(分区 / 三色表态 / 专家身份 / 可关闭说明 / 折叠按钮)\n *\n * 核心目标:**次要信息让位,主要信息抓得住**。手段只有三种 —— 分隔线、颜色语义、\n * 按钮形状。刻意不引入背景色块堆叠:那只会让\"一堵墙\"变成\"一堵彩色的墙\"。\n * 颜色仍然全部来自主题令牌(品牌色块是唯一例外,见 brand.dsefkao_ts)。\n * ========================================================================== */\n\n/* ---- 分区:带竖线的小标题 + 硬分隔线 ---- */\n\n.dsefkao_sectionDivider {\n border-top: 1px solid var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.28));\n padding-top: 10px;\n margin-top: 2px;\n}\n\n.dsefkao_sectionHeadRow {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n/* 分区标题:左侧品牌色竖线 + 加粗,成为扫读时的视觉锚点。 */\n.dsefkao_sectionTitleStrong {\n font-size: 12px;\n font-weight: 700;\n letter-spacing: 0.04em;\n padding-left: 8px;\n border-left: 3px solid var(--dsw-alias-brand-primary, #4a6cf7);\n color: var(--dsw-alias-label-primary, inherit);\n}\n\n.dsefkao_sectionCount {\n font-size: 11px;\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n/* ---- 展开 / 折叠:做成按钮,不要看起来像正文链接 ---- */\n\n.dsefkao_foldButton {\n font: inherit;\n font-size: 11px;\n padding: 2px 10px;\n border-radius: 999px;\n cursor: pointer;\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.45));\n background: var(--dsw-alias-bg-layer-2, transparent);\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n.dsefkao_foldButton:hover {\n border-color: var(--dsw-alias-brand-primary, #4a6cf7);\n color: var(--dsw-alias-label-primary, inherit);\n}\n\n/* ---- 可关闭的解释性说明 ---- */\n\n.dsefkao_explain {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n font-size: 12px;\n line-height: 1.6;\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n.dsefkao_explainText {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n.dsefkao_explainClose {\n flex: none;\n font: inherit;\n font-size: 11px;\n line-height: 1;\n padding: 3px 7px;\n border-radius: 6px;\n cursor: pointer;\n border: 1px solid transparent;\n background: transparent;\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n.dsefkao_explainClose:hover {\n border-color: var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.45));\n color: var(--dsw-alias-label-primary, inherit);\n}\n\n/* ---- 专家:头像 + 名称 + 维度 + 状态 ---- */\n\n.dsefkao_expertCard {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 8px 10px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.3));\n background: var(--dsw-alias-bg-layer-2, transparent);\n}\n\n.dsefkao_expertHead {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n/* 专家名用正文色与加粗:它是\"这条观点是谁提的\"的答案,必须比元数据更显眼。 */\n.dsefkao_expertNameStrong {\n font-size: 12px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary, inherit);\n}\n\n/* 缺陷子卡抬头里的作者标识(头像 + 名称)。 */\n.dsefkao_defectAuthor {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-size: 11px;\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n/* ---- 三态表态:绿 / 红 / 灰 -----------------------------------------------\n *\n * 用户要求:三个都做成按钮形状并各对应颜色;点中之后整块填充该颜色、文字变白。\n * 未点中 = 该颜色的描边 + 同色文字(颜色语义仍在,但不抢主行动的视觉权重)。\n */\n.dsefkao_verdictButton {\n box-sizing: border-box;\n min-height: 28px;\n font: inherit;\n font-size: 12px;\n font-weight: 600;\n padding: 4px 14px;\n border-radius: 8px;\n cursor: pointer;\n background: transparent;\n}\n\n.dsefkao_verdictEndorse {\n border: 1px solid var(--dsw-alias-state-success-primary, #2e7d32);\n color: var(--dsw-alias-state-success-primary, #2e7d32);\n}\n\n.dsefkao_verdictEndorseOn {\n border: 1px solid var(--dsw-alias-state-success-primary, #2e7d32);\n background: var(--dsw-alias-state-success-primary, #2e7d32);\n color: var(--dsw-alias-bg-base, #ffffff);\n}\n\n.dsefkao_verdictReject {\n border: 1px solid var(--dsw-alias-state-error-primary, #d9534f);\n color: var(--dsw-alias-state-error-primary, #d9534f);\n}\n\n.dsefkao_verdictRejectOn {\n border: 1px solid var(--dsw-alias-state-error-primary, #d9534f);\n background: var(--dsw-alias-state-error-primary, #d9534f);\n color: var(--dsw-alias-bg-base, #ffffff);\n}\n\n.dsefkao_verdictPending {\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.5));\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n.dsefkao_verdictPendingOn {\n border: 1px solid var(--dsw-alias-state-idle-primary, rgba(128, 128, 128, 0.85));\n background: var(--dsw-alias-state-idle-primary, rgba(128, 128, 128, 0.85));\n color: var(--dsw-alias-bg-base, #ffffff);\n}\n\n.dsefkao_verdictButton:disabled {\n cursor: progress;\n opacity: 0.6;\n}\n\n/* ---- 立场全貌(可滚动) ---- */\n\n.dsefkao_stanceFull {\n white-space: pre-wrap;\n word-break: break-word;\n max-height: 320px;\n overflow: auto;\n padding: 8px 10px;\n border-radius: 8px;\n border-left: 2px solid var(--dsw-alias-brand-primary, #4a6cf7);\n background: var(--dsw-alias-bg-layer-2, transparent);\n}\n\n/* ---- 角色定位编辑器 ---- */\n\n.dsefkao_roleEditor {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.dsefkao_roleInput {\n font: inherit;\n font-size: 12px;\n line-height: 1.6;\n padding: 6px 8px;\n border-radius: 6px;\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.4));\n background: var(--dsw-alias-bg-base, transparent);\n color: var(--dsw-alias-label-primary, inherit);\n resize: vertical;\n}\n\n";
|
|
399
462
|
const classMap$1 = {
|
|
400
463
|
"listTokens": "dsefkao_listTokens",
|
|
401
464
|
"root": "dsefkao_root",
|
|
@@ -441,6 +504,9 @@ const classMap$1 = {
|
|
|
441
504
|
"countPending": "dsefkao_countPending",
|
|
442
505
|
"countStrike": "dsefkao_countStrike",
|
|
443
506
|
"defect": "dsefkao_defect",
|
|
507
|
+
"defect_pending": "dsefkao_defect_pending",
|
|
508
|
+
"defect_endorsed": "dsefkao_defect_endorsed",
|
|
509
|
+
"defect_rejected": "dsefkao_defect_rejected",
|
|
444
510
|
"defectHead": "dsefkao_defectHead",
|
|
445
511
|
"quote": "dsefkao_quote",
|
|
446
512
|
"defectContent": "dsefkao_defectContent",
|
|
@@ -984,7 +1050,7 @@ function CrossfireCard(props) {
|
|
|
984
1050
|
}),
|
|
985
1051
|
review.status === "judging" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
986
1052
|
className: ReviewCard_module_css_default.muted,
|
|
987
|
-
children:
|
|
1053
|
+
children: t("countPendingOfTotal").replace("{pending}", String(review.counts.pending)).replace("{total}", String(review.counts.total))
|
|
988
1054
|
}) : null,
|
|
989
1055
|
review.status === "attacking" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
990
1056
|
className: ReviewCard_module_css_default.muted,
|
|
@@ -1442,7 +1508,7 @@ function CrossfireCard(props) {
|
|
|
1442
1508
|
const foldKey = defectFoldKey(review.reviewId, defect.id);
|
|
1443
1509
|
const isOpen = expandedDefects[foldKey] === true;
|
|
1444
1510
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1445
|
-
className: ReviewCard_module_css_default.defect
|
|
1511
|
+
className: `${ReviewCard_module_css_default.defect} ${ReviewCard_module_css_default[`defect_${defect.status}`] ?? ""}`,
|
|
1446
1512
|
children: [
|
|
1447
1513
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1448
1514
|
className: ReviewCard_module_css_default.defectHead,
|
|
@@ -1708,9 +1774,82 @@ function mergeSquad(list, squad) {
|
|
|
1708
1774
|
return list.some((item) => item.id === squad.id) ? list.map((item) => item.id === squad.id ? squad : item) : [...list, squad];
|
|
1709
1775
|
}
|
|
1710
1776
|
|
|
1777
|
+
//#endregion
|
|
1778
|
+
//#region src/client/icons.tsx
|
|
1779
|
+
const SHAPES = {
|
|
1780
|
+
layers: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
1781
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M8 2.2 2.4 5 8 7.8 13.6 5 8 2.2Z" }),
|
|
1782
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M2.4 8 8 10.8 13.6 8" }),
|
|
1783
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M2.4 11 8 13.8 13.6 11" })
|
|
1784
|
+
] }),
|
|
1785
|
+
gauge: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
1786
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M2.2 11.5a5.8 5.8 0 1 1 11.6 0" }),
|
|
1787
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M8 11.5 10.8 6.5" }),
|
|
1788
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
1789
|
+
cx: "8",
|
|
1790
|
+
cy: "11.5",
|
|
1791
|
+
r: "0.9"
|
|
1792
|
+
})
|
|
1793
|
+
] }),
|
|
1794
|
+
role: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
1795
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
1796
|
+
x: "2.2",
|
|
1797
|
+
y: "3.2",
|
|
1798
|
+
width: "11.6",
|
|
1799
|
+
height: "9.6",
|
|
1800
|
+
rx: "2"
|
|
1801
|
+
}),
|
|
1802
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
1803
|
+
cx: "6",
|
|
1804
|
+
cy: "7.2",
|
|
1805
|
+
r: "1.4"
|
|
1806
|
+
}),
|
|
1807
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M8.6 6.4h3.2M8.6 8.4h2.4" })
|
|
1808
|
+
] }),
|
|
1809
|
+
squad: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
1810
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
1811
|
+
cx: "6",
|
|
1812
|
+
cy: "6",
|
|
1813
|
+
r: "2"
|
|
1814
|
+
}),
|
|
1815
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M2.6 13c.3-2.1 1.7-3.2 3.4-3.2s3.1 1.1 3.4 3.2" }),
|
|
1816
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
1817
|
+
cx: "11.4",
|
|
1818
|
+
cy: "6.6",
|
|
1819
|
+
r: "1.5"
|
|
1820
|
+
}),
|
|
1821
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M11.4 9.4c1.5 0 2.5 1 2.8 2.6" })
|
|
1822
|
+
] }),
|
|
1823
|
+
panel: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
1824
|
+
x: "2.2",
|
|
1825
|
+
y: "3.2",
|
|
1826
|
+
width: "11.6",
|
|
1827
|
+
height: "9.6",
|
|
1828
|
+
rx: "2"
|
|
1829
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M6.2 3.2v9.6" })] }),
|
|
1830
|
+
skill: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M9.2 2 4.6 8.8h3L6.8 14l4.6-6.8h-3L9.2 2Z" }),
|
|
1831
|
+
message: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M3 3.4h10a1.6 1.6 0 0 1 1.6 1.6v5.2A1.6 1.6 0 0 1 13 11.8H8.4L5 14v-2.2H3A1.6 1.6 0 0 1 1.4 10.2V5A1.6 1.6 0 0 1 3 3.4Z" })
|
|
1832
|
+
};
|
|
1833
|
+
/** 渲染一个 16×16 线性图标,颜色跟随 `currentColor`。 */
|
|
1834
|
+
function InlineIcon({ name, size = 14 }) {
|
|
1835
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
1836
|
+
width: size,
|
|
1837
|
+
height: size,
|
|
1838
|
+
viewBox: "0 0 16 16",
|
|
1839
|
+
fill: "none",
|
|
1840
|
+
stroke: "currentColor",
|
|
1841
|
+
strokeWidth: "1.4",
|
|
1842
|
+
strokeLinecap: "round",
|
|
1843
|
+
strokeLinejoin: "round",
|
|
1844
|
+
"aria-hidden": "true",
|
|
1845
|
+
focusable: "false",
|
|
1846
|
+
children: SHAPES[name]
|
|
1847
|
+
});
|
|
1848
|
+
}
|
|
1849
|
+
|
|
1711
1850
|
//#endregion
|
|
1712
1851
|
//#region \0dsh-css:F:\AI\DSH\1changyong\dsh-plugin-crossfire\src\client\SettingsView.module.css.mjs
|
|
1713
|
-
const css = "/*\n * Crossfire settings page styles.\n *\n * 硬约束:**每一个颜色都必须来自主题令牌**(运行时 `Theme.ddxi2lc_listTokens` 的\n * `--dsw-alias-*` 一族),因此浅色与深色主题都无需自己写 override。\n *\n * 这里刻意不照抄圆桌会议的 `RoundTableSettings.ddxi2lc_module.ddxi2lc_css`:那份用的是硬编码\n * `#ffffff` / `rgba(0, 0, 0, 0.8)`,在深色主题下会呈现出白底黑字的割裂块。\n * 令牌已经由宿主按主题解析过,直接用才是正确的做法。\n */\n\n.ddxi2lc_root {\n display: flex;\n flex-direction: column;\n gap: 20px;\n max-width: 620px;\n padding: 8px 0;\n color: var(--dsw-alias-label-primary, inherit);\n}\n\n.ddxi2lc_notice {\n font-size: 12px;\n line-height: 1.6;\n}\n\n.ddxi2lc_noticeOk {\n color: var(--dsw-alias-state-success-primary, #2e7d32);\n}\n\n.ddxi2lc_noticeErr {\n color: var(--dsw-alias-state-error-primary, #d9534f);\n}\n\n.ddxi2lc_section {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.ddxi2lc_sectionTitle {\n font-size: 14px;\n font-weight: 600;\n}\n\n/* 区块抬头行:标题在左,\"新建\"这类**区块主操作**在右。\n *\n * 之前\"+ 新建预设\"与\"编辑/删除\"这类次要操作同级、且淹没在列表下方,用户实测\n * 反馈\"不够显眼\"。提为区块主操作后,它在本区里是唯一的实心按钮。 */\n.ddxi2lc_sectionHead {\n display: flex;\n align-items: center;\n gap: 10px;\n flex-wrap: wrap;\n}\n\n.ddxi2lc_sectionHead .ddxi2lc_sectionTitle {\n flex: 0 1 auto;\n}\n\n/* 把主操作推到右侧;窄屏换行时它自然塌成整行。 */\n.ddxi2lc_sectionHeadGap {\n flex: 1 1 auto;\n}\n\n/* ---- 区块主操作(品牌色实心) ---- */\n\n.ddxi2lc_primaryButton {\n font-size: 13px;\n font-weight: 600;\n padding: 6px 14px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-brand-primary, #4a6cf7);\n background: var(--dsw-alias-brand-primary, #4a6cf7);\n color: var(--dsw-alias-bg-base, #ffffff);\n cursor: pointer;\n}\n\n.ddxi2lc_primaryButton:disabled {\n cursor: not-allowed;\n border-color: var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.3));\n background: var(--dsw-alias-bg-layer-2, transparent);\n color: var(--dsw-alias-state-idle-primary, rgba(128, 128, 128, 0.7));\n}\n\n/* ---- 品牌块(provider 缩写 + 品牌色) ----\n *\n * 与 ReviewCard.ddxi2lc_module.ddxi2lc_css 的同名规则刻意各写一份:CSS Modules 的 classMap 是\n * 按模块生成的,跨模块复用类名只能靠把 classMap 传来传去,不值得。两处的大小\n * 差异也是真实的(这里是列表行,那里是密集的专家行)。\n */\n.ddxi2lc_brandBlock {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: none;\n width: 22px;\n height: 22px;\n border-radius: 6px;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: -0.02em;\n color: #ffffff;\n line-height: 1;\n user-select: none;\n}\n\n/* 行内的品牌块 + 路由文本:块给身份,文本给可读的全名(块的 title 在触屏上读不到)。 */\n.ddxi2lc_routeRow {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n.ddxi2lc_hint {\n font-size: 12px;\n line-height: 1.7;\n color: var(--dsw-alias-label-secondary, rgba(128, 128, 128, 0.9));\n}\n\n.ddxi2lc_field {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.ddxi2lc_label {\n font-size: 13px;\n font-weight: 600;\n}\n\n.ddxi2lc_input,\n.ddxi2lc_select {\n font-size: 13px;\n padding: 6px 10px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.4));\n background: var(--dsw-alias-bg-layer-1, transparent);\n color: var(--dsw-alias-label-primary, inherit);\n max-width: 260px;\n}\n\n.ddxi2lc_input:focus,\n.ddxi2lc_select:focus {\n outline: 2px solid var(--dsw-alias-brand-primary, #4a6cf7);\n outline-offset: 1px;\n}\n\n/* ---- 强度:三选一的分段控件 ---- */\n\n.ddxi2lc_segmented {\n display: inline-flex;\n gap: 0;\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.4));\n border-radius: 8px;\n overflow: hidden;\n width: fit-content;\n}\n\n.ddxi2lc_segment {\n font-size: 13px;\n padding: 6px 14px;\n border: none;\n background: var(--dsw-alias-bg-layer-1, transparent);\n color: var(--dsw-alias-label-primary, inherit);\n cursor: pointer;\n}\n\n.ddxi2lc_segment + .ddxi2lc_segment {\n border-left: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.4));\n}\n\n.ddxi2lc_segmentActive {\n background: var(--dsw-alias-brand-primary, #4a6cf7);\n color: var(--dsw-alias-bg-base, #ffffff);\n font-weight: 600;\n}\n\n.ddxi2lc_segment:disabled {\n cursor: progress;\n color: var(--dsw-alias-state-idle-primary, rgba(128, 128, 128, 0.7));\n}\n\n/* ---- 维度池 ---- */\n\n.ddxi2lc_chips {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n.ddxi2lc_chip {\n font-size: 12px;\n padding: 3px 10px;\n border-radius: 999px;\n border: 1px solid var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.3));\n background: var(--dsw-alias-bg-layer-2, transparent);\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n/* 带品牌块的成员 chip:左边品牌色块,右边成员 key。 */\n.ddxi2lc_chipWithBrand {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n padding: 3px 10px 3px 4px;\n border-radius: 999px;\n border: 1px solid var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.3));\n background: var(--dsw-alias-bg-layer-2, transparent);\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n/* 成员 chip 里的品牌块比列表行的再小一点:chip 本身只有 24px 高。 */\n.ddxi2lc_brandBlockSmall {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: none;\n width: 18px;\n height: 18px;\n border-radius: 5px;\n font-size: 9px;\n font-weight: 700;\n letter-spacing: -0.02em;\n color: #ffffff;\n line-height: 1;\n user-select: none;\n}\n\n.ddxi2lc_chipCustom {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n padding: 3px 6px 3px 10px;\n border-radius: 999px;\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.4));\n background: var(--dsw-alias-bg-layer-1, transparent);\n color: var(--dsw-alias-label-primary, inherit);\n}\n\n.ddxi2lc_chipRemove {\n border: none;\n background: transparent;\n color: var(--dsw-alias-state-error-primary, #d9534f);\n cursor: pointer;\n font-size: 12px;\n padding: 0 2px;\n line-height: 1;\n}\n\n.ddxi2lc_addRow {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n.ddxi2lc_addButton {\n font-size: 13px;\n padding: 6px 12px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.4));\n background: var(--dsw-alias-bg-layer-1, transparent);\n color: var(--dsw-alias-label-primary, inherit);\n cursor: pointer;\n}\n\n.ddxi2lc_addButton:disabled {\n cursor: not-allowed;\n color: var(--dsw-alias-state-idle-primary, rgba(128, 128, 128, 0.7));\n}\n\n/* ---- 边界说明 ---- */\n\n.ddxi2lc_boundary {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 12px 14px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.3));\n background: var(--dsw-alias-bg-layer-2, transparent);\n font-size: 12px;\n line-height: 1.7;\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n.ddxi2lc_boundaryTitle {\n font-size: 13px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary, inherit);\n}\n\n.ddxi2lc_batchNote {\n font-size: 12px;\n line-height: 1.6;\n padding: 10px 12px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-state-warn-primary, rgba(200, 150, 0, 0.5));\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n/* ------------------------------------------------------------------ *\n * 角色预设 / 阵容预设(后补的类,颜色同样只用主题令牌)\n * ------------------------------------------------------------------ */\n\n/* 比 .ddxi2lc_input / .ddxi2lc_select 的 260px 宽一些:角色说明与模型名更长。 */\n.ddxi2lc_inputWide {\n font-size: 13px;\n padding: 6px 10px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.4));\n background: var(--dsw-alias-bg-layer-1, transparent);\n color: var(--dsw-alias-label-primary, inherit);\n width: 100%;\n max-width: 420px;\n}\n\n.ddxi2lc_textarea {\n font-size: 13px;\n font-family: inherit;\n line-height: 1.5;\n padding: 6px 10px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.4));\n background: var(--dsw-alias-bg-layer-1, transparent);\n color: var(--dsw-alias-label-primary, inherit);\n width: 100%;\n max-width: 420px;\n resize: vertical;\n}\n\n.ddxi2lc_inputWide:focus,\n.ddxi2lc_textarea:focus {\n outline: 2px solid var(--dsw-alias-brand-primary, #4a6cf7);\n outline-offset: 1px;\n}\n\n/* ---- 列表 ---- */\n\n.ddxi2lc_list {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.ddxi2lc_listRow {\n display: flex;\n flex-direction: column;\n gap: 4px;\n padding: 8px 10px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.3));\n background: var(--dsw-alias-bg-layer-2, transparent);\n}\n\n.ddxi2lc_rowHead {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n.ddxi2lc_rowName {\n font-size: 13px;\n font-weight: 600;\n}\n\n.ddxi2lc_rowMeta {\n font-size: 12px;\n line-height: 1.6;\n color: var(--dsw-alias-label-secondary, inherit);\n word-break: break-word;\n}\n\n/* 把行内按钮推到右侧;列表换行时它自然塌成 0 宽。 */\n.ddxi2lc_spacer {\n flex: 1 1 auto;\n}\n\n.ddxi2lc_rowButton {\n font-size: 12px;\n padding: 3px 10px;\n border-radius: 6px;\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.4));\n background: var(--dsw-alias-bg-layer-1, transparent);\n color: var(--dsw-alias-label-primary, inherit);\n cursor: pointer;\n}\n\n.ddxi2lc_rowDanger {\n font-size: 12px;\n padding: 3px 10px;\n border-radius: 6px;\n border: 1px solid var(--dsw-alias-state-error-primary, #d9534f);\n background: transparent;\n color: var(--dsw-alias-state-error-primary, #d9534f);\n cursor: pointer;\n}\n\n.ddxi2lc_rowButton:disabled,\n.ddxi2lc_rowDanger:disabled {\n cursor: not-allowed;\n color: var(--dsw-alias-state-idle-primary, rgba(128, 128, 128, 0.7));\n border-color: var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.3));\n}\n\n/* ---- 内联编辑器 ---- */\n\n.ddxi2lc_editor {\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 12px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.4));\n background: var(--dsw-alias-bg-overlay, var(--dsw-alias-bg-layer-1, transparent));\n}\n\n.ddxi2lc_gridTwo {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n gap: 10px;\n}\n\n/* ---- 阵容成员行 ---- */\n\n.ddxi2lc_member {\n display: flex;\n align-items: flex-end;\n gap: 8px;\n flex-wrap: wrap;\n padding: 8px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.3));\n background: var(--dsw-alias-bg-layer-2, transparent);\n}\n\n.ddxi2lc_memberFields {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));\n gap: 8px;\n flex: 1 1 320px;\n}\n\n/* 上限提示:与 .ddxi2lc_notice 同字号,但用 warn 令牌(浅深色都可读)。 */\n.ddxi2lc_warn {\n color: var(--dsw-alias-state-warn-primary, rgba(200, 150, 0, 0.9));\n}\n\n";
|
|
1852
|
+
const css = "/*\n * Crossfire settings page styles.\n *\n * 硬约束:**每一个颜色都必须来自主题令牌**(运行时 `Theme.ddxi2lc_listTokens` 的\n * `--dsw-alias-*` 一族),因此浅色与深色主题都无需自己写 override。\n *\n * 这里刻意不照抄圆桌会议的 `RoundTableSettings.ddxi2lc_module.ddxi2lc_css`:那份用的是硬编码\n * `#ffffff` / `rgba(0, 0, 0, 0.8)`,在深色主题下会呈现出白底黑字的割裂块。\n * 令牌已经由宿主按主题解析过,直接用才是正确的做法。\n */\n\n/* ---- 本地设计令牌(这一批改动的全部依据) ----\n *\n * 字号四档、圆角三档、间距 4px 栅格。圆桌设置页有同值的一份:\n * CSS Module 的 classMap 按模块生成,跨包无法共享类名,只能共享**数值**。\n * 这两份文件是同一套规格的两个副本 —— 改一处时另一处必须同步,否则两个\n * 插件的设置页会重新分叉。\n */\n.ddxi2lc_root {\n --cf-fs-11: 11px;\n --cf-fs-12: 12px;\n --cf-fs-13: 13px;\n --cf-fs-14: 14px;\n --cf-radius-s: 6px;\n --cf-radius-m: 8px;\n --cf-radius-l: 12px;\n --cf-space-1: 4px;\n --cf-space-2: 8px;\n --cf-space-3: 12px;\n --cf-space-4: 16px;\n --cf-control-h: 32px;\n --cf-border: var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.3));\n --cf-border-strong: var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.4));\n --cf-bg-soft: var(--dsw-alias-bg-layer-2, transparent);\n --cf-bg-card: var(--dsw-alias-bg-layer-1, transparent);\n --cf-fg: var(--dsw-alias-label-primary, inherit);\n --cf-fg-2: var(--dsw-alias-label-secondary, inherit);\n --cf-brand: var(--dsw-alias-brand-primary, #4a6cf7);\n --cf-on-brand: var(--dsw-alias-bg-base, #ffffff);\n\n display: flex;\n flex-direction: column;\n gap: var(--cf-space-4);\n max-width: 720px;\n padding: 8px 0;\n color: var(--cf-fg);\n font-size: var(--cf-fs-13);\n line-height: 1.6;\n}\n\n.ddxi2lc_notice {\n font-size: 12px;\n line-height: 1.6;\n}\n\n.ddxi2lc_noticeOk {\n color: var(--dsw-alias-state-success-primary, #2e7d32);\n}\n\n.ddxi2lc_noticeErr {\n color: var(--dsw-alias-state-error-primary, #d9534f);\n}\n\n/* 每个区块是一张卡片:边框 + 圆角 + 浅底把\"一组设置\"圈起来。\n * 之前所有区块靠 20px 空隙分隔,长页面滚到一半就分不清哪些控件属于同一组。 */\n.ddxi2lc_section {\n display: flex;\n flex-direction: column;\n gap: var(--cf-space-3);\n padding: var(--cf-space-4);\n border: 1px solid var(--cf-border);\n border-radius: var(--cf-radius-l);\n background: var(--cf-bg-card);\n}\n\n/* 第一个区块是整页标题 + 导语,不做卡片:给它加边框会变成\"一个框里的标题\"。 */\n.ddxi2lc_root > .ddxi2lc_section:first-child {\n padding: 0 2px var(--cf-space-1);\n border: none;\n background: transparent;\n gap: var(--cf-space-1);\n}\n\n.ddxi2lc_sectionTitle {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: var(--cf-fs-14);\n font-weight: 650;\n letter-spacing: 0.01em;\n}\n\n/* 图标降一档饱和度:它是\"目录\"记号,不和文字抢。 */\n.ddxi2lc_sectionTitle svg {\n flex: none;\n color: var(--cf-fg-2);\n}\n\n/* 区块抬头行:标题在左,\"新建\"这类**区块主操作**在右。\n *\n * 之前\"+ 新建预设\"与\"编辑/删除\"这类次要操作同级、且淹没在列表下方,用户实测\n * 反馈\"不够显眼\"。提为区块主操作后,它在本区里是唯一的实心按钮。 */\n.ddxi2lc_sectionHead {\n display: flex;\n align-items: center;\n gap: 10px;\n flex-wrap: wrap;\n}\n\n.ddxi2lc_sectionHead .ddxi2lc_sectionTitle {\n flex: 0 1 auto;\n}\n\n/* 把主操作推到右侧;窄屏换行时它自然塌成整行。 */\n.ddxi2lc_sectionHeadGap {\n flex: 1 1 auto;\n}\n\n/* ---- 区块主操作(品牌色实心) ---- */\n\n.ddxi2lc_primaryButton {\n box-sizing: border-box;\n min-height: var(--cf-control-h);\n font-size: var(--cf-fs-13);\n font-weight: 600;\n padding: 6px 14px;\n border-radius: var(--cf-radius-m);\n border: 1px solid var(--cf-brand);\n background: var(--cf-brand);\n color: var(--cf-on-brand);\n cursor: pointer;\n}\n\n.ddxi2lc_primaryButton:hover:not(:disabled) {\n filter: brightness(1.06);\n}\n\n.ddxi2lc_primaryButton:disabled {\n cursor: not-allowed;\n border-color: var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.3));\n background: var(--dsw-alias-bg-layer-2, transparent);\n color: var(--dsw-alias-state-idle-primary, rgba(128, 128, 128, 0.7));\n}\n\n/* ---- 品牌块(provider 缩写 + 品牌色) ----\n *\n * 与 ReviewCard.ddxi2lc_module.ddxi2lc_css 的同名规则刻意各写一份:CSS Modules 的 classMap 是\n * 按模块生成的,跨模块复用类名只能靠把 classMap 传来传去,不值得。两处的大小\n * 差异也是真实的(这里是列表行,那里是密集的专家行)。\n */\n.ddxi2lc_brandBlock {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: none;\n width: 22px;\n height: 22px;\n border-radius: 6px;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: -0.02em;\n color: #ffffff;\n line-height: 1;\n user-select: none;\n}\n\n/* 行内的品牌块 + 路由文本:块给身份,文本给可读的全名(块的 title 在触屏上读不到)。 */\n.ddxi2lc_routeRow {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n.ddxi2lc_hint {\n font-size: 12px;\n line-height: 1.7;\n color: var(--dsw-alias-label-secondary, rgba(128, 128, 128, 0.9));\n}\n\n.ddxi2lc_field {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.ddxi2lc_label {\n font-size: 13px;\n font-weight: 600;\n}\n\n.ddxi2lc_input,\n.ddxi2lc_select {\n box-sizing: border-box;\n min-height: var(--cf-control-h);\n width: 100%;\n max-width: 320px;\n font-size: var(--cf-fs-13);\n padding: 6px 10px;\n border-radius: var(--cf-radius-m);\n border: 1px solid var(--cf-border-strong);\n background: var(--cf-bg-soft);\n color: var(--cf-fg);\n}\n\n.ddxi2lc_input:focus,\n.ddxi2lc_select:focus {\n outline: 2px solid var(--dsw-alias-brand-primary, #4a6cf7);\n outline-offset: 1px;\n}\n\n/* ---- 强度:三选一的分段控件 ---- */\n\n.ddxi2lc_segmented {\n display: inline-flex;\n gap: 2px;\n padding: 2px;\n border: 1px solid var(--cf-border);\n border-radius: var(--cf-radius-l);\n background: var(--cf-bg-soft);\n width: fit-content;\n}\n\n.ddxi2lc_segment {\n box-sizing: border-box;\n min-height: 28px;\n font-size: var(--cf-fs-13);\n padding: 5px 14px;\n border: none;\n border-radius: var(--cf-radius-m);\n background: transparent;\n color: var(--cf-fg);\n cursor: pointer;\n}\n\n/* 组内不再画分隔线:间隙 + 圆角已经能把两段分开,再加竖线会显得像表格。 */\n.ddxi2lc_segment + .ddxi2lc_segment {\n border-left: none;\n}\n\n.ddxi2lc_segmentActive {\n background: var(--cf-brand);\n color: var(--cf-on-brand);\n font-weight: 600;\n}\n\n.ddxi2lc_segment:disabled {\n cursor: progress;\n color: var(--dsw-alias-state-idle-primary, rgba(128, 128, 128, 0.7));\n}\n\n/* ---- 维度池 ---- */\n\n.ddxi2lc_chips {\n display: flex;\n flex-wrap: wrap;\n gap: var(--cf-space-2);\n}\n\n.ddxi2lc_chip {\n box-sizing: border-box;\n display: inline-flex;\n align-items: center;\n min-height: 24px;\n font-size: var(--cf-fs-12);\n padding: 2px 10px;\n border-radius: 999px;\n border: 1px solid var(--cf-border);\n background: var(--cf-bg-soft);\n color: var(--cf-fg-2);\n}\n\n/* 带品牌块的成员 chip:左边品牌色块,右边成员 key。 */\n.ddxi2lc_chipWithBrand {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n padding: 3px 10px 3px 4px;\n border-radius: 999px;\n border: 1px solid var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.3));\n background: var(--dsw-alias-bg-layer-2, transparent);\n color: var(--dsw-alias-label-secondary, inherit);\n}\n\n/* 成员 chip 里的品牌块比列表行的再小一点:chip 本身只有 24px 高。 */\n.ddxi2lc_brandBlockSmall {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: none;\n width: 18px;\n height: 18px;\n border-radius: 5px;\n font-size: 9px;\n font-weight: 700;\n letter-spacing: -0.02em;\n color: #ffffff;\n line-height: 1;\n user-select: none;\n}\n\n.ddxi2lc_chipCustom {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n padding: 3px 6px 3px 10px;\n border-radius: 999px;\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.4));\n background: var(--dsw-alias-bg-layer-1, transparent);\n color: var(--dsw-alias-label-primary, inherit);\n}\n\n.ddxi2lc_chipRemove {\n border: none;\n background: transparent;\n color: var(--dsw-alias-state-error-primary, #d9534f);\n cursor: pointer;\n font-size: 12px;\n padding: 0 2px;\n line-height: 1;\n}\n\n.ddxi2lc_addRow {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n.ddxi2lc_addButton {\n box-sizing: border-box;\n min-height: var(--cf-control-h);\n font-size: var(--cf-fs-13);\n padding: 6px 12px;\n border-radius: var(--cf-radius-m);\n border: 1px solid var(--cf-border-strong);\n background: transparent;\n color: var(--cf-fg);\n cursor: pointer;\n}\n\n.ddxi2lc_addButton:hover:not(:disabled) {\n background: var(--cf-bg-soft);\n}\n\n.ddxi2lc_addButton:disabled {\n cursor: not-allowed;\n color: var(--dsw-alias-state-idle-primary, rgba(128, 128, 128, 0.7));\n}\n\n/* ---- 边界说明 ---- */\n\n.ddxi2lc_boundary {\n display: flex;\n flex-direction: column;\n gap: var(--cf-space-2);\n padding: 14px 16px;\n border-radius: var(--cf-radius-l);\n border: 1px solid var(--cf-border);\n background: var(--cf-bg-soft);\n font-size: var(--cf-fs-12);\n line-height: 1.7;\n color: var(--cf-fg-2);\n}\n\n.ddxi2lc_boundaryTitle {\n font-size: var(--cf-fs-13);\n font-weight: 600;\n color: var(--cf-fg);\n}\n\n.ddxi2lc_batchNote {\n font-size: var(--cf-fs-12);\n line-height: 1.6;\n padding: 10px 12px;\n border-radius: var(--cf-radius-m);\n border: 1px solid var(--dsw-alias-state-warn-primary, rgba(200, 150, 0, 0.5));\n background: color-mix(in srgb, var(--dsw-alias-state-warn-primary, #c89600) 8%, transparent);\n color: var(--cf-fg-2);\n}\n\n/* ------------------------------------------------------------------ *\n * 角色预设 / 阵容预设(后补的类,颜色同样只用主题令牌)\n * ------------------------------------------------------------------ */\n\n/* 比 .ddxi2lc_input / .ddxi2lc_select 的 320px 宽一些:角色说明与模型名更长。 */\n.ddxi2lc_inputWide {\n box-sizing: border-box;\n min-height: var(--cf-control-h);\n font-size: var(--cf-fs-13);\n padding: 6px 10px;\n border-radius: var(--cf-radius-m);\n border: 1px solid var(--cf-border-strong);\n background: var(--cf-bg-soft);\n color: var(--cf-fg);\n width: 100%;\n max-width: 480px;\n}\n\n.ddxi2lc_textarea {\n box-sizing: border-box;\n font-size: var(--cf-fs-13);\n font-family: inherit;\n line-height: 1.5;\n padding: 8px 10px;\n border-radius: var(--cf-radius-m);\n border: 1px solid var(--cf-border-strong);\n background: var(--cf-bg-soft);\n color: var(--cf-fg);\n width: 100%;\n max-width: 480px;\n resize: vertical;\n}\n\n.ddxi2lc_inputWide:focus,\n.ddxi2lc_textarea:focus {\n outline: 2px solid var(--dsw-alias-brand-primary, #4a6cf7);\n outline-offset: 1px;\n}\n\n/* ---- 列表 ---- */\n\n.ddxi2lc_list {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.ddxi2lc_listRow {\n display: flex;\n flex-direction: column;\n gap: var(--cf-space-1);\n padding: 10px 12px;\n border-radius: var(--cf-radius-m);\n border: 1px solid var(--cf-border);\n background: var(--cf-bg-soft);\n}\n\n.ddxi2lc_rowHead {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n.ddxi2lc_rowName {\n font-size: 13px;\n font-weight: 600;\n}\n\n.ddxi2lc_rowMeta {\n font-size: 12px;\n line-height: 1.6;\n color: var(--dsw-alias-label-secondary, inherit);\n word-break: break-word;\n}\n\n/* 把行内按钮推到右侧;列表换行时它自然塌成 0 宽。 */\n.ddxi2lc_spacer {\n flex: 1 1 auto;\n}\n\n.ddxi2lc_rowButton {\n box-sizing: border-box;\n min-height: 28px;\n font-size: var(--cf-fs-12);\n padding: 3px 10px;\n border-radius: var(--cf-radius-s);\n border: 1px solid var(--cf-border-strong);\n background: transparent;\n color: var(--cf-fg);\n cursor: pointer;\n}\n\n.ddxi2lc_rowButton:hover:not(:disabled) {\n background: var(--cf-bg-soft);\n}\n\n.ddxi2lc_rowDanger {\n box-sizing: border-box;\n min-height: 28px;\n font-size: var(--cf-fs-12);\n padding: 3px 10px;\n border-radius: var(--cf-radius-s);\n border: 1px solid var(--dsw-alias-state-error-primary, #d9534f);\n background: transparent;\n color: var(--dsw-alias-state-error-primary, #d9534f);\n cursor: pointer;\n}\n\n.ddxi2lc_rowDanger:hover:not(:disabled) {\n background: color-mix(in srgb, var(--dsw-alias-state-error-primary, #d9534f) 7%, transparent);\n}\n\n.ddxi2lc_rowButton:disabled,\n.ddxi2lc_rowDanger:disabled {\n cursor: not-allowed;\n color: var(--dsw-alias-state-idle-primary, rgba(128, 128, 128, 0.7));\n border-color: var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.3));\n}\n\n/* ---- 内联编辑器 ---- */\n\n.ddxi2lc_editor {\n display: flex;\n flex-direction: column;\n gap: var(--cf-space-3);\n padding: 14px;\n border-radius: var(--cf-radius-l);\n border: 1px solid var(--cf-border-strong);\n background: var(--cf-bg-soft);\n}\n\n.ddxi2lc_gridTwo {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n gap: 10px;\n}\n\n/* ---- 阵容成员行 ---- */\n\n.ddxi2lc_member {\n display: flex;\n align-items: flex-end;\n gap: var(--cf-space-2);\n flex-wrap: wrap;\n padding: 10px;\n border-radius: var(--cf-radius-l);\n border: 1px solid var(--cf-border);\n background: var(--cf-bg-soft);\n}\n\n.ddxi2lc_memberFields {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));\n gap: 8px;\n flex: 1 1 320px;\n}\n\n/* 上限提示:与 .ddxi2lc_notice 同字号,但用 warn 令牌(浅深色都可读)。 */\n.ddxi2lc_warn {\n color: var(--dsw-alias-state-warn-primary, rgba(200, 150, 0, 0.9));\n}\n\n";
|
|
1714
1853
|
const classMap = {
|
|
1715
1854
|
"listTokens": "ddxi2lc_listTokens",
|
|
1716
1855
|
"module": "ddxi2lc_module",
|
|
@@ -2232,9 +2371,9 @@ function CrossfireSettings(props) {
|
|
|
2232
2371
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2233
2372
|
className: SettingsView_module_css_default.section,
|
|
2234
2373
|
children: [
|
|
2235
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
2374
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2236
2375
|
className: SettingsView_module_css_default.sectionTitle,
|
|
2237
|
-
children: t("dimensionsTitle")
|
|
2376
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(InlineIcon, { name: "layers" }), t("dimensionsTitle")]
|
|
2238
2377
|
}),
|
|
2239
2378
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2240
2379
|
className: SettingsView_module_css_default.hint,
|
|
@@ -2309,9 +2448,9 @@ function CrossfireSettings(props) {
|
|
|
2309
2448
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2310
2449
|
className: SettingsView_module_css_default.section,
|
|
2311
2450
|
children: [
|
|
2312
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
2451
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2313
2452
|
className: SettingsView_module_css_default.sectionTitle,
|
|
2314
|
-
children: t("budgetTitle")
|
|
2453
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(InlineIcon, { name: "gauge" }), t("budgetTitle")]
|
|
2315
2454
|
}),
|
|
2316
2455
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2317
2456
|
className: SettingsView_module_css_default.hint,
|
|
@@ -2346,9 +2485,9 @@ function CrossfireSettings(props) {
|
|
|
2346
2485
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2347
2486
|
className: SettingsView_module_css_default.sectionHead,
|
|
2348
2487
|
children: [
|
|
2349
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
2488
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2350
2489
|
className: SettingsView_module_css_default.sectionTitle,
|
|
2351
|
-
children: t("settingsPresetsTitle")
|
|
2490
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(InlineIcon, { name: "role" }), t("settingsPresetsTitle")]
|
|
2352
2491
|
}),
|
|
2353
2492
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: SettingsView_module_css_default.sectionHeadGap }),
|
|
2354
2493
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
@@ -2563,9 +2702,9 @@ function CrossfireSettings(props) {
|
|
|
2563
2702
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2564
2703
|
className: SettingsView_module_css_default.sectionHead,
|
|
2565
2704
|
children: [
|
|
2566
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
2705
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2567
2706
|
className: SettingsView_module_css_default.sectionTitle,
|
|
2568
|
-
children: t("settingsSquadsTitle")
|
|
2707
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(InlineIcon, { name: "squad" }), t("settingsSquadsTitle")]
|
|
2569
2708
|
}),
|
|
2570
2709
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: SettingsView_module_css_default.sectionHeadGap }),
|
|
2571
2710
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
@@ -2981,6 +3120,7 @@ const zh = {
|
|
|
2981
3120
|
countEndorsed: "认定",
|
|
2982
3121
|
countRejected: "驳回",
|
|
2983
3122
|
countPending: "待定",
|
|
3123
|
+
countPendingOfTotal: "待定 {pending} / 共 {total} 条",
|
|
2984
3124
|
defectEmpty: "还没有可裁决的缺陷。",
|
|
2985
3125
|
defectsTitle: "待裁决缺陷",
|
|
2986
3126
|
evidenceRepro: "可复现步骤",
|
|
@@ -3158,6 +3298,7 @@ const en = {
|
|
|
3158
3298
|
countEndorsed: "Endorsed",
|
|
3159
3299
|
countRejected: "Rejected",
|
|
3160
3300
|
countPending: "Pending",
|
|
3301
|
+
countPendingOfTotal: "Pending {pending} / {total} total",
|
|
3161
3302
|
defectEmpty: "No defects to adjudicate yet.",
|
|
3162
3303
|
defectsTitle: "Defects to adjudicate",
|
|
3163
3304
|
evidenceRepro: "Repro steps",
|
package/lib/index.js
CHANGED
|
@@ -2226,7 +2226,7 @@ async function spawnExpert(ctx, config, record, expert, parent, signal, limits =
|
|
|
2226
2226
|
* @module dsh-plugin-crossfire/version
|
|
2227
2227
|
*/
|
|
2228
2228
|
/** 当前插件版本(必须与 package.json 的 version 逐字一致)。 */
|
|
2229
|
-
const PLUGIN_VERSION = "0.1.
|
|
2229
|
+
const PLUGIN_VERSION = "0.1.3";
|
|
2230
2230
|
/** 导出物头部使用的插件标识行。 */
|
|
2231
2231
|
const PLUGIN_ID = `@y9931666/dsh-plugin-crossfire v${PLUGIN_VERSION}`;
|
|
2232
2232
|
/** DeepSeek Harness 版本区间(导出头部展示用)。
|
package/package.json
CHANGED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# 针锋 Crossfire v0.1.2
|
|
2
|
+
|
|
3
|
+
**观点标题压缩版。** 只动展示逻辑,不碰红队协议、不碰提示词。
|
|
4
|
+
|
|
5
|
+
## 修了什么
|
|
6
|
+
|
|
7
|
+
### 观点标题过长且含无用信息(用户实测反馈)
|
|
8
|
+
|
|
9
|
+
**症状**:折叠态的标题一行显示不下,后面被截断;而且标题里塞了大段路径。
|
|
10
|
+
|
|
11
|
+
用户原话:"标题过长,导致后面无法显示;其次这个标题有很多无用的信息,就比如说路径的位置。"
|
|
12
|
+
|
|
13
|
+
**根因不在提示词、也不在红队。** 红队的报告信封格式是 `观点 N(指向 X,维度Y)`,其中 `X`
|
|
14
|
+
是**完整路径**(如 `dsh-plugin-crossfire/src/client/ReviewCard.module.css:106-176`)——
|
|
15
|
+
红队写完整路径是**对的**,审计时要能定位到文件。问题出在展示层:
|
|
16
|
+
`defectTitle()` 把整个前缀**原样当标题**返回,40 多字符的路径直接把 80 字上限吃满。
|
|
17
|
+
|
|
18
|
+
**修法**:标题压缩成 `观点 N · 文件名:行号 · 维度`。
|
|
19
|
+
|
|
20
|
+
| | 改前 | 改后 |
|
|
21
|
+
| --- | --- | --- |
|
|
22
|
+
| 标题 | `观点 2(指向 dsh-plugin-crossfire/src/client/ReviewCard.module.css:106-176,维度 交…` | `观点 2 · ReviewCard.module.css:106-176 · 交互` |
|
|
23
|
+
|
|
24
|
+
三条刻意的设计:
|
|
25
|
+
|
|
26
|
+
1. **完整路径仍然留在缺陷正文里**(展开即可看到)—— 可追溯性一点没丢,只是不再占标题。
|
|
27
|
+
2. **行号保留**(`ReviewCard.module.css:106-176`),因为它是"跳到哪一行"的关键信息。
|
|
28
|
+
3. **形状不认识时原样退回**(例如旧格式 `观点 2 (指向 y)`、或括号里缺 `维度`)——
|
|
29
|
+
不硬拼一个残缺标题。
|
|
30
|
+
|
|
31
|
+
Windows 反斜杠路径(`src\client\wire.ts:205-215`)同样压成文件名。
|
|
32
|
+
|
|
33
|
+
## 新增断言
|
|
34
|
+
|
|
35
|
+
`test/review-card.test.mjs` 增补 5 条,覆盖:完整路径压缩、反斜杠路径、裸文件名、
|
|
36
|
+
"形状不认识时退回原前缀"、以及"压缩结果必须显著短于原始长信封且仍受 80 字上限约束"。
|
|
37
|
+
|
|
38
|
+
## 安装
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
dsh plugin --profile web add @y9931666/dsh-plugin-crossfire
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
装后**重启 DeepSeek Harness 进程**并刷新浏览器。要求 DeepSeek Harness `0.2.0-rc.2+`、Node `>= 22`。
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# v0.1.3 — 界面视觉优化
|
|
2
|
+
|
|
3
|
+
本版只改界面,不改任何工具语义、状态机与数据格式。
|
|
4
|
+
|
|
5
|
+
## 评审卡片
|
|
6
|
+
|
|
7
|
+
- **状态色条**:每条缺陷左侧 3px 竖条按表态着色(认定=绿 / 驳回=红 / 待定=灰)。一条评审常有十几条缺陷,此前只能靠底部按钮描边逐条辨认状态,现在分布一眼可见。
|
|
8
|
+
- **间距与规格**:缺陷卡间距 4px → 8px,内边距 10→12,圆角 8→10,正文行高 1.65。
|
|
9
|
+
- **三态按钮**:统一 28px 高,不再随内边距漂移。
|
|
10
|
+
- **折叠标题**:信封压缩失败时退成「观点 N · 核心句」,不再把完整定位串(`F:\…\ReviewCard.tsx:154-…`)塞进标题;完整路径仍保留在展开后的正文里。
|
|
11
|
+
- **状态区文案**:「待定 9 / 表态 9」→「待定 9 / 共 9 条」,与左侧「待裁决」药丸不再重复。
|
|
12
|
+
|
|
13
|
+
## 设置页
|
|
14
|
+
|
|
15
|
+
- 区块改为**卡片分组**(边框 + 12px 圆角 + 浅底 + 16px 内边距),首个标题区块不套卡片。
|
|
16
|
+
- 控件统一:输入 32px 高 / 8px 圆角 / 320 宽;分段按钮从连体改为有间隙的胶囊组。
|
|
17
|
+
- 新增长度为 16×16 的内联 SVG 图标(`src/client/icons.tsx`):零依赖、`currentColor` 随主题变色。
|
|
18
|
+
|
|
19
|
+
## 测试
|
|
20
|
+
|
|
21
|
+
- `test/review-card.test.mjs` 更新标题兜底断言;全量 226 项通过。
|
package/lib/types/budget.d.ts
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Crossfire 预算:一条**熔断线**,不是一台状态机。
|
|
3
|
-
*
|
|
4
|
-
* ## 为什么刻意没有 active / muted / ended
|
|
5
|
-
*
|
|
6
|
-
* 圆桌的预算有三态与"闭麦":一次会议是持续存在的对象,预算耗尽只是把它**调成
|
|
7
|
-
* 静音**,之后还能靠 `roundtable_set_budget` 补预算复活。针锋没有多轮辩论,
|
|
8
|
-
* 预算的角色因此完全不同 —— 它唯一的作用是**在超限的那一刻把后续工作停下来并
|
|
9
|
-
* 报错**。停下不改变任何评审状态:`status` 由 `review.ts` 独占管理,本模块
|
|
10
|
-
* **一行都不写 `record.status`**(这是设计边界,不是疏忽)。
|
|
11
|
-
*
|
|
12
|
-
* ## 两条判据(与设置页「0 = 不限制」严格一致)
|
|
13
|
-
*
|
|
14
|
-
* - `rounds`:`usedRounds > maxRounds`。设 N 轮就能开满 N 轮,只有想开第 N+1 轮
|
|
15
|
-
* 才触顶 —— `>=` 会让 `maxRounds = 10` 实际只能开 9 轮,对面向用户的设置项
|
|
16
|
-
* 不直觉(圆桌已经踩过这个坑,注释里写明了原因,这里沿用同一口径)。
|
|
17
|
-
* - `tokens`:`usedTokens >= maxTokens`,且仅当 `maxTokens > 0` 时才判。
|
|
18
|
-
* - 两条轴的 `max*` 为 `0` 时该轴**不限制**(设置页对这四个数字字段都写着
|
|
19
|
-
* 「0 = 不限制」,实现必须与文案一致,否则用户关掉限制反而被立刻熔断)。
|
|
20
|
-
*
|
|
21
|
-
* ## 调用方契约
|
|
22
|
-
*
|
|
23
|
-
* `chargeTokens` / `beginPass` 都是**原地修改** `record` 的纯计算,不落盘 ——
|
|
24
|
-
* 落盘由调用方负责(状态写入统一走 `state.ts` 的原子写 + 进程内锁)。
|
|
25
|
-
*
|
|
26
|
-
* @module dsh-plugin-crossfire/budget
|
|
27
|
-
*/
|
|
28
|
-
import type { CrossfireRecord } from './types.ts';
|
|
29
|
-
/**
|
|
30
|
-
* 估算文本的 token 成本(粗估:CJK 0.6/字,其它非空白 0.3/字符,向上取整)。
|
|
31
|
-
*
|
|
32
|
-
* 口径与圆桌 `budget.ts` 的 `estimateTokens` **完全一致**(逐字符 `for...of`
|
|
33
|
-
* 按码点遍历,`code >= 0x2e80` 记 CJK,空白不计入 other,最后 `Math.ceil`)。
|
|
34
|
-
* 这是刻意照搬而不是重新设计:两个插件对同一段中文给出的预算数字必须可比。
|
|
35
|
-
*
|
|
36
|
-
* 再次强调:这是一个**已说出的文本长度估算**,不是模型真实用量。
|
|
37
|
-
*/
|
|
38
|
-
export declare function estimateTokens(text: string): number;
|
|
39
|
-
/**
|
|
40
|
-
* 哪条预算轴被突破(无则 `undefined`)。
|
|
41
|
-
*
|
|
42
|
-
* 语义(与圆桌一致,见文件头):两条轴的 `max*` 为 `0` 表示该轴不限制。
|
|
43
|
-
* 返回的是**轴名**,不是布尔 —— 调用方需要把它透出给用户,让"是哪条线断了"
|
|
44
|
-
* 可诊断,而不是笼统的一句"预算用完了"。
|
|
45
|
-
*/
|
|
46
|
-
export declare function budgetExceeded(record: CrossfireRecord): 'rounds' | 'tokens' | undefined;
|
|
47
|
-
/**
|
|
48
|
-
* 预算熔断错误。
|
|
49
|
-
*
|
|
50
|
-
* 不是"会议已结束",也不是"会议被静音":针锋只有这一种停止原因,且**可恢复**
|
|
51
|
-
* —— 用户把设置里的上限调大即可继续(状态从未被改动,所以不存在"复活"一说)。
|
|
52
|
-
*/
|
|
53
|
-
export declare class CrossfireMutedError extends Error {
|
|
54
|
-
readonly axis: 'rounds' | 'tokens';
|
|
55
|
-
constructor(reviewId: string, axis: 'rounds' | 'tokens');
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* 熔断门禁:超限即抛 {@link CrossfireMutedError},否则什么都不做。
|
|
59
|
-
*
|
|
60
|
-
* **不修改 `record`**(与圆桌 `markMuted` 的本质区别):针锋不把"超预算"写成
|
|
61
|
-
* 一个持久状态,因为那个状态会反过来挡住用户自救的路径 —— 圆桌的真实事故就是
|
|
62
|
-
* `ensureActive` 先改状态再抛错,导致连补预算/导出都被同一个门禁拒绝,花完预算
|
|
63
|
-
* 的会议永远卡在 `active` 却什么都做不了。这里只抛、不改,问题无从被固化。
|
|
64
|
-
*
|
|
65
|
-
* @param record - 被检查的评审记录。
|
|
66
|
-
* @param options.allowMuted - 收尾路径(导出、表态、定稿)必须保持可达,
|
|
67
|
-
* 否则用户连"把已经打出来的缺陷看完并裁决"都做不了;置 `true` 时直接放行。
|
|
68
|
-
*/
|
|
69
|
-
export declare function assertBudget(record: CrossfireRecord, options?: {
|
|
70
|
-
allowMuted?: boolean;
|
|
71
|
-
}): void;
|
|
72
|
-
/**
|
|
73
|
-
* 把一段专家输出计入 token 用量(**原地修改** `record`,由调用方负责落盘)。
|
|
74
|
-
*
|
|
75
|
-
* 只在文本非空时累加,避免空发言把 `usedTokens` 上一堆无意义的 `+0` 调用;
|
|
76
|
-
* 估算口径交给 {@link estimateTokens},本函数不做任何额外加权。
|
|
77
|
-
*/
|
|
78
|
-
export declare function chargeTokens(record: CrossfireRecord, text: string): void;
|
|
79
|
-
/**
|
|
80
|
-
* 推进一轮:`reviewPass += 1` 并同步 `budget.usedRounds`,返回新的轮次。
|
|
81
|
-
*
|
|
82
|
-
* 这里**不**做预算检查:门禁由调用方在动手之前调用 {@link assertBudget},因为
|
|
83
|
-
* "先检查后推进"的顺序一旦反过来,第 N+1 轮的副作用就已经发生了。
|
|
84
|
-
* `usedRounds` 与 `reviewPass` 必须同进同退,两者脱钩会让预算判据失去意义。
|
|
85
|
-
*/
|
|
86
|
-
export declare function beginPass(record: CrossfireRecord): number;
|