@waterwx/dsh-novel-forge 1.1.8 → 1.2.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.
- package/README.md +2 -2
- package/lib/client.js +2262 -1242
- package/lib/client.js.map +1 -1
- package/lib/index.js +1017 -551
- package/lib/index.js.map +1 -1
- package/lib/types/bookshelf.d.ts +1 -1
- package/lib/types/client/api.d.ts +6 -0
- package/lib/types/client/locales.d.ts +8 -0
- package/lib/types/client/panel/NovelPanel.d.ts +1 -1
- package/lib/types/client/panel/RunPanel.d.ts +5 -0
- package/lib/types/engine.d.ts +7 -1
- package/lib/types/index.d.ts +3 -1
- package/lib/types/protocol.d.ts +77 -0
- package/lib/types/run.d.ts +32 -0
- package/package.json +2 -1
- package/src/bookshelf.ts +2 -2
- package/src/client/api.ts +23 -0
- package/src/client/locales.ts +16 -0
- package/src/client/panel/AssetsTab.tsx +19 -0
- package/src/client/panel/CreateBookView.tsx +3 -3
- package/src/client/panel/NovelPanel.tsx +528 -225
- package/src/client/panel/RunPanel.tsx +204 -0
- package/src/client/panel/ShelfView.tsx +13 -12
- package/src/client/panel/panel.module.css +192 -68
- package/src/engine.ts +42 -1
- package/src/index.ts +11 -5
- package/src/protocol.ts +78 -0
- package/src/routes.ts +105 -4
- package/src/run.ts +330 -0
package/lib/types/bookshelf.d.ts
CHANGED
|
@@ -32,6 +32,6 @@ export declare function activateBook(id: string): BookEntry | undefined;
|
|
|
32
32
|
export declare function removeBook(id: string): boolean;
|
|
33
33
|
/** 当前书输出目录(无书架则 undefined,回退 settings)。 */
|
|
34
34
|
export declare function activeBookOutputDir(): string | undefined;
|
|
35
|
-
/**
|
|
35
|
+
/** 默认输出目录推断:小说目录/书名。 */
|
|
36
36
|
export declare function defaultOutputDirFor(bookName: string): string;
|
|
37
37
|
export {};
|
|
@@ -116,6 +116,12 @@ export declare class NovelApi {
|
|
|
116
116
|
bookActivate(id: string): Promise<import('../protocol.ts').BookshelfSnapshot>;
|
|
117
117
|
/** 移除书架条目。 */
|
|
118
118
|
bookRemove(id: string): Promise<import('../protocol.ts').BookshelfSnapshot>;
|
|
119
|
+
/** 生产单:启动批量生产(区间或新增 N 章;计划不足自动补)。 */
|
|
120
|
+
runStart(req: import('../protocol.ts').RunStartRequest): Promise<import('../protocol.ts').RunState>;
|
|
121
|
+
/** 生产单控制:pause / resume / stop。 */
|
|
122
|
+
runControl(action: 'pause' | 'resume' | 'stop'): Promise<import('../protocol.ts').RunState | null>;
|
|
123
|
+
/** 生产单状态(无生产单返回 null)。 */
|
|
124
|
+
runStatus(): Promise<import('../protocol.ts').RunState | null>;
|
|
119
125
|
/** Get project writing assets + built-in libraries. */
|
|
120
126
|
assets(): Promise<AssetsResponse>;
|
|
121
127
|
/** Patch project writing assets. */
|
|
@@ -141,6 +141,14 @@ export declare const zh: {
|
|
|
141
141
|
readonly 'settings.outputDir': "输出目录";
|
|
142
142
|
readonly 'settings.provider': "模型提供商";
|
|
143
143
|
readonly 'settings.model': "模型";
|
|
144
|
+
readonly 'settings.modelCustom': "自定义…";
|
|
145
|
+
readonly 'settings.modelCustomPlaceholder': "输入任意模型 id";
|
|
146
|
+
readonly 'settings.reasoningEffort': "思考强度 (reasoningEffort)";
|
|
147
|
+
readonly 'settings.reasoning.off': "off · 关闭思考(推荐日常写文)";
|
|
148
|
+
readonly 'settings.reasoning.low': "low · 低思考";
|
|
149
|
+
readonly 'settings.reasoning.high': "high · 高思考";
|
|
150
|
+
readonly 'settings.reasoning.max': "max · 最高思考(质检/推演)";
|
|
151
|
+
readonly 'settings.reasoningHint': "off 最快最省;high/max 更慢更贵但推理更强";
|
|
144
152
|
readonly 'settings.theme': "主题";
|
|
145
153
|
readonly 'settings.themeLiquid': "iOS 液态玻璃(绿)";
|
|
146
154
|
readonly 'settings.themeClassic': "经典毛玻璃(蓝)";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { NovelApi } from '../api.ts';
|
|
2
2
|
import type { PanelController } from './controller.ts';
|
|
3
3
|
/** The panel's tab identifiers. */
|
|
4
|
-
export type NovelTab = 'workflow' | 'overview' | 'blurb' | 'plan' | 'bible' | 'world' | 'foreshadow' | 'assistant' | 'settings' | 'characters' | 'roles' | 'facts' | 'plotlines' | 'reviews' | 'progress' | 'breakdown' | 'storyboard' | 'assetsGenre' | 'assetsProgression' | 'assetsTemplates' | 'assetsRules' | 'assetsStyle';
|
|
4
|
+
export type NovelTab = 'workflow' | 'overview' | 'blurb' | 'plan' | 'bible' | 'world' | 'foreshadow' | 'assistant' | 'settings' | 'characters' | 'roles' | 'facts' | 'plotlines' | 'reviews' | 'progress' | 'breakdown' | 'storyboard' | 'assetsGenre' | 'assetsProgression' | 'assetsTemplates' | 'assetsRules' | 'assetsStyle' | 'run' | 'book' | 'assets';
|
|
5
5
|
/** Panel shell props. */
|
|
6
6
|
export interface NovelPanelProps {
|
|
7
7
|
/** The panel state owner (open/close/toggle). */
|
package/lib/types/engine.d.ts
CHANGED
|
@@ -5,6 +5,12 @@
|
|
|
5
5
|
* tracking, project persistence, and whole-book export. Pure Node (no
|
|
6
6
|
* web-server dependencies), so routes stay thin and logic is testable.
|
|
7
7
|
*/
|
|
8
|
+
/**
|
|
9
|
+
* 内容合规红线(平台硬性要求):所有书籍、所有章节无条件生效,
|
|
10
|
+
* 优先级高于单书大纲/圣经中的任何设定与作者自定义红线。
|
|
11
|
+
* 注入点:章节生成系统提示 + 审稿系统提示(命中即 high)。
|
|
12
|
+
*/
|
|
13
|
+
export declare const COMPLIANCE_REDLINES: ReadonlyArray<string>;
|
|
8
14
|
import type { Context } from '@deepseek-ai/cordis';
|
|
9
15
|
import type { AuditIssue, AuthorReview, BreakdownResponse, ChapterPlan, Foreshadow, NovelConfig, OutlineCandidate, Plotline, PlotlineHealthReport, PlotlinePlan, ProjectState, ReviewReport, RoleRecord, RoleStatusCard, StoryBible, StoryboardPlanResponse, StoryboardResponse, Volume, WorldState } from './protocol.ts';
|
|
10
16
|
/** Project state file name inside the output dir. */
|
|
@@ -168,7 +174,7 @@ export declare function markForeshadowPlanted(project: ProjectState, outputDir:
|
|
|
168
174
|
*/
|
|
169
175
|
export declare function extractFacts(ctx: Context, config: NovelConfig, project: ProjectState, outputDir: string, chapterNo: number): Promise<number>;
|
|
170
176
|
/** 全书一致性质检:LLM 分批扫描已生成章节 + 设定 + 事实库,聚合矛盾清单。 */
|
|
171
|
-
export declare function auditBook(ctx: Context, config: NovelConfig, project: ProjectState, outputDir: string): Promise<AuditIssue[]>;
|
|
177
|
+
export declare function auditBook(ctx: Context, config: NovelConfig, project: ProjectState, outputDir: string, onProgress?: (completedBatches: number, totalBatches: number) => void): Promise<AuditIssue[]>;
|
|
172
178
|
/** 小说简介:AI 生成或按已写开头补全(面向读者的作品门面)。 */
|
|
173
179
|
export declare function generateBlurb(ctx: Context, config: NovelConfig, project: ProjectState, partial?: string): Promise<string>;
|
|
174
180
|
/**
|
package/lib/types/index.d.ts
CHANGED
|
@@ -33,6 +33,8 @@ export interface Config {
|
|
|
33
33
|
provider?: string;
|
|
34
34
|
/** LLM model id. */
|
|
35
35
|
model?: string;
|
|
36
|
+
/** LLM reasoning effort (off/low/high/max). */
|
|
37
|
+
reasoningEffort?: 'off' | 'low' | 'high' | 'max';
|
|
36
38
|
/** Target characters per chapter. */
|
|
37
39
|
chapterChars?: number;
|
|
38
40
|
/** Max output tokens per chapter call. */
|
|
@@ -48,7 +50,7 @@ export interface Config {
|
|
|
48
50
|
}
|
|
49
51
|
export declare const Config: z<Config>;
|
|
50
52
|
/** Model-facing announcement: plugin presence, capabilities, and limits. */
|
|
51
|
-
export declare const NOVEL_GUIDANCE = "\u672C\u673A\u5DF2\u5B89\u88C5 dsh-novel-forge \u63D2\u4EF6\uFF08AI \u7F16\u8BD1\u5C0F\u8BF4\u5DE5\u4F5C\u53F0\uFF09\uFF1A\u4FA7\u8FB9\u680F\u300C\u5C0F\u8BF4\u5DE5\u574A\u300D\u5165\u53E3\u3002\u80FD\u529B\uFF1A\u8BFB\u53D6 docx \u5927\u7EB2\uFF08\u9ED8\u8BA4
|
|
53
|
+
export declare const NOVEL_GUIDANCE = "\u672C\u673A\u5DF2\u5B89\u88C5 dsh-novel-forge \u63D2\u4EF6\uFF08AI \u7F16\u8BD1\u5C0F\u8BF4\u5DE5\u4F5C\u53F0\uFF09\uFF1A\u4FA7\u8FB9\u680F\u300C\u5C0F\u8BF4\u5DE5\u574A\u300D\u5165\u53E3\u3002\u80FD\u529B\uFF1A\u8BFB\u53D6 docx \u5927\u7EB2\uFF08\u9ED8\u8BA4 D:\\ryan work\\harness\\novels\\\u5F52\u589F\u7389\u4E3B \u5927\u7EB2\uFF09\u6216\u7C98\u8D34\u5927\u7EB2\u6587\u672C\uFF1B\u7528 LLM \u63D0\u70BC\u9053\u85CF\uFF08\u4EBA\u8BBE/\u4E16\u754C\u89C2/\u91D1\u624B\u6307\u89C4\u5219/\u5199\u4F5C\u7EA2\u7EBF\uFF0C\u5373\u8BBE\u5B9A\u5723\u7ECF\uFF09\uFF1B\u751F\u6210\u5377\u8BA1\u5212\u4E0E\u7AE0\u8282\u8BA1\u5212\uFF1B\u9010\u7AE0\u8C03\u7528 LLM \u751F\u6210 3000-4000 \u5B57\u6B63\u6587\u5E76\u4FDD\u5B58\u4E3A Markdown\uFF08\u9ED8\u8BA4\u8F93\u51FA\u5230 D:\\ryan work\\harness\\novels\\\u5F52\u589F\u7389\u4E3B\uFF09\uFF1B\u6BCF\u7AE0\u81EA\u52A8\u751F\u6210\u6458\u8981\uFF08\u53D9\u4E8B\u8BB0\u5FC6\uFF09\u3001\u81EA\u52A8 AI \u5BA1\u7A3F\uFF08\u4EBA\u8BBE/\u8BBE\u5B9A/\u7EA2\u7EBF/\u6587\u7B14/\u723D\u70B9/\u903B\u8F91\uFF09\uFF0C\u652F\u6301\u6309\u5BA1\u7A3F\u610F\u89C1\u91CD\u5199\u3001\u53BB AI \u5473\u6DA6\u8272\u3001\u6697\u7EBF\uFF08\u4F0F\u7B14\uFF09\u7BA1\u7406\u3001\u6279\u91CF\u8FDE\u5199\u4E0E\u5168\u672C\u5BFC\u51FA\uFF08txt/md\uFF09\u3002\u9650\u5236\uFF1A\u751F\u6210\u6D88\u8017 LLM API \u989D\u5EA6\uFF1B\u8F93\u51FA\u76EE\u5F55\u4E0E\u6A21\u578B\u53EF\u5728\u63D2\u4EF6\u8BBE\u7F6E\u4E2D\u4FEE\u6539\uFF1B\u7AE0\u8282\u6B63\u6587\u8D28\u91CF\u53D6\u51B3\u4E8E\u5927\u7EB2\u5B8C\u6574\u5EA6\u3002\u7528\u6237\u63D0\u5230\u300C\u5C0F\u8BF4 / \u5927\u7EB2 / \u5199\u5C0F\u8BF4 / \u7AE0\u8282 / \u5BA1\u7A3F / \u6DA6\u8272 / \u5F52\u589F\u7389\u4E3B\u300D\u65F6\u5373\u6307\u672C\u63D2\u4EF6\uFF0C\u8BF7\u636E\u6B64\u534F\u4F5C\u3002";
|
|
52
54
|
/** Resolve a config-like value into the full runtime config. */
|
|
53
55
|
export declare function resolveConfig(value: Partial<Config> | undefined): NovelConfig;
|
|
54
56
|
/**
|
package/lib/types/protocol.d.ts
CHANGED
|
@@ -73,6 +73,12 @@ export declare const NOVEL_API: {
|
|
|
73
73
|
readonly storyboard: "/api/dsh-novel-forge/storyboard";
|
|
74
74
|
/** 漫剧分集计划:读一卷 → 按故事弧线分集(高潮拆集/过渡并章)。 */
|
|
75
75
|
readonly storyboardPlan: "/api/dsh-novel-forge/storyboard/plan";
|
|
76
|
+
/** 生产单:启动批量生产(计划补足 + 逐章生成 + 被拒分级处理)。 */
|
|
77
|
+
readonly runStart: "/api/dsh-novel-forge/run/start";
|
|
78
|
+
/** 生产单控制:pause / resume / stop。 */
|
|
79
|
+
readonly runControl: "/api/dsh-novel-forge/run/control";
|
|
80
|
+
/** 生产单状态(含进度统计与日志)。 */
|
|
81
|
+
readonly runStatus: "/api/dsh-novel-forge/run/status";
|
|
76
82
|
readonly config: "/api/dsh-novel-forge/config";
|
|
77
83
|
readonly openFolder: "/api/dsh-novel-forge/open-folder";
|
|
78
84
|
};
|
|
@@ -279,6 +285,26 @@ export interface AuditResponse {
|
|
|
279
285
|
/** 质检时间。 */
|
|
280
286
|
auditedAt: string;
|
|
281
287
|
}
|
|
288
|
+
/** 全书质检的实时状态(通过 /status 暴露给面板/外部读取)。 */
|
|
289
|
+
export interface AuditStatus {
|
|
290
|
+
status: 'idle' | 'running' | 'done' | 'error';
|
|
291
|
+
/** 开始时间(ISO)。 */
|
|
292
|
+
startedAt?: string;
|
|
293
|
+
/** 结束时间(ISO)。 */
|
|
294
|
+
finishedAt?: string;
|
|
295
|
+
/** 总批次数(0 = 尚未开始/无章节)。 */
|
|
296
|
+
totalBatches: number;
|
|
297
|
+
/** 已完成批次数。 */
|
|
298
|
+
completedBatches: number;
|
|
299
|
+
/** 参与质检的章节数。 */
|
|
300
|
+
auditedChapters?: number;
|
|
301
|
+
/** 发现的问题数(done 后有效)。 */
|
|
302
|
+
issuesCount?: number;
|
|
303
|
+
/** 最近一次质检的问题清单(done 后有效;error 时为空)。 */
|
|
304
|
+
issues?: AuditIssue[];
|
|
305
|
+
/** 失败信息(error 时有效)。 */
|
|
306
|
+
error?: string;
|
|
307
|
+
}
|
|
282
308
|
/** 一条剧情线(主线/支线/人物线/悬念线)。 */
|
|
283
309
|
export interface Plotline {
|
|
284
310
|
/** 稳定 id。 */
|
|
@@ -706,6 +732,8 @@ export interface NovelConfig {
|
|
|
706
732
|
provider: string;
|
|
707
733
|
/** LLM model id (e.g. deepseek-v4-flash). */
|
|
708
734
|
model: string;
|
|
735
|
+
/** LLM reasoning effort: off = no thinking; low/high/max = thinking intensity. */
|
|
736
|
+
reasoningEffort: 'off' | 'low' | 'high' | 'max';
|
|
709
737
|
/** Target characters per chapter. */
|
|
710
738
|
chapterChars: number;
|
|
711
739
|
/** Max output tokens per chapter call. */
|
|
@@ -726,6 +754,54 @@ export interface StatusResponse {
|
|
|
726
754
|
project?: ProjectState;
|
|
727
755
|
/** Chapter files already on disk (basenames, sorted). */
|
|
728
756
|
generatedFiles: string[];
|
|
757
|
+
/** 全书质检实时状态(用于面板/外部读取进度)。 */
|
|
758
|
+
audit?: AuditStatus;
|
|
759
|
+
}
|
|
760
|
+
/** 生产单状态(生产单 = 区间批量生产执行器:计划补足 + 逐章生成 + 被拒分级处理)。 */
|
|
761
|
+
export interface RunState {
|
|
762
|
+
runId: string;
|
|
763
|
+
/** 区间起点(含)。 */
|
|
764
|
+
startNo: number;
|
|
765
|
+
/** 区间终点(含)。 */
|
|
766
|
+
endNo: number;
|
|
767
|
+
status: 'running' | 'paused' | 'done' | 'stopped' | 'error';
|
|
768
|
+
/** 当前处理到的章号(已 approved 的章会快进)。 */
|
|
769
|
+
currentNo: number;
|
|
770
|
+
stats: {
|
|
771
|
+
/** 新生成(含审稿)的章数。 */
|
|
772
|
+
generated: number;
|
|
773
|
+
/** 被拒后按意见修订并通过的章数。 */
|
|
774
|
+
revised: number;
|
|
775
|
+
/** 被拒后豁免通过(无 high)的章数。 */
|
|
776
|
+
exempted: number;
|
|
777
|
+
/** error 后重新生成的章数。 */
|
|
778
|
+
regenerated: number;
|
|
779
|
+
/** 生成失败的章数。 */
|
|
780
|
+
error: number;
|
|
781
|
+
};
|
|
782
|
+
/** 两轮修订仍不过、保留草稿待人工的章号。 */
|
|
783
|
+
pendingManual: number[];
|
|
784
|
+
/** 运行日志(保留最近 300 条)。 */
|
|
785
|
+
log: Array<{
|
|
786
|
+
at: string;
|
|
787
|
+
text: string;
|
|
788
|
+
}>;
|
|
789
|
+
startedAt: string;
|
|
790
|
+
updatedAt: string;
|
|
791
|
+
error?: string;
|
|
792
|
+
}
|
|
793
|
+
/** POST /run/start request. */
|
|
794
|
+
export interface RunStartRequest {
|
|
795
|
+
/** 起始章号(默认 1)。 */
|
|
796
|
+
startNo?: number;
|
|
797
|
+
/** 结束章号(含;超出计划时自动补计划)。 */
|
|
798
|
+
endNo?: number;
|
|
799
|
+
/** 或指定新增章数(从当前最后一章 +1 起)。 */
|
|
800
|
+
count?: number;
|
|
801
|
+
}
|
|
802
|
+
/** POST /run/control request. */
|
|
803
|
+
export interface RunControlRequest {
|
|
804
|
+
action: 'pause' | 'resume' | 'stop';
|
|
729
805
|
}
|
|
730
806
|
/** POST /load-outline request: either a docx path or raw text. */
|
|
731
807
|
export interface LoadOutlineRequest {
|
|
@@ -887,6 +963,7 @@ export interface ConfigPatch {
|
|
|
887
963
|
outputDir?: string;
|
|
888
964
|
provider?: string;
|
|
889
965
|
model?: string;
|
|
966
|
+
reasoningEffort?: 'off' | 'low' | 'high' | 'max';
|
|
890
967
|
chapterChars?: number;
|
|
891
968
|
maxTokens?: number;
|
|
892
969
|
reviewPassScore?: number;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
2
|
+
import type { NovelConfig, RunState } from './protocol.ts';
|
|
3
|
+
export declare class ProductionRunner {
|
|
4
|
+
private deps;
|
|
5
|
+
private state;
|
|
6
|
+
private working;
|
|
7
|
+
private pauseRequested;
|
|
8
|
+
private stopRequested;
|
|
9
|
+
/** 生产单绑定目录(start 时快照;所有读写固定用它,防止运行中切书导致写错目录)。 */
|
|
10
|
+
private bookDir;
|
|
11
|
+
constructor(deps: {
|
|
12
|
+
ctx: Context;
|
|
13
|
+
getConfig: () => NovelConfig;
|
|
14
|
+
});
|
|
15
|
+
/** 当前生产单状态(内存优先;web 重启后从磁盘恢复)。 */
|
|
16
|
+
status(): RunState | null;
|
|
17
|
+
private persist;
|
|
18
|
+
private log;
|
|
19
|
+
/** 启动/续跑生产单:startNo..endNo 区间,endNo 超出计划时先自动补计划。 */
|
|
20
|
+
start(startNo: number, endNo: number): Promise<RunState>;
|
|
21
|
+
pause(): void;
|
|
22
|
+
resume(): void;
|
|
23
|
+
stop(): void;
|
|
24
|
+
/** 主循环:从 currentNo 扫描到 endNo,逐章处理;支持暂停/停止。 */
|
|
25
|
+
private loop;
|
|
26
|
+
private processChapter;
|
|
27
|
+
/** 完整质量门:生成 → 摘要+事实 → 伏笔标记 → 审稿 → 作者复盘。 */
|
|
28
|
+
private produce;
|
|
29
|
+
/** 被拒分级处理:无 high 豁免;有 high 按意见修订(最多 2 轮)+ 验证模式;仍不过 → 待人工。 */
|
|
30
|
+
private handleRejected;
|
|
31
|
+
private applyDraft;
|
|
32
|
+
}
|
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 chapters with LLM, generate chapter by chapter (3000-4000 chars each), and save every chapter as Markdown into your chosen output folder.",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": "^22.19.0 || >=24.0.0"
|
|
@@ -86,6 +86,7 @@
|
|
|
86
86
|
],
|
|
87
87
|
"license": "Apache-2.0",
|
|
88
88
|
"scripts": {
|
|
89
|
+
"postinstall": "node scripts/patch-schemastery.mjs",
|
|
89
90
|
"build": "tsc -p tsconfig.build.json && tsdown",
|
|
90
91
|
"bundle": "tsdown",
|
|
91
92
|
"watch": "tsdown --watch",
|
package/src/bookshelf.ts
CHANGED
|
@@ -144,8 +144,8 @@ export function activeBookOutputDir(): string | undefined {
|
|
|
144
144
|
return book?.outputDir
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
-
/**
|
|
147
|
+
/** 默认输出目录推断:小说目录/书名。 */
|
|
148
148
|
export function defaultOutputDirFor(bookName: string): string {
|
|
149
149
|
const clean = bookName.replace(/[\\/:*?"<>|]/g, '').trim().slice(0, 40) || '未命名小说'
|
|
150
|
-
return join(
|
|
150
|
+
return join('D:\\ryan work\\harness\\novels', clean)
|
|
151
151
|
}
|
package/src/client/api.ts
CHANGED
|
@@ -254,6 +254,29 @@ export class NovelApi {
|
|
|
254
254
|
return postJson<import('../protocol.ts').BookshelfSnapshot>('/api/dsh-novel-forge/bookshelf/remove', { id })
|
|
255
255
|
}
|
|
256
256
|
|
|
257
|
+
/** 生产单:启动批量生产(区间或新增 N 章;计划不足自动补)。 */
|
|
258
|
+
async runStart(req: import('../protocol.ts').RunStartRequest): Promise<import('../protocol.ts').RunState> {
|
|
259
|
+
return postJson<import('../protocol.ts').RunState>(NOVEL_API.runStart, req)
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/** 生产单控制:pause / resume / stop。 */
|
|
263
|
+
async runControl(action: 'pause' | 'resume' | 'stop'): Promise<import('../protocol.ts').RunState | null> {
|
|
264
|
+
const response = await fetch(NOVEL_API.runControl, {
|
|
265
|
+
method: 'POST',
|
|
266
|
+
headers: { 'Content-Type': 'application/json' },
|
|
267
|
+
body: JSON.stringify({ action }),
|
|
268
|
+
})
|
|
269
|
+
if (response.status === 400) return null
|
|
270
|
+
return readJson<import('../protocol.ts').RunState>(response)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/** 生产单状态(无生产单返回 null)。 */
|
|
274
|
+
async runStatus(): Promise<import('../protocol.ts').RunState | null> {
|
|
275
|
+
const response = await fetch(NOVEL_API.runStatus)
|
|
276
|
+
if (response.status === 404) return null
|
|
277
|
+
return readJson<import('../protocol.ts').RunState | null>(response)
|
|
278
|
+
}
|
|
279
|
+
|
|
257
280
|
/** Get project writing assets + built-in libraries. */
|
|
258
281
|
async assets(): Promise<AssetsResponse> {
|
|
259
282
|
const response = await fetch(NOVEL_API.assets)
|
package/src/client/locales.ts
CHANGED
|
@@ -142,6 +142,14 @@ export const zh = {
|
|
|
142
142
|
'settings.outputDir': '输出目录',
|
|
143
143
|
'settings.provider': '模型提供商',
|
|
144
144
|
'settings.model': '模型',
|
|
145
|
+
'settings.modelCustom': '自定义…',
|
|
146
|
+
'settings.modelCustomPlaceholder': '输入任意模型 id',
|
|
147
|
+
'settings.reasoningEffort': '思考强度 (reasoningEffort)',
|
|
148
|
+
'settings.reasoning.off': 'off · 关闭思考(推荐日常写文)',
|
|
149
|
+
'settings.reasoning.low': 'low · 低思考',
|
|
150
|
+
'settings.reasoning.high': 'high · 高思考',
|
|
151
|
+
'settings.reasoning.max': 'max · 最高思考(质检/推演)',
|
|
152
|
+
'settings.reasoningHint': 'off 最快最省;high/max 更慢更贵但推理更强',
|
|
145
153
|
'settings.theme': '主题',
|
|
146
154
|
'settings.themeLiquid': 'iOS 液态玻璃(绿)',
|
|
147
155
|
'settings.themeClassic': '经典毛玻璃(蓝)',
|
|
@@ -285,6 +293,14 @@ export const en: Record<string, string> = {
|
|
|
285
293
|
'settings.outputDir': 'Output directory',
|
|
286
294
|
'settings.provider': 'Provider',
|
|
287
295
|
'settings.model': 'Model',
|
|
296
|
+
'settings.modelCustom': 'Custom…',
|
|
297
|
+
'settings.modelCustomPlaceholder': 'Type any model id',
|
|
298
|
+
'settings.reasoningEffort': 'Reasoning effort',
|
|
299
|
+
'settings.reasoning.off': 'off · no thinking (recommended)',
|
|
300
|
+
'settings.reasoning.low': 'low',
|
|
301
|
+
'settings.reasoning.high': 'high',
|
|
302
|
+
'settings.reasoning.max': 'max (audit/reasoning)',
|
|
303
|
+
'settings.reasoningHint': 'off is fastest/cheapest; high/max are slower and stronger',
|
|
288
304
|
'settings.theme': 'Theme',
|
|
289
305
|
'settings.themeLiquid': 'iOS Liquid Glass (green)',
|
|
290
306
|
'settings.themeClassic': 'Classic frosted (blue)',
|
|
@@ -206,6 +206,10 @@ export function AssetsTab({ api, initialTab = 'genre' }: AssetsTabProps) {
|
|
|
206
206
|
|
|
207
207
|
return (
|
|
208
208
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 12, flex: 1, minHeight: 0 }}>
|
|
209
|
+
<div className={css.row} style={{ justifyContent: 'space-between', flexWrap: 'wrap' }}>
|
|
210
|
+
<span className={css.cardTitle} style={{ fontSize: 17, fontWeight: 700 }}>🧰 创作资产</span>
|
|
211
|
+
<span className={css.meta}>题材基底 / 推进模式 / 笔法帖 / 文戒 / 心法</span>
|
|
212
|
+
</div>
|
|
209
213
|
{error !== '' && <div className={css.card} style={{ borderColor: 'var(--nf-error)' }}><span style={{ color: 'var(--nf-error)' }}>{tt('common.error')}: {error}</span></div>}
|
|
210
214
|
{notice !== '' && <div className={css.card}><span style={{ color: 'var(--nf-success)' }}>{notice}</span></div>}
|
|
211
215
|
|
|
@@ -235,6 +239,21 @@ export function AssetsTab({ api, initialTab = 'genre' }: AssetsTabProps) {
|
|
|
235
239
|
</div>
|
|
236
240
|
</div>
|
|
237
241
|
|
|
242
|
+
{/* 子页签:题材基底 / 推进模式 / 笔法帖 / 文戒 / 心法 */}
|
|
243
|
+
<div className={css.row} style={{ flexWrap: 'wrap', gap: 6 }}>
|
|
244
|
+
{SUB_TABS.map(t => (
|
|
245
|
+
<button
|
|
246
|
+
key={t.id}
|
|
247
|
+
type="button"
|
|
248
|
+
className={`${css.button} ${assetTab === t.id ? css.buttonPrimary : ''}`}
|
|
249
|
+
style={{ fontSize: 14, flex: 1 }}
|
|
250
|
+
onClick={() => { setAssetTab(t.id) }}
|
|
251
|
+
>
|
|
252
|
+
{t.label}
|
|
253
|
+
</button>
|
|
254
|
+
))}
|
|
255
|
+
</div>
|
|
256
|
+
|
|
238
257
|
{/* 题材基底库 */}
|
|
239
258
|
{assetTab === 'genre' && (
|
|
240
259
|
<div className={css.card}>
|
|
@@ -86,8 +86,8 @@ export function CreateBookView({
|
|
|
86
86
|
|
|
87
87
|
/** 生成/换批:只补未暂留的空槽;exclude 传已暂留方案的卖点方向。 */
|
|
88
88
|
const handleSuggest = async (): Promise<void> => {
|
|
89
|
-
if (idea.trim().length <
|
|
90
|
-
setError('想法太短(<
|
|
89
|
+
if (idea.trim().length < 10) {
|
|
90
|
+
setError('想法太短(<10 字):至少写一句完整想法,如「男主穿越修仙界靠做菜无敌」')
|
|
91
91
|
return
|
|
92
92
|
}
|
|
93
93
|
setSuggesting(true)
|
|
@@ -253,7 +253,7 @@ export function CreateBookView({
|
|
|
253
253
|
<button
|
|
254
254
|
type="button"
|
|
255
255
|
className={`${css.button} ${css.buttonSmall} ${css.buttonPrimary}`}
|
|
256
|
-
disabled={suggesting || idea.trim().length <
|
|
256
|
+
disabled={suggesting || idea.trim().length < 10}
|
|
257
257
|
onClick={() => { void handleSuggest() }}
|
|
258
258
|
title="生成 3 个方向不同的大纲方案供选择(约消耗 6-8k token)"
|
|
259
259
|
>
|