@waterwx/dsh-novel-forge 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +180 -0
  3. package/cordis.patch.yml +13 -0
  4. package/lib/client.js +3924 -0
  5. package/lib/client.js.map +1 -0
  6. package/lib/index.js +3120 -0
  7. package/lib/index.js.map +1 -0
  8. package/lib/types/assets.d.ts +35 -0
  9. package/lib/types/assistant.d.ts +43 -0
  10. package/lib/types/bookshelf.d.ts +35 -0
  11. package/lib/types/client/api.d.ts +68 -0
  12. package/lib/types/client/docx.d.ts +15 -0
  13. package/lib/types/client/index.d.ts +14 -0
  14. package/lib/types/client/locales.d.ts +139 -0
  15. package/lib/types/client/mount.d.ts +9 -0
  16. package/lib/types/client/panel/AssetsTab.d.ts +7 -0
  17. package/lib/types/client/panel/AssistantTab.d.ts +7 -0
  18. package/lib/types/client/panel/BookshelfBar.d.ts +11 -0
  19. package/lib/types/client/panel/NovelPanel.d.ts +13 -0
  20. package/lib/types/client/panel/controller.d.ts +19 -0
  21. package/lib/types/client/panel/helpers.d.ts +8 -0
  22. package/lib/types/client/sidebar-entry.d.ts +13 -0
  23. package/lib/types/docx.d.ts +19 -0
  24. package/lib/types/engine.d.ts +95 -0
  25. package/lib/types/index.d.ts +55 -0
  26. package/lib/types/protocol.d.ts +521 -0
  27. package/lib/types/routes.d.ts +29 -0
  28. package/package.json +105 -0
  29. package/src/assets.ts +518 -0
  30. package/src/assistant.ts +547 -0
  31. package/src/bookshelf.ts +137 -0
  32. package/src/client/api.ts +254 -0
  33. package/src/client/css-modules.d.ts +8 -0
  34. package/src/client/docx.ts +69 -0
  35. package/src/client/index.ts +34 -0
  36. package/src/client/locales.ts +271 -0
  37. package/src/client/mount.tsx +97 -0
  38. package/src/client/panel/AssetsTab.tsx +341 -0
  39. package/src/client/panel/AssistantTab.tsx +188 -0
  40. package/src/client/panel/BookshelfBar.tsx +116 -0
  41. package/src/client/panel/NovelPanel.tsx +990 -0
  42. package/src/client/panel/controller.ts +45 -0
  43. package/src/client/panel/helpers.ts +17 -0
  44. package/src/client/panel/panel.module.css +894 -0
  45. package/src/client/sidebar-entry.ts +122 -0
  46. package/src/docx.ts +83 -0
  47. package/src/engine.ts +1019 -0
  48. package/src/index.ts +184 -0
  49. package/src/protocol.ts +539 -0
  50. package/src/routes.ts +955 -0
