@waterwx/dsh-novel-forge 2.1.2 → 2.1.4

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.
@@ -87,4 +87,18 @@ export interface AiScanRuleHit {
87
87
  count: number;
88
88
  }>;
89
89
  }
90
+ /**
91
+ * 对话字符数(含引号本身)。
92
+ *
93
+ * 导出供写法指纹复用:两处必须同一口径,否则「对话占比」在两个模块里会给出不同的数。
94
+ * @param text - 任意正文。
95
+ * @returns 对话部分的总字符数。
96
+ */
97
+ export declare function dialogueCharsOf(text: string): number;
98
+ /**
99
+ * 按真实书稿的换行形态切自然段:单换行分隔,剔除 # 标题行。
100
+ * @param text - 章节正文(可含标题行)。
101
+ * @returns 自然段数组(已 trim、去空)。
102
+ */
103
+ export declare function paragraphsOf(text: string): string[];
90
104
  export declare function scanAiFlavor(text: string, rules?: readonly AiScanRuleInput[]): AiScanResult;
@@ -280,6 +280,8 @@ export declare class NovelApi {
280
280
  assistant(message: string, onFrame: (frame: import('../protocol.ts').AssistantFrame) => void): Promise<void>;
281
281
  /** 张力曲线:数据 + 规则核对。 */
282
282
  tension(): Promise<import('../protocol.ts').TensionResponse>;
283
+ /** 风格漂移曲线:逐章指标 + 参照基线(与审稿同源)。 */
284
+ styleHistory(): Promise<import('../protocol.ts').StyleHistoryResponse>;
283
285
  /** 设置张力曲线预设。 */
284
286
  tensionPreset(preset: import('../protocol.ts').TensionCurvePreset): Promise<void>;
285
287
  /** 设置/清除某一章的目标张力。 */
@@ -0,0 +1,18 @@
1
+ /**
2
+ * 风格漂移曲线:把「每一章离本书自己的风格基线有多远」画出来。
3
+ *
4
+ * 图上三条信息:基线(虚线,本书已过审章节的中位数)、逐章实际(折线)、显著偏离的章(红点)。
5
+ * 指标可切换:句均长 / 短句占比 / 对话占比 / 段落均长。
6
+ *
7
+ * 两点刻意的设计:
8
+ * 1. **基线与审稿同源** —— 用的是同一份 styleBaseline,所以图上看到的就是审稿判偏离用的那把尺子。
9
+ * 若各画各的,作者会看到两个不一致的结论。
10
+ * 2. **数值即时算、不落盘** —— 数据源就是章节文件本身,存一份快照只会带来陈旧问题。
11
+ *
12
+ * 只画四项长度/比率指标:句长起伏与意象密度是诊断用的,画成曲线反而难读。
13
+ */
14
+ import type { NovelApi } from '../api.ts';
15
+ export interface StyleViewProps {
16
+ api: NovelApi;
17
+ }
18
+ export default function StyleView({ api }: StyleViewProps): JSX.Element;
@@ -12,6 +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 StyleFingerprint } from './style-fingerprint.ts';
15
16
  import { fullTextBudget } from './model-capability.ts';
16
17
  import { applyModelCapability } from './model-request.ts';
17
18
  import type { AdaptationDimension, AdaptAnalyzeResponse, AdaptationMapping, AdaptationRules, AdaptProposeResponse, AuditIssue, AuthorReview, ExportScope, TimelineEvent, TimelineIssue, BreakdownResponse, ChapterPlan, Foreshadow, NovelConfig, OutlineCandidate, Plotline, PlotlineHealthReport, PlotlinePlan, ProjectState, ReviewReport, RoleRecord, RoleStatusCard, StoryBible, AddModelRequest, AddModelResponse, LlmModelsResponse, LlmVendorsResponse, LlmProvidersResponse, RemoveProviderRequest, RemoveProviderResponse, LlmTestResponse, Volume, WorldState, AdaptMaterializeRequest, AdaptMaterializeResponse, AdaptMaterializeSaveRequest, AdaptMaterializeSaveResponse, MarketRadarRequest, MarketRadarResult, MarketRadarSignal, MarketRadarBriefRequest, ProductionFoundation, MarketCreativeBrief, BookAnalysisRequest, BookAnalysisResult, IdeaInspirationRequest, IdeaInspirationResult, DirectorRequest, DirectorAdvice } from './protocol.ts';
