@waterwx/dsh-novel-forge 1.7.0 → 1.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +1 -1
  2. package/lib/client.js +3197 -1887
  3. package/lib/client.js.map +1 -1
  4. package/lib/index.js +1026 -84
  5. package/lib/index.js.map +1 -1
  6. package/lib/types/client/api.d.ts +2 -0
  7. package/lib/types/client/locales.d.ts +0 -1
  8. package/lib/types/client/panel/FlowGuide.d.ts +21 -0
  9. package/lib/types/client/panel/MangaRoleLibrary.d.ts +18 -0
  10. package/lib/types/client/panel/MangaWorkspace.d.ts +2 -2
  11. package/lib/types/client/panel/StoryboardTab.d.ts +5 -1
  12. package/lib/types/engine.d.ts +15 -8
  13. package/lib/types/protocol.d.ts +138 -10
  14. package/lib/types/shot-language.d.ts +46 -0
  15. package/lib/types/story-beat-language.d.ts +35 -0
  16. package/package.json +5 -2
  17. package/skills/chapter-batch/SKILL.md +81 -0
  18. package/src/client/api.ts +5 -0
  19. package/src/client/locales.ts +0 -1
  20. package/src/client/panel/AssetsTab.tsx +22 -22
  21. package/src/client/panel/AssistantTab.tsx +3 -3
  22. package/src/client/panel/BookshelfBar.tsx +1 -1
  23. package/src/client/panel/CreateBookView.tsx +14 -10
  24. package/src/client/panel/FlowGuide.tsx +83 -0
  25. package/src/client/panel/ImportModal.tsx +11 -2
  26. package/src/client/panel/MangaRoleLibrary.tsx +810 -0
  27. package/src/client/panel/MangaWorkspace.tsx +359 -297
  28. package/src/client/panel/NovelPanel.tsx +108 -107
  29. package/src/client/panel/ReaderView.tsx +4 -4
  30. package/src/client/panel/RunPanel.tsx +9 -9
  31. package/src/client/panel/SceneLibrary.tsx +68 -67
  32. package/src/client/panel/ShelfView.tsx +2 -2
  33. package/src/client/panel/StoryboardTab.tsx +145 -49
  34. package/src/client/panel/StyleCard.tsx +13 -13
  35. package/src/client/panel/StyleGallery.tsx +2 -2
  36. package/src/client/panel/WorldTab.tsx +5 -5
  37. package/src/client/panel/panel.module.css +861 -510
  38. package/src/client/panel/views.tsx +24 -24
  39. package/src/engine.ts +514 -62
  40. package/src/index.ts +1 -0
  41. package/src/protocol.ts +147 -10
  42. package/src/routes.ts +216 -7
  43. package/src/run.ts +5 -4
  44. package/src/shot-language.ts +181 -0
  45. package/src/story-beat-language.ts +130 -0
  46. package/lib/types/client/panel/RoleVisualPanel.d.ts +0 -15
  47. package/src/client/panel/RoleVisualPanel.tsx +0 -485
