dsh-maoying 0.1.13 → 0.1.15
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 +3 -3
- package/README.md +3 -3
- package/docs/creator-panel.md +19 -9
- package/docs/design.md +2 -2
- package/index.mjs +56 -2
- package/lib/base.css +45 -2
- package/lib/client.js +338 -48
- package/lib/novel-parse.mjs +6 -2
- package/lib/panel-core.mjs +135 -10
- package/lib/self-update.mjs +263 -0
- package/lib/version.mjs +11 -0
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -16,7 +16,7 @@ Once installed, the main agent becomes the **editor-in-chief of your novel**: it
|
|
|
16
16
|
- **Context on demand**: the chief reads index.yml for the big picture, then pulls only what the chapter at hand needs (previous chapter's ending ≈1500 chars + its outline + on-stage character cards + active foreshadow excerpts) — never the whole book.
|
|
17
17
|
- **Narrow tool surface**: the chief sees only "role delegation + internal retrieval + coordination"; helpers carry their line's tools (reviewer read-only, researcher web+notes only) — less per-request tool overhead and context weight.
|
|
18
18
|
- **Research**: the research helper searches the web for historical/geographic/domain material, files notes under `novel/research/`, separating fact from common lore from legend, with sources — a dividend of the DSH architecture.
|
|
19
|
-
- **Creator panel**: a full-page web panel from the DSH sidebar — Overview / Chapters / Characters / Foreshadowing / Relations / Timeline / Worldbuilding / Index Board
|
|
19
|
+
- **Creator panel**: a full-page web panel from the DSH sidebar — Overview (with in-place manuscript metadata editing) / Chapters / Characters / Foreshadowing / Relations / Timeline / Worldbuilding / Index Board; full-page chapter reading (outline side-by-side + five-dimension fields + status flow), form-based project.yml editing, UI language (中文/English), six font-size steps, and automatic new-version detection.
|
|
20
20
|
- **CLI fallback**: `maoying init / stats / index / export / brief / check` — six zero-dependency commands for scaffolding, stats, index rebuild, export, per-chapter writing cards and static checks.
|
|
21
21
|
|
|
22
22
|
## Quick Start
|
|
@@ -74,8 +74,8 @@ A reply of "ok / sure / go on" counts as confirmation; the chief advances direct
|
|
|
74
74
|
Open "**毛颖创作台**" at the bottom of the DSH sidebar — a full-page cockpit for the human eye, beyond what agents do:
|
|
75
75
|
|
|
76
76
|
- **Chapters**: status filters + the writing queue; click a chapter for full-page reading — back button, outline side-by-side (Markdown rendered), read-only manuscript, five narrative-dimension fields, one-click status flow.
|
|
77
|
-
- **
|
|
78
|
-
- **Top bar**: current version always visible; a "new version ↗" badge links to the release page when npm has a newer one; the gear opens Settings — UI language (中文/English), six font-size steps (100%–150%), all applied immediately and remembered.
|
|
77
|
+
- **Overview**: the "Edit" button on the manuscript card expands form-based editing of project.yml — title / author / genre / planned length / language / workflow phase / co-writing mode / synopsis (the former "Novel Settings" tab, merged in); saving preserves the file's existing comments. If project.yml was changed externally after the form loaded (manual edits or an agent advancing the phase), saving is rejected with a prompt to re-edit from the latest content instead of overwriting.
|
|
78
|
+
- **Top bar**: current version always visible; a "new version ↗" badge links to the release page when npm has a newer one, next to an "Upgrade now" button that installs the latest release into the current DSH profile with pnpm (profile-directory installs only; a DeepSeek restart is required to take effect); the gear opens Settings — UI language (中文/English), six font-size steps (100%–150%), all applied immediately and remembered.
|
|
79
79
|
|
|
80
80
|
The panel only writes state (chapter status, project.yml, index.yml rebuilds), never prose — prose belongs to the author and the chief.
|
|
81
81
|
|
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
- **上下文按需取**:主编先读 index.yml 概览,写到哪章读哪章(前章结尾约 1500 字 + 该章大纲 + 出场角色卡 + 活跃伏笔节选),不把整本书塞进上下文。
|
|
17
17
|
- **工具面收窄**:主编只见「角色委派 + 内部检索 + 协调」白名单,帮手各带本行工具(审校只读、考据只带联网+落笔记);每次请求的工具开销与上下文占用随之下降。
|
|
18
18
|
- **素材考据**:research 帮手联网检索历史/地理/专业领域素材,笔记落 `novel/research/`,区分史实/通说/演义并附来源——DSH 架构的红利。
|
|
19
|
-
- **创作台面板**:DSH
|
|
19
|
+
- **创作台面板**:DSH 侧栏一键打开的全页可视化面板——概览(含书稿元信息编辑)/章节/角色/伏笔/关系图/时间线/设定/索引板八个 tab;章节整页阅读(章纲对照 + 五维 + 状态流转)、project.yml 表单化编辑、顶栏字号六档缩放与自动新版检测。
|
|
20
20
|
- **CLI 兜底**:`maoying init / stats / index / export / brief / check` 六条零依赖命令管脚手架、统计、索引重建、导出、写作卡打包与静态体检。
|
|
21
21
|
|
|
22
22
|
## 快速开始
|
|
@@ -74,8 +74,8 @@ npm install -g dsh-maoying # 或装成全局 maoying 命令
|
|
|
74
74
|
DSH 左侧栏底部点「**毛颖创作台**」打开全页面板(改文件、跑流程之外,给肉眼看的驾驶舱):
|
|
75
75
|
|
|
76
76
|
- **章节**:状态筛选 + 按单写作清单;点章节进整页阅读——返回按钮、章纲对照(Markdown 渲染)、只读正文、五维叙事字段、一键状态流转。
|
|
77
|
-
-
|
|
78
|
-
- **顶栏**:当前版本号常驻;npm 上有新版时亮「有新版
|
|
77
|
+
- **概览**:书稿卡右上「编辑」按钮展开 project.yml 的书名/作者/题材/篇幅/语言/工作流阶段/共创模式/简介表单化编辑(原「小说设置」tab 并入),保存保留文件里原有注释;表单载入后 project.yml 被外部(作者手改 / agent 推进阶段)改过时拒绝保存并提示重改,不覆盖人工改动。
|
|
78
|
+
- **顶栏**:当前版本号常驻;npm 上有新版时亮「有新版 ↗」徽章直连发布页,旁边「一键升级」按钮直接用 pnpm 把最新包装进当前 DSH 档(仅支持 profile 目录安装;装完需重启 DeepSeek 才生效);齿轮打开系统设置,界面语言中文/English 可切、字号六档(100%–150%)整体缩放,均立即生效并记忆。
|
|
79
79
|
|
|
80
80
|
面板只写状态(章节状态、project.yml、index.yml 重建),不改正文——正文永远归作者与主编。
|
|
81
81
|
|
package/docs/creator-panel.md
CHANGED
|
@@ -39,17 +39,21 @@ webServer.register({ kind: 'prefix', path: '/api/maoying', handler: async (req,
|
|
|
39
39
|
|---|---|---|---|
|
|
40
40
|
| `client.css` | GET | — | text/css(lib/base.css 现读,no-cache) |
|
|
41
41
|
| `update-check` | GET | — | `{ ok, current, latest, updateAvailable, releaseUrl, checkedAt, error, source }`——新版检测(npm registry → npmmirror → GitHub 三级源,1h 缓存 + 7 天 stale-if-error,全败静默降级为 latest=null / error 非空) |
|
|
42
|
+
| `self-update` | POST | — | `{ ok, from, to, restartRequired: true }` 或 `{ ok: false, kind, error }`——面板内一键升级:安装形态/pnpm 探测通过后跑 `pnpm --dir <profileDir> add dsh-maoying@latest`(180s 超时,无 shell)。仅支持 `<dshHome>/profiles/<profile>/node_modules/dsh-maoying` 真实目录安装;symlink/dev 检出(dev-link)与非 profile 布局(unsupported-layout)、pnpm 缺失(pnpm-missing)、超时(timeout)、pnpm 非零退出(pnpm-failed)一律 200 + ok:false。**装完只换磁盘包,运行中进程仍是旧代码,restartRequired 恒 true——重启 DeepSeek 后生效** |
|
|
42
43
|
| `host-debug` | GET | — | `{ ok, hostVersion, trace, shape, registry, runtime }`——现场诊断(见下节) |
|
|
43
44
|
| `workspaces` | GET | — | `{ ok, workspaces: [{ path, name, hasNovel }] }`——workspaceRegistry.list() 逐根探测 `<root>/novel/project.yml` |
|
|
44
45
|
| `state` | GET | `path=<root>` | 全量快照(见 §3 契约) |
|
|
46
|
+
| `check` | GET | `path=<root>` | `{ ok, problems: [{ level, file, message }] }`——静态体检:与 CLI check 同源的 checkProject,读章节 frontmatter 原文(快照会把越枚举 status 归一成 pending,这里透出原始问题清单;level ∈ error/warn,可为空数组),概览「静态体检」黄条取数 |
|
|
45
47
|
| `chapter` | GET | `path`, `file`(chapters/ 下文件名) | `{ ok, file, frontmatter, body, outline }`——正文全文 + 对应章纲(`{ file, words, body }`,outlines/ 无对应文件时 null),章节抽屉用 |
|
|
48
|
+
| `outline` | GET | `path`, `file`(outlines/ 下 vXX-cXXX 命名的文件名) | `{ ok, file, words, body }`——章纲全文(章纲纯 markdown 无 frontmatter,body 即原文,words 为 CJK 计数),章节 tab 的章纲弹层用;非章纲文件名 422、章纲不存在 404 |
|
|
46
49
|
| `chapter-status` | POST | `{ path, file, status }` | `{ ok }`——frontmatter status 行原地替换;status 必须 ∈ pending/writing/review/accepted |
|
|
50
|
+
| `chapter-create` | POST | `{ path, file }`(outlines/ 下章纲文件名) | `{ ok, file, title }`——从章纲建章:chapters/ 落同 stem 的 status=pending 空章节(frontmatter 按 chapters/vXX-cXXX.md 模板,summary 与五维留空、正文待写;title 取章纲首条 `#` 标题行、去结尾「章纲」后缀),建后走既有 pending 流转;目标章节已存在 409、章纲不存在 404、非章纲文件名 422 |
|
|
47
51
|
| `foreshadow-status` | POST | `{ path, id, status }` | `{ ok }`——foreshadows.yml 对应条目 status 行替换;∈ planted/progressing/resolved/abandoned |
|
|
48
52
|
| `phase` | POST | `{ path, phase }` | `{ ok }`——project.yml phase 行替换;七态枚举 |
|
|
49
|
-
| `project-save` | POST | `{ path, values }`(8
|
|
53
|
+
| `project-save` | POST | `{ path, values, base_hash? }`(values 为 8 平键 title/author/genre/length/language/phase/mode/description;base_hash 为表单载入时的 project.yml 指纹,即快照 `project_hash`) | `{ ok, project_hash }`——mutateProject 逐键改写保行尾注释、缺键按模板补齐;phase/mode 越枚举与空书名 422;base_hash 与写入前现读原文不一致 → 409「外部修改冲突」拒写不落盘(人工改动保护:表单载入后文件被作者/agent 改过即拒绝静默覆盖;缺省 base_hash 跳过检查,兼容旧调用方)。project_hash 为写后新指纹,客户端可作下一轮基线 |
|
|
50
54
|
| `rebuild-index` | POST | `{ path }` | `{ ok, chapters }`——照 CLI index 同一逻辑重建 index.yml |
|
|
51
55
|
|
|
52
|
-
共
|
|
56
|
+
共 15 条。写操作全部「行级原地替换」:只在 frontmatter/目标 yml 的对应行上做字符串手术,写前读、写后校验(替换计数 ≠1 即 409 拒写并报错),不做整文件重排——把对 git diff 的扰动压到最小,也与 agent 的写法兼容。(`self-update` 是例外:它不碰小说文件,跑的是 pnpm 安装;`chapter-create` 是另一例外:它从章纲落一个全新章节文件,不动任何既有文件的字节。)
|
|
53
57
|
|
|
54
58
|
### host-debug 端点(现场诊断)
|
|
55
59
|
|
|
@@ -71,13 +75,14 @@ webServer.register({ kind: 'prefix', path: '/api/maoying', handler: async (req,
|
|
|
71
75
|
"root": "<workspace root>",
|
|
72
76
|
"novelDir": "<root>/novel",
|
|
73
77
|
"found": true, // false 时其余字段缺省,面板显示「未初始化」引导
|
|
78
|
+
"project_hash": "9f86d0…", // sha256(project.yml 原文) hex——面板保存回传 project-save 的 base_hash 做旧版检测(人工改动保护),读不到为 null;found:false 时恒为 null
|
|
74
79
|
"project": { "title","author","genre","length","language","phase","mode","description" }, // mode: ai-draft|co-write|author-draft(五期)
|
|
75
80
|
"chapters": [{ "file","volume","num","title","status","words","summary",
|
|
76
81
|
"cognitive_frame","emotional_anchor","world_texture",
|
|
77
82
|
"suspense","interpersonal_tension" }], // 按 volume,num 升序;words 为 CJK 计数
|
|
78
83
|
"characters": [{ "file","name","age","gender","role","appearance","personality",
|
|
79
84
|
"background","motivation","arc","markers" }], // markers 数组,缺省 []
|
|
80
|
-
"relations": [{ "a","b","type","description","intensity","started","ended" }],
|
|
85
|
+
"relations": [{ "a","b","type","description","intensity","started","ended" }], // a/b:relations.yml 里是角色 slug,出快照时对得上角色卡的端点归一成 name(关系图按 name 找节点),对不上原样保留
|
|
81
86
|
"foreshadows": [{ "id","title","status","priority","planted_chapter",
|
|
82
87
|
"expected_resolve","resolved_chapter","notes","source" }], // source: plan|backfill,缺省 plan
|
|
83
88
|
"timeline": [{ "year","title","description","importance","chapter" }],
|
|
@@ -101,12 +106,12 @@ webServer.register({ kind: 'prefix', path: '/api/maoying', handler: async (req,
|
|
|
101
106
|
挂载:
|
|
102
107
|
- 左侧栏底部动作位入口「毛颖创作台」(sidebar.footer.action 键控槽,ctx.effect 注入 + MutationObserver 自愈);
|
|
103
108
|
- 会话中栏全页 overlay:`html[data-dsh-maoying-active]` 驱动,`dsh-panel-activate` 事件与 mnemon/taskboard/ssh 等全页面板互斥,点侧栏会话行自动关闭;
|
|
104
|
-
- 顶部工作区选择器(workspaces 路由取数,hasNovel 的排前;无 novel 工作区显示引导卡:一句 `maoying init` 提示 +
|
|
109
|
+
- 顶部工作区选择器(workspaces 路由取数,hasNovel 的排前;无 novel 工作区显示引导卡:一句 `maoying init` 提示 + 复制按钮)。面板懒挂载、关闭不卸载,故每次打开都借 `dsh-panel-activate` 广播重拉一次 workspaces——外部新建/删除的工作区重开即见。
|
|
105
110
|
|
|
106
|
-
|
|
111
|
+
八个 tab(顺序即默认排布):
|
|
107
112
|
|
|
108
|
-
1. **概览**:project 卡(书名大字 + 类型/篇幅/语言 pills + 阶段徽章 +
|
|
109
|
-
2. **章节**:表格(卷-章/标题/状态徽章/字数/梗概),状态筛选 pills;表格上方「按单写作」区块——有章纲且 status=pending 的章按序列出(章纲节选 +
|
|
113
|
+
1. **概览**:project 卡(书名大字 + 类型/篇幅/语言 pills + 阶段徽章 + 简介,卡右上「编辑」按钮展开书稿元信息表单——原「小说设置」tab 并入,见 §4.1「小说设置并入概览」条目);统计卡(总字数/章节数/状态分布条);最近更新(chapters 按 mtime 前 5);warnings 黄条;静态体检黄条(check 路由问题清单,见 §4.1「人工改动保护 + 静态体检黄条」(0.1.14)条目)。
|
|
114
|
+
2. **章节**:表格(卷-章/标题/状态徽章/字数/梗概),状态筛选 pills;表格上方「按单写作」区块——有章纲且 status=pending 的章按序列出(章纲节选 + 状态徽章),未建章的孤儿章纲照列(「未建章」徽章 + 「建章」一键:POST chapter-create 落 pending 空章并重拉快照;行点击开章纲全文弹层,0.1.15 起),共创模式的主工作清单;行点击开整页阅读视图(0.1.12 起替代原右侧抽屉——fixed 侧弹层会被 DSH 侧边栏遮挡):返回按钮 + GET chapter 正文(只读,等宽滚动)+ 章纲对照栏(左右分栏或折叠段)+ 复制文件路径按钮 + 五维字段卡 + 状态流转按钮(当前态 → 下一态一键,pending→writing→review→accepted)。
|
|
110
115
|
3. **角色**:卡墙(姓名/role 徽章四色/年龄性别/性格一句/动机一行),点击看全卡(含 arc/markers/background)。
|
|
111
116
|
4. **伏笔**:四列看板(埋设/推进/已回收/弃用),卡片含 priority 色点(high 红/normal 灰/low 淡)、`source: backfill` 的「回填」小徽章(账本回填来的条目,区别于写前规划)、埋设章→预期回收章、notes 全文直显(0.1.10 去掉两行截断);卡片按钮:推进到下一状态(planted→progressing→resolved;abandoned 只能从看板拖不动、按钮给「弃用」)。列头计数。
|
|
112
117
|
5. **关系图**:canvas 力导向图——节点=角色(radius 随出场无关、固定 18px,名字 label 下方),边=relations(intensity 1-5 → 线宽 1-4px,type 标在边中点 hover 显示 description);拖拽节点、空白处滚轮缩放、双击复位;intensity 缺省 3。角色多于 30 个时只画与 relations 相连的节点,其余进「未入图」列表。
|
|
@@ -116,16 +121,21 @@ webServer.register({ kind: 'prefix', path: '/api/maoying', handler: async (req,
|
|
|
116
121
|
|
|
117
122
|
状态徽章色(全局统一):pending 灰 / writing 蓝 / review 橙 / accepted 绿;phase 七态沿用七色 pill。文案全中文、禁 emoji 图标(用 ui-primitives 图标或纯 CSS)。
|
|
118
123
|
|
|
119
|
-
## 4.1 后续迭代记录(0.1.10
|
|
124
|
+
## 4.1 后续迭代记录(0.1.10 起)
|
|
120
125
|
|
|
121
126
|
- **MaskPortal**:抽屉/弹层统一 portal 到 `document.body`——面板容器挂在宿主会话中栏,中栏 transform/overflow 会把 `position: fixed` 劫持成相对自身并连同裁剪(桌面端「点了没反应」实测病灶);无 createPortal 的老宿主退回原地渲染。
|
|
122
127
|
- **顶栏版本区**:当前版本号常驻;`update-check` 路由(模仿 dsh-paoding:npm registry → npmmirror → GitHub 三级源,1h 缓存 + 7 天 stale-if-error,全败静默)发现新版时亮「有新版 ↗」徽章。
|
|
123
128
|
- **系统设置**:齿轮弹窗,字号六档(100/110/120/130/140/150%)——面板根与 portal 节点读同一 `--myt-zoom` 变量整体 zoom,localStorage 记忆。
|
|
124
|
-
- **界面语言**:系统设置可选中文/English——全部 UI 文案走 `I18N` 词典(zh/en 各
|
|
129
|
+
- **界面语言**:系统设置可选中文/English——全部 UI 文案走 `I18N` 词典(zh/en 各 211 条,`tt`/`tf` 取词 + `{x}` 插值,zh 值与历史硬编码逐字一致保证中文界面零回归),切换立即生效整树重渲并 localStorage 记忆;语言自称(中文/日本語)与侧栏入口品牌名不翻译。
|
|
125
130
|
- **Markdown 渲染**:零依赖渲染器(先整体转义再变换),章纲对照与世界观/科学设定描述不再裸显 md 源码;正文手稿仍走 pre。
|
|
126
131
|
- **章节整页阅读**:替代右侧抽屉(侧边栏遮挡问题),返回按钮 + 章纲对照 + 五维 + 状态流转原位保留;阅读态隐藏 tab 条。
|
|
127
132
|
- **小说设置 tab**(第 9 个):project.yml 平键表单化编辑,`POST project-save` → `mutateProject` 逐键改写保行尾注释、缺键按模板补齐、phase/mode 越枚举与空书名 422。
|
|
128
133
|
- **字号六档改百分比**(0.1.13):档位从中文名(小/标准/大…)改为 100/110/120/130/140/150% 直读,旧 localStorage 档位值回落 100%。
|
|
134
|
+
- **人工改动保护 + 静态体检黄条**(0.1.14):快照顶层新增 `project_hash`(project.yml 原文 sha256,读不到为 null)——小说设置保存回传 `base_hash`,表单载入后 project.yml 被作者手改或 agent(如 mutatePhase 推进 phase)动过时 `project-save` 409 拒写并报「外部修改冲突」,客户端重拉快照把表单放回磁盘真值,不再静默覆盖同名键丢人工改动;概览新增「静态体检」黄条(GET `check`,与 CLI check 同源的 checkProject,透出快照归一掩盖的越枚举等问题,含 error 级时整条转错误色)。路由 11 → 12 条;I18N zh/en 各 209 → 211 条(check.title / check.fullHint)。
|
|
135
|
+
- **面板内一键升级**(0.1.14):`POST self-update`(lib/self-update.mjs,依赖全部可注入)——顶栏新版徽章旁加「一键升级」按钮,安装形态(`<dshHome>/profiles/<profile>/node_modules/dsh-maoying` 真实目录,symlink/dev 检出一票否决)与 pnpm 可执行性(PATH → homebrew/pnpm 官方安装位候选)双闸通过后跑 `pnpm --dir <profileDir> add dsh-maoying@latest`(180s 超时、无 shell、模块级 in-flight 去重),成功后失效版本检测缓存并提示「已安装 {to},重启 DeepSeek 后生效」——**装完只换磁盘包,重启才生效,UI 文案照实说**;安装中按钮禁用(前端该请求超时放宽到 200s),「已安装,待重启」后徽章收起。路由 12 → 13 条;I18N zh/en 各 211 → 219 条(header.upgrade* / toast.selfUpdate.*)。
|
|
136
|
+
- **按单写作兼容孤儿章纲**(0.1.15):章纲先于正文时面板不再零入口——「按单写作」区块在联交(有章纲且 status=pending 的章)之外照列未建章的孤儿章纲(按章纲序保序混排):「未建章」徽章 + 章纲节选 + 字数 + 两个动作(行点击开章纲全文弹层:MaskPortal + MdView 渲染 GET `outline` 的 body、words 一并显示;「建章」按钮 POST `chapter-create` 在 chapters/ 落 status=pending 空章——frontmatter 按 chapters/vXX-cXXX.md 模板、title 取章纲首条标题行去「章纲」后缀,成功 toast 后重拉快照,新章进表格、走既有 pending 流转,孤儿行按新快照自然消失);chapters/ 为空而 outlines/ 非空时空态改提示「下方按单写作已列出未建章章纲」。路由 13 → 15 条(outline GET / chapter-create POST);I18N zh/en 各 219 → 230 条(chapters.outlineRowTip / noChapter / createBtn / creating / createTip / emptyWithOutlines、toast.chapterCreated、outline.modalAria / closeAria / loading / loadFailed)。
|
|
137
|
+
- **小说设置并入概览**:原第 9 个「小说设置」tab 撤销(八个 tab),改为概览书稿卡右上「编辑」按钮——点击后 hero 卡原位切换为书稿元信息表单(ProjectMetaForm,8 平键 + 取消/保存),保存链路不变(`POST project-save` 携 `base_hash` 409 人工改动保护,成功重拉快照并退出编辑态,取消直接退出不落盘)。路由不变;I18N zh/en 各 230 → 232 条(tabs.settings 移除,overview.edit / overview.editTip / novel.cancel 新增)。
|
|
138
|
+
- **role 徽章本地化**(0.1.15):角色卡墙与全卡弹层的 role 徽章不再直出角色卡 frontmatter 原文——契约四态(`protagonist|antagonist|supporting|minor`)按界面语言出标签(主角/反派/配角/龙套;英文面板 Protagonist/Antagonist/Supporting/Minor),role 是自由文本、契约外取值原样展示不翻译;tone 归色逻辑(roleTone 关键词匹配)不变。I18N zh/en 各 232 → 236 条(role.protagonist / antagonist / supporting / minor)。
|
|
129
139
|
|
|
130
140
|
## 5. 验收基线
|
|
131
141
|
|
package/docs/design.md
CHANGED
|
@@ -172,7 +172,7 @@ persona prefix 内容骨架(agent.cordis.yml 内联,中文书写):
|
|
|
172
172
|
- ✅ **技能包(黄金三章/爽点节奏/审校清单/伏笔管理)**——已做:skills/ 四目录随包分发,安装器复制到 `<home>/skills/maoying-*`(逐字节比较幂等 + .installed-version 标记,见 index.mjs installSkills)。
|
|
173
173
|
- ✅ **导出(MD/TXT/EPUB)**——已做:`maoying export [dir] --format md|txt|epub [--out <目录>]`;EPUB 由 lib/epub.mjs 零依赖打包(手写 stored zip + CRC32 表格法,EPUB 3 最小合法结构)。
|
|
174
174
|
- ✅ **0.2.1-rc.2+ AgentPreset 形态适配**——已做:presets/maoying/agent.cordis.v2.yml(主编 + 5 角色 preset 的 v2 组合源)+ index.mjs composeManagedRows 按宿主版本双形态切换(v1 内联 / v2 AgentPreset,宿主升降级重启即整块换形状;防漂移由 test/composition.test.mjs 逐字节断言,见 README「兼容面」)。
|
|
175
|
-
- ✅ **Web 创作台面板**——已做:/api/maoying/*
|
|
175
|
+
- ✅ **Web 创作台面板**——已做:/api/maoying/* 十五条路由 + 左侧栏入口 + 会话中栏全页面板,八视图(概览(含书稿元信息编辑)/章节/角色/伏笔看板/关系图/时间线/设定/索引板);设计与契约见 docs/creator-panel.md。
|
|
176
176
|
|
|
177
177
|
## 9. 五期:共创模式(作者亲笔稿)
|
|
178
178
|
|
|
@@ -206,4 +206,4 @@ project.yml 加 `mode` 字段:`ai-draft`(默认,现状)/ `co-write`(
|
|
|
206
206
|
|
|
207
207
|
- `maoying brief <dir> <章号>` 写作卡:章纲 + 前章结尾约 1500 字 + 出场角色卡 + 活跃伏笔节选,打成一个 md(stdout 或 --out)。给作者带去任意编辑器/其他 AI;
|
|
208
208
|
- `maoying check <dir>` 静态体检(**不调模型**——LLM 审校走主编):frontmatter 完整性、五维缺键、状态枚举、章号/卷号规范、伏笔章号引用存在性、时间线年份乱序、角色卡悬空引用;
|
|
209
|
-
- 面板三件:章节抽屉加章纲对照栏 + 复制文件路径按钮;伏笔看板卡上 `source: backfill` 显示「回填」徽章;章节 tab 顶部「按单写作」区块(有章纲且 status=pending
|
|
209
|
+
- 面板三件:章节抽屉加章纲对照栏 + 复制文件路径按钮;伏笔看板卡上 `source: backfill` 显示「回填」徽章;章节 tab 顶部「按单写作」区块(有章纲且 status=pending 的章,按序列章纲节选;未建章的孤儿章纲照列——可看章纲全文、一键建章,0.1.15 起)。
|
package/index.mjs
CHANGED
|
@@ -55,6 +55,7 @@ import { closeSync, copyFileSync, existsSync, mkdirSync, openSync, readdirSync,
|
|
|
55
55
|
import { createHash } from 'node:crypto'
|
|
56
56
|
import { pathToFileURL } from 'node:url'
|
|
57
57
|
import { getVersionInfo } from './lib/version.mjs'
|
|
58
|
+
import { selfUpdate } from './lib/self-update.mjs'
|
|
58
59
|
import { readFile } from 'node:fs/promises'
|
|
59
60
|
import { createRequire } from 'node:module'
|
|
60
61
|
import os from 'node:os'
|
|
@@ -62,6 +63,7 @@ import path from 'node:path'
|
|
|
62
63
|
import { fileURLToPath } from 'node:url'
|
|
63
64
|
|
|
64
65
|
import {
|
|
66
|
+
createChapterFromOutline,
|
|
65
67
|
listWorkspaces,
|
|
66
68
|
mutateChapterStatus,
|
|
67
69
|
mutateForeshadowStatus,
|
|
@@ -69,9 +71,12 @@ import {
|
|
|
69
71
|
mutateProject,
|
|
70
72
|
parseStateSnapshot,
|
|
71
73
|
readChapter,
|
|
74
|
+
readOutlineFile,
|
|
72
75
|
rebuildIndex,
|
|
73
76
|
resolveNovelRoot,
|
|
74
77
|
} from './lib/panel-core.mjs'
|
|
78
|
+
// 静态体检(check 路由)与 CLI check 同源:直接复用 novel-parse 的检查器。
|
|
79
|
+
import { checkProject } from './lib/novel-parse.mjs'
|
|
75
80
|
import { loadYamlLib, resolveDshHome } from './lib/yaml-loader.mjs'
|
|
76
81
|
|
|
77
82
|
// loadYamlLib / resolveDshHome 已迁至 lib/yaml-loader.mjs(创作台业务层与
|
|
@@ -937,7 +942,7 @@ export async function registerPrefixRouteWithRetry(webServer, route, { attempts
|
|
|
937
942
|
}
|
|
938
943
|
|
|
939
944
|
/**
|
|
940
|
-
* 注册创作台全部路由(docs/creator-panel.md §2 的
|
|
945
|
+
* 注册创作台全部路由(docs/creator-panel.md §2 的 15 条)。返回宿主 disposer;
|
|
941
946
|
* webServer 缺席(非 web 部署)时抛错,由 apply 侧统一降级为 warn。
|
|
942
947
|
*/
|
|
943
948
|
export async function registerCreatorRoutes(ctx) {
|
|
@@ -990,6 +995,23 @@ export async function registerCreatorRoutes(ctx) {
|
|
|
990
995
|
return
|
|
991
996
|
}
|
|
992
997
|
|
|
998
|
+
if (route === 'self-update' && req.method === 'POST') {
|
|
999
|
+
// 面板内一键升级:形态/pnpm 检查 + `pnpm --dir <profileDir> add
|
|
1000
|
+
// dsh-maoying@latest`(细节与安全边界见 lib/self-update.mjs)。可
|
|
1001
|
+
// 预期的失败(dev-link / unsupported-layout / pnpm-missing / 超时 /
|
|
1002
|
+
// pnpm 非零退出)统一 HTTP 200 + ok:false,kind 给客户端分流、
|
|
1003
|
+
// message 经 sanitizeMessage 脱敏后透出;成功也带 restartRequired:
|
|
1004
|
+
// true——装完的只是磁盘上的新包,运行中的进程仍是旧代码,必须重启
|
|
1005
|
+
// DeepSeek 才生效,客户端文案必须照实转述。
|
|
1006
|
+
const r = await selfUpdate({ logger: ctx.logger })
|
|
1007
|
+
if (r.ok) {
|
|
1008
|
+
json(res, 200, { ok: true, from: r.from ?? null, to: r.to ?? null, restartRequired: true })
|
|
1009
|
+
} else {
|
|
1010
|
+
json(res, 200, { ok: false, kind: r.kind, error: sanitizeMessage(r.message ?? r.kind) })
|
|
1011
|
+
}
|
|
1012
|
+
return
|
|
1013
|
+
}
|
|
1014
|
+
|
|
993
1015
|
if (route === 'host-debug' && req.method === 'GET') {
|
|
994
1016
|
// 现场诊断:宿主版本五级探针轨迹 + 形状判定。与 client.css 同级信任
|
|
995
1017
|
// 围栏(loopback + 可信 Host 才放行),只暴露版本与路径类信息。
|
|
@@ -1057,6 +1079,17 @@ export async function registerCreatorRoutes(ctx) {
|
|
|
1057
1079
|
return
|
|
1058
1080
|
}
|
|
1059
1081
|
|
|
1082
|
+
if (route === 'check' && req.method === 'GET') {
|
|
1083
|
+
// 静态体检:checkProject 读章节 frontmatter 原文——快照会把越枚举
|
|
1084
|
+
// status 归一成 pending(人工改坏在面板里看不出来),这里原样透出
|
|
1085
|
+
// 问题清单,概览「静态体检」黄条取数。checkProject 自带的 ok 是
|
|
1086
|
+
// 「无 error」语义、summary 是 CLI 退出码原料,都不进面板契约
|
|
1087
|
+
// (§2 出参就是 { ok, problems }),客户端自己按 level 计数。
|
|
1088
|
+
const novelDir = resolveNovelRoot(url.searchParams.get('path'), ctx.workspaceRegistry)
|
|
1089
|
+
json(res, 200, { ok: true, problems: checkProject(novelDir).problems })
|
|
1090
|
+
return
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1060
1093
|
if (route === 'chapter' && req.method === 'GET') {
|
|
1061
1094
|
// 章节正文全文(抽屉用):file 只认 chapters/ 下纯文件名;响应组装
|
|
1062
1095
|
// 直传 readChapter 结果(五期起含对应章纲 outline 字段,无章纲为
|
|
@@ -1066,6 +1099,15 @@ export async function registerCreatorRoutes(ctx) {
|
|
|
1066
1099
|
return
|
|
1067
1100
|
}
|
|
1068
1101
|
|
|
1102
|
+
if (route === 'outline' && req.method === 'GET') {
|
|
1103
|
+
// 章纲全文(0.1.15,章纲弹层用):file 只认 outlines/ 下合 vXX-cXXX
|
|
1104
|
+
// 规范的纯文件名;响应 { ok, file, words, body }——章纲纯 markdown
|
|
1105
|
+
// 无 frontmatter,body 即原文,words 为 CJK 计数。
|
|
1106
|
+
const novelDir = resolveNovelRoot(url.searchParams.get('path'), ctx.workspaceRegistry)
|
|
1107
|
+
json(res, 200, readOutlineFile(novelDir, url.searchParams.get('file')))
|
|
1108
|
+
return
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1069
1111
|
if (route === 'chapter-status' && req.method === 'POST') {
|
|
1070
1112
|
const body = await readBody(req, res)
|
|
1071
1113
|
const novelDir = resolveNovelRoot(body.path, ctx.workspaceRegistry)
|
|
@@ -1073,6 +1115,16 @@ export async function registerCreatorRoutes(ctx) {
|
|
|
1073
1115
|
return
|
|
1074
1116
|
}
|
|
1075
1117
|
|
|
1118
|
+
if (route === 'chapter-create' && req.method === 'POST') {
|
|
1119
|
+
// 从章纲建章(0.1.15,按单写作的未建章章纲一键建章):chapters/
|
|
1120
|
+
// 落 status=pending 空章节(frontmatter 按模板、正文待写),建后走
|
|
1121
|
+
// 既有 pending 流转;已存在 409、章纲缺失 404、非章纲文件名 422。
|
|
1122
|
+
const body = await readBody(req, res)
|
|
1123
|
+
const novelDir = resolveNovelRoot(body.path, ctx.workspaceRegistry)
|
|
1124
|
+
json(res, 200, createChapterFromOutline(novelDir, body.file))
|
|
1125
|
+
return
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1076
1128
|
if (route === 'foreshadow-status' && req.method === 'POST') {
|
|
1077
1129
|
const body = await readBody(req, res)
|
|
1078
1130
|
const novelDir = resolveNovelRoot(body.path, ctx.workspaceRegistry)
|
|
@@ -1090,7 +1142,9 @@ export async function registerCreatorRoutes(ctx) {
|
|
|
1090
1142
|
if (route === 'project-save' && req.method === 'POST') {
|
|
1091
1143
|
const body = await readBody(req, res)
|
|
1092
1144
|
const novelDir = resolveNovelRoot(body.path, ctx.workspaceRegistry)
|
|
1093
|
-
|
|
1145
|
+
// base_hash = 面板表单载入时的 project.yml 指纹:外部改过 → 409 拒写
|
|
1146
|
+
// (人工改动保护);旧客户端不带该字段 → undefined,跳过检查。
|
|
1147
|
+
json(res, 200, mutateProject(novelDir, body.values, { baseHash: body.base_hash }))
|
|
1094
1148
|
return
|
|
1095
1149
|
}
|
|
1096
1150
|
|
package/lib/base.css
CHANGED
|
@@ -294,6 +294,13 @@ html[data-dsh-maoying-active]:not([data-dsh-mnemon-active]):not([data-dsh-taskbo
|
|
|
294
294
|
.myt-stripIcon { display: inline-flex; flex: none; }
|
|
295
295
|
.myt-stripText { min-width: 0; overflow-wrap: anywhere; }
|
|
296
296
|
.myt-stripWarn { border-color: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 40%, transparent); background: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 10%, transparent); color: var(--dsw-alias-state-warn-primary);}
|
|
297
|
+
/* 静态体检黄条的错误强调(叠加在 myt-stripWarn 上):含 error 级问题时整条
|
|
298
|
+
转错误色——纯警告(0 错误)保持黄色不动,一眼分清「有错」和「仅提醒」。 */
|
|
299
|
+
.myt-stripCheckErr {
|
|
300
|
+
border-color: color-mix(in srgb, var(--dsw-alias-state-error-primary) 40%, transparent);
|
|
301
|
+
background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent);
|
|
302
|
+
color: var(--dsw-alias-state-error-primary);
|
|
303
|
+
}
|
|
297
304
|
.myt-stripError {
|
|
298
305
|
border-color: color-mix(in srgb, var(--dsw-alias-state-error-primary) 40%, transparent);
|
|
299
306
|
background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent);
|
|
@@ -330,8 +337,10 @@ html[data-dsh-maoying-active]:not([data-dsh-mnemon-active]):not([data-dsh-taskbo
|
|
|
330
337
|
@keyframes myt-skel-sweep { to { transform: translateX(100%); } }
|
|
331
338
|
.myt-skelCard { height: 120px; border: 1px solid var(--dsw-alias-border-l2); border-radius: 14px; background: var(--dsw-alias-bg-layer-3); display: flex; flex-direction: column; gap: 10px; padding: 16px; box-sizing: border-box; }
|
|
332
339
|
.myt-skelRow { height: 14px; }
|
|
333
|
-
/* ──
|
|
340
|
+
/* ── 概览:书稿卡(含「编辑」入口)/ 统计卡 / 最近更新 ────────────────────── */
|
|
334
341
|
.myt-hero { gap: 12px; }
|
|
342
|
+
/* hero 卡右上「编辑」按钮:占位推到行尾(原小说设置 tab 并入概览)*/
|
|
343
|
+
.myt-heroEdit { margin-left: auto; }
|
|
335
344
|
.myt-heroTitle { margin: 0; font-size: 24px; line-height: 32px; font-weight: 700; color: var(--dsw-alias-label-primary); overflow-wrap: anywhere; }
|
|
336
345
|
.myt-heroMeta { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
|
|
337
346
|
.myt-heroDesc { margin: 0; font-size: 13px; line-height: 20px; color: var(--dsw-alias-label-secondary); overflow-wrap: anywhere; white-space: pre-wrap; }
|
|
@@ -519,6 +528,22 @@ html[data-dsh-maoying-active]:not([data-dsh-mnemon-active]):not([data-dsh-taskbo
|
|
|
519
528
|
text-overflow: ellipsis;
|
|
520
529
|
}
|
|
521
530
|
|
|
531
|
+
/* 孤儿章纲「未建章」徽章(0.1.15:按单写作兼容未建章章纲,章纲先于正文)*/
|
|
532
|
+
.myt-noChapterBadge {
|
|
533
|
+
flex: none;
|
|
534
|
+
padding: 0 6px;
|
|
535
|
+
border: 1px solid color-mix(in srgb, var(--dsw-alias-state-business-primary) 40%, transparent);
|
|
536
|
+
border-radius: 999px;
|
|
537
|
+
background: color-mix(in srgb, var(--dsw-alias-state-business-primary) 12%, transparent);
|
|
538
|
+
color: var(--dsw-alias-state-business-primary);
|
|
539
|
+
font-size: 10px;
|
|
540
|
+
line-height: 14px;
|
|
541
|
+
font-weight: 500;
|
|
542
|
+
white-space: nowrap;
|
|
543
|
+
}
|
|
544
|
+
/* 孤儿章纲行内的「建章」动作按钮:沿用伏笔卡 fsActions 的紧凑尺寸 */
|
|
545
|
+
.myt-writeOrderRow .myt-btn { flex: none; padding: 2px 8px; font-size: 11px; line-height: 16px; }
|
|
546
|
+
|
|
522
547
|
/* ── 角色 tab:卡墙 + 全卡弹层 ─────────────────────────────────────────── */
|
|
523
548
|
.myt-charWall { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 12px; }
|
|
524
549
|
.myt-charCard { cursor: pointer; gap: 8px; text-align: left; }
|
|
@@ -705,6 +730,24 @@ html[data-dsh-maoying-active]:not([data-dsh-mnemon-active]):not([data-dsh-taskbo
|
|
|
705
730
|
}
|
|
706
731
|
.myt-verBadge:hover { background: color-mix(in srgb, var(--dsw-alias-state-business-primary) 22%, transparent); }
|
|
707
732
|
|
|
733
|
+
/* 一键升级按钮:与版本徽章同形(描边胶囊),装完/安装中禁用降透明度 */
|
|
734
|
+
.myt-verUpgrade {
|
|
735
|
+
margin-left: 6px;
|
|
736
|
+
padding: 2px 10px;
|
|
737
|
+
border: 1px solid color-mix(in srgb, var(--dsw-alias-state-business-primary) 45%, transparent);
|
|
738
|
+
border-radius: 999px;
|
|
739
|
+
background: transparent;
|
|
740
|
+
color: var(--dsw-alias-state-business-primary);
|
|
741
|
+
font-size: 11px;
|
|
742
|
+
line-height: 16px;
|
|
743
|
+
font-weight: 500;
|
|
744
|
+
vertical-align: middle;
|
|
745
|
+
white-space: nowrap;
|
|
746
|
+
cursor: pointer;
|
|
747
|
+
}
|
|
748
|
+
.myt-verUpgrade:hover:not(:disabled) { background: color-mix(in srgb, var(--dsw-alias-state-business-primary) 12%, transparent); }
|
|
749
|
+
.myt-verUpgrade:disabled { opacity: 0.6; cursor: default; }
|
|
750
|
+
|
|
708
751
|
/* ── 版本号 + 系统设置(字号五档,面板整体 zoom)────────────────────────── */
|
|
709
752
|
.myt-verTag {
|
|
710
753
|
display: inline-block;
|
|
@@ -820,7 +863,7 @@ html[data-dsh-maoying-active]:not([data-dsh-mnemon-active]):not([data-dsh-taskbo
|
|
|
820
863
|
.myt-md a:hover { text-decoration: underline; }
|
|
821
864
|
.myt-md strong { color: var(--dsw-alias-label-primary); }
|
|
822
865
|
|
|
823
|
-
/* ──
|
|
866
|
+
/* ── 书稿元信息表单(概览「编辑」态展开)────────────────────────────────── */
|
|
824
867
|
.myt-setGrid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px 16px; }
|
|
825
868
|
.myt-setField { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
|
|
826
869
|
.myt-setFieldLabel { font-size: 11px; line-height: 16px; color: var(--dsw-alias-label-tertiary); }
|