@waterwx/dsh-novel-forge 1.1.8 → 1.2.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 (126) hide show
  1. package/README.md +231 -349
  2. package/lib/client.js +54833 -9926
  3. package/lib/index.js +12137 -4734
  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 +18 -3
  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 +153 -9
  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 +33 -0
  14. package/lib/types/client/locales.d.ts +34 -9
  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 +8 -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/ReasoningSection.d.ts +10 -0
  36. package/lib/types/client/panel/RunPanel.d.ts +5 -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/toast.d.ts +2 -0
  41. package/lib/types/client/panel/views.d.ts +25 -7
  42. package/lib/types/client/run-status.d.ts +19 -0
  43. package/lib/types/client/text.d.ts +9 -0
  44. package/lib/types/docx-core.d.ts +16 -0
  45. package/lib/types/docx.d.ts +2 -3
  46. package/lib/types/engine.d.ts +152 -40
  47. package/lib/types/global-assets.d.ts +6 -0
  48. package/lib/types/index.d.ts +20 -2
  49. package/lib/types/llm-live.d.ts +44 -0
  50. package/lib/types/market-radar-scan.d.ts +19 -0
  51. package/lib/types/market-radar-sources.d.ts +31 -0
  52. package/lib/types/migrate-output.d.ts +25 -0
  53. package/lib/types/novel-context.d.ts +80 -0
  54. package/lib/types/prompt-utils.d.ts +35 -0
  55. package/lib/types/prompting.d.ts +10 -0
  56. package/lib/types/protocol.d.ts +1106 -123
  57. package/lib/types/review-policy.d.ts +97 -0
  58. package/lib/types/routes.d.ts +4 -0
  59. package/lib/types/run.d.ts +34 -0
  60. package/lib/types/stage-contract.d.ts +60 -0
  61. package/package.json +52 -27
  62. package/scripts/patch-schemastery.mjs +47 -0
  63. package/src/action-guard.ts +127 -0
  64. package/src/ai-scan.ts +179 -0
  65. package/src/assets.ts +1774 -520
  66. package/src/assistant.ts +1082 -770
  67. package/src/author-assets.ts +149 -0
  68. package/src/bookshelf.ts +187 -151
  69. package/src/client/api.ts +684 -387
  70. package/src/client/docx.ts +37 -69
  71. package/src/client/index.ts +3 -1
  72. package/src/client/llmLive.ts +101 -0
  73. package/src/client/locales.ts +418 -367
  74. package/src/client/mount.tsx +1 -1
  75. package/src/client/panel/AdaptModeView.tsx +535 -0
  76. package/src/client/panel/AiTag.tsx +7 -0
  77. package/src/client/panel/AssetsTab.tsx +448 -439
  78. package/src/client/panel/AssistantTab.tsx +355 -301
  79. package/src/client/panel/AuthorAssetsView.tsx +206 -0
  80. package/src/client/panel/AuthorHome.tsx +309 -0
  81. package/src/client/panel/BookAnalysisView.tsx +63 -0
  82. package/src/client/panel/CmdPalette.tsx +102 -0
  83. package/src/client/panel/CreateBookView.tsx +366 -348
  84. package/src/client/panel/DirectorView.tsx +200 -0
  85. package/src/client/panel/GlobalAssetLibraryView.tsx +168 -0
  86. package/src/client/panel/IdeaInspirationView.tsx +76 -0
  87. package/src/client/panel/ImportModal.tsx +303 -0
  88. package/src/client/panel/KnowledgeBaseView.tsx +125 -0
  89. package/src/client/panel/LiveFeedLog.tsx +67 -0
  90. package/src/client/panel/MarketRadarView.tsx +377 -0
  91. package/src/client/panel/ModelManager.tsx +205 -0
  92. package/src/client/panel/NovelPanel.tsx +5403 -4294
  93. package/src/client/panel/PageHeader.tsx +37 -0
  94. package/src/client/panel/ProgressConsole.tsx +63 -0
  95. package/src/client/panel/ReaderView.tsx +306 -306
  96. package/src/client/panel/ReasoningSection.tsx +31 -0
  97. package/src/client/panel/RunPanel.tsx +223 -0
  98. package/src/client/panel/SettingsView.tsx +305 -0
  99. package/src/client/panel/ShelfView.tsx +266 -235
  100. package/src/client/panel/SubPage.tsx +42 -0
  101. package/src/client/panel/WorldTab.tsx +202 -202
  102. package/src/client/panel/panel.module.css +5764 -2817
  103. package/src/client/panel/toast.ts +51 -0
  104. package/src/client/panel/views.tsx +401 -314
  105. package/src/client/run-status.ts +143 -0
  106. package/src/client/sidebar-entry.ts +1 -1
  107. package/src/client/text.ts +28 -0
  108. package/src/docx-core.ts +53 -0
  109. package/src/docx.ts +44 -83
  110. package/src/engine.ts +4231 -2746
  111. package/src/global-assets.ts +59 -0
  112. package/src/index.ts +311 -246
  113. package/src/llm-live.ts +68 -0
  114. package/src/market-radar-scan.ts +41 -0
  115. package/src/market-radar-sources.ts +173 -0
  116. package/src/migrate-output.ts +76 -0
  117. package/src/novel-context.ts +286 -0
  118. package/src/prompting.ts +26 -0
  119. package/src/protocol.ts +2072 -1104
  120. package/src/review-policy.ts +161 -0
  121. package/src/routes.ts +3405 -2050
  122. package/src/run.ts +342 -0
  123. package/src/stage-contract.ts +189 -0
  124. package/lib/client.js.map +0 -1
  125. package/lib/index.js.map +0 -1
  126. package/src/client/panel/BookshelfBar.tsx +0 -170