@@ -68,6 +69,31 @@ export declare function planVolumes(ctx: Context, config: NovelConfig, outline:
68
69
  * Plan chapters from an outline (optionally for one volume).
69
70
  */
70
71
  export declare function planChapters(ctx: Context, config: NovelConfig, project: ProjectState, chapterCount: number, volumeNo?: number, outputDir?: string): Promise<ChapterPlan[]>;
72
+ /**
73
+ * 风格符合度事实块:拿本章指标与**本书已过审章节的中位数**(或绑定写法样本)对照。
74
+ *
75
+ * 取第一个带 sourceText 的写法资产做参照(提取时 routes.ts 会存下样本前 3000 字)。
76
+ * 样本过短(指标无统计意义)时返回空串——宁可不给,也不给噪声。
77
+ * @param project - 当前书项目状态。
78
+ * @param bodyText - 本章正文(已剥标题)。
79
+ * @returns 可注入审稿 prompt 的文本;没有可用参照时为空串。
80
+ */
81
+ /** 风格基线:参照指纹 + 人话说明。 */
82
+ export interface StyleBaseline {
83
+ fingerprint: StyleFingerprint;
84
+ /** 供提示词与 UI 显示的来源说明。 */
85
+ label: string;
86
+ }
87
+ /**
88
+ * 解析本书的风格基线 —— **审稿与漂移曲线共用这一份**,保证两条路同源。
89
+ *
90
+ * 优先用本书**已过审章节的中位数**:单一样本的噪声会整个混进比对(样本碰巧长什么样,
91
+ * 后面所有章都在跟它比),多章中位数对离群值不敏感。不足 3 章时退回单一样本。
92
+ * @param outputDir - 书目录;缺省时只能用写法样本。
93
+ * @param project - 当前书项目状态。
94
+ * @returns 基线;既无已过审章节也无带样本文本的写法资产时返回 undefined。
95
+ */
96
+ export declare function styleBaseline(outputDir: string | undefined, project: ProjectState): StyleBaseline | undefined;
71
97
  /** Run the AI review on one chapter. */
72
98
  export declare function reviewChapter(ctx: Context, config: NovelConfig, project: ProjectState, outputDir: string, chapterNo: number): Promise<ReviewReport>;
73
99
  /**
@@ -33,6 +33,7 @@ export declare const NOVEL_API: {
33
33
  readonly revisionPlan: "/api/dsh-novel-forge/revision/plan";
34
34
  /** 张力曲线(GET 数据 / POST 设置)。 */
35
35
  readonly tension: "/api/dsh-novel-forge/tension";
36
+ readonly styleHistory: "/api/dsh-novel-forge/style-history";
36
37
  /** 提示词槽位(GET 列表 / POST 写入)。 */
37
38
  readonly promptSlots: "/api/dsh-novel-forge/prompt-slots";
38
39
  /** 故事时间线(GET 列表 / POST 操作)。 */
@@ -2026,6 +2027,32 @@ export interface PlotBeatTemplate {
2026
2027
  /** 适用题材。 */
2027
2028
  applicableGenres: string[];
2028
2029
  }
2030
+ /** 风格漂移曲线上的一个点(一章)。 */
2031
+ export interface StyleHistoryPoint {
2032
+ no: number;
2033
+ title: string;
2034
+ /** 该章指纹是否可用(过短的章没有统计意义) */
2035
+ measured: boolean;
2036
+ avgSentenceLength: number;
2037
+ shortSentenceRatio: number;
2038
+ dialogueRatio: number;
2039
+ avgParagraphLength: number;
2040
+ /** 超出容忍度的指标(指标 key + 方向箭头,如 avgSentenceLength↑;中文名由前端映射) */
2041
+ significant: string[];
2042
+ }
2043
+ /** 风格漂移曲线:逐章指标 + 参照基线。 */
2044
+ export interface StyleHistoryResponse {
2045
+ /** 基线说明,如「本书已过审 12 章的中位数」 */
2046
+ baselineLabel: string;
2047
+ /** 基线的四项关键指标(供图上画参考线);无基线时为 null */
2048
+ baseline: {
2049
+ avgSentenceLength: number;
2050
+ shortSentenceRatio: number;
2051
+ dialogueRatio: number;
2052
+ avgParagraphLength: number;
2053
+ } | null;
2054
+ points: StyleHistoryPoint[];
2055
+ }
2029
2056
  /** 写法引擎:从样本文本提取的叙事风格资产。 */
2030
2057
  export interface StyleAsset {
2031
2058
  /** 资产名(如「林越式痞坏」「冷峻猎手风」)。 */
@@ -0,0 +1,78 @@
1
+ /** 风格指标 key(同时是比对结果的稳定标识)。 */
2
+ export type StyleMetricKey = 'avgSentenceLength' | 'sentenceLengthCv' | 'shortSentenceRatio' | 'dialogueRatio' | 'avgParagraphLength' | 'imageryPer1000';
3
+ /** 一份文本的风格指标。 */
4
+ export interface StyleFingerprint {
5
+ /** 参与统计的字符数 */
6
+ chars: number;
7
+ /** 句子数 */
8
+ sentences: number;
9
+ /** 句均长(字符) */
10
+ avgSentenceLength: number;
11
+ /** 句长变异系数(越小越平;节奏起伏的代理) */
12
+ sentenceLengthCv: number;
13
+ /** 短句占比(句长 <= 12 字) */
14
+ shortSentenceRatio: number;
15
+ /** 对话占比(0-1) */
16
+ dialogueRatio: number;
17
+ /** 自然段均长(字符) */
18
+ avgParagraphLength: number;
19
+ /** 意象密度:每千字的比喻/意象标记词次数 */
20
+ imageryPer1000: number;
21
+ }
22
+ /** 一个指标的比对结果。 */
23
+ export interface StyleDeviation {
24
+ metric: StyleMetricKey;
25
+ /** 中文名,直接用于提示词 */
26
+ label: string;
27
+ reference: number;
28
+ actual: number;
29
+ /** 相对偏差(actual - reference)/ reference */
30
+ relative: number;
31
+ /** 偏高还是偏低 */
32
+ direction: 'higher' | 'lower';
33
+ /** 是否超出该指标的容忍度 */
34
+ significant: boolean;
35
+ }
36
+ /**
37
+ * 从文本抽取风格指纹。
38
+ * @param text - 任意正文(样本文本或章节正文;调用方需先剥掉标题行)。
39
+ * @returns 风格指标;文本短于 {@link MIN_CHARS} 时返回零值。
40
+ */
41
+ export declare function extractStyleFingerprint(text: string): StyleFingerprint;
42
+ /**
43
+ * 比对两份指纹。
44
+ *
45
+ * 参照值为 0 的指标直接跳过 —— 例如样本里本来就没有对话,此时「相对偏差」没有意义,
46
+ * 硬算会得到无穷大,反而制造假警报。
47
+ * @param reference - 参照指纹(写法样本)。
48
+ * @param actual - 本章指纹。
49
+ * @returns 每个可比指标一条结果(含是否显著)。
50
+ */
51
+ export declare function compareStyleFingerprint(reference: StyleFingerprint, actual: StyleFingerprint): StyleDeviation[];
52
+ /**
53
+ * 合并多份指纹:逐指标取**中位数**。
54
+ *
55
+ * 为什么是中位数而不是平均值:单章指标会被极端章拉偏(一个通篇无对话的章能把平均对话
56
+ * 占比拽下去),中位数对离群值不敏感。这也正是「用本书已过审章节当中位数基线」比
57
+ * 「用单一样本」更稳的原因 —— 样本自身长什么样会整个混进参照里。
58
+ * @param items - 多章指纹(过短的会被剔除,见 sentences === 0)。
59
+ * @returns 合并后的参照指纹;没有可用输入时返回零值指纹。
60
+ */
61
+ export declare function mergeFingerprints(items: readonly StyleFingerprint[]): StyleFingerprint;
62
+ /**
63
+ * 渲染**生成端**的风格目标(区间形式)。
64
+ * @param reference - 写法样本的指纹。
65
+ * @returns 可注入写作 prompt 的文本;样本过短时返回空串。
66
+ */
67
+ export declare function renderFingerprintTargets(reference: StyleFingerprint): string;
68
+ /**
69
+ * 渲染**审稿端**的风格比对(事实锚点)。
70
+ *
71
+ * 没有显著偏差时会**明确写出「没有可量化的风格偏离」**——否则模型会为了凑维度而
72
+ * 凭印象报一条「文风不符」,那正是这类审查最容易产出的假问题。
73
+ * @param reference - 写法样本的指纹。
74
+ * @param actual - 本章指纹。
75
+ * @param deviations - {@link compareStyleFingerprint} 的结果。
76
+ * @returns 可注入审稿 prompt 的文本;样本过短时返回空串。
77
+ */
78
+ export declare function renderFingerprintComparison(reference: StyleFingerprint, actual: StyleFingerprint, deviations: StyleDeviation[], sourceLabel?: string): string;
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 volumes & chapters with LLM, generate chapter by chapter (3000-4000 chars each), auto AI review & book audit, and export the full book as Markdown/TXT into your chosen output folder.",
4
- "version": "2.1.2",
4
+ "version": "2.1.4",
5
5
  "type": "module",
6
6
  "author": "waterwx",
7
7
  "repository": {