dsh-novel-writer 5.2.0 → 5.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/lib/client.js +823 -278
  2. package/package.json +2 -2
package/lib/client.js CHANGED
@@ -24,166 +24,700 @@ window.__ModuleLoader__.load({
24
24
  let react = require("react");
25
25
  let react_dom_client = require("react-dom/client");
26
26
 
27
+ // ---- 官方原语适配层(v5.5.0)----
28
+ // 第三方插件(@linxin666/dsh-client-ui-market 等 6 个)同样直接 require 官方 primitives,
29
+ // 且都未声明 dsh.client.external —— 该包属客户端基线模块;这里仍包 try/catch,取不到即回退。
30
+ /* ===== B:官方原语适配层(v5.5.0 · @deepseek-ai/dsh-client-ui-primitives → 自绘回退) =====
31
+ * 插入位置:client.js 的 factory 内、`let react = require("react");` 之后(任意组件定义之前)。
32
+ * 用法(本块最后一行已给出):
33
+ * * 对外只有 NW_UI:{ available, Button, Switch, SegmentedControl, Modal, RiskConfirmation,
34
+ * Tag, Tooltip, DisclosureRow }
35
+ *
36
+ * 设计约束(宿主启动安全第一):
37
+ * 1. require 官方包只在 try/catch 内;取不到模块 / 缺某个组件 / 官方组件渲染抛错 → 逐控件回退;
38
+ * 本层在任何情况下都不向上抛,也不写 console(用 NW_UI.available 自检即可)。
39
+ * 2. 回退实现复用既有 nw* 类名,外观全部由 CSS 承担:本块没有任何内联 style、没有任何颜色字面量。
40
+ * 3. 不硬编码任何文案:文案由调用方传入,缺失时给空串等安全默认值。
41
+ * 4. 回退分支与官方分支的 props 映射共用同一套归一化函数,保证两条路径语义一致。
42
+ */
43
+ function createNovelWriterUI(react, require) {
44
+ var NOOP = function () {};
45
+ var canCreate = !!react && typeof react.createElement === "function";
46
+ var h = canCreate
47
+ ? function () { return react.createElement.apply(react, arguments); }
48
+ : function () { return null; };
49
+
50
+ /* ---------- 归一化小工具(只做类型兜底,不产生文案) ---------- */
51
+ function text(value) { return typeof value === "string" ? value : ""; }
52
+ function optionalText(value) { return typeof value === "string" && value !== "" ? value : void 0; }
53
+ function callback(value) { return typeof value === "function" ? value : null; }
54
+ function isOn(value) { return value === true; }
55
+ function classNameOf(base, extra) { return optionalText(extra) === void 0 ? base : base + " " + extra; }
56
+ function isElement(node) {
57
+ if (!node || typeof node !== "object") return false;
58
+ if (typeof react.isValidElement === "function") return react.isValidElement(node) === true;
59
+ return node.$$typeof !== void 0 && node.$$typeof !== null;
60
+ }
61
+ // 元素锚点:官方 Tooltip 用 cloneElement 注入 ref/handler,children 必须是单个元素
62
+ function anchorOf(children) {
63
+ return isElement(children) ? children : h("span", null, children);
64
+ }
65
+
66
+ /* ---------- 官方模块加载(唯一的 require,全程 try/catch) ---------- */
67
+ // 包名与第三方插件(@linxin666/dsh-client-ui-preset-center 等)用的是同一个,属客户端基线包。
68
+ // 调用按完整包名字面量书写(不抽成变量):契约与审计工具都按这一行字面匹配"必须包在 try 内"。
69
+ var primitives = null;
70
+ try {
71
+ if (typeof require === "function") primitives = require("@deepseek-ai/dsh-client-ui-primitives");
72
+ } catch (err) {
73
+ primitives = null; // 模块不在客户端基线里 / 解析失败 → 全部走回退
74
+ }
75
+ if (!primitives || (typeof primitives !== "object" && typeof primitives !== "function")) primitives = null;
76
+
77
+ function typeTag(value) {
78
+ if (!value || typeof value !== "object") return "";
79
+ try { return String(value.$$typeof); } catch (err) { return ""; }
80
+ }
81
+ // 可渲染的组件类型:函数组件/类组件,或 React.memo / forwardRef / lazy 包装对象
82
+ function isComponentType(value) {
83
+ if (typeof value === "function") return true;
84
+ var tag = typeTag(value);
85
+ return tag.indexOf("react.memo") >= 0 || tag.indexOf("react.forward_ref") >= 0 || tag.indexOf("react.lazy") >= 0;
86
+ }
87
+ // 取"可直接调用"的内层函数:memo/forwardRef 拆包(保住"官方组件抛错也能被 try/catch 兜住");
88
+ // 类组件不能当函数调用(返回 null,改走 createElement,由 React 渲染)。
89
+ function invocable(value, depth) {
90
+ if (typeof value === "function") {
91
+ if (value.prototype && value.prototype.isReactComponent) return null;
92
+ return value;
93
+ }
94
+ if (!value || typeof value !== "object" || depth > 3) return null;
95
+ var tag = typeTag(value);
96
+ if (tag.indexOf("react.memo") >= 0) return invocable(value.type, depth + 1);
97
+ if (tag.indexOf("react.forward_ref") >= 0) {
98
+ var render = value.render;
99
+ if (typeof render !== "function") return null;
100
+ return function (props) { return render(props, null); };
101
+ }
102
+ return null;
103
+ }
104
+ function pick(name) {
105
+ if (primitives === null) return null;
106
+ try {
107
+ var value = primitives[name];
108
+ if (isComponentType(value)) return value;
109
+ var inner = primitives["default"]; // ESM/CJS 互操作兜底
110
+ if (inner && typeof inner === "object" && isComponentType(inner[name])) return inner[name];
111
+ } catch (err) { /* 访问器抛错 → 视为该组件缺失 */ }
112
+ return null;
113
+ }
114
+
115
+ var NAMES = ["Button", "Switch", "SegmentedControl", "Modal", "RiskConfirmation", "Tag", "Tooltip", "DisclosureRow"];
116
+ var official = {};
117
+ var resolved = 0;
118
+ for (var n = 0; n < NAMES.length; n += 1) {
119
+ official[NAMES[n]] = pick(NAMES[n]);
120
+ if (official[NAMES[n]] !== null) resolved += 1;
121
+ }
122
+ // available:8 个官方原语是否**全部**拿到(模块解析层面;个别组件运行时抛错仍会逐次回退,不会翻转它)
123
+ var available = resolved === NAMES.length;
124
+
125
+ /* ---------- 统一的"官方优先,抛错即回退"包装 ---------- */
126
+ // prepare(props) 在每次渲染恰好调用一次(分段控件/折叠行的 hook 都在这里,保证 hook 数量稳定);
127
+ // mapProps(props, ctx) 给出官方 props;回退实现拿到同一份 ctx。
128
+ function adapt(name, fallback, prepare, mapProps) {
129
+ var component = official[name];
130
+ if (component === null || !canCreate) return fallback;
131
+ var call = null;
132
+ try { call = invocable(component, 0); } catch (err) { call = null; }
133
+ return function Adapted(props) {
134
+ var input = props || {};
135
+ var context = null;
136
+ try { context = prepare === null ? null : prepare(input); } catch (err) { context = null; }
137
+ var mapped = null;
138
+ try { mapped = mapProps(input, context); } catch (err) { mapped = null; }
139
+ if (mapped !== null) {
140
+ try {
141
+ var node = call !== null ? call(mapped) : h(component, mapped);
142
+ if (node !== void 0) return node; // null 是合法节点(例如 open:false 的弹窗),只有 undefined 才当失败
143
+ } catch (err2) { /* 官方组件抛错 → 落到下面的回退实现 */ }
144
+ }
145
+ return fallback(input, context);
146
+ };
147
+ }
148
+
149
+ /* ---------- 回退实现(只复用既有 nw* 类名;样式全在 CSS) ---------- */
150
+
151
+ // --- Button ---
152
+ var BUTTON_VARIANTS = { primary: 1, ghost: 1, outline: 1, toolbar: 1 };
153
+ var BUTTON_CLASS = {
154
+ primary: "nwBtn nwBtnPrimary",
155
+ ghost: "nwBtn nwBtnGhost",
156
+ outline: "nwBtn",
157
+ toolbar: "nwBtn nwBtnGhost"
158
+ };
159
+ function buttonVariant(value) {
160
+ var variant = text(value);
161
+ return BUTTON_VARIANTS[variant] === 1 ? variant : "ghost"; // 与官方默认 variant 对齐
162
+ }
163
+ function mapButton(props) {
164
+ return {
165
+ variant: buttonVariant(props.variant),
166
+ size: props.size === "sm" ? "sm" : "md",
167
+ disabled: isOn(props.disabled),
168
+ title: optionalText(props.title),
169
+ onClick: callback(props.onClick) || void 0,
170
+ className: optionalText(props.className),
171
+ children: props.children
172
+ };
173
+ }
174
+ function fallbackButton(props) {
175
+ return h("button", {
176
+ type: "button",
177
+ className: classNameOf(BUTTON_CLASS[buttonVariant(props.variant)], props.className),
178
+ disabled: isOn(props.disabled),
179
+ title: optionalText(props.title),
180
+ onClick: callback(props.onClick) || void 0
181
+ }, props.children);
182
+ }
183
+
184
+ // --- Switch ---
185
+ function mapSwitch(props) {
186
+ return {
187
+ checked: isOn(props.checked),
188
+ onChange: callback(props.onChange) || NOOP,
189
+ label: text(props.label), // 官方必填:缺失兜底成空串(不硬编码文案)
190
+ disabled: isOn(props.disabled),
191
+ title: optionalText(props.title),
192
+ className: optionalText(props.className)
193
+ };
194
+ }
195
+ function fallbackSwitch(props) {
196
+ var checked = isOn(props.checked);
197
+ return h("button", {
198
+ type: "button",
199
+ role: "switch",
200
+ "aria-checked": checked ? "true" : "false",
201
+ "aria-label": optionalText(props.label),
202
+ disabled: isOn(props.disabled),
203
+ title: optionalText(props.title),
204
+ className: classNameOf("nwSwitch" + (checked ? " nwSwitchOn" : ""), props.className),
205
+ onClick: function () {
206
+ var onChange = callback(props.onChange);
207
+ if (onChange !== null) onChange(!checked);
208
+ }
209
+ }, h("span", { className: "nwSwitchKnob" }));
210
+ }
211
+
212
+ // --- SegmentedControl(官方必填 id,适配层自己生成稳定 id) ---
213
+ var useIdHook = typeof react.useId === "function" ? react.useId : null;
214
+ var useRefHook = typeof react.useRef === "function" ? react.useRef : null;
215
+ var controlSeq = 0;
216
+ function normalizeOptions(options) {
217
+ var list = [];
218
+ var source = Array.isArray(options) ? options : [];
219
+ for (var i = 0; i < source.length; i += 1) {
220
+ var option = source[i];
221
+ if (!option || typeof option !== "object") continue;
222
+ var value = text(option.value);
223
+ if (value === "") continue;
224
+ list.push({
225
+ value: value,
226
+ label: text(option.label) || value,
227
+ disabled: isOn(option.disabled),
228
+ title: optionalText(option.title)
229
+ });
230
+ }
231
+ return list;
232
+ }
233
+ function prepareSegments(props) {
234
+ // 无论调用方是否自带 id,都先按固定分支取一次自动 id:hook 数量每帧恒定
235
+ var auto;
236
+ if (useIdHook !== null) auto = "nw-seg-" + String(useIdHook()).replace(/[^A-Za-z0-9_-]/g, "");
237
+ else if (useRefHook !== null) {
238
+ var box = useRefHook(null);
239
+ if (box === null || typeof box !== "object") box = { current: null };
240
+ if (box.current === null) {
241
+ controlSeq += 1;
242
+ box.current = "nw-seg-" + controlSeq;
243
+ }
244
+ auto = box.current;
245
+ } else {
246
+ auto = "nw-seg-fallback";
247
+ }
248
+ return { id: optionalText(props.id) || auto, options: normalizeOptions(props.options) }; // 自带 id(可选)优先
249
+ }
250
+ function mapSegments(props, context) {
251
+ return {
252
+ id: context !== null && context !== void 0 ? context.id : text(props.id),
253
+ value: text(props.value),
254
+ options: context !== null && context !== void 0 ? context.options : normalizeOptions(props.options),
255
+ onChange: callback(props.onChange) || NOOP,
256
+ label: text(props.label), // 官方必填
257
+ disabled: isOn(props.disabled),
258
+ className: optionalText(props.className)
259
+ };
260
+ }
261
+ function fallbackSegments(props, context) {
262
+ var value = text(props.value);
263
+ var disabled = isOn(props.disabled);
264
+ var onChange = callback(props.onChange);
265
+ var options = context !== null && context !== void 0 ? context.options : normalizeOptions(props.options);
266
+ var tabs = [];
267
+ for (var i = 0; i < options.length; i += 1) {
268
+ var option = options[i];
269
+ var active = option.value === value;
270
+ tabs.push(h("button", {
271
+ key: option.value,
272
+ type: "button",
273
+ role: "tab",
274
+ "aria-selected": active ? "true" : "false",
275
+ disabled: disabled || option.disabled === true,
276
+ title: option.title,
277
+ className: "nwSegBtn" + (active ? " nwSegBtnOn" : ""),
278
+ onClick: (function (next, isActive) {
279
+ return function () { if (!isActive && onChange !== null) onChange(next); };
280
+ })(option.value, active)
281
+ }, option.label));
282
+ }
283
+ return h("div", {
284
+ className: classNameOf("nwSeg", props.className),
285
+ role: "tablist",
286
+ "aria-label": optionalText(props.label)
287
+ }, tabs);
288
+ }
289
+
290
+ // --- Modal ---
291
+ function mapModal(props) {
292
+ return {
293
+ open: isOn(props.open),
294
+ onClose: callback(props.onClose) || NOOP, // 官方必填
295
+ title: text(props.title), // 官方必填
296
+ closeLabel: text(props.closeLabel), // 官方必填:缺失即空串(不硬编码文案)
297
+ description: optionalText(props.description),
298
+ children: props.children,
299
+ footer: props.footer
300
+ };
301
+ }
302
+ function fallbackModal(props) {
303
+ if (!isOn(props.open)) return null;
304
+ var title = text(props.title);
305
+ var description = optionalText(props.description);
306
+ var children = props.children === void 0 || props.children === null ? null : h("div", null, props.children);
307
+ return h("div", { className: "nwModal", role: "presentation" },
308
+ h("div", { className: "nwModalBox", role: "dialog", "aria-modal": "true", "aria-label": title },
309
+ title === "" ? null : h("div", { className: "nwModalTitle" }, title),
310
+ description === void 0 ? null : h("div", { className: "nwModalText" }, description),
311
+ children,
312
+ props.footer === void 0 || props.footer === null ? null : h("div", { className: "nwModalBtns" }, props.footer)
313
+ ));
314
+ }
315
+
316
+ // --- RiskConfirmation ---
317
+ function riskLabels(props) {
318
+ var cancelLabel = text(props.cancelLabel);
319
+ return {
320
+ cancelLabel: cancelLabel,
321
+ // 官方多一个必填 closeLabel(弹窗右上角 × 的无障碍名);
322
+ // 调用方没给就借用 cancelLabel,仍然不产生硬编码文案
323
+ closeLabel: text(props.closeLabel) || cancelLabel
324
+ };
325
+ }
326
+ function mapRisk(props) {
327
+ var labels = riskLabels(props);
328
+ return {
329
+ open: isOn(props.open),
330
+ title: text(props.title),
331
+ description: text(props.description),
332
+ acknowledgeLabel: text(props.acknowledgeLabel),
333
+ cancelLabel: labels.cancelLabel,
334
+ closeLabel: labels.closeLabel,
335
+ confirmLabel: text(props.confirmLabel),
336
+ acknowledged: isOn(props.acknowledged),
337
+ disabled: isOn(props.disabled),
338
+ onAcknowledgedChange: callback(props.onAcknowledgedChange) || NOOP,
339
+ onCancel: callback(props.onCancel) || NOOP,
340
+ onConfirm: callback(props.onConfirm) || NOOP
341
+ };
342
+ }
343
+ function fallbackRisk(props) {
344
+ var labels = riskLabels(props);
345
+ var acknowledged = isOn(props.acknowledged);
346
+ var disabled = isOn(props.disabled);
347
+ var onAck = callback(props.onAcknowledgedChange);
348
+ var onCancel = callback(props.onCancel);
349
+ var onConfirm = callback(props.onConfirm);
350
+ return fallbackModal({
351
+ open: props.open,
352
+ title: props.title,
353
+ description: props.description,
354
+ children: h("label", { className: "nwModalText" },
355
+ h("input", {
356
+ type: "checkbox",
357
+ checked: acknowledged,
358
+ disabled: disabled,
359
+ // 没有回调时给 readOnly,避免 React 对"受控字段无 onChange"报 console.error
360
+ readOnly: onAck === null,
361
+ onChange: onAck === null ? void 0 : function (event) {
362
+ onAck(!!(event && event.target && event.target.checked === true));
363
+ }
364
+ }),
365
+ h("span", null, text(props.acknowledgeLabel))),
366
+ footer: h("div", { className: "nwModalBtns" },
367
+ h("button", {
368
+ type: "button",
369
+ className: "nwBtn nwBtnGhost",
370
+ onClick: onCancel === null ? void 0 : onCancel
371
+ }, labels.cancelLabel),
372
+ h("button", {
373
+ type: "button",
374
+ className: "nwBtn nwBtnDanger",
375
+ disabled: disabled || !acknowledged,
376
+ onClick: onConfirm === null ? void 0 : onConfirm
377
+ }, text(props.confirmLabel)))
378
+ });
379
+ }
380
+
381
+ // --- Tag ---
382
+ var TAG_TONES = { outline: 1, solid: 1, neutral: 1, quiet: 1, success: 1, info: 1, warning: 1, danger: 1 };
383
+ function tagTone(value) {
384
+ var tone = text(value);
385
+ return TAG_TONES[tone] === 1 ? tone : "outline"; // 与官方默认 tone 对齐
386
+ }
387
+ function mapTag(props) {
388
+ return { tone: tagTone(props.tone), className: optionalText(props.className), children: props.children };
389
+ }
390
+ function fallbackTag(props) {
391
+ // 既有 nw* 只有"开/关"两种胶囊:成功态映射到 nwBadgeOn,其余中性 tone 共用 nwBadge
392
+ var tone = tagTone(props.tone);
393
+ return h("span", { className: classNameOf(tone === "success" ? "nwBadge nwBadgeOn" : "nwBadge", props.className) }, props.children);
394
+ }
395
+
396
+ // --- Tooltip ---
397
+ function mapTooltip(props) {
398
+ var label = props.label;
399
+ if (typeof label !== "function") label = text(label); // 官方的 label 可以是 () => string
400
+ return { label: label, children: anchorOf(props.children) };
401
+ }
402
+ function fallbackTooltip(props) {
403
+ var label = "";
404
+ try {
405
+ label = typeof props.label === "function" ? text(props.label()) : text(props.label);
406
+ } catch (err) { label = ""; }
407
+ var anchor = anchorOf(props.children);
408
+ if (label === "" || anchor === null || typeof react.cloneElement !== "function") return anchor;
409
+ try {
410
+ return react.cloneElement(anchor, { title: label }); // 回退用原生 title 气泡
411
+ } catch (err) { return anchor; }
412
+ }
413
+
414
+ // --- DisclosureRow(官方是受控组件,回退实现自己持有展开态) ---
415
+ var useStateHook = typeof react.useState === "function" ? react.useState : null;
416
+ function prepareDisclosure(props) {
417
+ if (useStateHook === null) return { open: isOn(props.defaultOpen), toggle: NOOP };
418
+ var pair = useStateHook(isOn(props.defaultOpen));
419
+ var setter = callback(pair === null || pair === void 0 ? null : pair[1]);
420
+ return {
421
+ open: pair !== null && pair !== void 0 && pair[0] === true,
422
+ toggle: function () {
423
+ if (setter !== null) setter(function (value) { return value !== true; });
424
+ }
425
+ };
426
+ }
427
+ function mapDisclosure(props, context) {
428
+ var open = isOn(props.defaultOpen);
429
+ var toggle = NOOP;
430
+ if (context !== null && context !== void 0) { open = context.open === true; toggle = context.toggle; }
431
+ return {
432
+ icon: null, // 官方必填 icon:给 null,官方自带的 chevron 足以表达展开态
433
+ title: text(props.title),
434
+ open: open,
435
+ expandable: true,
436
+ onToggle: toggle,
437
+ expandOnRowClick: true,
438
+ children: props.children,
439
+ className: optionalText(props.className)
440
+ };
441
+ }
442
+ function fallbackDisclosure(props, context) {
443
+ var open = isOn(props.defaultOpen);
444
+ var toggle = NOOP;
445
+ if (context !== null && context !== void 0) { open = context.open === true; toggle = context.toggle; }
446
+ return h("div", { className: classNameOf("nwToolGroup", props.className) },
447
+ h("div", { className: "nwToolGroupHead" },
448
+ h("button", {
449
+ type: "button",
450
+ className: "nwToolGroupHeadBtn",
451
+ "aria-expanded": open ? "true" : "false",
452
+ onClick: toggle
453
+ },
454
+ h("span", { className: "nwToolGroupHeadText" },
455
+ h("span", { className: "nwToolGroupName" }, text(props.title))),
456
+ h("span", { className: "nwToolGroupArrow" }, open ? "\u25be" : "\u25b8")
457
+ )
458
+ ),
459
+ open ? h("div", { className: "nwToolGroupBody" }, props.children) : null);
460
+ }
461
+
462
+ /* ---------- 冻结 API ---------- */
463
+ return {
464
+ available: available,
465
+ Button: adapt("Button", fallbackButton, null, mapButton),
466
+ Switch: adapt("Switch", fallbackSwitch, null, mapSwitch),
467
+ SegmentedControl: adapt("SegmentedControl", fallbackSegments, prepareSegments, mapSegments),
468
+ Modal: adapt("Modal", fallbackModal, null, mapModal),
469
+ RiskConfirmation: adapt("RiskConfirmation", fallbackRisk, null, mapRisk),
470
+ Tag: adapt("Tag", fallbackTag, null, mapTag),
471
+ Tooltip: adapt("Tooltip", fallbackTooltip, null, mapTooltip),
472
+ DisclosureRow: adapt("DisclosureRow", fallbackDisclosure, prepareDisclosure, mapDisclosure)
473
+ };
474
+ }
475
+
476
+ var NW_UI = createNovelWriterUI(react, require);
477
+
27
478
  // ---- 样式 ----
28
479
  var css = `
29
480
 
481
+
482
+
30
483
  /* ===== 杂项 ===== */
31
484
  /* v4.3.0:删掉永不匹配的 [data-pane=…](宿主全树 0 命中,真实列名是 CSS-module 的 …_sidebarCol / …_centerCol) */
32
485
  [class*=centerCol]{position:relative}
33
- [data-dsh-novel-writer-view]{position:absolute;inset:0;z-index:60;background:var(--dsw-alias-bg-base,#fff);display:none;overflow:auto}
486
+ [data-dsh-novel-writer-view]{position:absolute;inset:0;z-index:60;background:var(--dsw-alias-bg-base);display:none;overflow:auto}
34
487
  html[data-dsh-novel-writer-active]:not([data-dsh-ssh-active]):not([data-dsh-taskboard-active]) [data-dsh-novel-writer-view]{display:block}
35
488
  html[data-dsh-novel-writer-active]:not([data-dsh-ssh-active]):not([data-dsh-taskboard-active]) [class*=centerCol]>:not([data-dsh-novel-writer-view]){display:none!important}
36
489
  /* v4.3.0:宿主折叠标记挂在 frame 上(<div class="…_frame" data-sidebar-collapsed>),并不存在 data-dsh-frame 属性 */
37
490
  [data-sidebar-collapsed] .nwEntry{justify-content:center;width:100%;padding:0}
38
491
  [data-sidebar-collapsed] .nwEntryLabel{display:none}
39
- .nwSectionTitle{display:flex;align-items:center;gap:6px;font-weight:700;font-size:13px;margin-top:6px}.nwTolVer{font-size:10px;color:var(--dsw-alias-label-tertiary,#94a3b8);margin:-2px 0 2px;letter-spacing:.03em}.nwTolCard{background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l2,#e2e8f0);border-radius:12px;padding:4px 14px 8px;margin:8px 0 4px;box-shadow:0 1px 4px rgba(15,23,42,.05)}.nwTolRow{display:flex;align-items:center;gap:10px;padding:9px 2px}.nwTolRow+.nwTolRow{border-top:1px dashed var(--dsw-alias-border-l2,#eef2f7)}.nwTolName{flex:1;min-width:0;font-size:13px;color:var(--dsw-alias-label-primary,#334155);font-weight:500;display:flex;align-items:center;gap:8px;white-space:nowrap}.nwTolIcon{font-size:15px;opacity:.9;flex-shrink:0}.nwTolField{width:66px;flex-shrink:0;display:flex;flex-direction:column;align-items:stretch;gap:3px}.nwTolInput{width:100%;height:30px;padding:0 4px;box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2,#d4d4d8);border-radius:8px;font-size:13px;text-align:center;color:var(--dsw-alias-label-primary,#18181b);background:var(--dsw-alias-bg-layer-2,#fafafa);transition:border-color .18s,box-shadow .18s,background .18s;-moz-appearance:textfield}.nwTolInput::-webkit-outer-spin-button,.nwTolInput::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}.nwTolInput:hover{border-color:#a1a1aa}.nwTolInput:focus{outline:none;border-color:#6366f1;box-shadow:0 0 0 3px rgba(99,102,241,.16);background:#fff}.nwTolInput::placeholder{color:#c4c8d0;font-size:11px}.nwTolCaption{font-size:10px;color:var(--dsw-alias-label-tertiary,#94a3b8);text-align:center}.nwTolSep{width:14px;flex-shrink:0;font-size:12px;color:var(--dsw-alias-label-tertiary,#b0b8c4);text-align:center}.nwTolPct{width:14px;flex-shrink:0;font-size:13px;color:var(--dsw-alias-label-secondary,#64748b);text-align:left}.nwTolBtns{display:flex;gap:12px;margin-top:16px}.nwBtnPrimary{background:linear-gradient(135deg,#6366f1,#8b5cf6);border:none;color:#fff;font-weight:600;font-size:13px;padding:9px 20px;border-radius:10px;cursor:pointer;box-shadow:0 2px 6px rgba(99,102,241,.28);transition:transform .15s,box-shadow .15s,opacity .15s}.nwBtnPrimary:hover{transform:translateY(-1px);box-shadow:0 4px 12px rgba(99,102,241,.38)}.nwBtnPrimary:active{transform:translateY(0);box-shadow:0 1px 3px rgba(99,102,241,.3)}.nwBtnDone{background:linear-gradient(135deg,#10b981,#34d399);box-shadow:0 2px 6px rgba(16,185,129,.3)}.nwBtnGhost{background:transparent;border:1px solid var(--dsw-alias-border-l2,#cbd5e1);color:var(--dsw-alias-label-secondary,#475569);font-size:13px;padding:9px 18px;border-radius:10px;cursor:pointer;transition:background .15s,border-color .15s,transform .15s}.nwBtnGhost:hover{background:var(--dsw-alias-bg-layer-2,#f1f5f9);border-color:#a1a1aa;transform:translateY(-1px)}.nwUpdateBar{display:block;margin:4px 0 8px;padding:7px 10px;border-radius:6px;background:#e8f5e9;color:#1b5e20;font-size:12px;text-decoration:none;border:1px solid #a5d6a7;cursor:pointer}.nwUpdateBar:hover{background:#c8e6c9}.nwUpdateStale{display:block;margin:4px 0 8px;padding:7px 10px;border-radius:6px;background:var(--dsw-alias-bg-layer-2,#f1f5f9);color:var(--dsw-alias-label-tertiary,#64748b);font-size:11px;line-height:1.5;border:1px solid var(--dsw-alias-border-l2,#e2e8f0)}
40
- .nwNavEntry{display:flex;align-items:center;justify-content:space-between;width:100%;box-sizing:border-box;padding:8px 10px;margin:4px 0;border:1px solid var(--dsw-alias-border);border-radius:8px;background:var(--dsw-alias-surface-1);cursor:pointer;text-align:left;color:inherit;font:inherit}
41
- .nwNavEntry:hover{border-color:#22c55e}
492
+ .nwSectionTitle{display:inline-flex;align-items:center;gap:6px;font-weight:700;font-size:13px;line-height:19px;letter-spacing:.02em;color:var(--dsw-alias-state-business-primary);background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 8%,var(--dsw-alias-bg-layer-1));border:0.5px solid color-mix(in srgb,var(--dsw-alias-state-business-primary) 16%,var(--dsw-alias-bg-layer-1));border-radius:999px;corner-shape:round;padding:3px 12px;margin-top:10px}.nwTolVer{font-size:10px;line-height:14px;color:var(--dsw-alias-label-tertiary);margin:-2px 0 2px;letter-spacing:.03em}.nwTolCard{border-radius:var(--dsw-radius-xl);border:0.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-layer-2);padding:4px 14px 8px;margin:8px 0 4px}.nwTolRow{display:flex;align-items:center;gap:10px;padding:9px 2px}.nwTolRow+.nwTolRow{border-top:1px dashed var(--dsw-alias-border-l2)}.nwTolName{flex:1;min-width:0;font-size:13px;line-height:20px;color:var(--dsw-alias-label-primary);font-weight:500;display:flex;align-items:center;gap:8px;white-space:nowrap}.nwTolIcon{font-size:15px;line-height:22px;opacity:.9;flex-shrink:0}.nwTolField{width:66px;flex-shrink:0;display:flex;flex-direction:column;align-items:stretch;gap:3px}.nwTolInput{width:100%;height:30px;padding:0 4px;box-sizing:border-box;border:0.5px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-sm);font-size:13px;line-height:20px;text-align:center;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);transition:border-color .18s,box-shadow .18s,background .18s;-moz-appearance:textfield}.nwTolInput::-webkit-outer-spin-button,.nwTolInput::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}.nwTolInput:hover{border-color:var(--dsw-alias-border-l3)}.nwTolInput:focus{outline:none;border-color:var(--dsw-alias-state-business-primary);box-shadow:0 0 0 3px var(--dsw-focus-ring-color);background:var(--dsw-alias-bg-layer-1)}.nwTolInput::placeholder{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}.nwTolCaption{font-size:10px;line-height:14px;color:var(--dsw-alias-label-tertiary);text-align:center}.nwTolSep{width:14px;flex-shrink:0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary);text-align:center}.nwTolPct{width:14px;flex-shrink:0;font-size:13px;line-height:20px;color:var(--dsw-alias-label-secondary);text-align:left}.nwTolBtns{display:flex;gap:12px;margin-top:16px}.nwBtn.nwBtnPrimary{background:var(--dsw-alias-state-business-primary);border:none;color:var(--dsw-alias-label-primary-foreground);font-weight:600;font-size:13px;line-height:20px;padding:9px 20px;border-radius:var(--dsw-radius-sm);cursor:pointer;box-shadow:var(--dsw-elevation-soft);transition:transform .15s,box-shadow .15s,opacity .15s,filter .15s;font-family:inherit}.nwBtnPrimary:hover{transform:translateY(-1px);box-shadow:var(--dsw-elevation-panel);filter:brightness(1.12)}.nwBtnPrimary:active{transform:translateY(0);box-shadow:var(--dsw-elevation-soft)}.nwBtn.nwBtnDone{background:linear-gradient(135deg,var(--dsw-alias-state-success-primary),var(--dsw-alias-state-success-primary));box-shadow:var(--dsw-elevation-soft);font-family:inherit}.nwBtn.nwBtnGhost{background:transparent;border:0.5px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);font-size:13px;line-height:20px;padding:9px 18px;border-radius:var(--dsw-radius-sm);cursor:pointer;transition:background .15s,border-color .15s,transform .15s;font-family:inherit}.nwBtnGhost:hover{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-border-l3);transform:translateY(-1px)}.nwUpdateBar{display:block;margin:4px 0 8px;padding:7px 10px;border-radius:var(--dsw-radius-sm);background:var(--dsw-alias-state-success-tertiary);color:var(--dsw-alias-state-success-primary);font-size:12px;line-height:18px;text-decoration:none;border:1px solid var(--dsw-alias-state-success-primary);cursor:pointer}.nwUpdateBar:hover{background:var(--dsw-alias-interactive-bg-hover)}.nwUpdateStale{display:block;margin:4px 0 8px;padding:7px 10px;border-radius:var(--dsw-radius-sm);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:1.5;border:0.5px solid var(--dsw-alias-border-l2)}
493
+ .nwNavEntry{display:flex;align-items:center;gap:10px;width:100%;box-sizing:border-box;padding:9px 12px;margin:4px 0;border:0;border-radius:var(--dsw-radius-lg);background:var(--dsw-alias-bg-layer-1);box-shadow:var(--dsw-elevation-soft);cursor:pointer;text-align:left;color:inherit;font:inherit;transition:box-shadow .2s var(--nw-ease),transform .18s var(--nw-ease)}
494
+ .nwNavEntry:hover{box-shadow:var(--dsw-elevation-panel);transform:translateY(-1px);font-family:inherit}
42
495
  .nwNavEntryText{flex:1;min-width:0;padding-right:8px}
43
- .nwNavEntryTitle{font-size:13px;font-weight:600}
44
- .nwNavEntryHint{font-size:11px;color:var(--dsw-alias-label-tertiary);margin-top:2px}
496
+ .nwNavEntryTitle{font-size:13px;line-height:20px;font-weight:600}
497
+ .nwNavEntryHint{font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary);margin-top:2px}
45
498
  .nwNavEntryRight{display:flex;align-items:center;gap:6px;flex:none}
46
499
  .nwNavEntryRight .nwBadge{white-space:nowrap;flex:none}
47
- .nwNavEntryArrow{font-size:18px;color:var(--dsw-alias-label-tertiary);flex:none}
48
- .nwBackBtn{font-size:12px;padding:3px 8px;margin:2px 0 6px;border:1px solid var(--dsw-alias-border);border-radius:6px;background:var(--dsw-alias-surface-1);cursor:pointer}
49
- .nwModelBtn{font-size:11px;padding:2px 6px;margin-top:4px;border:1px solid #7c3aed;color:#7c3aed;border-radius:6px;background:transparent;cursor:pointer}
500
+ .nwNavEntryArrow{font-size:18px;line-height:26px;color:var(--dsw-alias-label-tertiary);flex:none}
501
+ .nwBackBtn{display:flex;width:fit-content;align-items:center;justify-content:center;gap:6px;height:30px;padding:0 14px;margin:2px 0 6px;border:0.5px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);font-size:13px;line-height:20px;cursor:pointer;font-family:inherit;transition:background .16s var(--nw-ease),color .16s var(--nw-ease),box-shadow .2s var(--nw-ease)}
502
+ .nwModelBtn{font-size:11px;line-height:16px;padding:2px 6px;margin-top:4px;border:0.5px solid var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary);border-radius:var(--dsw-radius-sm);background:transparent;cursor:pointer;font-family:inherit}
50
503
  .nwToolRight{display:flex;flex-direction:column;align-items:flex-end;gap:4px}
51
504
 
52
505
  /* ===== 导航/入口 ===== */
53
- .nwEntry{width:100%;height:32px;color:var(--dsw-alias-label-secondary);cursor:pointer;white-space:nowrap;background:0 0;border:none;border-radius:8px;align-items:center;gap:8px;padding:0 12px;font-size:13px;display:flex}
54
- .nwEntry:hover{background:var(--dsw-specific-sidebar-nav-item-hover);color:var(--dsw-alias-label-primary)}
506
+ .nwEntry{width:100%;height:32px;color:var(--dsw-alias-label-secondary);cursor:pointer;white-space:nowrap;background:0 0;border:none;border-radius:var(--dsw-radius-md);align-items:center;gap:8px;padding:0 12px;font-size:13px;line-height:20px;display:flex;font-family:inherit}
507
+ .nwEntry:hover{background:var(--dsw-specific-sidebar-nav-item-hover);color:var(--dsw-alias-label-primary);font-family:inherit}
55
508
  .nwEntry[data-active]{background:var(--dsw-specific-sidebar-nav-item-active);color:var(--dsw-alias-label-primary);font-weight:600}
56
- .nwEntryIcon{flex:none;justify-content:center;align-items:center;display:inline-flex;width:16px;font-size:13px}
509
+ .nwEntryIcon{flex:none;justify-content:center;align-items:center;display:inline-flex;width:16px;font-size:13px;line-height:20px}
57
510
  .nwEntryLabel{text-overflow:ellipsis;overflow:hidden}
58
- .nwPanel{max-width:560px;margin:0 auto;padding:28px 24px;color:var(--dsw-alias-label-primary);font-family:var(--dsw-font-family);display:flex;flex-direction:column;gap:10px}
511
+ .nwPanel{--nw-ease:cubic-bezier(.16,1,.3,1);scrollbar-gutter:stable;width:100%;max-width:560px;margin:0 auto;padding:28px 24px;color:var(--dsw-alias-label-primary);font-family:var(--dsw-font-family);display:flex;flex-direction:column;gap:10px}
59
512
  .nwPanelHeader{display:flex;align-items:center;gap:10px}
60
- .nwPanelTitle{font-size:16px;font-weight:700;flex:1;min-width:0}
61
- .nwClose{appearance:none;font:inherit;cursor:pointer;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-secondary);width:30px;height:30px;line-height:1}
62
- .nwRefresh{appearance:none;font:inherit;cursor:pointer;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);font-size:11px;line-height:1;padding:5px 9px}
63
- .nwRefresh:hover{background:var(--dsw-alias-bg-layer-2)}
513
+ .nwPanelTitle{font-size:16px;line-height:24px;font-weight:700;letter-spacing:-.02em;flex:1;min-width:0}
514
+ .nwClose{appearance:none;font:inherit;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;border:0.5px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);width:30px;height:30px;padding:0;font-size:15px;line-height:1;transition:background .16s var(--nw-ease),color .16s var(--nw-ease),box-shadow .2s var(--nw-ease)}
515
+ .nwRefresh{appearance:none;font:inherit;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;border:0.5px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);height:30px;padding:0 12px;font-size:12px;line-height:18px;transition:background .16s var(--nw-ease),color .16s var(--nw-ease),box-shadow .2s var(--nw-ease)}
516
+ .nwRefresh:hover,.nwClose:hover,.nwBackBtn:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary);font-family:inherit}
64
517
  .nwDesc{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.6;margin:0}
65
518
  .nwRefreshSpin{animation:nwspin 1s linear infinite}
66
519
 
67
520
  /* ===== 横幅与开关 ===== */
68
- .nwBanner{display:flex;align-items:center;gap:10px;border-radius:12px;padding:14px 16px;font-size:15px;font-weight:700;border:1px solid}
69
- .nwBannerOn{background:rgba(34,197,94,.12);border-color:#22c55e;color:#15803d}
70
- .nwBannerOff{background:rgba(239,68,68,.10);border-color:#ef4444;color:#b91c1c}
521
+ .nwBanner{display:flex;align-items:center;gap:10px;border-radius:var(--dsw-radius-lg);padding:14px 16px;font-size:15px;line-height:22px;font-weight:700;border:0.5px solid}
522
+ .nwBannerOn{background:var(--dsw-alias-state-success-tertiary);border-color:var(--dsw-alias-state-success-primary);color:var(--dsw-alias-state-success-primary)}
523
+ .nwBannerOff{background:var(--dsw-alias-state-danger-tertiary);border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-label-error)}
71
524
  .nwBannerIcon{flex:none;font-size:18px;line-height:1}
72
- .nwBannerSub{font-size:12px;font-weight:500;opacity:.9;margin-left:6px}
73
- .nwRow{display:flex;align-items:center;gap:12px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);border-radius:12px;padding:12px 14px;transition:border-color .18s,background .18s}
74
- .nwRowOn{border-color:#22c55e;background:rgba(34,197,94,.07)}
75
- .nwRowOff{border-color:var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2)}
525
+ .nwBannerSub{font-size:12px;line-height:18px;font-weight:500;opacity:.9;margin-left:6px}
526
+ .nwRow{display:flex;align-items:center;gap:14px;border:0;background:var(--dsw-alias-bg-layer-1);border-radius:var(--dsw-radius-lg);padding:14px 16px;box-shadow:var(--dsw-elevation-soft);transition:box-shadow .2s var(--nw-ease),transform .18s var(--nw-ease)}
527
+ .nwRow:hover{box-shadow:var(--dsw-elevation-panel);transform:translateY(-1px)}
528
+ .nwRowOn{background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 10%,var(--dsw-alias-bg-layer-1));box-shadow:var(--dsw-elevation-panel)}
529
+ .nwRowOff{background:var(--dsw-alias-bg-layer-1)}
76
530
  .nwRowText{flex:1;min-width:0}
77
531
  .nwRowLabel{font-size:13px;font-weight:600;line-height:1.5}
78
532
  .nwRowHint{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:1.5;margin-top:2px}
79
533
  .nwSwitchWrap{flex:none;display:flex;align-items:center;gap:8px}
80
- .nwSwitch{appearance:none;flex:none;width:56px;height:30px;border-radius:999px;border:2px solid #9ca3af;background:#9ca3af;cursor:pointer;position:relative;padding:0;transition:background .18s,border-color .18s,box-shadow .18s}
81
- .nwSwitch:hover{box-shadow:0 0 0 4px rgba(156,163,175,.20)}
82
- .nwSwitch:focus-visible{outline:2px solid #22c55e;outline-offset:2px}
83
- .nwSwitchOn{background:#22c55e;border-color:#22c55e;box-shadow:0 0 0 4px rgba(34,197,94,.18)}
84
- .nwSwitchOn:hover{box-shadow:0 0 0 4px rgba(34,197,94,.28)}
85
- .nwSwitchKnob{position:absolute;top:2px;left:2px;width:22px;height:22px;border-radius:50%;background:#fff;box-shadow:0 1px 3px rgba(0,0,0,.3);transition:left .18s}
86
- .nwSwitchOn .nwSwitchKnob{left:28px}
534
+ .nwSwitch{appearance:none;flex:none;width:56px;height:30px;box-sizing:border-box;border-radius:999px;border:0;background:var(--dsw-alias-bg-layer-4);cursor:pointer;position:relative;padding:0;transition:background .18s,border-color .18s;corner-shape:round}
535
+ .nwSwitch:hover{border-color:var(--dsw-alias-border-l4)}
536
+ .nwSwitch:focus-visible{outline:2px solid var(--dsw-focus-ring-color);outline-offset:2px}
537
+ .nwSwitchOn{background:var(--dsw-alias-state-business-primary);border-color:var(--dsw-alias-state-business-primary)}
538
+ .nwSwitchOn:hover{border-color:var(--dsw-alias-state-business-primary)}
539
+ .nwSwitchKnob{position:absolute;top:4px;left:4px;width:22px;height:22px;border-radius:50%;background:var(--dsw-alias-switch-thumb);box-shadow:var(--dsw-elevation-soft);transition:left .18s;corner-shape:round}
540
+ .nwSwitchOn .nwSwitchKnob{left:30px}
87
541
  .nwSwitch:disabled{opacity:.5;cursor:default;box-shadow:none}
88
- .nwSwitchSmall{appearance:none;flex:none;width:40px;height:22px;border-radius:999px;border:2px solid #9ca3af;background:#9ca3af;cursor:pointer;position:relative;padding:0;transition:background .16s,border-color .16s}
89
- .nwSwitchSmallOn{background:#22c55e;border-color:#22c55e}
90
- .nwSwitchSmallKnob{position:absolute;top:2px;left:2px;width:14px;height:14px;border-radius:50%;background:#fff;transition:left .16s}
91
- .nwSwitchSmallOn .nwSwitchSmallKnob{left:20px}
92
- .nwSeg{display:flex;gap:4px;flex:none}
93
- .nwSegBtn{appearance:none;border:1px solid #cbd5e1;background:#fff;color:#475569;font-size:12px;padding:5px 10px;border-radius:8px;cursor:pointer;transition:background .16s,border-color .16s,color .16s}
94
- .nwSegBtn:hover{border-color:#94a3b8}
95
- .nwSegBtnOn{background:#22c55e;border-color:#22c55e;color:#fff;font-weight:600}
96
- .nwSegBtn:disabled{opacity:.6;cursor:default}
542
+ .nwSwitchSmall{appearance:none;flex:none;width:40px;height:22px;box-sizing:border-box;border-radius:999px;border:0;background:var(--dsw-alias-bg-layer-4);cursor:pointer;position:relative;padding:0;transition:background .16s,border-color .16s;corner-shape:round}
543
+ .nwSwitchSmallOn{background:var(--dsw-alias-state-business-primary);border-color:var(--dsw-alias-state-business-primary)}
544
+ .nwSwitchSmallKnob{position:absolute;top:4px;left:4px;width:14px;height:14px;border-radius:50%;background:var(--dsw-alias-switch-thumb);transition:left .16s;corner-shape:round}
545
+ .nwSwitchSmallOn .nwSwitchSmallKnob{left:22px}
546
+ .nwSeg{display:flex;gap:3px;flex:none;background:var(--dsw-alias-bg-layer-2);border-radius:var(--dsw-radius-md);padding:3px}
547
+ .nwSegBtn{appearance:none;border:0.5px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font-size:12px;line-height:18px;padding:5px 10px;border-radius:var(--dsw-radius-sm);cursor:pointer;transition:background .16s,border-color .16s,color .16s;font-family:inherit}
548
+ .nwSegBtn:hover{border-color:var(--dsw-alias-border-l3);font-family:inherit}
549
+ .nwSegBtnOn{background:var(--dsw-alias-state-business-primary);border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-label-primary-foreground);font-weight:600}
550
+ .nwSegBtn:disabled{opacity:.6;cursor:default;font-family:inherit}
97
551
 
98
552
  /* ===== 徽章 ===== */
99
- .nwBadge{flex:none;border-radius:999px;padding:3px 10px;font-size:12px;font-weight:700;white-space:nowrap;letter-spacing:.5px}
100
- .nwBadgeOn{background:#22c55e;color:#fff}
101
- .nwBadgeOff{background:#e5e7eb;color:#6b7280}
553
+ .nwBadge{flex:none;border-radius:999px;padding:3px 10px;font-size:12px;line-height:18px;font-weight:700;white-space:nowrap;letter-spacing:.5px;corner-shape:round}
554
+ .nwBadgeOn{background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 18%,var(--dsw-alias-bg-layer-1));color:var(--dsw-alias-state-business-primary)}
555
+ .nwBadgeOff{background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-tertiary)}
102
556
 
103
557
  /* ===== 动画与杂项 ===== */
104
558
  @keyframes nwspin{from{transform:rotate(0)}to{transform:rotate(360deg)}}
105
- @keyframes nwflash{0%{background:rgba(34,197,94,.25)}100%{background:transparent}}
559
+ @keyframes nwflash{0%{background:var(--dsw-alias-state-success-tertiary)}100%{background:transparent}}
106
560
  /* v4.3.0:错误提示的红色闪烁(旧版错误分支恒不可达,见 PanelView msg 类名) */
107
- @keyframes nwflashErr{0%{background:rgba(239,68,68,.18)}100%{background:transparent}}
561
+ @keyframes nwflashErr{0%{background:var(--dsw-alias-state-error-secondary)}100%{background:transparent}}
108
562
 
109
563
  /* ===== 路径/按钮/状态 ===== */
110
- .nwPlotOk{color:#22c55e}
564
+ .nwPlotOk{color:var(--dsw-alias-state-success-primary)}
111
565
  .nwFlash{animation:nwflash 1.2s ease}
112
566
  .nwFlashErr{animation:nwflashErr 1.2s ease}
113
- .nwBtnDanger{background:#ef4444;color:#fff;border-color:#ef4444}
114
- .nwPlotBox{margin-top:6px;padding:6px 8px;background:rgba(127,127,127,.08);border-radius:8px}
567
+ .nwBtn.nwBtnDanger{background:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-label-primary-foreground);border-color:var(--dsw-alias-state-error-primary);font-family:inherit}
568
+ .nwPlotBox{margin-top:6px;padding:6px 8px;background:var(--dsw-alias-bg-layer-2);border-radius:var(--dsw-radius-sm)}
115
569
  .nwPlotPath{font-size:11px;line-height:1.5;color:var(--dsw-alias-label-secondary);word-break:break-all}
116
570
  .nwBtnGroup{display:flex;gap:6px;margin-top:6px}
117
- .nwBtn{font-size:11px;line-height:1;padding:5px 10px;border-radius:8px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);cursor:pointer}
118
- .nwBtn:hover{background:var(--dsw-alias-bg-layer-2)}
119
- .nwBtn:disabled{opacity:.45;cursor:not-allowed}
571
+ .nwBtn{font-size:11px;line-height:1;padding:5px 10px;border-radius:var(--dsw-radius-sm);border:0.5px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);cursor:pointer;font-family:inherit;transition:background .16s var(--nw-ease),box-shadow .2s var(--nw-ease),transform .18s var(--nw-ease)}
572
+ .nwBtn:hover{background:var(--dsw-alias-bg-layer-2);font-family:inherit}
573
+ .nwBtn:disabled{opacity:.45;cursor:not-allowed;font-family:inherit}
120
574
  .nwPlotMsg{font-size:11px;line-height:1.5;color:var(--dsw-alias-label-secondary);margin-top:4px;word-break:break-all}
121
- .nwPlotErr{color:#ef4444}
575
+ .nwPlotErr{color:var(--dsw-alias-label-error)}
122
576
  .nwStatus{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:1.5;word-break:break-all}
123
- .nwFoot{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:1.6;border-top:1px solid var(--dsw-alias-border-l2);padding-top:10px;margin-top:4px}
124
- .nwGroupState{width:16px;height:16px;border-radius:4px;border:2px solid #9ca3af;display:inline-flex;align-items:center;justify-content:center;position:relative;overflow:hidden;background:#fff}
125
- .nwGroupStateOn{border-color:#22c55e;background:#22c55e}
126
- .nwGroupStatePartial{border-color:#22c55e;background:#fff}
127
- .nwGroupStateOff{border-color:#9ca3af;background:#fff}
128
- .nwGroupStateCheck{color:#fff;font-size:11px;font-weight:700;line-height:1}
129
- .nwGroupStateCheckPartial{width:10px;height:10px;border-radius:2px;background:#22c55e;color:transparent}
577
+ .nwFoot{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:1.6;border-top:0.5px solid var(--dsw-alias-border-l2);padding-top:10px;margin-top:4px}
578
+ .nwGroupState{width:16px;height:16px;border-radius:var(--dsw-radius-xs);border:0.5px solid var(--dsw-alias-border-l3);display:inline-flex;align-items:center;justify-content:center;position:relative;overflow:hidden;background:var(--dsw-alias-bg-layer-1);box-sizing:border-box}
579
+ .nwGroupStateOn{border-color:var(--dsw-alias-state-business-primary);background:var(--dsw-alias-state-business-primary)}
580
+ .nwGroupStatePartial{border-color:var(--dsw-alias-state-business-primary);background:var(--dsw-alias-bg-layer-1)}
581
+ .nwGroupStateOff{border-color:var(--dsw-alias-border-l3);background:var(--dsw-alias-bg-layer-1)}
582
+ .nwGroupStateCheck{color:var(--dsw-alias-label-primary-foreground);font-size:11px;font-weight:700;line-height:1}
583
+ .nwGroupStateCheckPartial{width:10px;height:10px;border-radius:var(--dsw-radius-xs);background:var(--dsw-alias-state-business-primary);color:transparent}
130
584
 
131
585
  /* ===== 非净化模式 ===== */
132
- .nwToolRowRaw{border-color:#ef4444}
133
- .nwToolLabelRaw{color:#ef4444;font-weight:700}
134
- .nwRawDanger{color:#ef4444!important;font-weight:600}
135
- .nwRawOn{font-size:11px;color:#ef4444;margin-top:2px;font-weight:600}
586
+ .nwToolRow.nwToolRowRaw{background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 5%,var(--dsw-alias-bg-layer-1));box-shadow:var(--dsw-elevation-panel)}
587
+ .nwToolLabelRaw{color:var(--dsw-alias-label-error);font-weight:700}
588
+ .nwRawDanger{color:var(--dsw-alias-label-error)!important;font-weight:600}
589
+ .nwRawOn{font-size:11px;line-height:16px;color:var(--dsw-alias-label-error);margin-top:2px;font-weight:600}
136
590
 
137
591
  /* ===== 确认框(v5.2.0:面板内卡片,不再用全屏遮罩)===== */
138
- /* 旧版写死 rgba(0,0,0,.5)、上一版换成宿主 mask token + backdrop-blur —— 两者都是"整屏遮罩",
592
+ /* 旧版用写死的半透明黑遮罩、上一版换成宿主 mask token + backdrop-blur —— 两者都是"整屏遮罩",
139
593
  会把整个应用(侧栏、会话、面板)糊成一片均匀的灰,用户直观感受就是"背景突变了"。
140
594
  但这两个确认框(先确认开启 / 再输入承诺文字)本来就是 PanelView 正文的一部分:
141
595
  它们出现时已经替换掉正常正文,所以**完全不需要遮罩**——做成一枚居中卡片即可,
142
596
  背景一点不变,也不会盖住侧栏与会话。 */
143
597
  .nwModal{display:block;margin:18px auto 0;width:100%;max-width:460px}
144
- .nwModalBox{background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-primary);border:1px solid var(--dsw-alias-border-l2,transparent);border-radius:16px;padding:18px 20px;box-shadow:var(--dsw-shadow-lv3,0 12px 40px rgba(15,23,42,.18))}
598
+ .nwModalBox{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);border-radius:var(--dsw-radius-panel);padding:18px 20px;box-shadow:var(--dsw-elevation-prominent)}
145
599
  .nwModalTitle{font-size:15px;font-weight:600;line-height:1.5;margin-bottom:8px}
146
600
  .nwModalText{font-size:13px;line-height:1.7;color:var(--dsw-alias-label-secondary);white-space:pre-line;margin-bottom:14px}
147
- .nwModalInput{width:100%;box-sizing:border-box;padding:9px 11px;font:inherit;font-size:13px;border:1px solid var(--dsw-alias-border-l2,#d4d4d8);border-radius:10px;background:var(--dsw-alias-bg-layer-1,#fafafa);color:var(--dsw-alias-label-primary);margin-bottom:14px}
148
- .nwModalInput:focus{outline:none;border-color:var(--dsw-alias-brand-primary,#6366f1);box-shadow:0 0 0 3px rgba(99,102,241,.16)}
601
+ .nwModalInput{width:100%;box-sizing:border-box;padding:9px 11px;font:inherit;font-size:13px;line-height:20px;border:0.5px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);margin-bottom:14px}
602
+ .nwModalInput:focus{outline:none;border-color:var(--dsw-alias-state-business-primary);box-shadow:0 0 0 3px var(--dsw-focus-ring-color)}
149
603
  .nwModalBtns{display:flex;gap:10px;justify-content:flex-end}
150
604
  /* 卡片里的按钮:给足点击面积,并与卡片底色拉开层次 */
151
- .nwModalBtns .nwBtn{min-width:88px;height:34px;padding:0 14px;font-size:13px;border-radius:10px;background:var(--dsw-alias-bg-layer-1,#fff)}
152
- .nwModalBtns .nwBtn:hover{background:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-3,#f4f4f5))}
153
- .nwModalBtns .nwBtnDanger{background:var(--dsw-alias-state-error-primary,#ef4444);border-color:transparent;color:#fff}
605
+ .nwModalBtns .nwBtn{min-width:88px;height:34px;padding:0 14px;font-size:13px;line-height:20px;border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1)}
606
+ .nwModalBtns .nwBtn:hover{background:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-3))}
607
+ .nwModalBtns .nwBtn.nwBtnDanger{background:var(--dsw-alias-state-error-primary);border-color:transparent;color:var(--dsw-alias-label-primary-foreground)}
154
608
  .nwModalBtns .nwBtnDanger:hover{filter:brightness(1.05)}
155
609
  /* 入场:卡片轻微上浮即可,不再有整屏淡入(并尊重系统「减少动态效果」设置) */
156
610
  @keyframes nwModalBoxIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
157
611
  @media (prefers-reduced-motion: no-preference){
158
- .nwModalBox{animation:nwModalBoxIn .18s ease-out}
159
- }
612
+ .nwModalBox{animation:nwModalBoxIn .18s ease-out}}
160
613
 
161
614
  /* ===== 工具行/开关/说明 ===== */
162
- .nwToolItem{width:100%;margin-bottom:2px}
615
+ .nwToolItem{width:100%;margin-bottom:6px}
163
616
  .nwToolItem .nwPlotBox{width:100%;box-sizing:border-box}
164
- .nwToolsHint{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:1.5;margin:2px 0 4px}
165
- .nwToolRow{display:flex;align-items:center;gap:10px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);border-radius:10px;padding:8px 12px}
166
- .nwToolRowOn{border-color:rgba(34,197,94,.55);background:rgba(34,197,94,.05)}
617
+ .nwToolsHint{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;margin:6px 0 14px}
618
+ .nwToolRow{display:flex;align-items:center;gap:10px;border:0;background:var(--dsw-alias-bg-layer-1);border-radius:var(--dsw-radius-md);padding:10px 14px;box-shadow:var(--dsw-elevation-soft);transition:box-shadow .2s var(--nw-ease),transform .18s var(--nw-ease)}
619
+ .nwToolRow:hover{box-shadow:var(--dsw-elevation-panel);transform:translateY(-1px)}
620
+ .nwToolRow+.nwToolRow{margin-top:10px}
621
+ .nwRow+.nwToolRow,.nwToolRow+.nwRow{margin-top:10px}
622
+ .nwToolRow+.nwPlotBox{margin-top:10px}
623
+ .nwToolRowOn{background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 10%,var(--dsw-alias-bg-layer-1));box-shadow:var(--dsw-elevation-panel)}
167
624
  .nwToolLabel{flex:1;min-width:0;font-size:12px;line-height:1.4;color:var(--dsw-alias-label-primary)}
168
625
  .nwToolDesc{font-size:11px;line-height:1.45;color:var(--dsw-alias-label-tertiary);margin-top:2px}
169
626
 
170
627
  /* ===== 工具组 ===== */
171
- .nwToolGroup{border:1px solid var(--dsw-alias-border-l2);border-radius:10px;margin-bottom:8px;overflow:hidden}
172
- .nwToolGroupHead{display:flex;align-items:center;gap:6px;width:100%;padding:9px 12px;background:rgba(127,127,127,.08);border:none;cursor:default;font-size:13px;text-align:left;color:inherit}
173
- .nwToolGroupHead:hover{background:rgba(127,127,127,.14)}
174
- .nwToolGroupIcon{font-size:15px}
628
+ .nwToolGroup{border:0;border-radius:var(--dsw-radius-md);margin-bottom:8px;overflow:hidden;background:var(--dsw-alias-bg-layer-1);box-shadow:var(--dsw-elevation-soft)}
629
+ .nwToolGroupBody .nwToolRow{box-shadow:none;background:transparent;transform:none}
630
+ .nwToolGroupBody .nwToolRowOn{background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 10%,var(--dsw-alias-bg-layer-1))}
631
+ .nwToolGroupBody .nwToolRow:hover{box-shadow:none;transform:none}
632
+ .nwToolGroupHead{display:flex;align-items:center;gap:6px;width:100%;padding:9px 12px;background:var(--dsw-alias-bg-layer-2);border:none;cursor:default;font-size:13px;line-height:20px;text-align:left;color:inherit;font-family:inherit}
633
+ .nwToolGroupHead:hover{background:var(--dsw-alias-bg-layer-3);font-family:inherit}
634
+ .nwToolGroupIcon{font-size:15px;line-height:22px}
175
635
  .nwToolGroupName{font-weight:600}
176
- .nwToolGroupBadge{margin-left:auto;font-size:11px;color:#c62828;background:rgba(198,40,40,.1);border-radius:10px;padding:1px 7px}
177
- .nwToolGroupArrow{font-size:12px;opacity:.6}
636
+ .nwToolGroupBadge{margin-left:auto;font-size:11px;line-height:16px;color:var(--dsw-alias-label-error);background:var(--dsw-alias-state-error-secondary);border-radius:var(--dsw-radius-md);padding:1px 7px}
637
+ .nwToolGroupArrow{font-size:12px;line-height:18px;opacity:.6}
178
638
  .nwToolGroupBody{padding:4px 8px 8px}
179
639
  .nwToolGroupToggle{flex:none;width:24px;height:24px;display:flex;align-items:center;justify-content:center;background:none;border:none;cursor:pointer;padding:0}
180
- .nwToolGroupHeadBtn{flex:1;display:flex;align-items:center;gap:8px;background:none;border:none;cursor:pointer;padding:0;text-align:left;color:inherit;font-size:13px}
640
+ .nwToolGroupHeadBtn{flex:1;display:flex;align-items:center;gap:8px;background:none;border:none;cursor:pointer;padding:0;text-align:left;color:inherit;font-size:13px;line-height:20px}
181
641
  .nwToolGroupHeadText{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
182
642
  .nwToolGroupDescInline{font-size:11px;line-height:1.4;color:var(--dsw-alias-label-tertiary);font-weight:400;white-space:normal}
183
643
 
184
644
  /* ===== v5.2.0:官方主栏全局面板 ===== */
185
645
  /* 主栏页面:栅格、滚动容器与选中态都由宿主 layout 负责,这里只管内容排版 */
186
- .nwPanelMain{max-width:760px;margin:0 auto;padding:28px 24px 48px;gap:10px;box-sizing:border-box;height:100%;overflow:auto}
646
+ .nwPanelMain{width:100%;max-width:760px;margin:0 auto;padding:28px 24px 48px;gap:10px;box-sizing:border-box;height:100%;overflow:auto;scrollbar-gutter:stable}
647
+
648
+ /* ===== v5.5.0:官方语义类(内联样式收编 + 官方设置卡片材质)===== */
649
+ .nwBoxed{background:var(--dsw-alias-bg-layer-1);border:0.5px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-md);padding:8px 12px}
650
+ .nwSubCard{background:var(--dsw-alias-bg-layer-1);border:0.5px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-lg);padding:10px 12px}
651
+ .nwChip{height:24px;padding:0 10px;border-radius:var(--dsw-radius-sm);border:0.5px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px;cursor:pointer;font-family:inherit;transition:background .16s var(--nw-ease),box-shadow .2s var(--nw-ease)}
652
+ .nwChip:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary);font-family:inherit}
653
+ .nwTextInput{height:32px;box-sizing:border-box;padding:0 12px;border:0.5px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font-size:13px;line-height:20px;font-family:inherit}
654
+ .nwTextArea{box-sizing:border-box;min-height:56px;padding:10px 12px;border:0.5px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font-size:13px;line-height:20px;font-family:inherit}
655
+ .nwSelect{height:32px;padding:0 10px;border:0.5px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font-size:13px;line-height:20px;font-family:inherit}
656
+ .nwNote{font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary)}
657
+ .nwMuted{font-size:13px;line-height:20px;color:var(--dsw-alias-label-secondary)}
658
+ .nwSignOk{color:var(--dsw-alias-state-success-primary)}
659
+ .nwSignBad{color:var(--dsw-alias-label-error)}
660
+ .nwGutter{font-size:13px;line-height:18px;color:var(--dsw-alias-label-tertiary);text-align:center}
661
+ .nwDashedNote{margin-top:10px;padding:10px 12px;border:1px dashed var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1)}
662
+ .nwDividerRow{border-bottom:0.5px solid var(--dsw-alias-border-l2)}
663
+ .nwSettingsCard{border-radius:var(--dsw-radius-xl);border:0.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-layer-2);padding:14px 16px}
664
+ .nwSettingsCardTitle{font-size:14px;line-height:22px;font-weight:600;color:var(--dsw-alias-label-primary)}
665
+ .nwReportPre{font-size:12px;line-height:20px;color:var(--dsw-alias-label-secondary)}
666
+ .nwGroupName{font-size:12px;line-height:18px;font-weight:600;color:var(--dsw-alias-label-secondary)}
667
+ .nwFileRow{display:flex;align-items:center;gap:8px;width:100%;box-sizing:border-box;padding:7px 10px;cursor:pointer;font-family:inherit}
668
+ .nwFileLabel{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px;line-height:18px;color:var(--dsw-alias-label-primary)}
669
+ .nwStatRow{display:flex;align-items:center;gap:8px;font-size:11px;line-height:16px}
670
+ .nwStatName{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-primary)}
671
+ .nwChevron{font-size:13px;line-height:18px}
672
+ .nwFieldLabel{display:flex;align-items:center;gap:6px;font-size:13px;line-height:20px;font-weight:500;color:var(--dsw-alias-label-primary)}
673
+ .nwWarnBanner{border-radius:var(--dsw-radius-sm);border:1px solid var(--dsw-alias-state-warn-primary);background:var(--dsw-alias-state-warn-tertiary);color:var(--dsw-alias-state-warn-label);font-size:11px;line-height:16px}
674
+ .nwNoteAccent{color:var(--dsw-alias-state-business-primary)}
675
+ .nwNoteWarn{color:var(--dsw-alias-state-warn-label)}
676
+ .nwNoteOk{color:var(--dsw-alias-state-success-primary)}
677
+ .nwNoteDim{color:var(--dsw-alias-label-dimmed)}
678
+ .nwAccentStrong{font-size:13px;line-height:20px;font-weight:600;color:var(--dsw-alias-state-business-primary)}
679
+ .nwTolSign{width:12px;flex:none;text-align:center;font-size:14px;line-height:20px;font-weight:600}
680
+ .nwTolSign.nwSignOk{color:var(--dsw-alias-state-success-primary)}
681
+ .nwTolSign.nwSignBad{color:var(--dsw-alias-label-error)}
682
+ .nwInputErr{border-color:var(--dsw-alias-state-error-primary);box-shadow:0 0 0 3px var(--dsw-alias-interactive-bg-hover-danger)}
683
+ .nwCreationInput{width:100%;box-sizing:border-box;resize:vertical}
684
+ .nwPlotBoxEmpty{color:var(--dsw-alias-label-tertiary)}
685
+
686
+ .nwToolGroupHeadOpen{color:var(--dsw-alias-label-primary)}
687
+ .nwTextInput:focus-visible,.nwTextArea:focus-visible,.nwSelect:focus-visible,.nwTolInput:focus-visible,.nwModalInput:focus-visible{outline:var(--dsw-focus-ring-width,2px) solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:1px}
688
+
689
+ /* ===== v5.5.0:内联样式收编层(总负责人所有,拼在 A 的 CSS 之后)===== */
690
+ /* 这些类承接原先写在 React 内联 style 里的**外观**(色值/圆角/字号/描边),
691
+ 使外观全部由 CSS + 官方语义 token 掌握:深浅主题自动跟随,不再有硬编码色值。
692
+ 只放 A 的清单里没有的类;A 已提供 nwBoxed / nwSubCard / nwChip / nwTextInput /
693
+ nwTextArea / nwSelect / nwNote / nwMuted / nwSignOk / nwSignBad / nwGutter /
694
+ nwDashedNote / nwDividerRow 的外观,这里不重复定义。 */
695
+ /* 阈值行里的 +/- 记号:几何由这里给,颜色由 A 的 nwSignOk / nwSignBad 给 */
696
+ /* 新建书名输入框的错误态:由 JS 加类(替代原先 ev.target.style 写死色值) */
697
+ /* 输入类控件的键盘焦点:官方 focus.css 的全局兜底之外,再显式声明一次 */
698
+ /* 设置页「插件卡」:官方设置卡片材质(R20 + 0.5px 发丝描边 + 卡片底色别名) */
699
+
700
+ /* ===== 方案 B(官网式重构):hero 头部 / 分区 kicker / 图标卡 / 状态 pill ===== */
701
+ .nwHero{display:flex;flex-direction:column;gap:6px;border-radius:var(--dsw-radius-xl);padding:18px 18px 16px;border:0.5px solid var(--dsw-alias-border-l4);background-color:var(--dsw-alias-bg-layer-2);background-image:radial-gradient(340px 120px at top center,color-mix(in srgb,var(--dsw-alias-state-business-primary) 12%,var(--dsw-alias-bg-layer-2)) 0%,var(--dsw-alias-bg-layer-2) 72%);box-shadow:none}
702
+ .nwHero .nwBanner{background:transparent;border-color:transparent;padding:2px 0;box-shadow:none;letter-spacing:-.01em}
703
+ .nwHero .nwBannerOn,.nwHero .nwBannerOff{background:transparent;border-color:transparent}
704
+ .nwHeroDesc{font-size:12.5px;line-height:18px;color:var(--dsw-alias-label-tertiary)}
705
+ .nwSection{display:flex;flex-direction:column;gap:12px;margin-top:16px}
706
+ .nwKickerRow{display:flex;align-items:center;gap:10px}
707
+ .nwKicker{display:inline-flex;align-items:center;gap:6px;padding:3px 12px;border-radius:999px;corner-shape:round;background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 8%,var(--dsw-alias-bg-layer-1));border:0.5px solid color-mix(in srgb,var(--dsw-alias-state-business-primary) 16%,var(--dsw-alias-bg-layer-1));color:var(--dsw-alias-state-business-primary);font-size:12px;line-height:17px;font-weight:700;letter-spacing:.04em;flex:none}
708
+ .nwKickerRule{flex:1;min-width:0;height:1px;background:var(--dsw-alias-border-l2)}
709
+ .nwIconSlot{flex:none;width:34px;height:34px;display:flex;align-items:center;justify-content:center;border-radius:var(--dsw-radius-md);background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 8%,var(--dsw-alias-bg-layer-1));border:0.5px solid color-mix(in srgb,var(--dsw-alias-state-business-primary) 16%,var(--dsw-alias-bg-layer-1))}
710
+ .nwIconGlyph{font-size:15px;line-height:20px;color:var(--dsw-alias-state-business-primary)}
711
+ .nwPillRow{display:flex;flex-wrap:wrap;gap:8px}
712
+ .nwPill{display:inline-flex;align-items:baseline;gap:8px;padding:7px 16px;border-radius:999px;corner-shape:round;background:var(--dsw-alias-bg-layer-2);transition:background .16s var(--nw-ease)}
713
+ .nwPillKey{font-size:12.5px;line-height:18px;color:var(--dsw-alias-label-tertiary)}
714
+ .nwPillVal{font-size:13px;line-height:18px;font-weight:650;color:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums}
715
+ /* 微交互总门:减少动态效果时关掉 hover 抬升与过渡(模态入场动画原本就在 no-preference 门内) */
716
+ @media (prefers-reduced-motion: reduce){
717
+ .nwRow,.nwNavEntry,.nwToolRow,.nwBtn,.nwChip,.nwPill,.nwSwitch,.nwSwitchKnob,.nwSegBtn{transition:none!important}
718
+ .nwRow:hover,.nwNavEntry:hover,.nwToolRow:hover,.nwBtnPrimary:hover,.nwBtnGhost:hover{transform:none!important}
719
+ .nwBtnPrimary:hover{filter:none!important}
720
+ }
187
721
  `;
188
722
  // v3.0.0-UI3:已存在旧 style 标签也覆盖内容——否则插件脚本重复执行时新 CSS 永不注入(UI 停留在旧版)
189
723
  if (typeof document !== "undefined") {
@@ -209,6 +743,10 @@ var css = `
209
743
  "panel.autoAnalyze": "分析作品时自动使用",
210
744
  "panel.autoAnalyzeHint": "开启后,AI 在分析作品时会主动附带句式分析;关闭则仅在用户明确要求时使用。",
211
745
  "panel.spMode": "系统提示词",
746
+ "panel.secSwitch": "开关",
747
+ "panel.secPrompt": "提示词",
748
+ "panel.secStatus": "状态",
749
+ "panel.heroDesc": "18 个工具 · 6 维文笔基线 · 全本地推理,正文不外传",
212
750
  "panel.spModeHint": "控制插件往系统提示词里注入多少内容:关闭=完全不注入;精简=一句话(推荐);完整=完整工作流说明。改完下一轮对话生效。",
213
751
  "sp.off": "关闭",
214
752
  "sp.brief": "精简",
@@ -425,6 +963,10 @@ var css = `
425
963
  "panel.autoAnalyze": "Auto-use when analyzing works",
426
964
  "panel.autoAnalyzeHint": "When on, the AI proactively includes sentence analysis; otherwise only on explicit request.",
427
965
  "panel.spMode": "System prompt",
966
+ "panel.secSwitch": "Switch",
967
+ "panel.secPrompt": "Prompts",
968
+ "panel.secStatus": "Status",
969
+ "panel.heroDesc": "18 tools · 6-dimension style baseline · fully local — text never leaves your machine",
428
970
  "panel.spModeHint": "How much the plugin injects into the system prompt: Off = nothing; Brief = one line (recommended); Full = the complete workflow. Applies from the next turn.",
429
971
  "sp.off": "Off",
430
972
  "sp.brief": "Brief",
@@ -1049,19 +1591,19 @@ var css = `
1049
1591
  // v4.3.0:同上,删掉永不匹配的 [data-pane="conversation"](真实标记是 …_centerCol)
1050
1592
  return document.querySelector('[class*="centerCol"]') ?? void 0;
1051
1593
  }
1594
+ /**
1595
+ * v5.5.0:开关交给官方 primitives(Switch 36×20,关闭态滑块读 --dsw-alias-switch-thumb),
1596
+ * 取不到官方包时由 NW_UI 内部回退到同一套 token 样式的本地实现(见「官方原语适配层」)。
1597
+ * 保留这个局部名字,6 个调用点无需改动;label 是无障碍名称,由调用点传入。
1598
+ */
1052
1599
  function Switch(props) {
1053
- return react.createElement(
1054
- "button",
1055
- {
1056
- type: "button",
1057
- role: "switch",
1058
- "aria-checked": props.checked ? "true" : "false",
1059
- disabled: props.disabled,
1060
- className: "nwSwitch" + (props.checked ? " nwSwitchOn" : ""),
1061
- onClick: function () { props.onChange(!props.checked); }
1062
- },
1063
- react.createElement("span", { className: "nwSwitchKnob" })
1064
- );
1600
+ // 必须用 createElement 变成子组件:官方 Switch 内部用 hook,直接调用会把 hook 混进本组件的 hook 链
1601
+ return react.createElement(NW_UI.Switch, {
1602
+ checked: props.checked,
1603
+ disabled: props.disabled,
1604
+ onChange: props.onChange,
1605
+ label: props.label || ""
1606
+ });
1065
1607
  }
1066
1608
  function PanelView(props) {
1067
1609
  // v3.7.0 高1:isEn 声明在视图作用域(此前只在 rawSetPromise 内部,基线视图引用 ReferenceError 必崩)
@@ -1085,6 +1627,7 @@ var css = `
1085
1627
  var loadingHint = state.loading ? el("div", { className: "nwStatus" }, "⟳ " + t("panel.loading")) : null;
1086
1628
  var entry = function (title, hint, count, viewName) {
1087
1629
  return el("button", { type: "button", className: "nwNavEntry", onClick: function () { props.controller.openView(viewName); } },
1630
+ el("div", { className: "nwIconSlot" }, el("span", { className: "nwIconGlyph" }, viewName === "features" ? "⚙" : viewName === "tools" ? "🛠" : viewName === "creation" ? "📖" : viewName === "baseline" ? "🎯" : "▸")),
1088
1631
  el("div", { className: "nwNavEntryText" },
1089
1632
  el("div", { className: "nwNavEntryTitle" }, title),
1090
1633
  el("div", { className: "nwNavEntryHint" }, hint)
@@ -1108,15 +1651,14 @@ var css = `
1108
1651
  };
1109
1652
  var switchRow = function (name, fOn, onToggle, extra) {
1110
1653
  return el("div", { className: "nwToolRow" + (fOn ? " nwToolRowOn" : ""), key: name },
1654
+ el("div", { className: "nwIconSlot" }, el("span", { className: "nwIconGlyph" }, name === "emotionCaveat" ? "💧" : name === "emotionComplexity" ? "🎭" : name === "genreTheme" ? "🏷" : name === "webnovelVibe" ? "📡" : name === "semanticEmbedding" ? "🧠" : name === "semanticSearch" ? "🔎" : name === "semanticStyle" ? "🖌" : name === "semanticImplicit" ? "👁" : "⚙")),
1111
1655
  el("div", { className: "nwToolLabel" },
1112
1656
  el("div", null, t("feature." + name)),
1113
1657
  el("div", { className: "nwToolDesc" }, t("feature." + name + ".desc")),
1114
1658
  extra || null
1115
1659
  ),
1116
- el("button", { type: "button", role: "switch", "aria-checked": fOn ? "true" : "false", className: "nwSwitchSmall" + (fOn ? " nwSwitchSmallOn" : ""), disabled: state.loading,
1117
- onClick: function () { onToggle(name, !fOn); } },
1118
- el("span", { className: "nwSwitchSmallKnob" })
1119
- )
1660
+ el(Switch, { checked: fOn, disabled: state.loading, label: String(t("feature." + name)),
1661
+ onChange: function () { onToggle(name, !fOn); } })
1120
1662
  );
1121
1663
  };
1122
1664
  function formatSize(bytes) {
@@ -1205,14 +1747,13 @@ var TOOL_GROUPS = [
1205
1747
  else if (name === "novel_sentence_analysis") pathExtra = pathBox(t("dir.analysis"), dirs.analysisDir || "", "analysis-dir");
1206
1748
  return el("div", { key: name, className: "nwToolItem" },
1207
1749
  el("div", { className: "nwToolRow" + (checked ? " nwToolRowOn" : "") },
1750
+ el("div", { className: "nwIconSlot" }, el("span", { className: "nwIconGlyph" }, "🧩")),
1208
1751
  el("div", { className: "nwToolLabel" },
1209
1752
  el("div", null, String(t("tool." + name)).replace(/\s+novel_\w+$/, "")),
1210
1753
  el("div", { className: "nwToolDesc" }, desc)
1211
1754
  ),
1212
- el("button", { type: "button", role: "switch", "aria-checked": checked ? "true" : "false", className: "nwSwitchSmall" + (checked ? " nwSwitchSmallOn" : ""), disabled: state.loading,
1213
- onClick: function () { props.toggle({ tools: (function (patch) { patch[name] = !checked; return patch; })({}) }); } },
1214
- el("span", { className: "nwSwitchSmallKnob" })
1215
- )
1755
+ el(Switch, { checked: checked, disabled: state.loading, label: String(t("tool." + name)).replace(/\s+novel_\w+$/, ""),
1756
+ onChange: function () { props.toggle({ tools: (function (patch) { patch[name] = !checked; return patch; })({}) }); } })
1216
1757
  ),
1217
1758
  pathExtra || null
1218
1759
  );
@@ -1343,34 +1884,34 @@ var TOOL_GROUPS = [
1343
1884
  el("div", { className: "nwSectionTitle" }, "🎯 " + t("panel.baselineTitle")),
1344
1885
  el("div", { className: "nwToolsHint" }, t("baseline.desc")),
1345
1886
  el("div", { className: "nwTolVer" }, "v3.0.0-UI3"),
1346
- el("div", { className: "nwTolCard", style: { background: "#fff", border: "1px solid #e2e8f0", borderRadius: "12px", padding: "4px 14px 8px", margin: "8px 0 4px" } },
1887
+ el("div", { className: "nwTolCard" },
1347
1888
  NW_METRICS.map(function (item) {
1348
1889
  var mk2 = item[0];
1349
1890
  var d = draft[mk2] || DEFAULT_TOL;
1350
1891
  return el("div", { className: "nwTolRow", key: mk2, style: { display: "flex", alignItems: "center", gap: "10px", padding: "9px 2px" } },
1351
- el("div", { className: "nwTolName", style: { flex: "1", minWidth: "0", display: "flex", alignItems: "center", gap: "8px", fontSize: "13px", fontWeight: "500", whiteSpace: "nowrap", color: "#334155" } }, el("span", { className: "nwTolIcon", style: { fontSize: "15px" } }, item[2]), isEn ? (item[3] || item[1]) : item[1]),
1892
+ el("div", { className: "nwTolName" }, el("span", { className: "nwTolIcon" }, item[2]), isEn ? (item[3] || item[1]) : item[1]),
1352
1893
  el("div", { className: "nwTolField", style: { width: "74px", flexShrink: "0", display: "flex", flexDirection: "column", gap: "3px" } },
1353
1894
  el("div", { style: { display: "flex", alignItems: "center", gap: "4px" } },
1354
- el("span", { className: "nwTolSign", style: { width: "12px", flexShrink: "0", textAlign: "center", fontSize: "14px", color: "#ef4444", fontWeight: "600" } }, "−"),
1355
- el("input", { type: "number", className: "nwTolInput", placeholder: t("tol.recommend"), value: d.low, min: 0, max: 99, disabled: state.loading, style: { width: "100%", height: "30px", boxSizing: "border-box", border: "1px solid #d4d4d8", borderRadius: "8px", fontSize: "13px", textAlign: "center", background: "#fafafa", color: "#18181b", outline: "none", MozAppearance: "textfield" }, onChange: function (ev) { setDraft(mk2, "low", ev.target.value); } })
1895
+ el("span", { className: "nwTolSign" }, "−"),
1896
+ el("input", { type: "number", className: "nwTolInput", placeholder: t("tol.recommend"), value: d.low, min: 0, max: 99, disabled: state.loading, onChange: function (ev) { setDraft(mk2, "low", ev.target.value); } })
1356
1897
  ),
1357
- el("span", { className: "nwTolCaption", style: { fontSize: "10px", color: "#94a3b8", textAlign: "center" } }, t("baseline.low"))
1898
+ el("span", { className: "nwTolCaption" }, t("baseline.low"))
1358
1899
  ),
1359
- el("span", { className: "nwTolSep", style: { width: "14px", flexShrink: "0", fontSize: "12px", color: "#b0b8c4", textAlign: "center" } }, "~"),
1900
+ el("span", { className: "nwTolSep" }, "~"),
1360
1901
  el("div", { className: "nwTolField", style: { width: "74px", flexShrink: "0", display: "flex", flexDirection: "column", gap: "3px" } },
1361
1902
  el("div", { style: { display: "flex", alignItems: "center", gap: "4px" } },
1362
- el("span", { className: "nwTolSign", style: { width: "12px", flexShrink: "0", textAlign: "center", fontSize: "14px", color: "#10b981", fontWeight: "600" } }, "+"),
1363
- el("input", { type: "number", className: "nwTolInput", placeholder: t("tol.recommend"), value: d.high, min: 0, max: 99, disabled: state.loading, style: { width: "100%", height: "30px", boxSizing: "border-box", border: "1px solid #d4d4d8", borderRadius: "8px", fontSize: "13px", textAlign: "center", background: "#fafafa", color: "#18181b", outline: "none", MozAppearance: "textfield" }, onChange: function (ev) { setDraft(mk2, "high", ev.target.value); } })
1903
+ el("span", { className: "nwTolSign" }, "+"),
1904
+ el("input", { type: "number", className: "nwTolInput", placeholder: t("tol.recommend"), value: d.high, min: 0, max: 99, disabled: state.loading, onChange: function (ev) { setDraft(mk2, "high", ev.target.value); } })
1364
1905
  ),
1365
- el("span", { className: "nwTolCaption", style: { fontSize: "10px", color: "#94a3b8", textAlign: "center" } }, t("baseline.high"))
1906
+ el("span", { className: "nwTolCaption" }, t("baseline.high"))
1366
1907
  ),
1367
- el("span", { className: "nwTolPct", style: { width: "14px", flexShrink: "0", fontSize: "13px", color: "#64748b" } }, "%")
1908
+ el("span", { className: "nwTolPct" }, "%")
1368
1909
  );
1369
1910
  })
1370
1911
  ),
1371
1912
  el("div", { className: "nwTolBtns", style: { display: "flex", gap: "12px", marginTop: "16px" } },
1372
- el("button", { type: "button", className: "nwBtn nwBtnPrimary" + (state.baselineSaved ? " nwBtnDone" : ""), disabled: state.loading, onClick: saveTol, style: state.baselineSaved ? { background: "linear-gradient(135deg,#10b981,#34d399)", border: "none", color: "#fff", fontWeight: "600", fontSize: "13px", padding: "9px 20px", borderRadius: "10px", cursor: "pointer" } : { background: "linear-gradient(135deg,#6366f1,#8b5cf6)", border: "none", color: "#fff", fontWeight: "600", fontSize: "13px", padding: "9px 20px", borderRadius: "10px", cursor: "pointer" } }, state.baselineSaved ? "✓ " + t("baseline.saved") : "💾 " + t("baseline.save")),
1373
- el("button", { type: "button", className: "nwBtn nwBtnGhost", disabled: state.loading, onClick: resetTol, style: { background: "transparent", border: "1px solid #cbd5e1", color: "#475569", fontSize: "13px", padding: "9px 18px", borderRadius: "10px", cursor: "pointer" } }, "↺ " + t("baseline.reset"))
1913
+ el("button", { type: "button", className: "nwBtn nwBtnPrimary" + (state.baselineSaved ? " nwBtnDone" : ""), disabled: state.loading, onClick: saveTol }, state.baselineSaved ? "✓ " + t("baseline.saved") : "💾 " + t("baseline.save")),
1914
+ el("button", { type: "button", className: "nwBtn nwBtnGhost", disabled: state.loading, onClick: resetTol }, "↺ " + t("baseline.reset"))
1374
1915
  )
1375
1916
  );
1376
1917
  } else if (view === "reports") {
@@ -1399,18 +1940,18 @@ var TOOL_GROUPS = [
1399
1940
  el("div", { className: "nwToolsHint" }, t("reports.hint")),
1400
1941
  state.reportContent ?
1401
1942
  el("div", null,
1402
- el("button", { type: "button", style: { margin: "6px 0", padding: "4px 10px", borderRadius: "6px", border: "1px solid #cbd5e1", background: "#fff", fontSize: "12px", cursor: "pointer", color: "#475569" }, onClick: function () { props.controller.set({ reportContent: null }); } }, "‹ " + t("reports.back")),
1403
- el("pre", { style: { margin: "4px 0 0", padding: "10px", borderRadius: "8px", background: "#fff", border: "1px solid #e2e8f0", fontSize: "11px", lineHeight: "1.7", color: "#334155", whiteSpace: "pre-wrap", wordBreak: "break-all", maxHeight: "70vh", overflow: "auto" } }, state.reportContent)
1943
+ el("button", { type: "button", className: "nwChip", style: { margin: "6px 0" }, onClick: function () { props.controller.set({ reportContent: null }); } }, "‹ " + t("reports.back")),
1944
+ el("pre", { className: "nwBoxed nwReportPre", style: { margin: "4px 0 0", whiteSpace: "pre-wrap", wordBreak: "break-all", maxHeight: "70vh", overflow: "auto" } }, state.reportContent)
1404
1945
  )
1405
1946
  : el("div", { style: { display: "flex", flexDirection: "column", gap: "8px" } },
1406
- reportsLoading ? el("div", { style: { fontSize: "12px", color: "#94a3b8", padding: "10px 0" } }, t("reports.loading2")) : totalFiles === 0 ? el("div", { style: { fontSize: "12px", color: "#94a3b8", padding: "10px 0" } }, t("reports.empty")) : null,
1947
+ reportsLoading ? el("div", { className: "nwMuted", style: { padding: "10px 0" } }, t("reports.loading2")) : totalFiles === 0 ? el("div", { className: "nwMuted", style: { padding: "10px 0" } }, t("reports.empty")) : null,
1407
1948
  groups.map(function (g) {
1408
1949
  return el("div", { key: g.name },
1409
- el("div", { style: { fontSize: "12px", fontWeight: "600", color: "#475569", margin: "6px 0 4px" } }, g.name + "(" + (g.files || []).length + ")"),
1950
+ el("div", { className: "nwGroupName", style: { margin: "6px 0 4px" } }, g.name + "(" + (g.files || []).length + ")"),
1410
1951
  (g.files || []).map(function (f) {
1411
- return el("button", { key: f.file, type: "button", style: { display: "flex", alignItems: "center", width: "100%", padding: "7px 10px", marginBottom: "4px", borderRadius: "8px", border: "1px solid #e2e8f0", background: "#fff", cursor: "pointer", textAlign: "left" }, onClick: function () { openReport(f.file); } },
1412
- el("span", { style: { flex: "1", fontSize: "12px", color: "#334155", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, f.label),
1413
- el("span", { style: { fontSize: "10px", color: "#94a3b8" } }, (f.time || "").slice(0, 16).replace("T", " "))
1952
+ return el("button", { key: f.file, type: "button", className: "nwBoxed nwFileRow", style: { marginBottom: "4px", textAlign: "left" }, onClick: function () { openReport(f.file); } },
1953
+ el("span", { className: "nwFileLabel", }, f.label),
1954
+ el("span", { className: "nwNote", }, (f.time || "").slice(0, 16).replace("T", " "))
1414
1955
  );
1415
1956
  })
1416
1957
  );
@@ -1470,7 +2011,7 @@ var TOOL_GROUPS = [
1470
2011
  }
1471
2012
  if (!nb) {
1472
2013
  var elInp = document.getElementById("nwNewBookInput");
1473
- if (elInp) { elInp.focus(); elInp.style.borderColor = "#ef4444"; elInp.style.boxShadow = "0 0 0 3px rgba(239,68,68,.15)"; }
2014
+ if (elInp) { elInp.focus(); elInp.classList.add("nwInputErr"); }
1474
2015
  props.controller.set({ revealMsg: t("creation.needName"), revealErr: true }, { silent: true });
1475
2016
  return;
1476
2017
  }
@@ -1481,15 +2022,15 @@ var TOOL_GROUPS = [
1481
2022
  };
1482
2023
  function bookRow(name, profile, isDefault, hasProfile) {
1483
2024
  var filled = filledCount(profile);
1484
- return el("div", { key: "bk-" + name, style: { display: "flex", alignItems: "center", gap: "10px", padding: "10px 12px", borderRadius: "10px", border: "1px solid #e2e8f0", background: "#fff" } },
2025
+ return el("div", { key: "bk-" + name, className: "nwBoxed nwFileRow", style: { gap: "10px", padding: "10px 12px" } },
1485
2026
  el("div", { style: { flex: "1", minWidth: "0" } },
1486
- el("div", { style: { fontSize: "13px", fontWeight: "600", color: "#334155" } }, isDefault ? "🌐 " + t("creation.default") : "📕 " + name),
1487
- el("div", { style: { fontSize: "11px", color: filled > 0 ? "#6366f1" : "#94a3b8", marginTop: "2px" } }, summaryOf(profile)),
1488
- isDefault ? el("div", { style: { fontSize: "10px", color: "#cbd5e1", marginTop: "1px" } }, t("creation.defaultHint")) : null
2027
+ el("div", { className: "nwRowLabel", }, isDefault ? "🌐 " + t("creation.default") : "📕 " + name),
2028
+ el("div", { className: "nwNote" + (filled > 0 ? " nwNoteAccent" : ""), style: { marginTop: "2px" } }, summaryOf(profile)),
2029
+ isDefault ? el("div", { className: "nwNote", style: { marginTop: "1px" } }, t("creation.defaultHint")) : null
1489
2030
  ),
1490
- el("button", { type: "button", onClick: function () { props.controller.set({ creationBook: name, creationDraft: null, creationDirty: false }, { silent: true }); props.controller.openView("creation-form"); } }, t("creation.edit")),
2031
+ el("button", { type: "button", className: "nwChip", onClick: function () { props.controller.set({ creationBook: name, creationDraft: null, creationDirty: false }, { silent: true }); props.controller.openView("creation-form"); } }, t("creation.edit")),
1491
2032
  // v4.0.0 修正:书库里的书若没有设定条目,不渲染"删除"(此前点了空操作却提示"已删除")
1492
- (isDefault || !hasProfile) ? null : el("button", { type: "button", onClick: function () { var patch = {}; patch[name] = null; props.toggle({ creationProfiles: patch }); props.controller.set({ revealMsg: t("creation.deleted"), revealErr: false }, { silent: true }); } }, t("creation.delete"))
2033
+ (isDefault || !hasProfile) ? null : el("button", { type: "button", className: "nwChip", onClick: function () { var patch = {}; patch[name] = null; props.toggle({ creationProfiles: patch }); props.controller.set({ revealMsg: t("creation.deleted"), revealErr: false }, { silent: true }); } }, t("creation.delete"))
1493
2034
  );
1494
2035
  }
1495
2036
  body = el("div", null,
@@ -1498,8 +2039,8 @@ var TOOL_GROUPS = [
1498
2039
  el("div", { className: "nwToolsHint" }, t("creation.desc2")),
1499
2040
  // 新建设定
1500
2041
  el("div", { style: { display: "flex", gap: "8px", marginTop: "8px", marginBottom: "10px" } },
1501
- el("input", { id: "nwNewBookInput", type: "text", value: newBookName, placeholder: t("creation.newPlaceholder"), style: { flex: "1", height: "34px", boxSizing: "border-box", padding: "0 12px", border: "1px solid #d4d4d8", borderRadius: "10px", fontSize: "13px", background: "#fff", color: "#18181b", outline: "none", transition: "border-color .15s ease, box-shadow .15s ease" }, onInput: function (ev) { props.controller.set({ creationNewBook: ev.target.value }, { silent: true }); }, onFocus: function (ev) { ev.target.style.borderColor = "#6366f1"; ev.target.style.boxShadow = "0 0 0 3px rgba(99,102,241,.14)"; }, onBlur: function (ev) { ev.target.style.borderColor = "#d4d4d8"; ev.target.style.boxShadow = "none"; }, onKeyDown: function (ev) { if (ev.key === "Enter") { ev.preventDefault(); createNewBook(); } } }),
1502
- el("button", { type: "button", style: { height: "34px", padding: "0 16px", borderRadius: "10px", border: "none", background: "linear-gradient(135deg,#6366f1,#8b5cf6)", color: "#fff", fontWeight: "600", fontSize: "13px", cursor: "pointer" }, onClick: createNewBook }, t("creation.new"))
2042
+ el("input", { id: "nwNewBookInput", type: "text", value: newBookName, placeholder: t("creation.newPlaceholder"), className: "nwTextInput", style: { flex: "1", height: "34px" }, onInput: function (ev) { props.controller.set({ creationNewBook: ev.target.value }, { silent: true }); }, onKeyDown: function (ev) { if (ev.key === "Enter") { ev.preventDefault(); createNewBook(); } } }),
2043
+ el("button", { type: "button", className: "nwBtn nwBtnPrimary", style: { height: "34px", padding: "0 16px" }, onClick: createNewBook }, t("creation.new"))
1503
2044
  ),
1504
2045
  el("div", { style: { display: "flex", flexDirection: "column", gap: "8px" } },
1505
2046
  bookRow("", cpGlobal, true),
@@ -1572,21 +2113,21 @@ var TOOL_GROUPS = [
1572
2113
  el("div", { className: "nwSectionTitle" }, "🎨 " + (curBook ? curBook : t("creation.default")) + " · " + t("creation.formTitle")),
1573
2114
  // 快速切换
1574
2115
  el("div", { style: { display: "flex", alignItems: "center", gap: "8px", margin: "4px 0 10px" } },
1575
- el("span", { style: { fontSize: "12px", color: "#64748b" } }, t("creation.bookFor")),
1576
- el("select", { value: curBook, style: { height: "32px", padding: "0 10px", borderRadius: "8px", border: "1px solid #d4d4d8", background: "#fff", color: "#18181b", fontSize: "13px", outline: "none" }, onChange: function (ev) { switchBook(ev.target.value); } }, switchOpts)
2116
+ el("span", { className: "nwMuted", }, t("creation.bookFor")),
2117
+ el("select", { className: "nwSelect", value: curBook, onChange: function (ev) { switchBook(ev.target.value); } }, switchOpts)
1577
2118
  ),
1578
2119
  el("div", { style: { display: "flex", flexDirection: "column", gap: "10px" } },
1579
2120
  CREATION_FIELDS.map(function (item) {
1580
2121
  var cf4 = item[0];
1581
2122
  return el("div", { key: cf4, style: { display: "flex", flexDirection: "column", gap: "4px" } },
1582
- el("div", { style: { fontSize: "13px", fontWeight: "500", color: "#334155", display: "flex", alignItems: "center", gap: "6px" } }, item[1] + " " + item[2]),
1583
- el("textarea", { className: "nwCreationInput", value: formDraft[cf4] || "", rows: 2, placeholder: item[3], style: { width: "100%", boxSizing: "border-box", minHeight: "56px", padding: "10px 12px", border: "1px solid #d4d4d8", borderRadius: "10px", fontSize: "13px", fontFamily: "inherit", resize: "vertical", background: "#fff", color: "#18181b", outline: "none", lineHeight: "1.6", transition: "border-color .15s ease, box-shadow .15s ease, background .15s ease", boxShadow: "0 1px 2px rgba(15,23,42,.04)" }, onInput: function (ev) { setFormDraft(cf4, ev.target.value); }, onFocus: function (ev) { ev.target.style.borderColor = "#6366f1"; ev.target.style.boxShadow = "0 0 0 3px rgba(99,102,241,.14)"; }, onBlur: function (ev) { ev.target.style.borderColor = "#d4d4d8"; ev.target.style.boxShadow = "0 1px 2px rgba(15,23,42,.04)"; } })
2123
+ el("div", { className: "nwFieldLabel", }, item[1] + " " + item[2]),
2124
+ el("textarea", { className: "nwCreationInput nwTextArea", value: formDraft[cf4] || "", rows: 2, placeholder: item[3], style: { width: "100%", resize: "vertical" }, onInput: function (ev) { setFormDraft(cf4, ev.target.value); } })
1584
2125
  );
1585
2126
  })
1586
2127
  ),
1587
2128
  el("div", { style: { display: "flex", gap: "12px", marginTop: "14px" } },
1588
- el("button", { type: "button", className: "nwBtn", style: { background: "linear-gradient(135deg,#6366f1,#8b5cf6)", border: "none", color: "#fff", fontWeight: "600", fontSize: "13px", padding: "9px 20px", borderRadius: "10px", cursor: "pointer" }, onClick: saveForm }, "💾 " + t("creation.save")),
1589
- el("button", { type: "button", className: "nwBtn", style: { background: "transparent", border: "1px solid #cbd5e1", color: "#475569", fontSize: "13px", padding: "9px 18px", borderRadius: "10px", cursor: "pointer" }, onClick: clearForm }, "↺ " + t("creation.clear"))
2129
+ el("button", { type: "button", className: "nwBtn", style: { padding: "9px 20px" }, onClick: saveForm }, "💾 " + t("creation.save")),
2130
+ el("button", { type: "button", className: "nwBtn", style: { padding: "9px 18px" }, onClick: clearForm }, "↺ " + t("creation.clear"))
1590
2131
  )
1591
2132
  );
1592
2133
  } } else {
@@ -1615,7 +2156,7 @@ var TOOL_GROUPS = [
1615
2156
  }
1616
2157
  body = el("div", null,
1617
2158
  // v3.5.0 #37:保存失败/宿主不可达提示条
1618
- (state.saveFailed || state.hostOk === false) ? el("div", { key: "saveBanner", style: { padding: "7px 10px", marginBottom: "8px", borderRadius: "8px", background: "#fef3c7", border: "1px solid #fcd34d", color: "#92400e", fontSize: "11px", lineHeight: "1.5" } }, state.hostOk === false ? t("panel.localOnly") : t("panel.saveFailed")) : null,
2159
+ (state.saveFailed || state.hostOk === false) ? el("div", { key: "saveBanner", className: "nwWarnBanner", style: { padding: "7px 10px", marginBottom: "8px" } }, state.hostOk === false ? t("panel.localOnly") : t("panel.saveFailed")) : null,
1619
2160
  // v2.6.5:更新提示条(有新版才显示,点击跳 GitHub Release)
1620
2161
  // v4.3.0:ok === false 时不显示该条——避免用"上次检查到的版本"冒充本次结果
1621
2162
  updateCheck && !updateFailed && updateCheck.updateAvailable && updateCheck.releaseUrl
@@ -1627,124 +2168,151 @@ var TOOL_GROUPS = [
1627
2168
  updateFailed
1628
2169
  ? el("div", { key: "updateStale", className: "nwUpdateStale" }, updateFailedText)
1629
2170
  : null,
1630
- el("div", { className: "nwRow " + (state.enabled ? "nwRowOn" : "nwRowOff") },
1631
- el("div", { className: "nwRowText" },
1632
- el("div", { className: "nwRowLabel" }, t("panel.enabled")),
1633
- el("div", { className: "nwRowHint" }, t("panel.enabledHint"))
2171
+ el("div", { className: "nwSection" },
2172
+ el("div", { className: "nwKickerRow" },
2173
+ el("div", { className: "nwKicker" }, t("panel.secSwitch")),
2174
+ el("div", { className: "nwKickerRule" })
1634
2175
  ),
1635
- el("div", { className: "nwSwitchWrap" },
1636
- el(Switch, { checked: state.enabled, disabled: state.loading, onChange: function (value) { props.toggle({ enabled: value }); } }),
1637
- el("span", { className: "nwBadge " + (state.enabled ? "nwBadgeOn" : "nwBadgeOff") }, state.enabled ? t("badge.on") : t("badge.off"))
1638
- )
1639
- ),
1640
- el("div", { className: "nwRow " + (state.autoAnalyze ? "nwRowOn" : "nwRowOff") },
1641
- el("div", { className: "nwRowText" },
1642
- el("div", { className: "nwRowLabel" }, t("panel.autoAnalyze")),
1643
- el("div", { className: "nwRowHint" }, t("panel.autoAnalyzeHint"))
2176
+ el("div", { className: "nwRow " + (state.enabled ? "nwRowOn" : "nwRowOff") },
2177
+ el("div", { className: "nwIconSlot" }, el("span", { className: "nwIconGlyph" }, "🎚")),
2178
+ el("div", { className: "nwRowText" },
2179
+ el("div", { className: "nwRowLabel" }, t("panel.enabled")),
2180
+ el("div", { className: "nwRowHint" }, t("panel.enabledHint"))
2181
+ ),
2182
+ el("div", { className: "nwSwitchWrap" },
2183
+ el(Switch, { checked: state.enabled, disabled: state.loading, label: t("panel.enabled"), onChange: function (value) { props.toggle({ enabled: value }); } }),
2184
+ el("span", { className: "nwBadge " + (state.enabled ? "nwBadgeOn" : "nwBadgeOff") }, state.enabled ? t("badge.on") : t("badge.off"))
2185
+ )
2186
+ ),
2187
+ el("div", { className: "nwRow " + (state.autoAnalyze ? "nwRowOn" : "nwRowOff") },
2188
+ el("div", { className: "nwIconSlot" }, el("span", { className: "nwIconGlyph" }, "📊")),
2189
+ el("div", { className: "nwRowText" },
2190
+ el("div", { className: "nwRowLabel" }, t("panel.autoAnalyze")),
2191
+ el("div", { className: "nwRowHint" }, t("panel.autoAnalyzeHint"))
2192
+ ),
2193
+ el("div", { className: "nwSwitchWrap" },
2194
+ el(Switch, { checked: state.autoAnalyze, disabled: state.loading, label: t("panel.autoAnalyze"), onChange: function (value) { props.toggle({ autoAnalyze: value }); } }),
2195
+ el("span", { className: "nwBadge " + (state.autoAnalyze ? "nwBadgeOn" : "nwBadgeOff") }, state.autoAnalyze ? t("badge.on") : t("badge.off"))
2196
+ )
2197
+ ),
2198
+ // v5.0.0:精简工作流——全局开关,与相邻开关同一套 switch/badge 组件。
2199
+ // B2 重排:由「提示词档位 / 提示词场景」两行之下移入「开关」分区——与「提示词场景」行的区别:它不受"仅完整档生效"限制(不给 disabled 逻辑),任何时候都可切换;
2200
+ // 打开(true)= 提示词换"按需调用"最简档,关闭(false,默认)= 维持 5.0.0 的自检/收尾/复测清单。
2201
+ el("div", { className: "nwRow " + (state.leanWorkflow ? "nwRowOn" : "nwRowOff") },
2202
+ el("div", { className: "nwIconSlot" }, el("span", { className: "nwIconGlyph" }, "⚡")),
2203
+ el("div", { className: "nwRowText" },
2204
+ el("div", { className: "nwRowLabel" }, t("panel.leanWorkflow")),
2205
+ el("div", { className: "nwRowHint" }, t("panel.leanWorkflowHint"))
2206
+ ),
2207
+ el("div", { className: "nwSwitchWrap" },
2208
+ el(Switch, { checked: state.leanWorkflow === true, disabled: state.loading, label: t("panel.leanWorkflow"), onChange: function (value) { props.toggle({ leanWorkflow: value }); } }),
2209
+ el("span", { className: "nwBadge " + (state.leanWorkflow ? "nwBadgeOn" : "nwBadgeOff") }, state.leanWorkflow ? t("badge.on") : t("badge.off"))
2210
+ )
1644
2211
  ),
1645
- el("div", { className: "nwSwitchWrap" },
1646
- el(Switch, { checked: state.autoAnalyze, disabled: state.loading, onChange: function (value) { props.toggle({ autoAnalyze: value }); } }),
1647
- el("span", { className: "nwBadge " + (state.autoAnalyze ? "nwBadgeOn" : "nwBadgeOff") }, state.autoAnalyze ? t("badge.on") : t("badge.off"))
1648
- )
1649
2212
  ),
1650
- // v4.0.0:系统提示词注入档位(关闭 / 精简 / 完整)——三档分段按钮,沿用 dsh-unrestricted 的开关思路
1651
- el("div", { className: "nwRow " + (state.systemPromptMode !== "off" ? "nwRowOn" : "nwRowOff") },
1652
- el("div", { className: "nwRowText" },
1653
- el("div", { className: "nwRowLabel" }, t("panel.spMode")),
1654
- el("div", { className: "nwRowHint" }, t("panel.spModeHint"))
2213
+ el("div", { className: "nwSection" },
2214
+ el("div", { className: "nwKickerRow" },
2215
+ el("div", { className: "nwKicker" }, t("panel.secPrompt")),
2216
+ el("div", { className: "nwKickerRule" })
1655
2217
  ),
1656
- el("div", { className: "nwSeg" },
1657
- ["off", "brief", "full"].map(function (m) {
1658
- return el("button", { key: m, type: "button", className: "nwSegBtn" + (state.systemPromptMode === m ? " nwSegBtnOn" : ""), disabled: state.loading,
1659
- onClick: function () { if (state.systemPromptMode !== m) props.toggle({ systemPromptMode: m }); } }, t("sp." + m));
2218
+ // v4.0.0:系统提示词注入档位(关闭 / 精简 / 完整)——三档分段按钮,沿用 dsh-unrestricted 的开关思路
2219
+ el("div", { className: "nwRow " + (state.systemPromptMode !== "off" ? "nwRowOn" : "nwRowOff") },
2220
+ el("div", { className: "nwIconSlot" }, el("span", { className: "nwIconGlyph" }, "📝")),
2221
+ el("div", { className: "nwRowText" },
2222
+ el("div", { className: "nwRowLabel" }, t("panel.spMode")),
2223
+ el("div", { className: "nwRowHint" }, t("panel.spModeHint"))
2224
+ ),
2225
+ el(NW_UI.SegmentedControl, {
2226
+ label: t("panel.spMode"),
2227
+ value: state.systemPromptMode,
2228
+ disabled: state.loading,
2229
+ options: ["off", "brief", "full"].map(function (m) { return { value: m, label: t("sp." + m) }; }),
2230
+ onChange: function (m) { if (state.systemPromptMode !== m) props.toggle({ systemPromptMode: m }); }
1660
2231
  })
1661
- )
1662
- ),
1663
- // v5.0.0:提示词场景——与上面的档位正交:档位决定"注入多少",场景决定"注入什么"。
1664
- // v5.1.1 修正:**不再因为档位非 full 就把按钮 disabled**——用户反馈"场景里点不了其他选项",
1665
- // 因为默认档位是 off/精简,五个按钮全灰且没有任何解释,等于把"选场景"锁死在一个看不见的前提后面。
1666
- // 现在按钮始终可点(只受 loading 限制),档位非 full 时改用行内提示说明"已保存但暂不注入"。
1667
- // 场景枚举必须与 lib/prompts.js 的 PROMPT_SCENES 保持一致(客户端是独立 bundle,不能 import)。
1668
- el("div", { className: "nwRow " + (state.systemPromptMode === "full" ? "nwRowOn" : "nwRowOff") },
1669
- el("div", { className: "nwRowText" },
1670
- el("div", { className: "nwRowLabel" }, t("panel.spScene")),
1671
- el("div", { className: "nwRowHint" }, t("panel.spSceneHint") + (state.systemPromptMode === "full" ? "" : " " + t("panel.spSceneInactive")))
1672
2232
  ),
1673
- el("div", { className: "nwSeg" },
1674
- ["general", "writing", "revising", "auditing", "setup"].map(function (s) {
1675
- var cur = state.promptScene || "general";
1676
- return el("button", { key: s, type: "button", className: "nwSegBtn" + (cur === s ? " nwSegBtnOn" : ""), disabled: state.loading,
1677
- onClick: function () { if (cur !== s) props.toggle({ promptScene: s }); } }, t("sc." + s));
2233
+ // v5.0.0:提示词场景——与上面的档位正交:档位决定"注入多少",场景决定"注入什么"。
2234
+ // v5.1.1 修正:**不再因为档位非 full 就把按钮 disabled**——用户反馈"场景里点不了其他选项",
2235
+ // 因为默认档位是 off/精简,五个按钮全灰且没有任何解释,等于把"选场景"锁死在一个看不见的前提后面。
2236
+ // 现在按钮始终可点(只受 loading 限制),档位非 full 时改用行内提示说明"已保存但暂不注入"。
2237
+ // 场景枚举必须与 lib/prompts.js 的 PROMPT_SCENES 保持一致(客户端是独立 bundle,不能 import)。
2238
+ el("div", { className: "nwRow " + (state.systemPromptMode === "full" ? "nwRowOn" : "nwRowOff") },
2239
+ el("div", { className: "nwIconSlot" }, el("span", { className: "nwIconGlyph" }, "🎛")),
2240
+ el("div", { className: "nwRowText" },
2241
+ el("div", { className: "nwRowLabel" }, t("panel.spScene")),
2242
+ el("div", { className: "nwRowHint" }, t("panel.spSceneHint") + (state.systemPromptMode === "full" ? "" : " " + t("panel.spSceneInactive")))
2243
+ ),
2244
+ el(NW_UI.SegmentedControl, {
2245
+ label: t("panel.spScene"),
2246
+ value: state.promptScene || "general",
2247
+ disabled: state.loading,
2248
+ options: ["general", "writing", "revising", "auditing", "setup"].map(function (s) { return { value: s, label: t("sc." + s) }; }),
2249
+ onChange: function (s) { if ((state.promptScene || "general") !== s) props.toggle({ promptScene: s }); }
1678
2250
  })
1679
- )
1680
- ),
1681
- // v5.0.0:精简工作流——全局开关,紧挨「提示词档位 / 提示词场景」两行之下、与相邻开关同一套 switch/badge 组件。
1682
- // 与上一条「提示词场景」的区别:它不受"仅完整档生效"限制(不给 disabled 逻辑),任何时候都可切换;
1683
- // 打开(true)= 提示词换"按需调用"最简档,关闭(false,默认)= 维持 5.0.0 的自检/收尾/复测清单。
1684
- el("div", { className: "nwRow " + (state.leanWorkflow ? "nwRowOn" : "nwRowOff") },
1685
- el("div", { className: "nwRowText" },
1686
- el("div", { className: "nwRowLabel" }, t("panel.leanWorkflow")),
1687
- el("div", { className: "nwRowHint" }, t("panel.leanWorkflowHint"))
1688
2251
  ),
1689
- el("div", { className: "nwSwitchWrap" },
1690
- el(Switch, { checked: state.leanWorkflow === true, disabled: state.loading, onChange: function (value) { props.toggle({ leanWorkflow: value }); } }),
1691
- el("span", { className: "nwBadge " + (state.leanWorkflow ? "nwBadgeOn" : "nwBadgeOff") }, state.leanWorkflow ? t("badge.on") : t("badge.off"))
1692
- )
2252
+ (function () {
2253
+ var rawOn = f.rawWriting === true;
2254
+ return el("div", { className: "nwToolRow" + (rawOn ? " nwToolRowOn" : " nwToolRowRaw"), key: "rawWriting" },
2255
+ el("div", { className: "nwIconSlot" }, el("span", { className: "nwIconGlyph" }, "⚠️")),
2256
+ el("div", { className: "nwToolLabel" },
2257
+ el("div", { className: "nwToolLabelRaw" }, "🔞 " + t("feature.rawWriting")),
2258
+ el("div", { className: "nwToolDesc nwRawDanger" }, t("raw.danger")),
2259
+ rawOn ? el("div", { className: "nwRawOn" }, t("raw.on")) : null
2260
+ ),
2261
+ el(Switch, { checked: rawOn, disabled: state.loading, label: "🔞 " + t("feature.rawWriting"),
2262
+ onChange: function () { props.controller.rawToggle(!rawOn); } })
2263
+ );
2264
+ })(),
1693
2265
  ),
1694
- (function () {
1695
- var rawOn = f.rawWriting === true;
1696
- return el("div", { className: "nwToolRow" + (rawOn ? " nwToolRowOn" : " nwToolRowRaw"), key: "rawWriting" },
1697
- el("div", { className: "nwToolLabel" },
1698
- el("div", { className: "nwToolLabelRaw" }, "🔞 " + t("feature.rawWriting")),
1699
- el("div", { className: "nwToolDesc nwRawDanger" }, t("raw.danger")),
1700
- rawOn ? el("div", { className: "nwRawOn" }, t("raw.on")) : null
2266
+ el("div", { className: "nwSection" },
2267
+ el("div", { className: "nwKickerRow" },
2268
+ el("div", { className: "nwKicker" }, t("panel.secStatus")),
2269
+ el("div", { className: "nwKickerRule" })
2270
+ ),
2271
+ entry(t("panel.featuresTitle"), t("panel.featuresHint"), onFeatures, "features"),
2272
+ entry(t("panel.toolsTitle"), t("panel.toolsHint"), onTools, "tools"),
2273
+ // v3.2.0:原创模式(设定库)移到主面板
2274
+ entry(t("panel.creationTitle"), t("panel.creationHint"), null, "creation"),
2275
+ // v3.0.0:风格基线(六维 ±% 容差带)
2276
+ entry(t("panel.baselineTitle"), t("panel.baselineHint"), null, "baseline"),
2277
+ // v3.7.0:功能开关(情感深度/风格检测/语义增强)在二级「功能开关」页维护——主页不重复展示
2278
+ // v2.6.0:数据目录占用 + 语义引擎状态(一目了然)
2279
+ el("div", { className: "nwPillRow" },
2280
+ el("div", { className: "nwPill" },
2281
+ el("span", { className: "nwPillKey" }, t("dir.size")),
2282
+ el("span", { className: "nwPillVal" }, formatSize(state.dataDirSize))
1701
2283
  ),
1702
- el("button", { type: "button", role: "switch", "aria-checked": rawOn ? "true" : "false", className: "nwSwitchSmall" + (rawOn ? " nwSwitchSmallOn" : ""), disabled: state.loading,
1703
- onClick: function () { props.controller.rawToggle(!rawOn); } },
1704
- el("span", { className: "nwSwitchSmallKnob" })
2284
+ el("div", { className: "nwPill" },
2285
+ el("span", { className: "nwPillKey" }, t("model.engine")),
2286
+ el("span", { className: "nwPillVal" }, engineStatusText(state.embeddingStatus, t))
1705
2287
  )
1706
- );
1707
- })(),
1708
- entry(t("panel.featuresTitle"), t("panel.featuresHint"), onFeatures, "features"),
1709
- entry(t("panel.toolsTitle"), t("panel.toolsHint"), onTools, "tools"),
1710
- // v3.2.0:原创模式(设定库)移到主面板
1711
- entry(t("panel.creationTitle"), t("panel.creationHint"), null, "creation"),
1712
- // v3.0.0:风格基线(六维 ±% 容差带)
1713
- entry(t("panel.baselineTitle"), t("panel.baselineHint"), null, "baseline"),
1714
- // v3.7.0:功能开关(情感深度/风格检测/语义增强)在二级「功能开关」页维护——主页不重复展示
1715
- // v2.6.0:数据目录占用 + 语义引擎状态(一目了然)
1716
- el("div", { className: "nwRow nwRowOff" },
1717
- el("div", { className: "nwRowText" },
1718
- el("div", { className: "nwRowLabel" }, t("dir.size")),
1719
- el("div", { className: "nwRowHint" }, formatSize(state.dataDirSize) + " · " + engineStatusText(state.embeddingStatus, t))
1720
- )
2288
+ ),
2289
+ pathBox(t("dir.data"), dirs.dataDir || state.dataDir || "", "data-dir"),
2290
+ // v4.3.0:state.file / dirs.stateFile 此前写了 4 处却从不读取——宿主 reveal 支持 state-file
2291
+ //(lib/core.js allowedTargets 含 "state-file"),这里补一行路径卡把它用起来
2292
+ pathBox(t("dir.stateFile"), dirs.stateFile || state.file || "", "state-file"),
1721
2293
  ),
1722
- pathBox(t("dir.data"), dirs.dataDir || state.dataDir || "", "data-dir"),
1723
- // v4.3.0:state.file / dirs.stateFile 此前写了 4 处却从不读取——宿主 reveal 支持 state-file
1724
- //(lib/core.js allowedTargets 含 "state-file"),这里补一行路径卡把它用起来
1725
- pathBox(t("dir.stateFile"), dirs.stateFile || state.file || "", "state-file"),
1726
2294
  // v3.2.0:书库统计(写作打卡);null=未就绪不渲染,[]=真空提示
1727
- el("div", { key: "statsCard", style: { marginTop: "10px", padding: "10px 12px", borderRadius: "10px", border: "1px solid #e2e8f0", background: "#fff" } },
1728
- el("div", { style: { fontSize: "12px", fontWeight: "600", color: "#475569", marginBottom: "6px" } }, "📚 " + t("stats.title")),
2295
+ el("div", { key: "statsCard", className: "nwBoxed", style: { marginTop: "10px", padding: "10px 12px" } },
2296
+ el("div", { className: "nwGroupName", style: { marginBottom: "6px" } }, "📚 " + t("stats.title")),
1729
2297
  el("div", { style: { display: "flex", flexDirection: "column", gap: "5px" } },
1730
2298
  state.booksStats === null
1731
- ? el("div", { style: { fontSize: "11px", color: "#f59e0b" } }, t("stats.pending"))
2299
+ ? el("div", { className: "nwNote nwNoteWarn", }, t("stats.pending"))
1732
2300
  : (state.booksStats || []).length === 0
1733
- ? el("div", { style: { fontSize: "11px", color: "#94a3b8" } }, t("stats.empty"))
2301
+ ? el("div", { className: "nwNote", }, t("stats.empty"))
1734
2302
  : (state.booksStats || []).slice(0, 6).map(function (bs) {
1735
2303
  var charsText = fmtChars(bs.chars);
1736
- return el("div", { key: bs.name, style: { display: "flex", alignItems: "center", gap: "8px", fontSize: "11px" } },
1737
- el("span", { style: { flex: "1", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", color: "#334155" } }, "📕 " + bs.name),
1738
- el("span", { style: { color: "#94a3b8" } }, bs.chapters + " " + t("stats.chapters") + " · " + charsText + t("stats.chars")),
2304
+ return el("div", { key: bs.name, className: "nwStatRow" },
2305
+ el("span", { className: "nwStatRow", }, "📕 " + bs.name),
2306
+ el("span", { className: "nwStatName", }, bs.chapters + " " + t("stats.chapters") + " · " + charsText + t("stats.chars")),
1739
2307
  bs.recent7Chars > 0
1740
- ? el("span", { style: { color: "#10b981", fontWeight: "600" } }, "🔥 " + fmtChars(bs.recent7Chars) + " " + t("stats.week"))
1741
- : el("span", { style: { color: "#cbd5e1" } }, t("stats.idle"))
2308
+ ? el("span", { className: "nwNote nwNoteOk", }, "🔥 " + fmtChars(bs.recent7Chars) + " " + t("stats.week"))
2309
+ : el("span", { className: "nwNote nwNoteDim", }, t("stats.idle"))
1742
2310
  );
1743
2311
  })
1744
2312
  )
1745
2313
  ),
1746
2314
  // v3.2.0:报告历史入口
1747
- el("button", { key: "reportsEntry", type: "button", style: { display: "flex", alignItems: "center", width: "100%", marginTop: "8px", padding: "9px 12px", borderRadius: "10px", border: "1px solid #e2e8f0", background: "#fff", cursor: "pointer", textAlign: "left" }, onClick: function () {
2315
+ el("button", { key: "reportsEntry", type: "button", className: "nwBoxed nwFileRow", style: { marginTop: "8px", padding: "9px 12px", textAlign: "left" }, onClick: function () {
1748
2316
  props.controller.set({ reportsGroups: null, reportContent: null }, { silent: true });
1749
2317
  // v4.3.0:超时 + 错误透传(reports.readFailDetail 已被具体错误取代,该词条随之删除)
1750
2318
  fetchWithTimeout("/api/dsh-novel-writer/reports", { headers: { accept: "application/json" } }).then(function (r2) {
@@ -1760,12 +2328,12 @@ var TOOL_GROUPS = [
1760
2328
  });
1761
2329
  props.controller.openView("reports");
1762
2330
  } },
1763
- el("span", { style: { flex: "1", fontSize: "13px", fontWeight: "600", color: "#334155" } }, "📊 " + t("reports.title")),
1764
- el("span", { style: { fontSize: "11px", color: "#94a3b8" } }, t("reports.hint")),
1765
- el("span", { style: { fontSize: "13px", color: "#94a3b8" } }, "›")
2331
+ el("span", { className: "nwRowLabel", style: { flex: "1" } }, "📊 " + t("reports.title")),
2332
+ el("span", { className: "nwNote", }, t("reports.hint")),
2333
+ el("span", { className: "nwNote nwChevron", }, "›")
1766
2334
  ),
1767
2335
  // v3.2.0:体验演示
1768
- el("div", { key: "demoCard", style: { marginTop: "10px", padding: "10px 12px", borderRadius: "10px", border: "1px dashed #c7d2fe", background: "#f8f9ff" } },
2336
+ el("div", { key: "demoCard", className: "nwDashedNote", style: { marginTop: "10px" } },
1769
2337
  el("button", { type: "button", disabled: state.demoLoading, style: { display: "flex", alignItems: "center", gap: "8px", width: "100%", background: "transparent", border: "none", padding: 0, cursor: state.demoLoading ? "default" : "pointer", textAlign: "left" }, onClick: function () {
1770
2338
  if (state.demoLoading) return;
1771
2339
  props.controller.set({ demoLoading: true }, { silent: true });
@@ -1783,10 +2351,10 @@ var TOOL_GROUPS = [
1783
2351
  props.controller.set({ demoLoading: false }, { silent: true });
1784
2352
  });
1785
2353
  } },
1786
- el("span", { style: { fontSize: "13px", fontWeight: "600", color: "#6366f1" } }, state.demoLoading ? t("demo.loading") : t("demo.run")),
1787
- el("span", { style: { fontSize: "11px", color: "#94a3b8" } }, t("demo.hint"))
2354
+ el("span", { className: "nwAccentStrong", }, state.demoLoading ? t("demo.loading") : t("demo.run")),
2355
+ el("span", { className: "nwNote", }, t("demo.hint"))
1788
2356
  ),
1789
- state.demoReport ? el("pre", { style: { margin: "8px 0 0", padding: "10px", borderRadius: "8px", background: "#fff", border: "1px solid #e2e8f0", fontSize: "11px", lineHeight: "1.7", color: "#334155", whiteSpace: "pre-wrap", wordBreak: "break-all", maxHeight: "260px", overflow: "auto" } }, state.demoReport) : null
2357
+ state.demoReport ? el("pre", { className: "nwBoxed nwReportPre", style: { margin: "8px 0 0", maxHeight: "260px", overflow: "auto" } }, state.demoReport) : null
1790
2358
  )
1791
2359
  );
1792
2360
  }
@@ -1823,9 +2391,12 @@ var TOOL_GROUPS = [
1823
2391
  // v5.2.0:作为官方右栏 tab 时不画关闭按钮——关闭由宿主 tab chip 提供
1824
2392
  typeof props.onClose === "function" ? el("button", { type: "button", className: "nwClose", "aria-label": t("panel.close"), onClick: props.onClose }, "×") : null
1825
2393
  ),
1826
- el("div", { className: "nwBanner " + (on ? "nwBannerOn" : "nwBannerOff") },
1827
- el("span", { className: "nwBannerIcon" }, on ? "✔" : "✘"),
1828
- el("span", null, on ? t("banner.on") : t("banner.off"))
2394
+ el("div", { className: "nwHero" },
2395
+ el("div", { className: "nwBanner " + (on ? "nwBannerOn" : "nwBannerOff") },
2396
+ el("span", { className: "nwBannerIcon" }, on ? "✔" : "✘"),
2397
+ el("span", null, on ? t("banner.on") : t("banner.off"))
2398
+ ),
2399
+ el("div", { className: "nwHeroDesc" }, t("panel.heroDesc")),
1829
2400
  ),
1830
2401
  loadingHint,
1831
2402
  msg,
@@ -1844,43 +2415,17 @@ var TOOL_GROUPS = [
1844
2415
  var statusKey = state.enabled ? (state.autoAnalyze ? "card.status" : "card.statusAutoOff") : "card.statusOff";
1845
2416
  return react.createElement(
1846
2417
  "div",
1847
- { style: { padding: "12px 0", borderBottom: "1px solid var(--dsw-alias-border-l2, #e5e7eb)" } },
1848
- react.createElement("div", { style: { fontWeight: 600 } }, t("card.title")),
1849
- react.createElement("div", { style: { color: "var(--dsw-alias-text-l2, #888)", fontSize: 12, margin: "4px 0 8px" } }, t("card.desc")),
2418
+ { className: "nwSettingsCard" },
2419
+ react.createElement("div", { className: "nwSettingsCardTitle" }, t("card.title")),
2420
+ react.createElement("div", { className: "nwMuted", style: { margin: "4px 0 8px" } }, t("card.desc")),
1850
2421
  react.createElement("div", { style: { display: "flex", alignItems: "center", gap: 10, marginTop: 4 } },
1851
- react.createElement(
1852
- "span",
1853
- {
1854
- style: {
1855
- display: "inline-flex", alignItems: "center", gap: 6,
1856
- fontSize: 12, fontWeight: 700, lineHeight: 1,
1857
- padding: "4px 10px", borderRadius: 999,
1858
- background: state.enabled ? "rgba(34,197,94,.14)" : "rgba(239,68,68,.10)",
1859
- color: state.enabled ? "#15803d" : "#b91c1c",
1860
- border: "1px solid " + (state.enabled ? "#22c55e" : "#ef4444")
1861
- }
1862
- },
1863
- react.createElement("span", null, state.enabled ? "●" : "○"),
1864
- react.createElement("span", null, t(statusKey))
1865
- ),
1866
- react.createElement(
1867
- "button",
1868
- {
1869
- type: "button",
1870
- style: {
1871
- appearance: "none", font: "inherit", cursor: "pointer",
1872
- border: "1px solid var(--dsw-alias-border-l2, #d1d5db)", borderRadius: 8,
1873
- background: "var(--dsw-alias-bg-layer-3, #fff)", color: "inherit",
1874
- padding: "5px 12px", fontSize: 12
1875
- },
1876
- onClick: function () {
1877
- if (typeof props.onOpenPanel === "function") props.onOpenPanel();
1878
- }
1879
- },
1880
- t("card.open")
1881
- )
2422
+ react.createElement(NW_UI.Tag, { tone: state.enabled ? "success" : "danger" }, t(statusKey)),
2423
+ react.createElement(NW_UI.Button, {
2424
+ variant: "outline", size: "sm",
2425
+ onClick: function () { if (typeof props.onOpenPanel === "function") props.onOpenPanel(); }
2426
+ }, t("card.open"))
1882
2427
  ),
1883
- react.createElement("div", { style: { color: "var(--dsw-alias-text-l2, #888)", fontSize: 12, marginTop: 8 } }, t("card.hint"))
2428
+ react.createElement("div", { className: "nwMuted", style: { marginTop: 8 } }, t("card.hint"))
1884
2429
  );
1885
2430
  }
1886
2431
  function mountPanel(controller, toggle) {