package/src/ai-scan.ts ADDED
@@ -0,0 +1,179 @@
1
+ /**
2
+ * 本地 AI 味扫描器(不调 LLM,确定性检测)
3
+ *
4
+ * 用途:生成/润色后自动扫描,结果作为「事实锚点」注入审稿提示词,
5
+ * 让 LLM 做判断而非机械统计。也可用于 UI 展示 AI 味指标。
6
+ */
7
+
8
+ export interface AiScanResult {
9
+ /** 总评分 0-100,越高越像 AI 写的 */
10
+ aiScore: number
11
+ /** 命中的套话及次数 */
12
+ clicheHits: Array<{ word: string; count: number }>
13
+ /** 段落长度方差(过高=段落过于整齐) */
14
+ paragraphLengthVariance: number
15
+ /** 连续解释性叙事段数(≥3 视为问题) */
16
+ consecutiveExpositoryParagraphs: number
17
+ /** 句式重复率(相同开头句式占比) */
18
+ sentenceRepetitionRate: number
19
+ /** 过长段落数(>300字) */
20
+ longParagraphCount: number
21
+ /** 过短段落数(<20字) */
22
+ shortParagraphCount: number
23
+ /** 对话占比(0-1) */
24
+ dialogueRatio: number
25
+ /** 问题摘要,可直接注入审稿提示词 */
26
+ summary: string
27
+ }
28
+
29
+ /** 重灾区套话(高频出现即问题) */
30
+ const HEAVY_CLICHES = [
31
+ '不禁', '仿佛', '一时间', '不由得', '顿时', '然而',
32
+ '缓缓', '轻轻', '微微', '默默', '似乎', '终于',
33
+ '显然', '其实', '无法形容', '难以言喻', '不由自主',
34
+ ]
35
+
36
+ /** 轻微套话(偶尔出现可接受,高频才问题) */
37
+ const LIGHT_CLICHES = [
38
+ '心中', '脑海', '眼神', '嘴角', '眉头', '身影',
39
+ '气息', '光芒', '力量', '感觉', '知道', '明白',
40
+ ]
41
+
42
+ /** 解释性叙事开头模式 */
43
+ const EXPOSITORY_STARTS = [
44
+ '原来', '因为', '由于', '所以', '因此', '于是',
45
+ '这就是', '也就是说', '换句话说', '事实上', '实际上',
46
+ ]
47
+
48
+ export function scanAiFlavor(text: string): AiScanResult {
49
+ const paragraphs = text.split(/\n{2,}/).map(p => p.trim()).filter(p => p.length > 0)
50
+ const totalChars = text.length
51
+
52
+ // 1. 套话统计
53
+ const clicheHits: Array<{ word: string; count: number }> = []
54
+ let clicheTotal = 0
55
+ for (const word of [...HEAVY_CLICHES, ...LIGHT_CLICHES]) {
56
+ const count = countOccurrences(text, word)
57
+ if (count > 0) {
58
+ clicheHits.push({ word, count })
59
+ clicheTotal += count
60
+ }
61
+ }
62
+ clicheHits.sort((a, b) => b.count - a.count)
63
+
64
+ // 2. 段落长度方差
65
+ const paraLengths = paragraphs.map(p => p.length)
66
+ const avgLen = paraLengths.length > 0 ? paraLengths.reduce((a, b) => a + b, 0) / paraLengths.length : 0
67
+ const variance = paraLengths.length > 0
68
+ ? paraLengths.reduce((sum, len) => sum + Math.pow(len - avgLen, 2), 0) / paraLengths.length
69
+ : 0
70
+
71
+ // 3. 连续解释性叙事
72
+ let maxConsecutive = 0
73
+ let currentConsecutive = 0
74
+ for (const p of paragraphs) {
75
+ const isExpository = EXPOSITORY_STARTS.some(s => p.startsWith(s)) ||
76
+ (p.length > 150 && !p.includes('"') && !p.includes('「') && !p.includes('『'))
77
+ if (isExpository) {
78
+ currentConsecutive++
79
+ maxConsecutive = Math.max(maxConsecutive, currentConsecutive)
80
+ } else {
81
+ currentConsecutive = 0
82
+ }
83
+ }
84
+
85
+ // 4. 句式重复率(相同句首词)
86
+ const sentences = text.split(/[。!?!?]/).map(s => s.trim()).filter(s => s.length > 0)
87
+ const starterCounts = new Map<string, number>()
88
+ for (const s of sentences) {
89
+ const starter = s.slice(0, 2)
90
+ starterCounts.set(starter, (starterCounts.get(starter) ?? 0) + 1)
91
+ }
92
+ const repeatedStarters = Array.from(starterCounts.values()).filter(c => c >= 3).reduce((a, b) => a + b, 0)
93
+ const sentenceRepetitionRate = sentences.length > 0 ? repeatedStarters / sentences.length : 0
94
+
95
+ // 5. 段落长度分布
96
+ const longParagraphCount = paraLengths.filter(l => l > 300).length
97
+ const shortParagraphCount = paraLengths.filter(l => l < 20).length
98
+
99
+ // 6. 对话占比
100
+ const dialogueChars = (text.match(/["「『][^"」』]*["」』]/g) ?? []).join('').length
101
+ const dialogueRatio = totalChars > 0 ? dialogueChars / totalChars : 0
102
+
103
+ // 7. 综合 AI 味评分(0-100,越高越像 AI)
104
+ let aiScore = 0
105
+ // 套话密度(每千字套话数)
106
+ const clicheDensity = totalChars > 0 ? (clicheTotal / totalChars) * 1000 : 0
107
+ aiScore += Math.min(40, clicheDensity * 8)
108
+ // 段落过于整齐(方差小=AI特征)
109
+ if (variance < 2000 && paraLengths.length >= 5) aiScore += 15
110
+ // 连续解释性叙事
111
+ aiScore += Math.min(20, maxConsecutive * 5)
112
+ // 句式重复
113
+ aiScore += Math.min(15, sentenceRepetitionRate * 30)
114
+ // 对话过少
115
+ if (dialogueRatio < 0.05 && totalChars > 1000) aiScore += 10
116
+ aiScore = Math.min(100, Math.round(aiScore))
117
+
118
+ // 8. 问题摘要
119
+ const issues: string[] = []
120
+ if (clicheDensity > 3) {
121
+ const topCliches = clicheHits.slice(0, 5).map(h => `${h.word}×${h.count}`).join('、')
122
+ issues.push(`套话密度偏高(每千字 ${clicheDensity.toFixed(1)} 次):${topCliches}`)
123
+ }
124
+ if (maxConsecutive >= 3) issues.push(`连续 ${maxConsecutive} 段解释性叙事,缺少对话/动作`)
125
+ if (sentenceRepetitionRate > 0.15) issues.push(`句式重复率 ${(sentenceRepetitionRate * 100).toFixed(0)}%,开头句式单一`)
126
+ if (longParagraphCount > 3) issues.push(`${longParagraphCount} 段超过 300 字,段落过长`)
127
+ if (dialogueRatio < 0.05 && totalChars > 1000) issues.push('对话占比过低,整章偏叙述')
128
+
129
+ const summary = issues.length > 0
130
+ ? `本地 AI 味扫描(AI 味指数 ${aiScore}/100):\n` + issues.map(i => `- ${i}`).join('\n')
131
+ : `本地 AI 味扫描(AI 味指数 ${aiScore}/100):未发现明显问题。`
132
+
133
+ return {
134
+ aiScore,
135
+ clicheHits,
136
+ paragraphLengthVariance: Math.round(variance),
137
+ consecutiveExpositoryParagraphs: maxConsecutive,
138
+ sentenceRepetitionRate: Math.round(sentenceRepetitionRate * 100) / 100,
139
+ longParagraphCount,
140
+ shortParagraphCount,
141
+ dialogueRatio: Math.round(dialogueRatio * 100) / 100,
142
+ summary,
143
+ }
144
+ }
145
+
146
+ function countOccurrences(text: string, word: string): number {
147
+ if (word.length === 0) return 0
148
+ let count = 0
149
+ let idx = text.indexOf(word)
150
+ while (idx !== -1) {
151
+ count++
152
+ idx = text.indexOf(word, idx + word.length)
153
+ }
154
+ return count
155
+ }
156
+
157
+ /** 扫描前后对比(润色时用) */
158
+ export function compareAiScan(before: AiScanResult, after: AiScanResult): {
159
+ improved: boolean
160
+ deltaScore: number
161
+ details: string[]
162
+ } {
163
+ const deltaScore = before.aiScore - after.aiScore
164
+ const details: string[] = []
165
+ if (deltaScore > 0) details.push(`AI 味指数下降 ${deltaScore} 分(${before.aiScore} → ${after.aiScore})`)
166
+ else if (deltaScore < 0) details.push(`AI 味指数上升 ${-deltaScore} 分(${before.aiScore} → ${after.aiScore}),需检查`)
167
+ else details.push(`AI 味指数持平(${before.aiScore})`)
168
+
169
+ const beforeCliches = before.clicheHits.reduce((a, b) => a + b.count, 0)
170
+ const afterCliches = after.clicheHits.reduce((a, b) => a + b.count, 0)
171
+ if (afterCliches < beforeCliches) details.push(`套话减少 ${beforeCliches - afterCliches} 处`)
172
+ else if (afterCliches > beforeCliches) details.push(`套话增加 ${afterCliches - beforeCliches} 处`)
173
+
174
+ return {
175
+ improved: deltaScore > 0,
176
+ deltaScore,
177
+ details,
178
+ }
179
+ }