dsh-novel-writer 5.5.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +12 -17
- package/README.md +10 -16
- package/lib/client.js +572 -171
- package/package.json +2 -2
- package/server.json +2 -2
package/README.en.md
CHANGED
|
@@ -16,9 +16,7 @@ Sentence, emotion and semantic analysis all run **on your machine**: a 24MB Chin
|
|
|
16
16
|
**zero API cost, your manuscript never leaves the device**. Built for DeepSeek Harness (DSH); the same engine is also
|
|
17
17
|
exposed as a **stdio MCP server** for Claude Desktop / Cursor.
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
[Website](https://siweina.github.io/dsh-novel-writer/) · [Technical docs](https://siweina.github.io/dsh-novel-writer/tools/) · [Install](#install) · [The writing loop](#the-writing-loop-end-to-end) · [60-second start](#60-second-start) · [See the output](#see-the-output) · [The 18 tools](#provided-tools-18) · [MCP server](#mcp-server-usable-outside-dsh)
|
|
19
|
+
[Website](https://siweina.github.io/dsh-novel-writer/) · [Technical docs (parameters & examples for all 18 tools)](https://siweina.github.io/dsh-novel-writer/tools/) · [Install](#install) · [The writing loop](#the-writing-loop-end-to-end) · [60-second start](#60-second-start) · [See the output](#see-the-output) · [The 18 tools](#provided-tools-18) · [MCP server](#mcp-server-usable-outside-dsh)
|
|
22
20
|
|
|
23
21
|
---
|
|
24
22
|
|
|
@@ -34,12 +32,6 @@ exposed as a **stdio MCP server** for Claude Desktop / Cursor.
|
|
|
34
32
|
|
|
35
33
|
> **Everything local, read-only by default**: the brief and the structure view **write no files at all**; the fix desk writes only its own list file, **never touches your prose and never writes prose for you**; semantic search and emotion analysis cost zero tokens.
|
|
36
34
|
|
|
37
|
-
> **Token-saving mode (new in v5.1.0)**: the sidebar's **Lean workflow** switch (or `novel_sentence_config` with `leanWorkflow=true`) keeps exactly one rule — **call tools on demand**: no automatic self-checks, no re-checking fix lists, no registry updates; report tools return the brief form unless you explicitly pass `brief` (style report: 181 chars vs 1725 for the full one).
|
|
38
|
-
>
|
|
39
|
-
> **v5.1.0 cost & feel fixes**: v5.0.0 had turned "always run a self-check after writing + the closing trio" and "mark every item + verify repeatedly" into mandatory checklists — one chapter went from 3 tool calls to 6, and revising took 30+. Together with "build sentences to the skeletons" and "fix any out-of-band dimension", that made prose stiff. This release makes all of it **on demand**: a chapter now takes **2 calls and up**, the fix-list cap dropped **40 → 12** with only the worst paragraph per dimension, and it now says plainly that **out-of-band ≠ defect** (keep lyrical / psychological / elliptical passages when they read fine — `mark` them `skip`).
|
|
40
|
-
>
|
|
41
|
-
> **v5.2.0 official sidebar integration**: the browser half no longer injects DOM into the host. It uses the DSH 0.2.0 seats — `ctx.sidebarRightTabs.register` declares the tab type, `sidebar.right.pane.tab` provides the body (tab chip, docking, splitting, per-session layout and theming all belong to the host), `sidebar.footer.action` adds the left-sidebar entry, and `sidebar.right.pane.tab.title` keeps an open tab's label following language changes. Hosts without these services fall back to the old path automatically; tools, parameters and the state file are unchanged (still 18 tools).
|
|
42
|
-
|
|
43
35
|
## What problem does it solve
|
|
44
36
|
|
|
45
37
|
| Your pain | What you get here |
|
|
@@ -59,19 +51,22 @@ exposed as a **stdio MCP server** for Claude Desktop / Cursor.
|
|
|
59
51
|
**Option 1: npm (recommended)**
|
|
60
52
|
|
|
61
53
|
```sh
|
|
62
|
-
dsh plugin
|
|
54
|
+
dsh plugin add dsh-novel-writer
|
|
63
55
|
```
|
|
64
56
|
|
|
57
|
+
(Add `--profile <name>` to target a specific profile, e.g. `--profile desktop`.)
|
|
58
|
+
|
|
65
59
|
**Option 2: From GitHub**
|
|
66
60
|
|
|
67
61
|
```sh
|
|
68
|
-
dsh plugin
|
|
62
|
+
dsh plugin add github:siweina/dsh-novel-writer#main
|
|
69
63
|
```
|
|
70
64
|
|
|
71
65
|
**Option 3: MCP (no DSH required)** — see [MCP server](#mcp-server-usable-outside-dsh)
|
|
72
66
|
|
|
73
|
-
Requires **Node >= 22.3**. After installing, **restart
|
|
74
|
-
appears in the sidebar.
|
|
67
|
+
Requires **Node >= 22.3**. After installing, **restart DSH** (desktop / web); a 「写作助手功能」 ("Writing Assistant")
|
|
68
|
+
entry appears in the left sidebar's main nav (official DSH 0.2.0+ seat; older hosts fall back to the previous
|
|
69
|
+
mounting path automatically).
|
|
75
70
|
|
|
76
71
|
## 60-second start
|
|
77
72
|
|
|
@@ -155,7 +150,7 @@ fixAnchors:3 条原著锚段(对话 / 心理 / 描写各一条,供逐句
|
|
|
155
150
|
| Cost | **0 (local inference)** | Billed per token | Self-hosted |
|
|
156
151
|
| Purpose-built for Chinese fiction | **Yes** | Generic | No |
|
|
157
152
|
| Style baseline (μ±σ) | **Yes** | Rare | No |
|
|
158
|
-
| DSH integration | **18 tools +
|
|
153
|
+
| DSH integration | **18 tools + official seats & website-style UI** | None | None |
|
|
159
154
|
| Usable without DSH | **Yes (MCP)** | Yes | You wrap it yourself |
|
|
160
155
|
|
|
161
156
|
> **A note for non-Chinese users**: this plugin is designed for analysing and writing Chinese fiction — the sentence-pattern,
|
|
@@ -175,10 +170,10 @@ fixAnchors:3 条原著锚段(对话 / 心理 / 描写各一条,供逐句
|
|
|
175
170
|
6. **Emotion purification & quantification**: strong/weak emotion-word grading, pollution detection, caveat warning + AI re-verification; Valence sliding window → variance V / delta Δ / conflict index C + implicit imagery carriers.
|
|
176
171
|
7. **Worldview & pragmatics detection**: auto cultural-baseline detection with confidence; speechStyle title/honorifics/rituals/tone norms; genre & theme + webnovel signals.
|
|
177
172
|
8. **Writing toolkit**: plot tracking / five settings tables (characters·locations·items·timeline·worldview) / chapter summaries / continuity audit / batch import / style check / continuation writing.
|
|
178
|
-
9. **Per-tool UI toggles**: 「写作助手功能」 ("Writing Assistant") sidebar panel (master + grouped tool toggles + feature toggles + prompt mode/scene/**lean workflow
|
|
179
|
-
10. **Style Baseline**: Six writing metrics (syntactic complexity / modifier density / abstraction / action density / hedging / gap index) + per-chapter μ±σ baseline band; `novel_style_report` outputs the band, `novel_style_check` compares new chapters (in-band ✓ / out-of-band ⚠); per-metric ±% tolerance configurable in the
|
|
173
|
+
9. **Per-tool UI toggles**: a 「写作助手功能」 ("Writing Assistant") entry in the left sidebar plus a main-column panel (**official DSH 0.2.0 seats** since v5.2.0; hosts older than 0.2.0 fall back automatically) — master switch + grouped tool toggles + feature toggles + prompt mode/scene/**lean workflow**, plain-language labels, data-dir usage & semantic-engine status. **The v5.5.0 UI is rebuilt on the official design system with a website-style overhaul**: all colors go through `--dsw-alias-*` semantic tokens (three themes in sync with the host), controls use the official UI primitives, DeepSeek-blue accents, a glow hero header with section kickers, and equal-width pages across every level.<br>**v6.0.0 goes further (Plan A)**: source split + build chain (assert-failed builds never write, `--check` verifies source==artifact), 8 unified components with a single row pattern (`PreferenceRow`), group cards across every switch page, 19+5 line icons, a slider as auxiliary input on the tolerance rows, and page-level push-pop transition animations (zero state / zero hook).
|
|
174
|
+
10. **Style Baseline**: Six writing metrics (syntactic complexity / modifier density / abstraction / action density / hedging / gap index) + per-chapter μ±σ baseline band; `novel_style_report` outputs the band, `novel_style_check` compares new chapters (in-band ✓ / out-of-band ⚠); per-metric ±% tolerance configurable in the panel's **Six-metric baseline** view (**recommended = 1.5× σ of the book's chapter variance**, rounded, clamped to ±10%~100%; leave blank to use recommended) — free theme, writing style kept inside the band.
|
|
180
175
|
11. **Writing sentinels**: `novel_continuity_check` extended — ①**bridge check** (`chapter`: time jumps / semantic distance / character continuity / hook handoff, with quoted evidence) ②**OOC check** (`ooc`: per-character emotion baseline deviation) ③**outline drift** (`outline`: direction vs body keyword overlap); **brief mode** for report tools.
|
|
181
|
-
12. **Original mode & creation files**: fill in creation settings in the
|
|
176
|
+
12. **Original mode & creation files**: fill in creation settings in the panel's **Original mode** view (worldview/characters/forbidden/main conflict/genre/extras, blank = model decides, per-book profile library); novel_outline maintains creation files (bible/characters/outline/hooks/status), enforcing the bible → outline → hook chain with dynamic batches (10→20→30 chapters) to prevent plot jumps and OOC.
|
|
182
177
|
13. **Experience & stats**: main panel **library stats** (per-book chapters/total chars/7-day active chars, 🔥 green), **🎬 demo** (built-in sample, no files, runs the 6-dim baseline), **📊 report history** (analysis/style-reports browsing); actionable error hints; slimmer tool descriptions.
|
|
183
178
|
|
|
184
179
|
---
|
package/README.md
CHANGED
|
@@ -15,9 +15,7 @@
|
|
|
15
15
|
句式 / 情感 / 语义全部**在本地算**:24MB 中文模型随包分发,**零 API 花费、正文不出本机**。
|
|
16
16
|
为 DeepSeek Harness(DSH)打造;同一套能力也可作为 **MCP 服务器**给 Claude Desktop / Cursor 使用。
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
[官网](https://siweina.github.io/dsh-novel-writer/) · [技术文档](https://siweina.github.io/dsh-novel-writer/tools/) · [安装](#安装) · [写作流程](#写作流程一条能走完的闭环) · [60 秒上手](#60-秒上手) · [看看输出](#看看输出长什么样) · [18 个工具](#提供的工具18-个) · [MCP 服务器](#mcp-服务器非-dsh-用户也能用)
|
|
18
|
+
[官网](https://siweina.github.io/dsh-novel-writer/) · [技术文档(18 个工具的参数与示例)](https://siweina.github.io/dsh-novel-writer/tools/) · [安装](#安装) · [写作流程](#写作流程一条能走完的闭环) · [60 秒上手](#60-秒上手) · [看看输出](#看看输出长什么样) · [18 个工具](#提供的工具18-个) · [MCP 服务器](#mcp-服务器非-dsh-用户也能用)
|
|
21
19
|
|
|
22
20
|
---
|
|
23
21
|
|
|
@@ -33,12 +31,6 @@
|
|
|
33
31
|
|
|
34
32
|
> **全程本地、只读优先**:开写包与结构视图**不写任何文件**;改稿台只写自己的清单文件、**从不改正文,也不代写正文**;语义检索与情感分析零 token。
|
|
35
33
|
|
|
36
|
-
> **省 token 模式(v5.1.0 新增)**:侧边栏「精简工作流」开关(或 `novel_sentence_config` 传 `leanWorkflow=true`)打开后只保留一条原则——**工具按需调用**:不主动跑自检、不复测清单、不登记伏笔/钩子/摘要;报告类工具未显式传 `brief` 时默认走精简输出(风格画像实测 181 字符 vs 完整 1725 字符)。
|
|
37
|
-
>
|
|
38
|
-
> **v5.1.0 成本与手感修复**:v5.0.0 曾把「写完必须自检 + 收尾三件」「改稿逐条 mark + 反复 verify」写成必做清单,一章从 3 次调用涨到 6 次、改稿要 30+ 次;同时"照骨架造句"与"任一维度出带必改"会让文章越改越僵。本版全部改回**按需**:写一章 **2 次调用起**,改稿清单上限 **40 → 12 条**、同一维度只报最严重的 1 段,并明确了**出带不等于缺陷**(抒情/心理/留白段读起来不别扭就保留,可用 `mark` 标 `skip`)。
|
|
39
|
-
>
|
|
40
|
-
> **v5.2.0 官方侧边栏适配**:浏览器半边不再往宿主 DOM 里塞东西。改用 DSH 0.2.0 的官方席位——`ctx.sidebarRightTabs.register` 声明 tab 类型 + `sidebar.right.pane.tab` 提供正文(tab chip、停靠、分栏、每会话布局、深浅主题全归宿主)+ `sidebar.footer.action` 放左栏入口 + `sidebar.right.pane.tab.title` 让已打开的 tab 跟随语言切换。检测不到这些服务的宿主自动回退旧路径;工具、参数、状态文件完全不变(仍 18 个工具)。
|
|
41
|
-
|
|
42
34
|
## 它解决什么问题
|
|
43
35
|
|
|
44
36
|
| 你的困扰 | 这里给的答案 |
|
|
@@ -58,18 +50,20 @@
|
|
|
58
50
|
**方式一:npm(推荐)**
|
|
59
51
|
|
|
60
52
|
```sh
|
|
61
|
-
dsh plugin
|
|
53
|
+
dsh plugin add dsh-novel-writer
|
|
62
54
|
```
|
|
63
55
|
|
|
56
|
+
(需要指定配置时加 `--profile <名称>`,如 `--profile desktop`。)
|
|
57
|
+
|
|
64
58
|
**方式二:从 GitHub 安装**
|
|
65
59
|
|
|
66
60
|
```sh
|
|
67
|
-
dsh plugin
|
|
61
|
+
dsh plugin add github:siweina/dsh-novel-writer#main
|
|
68
62
|
```
|
|
69
63
|
|
|
70
64
|
**方式三:MCP(不用 DSH 也能用)** —— 见 [MCP 服务器](#mcp-服务器非-dsh-用户也能用)
|
|
71
65
|
|
|
72
|
-
要求 **Node ≥ 22.3**。安装后**重启 Web
|
|
66
|
+
要求 **Node ≥ 22.3**。安装后**重启 DSH**(桌面端 / Web),左栏主导航区出现「写作助手功能」入口(DSH 0.2.0+ 官方席位;更早宿主自动回退旧挂载)。
|
|
73
67
|
|
|
74
68
|
## 60 秒上手
|
|
75
69
|
|
|
@@ -155,7 +149,7 @@ fixAnchors:3 条原著锚段(对话 / 心理 / 描写各一条,供逐句
|
|
|
155
149
|
| 花费 | **0(本地推理)** | 按 token 计费 | 自建 |
|
|
156
150
|
| 中文小说专用 | **是** | 通用 | 否 |
|
|
157
151
|
| 风格基线(μ±σ) | **有** | 少见 | 无 |
|
|
158
|
-
| 与 DSH 集成 | **18 工具 +
|
|
152
|
+
| 与 DSH 集成 | **18 工具 + 官方席位与官网式界面** | 无 | 无 |
|
|
159
153
|
| 非 DSH 用户可用 | **可以(MCP)** | 可以 | 需自己封装 |
|
|
160
154
|
|
|
161
155
|
> **致非中文用户**:本插件为中文小说分析写作而设计——句式、情感、意象等核心能力以及内置的语义模型,全部针对中文语料构建与调优。在深耕中文的同时兼顾英文等其他语言,确实超出了我目前的能力范围。若因此给您带来不便,我深感抱歉,恳请谅解。
|
|
@@ -172,10 +166,10 @@ fixAnchors:3 条原著锚段(对话 / 心理 / 描写各一条,供逐句
|
|
|
172
166
|
6. **情感净化 + 量化**:强/弱情绪词分级、污染源检测、caveat 预警 + AI 复核;Valence 滑动窗口 → 方差 V / 斜率 Δ / 矛盾指数 C + 隐性意象载体。
|
|
173
167
|
7. **世界观与语用检测**:文化基准自动判断(西/东/混合)+ 置信度;speechStyle 称谓/客套/仪式/语气规范;题材流派 + 网文信号。
|
|
174
168
|
8. **写作辅助全家桶**:伏笔登记表 / 设定五张表(人物·地点·道具·时间线·世界观)/ 章节摘要 / 连贯性审计 / 批量导入 / 风格自检 / 续写辅助。
|
|
175
|
-
9. **全工具 UI
|
|
176
|
-
10. **风格基线**:文笔六维测量(句法复杂度/修饰密度/抽象度/动作密度/不确定性/留白指数)+ 按章节 μ±σ 基线带;`novel_style_report` 输出基线带,`novel_style_check` 对照新章偏差(带内 ✓ / 出带
|
|
169
|
+
9. **全工具 UI 开关**:左栏「写作助手功能」入口 + 主栏面板(**DSH 0.2.0 官方席位**,v5.2.0 起;老宿主 < 0.2.0 自动回退旧挂载)——总开关 + 工具开关分组 + 功能开关 + 提示词档位/场景/**精简工作流**,大白话文案,显示数据目录占用与语义引擎状态。**v5.5.0 界面按官方设计体系统一并做官网式重构**:颜色全走 `--dsw-alias-*` 语义 token(三主题与官方同源)、控件用官方 UI primitives、品牌蓝强调、光晕头部与分区 kicker、各级页面等宽。<br>**v6.0.0 再进一步(方案 A)**:源码分块 + 构建链装配(构建失败不写盘、`--check` 校验源与产物一致)、8 个统一组件与单一行模式(`PreferenceRow`)、分组卡片贯穿全部开关页、19+5 枚线图标、阈值 slider 辅助输入、页面级 push-pop 过渡动画(零 state / 零 hook)。
|
|
170
|
+
10. **风格基线**:文笔六维测量(句法复杂度/修饰密度/抽象度/动作密度/不确定性/留白指数)+ 按章节 μ±σ 基线带;`novel_style_report` 输出基线带,`novel_style_check` 对照新章偏差(带内 ✓ / 出带 ⚠);面板「六维基线」视图可自定义每维 ±% 容差(**推荐值 = 原著章节波动的 1.5 倍 σ**,自动取整、限 ±10%~100%;输入框留空即用推荐)——原创/续写时主题自由、写法保持在基线带内。
|
|
177
171
|
11. **写作哨兵三件套**:`novel_continuity_check` 扩展——①**衔接检查**(chapter 参数:时间硬跳/语义距离/人物延续/钩子承接四路检测,带原文引用)②**OOC 检测**(ooc 参数:角色情绪基线偏离)③**大纲走偏**(outline 参数:方向行 vs 正文关键词重合);报告工具支持 **brief 精简模式**。
|
|
178
|
-
12.
|
|
172
|
+
12. **原创模式与创作资料**:面板「原创模式」填写创作设定(世界观/角色/禁忌/主线/题材/额外要求,留空=模型自定,多书独立设定库);novel_outline 维护创作资料(创作设定/人物/剧情大纲/钩子记录/创作状态卡),原创强制「设定书→大纲→钩子」链,动态批次(10→20→30 章)防剧情跳跃与角色 OOC。
|
|
179
173
|
13. **体验与统计**:主面板**书库统计卡**(每本章数/总字数/近 7 天活跃字数,活跃🔥标绿)、**🎬 体验演示**(内置示例不落盘跑六维基线)、**📊 报告历史**(analysis/style-reports 列表浏览);错误提示带解决步骤;工具说明压缩省 token。
|
|
180
174
|
|
|
181
175
|
---
|
package/lib/client.js
CHANGED
|
@@ -16,6 +16,10 @@ window.__ModuleLoader__.load({
|
|
|
16
16
|
var module = { exports: {} };
|
|
17
17
|
// v3.5.0 R1:rawTimer 必须在 createController 闭包外/顶部声明(旧位置在渲染函数内,api 方法访问不到 → ReferenceError)
|
|
18
18
|
var rawTimer = null;
|
|
19
|
+
// v6.0.0 页面过渡方向记忆(HyperOS push/pop 分层;模块级而非 state 字段/非 hook——行为契约零变化)
|
|
20
|
+
var nwViewOrder = { main: 0, features: 1, tools: 1, baseline: 1, reports: 1, creation: 1, model: 2, "creation-form": 2 };
|
|
21
|
+
var nwPrevView = "main";
|
|
22
|
+
var nwViewDir = "fwd";
|
|
19
23
|
var reportReqId = 0; // v3.5.0 M15:模块级请求序号(渲染闭包变量每次渲染归零,守卫失效)
|
|
20
24
|
var baselineFlashTimer = null; // v4.0.0:基线"已保存"闪烁定时器(此前从不清理:连续保存互相抢状态、卸载后仍写状态)
|
|
21
25
|
var localeId = ""; // v4.3.0:宿主 locale 服务的当前语言(apply 内订阅写入;为空则回退 documentElement.lang)
|
|
@@ -475,6 +479,277 @@ function createNovelWriterUI(react, require) {
|
|
|
475
479
|
|
|
476
480
|
var NW_UI = createNovelWriterUI(react, require);
|
|
477
481
|
|
|
482
|
+
// ---- V6 构建链标记区(tools/build-client.mjs 装配;禁止手改标记之间内容) ----
|
|
483
|
+
/* ===== V6:icons ===== */
|
|
484
|
+
/* --- V6-icons-a.js --- */
|
|
485
|
+
// V6-icons-a.js —— M4a 线图标·主视图批(5 字形)
|
|
486
|
+
// 契约:CONTRACT-V6.md 第 1 条铁律 + 第 7 条拆分附录(M4a 行与图标统一规范)。
|
|
487
|
+
// 统一规范:viewBox="0 0 24 24" / fill="none" / stroke="currentColor" / stroke-width="1.5"
|
|
488
|
+
// stroke-linecap="round" stroke-linejoin="round;SVG 内零颜色字面量;不引入字体与图片资源。
|
|
489
|
+
// key 用语义名(master / autoAnalyze / lean / prompt / scene);
|
|
490
|
+
// key 对应的原字形与设计理由见 V6-icons-a-notes.md 对照表(本文件不放字形字符,避免误扫描)。
|
|
491
|
+
// ES5:var + 对象字面量;自包含,不 import 任何本地文件。
|
|
492
|
+
var NW_ICONS_A = {
|
|
493
|
+
master: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12 4V9"/><path d="M12 15V20"/><rect x="7" y="9" width="10" height="6" rx="3"/></svg>',
|
|
494
|
+
autoAnalyze: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5H20"/><path d="M8 19.5V14"/><path d="M12 19.5V11"/><path d="M16 19.5V7"/></svg>',
|
|
495
|
+
lean: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12.5 2.5L5 13H11L10 21.5L18.5 11H12.5Z"/></svg>',
|
|
496
|
+
prompt: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M14 3.5H7.5A1.5 1.5 0 0 0 6 5V19A1.5 1.5 0 0 0 7.5 20.5H16.5A1.5 1.5 0 0 0 18 19V8Z"/><path d="M14 3.5V7A1 1 0 0 0 15 8H18"/><path d="M9 11H15"/><path d="M9 14.5H15"/><path d="M9 18H13.5"/></svg>',
|
|
497
|
+
scene: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M5.5 4V11.5"/><path d="M5.5 16.5V20"/><path d="M3.5 14H7.5"/><path d="M12 4V6.5"/><path d="M12 11.5V20"/><path d="M10 9H14"/><path d="M18.5 4V10"/><path d="M18.5 15V20"/><path d="M16.5 12.5H20.5"/></svg>'
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
/* --- V6-icons-b1a.js --- */
|
|
501
|
+
// V6-icons-b1a.js —— M4b1a 线图标·情感批(3 字形)
|
|
502
|
+
// 契约:CONTRACT-V6.md 第 1 条铁律 + 第 7 条拆分附录(M4b1a 行与图标统一规范)。
|
|
503
|
+
// 统一规范:viewBox="0 0 24 24" / fill="none" / stroke="currentColor" / stroke-width="1.5"
|
|
504
|
+
// stroke-linecap="round" stroke-linejoin="round;SVG 内零颜色字面量;不引入字体与图片资源。
|
|
505
|
+
// key 用语义名(emotionCaveat / emotionComplexity / genreTheme);
|
|
506
|
+
// key 对应的原字形与画法理由见 V6-icons-b1a-notes.md 对照表(本文件不放字形字符,避免误扫描)。
|
|
507
|
+
// ES5:var + 对象字面量;自包含,不 import 任何本地文件。
|
|
508
|
+
var NW_ICONS_B1A = {
|
|
509
|
+
emotionCaveat: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3.5C12 3.5 5.5 11 5.5 15.5C5.5 19.09 8.41 22 12 22C15.59 22 18.5 19.09 18.5 15.5C18.5 11 12 3.5 12 3.5Z"/><path d="M14.6 16.6C15.1 17.4 15.3 18.3 15.1 19.1"/></svg>',
|
|
510
|
+
emotionComplexity: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><ellipse cx="12" cy="12" rx="8" ry="9"/><path d="M12 3V21"/><path d="M7.6 11.2C8.3 10 9.7 10 10.4 11.2"/><path d="M13.6 11.2C14.3 10 15.7 10 16.4 11.2"/></svg>',
|
|
511
|
+
genreTheme: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M6 3.5H18V13.2L12 20.5L6 13.2Z"/><circle cx="12" cy="7" r="1.5"/><path d="M9.5 11.5H14.5"/></svg>'
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
/* --- V6-icons-b1b.js --- */
|
|
515
|
+
// V6 题材语义图标 · 微批 b1b(M4b1b),共 2 枚 24px 线性图标。
|
|
516
|
+
// 数据模块:NW_ICONS_B1B 为 key 到 SVG 字符串的映射表,供总装配方合并进图标集。
|
|
517
|
+
// 规范约定:viewBox 24、currentColor 描边继承、全文件零颜色字面量。
|
|
518
|
+
// 语法约定:仅 ES5;字符串值用单引号包裹,SVG 属性一律双引号;图形元素自闭合。
|
|
519
|
+
// key 一览:webnovelVibe(雷达波,网文信号)/ semanticEmbedding(半脑轮廓,语义增强)。
|
|
520
|
+
var NW_ICONS_B1B = {
|
|
521
|
+
webnovelVibe: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M7 20L12 12"/><path d="M11.1 8.6A3.5 3.5 0 0 1 15.4 11.1"/><path d="M10.4 6.2A6 6 0 0 1 17.8 10.4"/></svg>',
|
|
522
|
+
semanticEmbedding: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M15 5A7 7 0 0 0 15 19Z"/><path d="M13.5 8q-2 1.5-1.2 3.2"/><path d="M13.5 12.4q-1.7 0.6-1.4 2.6"/><path d="M13.4 16.3q-1.4 1-1.6-0.1"/></svg>'
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
/* --- V6-icons-b2.js --- */
|
|
526
|
+
// V6-icons-b2.js —— M4b2 线图标·语义批(4 字形)
|
|
527
|
+
// 契约:CONTRACT-V6.md 第 1 条铁律 + 第 7 条拆分附录(M4b2 行与图标统一规范)。
|
|
528
|
+
// 统一规范:viewBox="0 0 24 24" / fill="none" / stroke="currentColor" / stroke-width="1.5"
|
|
529
|
+
// stroke-linecap="round" stroke-linejoin="round;SVG 内零颜色字面量;不引入字体与图片资源。
|
|
530
|
+
// key 用语义名(semanticSearch / semanticStyle / semanticImplicit / fallback);
|
|
531
|
+
// key 对应的原字形与设计理由见 V6-icons-b2-notes.md 对照表(本文件不放字形字符,避免误扫描)。
|
|
532
|
+
// ES5:var + 对象字面量;自包含,不 import 任何本地文件。
|
|
533
|
+
var NW_ICONS_B2 = {
|
|
534
|
+
semanticSearch: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="10.5" cy="10.5" r="6.5"/><path d="M14.9 14.9L19.5 19.5"/></svg>',
|
|
535
|
+
semanticStyle: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6L9.5 14.5"/><path d="M7.9 12.9L11.1 16.1"/><path d="M7.9 12.9L6 18L11.1 16.1"/></svg>',
|
|
536
|
+
semanticImplicit: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M2.5 12C6 5.5 18 5.5 21.5 12C18 18.5 6 18.5 2.5 12Z"/><circle cx="12" cy="12" r="2.6"/></svg>',
|
|
537
|
+
fallback: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M10.60 6.78L10.54 3.73L13.46 3.73L13.40 6.78L15.82 8.18L18.43 6.60L19.89 9.13L17.22 10.60L17.22 13.40L19.89 14.87L18.43 17.40L15.82 15.82L13.40 17.22L13.46 20.27L10.54 20.27L10.60 17.22L8.18 15.82L5.57 17.40L4.11 14.87L6.78 13.40L6.78 10.60L4.11 9.13L5.57 6.60L8.18 8.18Z"/><circle cx="12" cy="12" r="2.6"/></svg>'
|
|
538
|
+
};
|
|
539
|
+
|
|
540
|
+
/* --- V6-icons-c.js --- */
|
|
541
|
+
/* V6-icons-c.js — M4c 线图标·工具组批(NW_ICONS_C)
|
|
542
|
+
key→字形:analyze=📊 工具组·分析测量 / settings=📚 工具组·设定 / create=✍️ 工具组·创作
|
|
543
|
+
toolRow=🧩 工具行通用 / danger=⚠️ 危险行(rawWriting)
|
|
544
|
+
规范:viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"
|
|
545
|
+
stroke-linecap="round" stroke-linejoin="round";SVG 内零颜色字面量;ES5 var;自包含。 */
|
|
546
|
+
var NW_ICONS_C = {
|
|
547
|
+
analyze: "<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M4 4v16h16\"/><path d=\"M8.5 20V15\"/><path d=\"M12.5 20V9\"/><path d=\"M16.5 20V12.5\"/></svg>",
|
|
548
|
+
settings: "<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"4\" y=\"5\" width=\"6\" height=\"14\" rx=\"1\"/><path d=\"M6.3 5v14\"/><path d=\"M4 9h2.3\"/><rect x=\"14\" y=\"5\" width=\"6\" height=\"14\" rx=\"1\"/><path d=\"M16.3 5v14\"/><path d=\"M14 9h2.3\"/></svg>",
|
|
549
|
+
create: "<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M7 9h10\"/><path d=\"M9.5 9V4.5h5V9\"/><path d=\"M7 9l5 12 5-12\"/><circle cx=\"12\" cy=\"13.3\" r=\"1.6\"/><path d=\"M12 21v-6.1\"/></svg>",
|
|
550
|
+
toolRow: "<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M5 18V8h4a2 2 0 0 1 4 0h4v4a2 2 0 0 1 0 4v2z\"/></svg>",
|
|
551
|
+
danger: "<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 4.5L21 19.5L3 19.5Z\"/><path d=\"M12 9.5V15.5\"/><path d=\"M12 18h0.01\"/></svg>"
|
|
552
|
+
};
|
|
553
|
+
|
|
554
|
+
/* --- V6-icons-d.js --- */
|
|
555
|
+
// V6-icons-d.js —— M4d-r 线图标·导航栏批(5 字形)
|
|
556
|
+
// 统一规范:viewBox="0 0 24 24" / fill="none" / stroke="currentColor" / stroke-width="1.5"
|
|
557
|
+
// stroke-linecap="round" stroke-linejoin="round;SVG 内零颜色字面量;不引入字体与图片资源。
|
|
558
|
+
// key 用语义名(navFeatures / navTools / navCreation / navBaseline / navArrow);
|
|
559
|
+
// key 对应的原字形与画法见 V6-icons-d-notes.md 对照表(本文件不放字形字符,避免误扫描)。
|
|
560
|
+
// ES5:var + 对象字面量;自包含,不 import 任何本地文件。
|
|
561
|
+
var NW_ICONS_D = {
|
|
562
|
+
navFeatures: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5.5"/><circle cx="12" cy="12" r="2"/><path d="M17.5 12H21.2"/><path d="M14.75 16.76L16.6 19.97"/><path d="M9.25 16.76L7.4 19.97"/><path d="M6.5 12H2.8"/><path d="M9.25 7.24L7.4 4.03"/><path d="M14.75 7.24L16.6 4.03"/></svg>',
|
|
563
|
+
navTools: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M20.48 7.85A4 4 0 1 1 16.15 3.52"/><path d="M13.67 10.33L5.5 18.5"/></svg>',
|
|
564
|
+
navCreation: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12 7C10 5.5 6.5 5.5 4 6.5V17.5C6.5 16.5 10 16.5 12 18"/><path d="M12 7C14 5.5 17.5 5.5 20 6.5V17.5C17.5 16.5 14 16.5 12 18"/><path d="M12 7V18"/></svg>',
|
|
565
|
+
navBaseline: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><circle cx="12" cy="12" r="5"/><path d="M12 12h0.01"/></svg>',
|
|
566
|
+
navArrow: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M9 6l6 6-6 6"/></svg>'
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
/* --- 构建链注入:图标表合并 --- */
|
|
570
|
+
var NW_ICONS = {};
|
|
571
|
+
(function () {
|
|
572
|
+
var tables = [NW_ICONS_A, NW_ICONS_B1A, NW_ICONS_B1B, NW_ICONS_B2, NW_ICONS_C, NW_ICONS_D];
|
|
573
|
+
for (var ti = 0; ti < tables.length; ti += 1) {
|
|
574
|
+
if (!tables[ti]) continue;
|
|
575
|
+
var ks = Object.keys(tables[ti]);
|
|
576
|
+
for (var ki = 0; ki < ks.length; ki += 1) NW_ICONS[ks[ki]] = tables[ti][ks[ki]];
|
|
577
|
+
}
|
|
578
|
+
})();
|
|
579
|
+
/* ===== /V6:icons ===== */
|
|
580
|
+
/* ===== V6:uikit ===== */
|
|
581
|
+
/* --- V6-uikit-rows.js --- */
|
|
582
|
+
// V6-uikit-rows.js (M3a-r) — 行组件纯函数: PreferenceRow / FieldRow.
|
|
583
|
+
// 契约: CONTRACT-V6.md §1 / §4 / §7(M3a 行); ES5 风格(var/function); 零内联样式声明;
|
|
584
|
+
// 零硬编码文案; 类名只用 §4 词汇表(不自造). el / h / t / NW_UI / ICONS / props 六参全部
|
|
585
|
+
// 由调用方注入: 本文件不自引依赖模块、不碰宿主上下文注入、不碰任何 hook; 官方 Switch 只作为
|
|
586
|
+
// h 的类型引用挂载, 绝不在本文件内直接调用. title / summary / label / trailing / bottom
|
|
587
|
+
// 文案槽一律直传(由调用方负责套 t()). 由总负责人经 tools/build-client.mjs 拼进 client.js.
|
|
588
|
+
|
|
589
|
+
// 1) 偏好行: .nwPref > (.nwPrefIcon > .nwPrefGlyph)? + .nwPrefText > (.nwPrefTitle +
|
|
590
|
+
// .nwPrefSummary?) + .nwPrefTrail(Switch 经 h 挂载 或 trailing 槽) + (.nwPrefBottom)?.
|
|
591
|
+
// tone === "danger" 时根类追加 nwPrefDanger; props.className 追加到根类(自动补一个分隔空格,
|
|
592
|
+
// 防 "nwPref" 与扩展类名直接黏连). ICONS 命中 -> 字形槽渲染 SVG 字符串; 未命中 -> 渲染原字形
|
|
593
|
+
// (SVG 字符串子节点如何注入由总负责人集成时决定, 此处只按字符串子节点渲染).
|
|
594
|
+
function PreferenceRow(el, h, t, NW_UI, ICONS, props) {
|
|
595
|
+
var p = props || {};
|
|
596
|
+
var extra = "";
|
|
597
|
+
if (p.className) extra = " " + String(p.className).replace(/^\s+/, "");
|
|
598
|
+
var iconNode = null;
|
|
599
|
+
if (p.icon) {
|
|
600
|
+
iconNode = h("div", { className: "nwPrefIcon" },
|
|
601
|
+
h("span", { className: "nwPrefGlyph" },
|
|
602
|
+
ICONS && ICONS[p.icon] ? ICONS[p.icon] : p.icon));
|
|
603
|
+
}
|
|
604
|
+
var trailNode;
|
|
605
|
+
if (typeof p.switchOnChange === "function") {
|
|
606
|
+
// 开关一律委托官方组件: 经 h 以引用挂载(形态同 client.js switchRow), 不直接调用.
|
|
607
|
+
trailNode = h(NW_UI.Switch, {
|
|
608
|
+
checked: !!p.switchOn,
|
|
609
|
+
disabled: !!p.switchDisabled,
|
|
610
|
+
label: p.switchLabel || "",
|
|
611
|
+
onChange: p.switchOnChange
|
|
612
|
+
});
|
|
613
|
+
} else {
|
|
614
|
+
trailNode = p.trailing;
|
|
615
|
+
}
|
|
616
|
+
var summaryNode = null;
|
|
617
|
+
if (p.summary !== undefined && p.summary !== null && p.summary !== "") {
|
|
618
|
+
summaryNode = h("div", { className: "nwPrefSummary" }, p.summary);
|
|
619
|
+
}
|
|
620
|
+
var bottomNode = null;
|
|
621
|
+
if (p.bottom !== undefined && p.bottom !== null && p.bottom !== "") {
|
|
622
|
+
bottomNode = h("div", { className: "nwPrefBottom" }, p.bottom);
|
|
623
|
+
}
|
|
624
|
+
return h("div", { key: p.key, className: "nwPref" + (p.tone === "danger" ? " nwPrefDanger" : "") + extra },
|
|
625
|
+
iconNode,
|
|
626
|
+
h("div", { className: "nwPrefText" },
|
|
627
|
+
h("div", { className: "nwPrefTitle" }, p.title),
|
|
628
|
+
summaryNode
|
|
629
|
+
),
|
|
630
|
+
h("div", { className: "nwPrefTrail" }, trailNode),
|
|
631
|
+
bottomNode
|
|
632
|
+
);
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// 2) 字段行: .nwField > span.nwMuted(label 槽) + .nwFieldCtl(control 槽), 槽序固定不可换.
|
|
636
|
+
// label 直传(调用方套好 t()); el / t / NW_UI / ICONS 为签名占位, 本函数不用.
|
|
637
|
+
function FieldRow(el, h, t, NW_UI, ICONS, props) {
|
|
638
|
+
var p = props || {};
|
|
639
|
+
return h("div", { className: "nwField" },
|
|
640
|
+
h("span", { className: "nwMuted" }, p.label),
|
|
641
|
+
h("div", { className: "nwFieldCtl" }, p.control));
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
/* --- V6-uikit-wrappers.js --- */
|
|
645
|
+
// V6-uikit-wrappers.js (M3b) — 六个包装组件纯函数.
|
|
646
|
+
// 契约: CONTRACT-V6.md §1 / §4 / §7(M3b 行); ES5 风格(var/function); 零内联样式声明;
|
|
647
|
+
// 零硬编码文案; 类名只用 §4 词汇表(不自造); el/h/t/NW_UI 由调用方注入,
|
|
648
|
+
// 不自引依赖模块、不碰宿主上下文注入、不碰 hook、不直接调用 NW_UI 组件、零新增状态字段.
|
|
649
|
+
// 统一签名 (el, h, t, NW_UI, props); 由总负责人经 tools/build-client.mjs 拼进 V6:uikit 标记区.
|
|
650
|
+
|
|
651
|
+
// 1) 分组卡片: .nwGCard > (.nwGCardTitle? + .nwGCardBody > children); title 经 t(), 缺省槽位为 null.
|
|
652
|
+
function GroupCard(el, h, t, NW_UI, props) {
|
|
653
|
+
var p = props || {};
|
|
654
|
+
return el("div", { className: "nwGCard" },
|
|
655
|
+
p.title ? el("div", { className: "nwGCardTitle" }, t(p.title)) : null,
|
|
656
|
+
el("div", { className: "nwGCardBody" }, p.children)
|
|
657
|
+
);
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
// 2) 分区标题三件套(复用既有类): .nwKickerRow > .nwKicker(经 t()) + .nwKickerRule.
|
|
661
|
+
function SectionTitle(el, h, t, NW_UI, props) {
|
|
662
|
+
var p = props || {};
|
|
663
|
+
return el("div", { className: "nwKickerRow" },
|
|
664
|
+
el("div", { className: "nwKicker" }, t(p.label)),
|
|
665
|
+
el("div", { className: "nwKickerRule" })
|
|
666
|
+
);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
// 3) 对话框卡(复用既有 nwModal 三件套): .nwModal > .nwModalBox > (.nwModalTitle? +
|
|
670
|
+
// .nwModalText? + children + .nwModalBtns(footer)?). 文案槽直传(调用方已拼好或已套 t()) —
|
|
671
|
+
// 任务书对本组件未标注 t(); 现状弹窗标题即 "🔞 " + t(...) 形态(client.js L2365/L2377).
|
|
672
|
+
function DialogCard(el, h, t, NW_UI, props) {
|
|
673
|
+
var p = props || {};
|
|
674
|
+
return el("div", { className: "nwModal" },
|
|
675
|
+
el("div", { className: "nwModalBox" },
|
|
676
|
+
p.title ? el("div", { className: "nwModalTitle" }, p.title) : null,
|
|
677
|
+
p.description ? el("div", { className: "nwModalText" }, p.description) : null,
|
|
678
|
+
p.children,
|
|
679
|
+
p.footer ? el("div", { className: "nwModalBtns" }, p.footer) : null
|
|
680
|
+
)
|
|
681
|
+
);
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
// 4) 空态: .nwEmpty > .nwEmptyTitle(经 t()) + .nwEmptyHint(经 t()).
|
|
685
|
+
function EmptyState(el, h, t, NW_UI, props) {
|
|
686
|
+
var p = props || {};
|
|
687
|
+
return el("div", { className: "nwEmpty" },
|
|
688
|
+
el("div", { className: "nwEmptyTitle" }, t(p.title)),
|
|
689
|
+
el("div", { className: "nwEmptyHint" }, t(p.hint))
|
|
690
|
+
);
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
// 5) 计数徽标(复用既有类): .nwBadge + .nwBadgeOn/.nwBadgeOff; 文本=label 直传, 否则 t(labelOn/labelOff).
|
|
694
|
+
function CountBadge(el, h, t, NW_UI, props) {
|
|
695
|
+
var p = props || {};
|
|
696
|
+
var on = !!p.on;
|
|
697
|
+
var text = (p.label !== undefined && p.label !== null)
|
|
698
|
+
? p.label
|
|
699
|
+
: t(on ? p.labelOn : p.labelOff);
|
|
700
|
+
return el("span", { className: "nwBadge " + (on ? "nwBadgeOn" : "nwBadgeOff") }, text);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
// 6) 行分隔线: 单 div .nwRowDiv, 无子节点; props.key 透传(GroupCard children 数组需 React key).
|
|
704
|
+
function RowDivider(el, h, t, NW_UI, props) {
|
|
705
|
+
return el("div", { key: (props || {}).key, className: "nwRowDiv" });
|
|
706
|
+
}
|
|
707
|
+
/* ===== /V6:uikit ===== */
|
|
708
|
+
/* ===== V6:slider ===== */
|
|
709
|
+
/* --- V6-slider.js --- */
|
|
710
|
+
// V6-slider.js (M5) — 阈值行 slider 辅助输入 augmentation.
|
|
711
|
+
// 契约: CONTRACT-V6.md §1/§3/§4; 只用新建类 nwRange/nwRangeVal; ES5 风格;
|
|
712
|
+
// 自包含文本块,不 import 本地文件; 不新增 state 键/控制器方法/保存逻辑;
|
|
713
|
+
// 无 react.use*/require/ctx.inject 调用; el 由调用方注入.
|
|
714
|
+
// 对应现有视图: lib/client.js L1891-1909 (nwTolRow 内 low/high 两组 nwTolField + nwTolInput).
|
|
715
|
+
function TolRangeRow(el, t, mk, d, setDraft, state) {
|
|
716
|
+
function mkOne(field, label) {
|
|
717
|
+
var v = d ? d[field] : "";
|
|
718
|
+
var empty = (v === "" || v === null || v === undefined);
|
|
719
|
+
var pos = mk + "-" + field + "-range";
|
|
720
|
+
function onEv(ev) { setDraft(mk, field, ev.target.value); }
|
|
721
|
+
return el("span", { key: pos, style: { display: "flex", alignItems: "center", gap: "4px", minWidth: "0" } },
|
|
722
|
+
el("input", { type: "range", className: "nwRange", min: 0, max: 99, step: 1,
|
|
723
|
+
value: empty ? 0 : v, disabled: !!(state && state.loading),
|
|
724
|
+
"aria-label": label, "aria-valuetext": empty ? label : (String(v) + "%"),
|
|
725
|
+
onChange: onEv, onInput: onEv }),
|
|
726
|
+
el("span", { className: "nwRangeVal" }, empty ? "-" : (String(v) + "%")));
|
|
727
|
+
}
|
|
728
|
+
return [mkOne("low", t("baseline.low")), mkOne("high", t("baseline.high"))];
|
|
729
|
+
}
|
|
730
|
+
/* ===== /V6:slider ===== */
|
|
731
|
+
|
|
732
|
+
// ---- V6 图标挂载胶水(总负责人手管区;构建只替换标记对之间内容,本段不受重建影响) ----
|
|
733
|
+
// nwGlyph: 表内有 key → 注入 SVG(React 不渲染 HTML 字符串,须经 dangerouslySetInnerHTML;
|
|
734
|
+
// 图标串由构建链从静态文件装配,非用户输入);无 key → 回退原字形文本。
|
|
735
|
+
function nwGlyph(el, cls, key, fallback) {
|
|
736
|
+
var svg = NW_ICONS ? NW_ICONS[key] : "";
|
|
737
|
+
if (svg) return el("span", { className: cls, "aria-hidden": "true", dangerouslySetInnerHTML: { __html: svg } });
|
|
738
|
+
return el("span", { className: cls }, fallback);
|
|
739
|
+
}
|
|
740
|
+
// nwIconEls: PreferenceRow 的 ICONS 参数需"值即节点",首次渲染时建表并缓存(el 引用稳定)。
|
|
741
|
+
var NW_ICONS_EL_CACHE = null;
|
|
742
|
+
function nwIconEls(el) {
|
|
743
|
+
if (NW_ICONS_EL_CACHE !== null) return NW_ICONS_EL_CACHE;
|
|
744
|
+
var out = {};
|
|
745
|
+
var ks = Object.keys(NW_ICONS);
|
|
746
|
+
for (var nwI = 0; nwI < ks.length; nwI += 1) {
|
|
747
|
+
out[ks[nwI]] = el("span", { className: "nwIco", "aria-hidden": "true", dangerouslySetInnerHTML: { __html: NW_ICONS[ks[nwI]] } });
|
|
748
|
+
}
|
|
749
|
+
NW_ICONS_EL_CACHE = out;
|
|
750
|
+
return out;
|
|
751
|
+
}
|
|
752
|
+
|
|
478
753
|
// ---- 样式 ----
|
|
479
754
|
var css = `
|
|
480
755
|
|
|
@@ -570,6 +845,10 @@ var css = `
|
|
|
570
845
|
.nwBtnGroup{display:flex;gap:6px;margin-top:6px}
|
|
571
846
|
.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
847
|
.nwBtn:hover{background:var(--dsw-alias-bg-layer-2);font-family:inherit}
|
|
848
|
+
/* v6 真机首验:本规则(L源序在后)与 .nwBtnPrimary:hover 同特异 → hover 底色被抢成浅灰(保存钮变全白)。复合放行: */
|
|
849
|
+
.nwBtn.nwBtnPrimary:hover{background:var(--dsw-alias-state-business-primary)}
|
|
850
|
+
.nwBtn.nwBtnDone:hover{background:linear-gradient(135deg,var(--dsw-alias-state-success-primary),var(--dsw-alias-state-success-primary))}
|
|
851
|
+
.nwBtn.nwBtnDanger:hover{background:var(--dsw-alias-state-error-primary)}
|
|
573
852
|
.nwBtn:disabled{opacity:.45;cursor:not-allowed;font-family:inherit}
|
|
574
853
|
.nwPlotMsg{font-size:11px;line-height:1.5;color:var(--dsw-alias-label-secondary);margin-top:4px;word-break:break-all}
|
|
575
854
|
.nwPlotErr{color:var(--dsw-alias-label-error)}
|
|
@@ -718,6 +997,111 @@ var css = `
|
|
|
718
997
|
.nwRow:hover,.nwNavEntry:hover,.nwToolRow:hover,.nwBtnPrimary:hover,.nwBtnGhost:hover{transform:none!important}
|
|
719
998
|
.nwBtnPrimary:hover{filter:none!important}
|
|
720
999
|
}
|
|
1000
|
+
/* ===== V6:css ===== */
|
|
1001
|
+
/* --- V6-components.css --- */
|
|
1002
|
+
/* V6-components.css (M2) — §4 新建 20 类的统一样式.两空格紧凑写法,零颜色字面量,token 仅白名单 --dsw-* (+沿用 --nw-ease 缓动变量). */
|
|
1003
|
+
.nwGCard{margin:0;border:0;border-radius:var(--dsw-radius-xl);background:var(--dsw-alias-bg-layer-2);box-shadow:var(--dsw-elevation-soft);overflow:hidden}
|
|
1004
|
+
.nwGCard+.nwGCard{margin-top:12px}
|
|
1005
|
+
.nwGCardTitle{padding:14px 16px 6px;font-size:14px;line-height:20px;font-weight:600;color:var(--dsw-alias-label-primary)}
|
|
1006
|
+
.nwGCardBody{padding:0}
|
|
1007
|
+
.nwPref{display:flex;align-items:center;gap:14px;padding:14px 16px;background:none;transition:background .16s var(--nw-ease,cubic-bezier(.16,1,.3,1))}
|
|
1008
|
+
.nwPref:hover{background:var(--dsw-alias-interactive-bg-hover)}
|
|
1009
|
+
.nwPrefIcon{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-2));border:0.5px solid color-mix(in srgb,var(--dsw-alias-state-business-primary) 16%,var(--dsw-alias-bg-layer-2));color:var(--dsw-alias-state-business-primary)}
|
|
1010
|
+
.nwPrefGlyph{display:inline-flex;align-items:center;justify-content:center;font-size:15px;line-height:20px;color:var(--dsw-alias-state-business-primary)}
|
|
1011
|
+
.nwPrefGlyph svg{width:20px;height:20px;display:block}
|
|
1012
|
+
.nwPrefText{flex:1;min-width:0}
|
|
1013
|
+
.nwPrefTitle{font-size:14px;line-height:20px;font-weight:500;color:var(--dsw-alias-label-primary)}
|
|
1014
|
+
.nwPrefSummary{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary);margin-top:2px}
|
|
1015
|
+
.nwPrefTrail{flex:none;display:flex;align-items:center;gap:8px;margin-left:auto}
|
|
1016
|
+
.nwPrefBottom{display:flex;align-items:center;gap:10px;padding:0 16px 12px}
|
|
1017
|
+
.nwPref.nwPrefDanger{background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 5%,var(--dsw-alias-bg-layer-2))}
|
|
1018
|
+
.nwRowDiv{height:0.5px;margin:0 16px;background:var(--dsw-alias-border-l2)}
|
|
1019
|
+
.nwEmpty{display:flex;flex-direction:column;align-items:center;text-align:center;padding:24px 16px}
|
|
1020
|
+
.nwEmptyTitle{font-size:14px;line-height:20px;font-weight:600;color:var(--dsw-alias-label-secondary)}
|
|
1021
|
+
.nwEmptyHint{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary);margin-top:4px}
|
|
1022
|
+
.nwField{display:flex;align-items:center;gap:10px;padding:10px 16px}
|
|
1023
|
+
.nwFieldCtl{flex:none;display:flex;align-items:center;gap:8px;margin-left:auto}
|
|
1024
|
+
/* .nwRange 本体外观归 M5(V6-slider-css.txt),此处仅预留 flex 布局槽,合并冲突以 M5 为准. */
|
|
1025
|
+
.nwRange{flex:1;min-width:0}
|
|
1026
|
+
.nwRangeVal{flex:none;min-width:36px;text-align:right;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums}
|
|
1027
|
+
@media (prefers-reduced-motion: reduce){.nwGCard,.nwPref{transition:none!important}}
|
|
1028
|
+
|
|
1029
|
+
/* --- V6-slider-css.txt --- */
|
|
1030
|
+
/* V6-slider-css.txt (M5) — 只含 nwRange 本体 + nwRangeVal, 由总负责人并入样式表. */
|
|
1031
|
+
/* 口径: 轨道/滑块/禁用态/焦点环/值显; 伪元素只做尺寸与圆角, 颜色走本体 token; 动效进 reduced-motion 门. */
|
|
1032
|
+
.nwRange {
|
|
1033
|
+
width: 96px;
|
|
1034
|
+
accent-color: var(--dsw-alias-state-business-primary);
|
|
1035
|
+
background: var(--dsw-alias-interactive-bg-active);
|
|
1036
|
+
border-radius: 999px;
|
|
1037
|
+
corner-shape: round;
|
|
1038
|
+
}
|
|
1039
|
+
.nwRange:focus-visible {
|
|
1040
|
+
outline: var(--dsw-focus-ring-width) solid var(--dsw-focus-ring-color);
|
|
1041
|
+
outline-offset: 2px;
|
|
1042
|
+
}
|
|
1043
|
+
.nwRange:disabled {
|
|
1044
|
+
opacity: 0.45;
|
|
1045
|
+
cursor: not-allowed;
|
|
1046
|
+
}
|
|
1047
|
+
.nwRange::-webkit-slider-runnable-track {
|
|
1048
|
+
height: 4px;
|
|
1049
|
+
border-radius: 999px;
|
|
1050
|
+
corner-shape: round;
|
|
1051
|
+
}
|
|
1052
|
+
.nwRange::-webkit-slider-thumb {
|
|
1053
|
+
width: 16px;
|
|
1054
|
+
height: 16px;
|
|
1055
|
+
border-radius: 50%;
|
|
1056
|
+
corner-shape: round;
|
|
1057
|
+
}
|
|
1058
|
+
.nwRange::-moz-range-track {
|
|
1059
|
+
height: 4px;
|
|
1060
|
+
border-radius: 999px;
|
|
1061
|
+
corner-shape: round;
|
|
1062
|
+
}
|
|
1063
|
+
.nwRange::-moz-range-thumb {
|
|
1064
|
+
width: 16px;
|
|
1065
|
+
height: 16px;
|
|
1066
|
+
border-radius: 50%;
|
|
1067
|
+
corner-shape: round;
|
|
1068
|
+
}
|
|
1069
|
+
.nwRangeVal {
|
|
1070
|
+
font-size: 12px;
|
|
1071
|
+
line-height: 16px;
|
|
1072
|
+
color: var(--dsw-alias-label-secondary);
|
|
1073
|
+
min-width: 36px;
|
|
1074
|
+
}
|
|
1075
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
1076
|
+
.nwRange {
|
|
1077
|
+
transition: opacity 0.15s ease;
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
/* ===== /V6:css ===== */
|
|
1081
|
+
/* ---- V6 挂载胶水样式(总负责人手管区) ---- */
|
|
1082
|
+
.nwIconGlyph svg,.nwToolGroupIcon svg{width:20px;height:20px;display:block}
|
|
1083
|
+
.nwIco{display:inline-flex;align-items:center;justify-content:center}
|
|
1084
|
+
.nwPrefGlyph .nwIco{color:inherit}
|
|
1085
|
+
/* ---- P2-1: .nwPref{background:none} 与 .nwRowOn 同特异性后写者胜 → 复合选择器放行 On 态底色(含 hover) ---- */
|
|
1086
|
+
.nwPref.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)}
|
|
1087
|
+
.nwPref.nwRowOn:hover{background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 10%,var(--dsw-alias-bg-layer-1));box-shadow:var(--dsw-elevation-panel)}
|
|
1088
|
+
/* ---- 裸堆叠行距(不在卡内的 PreferenceRow 与遗留 nwRow 混排);卡内行间由 RowDivider 分隔,分隔线打断相邻选择器故不叠加 ---- */
|
|
1089
|
+
.nwPref+.nwPref,.nwRow+.nwPref,.nwPref+.nwRow,.nwPref+.nwPlotBox{margin-top:10px}
|
|
1090
|
+
/* ---- 卡内元素拍平(导航卡/pill/路径收纳进 GroupCard 后的样式) ---- */
|
|
1091
|
+
.nwGCardBody .nwNavEntry{box-shadow:none;background:none;margin:0;border-radius:0;transform:none;padding:14px 16px}
|
|
1092
|
+
.nwGCardBody .nwNavEntry:hover{box-shadow:none;background:var(--dsw-alias-interactive-bg-hover);transform:none}
|
|
1093
|
+
.nwGCardBody .nwPillRow{padding:14px 16px 4px}
|
|
1094
|
+
.nwGCardBody .nwPlotBox{margin:8px 16px 4px}
|
|
1095
|
+
.nwGCardBody .nwRow{box-shadow:none;background:none;margin:0;border-radius:0;transform:none}
|
|
1096
|
+
.nwGCardBody .nwRow:hover{box-shadow:none;transform:none}
|
|
1097
|
+
/* ---- v6 页面级过渡(HyperOS/MIUⅨ 式 push-pop;方向类由 nwViewDir 决定;no-preference 门内播放) ---- */
|
|
1098
|
+
.nwView{min-width:0}
|
|
1099
|
+
@keyframes nwViewInFwd{from{opacity:0;transform:translateX(26px)}to{opacity:1;transform:none}}
|
|
1100
|
+
@keyframes nwViewInBack{from{opacity:0;transform:translateX(-26px)}to{opacity:1;transform:none}}
|
|
1101
|
+
@media (prefers-reduced-motion: no-preference){
|
|
1102
|
+
.nwViewFwd{animation:nwViewInFwd .3s var(--nw-ease)}
|
|
1103
|
+
.nwViewBack{animation:nwViewInBack .3s var(--nw-ease)}
|
|
1104
|
+
}
|
|
721
1105
|
`;
|
|
722
1106
|
// v3.0.0-UI3:已存在旧 style 标签也覆盖内容——否则插件脚本重复执行时新 CSS 永不注入(UI 停留在旧版)
|
|
723
1107
|
if (typeof document !== "undefined") {
|
|
@@ -1617,6 +2001,11 @@ var css = `
|
|
|
1617
2001
|
var state = props.controller.getSnapshot();
|
|
1618
2002
|
var on = state.enabled;
|
|
1619
2003
|
var view = state.view || "main";
|
|
2004
|
+
// v6.0.0:过渡方向(同 view 重渲染不重算——StrictMode 双渲染安全);零 state、零 hook
|
|
2005
|
+
if (nwPrevView !== view) {
|
|
2006
|
+
nwViewDir = (nwViewOrder[view] || 0) >= (nwViewOrder[nwPrevView] || 0) ? "fwd" : "back";
|
|
2007
|
+
nwPrevView = view;
|
|
2008
|
+
}
|
|
1620
2009
|
var el = react.createElement;
|
|
1621
2010
|
// v4.0.0 修正:三元优先级错误——此前 nwFlash 只拼在 nwPlotOk 分支,错误提示永远拿不到闪烁类
|
|
1622
2011
|
// v4.3.0 修正:错误分支此前要求 refreshedAt>0,而任何新提示写入都会把 refreshedAt 归零 → "错误提示也闪烁"恒不可达,
|
|
@@ -1626,18 +2015,18 @@ var css = `
|
|
|
1626
2015
|
// v4.3.0:加载期提示(panel.loading 词条早就存在却从未渲染——宿主 /state 挂起时面板只有一排灰按钮,用户不知道在等什么)
|
|
1627
2016
|
var loadingHint = state.loading ? el("div", { className: "nwStatus" }, "⟳ " + t("panel.loading")) : null;
|
|
1628
2017
|
var entry = function (title, hint, count, viewName) {
|
|
1629
|
-
return el("button", { type: "button", className: "nwNavEntry", onClick: function () { props.controller.openView(viewName); } },
|
|
1630
|
-
el("div", { className: "nwIconSlot" }, el
|
|
2018
|
+
return el("button", { type: "button", key: viewName, className: "nwNavEntry", onClick: function () { props.controller.openView(viewName); } },
|
|
2019
|
+
el("div", { className: "nwIconSlot" }, nwGlyph(el, "nwIconGlyph", viewName === "features" ? "navFeatures" : viewName === "tools" ? "navTools" : viewName === "creation" ? "navCreation" : viewName === "baseline" ? "navBaseline" : "navArrow", viewName === "features" ? "⚙" : viewName === "tools" ? "🛠" : viewName === "creation" ? "📖" : viewName === "baseline" ? "🎯" : "▸")),
|
|
1631
2020
|
el("div", { className: "nwNavEntryText" },
|
|
1632
2021
|
el("div", { className: "nwNavEntryTitle" }, title),
|
|
1633
2022
|
el("div", { className: "nwNavEntryHint" }, hint)
|
|
1634
2023
|
),
|
|
1635
2024
|
el("div", { className: "nwNavEntryRight" },
|
|
1636
|
-
count === null ? null : el
|
|
2025
|
+
count === null ? null : CountBadge(el, el, t, NW_UI, { on: count > 0, label: (function () {
|
|
1637
2026
|
// v4.0.0 修正:t() 缺键返回键名本身(永不为假值),此前 || 兜底恒不生效——改为显式判占位符
|
|
1638
2027
|
var tpl = t("group.onCount");
|
|
1639
2028
|
return (tpl.indexOf("{n}") === -1 ? "{n} 开" : tpl).replace("{n}", count);
|
|
1640
|
-
})()),
|
|
2029
|
+
})() }),
|
|
1641
2030
|
el("span", { className: "nwNavEntryArrow" }, "›")
|
|
1642
2031
|
)
|
|
1643
2032
|
);
|
|
@@ -1650,16 +2039,15 @@ var css = `
|
|
|
1650
2039
|
} }, "‹ " + t("panel.back"));
|
|
1651
2040
|
};
|
|
1652
2041
|
var switchRow = function (name, fOn, onToggle, extra) {
|
|
1653
|
-
return el
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
),
|
|
1660
|
-
el(Switch, { checked: fOn, disabled: state.loading, label: String(t("feature." + name)),
|
|
2042
|
+
return PreferenceRow(el, el, t, NW_UI, nwIconEls(el), {
|
|
2043
|
+
key: name,
|
|
2044
|
+
icon: NW_ICONS[name] ? name : "fallback",
|
|
2045
|
+
className: fOn ? "nwRowOn" : "",
|
|
2046
|
+
title: t("feature." + name),
|
|
2047
|
+
summary: extra ? [t("feature." + name + ".desc"), extra] : t("feature." + name + ".desc"),
|
|
2048
|
+
trailing: el(Switch, { checked: fOn, disabled: state.loading, label: String(t("feature." + name)),
|
|
1661
2049
|
onChange: function () { onToggle(name, !fOn); } })
|
|
1662
|
-
);
|
|
2050
|
+
});
|
|
1663
2051
|
};
|
|
1664
2052
|
function formatSize(bytes) {
|
|
1665
2053
|
if (!bytes || bytes <= 0) return "0 B";
|
|
@@ -1677,7 +2065,7 @@ var css = `
|
|
|
1677
2065
|
}
|
|
1678
2066
|
var pathBox = function (label, dir, target) {
|
|
1679
2067
|
if (!dir) {
|
|
1680
|
-
return el("div", { className: "nwPlotBox nwPlotBoxEmpty" },
|
|
2068
|
+
return el("div", { key: target, className: "nwPlotBox nwPlotBoxEmpty" },
|
|
1681
2069
|
el("div", { className: "nwPlotPath" }, label + ":" + t("plot.pathUnknown")),
|
|
1682
2070
|
el("div", { className: "nwBtnGroup" },
|
|
1683
2071
|
el("button", { type: "button", className: "nwBtn", disabled: true }, t("plot.open")),
|
|
@@ -1685,7 +2073,7 @@ var css = `
|
|
|
1685
2073
|
)
|
|
1686
2074
|
);
|
|
1687
2075
|
}
|
|
1688
|
-
return el("div", { className: "nwPlotBox" },
|
|
2076
|
+
return el("div", { key: target, className: "nwPlotBox" },
|
|
1689
2077
|
el("div", { className: "nwPlotPath", title: dir }, label + ":" + dir),
|
|
1690
2078
|
el("div", { className: "nwBtnGroup" },
|
|
1691
2079
|
el("button", { type: "button", className: "nwBtn", disabled: state.revealing, onClick: function () { props.controller.openDir(target); } }, t("plot.open")),
|
|
@@ -1726,7 +2114,7 @@ var TOOL_GROUPS = [
|
|
|
1726
2114
|
var patch = Object.assign({}, toolGroupOpen); patch[grp.key] = !isOpen;
|
|
1727
2115
|
props.controller.setToolGroupOpen(patch);
|
|
1728
2116
|
} },
|
|
1729
|
-
el
|
|
2117
|
+
nwGlyph(el, "nwToolGroupIcon", grp.key, grp.icon),
|
|
1730
2118
|
el("span", { className: "nwToolGroupHeadText" },
|
|
1731
2119
|
el("span", { className: "nwToolGroupName" }, t("toolGroup." + grp.key) + "(" + groupOnCount + "/" + grp.tools.length + ")"),
|
|
1732
2120
|
el("span", { className: "nwToolGroupDescInline" }, t("toolGroup." + grp.key + ".desc"))
|
|
@@ -1747,7 +2135,7 @@ var TOOL_GROUPS = [
|
|
|
1747
2135
|
else if (name === "novel_sentence_analysis") pathExtra = pathBox(t("dir.analysis"), dirs.analysisDir || "", "analysis-dir");
|
|
1748
2136
|
return el("div", { key: name, className: "nwToolItem" },
|
|
1749
2137
|
el("div", { className: "nwToolRow" + (checked ? " nwToolRowOn" : "") },
|
|
1750
|
-
el("div", { className: "nwIconSlot" }, el
|
|
2138
|
+
el("div", { className: "nwIconSlot" }, nwGlyph(el, "nwIconGlyph", "toolRow", "🧩")),
|
|
1751
2139
|
el("div", { className: "nwToolLabel" },
|
|
1752
2140
|
el("div", null, String(t("tool." + name)).replace(/\s+novel_\w+$/, "")),
|
|
1753
2141
|
el("div", { className: "nwToolDesc" }, desc)
|
|
@@ -1769,13 +2157,16 @@ var TOOL_GROUPS = [
|
|
|
1769
2157
|
backBtn(),
|
|
1770
2158
|
el("div", { className: "nwSectionTitle" }, t("panel.featuresTitle")),
|
|
1771
2159
|
el("div", { className: "nwToolsHint" }, t("panel.featuresHint")),
|
|
1772
|
-
detailFeatures.map(function (name) {
|
|
2160
|
+
GroupCard(el, el, t, NW_UI, { children: detailFeatures.map(function (name, idx) {
|
|
1773
2161
|
var extra = null;
|
|
1774
2162
|
if (name === "semanticEmbedding") {
|
|
1775
|
-
extra = el("button", { type: "button", className: "nwModelBtn", title: t("model.manage"), onClick: function (ev) { ev.stopPropagation(); props.controller.openView("model"); } }, "⚙ " + t("model.manage"));
|
|
2163
|
+
extra = el("button", { type: "button", key: "extra", className: "nwModelBtn", title: t("model.manage"), onClick: function (ev) { ev.stopPropagation(); props.controller.openView("model"); } }, "⚙ " + t("model.manage"));
|
|
1776
2164
|
}
|
|
1777
|
-
return
|
|
1778
|
-
|
|
2165
|
+
return [
|
|
2166
|
+
idx > 0 ? RowDivider(el, el, t, NW_UI, { key: "fd-" + name }) : null,
|
|
2167
|
+
switchRow(name, f[name] !== false, function (n, v) { props.toggle({ features: (function (patch) { patch[n] = v; return patch; })({}) }); }, extra)
|
|
2168
|
+
];
|
|
2169
|
+
}) }),
|
|
1779
2170
|
);
|
|
1780
2171
|
} else if (view === "tools") {
|
|
1781
2172
|
body = el("div", null,
|
|
@@ -1789,17 +2180,23 @@ var TOOL_GROUPS = [
|
|
|
1789
2180
|
backBtn(),
|
|
1790
2181
|
el("div", { className: "nwSectionTitle" }, "🖥 " + t("model.title")),
|
|
1791
2182
|
el("div", { className: "nwToolsHint" }, t("model.hint")),
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
el("div", { className: "
|
|
1795
|
-
el("div", { className: "
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
2183
|
+
GroupCard(el, el, t, NW_UI, { children: [
|
|
2184
|
+
// v2.6.0:语义引擎状态(模型就绪/未加载/缺失/失败)
|
|
2185
|
+
el("div", { key: "engine", className: "nwRow nwRowOff" },
|
|
2186
|
+
el("div", { className: "nwRowText" },
|
|
2187
|
+
el("div", { className: "nwRowLabel" }, t("model.engine")),
|
|
2188
|
+
el("div", { className: "nwRowHint" }, engineStatusText(state.embeddingStatus, t))
|
|
2189
|
+
)
|
|
2190
|
+
),
|
|
2191
|
+
RowDivider(el, el, t, NW_UI, { key: "rd-eng" }),
|
|
2192
|
+
switchRow("semanticSearch", f.semanticSearch !== false, function (n, v) { props.toggle({ features: (function (patch) { patch[n] = v; return patch; })({}) }); }),
|
|
2193
|
+
RowDivider(el, el, t, NW_UI, { key: "rd-s1" }),
|
|
2194
|
+
switchRow("semanticStyle", f.semanticStyle !== false, function (n, v) { props.toggle({ features: (function (patch) { patch[n] = v; return patch; })({}) }); }),
|
|
2195
|
+
RowDivider(el, el, t, NW_UI, { key: "rd-s2" }),
|
|
2196
|
+
switchRow("semanticImplicit", f.semanticImplicit !== false, function (n, v) { props.toggle({ features: (function (patch) { patch[n] = v; return patch; })({}) }); }),
|
|
2197
|
+
RowDivider(el, el, t, NW_UI, { key: "rd-s3" }),
|
|
2198
|
+
pathBox(t("dir.embedding"), dirs.embeddingDir || "", "embedding-dir")
|
|
2199
|
+
] }),
|
|
1803
2200
|
);
|
|
1804
2201
|
} else if (view === "baseline") {
|
|
1805
2202
|
// v3.0.0:风格基线——六维文笔指标 ±% 容差带(左=允许低于,右=允许高于)
|
|
@@ -1888,6 +2285,7 @@ var TOOL_GROUPS = [
|
|
|
1888
2285
|
NW_METRICS.map(function (item) {
|
|
1889
2286
|
var mk2 = item[0];
|
|
1890
2287
|
var d = draft[mk2] || DEFAULT_TOL;
|
|
2288
|
+
var aug = TolRangeRow(el, t, mk2, d, setDraft, state);
|
|
1891
2289
|
return el("div", { className: "nwTolRow", key: mk2, style: { display: "flex", alignItems: "center", gap: "10px", padding: "9px 2px" } },
|
|
1892
2290
|
el("div", { className: "nwTolName" }, el("span", { className: "nwTolIcon" }, item[2]), isEn ? (item[3] || item[1]) : item[1]),
|
|
1893
2291
|
el("div", { className: "nwTolField", style: { width: "74px", flexShrink: "0", display: "flex", flexDirection: "column", gap: "3px" } },
|
|
@@ -1897,6 +2295,7 @@ var TOOL_GROUPS = [
|
|
|
1897
2295
|
),
|
|
1898
2296
|
el("span", { className: "nwTolCaption" }, t("baseline.low"))
|
|
1899
2297
|
),
|
|
2298
|
+
aug[0],
|
|
1900
2299
|
el("span", { className: "nwTolSep" }, "~"),
|
|
1901
2300
|
el("div", { className: "nwTolField", style: { width: "74px", flexShrink: "0", display: "flex", flexDirection: "column", gap: "3px" } },
|
|
1902
2301
|
el("div", { style: { display: "flex", alignItems: "center", gap: "4px" } },
|
|
@@ -1905,6 +2304,7 @@ var TOOL_GROUPS = [
|
|
|
1905
2304
|
),
|
|
1906
2305
|
el("span", { className: "nwTolCaption" }, t("baseline.high"))
|
|
1907
2306
|
),
|
|
2307
|
+
aug[1],
|
|
1908
2308
|
el("span", { className: "nwTolPct" }, "%")
|
|
1909
2309
|
);
|
|
1910
2310
|
})
|
|
@@ -2169,127 +2569,132 @@ var TOOL_GROUPS = [
|
|
|
2169
2569
|
? el("div", { key: "updateStale", className: "nwUpdateStale" }, updateFailedText)
|
|
2170
2570
|
: null,
|
|
2171
2571
|
el("div", { className: "nwSection" },
|
|
2172
|
-
el
|
|
2173
|
-
|
|
2174
|
-
el
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
el("div", { className: "
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
)
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
)
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2572
|
+
SectionTitle(el, el, t, NW_UI, { label: "panel.secSwitch" }),
|
|
2573
|
+
GroupCard(el, el, t, NW_UI, { children: [
|
|
2574
|
+
PreferenceRow(el, el, t, NW_UI, nwIconEls(el), {
|
|
2575
|
+
key: "row-master",
|
|
2576
|
+
icon: "master",
|
|
2577
|
+
className: state.enabled ? "nwRowOn" : "",
|
|
2578
|
+
title: t("panel.enabled"),
|
|
2579
|
+
summary: t("panel.enabledHint"),
|
|
2580
|
+
trailing: el("div", { className: "nwSwitchWrap" },
|
|
2581
|
+
el(Switch, { checked: state.enabled, disabled: state.loading, label: t("panel.enabled"), onChange: function (value) { props.toggle({ enabled: value }); } }),
|
|
2582
|
+
CountBadge(el, el, t, NW_UI, { on: state.enabled, labelOn: "badge.on", labelOff: "badge.off" })
|
|
2583
|
+
)
|
|
2584
|
+
}),
|
|
2585
|
+
RowDivider(el, el, t, NW_UI, { key: "rd1" }),
|
|
2586
|
+
PreferenceRow(el, el, t, NW_UI, nwIconEls(el), {
|
|
2587
|
+
key: "row-autoAnalyze",
|
|
2588
|
+
icon: "autoAnalyze",
|
|
2589
|
+
className: state.autoAnalyze ? "nwRowOn" : "",
|
|
2590
|
+
title: t("panel.autoAnalyze"),
|
|
2591
|
+
summary: t("panel.autoAnalyzeHint"),
|
|
2592
|
+
trailing: el("div", { className: "nwSwitchWrap" },
|
|
2593
|
+
el(Switch, { checked: state.autoAnalyze, disabled: state.loading, label: t("panel.autoAnalyze"), onChange: function (value) { props.toggle({ autoAnalyze: value }); } }),
|
|
2594
|
+
CountBadge(el, el, t, NW_UI, { on: state.autoAnalyze, labelOn: "badge.on", labelOff: "badge.off" })
|
|
2595
|
+
)
|
|
2596
|
+
}),
|
|
2597
|
+
RowDivider(el, el, t, NW_UI, { key: "rd2" }),
|
|
2598
|
+
// v5.0.0:精简工作流——全局开关,与相邻开关同一套 switch/badge 组件。
|
|
2599
|
+
// B2 重排:由「提示词档位 / 提示词场景」两行之下移入「开关」分区——与「提示词场景」行的区别:它不受"仅完整档生效"限制(不给 disabled 逻辑),任何时候都可切换;
|
|
2600
|
+
// 打开(true)= 提示词换"按需调用"最简档,关闭(false,默认)= 维持 5.0.0 的自检/收尾/复测清单。
|
|
2601
|
+
PreferenceRow(el, el, t, NW_UI, nwIconEls(el), {
|
|
2602
|
+
key: "row-lean",
|
|
2603
|
+
icon: "lean",
|
|
2604
|
+
className: state.leanWorkflow ? "nwRowOn" : "",
|
|
2605
|
+
title: t("panel.leanWorkflow"),
|
|
2606
|
+
summary: t("panel.leanWorkflowHint"),
|
|
2607
|
+
trailing: el("div", { className: "nwSwitchWrap" },
|
|
2608
|
+
el(Switch, { checked: state.leanWorkflow === true, disabled: state.loading, label: t("panel.leanWorkflow"), onChange: function (value) { props.toggle({ leanWorkflow: value }); } }),
|
|
2609
|
+
CountBadge(el, el, t, NW_UI, { on: state.leanWorkflow, labelOn: "badge.on", labelOff: "badge.off" })
|
|
2610
|
+
)
|
|
2611
|
+
})
|
|
2612
|
+
] }),
|
|
2212
2613
|
),
|
|
2213
2614
|
el("div", { className: "nwSection" },
|
|
2214
|
-
el
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
el(
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
})
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
})
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2615
|
+
SectionTitle(el, el, t, NW_UI, { label: "panel.secPrompt" }),
|
|
2616
|
+
GroupCard(el, el, t, NW_UI, { children: [
|
|
2617
|
+
// v4.0.0:系统提示词注入档位(关闭 / 精简 / 完整)——三档分段按钮,沿用 dsh-unrestricted 的开关思路
|
|
2618
|
+
PreferenceRow(el, el, t, NW_UI, nwIconEls(el), {
|
|
2619
|
+
key: "row-prompt",
|
|
2620
|
+
icon: "prompt",
|
|
2621
|
+
className: state.systemPromptMode !== "off" ? "nwRowOn" : "",
|
|
2622
|
+
title: t("panel.spMode"),
|
|
2623
|
+
summary: t("panel.spModeHint"),
|
|
2624
|
+
trailing: el(NW_UI.SegmentedControl, {
|
|
2625
|
+
label: t("panel.spMode"),
|
|
2626
|
+
value: state.systemPromptMode,
|
|
2627
|
+
disabled: state.loading,
|
|
2628
|
+
options: ["off", "brief", "full"].map(function (m) { return { value: m, label: t("sp." + m) }; }),
|
|
2629
|
+
onChange: function (m) { if (state.systemPromptMode !== m) props.toggle({ systemPromptMode: m }); }
|
|
2630
|
+
})
|
|
2631
|
+
}),
|
|
2632
|
+
RowDivider(el, el, t, NW_UI, { key: "rd3" }),
|
|
2633
|
+
// v5.0.0:提示词场景——与上面的档位正交:档位决定"注入多少",场景决定"注入什么"。
|
|
2634
|
+
// v5.1.1 修正:**不再因为档位非 full 就把按钮 disabled**——用户反馈"场景里点不了其他选项",
|
|
2635
|
+
// 因为默认档位是 off/精简,五个按钮全灰且没有任何解释,等于把"选场景"锁死在一个看不见的前提后面。
|
|
2636
|
+
// 现在按钮始终可点(只受 loading 限制),档位非 full 时改用行内提示说明"已保存但暂不注入"。
|
|
2637
|
+
// 场景枚举必须与 lib/prompts.js 的 PROMPT_SCENES 保持一致(客户端是独立 bundle,不能 import)。
|
|
2638
|
+
PreferenceRow(el, el, t, NW_UI, nwIconEls(el), {
|
|
2639
|
+
key: "row-scene",
|
|
2640
|
+
icon: "scene",
|
|
2641
|
+
className: state.systemPromptMode === "full" ? "nwRowOn" : "",
|
|
2642
|
+
title: t("panel.spScene"),
|
|
2643
|
+
summary: t("panel.spSceneHint") + (state.systemPromptMode === "full" ? "" : " " + t("panel.spSceneInactive")),
|
|
2644
|
+
trailing: el(NW_UI.SegmentedControl, {
|
|
2645
|
+
label: t("panel.spScene"),
|
|
2646
|
+
value: state.promptScene || "general",
|
|
2647
|
+
disabled: state.loading,
|
|
2648
|
+
options: ["general", "writing", "revising", "auditing", "setup"].map(function (s) { return { value: s, label: t("sc." + s) }; }),
|
|
2649
|
+
onChange: function (s) { if ((state.promptScene || "general") !== s) props.toggle({ promptScene: s }); }
|
|
2650
|
+
})
|
|
2651
|
+
}),
|
|
2652
|
+
RowDivider(el, el, t, NW_UI, { key: "rd4" }),
|
|
2653
|
+
(function () {
|
|
2654
|
+
var rawOn = f.rawWriting === true;
|
|
2655
|
+
return PreferenceRow(el, el, t, NW_UI, nwIconEls(el), {
|
|
2656
|
+
key: "row-raw",
|
|
2657
|
+
tone: "danger",
|
|
2658
|
+
icon: "danger",
|
|
2659
|
+
title: "🔞 " + t("feature.rawWriting"),
|
|
2660
|
+
summary: el("div", { className: "nwRawDanger" }, t("raw.danger")),
|
|
2661
|
+
bottom: rawOn ? el("div", { className: "nwRawOn" }, t("raw.on")) : null,
|
|
2662
|
+
trailing: el(Switch, { checked: rawOn, disabled: state.loading, label: "🔞 " + t("feature.rawWriting"),
|
|
2663
|
+
onChange: function () { props.controller.rawToggle(!rawOn); } })
|
|
2664
|
+
});
|
|
2665
|
+
})()
|
|
2666
|
+
] }),
|
|
2265
2667
|
),
|
|
2266
2668
|
el("div", { className: "nwSection" },
|
|
2267
|
-
el
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
el("
|
|
2669
|
+
SectionTitle(el, el, t, NW_UI, { label: "panel.secStatus" }),
|
|
2670
|
+
GroupCard(el, el, t, NW_UI, { children: [
|
|
2671
|
+
entry(t("panel.featuresTitle"), t("panel.featuresHint"), onFeatures, "features"),
|
|
2672
|
+
RowDivider(el, el, t, NW_UI, { key: "rd5" }),
|
|
2673
|
+
entry(t("panel.toolsTitle"), t("panel.toolsHint"), onTools, "tools"),
|
|
2674
|
+
RowDivider(el, el, t, NW_UI, { key: "rd6" }),
|
|
2675
|
+
// v3.2.0:原创模式(设定库)移到主面板
|
|
2676
|
+
entry(t("panel.creationTitle"), t("panel.creationHint"), null, "creation"),
|
|
2677
|
+
RowDivider(el, el, t, NW_UI, { key: "rd7" }),
|
|
2678
|
+
// v3.0.0:风格基线(六维 ±% 容差带)
|
|
2679
|
+
entry(t("panel.baselineTitle"), t("panel.baselineHint"), null, "baseline"),
|
|
2680
|
+
// v3.7.0:功能开关(情感深度/风格检测/语义增强)在二级「功能开关」页维护——主页不重复展示
|
|
2681
|
+
RowDivider(el, el, t, NW_UI, { key: "rd8" }),
|
|
2682
|
+
// v2.6.0:数据目录占用 + 语义引擎状态(一目了然)
|
|
2683
|
+
el("div", { key: "pills", className: "nwPillRow" },
|
|
2684
|
+
el("div", { className: "nwPill" },
|
|
2685
|
+
el("span", { className: "nwPillKey" }, t("dir.size")),
|
|
2686
|
+
el("span", { className: "nwPillVal" }, formatSize(state.dataDirSize))
|
|
2687
|
+
),
|
|
2688
|
+
el("div", { className: "nwPill" },
|
|
2689
|
+
el("span", { className: "nwPillKey" }, t("model.engine")),
|
|
2690
|
+
el("span", { className: "nwPillVal" }, engineStatusText(state.embeddingStatus, t))
|
|
2691
|
+
)
|
|
2283
2692
|
),
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
)
|
|
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"),
|
|
2693
|
+
pathBox(t("dir.data"), dirs.dataDir || state.dataDir || "", "data-dir"),
|
|
2694
|
+
// v4.3.0:state.file / dirs.stateFile 此前写了 4 处却从不读取——宿主 reveal 支持 state-file
|
|
2695
|
+
//(lib/core.js allowedTargets 含 "state-file"),这里补一行路径卡把它用起来
|
|
2696
|
+
pathBox(t("dir.stateFile"), dirs.stateFile || state.file || "", "state-file")
|
|
2697
|
+
] }),
|
|
2293
2698
|
),
|
|
2294
2699
|
// v3.2.0:书库统计(写作打卡);null=未就绪不渲染,[]=真空提示
|
|
2295
2700
|
el("div", { key: "statsCard", className: "nwBoxed", style: { marginTop: "10px", padding: "10px 12px" } },
|
|
@@ -2360,29 +2765,25 @@ var TOOL_GROUPS = [
|
|
|
2360
2765
|
}
|
|
2361
2766
|
// 非净化模式弹窗(确认 + 倒计时 / 承诺输入)
|
|
2362
2767
|
if (state.rawModal) {
|
|
2363
|
-
body = el
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
el("
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
)
|
|
2373
|
-
);
|
|
2768
|
+
body = DialogCard(el, el, t, NW_UI, {
|
|
2769
|
+
title: "🔞 " + t("raw.confirmTitle"),
|
|
2770
|
+
description: t("raw.confirmText"),
|
|
2771
|
+
footer: [
|
|
2772
|
+
el("button", { key: "ok", type: "button", className: "nwBtn", disabled: state.rawCountdown > 0, onClick: function () { props.controller.rawConfirm(); } },
|
|
2773
|
+
state.rawCountdown > 0 ? t("raw.confirmWait") + " (" + state.rawCountdown + "s)" : t("raw.confirmOk")),
|
|
2774
|
+
el("button", { key: "cancel", type: "button", className: "nwBtn", onClick: function () { props.controller.rawCancel(); } }, t("raw.cancel"))
|
|
2775
|
+
]
|
|
2776
|
+
});
|
|
2374
2777
|
} else if (state.rawPromiseOpen) {
|
|
2375
|
-
body = el
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
el("
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
)
|
|
2385
|
-
);
|
|
2778
|
+
body = DialogCard(el, el, t, NW_UI, {
|
|
2779
|
+
title: "🔞 " + t("raw.promiseTitle"),
|
|
2780
|
+
description: t("raw.promiseText"),
|
|
2781
|
+
children: el("input", { key: "promise", type: "text", className: "nwModalInput", value: state.rawPromiseText || "", placeholder: t("raw.promisePlaceholder"), onChange: function (e) { props.controller.rawSetPromise(e.target.value); } }),
|
|
2782
|
+
footer: [
|
|
2783
|
+
el("button", { key: "ok", type: "button", className: "nwBtn" + (state.rawPromiseOk ? " nwBtnDanger" : ""), disabled: !state.rawPromiseOk, onClick: function () { props.controller.rawPromiseConfirm(); } }, t("raw.promiseOk")),
|
|
2784
|
+
el("button", { key: "cancel", type: "button", className: "nwBtn", onClick: function () { props.controller.rawCancel(); } }, t("raw.cancel"))
|
|
2785
|
+
]
|
|
2786
|
+
});
|
|
2386
2787
|
}
|
|
2387
2788
|
return el("div", { className: "nwPanel" + (props.variant === "main" ? " nwPanelMain" : "") },
|
|
2388
2789
|
el("div", { className: "nwPanelHeader" },
|
|
@@ -2400,7 +2801,7 @@ var TOOL_GROUPS = [
|
|
|
2400
2801
|
),
|
|
2401
2802
|
loadingHint,
|
|
2402
2803
|
msg,
|
|
2403
|
-
body
|
|
2804
|
+
el("div", { key: view, className: "nwView" + (nwViewDir === "back" ? " nwViewBack" : " nwViewFwd") }, body)
|
|
2404
2805
|
);
|
|
2405
2806
|
}
|
|
2406
2807
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-novel-writer",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "小说写作助手插件(
|
|
3
|
+
"version": "6.0.0",
|
|
4
|
+
"description": "小说写作助手插件(v6.0.0):句式/情感/意象分析、伏笔设定管理、本地语义检索、氛围光谱、风格画像报告、文笔六维基线带、原创模式与创作资料、写作哨兵(衔接/OOC/大纲);界面全面重构为「DSH 官方 token 用料 + MIUⅨ 风格信息架构」(源码分块 + 构建链装配、8 个统一组件、PreferenceRow 单一行模式、分组卡片、24 枚线图标、阈值 slider 辅助输入);附 stdio MCP 服务器(Claude Desktop / Cursor 可用)。",
|
|
5
5
|
"mcpName": "io.github.siweina/dsh-novel-writer",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "lib/index.js",
|
package/server.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "io.github.siweina/dsh-novel-writer",
|
|
4
4
|
"title": "dsh-novel-writer — 网文写作体检",
|
|
5
5
|
"description": "Offline toolkit: 18 tools for Chinese web-novel style, plot-thread and continuity audits.",
|
|
6
|
-
"version": "
|
|
6
|
+
"version": "6.0.0",
|
|
7
7
|
"repository": {
|
|
8
8
|
"url": "https://github.com/siweina/dsh-novel-writer",
|
|
9
9
|
"source": "github"
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
{
|
|
14
14
|
"registryType": "npm",
|
|
15
15
|
"identifier": "dsh-novel-writer",
|
|
16
|
-
"version": "
|
|
16
|
+
"version": "6.0.0",
|
|
17
17
|
"runtimeHint": "npx",
|
|
18
18
|
"packageArguments": [
|
|
19
19
|
{
|