@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.
- package/README.md +231 -349
- package/lib/client.js +54833 -9926
- package/lib/index.js +12137 -4734
- package/lib/types/action-guard.d.ts +61 -0
- package/lib/types/ai-scan.d.ts +36 -0
- package/lib/types/assets.d.ts +18 -3
- package/lib/types/assistant.d.ts +29 -0
- package/lib/types/author-assets.d.ts +25 -0
- package/lib/types/bookshelf.d.ts +11 -1
- package/lib/types/client/api.d.ts +153 -9
- package/lib/types/client/docx.d.ts +2 -0
- package/lib/types/client/index.d.ts +1 -1
- package/lib/types/client/llmLive.d.ts +33 -0
- package/lib/types/client/locales.d.ts +34 -9
- package/lib/types/client/panel/AdaptModeView.d.ts +5 -0
- package/lib/types/client/panel/AiTag.d.ts +1 -0
- package/lib/types/client/panel/AssistantTab.d.ts +4 -1
- package/lib/types/client/panel/AuthorAssetsView.d.ts +4 -0
- package/lib/types/client/panel/AuthorHome.d.ts +34 -0
- package/lib/types/client/panel/BookAnalysisView.d.ts +4 -0
- package/lib/types/client/panel/CmdPalette.d.ts +12 -0
- package/lib/types/client/panel/CreateBookView.d.ts +5 -1
- package/lib/types/client/panel/DirectorView.d.ts +7 -0
- package/lib/types/client/panel/GlobalAssetLibraryView.d.ts +4 -0
- package/lib/types/client/panel/IdeaInspirationView.d.ts +6 -0
- package/lib/types/client/panel/ImportModal.d.ts +7 -0
- package/lib/types/client/panel/KnowledgeBaseView.d.ts +4 -0
- package/lib/types/client/panel/LiveFeedLog.d.ts +8 -0
- package/lib/types/client/panel/MarketRadarView.d.ts +5 -0
- package/lib/types/client/panel/ModelManager.d.ts +14 -0
- package/lib/types/client/panel/NovelPanel.d.ts +6 -1
- package/lib/types/client/panel/PageHeader.d.ts +18 -0
- package/lib/types/client/panel/ProgressConsole.d.ts +17 -0
- package/lib/types/client/panel/ProgressHomeView.d.ts +5 -0
- package/lib/types/client/panel/ReasoningSection.d.ts +10 -0
- package/lib/types/client/panel/RunPanel.d.ts +5 -0
- package/lib/types/client/panel/SettingsView.d.ts +19 -0
- package/lib/types/client/panel/ShelfView.d.ts +12 -1
- package/lib/types/client/panel/SubPage.d.ts +27 -0
- package/lib/types/client/panel/toast.d.ts +2 -0
- package/lib/types/client/panel/views.d.ts +25 -7
- package/lib/types/client/run-status.d.ts +19 -0
- package/lib/types/client/text.d.ts +9 -0
- package/lib/types/docx-core.d.ts +16 -0
- package/lib/types/docx.d.ts +2 -3
- package/lib/types/engine.d.ts +152 -40
- package/lib/types/global-assets.d.ts +6 -0
- package/lib/types/index.d.ts +20 -2
- package/lib/types/llm-live.d.ts +44 -0
- package/lib/types/market-radar-scan.d.ts +19 -0
- package/lib/types/market-radar-sources.d.ts +31 -0
- package/lib/types/migrate-output.d.ts +25 -0
- package/lib/types/novel-context.d.ts +80 -0
- package/lib/types/prompt-utils.d.ts +35 -0
- package/lib/types/prompting.d.ts +10 -0
- package/lib/types/protocol.d.ts +1106 -123
- package/lib/types/review-policy.d.ts +97 -0
- package/lib/types/routes.d.ts +4 -0
- package/lib/types/run.d.ts +34 -0
- package/lib/types/stage-contract.d.ts +60 -0
- package/package.json +52 -27
- package/scripts/patch-schemastery.mjs +47 -0
- package/src/action-guard.ts +127 -0
- package/src/ai-scan.ts +179 -0
- package/src/assets.ts +1774 -520
- package/src/assistant.ts +1082 -770
- package/src/author-assets.ts +149 -0
- package/src/bookshelf.ts +187 -151
- package/src/client/api.ts +684 -387
- package/src/client/docx.ts +37 -69
- package/src/client/index.ts +3 -1
- package/src/client/llmLive.ts +101 -0
- package/src/client/locales.ts +418 -367
- package/src/client/mount.tsx +1 -1
- package/src/client/panel/AdaptModeView.tsx +535 -0
- package/src/client/panel/AiTag.tsx +7 -0
- package/src/client/panel/AssetsTab.tsx +448 -439
- package/src/client/panel/AssistantTab.tsx +355 -301
- package/src/client/panel/AuthorAssetsView.tsx +206 -0
- package/src/client/panel/AuthorHome.tsx +309 -0
- package/src/client/panel/BookAnalysisView.tsx +63 -0
- package/src/client/panel/CmdPalette.tsx +102 -0
- package/src/client/panel/CreateBookView.tsx +366 -348
- package/src/client/panel/DirectorView.tsx +200 -0
- package/src/client/panel/GlobalAssetLibraryView.tsx +168 -0
- package/src/client/panel/IdeaInspirationView.tsx +76 -0
- package/src/client/panel/ImportModal.tsx +303 -0
- package/src/client/panel/KnowledgeBaseView.tsx +125 -0
- package/src/client/panel/LiveFeedLog.tsx +67 -0
- package/src/client/panel/MarketRadarView.tsx +377 -0
- package/src/client/panel/ModelManager.tsx +205 -0
- package/src/client/panel/NovelPanel.tsx +5403 -4294
- package/src/client/panel/PageHeader.tsx +37 -0
- package/src/client/panel/ProgressConsole.tsx +63 -0
- package/src/client/panel/ReaderView.tsx +306 -306
- package/src/client/panel/ReasoningSection.tsx +31 -0
- package/src/client/panel/RunPanel.tsx +223 -0
- package/src/client/panel/SettingsView.tsx +305 -0
- package/src/client/panel/ShelfView.tsx +266 -235
- package/src/client/panel/SubPage.tsx +42 -0
- package/src/client/panel/WorldTab.tsx +202 -202
- package/src/client/panel/panel.module.css +5764 -2817
- package/src/client/panel/toast.ts +51 -0
- package/src/client/panel/views.tsx +401 -314
- package/src/client/run-status.ts +143 -0
- package/src/client/sidebar-entry.ts +1 -1
- package/src/client/text.ts +28 -0
- package/src/docx-core.ts +53 -0
- package/src/docx.ts +44 -83
- package/src/engine.ts +4231 -2746
- package/src/global-assets.ts +59 -0
- package/src/index.ts +311 -246
- package/src/llm-live.ts +68 -0
- package/src/market-radar-scan.ts +41 -0
- package/src/market-radar-sources.ts +173 -0
- package/src/migrate-output.ts +76 -0
- package/src/novel-context.ts +286 -0
- package/src/prompting.ts +26 -0
- package/src/protocol.ts +2072 -1104
- package/src/review-policy.ts +161 -0
- package/src/routes.ts +3405 -2050
- package/src/run.ts +342 -0
- package/src/stage-contract.ts +189 -0
- package/lib/client.js.map +0 -1
- package/lib/index.js.map +0 -1
- 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
|
+
}
|