@@ -0,0 +1,539 @@
1
+ /**
2
+ * dsh-novel-forge — shared protocol between the host half (Node) and the
3
+ * browser half (web GUI). Route paths, request/response shapes, the project
4
+ * state file format, and the NDJSON generation stream frames all live here so
5
+ * both halves spell exactly one vocabulary.
6
+ */
7
+
8
+ /** The /api/dsh-novel-forge route family (same-origin, loopback-fenced). */
9
+ export const NOVEL_API = {
10
+ status: '/api/dsh-novel-forge/status',
11
+ loadOutline: '/api/dsh-novel-forge/load-outline',
12
+ saveOutline: '/api/dsh-novel-forge/save-outline',
13
+ plan: '/api/dsh-novel-forge/plan',
14
+ volumes: '/api/dsh-novel-forge/volumes',
15
+ bible: '/api/dsh-novel-forge/bible',
16
+ assets: '/api/dsh-novel-forge/assets',
17
+ styleEngine: '/api/dsh-novel-forge/style-engine',
18
+ generate: '/api/dsh-novel-forge/generate',
19
+ review: '/api/dsh-novel-forge/review',
20
+ rewrite: '/api/dsh-novel-forge/rewrite',
21
+ polish: '/api/dsh-novel-forge/polish',
22
+ summary: '/api/dsh-novel-forge/summary',
23
+ foreshadow: '/api/dsh-novel-forge/foreshadow',
24
+ exportBook: '/api/dsh-novel-forge/export',
25
+ chapter: '/api/dsh-novel-forge/chapter',
26
+ assistant: '/api/dsh-novel-forge/assistant',
27
+ assistantHistory: '/api/dsh-novel-forge/assistant-history',
28
+ bookshelf: '/api/dsh-novel-forge/bookshelf',
29
+ config: '/api/dsh-novel-forge/config',
30
+ openFolder: '/api/dsh-novel-forge/open-folder',
31
+ } as const
32
+
33
+ /** 书架:一本书的条目。 */
34
+ export interface BookEntry {
35
+ /** 稳定 id。 */
36
+ id: string
37
+ /** 书名。 */
38
+ bookName: string
39
+ /** 该书输出目录(独立项目目录)。 */
40
+ outputDir: string
41
+ /** 创建时间。 */
42
+ createdAt: string
43
+ /** 最后活动时间。 */
44
+ updatedAt: string
45
+ }
46
+
47
+ /** 书架快照(含每本书的进度摘要)。 */
48
+ export interface BookshelfSnapshot {
49
+ books: Array<BookEntry & { done: number; total: number; hasProject: boolean }>
50
+ /** 当前激活的书 id(无则 null)。 */
51
+ activeBookId: string | null
52
+ }
53
+
54
+ /** POST /bookshelf 请求:创建新书。 */
55
+ export interface BookCreateRequest {
56
+ bookName: string
57
+ outputDir?: string
58
+ }
59
+
60
+ /** POST /bookshelf/activate 请求:切换当前书。 */
61
+ export interface BookActivateRequest {
62
+ id: string
63
+ }
64
+
65
+ /** POST /bookshelf/remove 请求:移除书架条目。 */
66
+ export interface BookRemoveRequest {
67
+ id: string
68
+ }
69
+
70
+ /** Chapter lifecycle states (the writing pipeline's state machine). */
71
+ export type ChapterStatus =
72
+ | 'pending' // planned, not started
73
+ | 'generating' // LLM writing right now
74
+ | 'written' // body on disk, awaiting review
75
+ | 'reviewing' // review in progress
76
+ | 'approved' // passed review (or user-approved)
77
+ | 'rejected' // review found problems
78
+ | 'error' // generation failed
79
+
80
+ /** One chapter in the plan. */
81
+ export interface ChapterPlan {
82
+ /** 1-based chapter number (stable identity; files are named from it). */
83
+ no: number
84
+ /** Volume this chapter belongs to (1-based; 0 = unassigned). */
85
+ volume: number
86
+ /** Chapter title, decided by the LLM plan step. */
87
+ title: string
88
+ /** Story beats / plot points for this chapter (model-facing guidance). */
89
+ beats: string
90
+ /** Target character count (defaults to the configured chapter size). */
91
+ targetChars: number
92
+ /** Generation/review state. */
93
+ status: ChapterStatus
94
+ /** Actual character count once generated. */
95
+ chars?: number
96
+ /** Failure message when status is 'error'. */
97
+ error?: string
98
+ /** Output file name once generated (relative to the output dir). */
99
+ file?: string
100
+ /** LLM summary of the chapter (narrative memory for later chapters). */
101
+ summary?: string
102
+ /** Latest review report (present once reviewed). */
103
+ review?: ReviewReport
104
+ }
105
+
106
+ /** One review finding. */
107
+ export interface ReviewIssue {
108
+ /** Severity: high = must fix, medium = should fix, low = suggestion. */
109
+ severity: 'high' | 'medium' | 'low'
110
+ /** What the problem is. */
111
+ item: string
112
+ /** Concrete suggestion for fixing it. */
113
+ suggestion: string
114
+ }
115
+
116
+ /** AI review report for one chapter. */
117
+ export interface ReviewReport {
118
+ /** Overall score 0-100. */
119
+ score: number
120
+ /** Pass threshold (config; 70 default). */
121
+ passed: boolean
122
+ /** One-line verdict. */
123
+ verdict: string
124
+ /** Individual findings. */
125
+ issues: ReviewIssue[]
126
+ /** When the review ran. */
127
+ reviewedAt: string
128
+ }
129
+
130
+ /** A volume of the book. */
131
+ export interface Volume {
132
+ /** 1-based volume number. */
133
+ no: number
134
+ /** Volume title. */
135
+ title: string
136
+ /** Volume positioning / summary. */
137
+ summary: string
138
+ /** First chapter number of this volume. */
139
+ chapterStart: number
140
+ /** Last chapter number (inclusive). */
141
+ chapterEnd: number
142
+ }
143
+
144
+ /** A character card from the story bible. */
145
+ export interface CharacterCard {
146
+ name: string
147
+ role: 'protagonist' | 'supporting' | 'antagonist' | 'other'
148
+ /** Personality / traits (short lines). */
149
+ traits: string[]
150
+ /** Goals and motivations. */
151
+ goals: string
152
+ /** Key relations to other characters. */
153
+ relations: string
154
+ }
155
+
156
+ /** The structured story bible (worldbuilding extracted from the outline). */
157
+ export interface StoryBible {
158
+ /** Genre + tone tags. */
159
+ genre: string
160
+ /** Worldbuilding rules (power system, geography, factions...). */
161
+ worldRules: string[]
162
+ /** Character cards. */
163
+ characters: CharacterCard[]
164
+ /** Writing red lines (forbidden content / must-avoid tropes). */
165
+ redLines: string[]
166
+ /** Style guidance (pacing, pov, tone). */
167
+ style: string[]
168
+ /** When the bible was generated. */
169
+ generatedAt?: string
170
+ }
171
+
172
+ /** A planted/active/resolved foreshadowing thread. */
173
+ export interface Foreshadow {
174
+ /** Stable id. */
175
+ id: string
176
+ /** What the foreshadow is. */
177
+ description: string
178
+ /** Chapter where it was planted (undefined = planned). */
179
+ plantedChapter?: number
180
+ /** Chapter where it should be paid off. */
181
+ targetChapter?: number
182
+ /** Lifecycle state. */
183
+ status: 'planned' | 'planted' | 'progressing' | 'resolved' | 'abandoned'
184
+ /** Resolution note when resolved. */
185
+ resolvedNote?: string
186
+ }
187
+
188
+ /** The persisted project: outline + bible + plan + progress. */
189
+ export interface ProjectState {
190
+ /** Book title (first non-empty line of the outline, usually). */
191
+ bookName: string
192
+ /** Full outline text (docx-extracted or pasted). */
193
+ outline: string
194
+ /** Source outline path when loaded from a docx. */
195
+ outlinePath?: string
196
+ /** Structured story bible (worldbuilding), if generated. */
197
+ bible?: StoryBible
198
+ /** Volumes, if planned. */
199
+ volumes?: Volume[]
200
+ /** Chapter plan. */
201
+ chapters: ChapterPlan[]
202
+ /** Foreshadowing threads. */
203
+ foreshadows: Foreshadow[]
204
+ /** 写作资产(题材基底/推进模式/反AI规则/写法资产)。 */
205
+ assets?: ProjectAssets
206
+ /** ISO timestamps. */
207
+ createdAt: string
208
+ updatedAt: string
209
+ }
210
+
211
+ /** Runtime config surface exposed to the panel (subset of plugin Config). */
212
+ export interface NovelConfig {
213
+ /** Absolute path of the default docx outline to load. */
214
+ outlinePath: string
215
+ /** Absolute output directory for chapters + project state. */
216
+ outputDir: string
217
+ /** LLM provider route (e.g. deepseek-official). */
218
+ provider: string
219
+ /** LLM model id (e.g. deepseek-v4-flash). */
220
+ model: string
221
+ /** Target characters per chapter. */
222
+ chapterChars: number
223
+ /** Max output tokens per chapter call. */
224
+ maxTokens: number
225
+ /** Review pass threshold (0-100). */
226
+ reviewPassScore: number
227
+ /** Whether generation auto-runs review after writing. */
228
+ autoReview: boolean
229
+ }
230
+
231
+ /** GET /status response. */
232
+ export interface StatusResponse {
233
+ config: NovelConfig
234
+ /** The persisted project, when one exists in the output dir. */
235
+ project?: ProjectState
236
+ /** Chapter files already on disk (basenames, sorted). */
237
+ generatedFiles: string[]
238
+ }
239
+
240
+ /** POST /load-outline request: either a docx path or raw text. */
241
+ export interface LoadOutlineRequest {
242
+ /** Absolute docx path; defaults to the configured outline path. */
243
+ path?: string
244
+ /** Raw outline text (takes precedence over path when present). */
245
+ text?: string
246
+ }
247
+
248
+ /** POST /load-outline response. */
249
+ export interface LoadOutlineResponse {
250
+ outline: string
251
+ bookName: string
252
+ chars: number
253
+ path?: string
254
+ }
255
+
256
+ /** POST /plan request. */
257
+ export interface PlanRequest {
258
+ /** Outline to plan from; defaults to the persisted project's outline. */
259
+ outline?: string
260
+ /** Number of chapters to plan (default: 30). */
261
+ chapterCount?: number
262
+ /** Volume to plan (1-based); when given, plans only that volume's chapters. */
263
+ volume?: number
264
+ }
265
+
266
+ /** POST /plan response. */
267
+ export interface PlanResponse {
268
+ chapters: ChapterPlan[]
269
+ volumes?: Volume[]
270
+ }
271
+
272
+ /** POST /volumes request/response. */
273
+ export interface VolumesRequest {
274
+ /** Outline to split into volumes; defaults to the project outline. */
275
+ outline?: string
276
+ }
277
+ export interface VolumesResponse {
278
+ volumes: Volume[]
279
+ }
280
+
281
+ /** POST /bible request/response. */
282
+ export interface BibleRequest {
283
+ /** Outline to extract from; defaults to the project outline. */
284
+ outline?: string
285
+ }
286
+ export interface BibleResponse {
287
+ bible: StoryBible
288
+ }
289
+
290
+ /** POST /generate request: one chapter of the current project. */
291
+ export interface GenerateRequest {
292
+ chapterNo: number
293
+ /** When true, skips the auto-review step. */
294
+ skipReview?: boolean
295
+ }
296
+
297
+ /** One NDJSON frame of a generation/review/rewrite stream. */
298
+ export type JobFrame =
299
+ | { type: 'start'; no: number; title: string }
300
+ | { type: 'delta'; text: string }
301
+ | { type: 'progress'; chars: number }
302
+ | { type: 'done'; no: number; file: string; chars: number; title: string }
303
+ | { type: 'review'; no: number; report: ReviewReport }
304
+ | { type: 'rewritten'; no: number; file: string; chars: number }
305
+ | { type: 'error'; no: number; message: string }
306
+
307
+ /** POST /review request: review one written chapter. */
308
+ export interface ReviewRequest {
309
+ chapterNo: number
310
+ }
311
+
312
+ /** POST /rewrite request: rewrite one chapter (optionally per review issues). */
313
+ export interface RewriteRequest {
314
+ chapterNo: number
315
+ /** Free-form instructions; defaults to fixing the review's high issues. */
316
+ instructions?: string
317
+ /**
318
+ * 局部修订:正文中的一段原文(无需完全精确,取一个自然段内的片段即可)。
319
+ * 提供时只重写该段,其余正文保持不变;不提供时整章重写。
320
+ */
321
+ target?: string
322
+ }
323
+
324
+ /** POST /polish request: de-AI-ify one chapter. */
325
+ export interface PolishRequest {
326
+ chapterNo: number
327
+ }
328
+
329
+ /** POST /summary request: (re)generate a chapter summary. */
330
+ export interface SummaryRequest {
331
+ chapterNo: number
332
+ }
333
+
334
+ /** POST /foreshadow request: create, update, or AI-suggest foreshadows. */
335
+ export interface ForeshadowRequest {
336
+ /** When true, runs the LLM suggestion pass (ignores other fields). */
337
+ suggest?: boolean
338
+ /** When given, updates that foreshadow instead of creating one. */
339
+ id?: string
340
+ description?: string
341
+ plantedChapter?: number
342
+ targetChapter?: number
343
+ status?: Foreshadow['status']
344
+ resolvedNote?: string
345
+ }
346
+ export interface ForeshadowResponse {
347
+ foreshadows: Foreshadow[]
348
+ }
349
+
350
+ /** GET /chapter response. */
351
+ export interface ChapterResponse {
352
+ no: number
353
+ title: string
354
+ markdown: string
355
+ }
356
+
357
+ /** POST /export request/response. */
358
+ export interface ExportRequest {
359
+ format: 'txt' | 'md'
360
+ }
361
+ export interface ExportResponse {
362
+ file: string
363
+ chars: number
364
+ chapters: number
365
+ }
366
+
367
+ /** POST /config request: patch any subset of the runtime config. */
368
+ export interface ConfigPatch {
369
+ outlinePath?: string
370
+ outputDir?: string
371
+ provider?: string
372
+ model?: string
373
+ chapterChars?: number
374
+ maxTokens?: number
375
+ reviewPassScore?: number
376
+ autoReview?: boolean
377
+ }
378
+
379
+ // ------------------------------------------------------------ assistant
380
+
381
+ /** One assistant conversation message (persisted per project). */
382
+ export interface AssistantMessage {
383
+ role: 'user' | 'assistant' | 'tool'
384
+ /** Message text (tool messages carry the tool result). */
385
+ content: string
386
+ /** ISO timestamp. */
387
+ ts: string
388
+ /** For tool messages: which tool ran. */
389
+ tool?: string
390
+ }
391
+
392
+ /** POST /assistant request: one user turn. */
393
+ export interface AssistantRequest {
394
+ message: string
395
+ }
396
+
397
+ /** One NDJSON frame of the assistant stream. */
398
+ export type AssistantFrame =
399
+ | { type: 'delta'; text: string }
400
+ | { type: 'tool'; name: string; status: 'start' | 'done' | 'error'; detail?: string }
401
+ /** Live output while a tool runs (e.g. chapter text being generated). */
402
+ | { type: 'toolDelta'; name: string; text: string }
403
+ | { type: 'done' }
404
+ | { type: 'error'; message: string }
405
+
406
+ /** GET /assistant-history response. */
407
+ export interface AssistantHistoryResponse {
408
+ messages: AssistantMessage[]
409
+ }
410
+
411
+ // ---------------------------------------------------------- writing assets
412
+
413
+ /** 题材基底库:一本书属于哪个阅读市场。树形(题材→子题材→下级)。 */
414
+ export interface GenreNode {
415
+ /** 题材名称(标签,如「仙侠修真」「都市异能」)。 */
416
+ name: string
417
+ /** 题材特征、常见爽点、叙事重心或读者期待。 */
418
+ description: string
419
+ /** 子题材。 */
420
+ children: GenreNode[]
421
+ }
422
+
423
+ /** 推进模式库:读者为什么继续看下一章。 */
424
+ export interface ProgressionMode {
425
+ /** 模式名称(如「升级变强」「经营扩张」「解谜揭露」)。 */
426
+ name: string
427
+ /** 核心驱动力:靠什么制造追读动力。 */
428
+ driver: string
429
+ /** 读者期待:每隔几章获得什么变化或回报。 */
430
+ readerExpectation: string
431
+ /** 常见兑现方式(爽点如何落地)。 */
432
+ payoffs: string[]
433
+ /** 节奏风险:最怕什么(重复升级、冲突变弱、谜题拖太久…)。 */
434
+ risks: string[]
435
+ /** 主模式或辅助模式。 */
436
+ primary: boolean
437
+ }
438
+
439
+ /** 反 AI 规则:一条「要避免的问题 + 修正方向」。 */
440
+ export interface AntiAiRule {
441
+ /** 规则名(如「禁止解释型心理描写」「AI 高频套话」)。 */
442
+ name: string
443
+ /** 要避免的表达问题,具体可检查。 */
444
+ avoid: string
445
+ /** 推荐修正方向。 */
446
+ fix: string
447
+ /** 命中即告警的具体表达模式(用于审稿逐条核对与去 AI 味检测)。 */
448
+ detectPatterns?: string[]
449
+ /** 是否内置全局规则(内置规则随插件发布,项目规则为用户自定义)。 */
450
+ builtin?: boolean
451
+ }
452
+
453
+ /** 预置写法模板(来自 AI-Novel-Writing-Assistant 内置数据,一键绑定无需样本文本)。 */
454
+ export interface StyleTemplate {
455
+ /** 模板 key(如 power-up-escalation)。 */
456
+ key: string
457
+ /** 模板名(如「爽文递进推进流」)。 */
458
+ name: string
459
+ /** 模板说明。 */
460
+ description: string
461
+ /** 分类(如「爽文流」「悬疑流」)。 */
462
+ category: string
463
+ /** 适用题材。 */
464
+ applicableGenres: string[]
465
+ /** 叙述规则。 */
466
+ proseRules: string[]
467
+ /** 角色/台词规则。 */
468
+ dialogueRules: string[]
469
+ /** 语言规则。 */
470
+ languageRules: string[]
471
+ /** 节奏规则。 */
472
+ rhythmRules: string[]
473
+ /** 该模板默认绑定的反 AI 规则 key(内置规则名)。 */
474
+ defaultAntiAiRuleKeys: string[]
475
+ }
476
+
477
+ /** 写法引擎:从样本文本提取的叙事风格资产。 */
478
+ export interface StyleAsset {
479
+ /** 资产名(如「林越式痞坏」「冷峻猎手风」)。 */
480
+ name: string
481
+ /** 叙述视角与句式节奏。 */
482
+ proseRules: string[]
483
+ /** 角色台词风格。 */
484
+ dialogueRules: string[]
485
+ /** 描写密度与情绪表达。 */
486
+ descriptionRules: string[]
487
+ /** 表达边界(不要做什么)。 */
488
+ boundaries: string[]
489
+ /** 来源样本文本(可空)。 */
490
+ sourceText?: string
491
+ /** 创建时间。 */
492
+ createdAt: string
493
+ }
494
+
495
+ /** 项目写作资产(题材基底 + 推进模式 + 反 AI 规则 + 写法资产)。 */
496
+ export interface ProjectAssets {
497
+ /** 本书选用的题材(可以是题材基底库中某节点名)。 */
498
+ genre?: GenreNode
499
+ /** 主推进模式。 */
500
+ primaryProgression?: ProgressionMode
501
+ /** 辅助推进模式。 */
502
+ auxiliaryProgressions: ProgressionMode[]
503
+ /** 生效的反 AI 规则(内置 + 自定义)。 */
504
+ antiAiRules: AntiAiRule[]
505
+ /** 绑定的写法资产。 */
506
+ styleAssets: StyleAsset[]
507
+ /** 资产更新时间。 */
508
+ updatedAt?: string
509
+ }
510
+
511
+ /** GET /assets response(含全局题材库与反 AI 规则库)。 */
512
+ export interface AssetsResponse {
513
+ projectAssets: ProjectAssets
514
+ /** 全局题材基底库(可复用资产,跨书)。 */
515
+ genreLibrary: GenreNode[]
516
+ /** 全局反 AI 规则库(内置默认规则)。 */
517
+ antiAiLibrary: AntiAiRule[]
518
+ /** 预置写法模板(一键绑定,无需样本文本)。 */
519
+ styleTemplates: StyleTemplate[]
520
+ /** 内置推进模式候选。 */
521
+ progressionLibrary: ProgressionMode[]
522
+ }
523
+
524
+ /** POST /assets request:更新项目写作资产(部分字段可选)。 */
525
+ export interface AssetsPatch {
526
+ genre?: GenreNode
527
+ primaryProgression?: ProgressionMode
528
+ auxiliaryProgressions?: ProgressionMode[]
529
+ antiAiRules?: AntiAiRule[]
530
+ styleAssets?: StyleAsset[]
531
+ }
532
+
533
+ /** POST /style-engine request:从样本文本提取写法资产。 */
534
+ export interface StyleEngineRequest {
535
+ /** 样本文本(风格来源)。 */
536
+ sampleText: string
537
+ /** 资产名(可选,默认「风格资产 N」)。 */
538
+ name?: string
539
+ }