@waterwx/dsh-novel-forge 1.2.0 → 1.3.0-alpha

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 (141) hide show
  1. package/README.md +236 -349
  2. package/lib/client.js +56064 -10565
  3. package/lib/index.js +13922 -5218
  4. package/lib/types/action-guard.d.ts +61 -0
  5. package/lib/types/ai-scan.d.ts +36 -0
  6. package/lib/types/assets.d.ts +24 -9
  7. package/lib/types/assistant.d.ts +29 -0
  8. package/lib/types/author-assets.d.ts +25 -0
  9. package/lib/types/bookshelf.d.ts +11 -1
  10. package/lib/types/client/api.d.ts +178 -10
  11. package/lib/types/client/docx.d.ts +2 -0
  12. package/lib/types/client/index.d.ts +1 -1
  13. package/lib/types/client/llmLive.d.ts +31 -0
  14. package/lib/types/client/locales.d.ts +28 -11
  15. package/lib/types/client/panel/AdaptModeView.d.ts +5 -0
  16. package/lib/types/client/panel/AiTag.d.ts +1 -0
  17. package/lib/types/client/panel/AssistantTab.d.ts +4 -1
  18. package/lib/types/client/panel/AuthorAssetsView.d.ts +4 -0
  19. package/lib/types/client/panel/AuthorHome.d.ts +34 -0
  20. package/lib/types/client/panel/BookAnalysisView.d.ts +4 -0
  21. package/lib/types/client/panel/CmdPalette.d.ts +12 -0
  22. package/lib/types/client/panel/CreateBookView.d.ts +5 -1
  23. package/lib/types/client/panel/DirectorView.d.ts +7 -0
  24. package/lib/types/client/panel/GlobalAssetLibraryView.d.ts +4 -0
  25. package/lib/types/client/panel/IdeaInspirationView.d.ts +6 -0
  26. package/lib/types/client/panel/ImportModal.d.ts +7 -0
  27. package/lib/types/client/panel/KnowledgeBaseView.d.ts +4 -0
  28. package/lib/types/client/panel/LiveFeedLog.d.ts +13 -0
  29. package/lib/types/client/panel/MarketRadarView.d.ts +5 -0
  30. package/lib/types/client/panel/ModelManager.d.ts +14 -0
  31. package/lib/types/client/panel/NovelPanel.d.ts +6 -1
  32. package/lib/types/client/panel/PageHeader.d.ts +18 -0
  33. package/lib/types/client/panel/ProgressConsole.d.ts +17 -0
  34. package/lib/types/client/panel/ProgressHomeView.d.ts +5 -0
  35. package/lib/types/client/panel/PromptSlotsView.d.ts +11 -0
  36. package/lib/types/client/panel/ReasoningSection.d.ts +10 -0
  37. package/lib/types/client/panel/SettingsView.d.ts +19 -0
  38. package/lib/types/client/panel/ShelfView.d.ts +12 -1
  39. package/lib/types/client/panel/SubPage.d.ts +27 -0
  40. package/lib/types/client/panel/TensionView.d.ts +14 -0
  41. package/lib/types/client/panel/TimelineView.d.ts +14 -0
  42. package/lib/types/client/panel/toast.d.ts +2 -0
  43. package/lib/types/client/panel/views.d.ts +25 -7
  44. package/lib/types/client/run-status.d.ts +19 -0
  45. package/lib/types/client/text.d.ts +9 -0
  46. package/lib/types/docx-core.d.ts +16 -0
  47. package/lib/types/docx.d.ts +2 -3
  48. package/lib/types/engine.d.ts +185 -41
  49. package/lib/types/global-assets.d.ts +6 -0
  50. package/lib/types/index.d.ts +22 -2
  51. package/lib/types/llm-live.d.ts +75 -0
  52. package/lib/types/llm-retry.d.ts +38 -0
  53. package/lib/types/market-radar-scan.d.ts +19 -0
  54. package/lib/types/market-radar-sources.d.ts +31 -0
  55. package/lib/types/migrate-output.d.ts +25 -0
  56. package/lib/types/novel-context.d.ts +80 -0
  57. package/lib/types/prompt-slots.d.ts +57 -0
  58. package/lib/types/prompt-utils.d.ts +35 -0
  59. package/lib/types/prompting.d.ts +10 -0
  60. package/lib/types/protocol.d.ts +1311 -125
  61. package/lib/types/review-policy.d.ts +97 -0
  62. package/lib/types/routes.d.ts +4 -0
  63. package/lib/types/run.d.ts +3 -1
  64. package/lib/types/snapshots.d.ts +46 -0
  65. package/lib/types/stage-contract.d.ts +60 -0
  66. package/lib/types/tension.d.ts +65 -0
  67. package/lib/types/timeline.d.ts +37 -0
  68. package/package.json +52 -27
  69. package/scripts/patch-schemastery.mjs +47 -0
  70. package/src/action-guard.ts +127 -0
  71. package/src/ai-scan.ts +179 -0
  72. package/src/assets.ts +1806 -520
  73. package/src/assistant.ts +1098 -770
  74. package/src/author-assets.ts +149 -0
  75. package/src/bookshelf.ts +187 -151
  76. package/src/client/api.ts +758 -410
  77. package/src/client/docx.ts +37 -69
  78. package/src/client/index.ts +3 -1
  79. package/src/client/llmLive.ts +111 -0
  80. package/src/client/locales.ts +418 -383
  81. package/src/client/mount.tsx +1 -1
  82. package/src/client/panel/AdaptModeView.tsx +535 -0
  83. package/src/client/panel/AiTag.tsx +7 -0
  84. package/src/client/panel/AssetsTab.tsx +448 -458
  85. package/src/client/panel/AssistantTab.tsx +355 -301
  86. package/src/client/panel/AuthorAssetsView.tsx +206 -0
  87. package/src/client/panel/AuthorHome.tsx +309 -0
  88. package/src/client/panel/BookAnalysisView.tsx +63 -0
  89. package/src/client/panel/CmdPalette.tsx +102 -0
  90. package/src/client/panel/CreateBookView.tsx +366 -348
  91. package/src/client/panel/DirectorView.tsx +200 -0
  92. package/src/client/panel/GlobalAssetLibraryView.tsx +168 -0
  93. package/src/client/panel/IdeaInspirationView.tsx +76 -0
  94. package/src/client/panel/ImportModal.tsx +303 -0
  95. package/src/client/panel/KnowledgeBaseView.tsx +125 -0
  96. package/src/client/panel/LiveFeedLog.tsx +204 -0
  97. package/src/client/panel/MarketRadarView.tsx +377 -0
  98. package/src/client/panel/ModelManager.tsx +205 -0
  99. package/src/client/panel/NovelPanel.tsx +5517 -4597
  100. package/src/client/panel/PageHeader.tsx +37 -0
  101. package/src/client/panel/ProgressConsole.tsx +63 -0
  102. package/src/client/panel/PromptSlotsView.tsx +97 -0
  103. package/src/client/panel/ReaderView.tsx +306 -306
  104. package/src/client/panel/ReasoningSection.tsx +31 -0
  105. package/src/client/panel/RunPanel.tsx +223 -204
  106. package/src/client/panel/SettingsView.tsx +340 -0
  107. package/src/client/panel/ShelfView.tsx +266 -236
  108. package/src/client/panel/SubPage.tsx +42 -0
  109. package/src/client/panel/TensionView.tsx +196 -0
  110. package/src/client/panel/TimelineView.tsx +192 -0
  111. package/src/client/panel/WorldTab.tsx +202 -202
  112. package/src/client/panel/panel.module.css +5764 -2941
  113. package/src/client/panel/toast.ts +51 -0
  114. package/src/client/panel/views.tsx +401 -314
  115. package/src/client/run-status.ts +143 -0
  116. package/src/client/sidebar-entry.ts +1 -1
  117. package/src/client/text.ts +28 -0
  118. package/src/docx-core.ts +53 -0
  119. package/src/docx.ts +44 -83
  120. package/src/engine.ts +4672 -2787
  121. package/src/global-assets.ts +59 -0
  122. package/src/index.ts +323 -252
  123. package/src/llm-live.ts +260 -0
  124. package/src/llm-retry.ts +77 -0
  125. package/src/market-radar-scan.ts +41 -0
  126. package/src/market-radar-sources.ts +173 -0
  127. package/src/migrate-output.ts +76 -0
  128. package/src/novel-context.ts +286 -0
  129. package/src/prompt-slots.ts +125 -0
  130. package/src/prompting.ts +26 -0
  131. package/src/protocol.ts +2383 -1182
  132. package/src/review-policy.ts +161 -0
  133. package/src/routes.ts +3724 -2151
  134. package/src/run.ts +346 -330
  135. package/src/snapshots.ts +174 -0
  136. package/src/stage-contract.ts +189 -0
  137. package/src/tension.ts +240 -0
  138. package/src/timeline.ts +159 -0
  139. package/lib/client.js.map +0 -1
  140. package/lib/index.js.map +0 -1
  141. package/src/client/panel/BookshelfBar.tsx +0 -170