@@ -100,6 +100,8 @@ export declare class NovelApi {
100
100
  /** 角色库:AI 提炼 / 采纳 / 更新 / 删除。 */
101
101
  imageTest(req: import('../protocol.ts').ImageTestRequest): Promise<import('../protocol.ts').ImageTestResponse>;
102
102
  roles(req: import('../protocol.ts').RolesRequest): Promise<import('../protocol.ts').RolesResponse>;
103
+ /** 漫剧角色库:从分镜提名 / 建卡 / 更新 / 删除 / 形象锚点 / 精修提示词。 */
104
+ mangaRoles(req: import('../protocol.ts').MangaRolesRequest): Promise<import('../protocol.ts').MangaRolesResponse>;
103
105
  /** 场景库:AI 提炼 / 采纳 / 更新 / 删除 / 图集。 */
104
106
  scenes(req: import('../protocol.ts').ScenesRequest): Promise<import('../protocol.ts').ScenesResponse>;
105
107
  /** 视觉世界观规则:提炼 / 保存。 */
@@ -153,7 +153,6 @@ export declare const zh: {
153
153
  readonly 'settings.reasoning.max': "max · 最高思考(质检/推演)";
154
154
  readonly 'settings.theme': "主题";
155
155
  readonly 'settings.themeLiquid': "iOS 液态玻璃(绿)";
156
- readonly 'settings.themeClassic': "经典毛玻璃(蓝)";
157
156
  readonly 'settings.themeNeumorph': "新拟物(双阴影)";
158
157
  readonly 'settings.themeHint': "存于浏览器本地,即时生效";
159
158
  readonly 'settings.chapterChars': "每章目标字数";
@@ -0,0 +1,21 @@
1
+ /**
2
+ * 漫剧工作台·全流程步骤条(唯一导航,方案X):
3
+ * ①创建方案 → ②视觉规则 → ③剧情骨架 → ④分镜表 → ⑤导入角色 → ⑥角色定妆 → ⑦视频提示词 → ⑧场景库 → ⑨导出使用。
4
+ * 每个步骤对应一个独立页面主体;完成度自动判定(读 project 现有字段)。
5
+ */
6
+ import type { ProjectState } from '../../protocol.ts';
7
+ /** 步骤 → 页面主体的一一对应目标。 */
8
+ export type FlowTarget = 'plan' | 'rules' | 'skeleton' | 'table' | 'import' | 'makeup' | 'prompts' | 'scenes' | 'export';
9
+ export interface FlowStep {
10
+ no: number;
11
+ label: string;
12
+ hint: string;
13
+ done: boolean;
14
+ target: FlowTarget;
15
+ }
16
+ /** 依据 project 数据计算 9 步完成度(与存储顺序无关,按生产顺序排列)。 */
17
+ export declare function computeFlowSteps(project: ProjectState | null): FlowStep[];
18
+ export declare function FlowGuide({ project, onNavigate, }: {
19
+ project: ProjectState | null;
20
+ onNavigate: (target: FlowTarget) => void;
21
+ }): import("react").JSX.Element;
@@ -0,0 +1,18 @@
1
+ import type { NovelApi } from '../api.ts';
2
+ import type { ImageModelConfig, ProjectState } from '../../protocol.ts';
3
+ export declare function MangaRoleLibrary({ api, project, refresh, styleId, filterId, imageApiEnabled, imageModels, focus, showCards, onProgress, }: {
4
+ api: NovelApi;
5
+ project: ProjectState | null;
6
+ refresh: () => void | Promise<void>;
7
+ styleId?: string;
8
+ filterId?: string;
9
+ /** 是否启用生图(豆包等出定妆图)。 */
10
+ imageApiEnabled?: boolean;
11
+ /** 生图模型库(出定妆图时可选择用哪条)。 */
12
+ imageModels?: ImageModelConfig[];
13
+ /** 步骤页聚焦:import=展开并滚动到导入区;cards=滚动到已建漫剧卡列表。 */
14
+ focus?: 'import' | 'cards';
15
+ /** 是否显示「已建漫剧卡」列表(第⑤步导入页可隐藏,只留导入区)。 */
16
+ showCards?: boolean;
17
+ onProgress?: (text: string, kind?: 'info' | 'done' | 'error') => void;
18
+ }): import("react").JSX.Element;
@@ -6,9 +6,9 @@ export declare function MangaWorkspace({ api, project, chapters, onProjectChange
6
6
  chapters: ChapterPlan[];
7
7
  /** 方案/资产变更已持久化后触发(刷新项目)。 */
8
8
  onProjectChanged?: () => void | Promise<void>;
9
- /** 是否启用生图。 */
9
+ /** 是否启用生图(漫剧卡出定妆图)。 */
10
10
  imageApiEnabled?: boolean;
11
- /** 生图模型库(AI 生图可选模型)。 */
11
+ /** 生图模型库(出定妆图时可选择模型)。 */
12
12
  imageModels?: ImageModelConfig[];
13
13
  /** 上报到「AI进度」控制台(漫剧工作台内所有 LLM/方案操作)。 */
14
14
  onProgress?: (text: string, kind?: 'info' | 'done' | 'error') => void;
@@ -1,6 +1,6 @@
1
1
  import type { NovelApi } from '../api.ts';
2
2
  import type { ChapterPlan, ProjectState } from '../../protocol.ts';
3
- export declare function StoryboardTab({ api, project, chapters, onProjectChanged, styleId, filterId, onProgress, }: {
3
+ export declare function StoryboardTab({ api, project, chapters, onProjectChanged, styleId, filterId, mode, onGoStep, onProgress, }: {
4
4
  api: NovelApi;
5
5
  project: ProjectState | null;
6
6
  chapters: ChapterPlan[];
@@ -10,6 +10,10 @@ export declare function StoryboardTab({ api, project, chapters, onProjectChanged
10
10
  styleId?: string;
11
11
  /** 可选滤镜风格 id。 */
12
12
  filterId?: string;
13
+ /** 页面模式:auto=按本章实际进度显示;skeleton/table/prompts=固定显示该步骤(前置不足显示提示,不降级)。 */
14
+ mode?: 'auto' | 'skeleton' | 'table' | 'prompts';
15
+ /** 「下一步」按钮回调(1=骨架→2=分镜表→3=提示词),供外层切步骤页。 */
16
+ onGoStep?: (n: 1 | 2 | 3) => void;
13
17
  /** 上报到「AI进度」控制台(分镜三步生成)。 */
14
18
  onProgress?: (text: string, kind?: 'info' | 'done' | 'error') => void;
15
19
  }): import("react").JSX.Element;
@@ -12,7 +12,7 @@
12
12
  */
13
13
  export declare const COMPLIANCE_REDLINES: ReadonlyArray<string>;
14
14
  import type { Context } from '@deepseek-ai/cordis';
15
- import type { AuditIssue, AuthorReview, BreakdownResponse, ChapterPlan, Foreshadow, NovelConfig, OutlineCandidate, Plotline, PlotlineHealthReport, PlotlinePlan, ProjectState, ReviewReport, RoleRecord, RoleStatusCard, SceneCard, StoryBible, StoryboardSkeleton, StoryboardTable, StoryboardPrompt, Volume, WorldState } from './protocol.ts';
15
+ import type { AuditIssue, AuthorReview, BreakdownResponse, ChapterPlan, Foreshadow, NovelConfig, OutlineCandidate, Plotline, PlotlineHealthReport, PlotlinePlan, ProjectState, ReviewReport, RoleRecord, RoleStatusCard, SceneCard, StoryBible, StoryboardSkeleton, StoryboardTable, StoryboardPrompt, MangaRoleCandidate, Volume, WorldState } from './protocol.ts';
16
16
  /** Project state file name inside the output dir. */
17
17
  export declare const PROJECT_FILE = "novel-project.json";
18
18
  /** Chapter output file name, e.g. 第001章_开篇.md */
@@ -109,16 +109,20 @@ export interface RoleVisualPrompt {
109
109
  /** 四类精修提示词(立绘/四视图/表情/细节,一次提炼直接产出)。 */
110
110
  promptKit?: RoleRecord['promptKit'];
111
111
  }
112
- /**
113
- * 为单个角色提炼「动漫形象描述词」:扫描该角色出场的已写章节正文,
114
- * 截取含外貌描写的段落,交给 LLM 提炼中文描述 + 英文绘图标签。
115
- */
112
+ /** 小说角色库:提炼单个角色的形象锚点并写回角色卡。 */
116
113
  export declare function extractRoleVisual(ctx: Context, config: NovelConfig, project: ProjectState, outputDir: string, roleName: string, styleId?: string, filterId?: string): Promise<RoleVisualPrompt>;
114
+ /** 漫剧角色卡:提炼形象锚点并写回漫剧卡(status → anchored)。 */
115
+ export declare function extractMangaRoleVisual(ctx: Context, config: NovelConfig, project: ProjectState, outputDir: string, cardId: string, styleId?: string, filterId?: string): Promise<RoleVisualPrompt>;
116
+ /** 小说角色库:角色四类生图提示词精修包(写回角色卡)。 */
117
+ export declare function generateRolePromptKit(ctx: Context, config: NovelConfig, project: ProjectState, roleName: string, styleId?: string, filterId?: string): Promise<RoleRecord['promptKit']>;
118
+ /** 漫剧角色卡:四类生图提示词精修包(写回漫剧卡,status → anchored)。 */
119
+ export declare function generateMangaRolePromptKit(ctx: Context, config: NovelConfig, project: ProjectState, cardId: string, styleId?: string, filterId?: string): Promise<RoleRecord['promptKit']>;
117
120
  /**
118
- * 角色四类生图提示词精修包(promptKit):以锚点 + 表情清单 + 视觉规则为输入,
119
- * LLM 产出 立绘/四视图/表情×N/细节 四套 zh+en 提示词(比前端拼装更精炼)。
121
+ * 漫剧角色库·提名(两段式):从某章分镜的 characters 提名候选角色名 →
122
+ * 规则过滤(精确名 + 身份/简称匹配,短名单 ≤5)→ LLM 确认(是/否 + 选哪个,不做开放检索)→
123
+ * 返回带漫剧卡建议的候选(未匹配时给出「回小说库补提炼 / 漫剧直接创建」判定)。
120
124
  */
121
- export declare function generateRolePromptKit(ctx: Context, config: NovelConfig, project: ProjectState, roleName: string, styleId?: string, filterId?: string): Promise<RoleRecord['promptKit']>;
125
+ export declare function nominateMangaRoles(ctx: Context, config: NovelConfig, project: ProjectState, outputDir: string, chapterNo: number): Promise<MangaRoleCandidate[]>;
122
126
  /** ✨ 从道藏/红线提炼「视觉世界观规则」:生图/生视频必须遵守的设定纠偏(如"商品=人,禁止常规超市商品")。 */
123
127
  export declare function extractVisualRules(ctx: Context, config: NovelConfig, project: ProjectState): Promise<string[]>;
124
128
  /**
@@ -143,7 +147,10 @@ export declare function testImageEndpoint(baseURL: string, apiKey: string, model
143
147
  message?: string;
144
148
  modelFound?: boolean;
145
149
  }>;
150
+ /** 小说角色库:生成角色定妆图并写回角色卡(imageUrl)。 */
146
151
  export declare function generateRoleReferenceImage(ctx: Context, config: NovelConfig, project: ProjectState, outputDir: string, name: string, style?: string, modelId?: string): Promise<string>;
152
+ /** 漫剧角色卡:生成定妆图并写回漫剧卡(imageUrl)。 */
153
+ export declare function generateMangaRoleReferenceImage(ctx: Context, config: NovelConfig, project: ProjectState, outputDir: string, cardId: string, style?: string, modelId?: string): Promise<string>;
147
154
  /** 🩺 剧情健康检查:基于已写章节数/各线状态/编年录,判断是否需要新线及添加时机。 */
148
155
  export declare function analyzePlotlineHealth(ctx: Context, config: NovelConfig, project: ProjectState): Promise<PlotlineHealthReport>;
149
156
  /** ✨ AI 剧情方案:基于健康检查结果设计下一阶段方向与建议新线。 */
@@ -4,6 +4,8 @@
4
4
  * state file format, and the NDJSON generation stream frames all live here so
5
5
  * both halves spell exactly one vocabulary.
6
6
  */
7
+ import type { ShotSizeId, CameraMoveId, CompositionId, LightingId } from './shot-language.ts';
8
+ import type { StoryFunctionId, EmotionId } from './story-beat-language.ts';
7
9
  /** The /api/dsh-novel-forge route family (same-origin, loopback-fenced). */
8
10
  export declare const NOVEL_API: {
9
11
  readonly status: "/api/dsh-novel-forge/status";
@@ -43,6 +45,8 @@ export declare const NOVEL_API: {
43
45
  readonly bookshelfImportTextPreview: "/api/dsh-novel-forge/bookshelf/import-text/preview";
44
46
  /** 漫剧方案管理:create/remove/activate。 */
45
47
  readonly manhuaPlans: "/api/dsh-novel-forge/manhua/plans";
48
+ /** 漫剧角色库:从分镜提名(规则+LLM两段式)/ 建卡 / 更新 / 删除 / 形象锚点 / 精修提示词。 */
49
+ readonly mangaRoles: "/api/dsh-novel-forge/manga/roles";
46
50
  /** 分镜·编剧级:单章 → 剧情骨架(节拍链)。 */
47
51
  readonly storyboardSkeleton: "/api/dsh-novel-forge/storyboard/skeleton";
48
52
  /** 分镜·导演级:骨架 → 分镜表(镜头级)。 */
@@ -206,10 +210,10 @@ export interface StoryboardBeat {
206
210
  id: string;
207
211
  /** 事件一句话(发生了什么)。 */
208
212
  event: string;
209
- /** 情绪走向(如 压抑→隐忍→惊惧)。 */
210
- emotion: string;
211
- /** 叙事功能:铺垫/冲突/转折/高潮/收束/伏笔/人物塑造。 */
212
- function: string;
213
+ /** 情绪走向(情绪词 id 数组,来自 story-beat-language 词库,链式)。 */
214
+ emotion: EmotionId[];
215
+ /** 叙事功能(枚举 id,来自 story-beat-language 词库)。 */
216
+ function: StoryFunctionId;
213
217
  /** 因果:承接上一节拍的原因(可选)。 */
214
218
  cause?: string;
215
219
  }
@@ -220,6 +224,8 @@ export interface StoryboardSkeleton {
220
224
  arc: string;
221
225
  /** 剧情节拍链(时间顺序,因果连贯)。 */
222
226
  beats: StoryboardBeat[];
227
+ /** 本章全部出镜角色(正文中的确切称谓,去重;漫剧角色库提名地基)。 */
228
+ characters?: string[];
223
229
  }
224
230
  /** POST /storyboard/skeleton 请求:生成单章剧情骨架。 */
225
231
  export interface StoryboardSkeletonRequest {
@@ -235,10 +241,12 @@ export interface StoryboardShot {
235
241
  id: string;
236
242
  /** 挂载的节拍 id(骨架中的 b1…)。 */
237
243
  beatId: string;
238
- /** 景别:远景/全景/中景/近景/特写。 */
239
- shot: string;
240
- /** 机位与运镜(如 低机位仰拍 + 缓慢推近)。 */
241
- camera: string;
244
+ /** 景别(枚举 id,来自 shot-language 词库)。 */
245
+ shot: ShotSizeId;
246
+ /** 机位与运镜(枚举 id 数组,来自 shot-language 词库)。 */
247
+ camera: CameraMoveId[];
248
+ /** 构图(可选,枚举 id)。 */
249
+ composition?: CompositionId;
242
250
  /** 时长(秒,1-12)。 */
243
251
  duration: number;
244
252
  /** 画面内容:角色动作 + 表情 + 服装/标志物。 */
@@ -247,12 +255,16 @@ export interface StoryboardShot {
247
255
  line: string;
248
256
  /** 音效(无则空字符串)。 */
249
257
  sound: string;
250
- /** 光效(如 雨夜顶光 + 手机屏幕冷光)。 */
251
- light: string;
258
+ /** 光效(枚举 id 数组,来自 shot-language 词库)。 */
259
+ light: LightingId[];
252
260
  /** 承接上一镜头结尾状态(位置/动作/情绪/服装)。 */
253
261
  prevState: string;
254
262
  /** 本镜头结束状态。 */
255
263
  nextState: string;
264
+ /** 本镜头出镜角色(引用小说库规范名/漫剧卡名,1-4 个;漫剧角色库提名地基)。 */
265
+ characters?: string[];
266
+ /** 本镜头已绑定漫剧卡的 id(定妆图引用,供出图/生视频时按卡取参考图)。 */
267
+ mangaRoleIds?: string[];
256
268
  }
257
269
  /** 分镜·导演级:单章分镜表。 */
258
270
  export interface StoryboardTable {
@@ -260,6 +272,10 @@ export interface StoryboardTable {
260
272
  shots: StoryboardShot[];
261
273
  /** 生成时注入的场景卡名称(前端标注消费关系)。 */
262
274
  usedScenes?: string[];
275
+ /** 本章全部出镜角色(各镜头 characters 去重汇总;骨架有则兜底)。 */
276
+ characters?: string[];
277
+ /** 本章已绑定漫剧卡的 id(去重汇总,定妆图引用)。 */
278
+ mangaRoleIds?: string[];
263
279
  }
264
280
  /** POST /storyboard/table 请求:骨架 → 分镜表。 */
265
281
  export interface StoryboardTableRequest {
@@ -281,6 +297,8 @@ export interface StoryboardPrompt {
281
297
  shotId: string;
282
298
  /** 即梦/视频模型可粘贴的中文提示词(画面+运镜+光效+风格词块)。 */
283
299
  text: string;
300
+ /** 本镜头绑定的漫剧卡 id(定妆图引用:出图/生视频时按卡取 imageUrl/立绘)。 */
301
+ mangaRoleIds?: string[];
284
302
  }
285
303
  /** 分镜持久化:单章的分镜产出(骨架 + 分镜表 + 视频提示词,可分别存在)。 */
286
304
  export interface ChapterStoryboard {
@@ -335,6 +353,112 @@ export interface MangaPlansRequest {
335
353
  export interface MangaPlansResponse {
336
354
  plans: MangaPlan[];
337
355
  }
356
+ /** 漫剧角色卡:制作角色(要建模/出图/锁一致性的「上镜角色」),与小说角色库弱关联。 */
357
+ export interface MangaRoleCard {
358
+ /** 稳定 id。 */
359
+ id: string;
360
+ /** 来源小说角色名(只读追溯,非强外键;禁止反向写回小说库)。 */
361
+ sourceRoleName?: string;
362
+ /** 漫剧用名(可短剧化改名)。 */
363
+ name: string;
364
+ /** 身份一句话。 */
365
+ identity: string;
366
+ /** 核心功能:主角/导师/感情线/反派/搭档/线人/功能性。 */
367
+ coreFunction: 'protagonist' | 'mentor' | 'love_interest' | 'antagonist' | 'sidekick' | 'informant' | 'functional';
368
+ /** 与主角的关系。 */
369
+ protagonistRelation: 'enemy' | 'friend' | 'mentor' | 'lover' | 'exploit' | 'neutral';
370
+ /** 口头禅/说话方式(配音一致性)。 */
371
+ speechStyle: string;
372
+ /** ≤3 个极致性格标签。 */
373
+ traits: string[];
374
+ /** 1-2 个辨识度外貌点。 */
375
+ appearance: string;
376
+ /** 3-5 个关键剧情节点。 */
377
+ keyScenes: string[];
378
+ /** 上场集数(分镜提名导入时记录)。 */
379
+ appearsInEpisodes: number[];
380
+ /** 待匹配 → 待确认 → 已导入 → 已定妆 → 已归档。 */
381
+ status: 'pending_match' | 'pending_confirm' | 'imported' | 'anchored' | 'archived';
382
+ /** 形象资产(沿用小说角色库同款结构)。 */
383
+ imagePrompt?: RoleRecord['imagePrompt'];
384
+ /** 情绪表情清单(锚点提炼时产出)。 */
385
+ expressions?: string[];
386
+ /** 四类精修生图提示词(立绘/四视图/表情/细节)。 */
387
+ promptKit?: RoleRecord['promptKit'];
388
+ /** 角色参考图。 */
389
+ imageUrl?: string;
390
+ /** 图集(图像锚点)。 */
391
+ gallery?: RoleImage[];
392
+ /** 生成锚点/精修时的方案风格 id(旧风格检测用)。 */
393
+ promptStyleId?: string;
394
+ createdAt: string;
395
+ updatedAt: string;
396
+ }
397
+ /** 分镜提名 → 小说角色库匹配的一条候选。 */
398
+ export interface MangaRoleCandidate {
399
+ /** 分镜里的原始称谓(正文确切用法)。 */
400
+ rawName: string;
401
+ /** matched=命中小说角色;ambiguous=多个候选待定;not_in_library=小说库没有;already_imported=已建漫剧卡(跳过)。 */
402
+ verdict: 'matched' | 'ambiguous' | 'not_in_library' | 'already_imported';
403
+ /** 规则阶段候选短名单(≤5,精确名优先 + 身份/简称匹配)。 */
404
+ matches: Array<{
405
+ roleName: string;
406
+ reason: string;
407
+ }>;
408
+ /** LLM/规则确认的命中角色名(ambiguous 时可能为空)。 */
409
+ matchedRoleName?: string;
410
+ /** not_in_library 时给出建议:backfill=正文有该称谓但小说库漏提炼(回小说库补);manga_new=漫剧新增角色(直接建卡)。 */
411
+ novelHint?: 'backfill' | 'manga_new';
412
+ /** 采纳时预填的漫剧卡建议。 */
413
+ suggested: {
414
+ name: string;
415
+ identity: string;
416
+ coreFunction: MangaRoleCard['coreFunction'];
417
+ protagonistRelation: MangaRoleCard['protagonistRelation'];
418
+ speechStyle: string;
419
+ traits: string[];
420
+ appearance: string;
421
+ keyScenes: string[];
422
+ };
423
+ }
424
+ /** POST /manga/roles 请求:漫剧角色库操作。 */
425
+ export interface MangaRolesRequest {
426
+ op: 'nominate' | 'adopt' | 'update' | 'remove' | 'visual' | 'promptKit' | 'image' | 'removeImage' | 'imageGenerate' | 'mode';
427
+ /** op='mode':短剧精简模式开关。 */
428
+ shortDramaMode?: boolean;
429
+ /** op='nominate':从哪章分镜提名角色。 */
430
+ chapterNo?: number;
431
+ /** op='adopt' / op='update':漫剧角色卡。 */
432
+ card?: MangaRoleCard;
433
+ /** op='remove' / 'visual' / 'promptKit' / 'image' / 'removeImage' / 'imageGenerate':漫剧卡 id。 */
434
+ id?: string;
435
+ /** op='visual' / 'promptKit':漫剧方案基底风格 id。 */
436
+ styleId?: string;
437
+ /** 可选滤镜风格 id。 */
438
+ filterId?: string;
439
+ /** op='image':定妆图 dataURL。 */
440
+ dataUrl?: string;
441
+ /** op='image':图集用途标签(立绘/四视图/表情-x/场景/细节);缺省视为角色参考图(imageUrl)。 */
442
+ label?: string;
443
+ /** op='imageGenerate':漫画风格预设 id(可选)。 */
444
+ style?: string;
445
+ /** op='imageGenerate':生图模型库条目 id(缺省用启用条目)。 */
446
+ modelId?: string;
447
+ }
448
+ /** POST /manga/roles 响应。 */
449
+ export interface MangaRolesResponse {
450
+ cards: MangaRoleCard[];
451
+ /** op='nominate' 时的 AI 提名候选。 */
452
+ candidates?: MangaRoleCandidate[];
453
+ /** op='visual' 时的形象锚点(已写入漫剧卡)。 */
454
+ visual?: RoleRecord['imagePrompt'];
455
+ /** op='promptKit' 时的四类精修提示词包(已写入漫剧卡)。 */
456
+ promptKit?: RoleRecord['promptKit'];
457
+ /** op='image' / 'imageGenerate' 时的定妆图 dataURL(已写入漫剧卡)。 */
458
+ imageUrl?: string;
459
+ /** op='mode' 时的短剧精简模式状态。 */
460
+ shortDramaMode?: boolean;
461
+ }
338
462
  /** Chapter lifecycle states (the writing pipeline's state machine). */
339
463
  export type ChapterStatus = 'pending' | 'generating' | 'written' | 'reviewing' | 'approved' | 'rejected' | 'error';
340
464
  /** One chapter in the plan. */
@@ -979,6 +1103,10 @@ export interface ProjectState {
979
1103
  plotlines?: Plotline[];
980
1104
  /** 角色库(作者维护 + AI 提炼的主表)。 */
981
1105
  roles?: RoleRecord[];
1106
+ /** 漫剧角色库(制作角色投影:从分镜提名导入,独立于小说角色库)。 */
1107
+ mangaRoles?: MangaRoleCard[];
1108
+ /** 短剧精简模式:漫剧角色库按 5-8 上镜角色 / 功能标签 / 关系闭环 / 人设极致化约束。 */
1109
+ shortDramaMode?: boolean;
982
1110
  /** 场景库(作者维护 + AI 提炼的主表)。 */
983
1111
  scenes?: SceneCard[];
984
1112
  /** 视觉世界观规则(生图/生视频必须遵守,从道藏提炼,注入所有提示词)。 */
@@ -0,0 +1,46 @@
1
+ /**
2
+ * 镜头语言词库(分镜规范化基准)。
3
+ * 分镜表的 shot/camera/composition/light 字段统一从这里取值(英文 id),
4
+ * 生成提示词时用 zh 中文词块;适配英文平台时用 en。
5
+ * 目的:让"景别/运镜/构图/光效"有标准词表,避免 LLM 自由发挥导致漂移。
6
+ */
7
+ /** 景别。 */
8
+ export type ShotSizeId = 'extreme_wide' | 'wide' | 'full' | 'medium' | 'medium_close' | 'close' | 'extreme_close' | 'big_extreme_close';
9
+ /** 运镜/机位。 */
10
+ export type CameraMoveId = 'static' | 'dolly_in' | 'dolly_out' | 'pan_left' | 'pan_right' | 'track_left' | 'track_right' | 'follow' | 'pedestal_up' | 'pedestal_down' | 'orbit' | 'handheld' | 'low_angle' | 'high_angle' | 'over_shoulder';
11
+ /** 构图。 */
12
+ export type CompositionId = 'rule_of_thirds' | 'center' | 'leading_line' | 'foreground' | 'low' | 'overhead' | 'symmetry';
13
+ /** 光效。 */
14
+ export type LightingId = 'front' | 'side' | 'back' | 'top' | 'rembrandt' | 'neon' | 'hard' | 'soft' | 'mood' | 'contrast';
15
+ export interface ShotLangEntry<T extends string> {
16
+ id: T;
17
+ /** 中文(提示词/展示用)。 */
18
+ zh: string;
19
+ /** 英文(英文平台适配用)。 */
20
+ en: string;
21
+ /** 一句话说明(下拉/卡片)。 */
22
+ hint: string;
23
+ }
24
+ export declare const SHOT_SIZES: readonly ShotLangEntry<ShotSizeId>[];
25
+ export declare const CAMERA_MOVES: readonly ShotLangEntry<CameraMoveId>[];
26
+ export declare const COMPOSITIONS: readonly ShotLangEntry<CompositionId>[];
27
+ export declare const LIGHTINGS: readonly ShotLangEntry<LightingId>[];
28
+ export interface ShotLanguage {
29
+ shot?: ShotSizeId;
30
+ camera: CameraMoveId[];
31
+ composition?: CompositionId;
32
+ light: LightingId[];
33
+ }
34
+ /** 从中文文本归一化到景别(处理旧数据/LLM 口语)。未知回退 medium。 */
35
+ export declare function normalizeShotSize(text: string | undefined): ShotSizeId;
36
+ /** 从中文文本归一化到运镜列表。 */
37
+ export declare function normalizeCameras(text: string | undefined): CameraMoveId[];
38
+ /** 从中文文本归一化到构图(可选)。 */
39
+ export declare function normalizeComposition(text: string | undefined): CompositionId | undefined;
40
+ /** 从中文文本归一化到光效列表。 */
41
+ export declare function normalizeLightings(text: string | undefined): LightingId[];
42
+ /** 取词条中文(ZHs 合并句子)。 */
43
+ export declare function sizeZh(id: ShotSizeId | undefined): string;
44
+ export declare function cameraZh(ids: CameraMoveId[] | undefined): string;
45
+ export declare function compoZh(id: CompositionId | undefined): string;
46
+ export declare function lightZh(ids: LightingId[] | undefined): string;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * 编剧词库(剧情骨架规范化基准)。
3
+ * 骨架的 function(叙事功能)与 emotion(情绪走向)统一从这里取值(英文 id),
4
+ * 生成提示词/展示时用 zh 中文。
5
+ * 目的:让"叙事功能/情绪"有标准词表,避免 LLM 自由发挥导致情绪链漂移。
6
+ */
7
+ /** 叙事功能(对应节拍在剧情中的作用)。 */
8
+ export type StoryFunctionId = 'exposition' | 'conflict' | 'turn' | 'climax' | 'resolve' | 'foreshadow' | 'character';
9
+ /** 情绪词(低→高能量的情绪阶,角色在节拍中的主要情绪点)。 */
10
+ export type EmotionId = 'calm' | 'indifferent' | 'expectant' | 'curious' | 'alert' | 'suppressed' | 'enduring' | 'worried' | 'irritable' | 'uneasy' | 'terrified' | 'angry' | 'collapsing' | 'resolute' | 'grieved' | 'relieved' | 'bittersweet' | 'triumphant' | 'reborn' | 'numb';
11
+ export interface BeatLangEntry<T extends string> {
12
+ id: T;
13
+ /** 中文(展示用)。 */
14
+ zh: string;
15
+ /** 英文(英文平台适配用)。 */
16
+ en: string;
17
+ /** 一句话说明。 */
18
+ hint: string;
19
+ }
20
+ /** 叙事功能词表。 */
21
+ export declare const STORY_FUNCTIONS: readonly BeatLangEntry<StoryFunctionId>[];
22
+ /** 情绪词表(按能量/阶段排序)。 */
23
+ export declare const EMOTIONS: readonly BeatLangEntry<EmotionId>[];
24
+ export interface StoryBeatLanguage {
25
+ function: StoryFunctionId;
26
+ emotion: EmotionId[];
27
+ }
28
+ /** 从中文归一化叙事功能(未知回退 exposition)。 */
29
+ export declare function normalizeStoryFunction(text: string | undefined): StoryFunctionId;
30
+ /** 从中文文本归一化情绪词列表(可含→箭头链)。 */
31
+ export declare function normalizeEmotions(text: string | undefined): EmotionId[];
32
+ /** 叙事功能中文。 */
33
+ export declare function functionZh(id: StoryFunctionId | undefined): string;
34
+ /** 情绪中文(箭头连接)。 */
35
+ export declare function emotionZh(ids: EmotionId[] | undefined): string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@waterwx/dsh-novel-forge",
3
3
  "description": "AI 编译小说工作台 for the dsh web GUI: import a novel outline (docx/text), plan chapters with LLM, generate chapter by chapter (3000-4000 chars each), and save every chapter as Markdown into your chosen output folder.",
4
- "version": "1.7.0",
4
+ "version": "1.7.3",
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "node": "^22.19.0 || >=24.0.0"
@@ -80,9 +80,9 @@
80
80
  "lib/**/*.js",
81
81
  "lib/**/*.js.map",
82
82
  "lib/**/*.d.ts",
83
- "lib/**/*.d.ts.map",
84
83
  "src",
85
84
  "assets/styles",
85
+ "skills",
86
86
  "scripts/patch-schemastery.mjs",
87
87
  "cordis.patch.yml",
88
88
  "README.md"
@@ -94,6 +94,9 @@
94
94
  "bundle": "tsdown",
95
95
  "watch": "tsdown --watch",
96
96
  "typecheck": "tsc --noEmit",
97
+ "check:styles": "node scripts/check-theme-sizes.mjs",
98
+ "release": "node scripts/release.mjs",
99
+ "release:dry": "node scripts/release.mjs --dry-run",
97
100
  "test": "vitest run"
98
101
  }
99
102
  }
@@ -0,0 +1,81 @@
1
+ ---
2
+ name: novel-forge-chapter-batch
3
+ description: 批量生成小说章节并值守处理审稿未过的章节。当用户说「生成 N 章」「连写」「批量生成」「继续写章节」「值守处理未过章节」时使用。依赖 dsh-novel-forge 插件(http://127.0.0.1:3080/api/dsh-novel-forge/*)。
4
+ ---
5
+
6
+ # 小说章节批量生产与值守处理
7
+
8
+ 把「批量生成章节 + 审稿未过处理」固化为可复现流程。所有操作通过 dsh-novel-forge 的 HTTP API 执行。
9
+
10
+ ## 0. 值守纪律(最高优先级,违反即空转)
11
+
12
+ 1. **启动后台任务后立即停下,不轮询、不连续 wait、不做无意义动作**——后台任务(批量生成/处理)由运行时在完成时自动通知。
13
+ 2. 只有收到「任务完成」通知或用户新消息时,才继续下一步(读结果 → 处理未过 → 汇报)。
14
+ 3. 等待期间可以做「与当前任务无关」的其他有用工作(如用户提出的其他问题、代码改进),但不得为等任务而空转。
15
+ 4. 任务因 web 重启中断时:等用户确认或检测到 3080 恢复后,复位卡死章节(generating/error → reset)再续跑,并向用户说明中断原因。
16
+
17
+ ## 1. 前置检查(每次必做)
18
+
19
+ 1. 确认服务在跑:`GET /api/dsh-novel-forge/status` 返回 200,读 `project.chapters` 数量与书名。
20
+ 2. 确认目标章节状态:列出 `pending`(待生成)章节号区间,避免重复生成已 approved/rejected 的章。
21
+ 3. **串行执行,禁止并行写 project.json**——多个脚本同时调 API 会造成状态互相覆盖(实测教训:第 166 章曾被并发回滚成 rejected)。
22
+
23
+ ## 2. 批量生成
24
+
25
+ 对目标章节区间(如 181-205)逐章串行调用:
26
+
27
+ ```
28
+ POST /api/dsh-novel-forge/generate
29
+ { "chapterNo": N, "skipReview": false }
30
+ ```
31
+
32
+ - `skipReview: false` 走完整质量门(生成 → 摘要+编年录 → AI 审稿 → 作者复盘)。
33
+ - 响应为 NDJSON 流;找 `{"type":"review","report":{...}}` 帧取 `score`/`passed`。
34
+ - 每章 2-5 分钟;失败自动重试 1 次(间隔 3 秒)。
35
+ - 预估总时长并告知用户(25 章 ≈ 45-75 分钟),放后台跑,期间可并行做别的(只读操作不受影响)。
36
+ - 推荐用 Node 脚本(fetch + 逐章循环),避免 PowerShell 的引号/UTF-8 转义坑;脚本用完删除。
37
+
38
+ ## 2. 未过章节处理(分级策略)
39
+
40
+ 生成完成后汇总:通过 / 未过(rejected)/ 失败(error)。对未过章按此分级:
41
+
42
+ ### A. 无 high(全是 medium/low 主观项)→ 豁免通过
43
+ ```
44
+ POST /api/dsh-novel-forge/chapter/approve { "chapterNo": N }
45
+ ```
46
+ 主观项(套话/节奏/比喻/心理描写)不值得逐章磨,直接通过。
47
+
48
+ ### B. 有 high 且问题明确(逻辑/设定/时间线/用词)→ 按意见修订 + 验证模式
49
+ 1. 取 review 的 high(无 high 取前 3 条 medium),拼指令:
50
+ `按审稿意见修订(优先处理):\n[severity] item → suggestion`
51
+ 2. `POST /rewrite` `{ chapterNo, instructions }` → 产出 pendingDraft
52
+ 3. **验证模式**:`POST /chapter/check` `{ chapterNo, text: draft, previousReport: review }`——携带上一轮报告,LLM 只核对原意见是否解决 + 只挑新增 high,不全新找茬
53
+ 4. 判定:`passed || issues 全非 high` → 可接受
54
+ 5. 可接受 → `POST /draft/apply` `{ chapterNo, report: { score: max(score,70), passed: true, issues: [], ... } }` 落盘为 approved
55
+ 6. 不可接受 → 看剩余 high 是否明确可修 → 第二轮修订(指令更精确,如「把第 141 章的养伤时间线统一为七天」)→ 再验证
56
+ 7. **每章最多修订 2 轮**,仍不过 → 保留草稿,标记「待人工」,不硬来
57
+
58
+ ### C. 有 high 但结构性(修订改不好,如逻辑矛盾被坐实)→ 重新生成
59
+ - `POST /draft/discard` 清草稿 → `POST /generate` 整章重写
60
+ - 重新生成后若只剩主观项 → 走 A 豁免
61
+
62
+ ### D. error 状态(无审稿、chars 丢失)→ 重置重生成
63
+ - `POST /chapter/reset` `{ chapterNo }` → `POST /generate`
64
+
65
+ ### 特殊注意
66
+ - **approved 但 review 有 high**:已通过状态保留,不折腾(high 可能已被正文处理或审稿误标)。
67
+ - **修订指令越精确越有效**:具体数字/行为(如「茶梗尺寸统一为半截指节」)一次就过;泛泛的「修正矛盾」容易改不到位。
68
+
69
+ ## 3. 收尾
70
+
71
+ 1. 最终验证:`GET /status` 读目标区间全部 approved。
72
+ 2. 可选:对涉及剧情的章节刷新剧情线进度(`POST /plotlines` `{ op:'refresh', id }`)——注意刷新函数只读最近 8 章摘要,早期章节的线可能刷不准,必要时手动 update progress + link 章节。
73
+ 3. 向用户汇报:通过/未过/失败数量 + 每章处理方式 + 遗留待人工项。
74
+
75
+ ## 4. 已知陷阱(血泪教训)
76
+
77
+ - **并发写 project.json 会互相覆盖**——多个生成/处理任务必须串行。
78
+ - **rewrite 的草稿可能不落盘**(偶发 500 或时序)——应用前检查 `pendingDraft` 是否存在,不存在则重跑 rewrite。
79
+ - **PowerShell 调 API 的引号/中文转义是坑**——统一用 Node 脚本。
80
+ - **status 接口会做数据快照**——以磁盘 novel-project.json 为准复核关键状态。
81
+ - **验证模式 500 偶发**——重试一次即可(LLM 瞬时错误)。
package/src/client/api.ts CHANGED
@@ -236,6 +236,11 @@ export class NovelApi {
236
236
  return postJson<import('../protocol.ts').RolesResponse>(NOVEL_API.roles, req)
237
237
  }
238
238
 
239
+ /** 漫剧角色库:从分镜提名 / 建卡 / 更新 / 删除 / 形象锚点 / 精修提示词。 */
240
+ async mangaRoles(req: import('../protocol.ts').MangaRolesRequest): Promise<import('../protocol.ts').MangaRolesResponse> {
241
+ return postJson<import('../protocol.ts').MangaRolesResponse>(NOVEL_API.mangaRoles, req)
242
+ }
243
+
239
244
  /** 场景库:AI 提炼 / 采纳 / 更新 / 删除 / 图集。 */
240
245
  async scenes(req: import('../protocol.ts').ScenesRequest): Promise<import('../protocol.ts').ScenesResponse> {
241
246
  return postJson<import('../protocol.ts').ScenesResponse>(NOVEL_API.scenes, req)
@@ -154,7 +154,6 @@ export const zh = {
154
154
  'settings.reasoning.max': 'max · 最高思考(质检/推演)',
155
155
  'settings.theme': '主题',
156
156
  'settings.themeLiquid': 'iOS 液态玻璃(绿)',
157
- 'settings.themeClassic': '经典毛玻璃(蓝)',
158
157
  'settings.themeNeumorph': '新拟物(双阴影)',
159
158
  'settings.themeHint': '存于浏览器本地,即时生效',
160
159
  'settings.chapterChars': '每章目标字数',