@waterwx/dsh-novel-forge 1.1.9 → 1.3.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.
@@ -20,10 +20,6 @@ export declare class NovelApi {
20
20
  outlineSuggest(idea: string, count?: number, exclude?: string[]): Promise<import('../protocol.ts').OutlineSuggestResponse>;
21
21
  /** 拆书分析:对已写章节做结构/人物/文风/卖点体检。 */
22
22
  breakdown(scope?: string, preset?: 'quick' | 'standard', budgetTokens?: number): Promise<import('../protocol.ts').BreakdownResponse>;
23
- /** 漫剧分镜生成:章节 → 角色锚点 + 分镜表。 */
24
- storyboard(chapterNo: number, genre?: string, platform?: string, tool?: string): Promise<import('../protocol.ts').StoryboardResponse>;
25
- /** 漫剧分集计划:读一卷 → 按故事弧线分集。 */
26
- storyboardPlan(volumeNo: number, platform?: string, maxEpisodes?: number): Promise<import('../protocol.ts').StoryboardPlanResponse>;
27
23
  plan(outline?: string, chapterCount?: number, volume?: number): Promise<PlanResponse>;
28
24
  volumes(outline?: string): Promise<VolumesResponse>;
29
25
  bible(outline?: string): Promise<BibleResponse>;
@@ -93,6 +89,10 @@ export declare class NovelApi {
93
89
  }>;
94
90
  /** 角色库:AI 提炼 / 采纳 / 更新 / 删除。 */
95
91
  roles(req: import('../protocol.ts').RolesRequest): Promise<import('../protocol.ts').RolesResponse>;
92
+ /** 场景库:AI 提炼 / 采纳 / 更新 / 删除 / 图集。 */
93
+ scenes(req: import('../protocol.ts').ScenesRequest): Promise<import('../protocol.ts').ScenesResponse>;
94
+ /** 视觉世界观规则:提炼 / 保存。 */
95
+ visualRules(req: import('../protocol.ts').VisualRulesRequest): Promise<import('../protocol.ts').VisualRulesResponse>;
96
96
  /** 小说简介:AI 生成/补全(partial 留空 = 全量),或手动保存。 */
97
97
  blurb(action: 'generate' | 'save', text?: string, partial?: string): Promise<{
98
98
  blurb: string;
@@ -141,6 +141,14 @@ export declare const zh: {
141
141
  readonly 'settings.outputDir': "输出目录";
142
142
  readonly 'settings.provider': "模型提供商";
143
143
  readonly 'settings.model': "模型";
144
+ readonly 'settings.modelCustom': "自定义…";
145
+ readonly 'settings.modelCustomPlaceholder': "输入任意模型 id";
146
+ readonly 'settings.reasoningEffort': "思考强度 (reasoningEffort)";
147
+ readonly 'settings.reasoning.off': "off · 关闭思考(推荐日常写文)";
148
+ readonly 'settings.reasoning.low': "low · 低思考";
149
+ readonly 'settings.reasoning.high': "high · 高思考";
150
+ readonly 'settings.reasoning.max': "max · 最高思考(质检/推演)";
151
+ readonly 'settings.reasoningHint': "off 最快最省;high/max 更慢更贵但推理更强";
144
152
  readonly 'settings.theme': "主题";
145
153
  readonly 'settings.themeLiquid': "iOS 液态玻璃(绿)";
146
154
  readonly 'settings.themeClassic': "经典毛玻璃(蓝)";
@@ -1,7 +1,7 @@
1
1
  import type { NovelApi } from '../api.ts';
2
2
  import type { PanelController } from './controller.ts';
3
3
  /** The panel's tab identifiers. */
4
- export type NovelTab = 'workflow' | 'overview' | 'blurb' | 'plan' | 'bible' | 'world' | 'foreshadow' | 'assistant' | 'settings' | 'characters' | 'roles' | 'facts' | 'plotlines' | 'reviews' | 'progress' | 'breakdown' | 'storyboard' | 'assetsGenre' | 'assetsProgression' | 'assetsTemplates' | 'assetsRules' | 'assetsStyle' | 'run';
4
+ export type NovelTab = 'workflow' | 'overview' | 'blurb' | 'plan' | 'bible' | 'world' | 'foreshadow' | 'assistant' | 'settings' | 'characters' | 'roles' | 'facts' | 'plotlines' | 'reviews' | 'progress' | 'breakdown' | 'roleImage' | 'scenes' | 'assetsGenre' | 'assetsProgression' | 'assetsTemplates' | 'assetsRules' | 'assetsStyle' | 'run' | 'book' | 'assets';
5
5
  /** Panel shell props. */
6
6
  export interface NovelPanelProps {
7
7
  /** The panel state owner (open/close/toggle). */
@@ -0,0 +1,12 @@
1
+ /** 漫画风格预设:每个预设对应一组出图提示词与排版偏好。 */
2
+ export interface ComicStylePreset {
3
+ id: string;
4
+ label: string;
5
+ /** 注入 imagePrompt 的风格描述词。 */
6
+ prompt: string;
7
+ /** 可选:排版偏好说明。 */
8
+ layoutHint?: string;
9
+ }
10
+ export declare const COMIC_STYLE_PRESETS: ComicStylePreset[];
11
+ export declare function getComicStylePrompt(styleId?: string): string;
12
+ export declare function getComicStyleLabel(styleId?: string): string;
@@ -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, StoryBible, StoryboardPlanResponse, StoryboardResponse, Volume, WorldState } from './protocol.ts';
15
+ import type { AuditIssue, AuthorReview, BreakdownResponse, ChapterPlan, Foreshadow, NovelConfig, OutlineCandidate, Plotline, PlotlineHealthReport, PlotlinePlan, ProjectState, ReviewReport, RoleRecord, RoleStatusCard, SceneCard, StoryBible, 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 */
@@ -45,7 +45,7 @@ export declare function readChapterFile(outputDir: string, chapter: ChapterPlan)
45
45
  /** Create a fresh project from an outline. */
46
46
  export declare function createProject(outline: string, outlinePath?: string): ProjectState;
47
47
  /** Extract the story bible from an outline. */
48
- export declare function extractBible(ctx: Context, config: NovelConfig, outline: string): Promise<StoryBible>;
48
+ export declare function extractBible(ctx: Context, config: NovelConfig, outline: string, project?: ProjectState): Promise<StoryBible>;
49
49
  /** Plan volumes from an outline. */
50
50
  export declare function planVolumes(ctx: Context, config: NovelConfig, outline: string): Promise<Volume[]>;
51
51
  /**
@@ -69,18 +69,31 @@ export declare function suggestPlotlines(ctx: Context, config: NovelConfig, proj
69
69
  export declare function refreshPlotlineProgress(ctx: Context, config: NovelConfig, project: ProjectState, line: Plotline): Promise<string>;
70
70
  /** ✨ AI 从全书提炼角色库:大纲 + 道藏 + 编年录 + 章节摘要 → 结构化角色清单。 */
71
71
  export declare function extractRoles(ctx: Context, config: NovelConfig, project: ProjectState): Promise<RoleRecord[]>;
72
+ /** ✨ AI 从全书提炼场景库:正文/编年录 → 高频重要场景的结构化视觉锚点。 */
73
+ export declare function extractScenes(ctx: Context, config: NovelConfig, project: ProjectState): Promise<SceneCard[]>;
72
74
  /** 动漫形象描述词(中文描述 + 英文 booru 标签 + 关键外貌标签)。 */
73
75
  export interface RoleVisualPrompt {
74
76
  zh: string;
75
77
  en: string;
76
78
  tags: string[];
77
79
  source: string;
80
+ /** 该角色所需情绪表情清单(6-12 个,如 疲惫/麻木/压抑悲伤)。 */
81
+ expressions?: string[];
82
+ /** 四类精修提示词(立绘/四视图/表情/细节,一次提炼直接产出)。 */
83
+ promptKit?: RoleRecord['promptKit'];
78
84
  }
79
85
  /**
80
86
  * 为单个角色提炼「动漫形象描述词」:扫描该角色出场的已写章节正文,
81
87
  * 截取含外貌描写的段落,交给 LLM 提炼中文描述 + 英文绘图标签。
82
88
  */
83
89
  export declare function extractRoleVisual(ctx: Context, config: NovelConfig, project: ProjectState, outputDir: string, roleName: string): Promise<RoleVisualPrompt>;
90
+ /**
91
+ * 角色四类生图提示词精修包(promptKit):以锚点 + 表情清单 + 视觉规则为输入,
92
+ * LLM 产出 立绘/四视图/表情×N/细节 四套 zh+en 提示词(比前端拼装更精炼)。
93
+ */
94
+ export declare function generateRolePromptKit(ctx: Context, config: NovelConfig, project: ProjectState, roleName: string): Promise<RoleRecord['promptKit']>;
95
+ /** ✨ 从道藏/红线提炼「视觉世界观规则」:生图/生视频必须遵守的设定纠偏(如"商品=人,禁止常规超市商品")。 */
96
+ export declare function extractVisualRules(ctx: Context, config: NovelConfig, project: ProjectState): Promise<string[]>;
84
97
  /**
85
98
  * 开书想法 → AI 大纲:输入一句话想法,生成 2-3 个方向不同、可直接开书的完整大纲方案。
86
99
  * @param count 本次生成几个(默认 3,最多 3)
@@ -96,17 +109,7 @@ export declare function suggestOutlines(ctx: Context, config: NovelConfig, idea:
96
109
  * @param budgetTokens token 预算上限(超过即截断章节取样)。
97
110
  */
98
111
  export declare function breakdownBook(ctx: Context, config: NovelConfig, project: ProjectState, outputDir: string, scope?: string, preset?: 'quick' | 'standard', budgetTokens?: number): Promise<BreakdownResponse>;
99
- /**
100
- * 漫剧分镜生成:把一章正文改编为短视频漫剧分镜(吸收 manga-script-master 方法论)。
101
- * 产出:本集标题 + 赛道节奏说明 + 角色视觉锚点卡 + 分镜表(8-12 格)+ 结尾钩子。
102
- * 角色锚点优先复用角色库 imagePrompt(无则从正文提炼)。
103
- */
104
- export declare function generateStoryboard(ctx: Context, config: NovelConfig, project: ProjectState, outputDir: string, chapterNo: number, genre: string, platform: string, tool: string): Promise<StoryboardResponse>;
105
- /**
106
- * 漫剧分集计划:AI 通读一卷的章节标题+摘要+beats,按故事弧线把章节分组为漫剧集。
107
- * 原则(吸收 manga-script 节奏模型):高潮章单独成集、过渡章合并、断点选在钩子最强处。
108
- */
109
- export declare function planStoryboardEpisodes(ctx: Context, config: NovelConfig, project: ProjectState, volumeNo: number, platform: string, maxEpisodes: number): Promise<StoryboardPlanResponse>;
112
+ export declare function generateRoleReferenceImage(ctx: Context, config: NovelConfig, project: ProjectState, outputDir: string, name: string, style?: string): Promise<string>;
110
113
  /** 🩺 剧情健康检查:基于已写章节数/各线状态/编年录,判断是否需要新线及添加时机。 */
111
114
  export declare function analyzePlotlineHealth(ctx: Context, config: NovelConfig, project: ProjectState): Promise<PlotlineHealthReport>;
112
115
  /** ✨ AI 剧情方案:基于健康检查结果设计下一阶段方向与建议新线。 */
@@ -174,7 +177,7 @@ export declare function markForeshadowPlanted(project: ProjectState, outputDir:
174
177
  */
175
178
  export declare function extractFacts(ctx: Context, config: NovelConfig, project: ProjectState, outputDir: string, chapterNo: number): Promise<number>;
176
179
  /** 全书一致性质检:LLM 分批扫描已生成章节 + 设定 + 事实库,聚合矛盾清单。 */
177
- export declare function auditBook(ctx: Context, config: NovelConfig, project: ProjectState, outputDir: string): Promise<AuditIssue[]>;
180
+ export declare function auditBook(ctx: Context, config: NovelConfig, project: ProjectState, outputDir: string, onProgress?: (completedBatches: number, totalBatches: number) => void): Promise<AuditIssue[]>;
178
181
  /** 小说简介:AI 生成或按已写开头补全(面向读者的作品门面)。 */
179
182
  export declare function generateBlurb(ctx: Context, config: NovelConfig, project: ProjectState, partial?: string): Promise<string>;
180
183
  /**
@@ -33,6 +33,8 @@ export interface Config {
33
33
  provider?: string;
34
34
  /** LLM model id. */
35
35
  model?: string;
36
+ /** LLM reasoning effort (off/low/high/max). */
37
+ reasoningEffort?: 'off' | 'low' | 'high' | 'max';
36
38
  /** Target characters per chapter. */
37
39
  chapterChars?: number;
38
40
  /** Max output tokens per chapter call. */
@@ -45,6 +47,12 @@ export interface Config {
45
47
  autoAuthorReview?: boolean;
46
48
  /** 修订/润色产出草稿后自动附带一次 AI 审查(默认开,可在设置页关闭省 token)。 */
47
49
  autoReviewAfterRevise?: boolean;
50
+ /** 豆包/Seedream 生图 API Key。 */
51
+ imageApiKey?: string;
52
+ /** 豆包/Seedream 生图模型 ID。 */
53
+ imageApiModel?: string;
54
+ /** 是否启用豆包生图(默认关)。 */
55
+ imageApiEnabled?: boolean;
48
56
  }
49
57
  export declare const Config: z<Config>;
50
58
  /** Model-facing announcement: plugin presence, capabilities, and limits. */
@@ -57,6 +57,8 @@ export declare const NOVEL_API: {
57
57
  readonly plotlines: "/api/dsh-novel-forge/plotlines";
58
58
  /** 角色库:AI 提炼 / 采纳 / 更新 / 删除。 */
59
59
  readonly roles: "/api/dsh-novel-forge/roles";
60
+ readonly scenes: "/api/dsh-novel-forge/scenes";
61
+ readonly visualRules: "/api/dsh-novel-forge/visual-rules";
60
62
  /** 作者复盘补跑:对已写章节补齐 authorReview(全书流式 / 单章 JSON)。 */
61
63
  readonly reviewBackfill: "/api/dsh-novel-forge/review/backfill";
62
64
  /** 章节复位:generating 卡死 → pending(可重新生成)。 */
@@ -70,9 +72,8 @@ export declare const NOVEL_API: {
70
72
  /** 拆书分析:对已写章节做结构/人物/文风/卖点四维体检(两阶段:源笔记→分节分析)。 */
71
73
  readonly breakdown: "/api/dsh-novel-forge/breakdown";
72
74
  /** 漫剧分镜生成:章节 → 角色锚点 + 分镜表(可适配豆包/Seedance/SD)。 */
73
- readonly storyboard: "/api/dsh-novel-forge/storyboard";
74
75
  /** 漫剧分集计划:读一卷 → 按故事弧线分集(高潮拆集/过渡并章)。 */
75
- readonly storyboardPlan: "/api/dsh-novel-forge/storyboard/plan";
76
+ /** 漫画脚本:章节 → 分页分格漫画脚本(含角色视觉锚点)。 */
76
77
  /** 生产单:启动批量生产(计划补足 + 逐章生成 + 被拒分级处理)。 */
77
78
  readonly runStart: "/api/dsh-novel-forge/run/start";
78
79
  /** 生产单控制:pause / resume / stop。 */
@@ -285,6 +286,26 @@ export interface AuditResponse {
285
286
  /** 质检时间。 */
286
287
  auditedAt: string;
287
288
  }
289
+ /** 全书质检的实时状态(通过 /status 暴露给面板/外部读取)。 */
290
+ export interface AuditStatus {
291
+ status: 'idle' | 'running' | 'done' | 'error';
292
+ /** 开始时间(ISO)。 */
293
+ startedAt?: string;
294
+ /** 结束时间(ISO)。 */
295
+ finishedAt?: string;
296
+ /** 总批次数(0 = 尚未开始/无章节)。 */
297
+ totalBatches: number;
298
+ /** 已完成批次数。 */
299
+ completedBatches: number;
300
+ /** 参与质检的章节数。 */
301
+ auditedChapters?: number;
302
+ /** 发现的问题数(done 后有效)。 */
303
+ issuesCount?: number;
304
+ /** 最近一次质检的问题清单(done 后有效;error 时为空)。 */
305
+ issues?: AuditIssue[];
306
+ /** 失败信息(error 时有效)。 */
307
+ error?: string;
308
+ }
288
309
  /** 一条剧情线(主线/支线/人物线/悬念线)。 */
289
310
  export interface Plotline {
290
311
  /** 稳定 id。 */
@@ -437,91 +458,6 @@ export interface BreakdownResponse {
437
458
  /** 估算消耗 token。 */
438
459
  usedTokens: number;
439
460
  }
440
- /** 漫剧分镜:一个角色锚点卡。 */
441
- export interface StoryboardCharacterCard {
442
- name: string;
443
- /** 视觉锚点:发型/五官/体型/标志物(中文描述)。 */
444
- visualAnchor: string;
445
- /** AI 标签组(英文,全分镜强制复用)。 */
446
- tags: string;
447
- /** 情绪表情库(英文,按需调用)。 */
448
- expressions: string[];
449
- }
450
- /** 漫剧分镜:一个分镜格。 */
451
- export interface StoryboardPanel {
452
- /** 序号。 */
453
- index: number;
454
- /** 时间码(如 0:00-0:03)。 */
455
- timecode: string;
456
- /** 景别(特写/中景/全景…)。 */
457
- shot: string;
458
- /** 画面描述(具象动作,非抽象情感)。 */
459
- visual: string;
460
- /** 台词(情绪标签 + 内容)。 */
461
- dialogue: string;
462
- /** 转场/动效。 */
463
- transition: string;
464
- /** AI 提示词(英文标签,豆包/SD 通用)。 */
465
- prompt: string;
466
- }
467
- /** POST /storyboard 请求:章节 → 漫剧分镜。 */
468
- export interface StoryboardRequest {
469
- chapterNo: number;
470
- /** 赛道类型:爽文/甜宠/悬疑/搞笑(留空由 AI 推断)。 */
471
- genre?: string;
472
- /** 目标平台(抖音/快手/B站…,默认抖音)。 */
473
- platform?: string;
474
- /** AI 工具偏好:doubao / seedance / sd / mj(影响提示词格式,默认 doubao)。 */
475
- tool?: string;
476
- }
477
- /** POST /storyboard 响应。 */
478
- export interface StoryboardResponse {
479
- /** 本集标题。 */
480
- title: string;
481
- /** 赛道与节奏说明。 */
482
- pacingNote: string;
483
- /** 本集钩子(结尾悬念)。 */
484
- hook: string;
485
- /** 角色锚点卡。 */
486
- characters: StoryboardCharacterCard[];
487
- /** 分镜表。 */
488
- panels: StoryboardPanel[];
489
- /** 结尾钩子台词。 */
490
- endingHook: string;
491
- }
492
- /** 漫剧分集计划:一集(含 1-N 章)。 */
493
- export interface StoryboardEpisode {
494
- /** 集号。 */
495
- index: number;
496
- /** 集标题(冲突+悬念句式)。 */
497
- title: string;
498
- /** 涵盖章节号。 */
499
- chapters: number[];
500
- /** 叙事任务:这集讲什么、为什么这么分。 */
501
- narrativeJob: string;
502
- /** 开头钩子。 */
503
- openingHook: string;
504
- /** 结尾钩子(逼看下集)。 */
505
- endingHook: string;
506
- }
507
- /** POST /storyboard/plan 请求:读一卷 → 输出漫剧分集计划。 */
508
- export interface StoryboardPlanRequest {
509
- /** 卷号(1-5)。 */
510
- volumeNo: number;
511
- /** 目标平台(默认抖音)。 */
512
- platform?: string;
513
- /** 每集约 1-2 分钟,最多多少集(默认 25)。 */
514
- maxEpisodes?: number;
515
- }
516
- /** POST /storyboard/plan 响应。 */
517
- export interface StoryboardPlanResponse {
518
- /** 本卷漫剧化的叙事策略说明。 */
519
- strategy: string;
520
- /** 分集计划。 */
521
- episodes: StoryboardEpisode[];
522
- /** 参与规划的章节数。 */
523
- chaptersScanned: number;
524
- }
525
461
  /** 角色卡:角色当前状态(从事实库聚合)。 */
526
462
  export interface RoleStatusCard {
527
463
  name: string;
@@ -565,14 +501,117 @@ export interface RoleRecord {
565
501
  /** 依据来源说明(哪几章哪些描写)。 */
566
502
  source?: string;
567
503
  };
504
+ /** 角色参考图(dataURL 或 URL)。用于漫画/漫剧出图时锁定角色一致性。 */
505
+ imageUrl?: string;
506
+ /** 角色图集(图像锚点):立绘/四视图/表情/场景/细节等,label 标注用途。 */
507
+ gallery?: RoleImage[];
508
+ /** 该角色所需的情绪表情清单(提炼时 LLM 补全 6-12 个,如"疲惫/麻木/压抑悲伤")。 */
509
+ expressions?: string[];
510
+ /** LLM 精修的四类生图提示词包(前端拼装版之外的高质量版,promptKit 接口生成)。 */
511
+ promptKit?: {
512
+ /** 立绘:全身/半身正视图。 */
513
+ portrait: {
514
+ zh: string;
515
+ en: string;
516
+ };
517
+ /** 四视图:正面/侧面/背面设定表。 */
518
+ sheet: {
519
+ zh: string;
520
+ en: string;
521
+ };
522
+ /** 表情:同一脸部锚换情绪,每表情一段。 */
523
+ expressions: {
524
+ name: string;
525
+ zh: string;
526
+ en: string;
527
+ }[];
528
+ /** 细节:标志物局部特写。 */
529
+ details: {
530
+ zh: string;
531
+ en: string;
532
+ };
533
+ };
534
+ /** 漫画/漫剧重要性:main=必须建卡保证一致;support=有锚点即可;extra=路人随机生成不约束。 */
535
+ importance?: 'main' | 'support' | 'extra';
536
+ }
537
+ /** 角色图集条目:一张已定型的角色图(图像锚点)。 */
538
+ export interface RoleImage {
539
+ /** 用途标签:立绘 / 四视图 / 表情-疲惫 / 场景 / 细节 等。 */
540
+ label: string;
541
+ /** dataURL 或 URL。 */
542
+ dataUrl: string;
543
+ }
544
+ /** 场景库条目:从正文提炼的「镜头场景」视觉锚点(漫剧分镜/生图用)。 */
545
+ export interface SceneCard {
546
+ /** 场景名(唯一键),如「旧茶渡·地下石殿」。 */
547
+ name: string;
548
+ /** 一句话定位:空间/氛围/功能。 */
549
+ summary: string;
550
+ /** 幕归属:第一幕·后场 / 第二幕·卖场 / …(便于按剧情段落组织)。 */
551
+ act?: string;
552
+ /** 时间光态:夜间闭店后 / 雨夜 / 凌晨 / 频闪灯… */
553
+ moment?: string;
554
+ /** 关键情节镜头 1-3 条:该场景拍什么(人物动作+情绪+镜头推进)。 */
555
+ beats?: string[];
556
+ /** 人物在场状态:主角/关键角色在该场景的状态一句话。 */
557
+ characterState?: string;
558
+ /** 环境构成:空间结构/陈设/标志物。 */
559
+ elements: string[];
560
+ /** 色调与光影(含 HEX 色板,可空)。 */
561
+ palette: string[];
562
+ /** 氛围关键词:压抑/神秘/空旷… */
563
+ moods: string[];
564
+ /** 中文生图提示词(连贯一段,写实电影感)。 */
565
+ zh: string;
566
+ /** 英文生图提示词(逗号分隔标签)。 */
567
+ en: string;
568
+ /** 关键标签。 */
569
+ tags: string[];
570
+ /** 依据来源(哪几章哪些描写)。 */
571
+ source?: string;
572
+ /** 场景图集(图像锚点):全景/局部/氛围 等。 */
573
+ gallery?: RoleImage[];
574
+ }
575
+ /** POST /scenes 请求:场景库提炼 / 采纳 / 更新 / 删除 / 图集。 */
576
+ export interface ScenesRequest {
577
+ op: 'extract' | 'adopt' | 'update' | 'remove' | 'image' | 'removeImage';
578
+ /** adopt / update 时传入的场景卡(adopt 可修改后采纳)。 */
579
+ scene?: SceneCard;
580
+ /** remove / image 时的场景名。 */
581
+ name?: string;
582
+ /** op='image' 时的图片 dataURL 与用途标签(全景/局部/氛围)。 */
583
+ dataUrl?: string;
584
+ label?: string;
585
+ }
586
+ /** POST /visual-rules 请求:视觉世界观规则提炼 / 保存。 */
587
+ export interface VisualRulesRequest {
588
+ op: 'extract' | 'save';
589
+ /** op=save 时的规则数组。 */
590
+ rules?: string[];
568
591
  }
569
- /** POST /roles 请求:角色库增删改 + AI 提炼。 */
592
+ /** POST /visual-rules 响应。 */
593
+ export interface VisualRulesResponse {
594
+ rules: string[];
595
+ }
596
+ /** POST /scenes 响应。 */
597
+ export interface ScenesResponse {
598
+ scenes: SceneCard[];
599
+ /** op=extract 时的 AI 候选场景。 */
600
+ candidates?: SceneCard[];
601
+ }
602
+ /** POST /roles 请求:角色库增删改 + AI 提炼 + 参考图上传。 */
570
603
  export interface RolesRequest {
571
- op: 'extract' | 'adopt' | 'update' | 'remove' | 'visual';
604
+ op: 'extract' | 'adopt' | 'update' | 'remove' | 'visual' | 'image' | 'removeImage' | 'imageGenerate' | 'promptKit';
572
605
  /** adopt / update 时传入的角色(adopt 可修改后采纳)。 */
573
606
  role?: RoleRecord;
574
607
  /** remove 时的角色名。 */
575
608
  name?: string;
609
+ /** op='image' 时的参考图 dataURL。 */
610
+ dataUrl?: string;
611
+ /** op='image' 时的图集用途标签(立绘/四视图/表情-x/场景/细节);缺省视为角色参考图(imageUrl)。 */
612
+ label?: string;
613
+ /** op='imageGenerate' 时的漫画风格预设 id(用于统一角色图与漫画页风格)。 */
614
+ style?: string;
576
615
  }
577
616
  /** POST /roles 响应。 */
578
617
  export interface RolesResponse {
@@ -581,6 +620,10 @@ export interface RolesResponse {
581
620
  candidates?: RoleRecord[];
582
621
  /** op=visual 时的动漫形象描述词(已写入角色卡)。 */
583
622
  visual?: RoleRecord['imagePrompt'];
623
+ /** op=promptKit 时的四类提示词精修包(已写入角色卡)。 */
624
+ promptKit?: RoleRecord['promptKit'];
625
+ /** op=image 时的角色参考图 dataURL。 */
626
+ imageUrl?: string;
584
627
  }
585
628
  /** POST /bible/patch 请求:局部修补设定圣经。 */
586
629
  export interface BiblePatchRequest {
@@ -696,8 +739,15 @@ export interface ProjectState {
696
739
  plotlines?: Plotline[];
697
740
  /** 角色库(作者维护 + AI 提炼的主表)。 */
698
741
  roles?: RoleRecord[];
742
+ /** 场景库(作者维护 + AI 提炼的主表)。 */
743
+ scenes?: SceneCard[];
744
+ /** 视觉世界观规则(生图/生视频必须遵守,从道藏提炼,注入所有提示词)。 */
745
+ visualRules?: string[];
699
746
  /** 人物志:角色当前状态聚合结果(从编年录刷新后存档,打开页面直接显示)。 */
700
747
  roleStatus?: RoleStatusCard[];
748
+ /** 漫剧分镜结果缓存:章号 → 分镜(持久化,刷新不丢)。 */
749
+ /** 漫剧分集计划缓存:卷号/0=全书 → 分集计划(持久化,刷新不丢)。 */
750
+ /** 漫画脚本缓存:章号 → 漫画脚本(持久化,刷新不丢)。 */
701
751
  /** ISO timestamps. */
702
752
  createdAt: string;
703
753
  updatedAt: string;
@@ -712,6 +762,8 @@ export interface NovelConfig {
712
762
  provider: string;
713
763
  /** LLM model id (e.g. deepseek-v4-flash). */
714
764
  model: string;
765
+ /** LLM reasoning effort: off = no thinking; low/high/max = thinking intensity. */
766
+ reasoningEffort: 'off' | 'low' | 'high' | 'max';
715
767
  /** Target characters per chapter. */
716
768
  chapterChars: number;
717
769
  /** Max output tokens per chapter call. */
@@ -724,6 +776,12 @@ export interface NovelConfig {
724
776
  autoAuthorReview: boolean;
725
777
  /** 修订/润色产出草稿后是否自动附带一次 AI 审查(工作区显示新稿评分与剩余问题)。 */
726
778
  autoReviewAfterRevise: boolean;
779
+ /** 豆包/Seedream 生图 API Key(保存在服务端配置,不暴露给前端明文回显)。 */
780
+ imageApiKey?: string;
781
+ /** 豆包/Seedream 生图模型 ID(如 doubao-seedream-5-0-pro-260628)。 */
782
+ imageApiModel?: string;
783
+ /** 是否启用豆包生图(默认关,避免误调用付费 API)。 */
784
+ imageApiEnabled?: boolean;
727
785
  }
728
786
  /** GET /status response. */
729
787
  export interface StatusResponse {
@@ -732,6 +790,8 @@ export interface StatusResponse {
732
790
  project?: ProjectState;
733
791
  /** Chapter files already on disk (basenames, sorted). */
734
792
  generatedFiles: string[];
793
+ /** 全书质检实时状态(用于面板/外部读取进度)。 */
794
+ audit?: AuditStatus;
735
795
  }
736
796
  /** 生产单状态(生产单 = 区间批量生产执行器:计划补足 + 逐章生成 + 被拒分级处理)。 */
737
797
  export interface RunState {
@@ -939,12 +999,16 @@ export interface ConfigPatch {
939
999
  outputDir?: string;
940
1000
  provider?: string;
941
1001
  model?: string;
1002
+ reasoningEffort?: 'off' | 'low' | 'high' | 'max';
942
1003
  chapterChars?: number;
943
1004
  maxTokens?: number;
944
1005
  reviewPassScore?: number;
945
1006
  autoReview?: boolean;
946
1007
  autoAuthorReview?: boolean;
947
1008
  autoReviewAfterRevise?: boolean;
1009
+ imageApiKey?: string;
1010
+ imageApiModel?: string;
1011
+ imageApiEnabled?: boolean;
948
1012
  }
949
1013
  /** One assistant conversation message (persisted per project). */
950
1014
  export interface AssistantMessage {
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.1.9",
4
+ "version": "1.3.0",
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "node": "^22.19.0 || >=24.0.0"
package/src/client/api.ts CHANGED
@@ -85,16 +85,6 @@ export class NovelApi {
85
85
  return postJson<import('../protocol.ts').BreakdownResponse>(NOVEL_API.breakdown, { scope, preset, budgetTokens })
86
86
  }
87
87
 
88
- /** 漫剧分镜生成:章节 → 角色锚点 + 分镜表。 */
89
- async storyboard(chapterNo: number, genre?: string, platform?: string, tool?: string): Promise<import('../protocol.ts').StoryboardResponse> {
90
- return postJson<import('../protocol.ts').StoryboardResponse>(NOVEL_API.storyboard, { chapterNo, genre, platform, tool })
91
- }
92
-
93
- /** 漫剧分集计划:读一卷 → 按故事弧线分集。 */
94
- async storyboardPlan(volumeNo: number, platform?: string, maxEpisodes?: number): Promise<import('../protocol.ts').StoryboardPlanResponse> {
95
- return postJson<import('../protocol.ts').StoryboardPlanResponse>(NOVEL_API.storyboardPlan, { volumeNo, platform, maxEpisodes })
96
- }
97
-
98
88
  async plan(outline?: string, chapterCount?: number, volume?: number): Promise<PlanResponse> {
99
89
  return postJson<PlanResponse>(NOVEL_API.plan, { outline, chapterCount, volume })
100
90
  }
@@ -217,6 +207,16 @@ export class NovelApi {
217
207
  return postJson<import('../protocol.ts').RolesResponse>(NOVEL_API.roles, req)
218
208
  }
219
209
 
210
+ /** 场景库:AI 提炼 / 采纳 / 更新 / 删除 / 图集。 */
211
+ async scenes(req: import('../protocol.ts').ScenesRequest): Promise<import('../protocol.ts').ScenesResponse> {
212
+ return postJson<import('../protocol.ts').ScenesResponse>(NOVEL_API.scenes, req)
213
+ }
214
+
215
+ /** 视觉世界观规则:提炼 / 保存。 */
216
+ async visualRules(req: import('../protocol.ts').VisualRulesRequest): Promise<import('../protocol.ts').VisualRulesResponse> {
217
+ return postJson<import('../protocol.ts').VisualRulesResponse>(NOVEL_API.visualRules, req)
218
+ }
219
+
220
220
  /** 小说简介:AI 生成/补全(partial 留空 = 全量),或手动保存。 */
221
221
  async blurb(action: 'generate' | 'save', text?: string, partial?: string): Promise<{ blurb: string }> {
222
222
  return postJson<{ blurb: string }>(NOVEL_API.blurb, { action, text, partial })
@@ -142,6 +142,14 @@ export const zh = {
142
142
  'settings.outputDir': '输出目录',
143
143
  'settings.provider': '模型提供商',
144
144
  'settings.model': '模型',
145
+ 'settings.modelCustom': '自定义…',
146
+ 'settings.modelCustomPlaceholder': '输入任意模型 id',
147
+ 'settings.reasoningEffort': '思考强度 (reasoningEffort)',
148
+ 'settings.reasoning.off': 'off · 关闭思考(推荐日常写文)',
149
+ 'settings.reasoning.low': 'low · 低思考',
150
+ 'settings.reasoning.high': 'high · 高思考',
151
+ 'settings.reasoning.max': 'max · 最高思考(质检/推演)',
152
+ 'settings.reasoningHint': 'off 最快最省;high/max 更慢更贵但推理更强',
145
153
  'settings.theme': '主题',
146
154
  'settings.themeLiquid': 'iOS 液态玻璃(绿)',
147
155
  'settings.themeClassic': '经典毛玻璃(蓝)',
@@ -285,6 +293,14 @@ export const en: Record<string, string> = {
285
293
  'settings.outputDir': 'Output directory',
286
294
  'settings.provider': 'Provider',
287
295
  'settings.model': 'Model',
296
+ 'settings.modelCustom': 'Custom…',
297
+ 'settings.modelCustomPlaceholder': 'Type any model id',
298
+ 'settings.reasoningEffort': 'Reasoning effort',
299
+ 'settings.reasoning.off': 'off · no thinking (recommended)',
300
+ 'settings.reasoning.low': 'low',
301
+ 'settings.reasoning.high': 'high',
302
+ 'settings.reasoning.max': 'max (audit/reasoning)',
303
+ 'settings.reasoningHint': 'off is fastest/cheapest; high/max are slower and stronger',
288
304
  'settings.theme': 'Theme',
289
305
  'settings.themeLiquid': 'iOS Liquid Glass (green)',
290
306
  'settings.themeClassic': 'Classic frosted (blue)',
@@ -206,6 +206,10 @@ export function AssetsTab({ api, initialTab = 'genre' }: AssetsTabProps) {
206
206
 
207
207
  return (
208
208
  <div style={{ display: 'flex', flexDirection: 'column', gap: 12, flex: 1, minHeight: 0 }}>
209
+ <div className={css.row} style={{ justifyContent: 'space-between', flexWrap: 'wrap' }}>
210
+ <span className={css.cardTitle} style={{ fontSize: 17, fontWeight: 700 }}>🧰 创作资产</span>
211
+ <span className={css.meta}>题材基底 / 推进模式 / 笔法帖 / 文戒 / 心法</span>
212
+ </div>
209
213
  {error !== '' && <div className={css.card} style={{ borderColor: 'var(--nf-error)' }}><span style={{ color: 'var(--nf-error)' }}>{tt('common.error')}: {error}</span></div>}
210
214
  {notice !== '' && <div className={css.card}><span style={{ color: 'var(--nf-success)' }}>{notice}</span></div>}
211
215
 
@@ -235,6 +239,21 @@ export function AssetsTab({ api, initialTab = 'genre' }: AssetsTabProps) {
235
239
  </div>
236
240
  </div>
237
241
 
242
+ {/* 子页签:题材基底 / 推进模式 / 笔法帖 / 文戒 / 心法 */}
243
+ <div className={css.row} style={{ flexWrap: 'wrap', gap: 6 }}>
244
+ {SUB_TABS.map(t => (
245
+ <button
246
+ key={t.id}
247
+ type="button"
248
+ className={`${css.button} ${assetTab === t.id ? css.buttonPrimary : ''}`}
249
+ style={{ fontSize: 14, flex: 1 }}
250
+ onClick={() => { setAssetTab(t.id) }}
251
+ >
252
+ {t.label}
253
+ </button>
254
+ ))}
255
+ </div>
256
+
238
257
  {/* 题材基底库 */}
239
258
  {assetTab === 'genre' && (
240
259
  <div className={css.card}>