@@ -0,0 +1,159 @@
1
+ /**
2
+ * 故事时间线:把「章节正文」压成可排序的时间事件,用于后续章节的时间连续性。
3
+ *
4
+ * 与编年录的分工:编年录记「事实」(人物状态/资源/关系),时间线记「顺序」
5
+ * (什么时候、在哪里、谁在场、发生了什么),专门用来抓时间倒流、地点瞬移与
6
+ * 跨越章节的时序矛盾。
7
+ *
8
+ * 本模块只放纯逻辑(可离线单测):排序、注入渲染、规则初筛、事件归一化;
9
+ * 模型交互(抽取/复核)在 engine.ts,落盘与路由在 routes.ts。
10
+ */
11
+
12
+ import type { ProjectState, TimelineEvent, TimelineIssue } from './protocol.ts'
13
+
14
+ /** 注入后续章节的时间线锚点条数(超过则显式声明省略,遵循截断显式化规则)。 */
15
+ export const TIMELINE_CONTEXT_LIMIT = 12
16
+
17
+ /** 时间线矛盾初筛的最少事件数(太少没有意义)。 */
18
+ const MIN_EVENTS_FOR_CHECK = 2
19
+
20
+ /** 归一化事件(清洗字符串、补 id/时间戳)。 */
21
+ export function normalizeTimelineEvent(raw: Partial<TimelineEvent>, chapterNo: number, index: number, now = new Date().toISOString()): TimelineEvent {
22
+ const characters = Array.isArray(raw.characters) ? raw.characters.filter(c => typeof c === 'string' && c.trim() !== '') : []
23
+ return {
24
+ id: typeof raw.id === 'string' && raw.id !== '' ? raw.id : `tl-${chapterNo}-${index}-${Math.random().toString(36).slice(2, 8)}`,
25
+ chapterNo: typeof raw.chapterNo === 'number' ? raw.chapterNo : chapterNo,
26
+ time: (raw.time ?? '').trim(),
27
+ order: typeof raw.order === 'number' && Number.isFinite(raw.order) ? raw.order : undefined,
28
+ place: (raw.place ?? '').trim(),
29
+ characters: characters.map(c => c.trim()),
30
+ event: (raw.event ?? '').trim(),
31
+ source: raw.source === 'manual' ? 'manual' : 'extracted',
32
+ createdAt: raw.createdAt ?? now,
33
+ }
34
+ }
35
+
36
+ /** 时间线排序:先章节、再 order(缺省排在该章末尾)、最后 createdAt。 */
37
+ export function sortTimeline(events: readonly TimelineEvent[]): TimelineEvent[] {
38
+ return [...events].sort((a, b) => {
39
+ if (a.chapterNo !== b.chapterNo) return a.chapterNo - b.chapterNo
40
+ const ao = a.order ?? Number.MAX_SAFE_INTEGER
41
+ const bo = b.order ?? Number.MAX_SAFE_INTEGER
42
+ if (ao !== bo) return ao - bo
43
+ return a.createdAt.localeCompare(b.createdAt)
44
+ })
45
+ }
46
+
47
+ /**
48
+ * 渲染注入到「写作/规划」提示词的时间线锚点块。
49
+ *
50
+ * @param project 项目状态。
51
+ * @param beforeChapter 只注入该章之前的锚点(含该章本身不注入,避免剧透)。
52
+ * @returns 提示词片段(无事件时返回空串)。带显式截断声明。
53
+ */
54
+ export function renderTimelineBlock(project: ProjectState, beforeChapter: number): string {
55
+ const all = sortTimeline(project.timeline ?? []).filter(e => e.chapterNo < beforeChapter)
56
+ if (all.length === 0) return ''
57
+ const shown = all.slice(-TIMELINE_CONTEXT_LIMIT)
58
+ const omitted = all.length - shown.length
59
+ const head = omitted > 0
60
+ ? `【故事时间线(共 ${all.length} 条,此处只列最近 ${shown.length} 条;更早 ${omitted} 条未列出)】`
61
+ : `【故事时间线(共 ${shown.length} 条)】`
62
+ const lines = shown.map(e => {
63
+ const parts = [`[第${e.chapterNo}章]`]
64
+ if (e.time !== '') parts.push(e.time)
65
+ if (e.place !== '') parts.push(`@${e.place}`)
66
+ if (e.characters.length > 0) parts.push(`(${e.characters.join('、')})`)
67
+ parts.push(e.event)
68
+ return '- ' + parts.join(' ')
69
+ })
70
+ return `${head}\n${lines.join('\n')}\n写作时必须与前文时间顺序、地点衔接一致;不得让时间倒流或让人物瞬移。`
71
+ }
72
+
73
+ /**
74
+ * 规则初筛时间线矛盾(不调模型,瞬时计算)。
75
+ *
76
+ * 覆盖三类最容易犯又最容易用规则抓到的错:
77
+ * 1) 同一章内事件顺序号倒退;
78
+ * 2) 全书顺序号在章节推进后倒退(时间倒流);
79
+ * 3) 相邻两章之间地点瞬移(同一角色在未交代移动的情况下跨地点)。
80
+ *
81
+ * @param project 项目状态。
82
+ * @returns 问题清单(可能为空)。
83
+ */
84
+ export function detectTimelineIssues(project: ProjectState): TimelineIssue[] {
85
+ // 注意:这里用**原始数组顺序**分组(= 模型/作者的叙述顺序),而不是按 order 排序后的顺序。
86
+ // 否则「模型自报 order 与叙述顺序打架」这一类问题会被排序抹平,永远查不出来。
87
+ const stored = project.timeline ?? []
88
+ if (stored.length < MIN_EVENTS_FOR_CHECK) return []
89
+ const issues: TimelineIssue[] = []
90
+
91
+ // 1) 同章内顺序倒退(叙述顺序 vs 自报 order)
92
+ const byChapter = new Map<number, TimelineEvent[]>()
93
+ for (const e of stored) {
94
+ const list = byChapter.get(e.chapterNo) ?? []
95
+ list.push(e)
96
+ byChapter.set(e.chapterNo, list)
97
+ }
98
+ for (const [no, list] of byChapter) {
99
+ let lastOrder: number | undefined
100
+ for (const e of list) {
101
+ if (e.order === undefined) continue
102
+ if (lastOrder !== undefined && e.order < lastOrder) {
103
+ issues.push({
104
+ severity: 'medium',
105
+ chapters: [no],
106
+ item: `第${no}章内时间顺序倒退:事件「${e.event}」的顺序号 ${e.order} 小于前一条 ${lastOrder}`,
107
+ suggestion: '检查这两条事件的先后关系,修正 order 或改写正文的时间描述。',
108
+ })
109
+ }
110
+ lastOrder = e.order
111
+ }
112
+ }
113
+
114
+ // 2) 跨章时间倒流(用每章最大 order 与上一章比较)
115
+ const chapterMax: Array<{ no: number; max: number; event: TimelineEvent }> = []
116
+ for (const [no, list] of [...byChapter].sort((a, b) => a[0] - b[0])) {
117
+ const withOrder = list.filter(e => e.order !== undefined)
118
+ if (withOrder.length === 0) continue
119
+ const maxEvent = withOrder.reduce((best, e) => ((e.order ?? 0) > (best.order ?? 0) ? e : best), withOrder[0]!)
120
+ chapterMax.push({ no, max: maxEvent.order ?? 0, event: maxEvent })
121
+ }
122
+ for (let i = 1; i < chapterMax.length; i++) {
123
+ const prev = chapterMax[i - 1]!
124
+ const cur = chapterMax[i]!
125
+ if (cur.max < prev.max) {
126
+ issues.push({
127
+ severity: 'high',
128
+ chapters: [prev.no, cur.no],
129
+ item: `时间倒流:第${cur.no}章(${cur.event.time || '未标注时间'})早于第${prev.no}章(${prev.event.time || '未标注时间'})`,
130
+ suggestion: '确认是否需要补写过渡(回忆/插叙)或调整章节顺序;若是有意插叙,请在正文中明确标注。',
131
+ })
132
+ }
133
+ }
134
+
135
+ // 3) 相邻章地点瞬移(同一角色、地点变化且新地点首次出现)
136
+ const seenPlaces = new Set<string>()
137
+ let prevPlace = ''
138
+ const lastChapterOf = new Map<string, string>()
139
+ for (const e of sortTimeline(stored)) {
140
+ if (e.place !== '') {
141
+ if (prevPlace !== '' && e.place !== prevPlace && !seenPlaces.has(e.place)) {
142
+ // 新地点首次出现:只有在同一角色上一章还在别处时才提示
143
+ const moved = e.characters.filter(c => lastChapterOf.get(c) !== undefined && lastChapterOf.get(c) !== e.place)
144
+ if (moved.length > 0) {
145
+ issues.push({
146
+ severity: 'medium',
147
+ chapters: [e.chapterNo],
148
+ item: `地点衔接存疑:${moved.join('、')} 上一场景在「${lastChapterOf.get(moved[0]!) ?? '?'}」,本章直接出现在「${e.place}」`,
149
+ suggestion: '补一句移动/时间流逝的交代,或确认是否漏写了赶路段落。',
150
+ })
151
+ }
152
+ }
153
+ seenPlaces.add(e.place)
154
+ prevPlace = e.place
155
+ for (const c of e.characters) lastChapterOf.set(c, e.place)
156
+ }
157
+ }
158
+ return issues
159
+ }