@waterwx/dsh-novel-forge 2.0.0-alpha.1 → 2.0.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 +286 -285
- package/lib/client.js +19561 -19439
- package/lib/index.js +8159 -4456
- package/lib/types/action-guard.d.ts +61 -0
- package/lib/types/ai-scan.d.ts +0 -6
- package/lib/types/assets.d.ts +20 -9
- package/lib/types/assistant.d.ts +29 -0
- package/lib/types/author-assets.d.ts +1 -3
- package/lib/types/bookshelf.d.ts +13 -3
- package/lib/types/client/api.d.ts +136 -27
- package/lib/types/client/chip-prefs.d.ts +36 -0
- package/lib/types/client/docx.d.ts +2 -2
- package/lib/types/client/llmLive.d.ts +31 -0
- package/lib/types/client/locales.d.ts +15 -70
- package/lib/types/client/mount.d.ts +0 -2
- 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/AuthorHome.d.ts +13 -11
- 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/IdeaInspirationView.d.ts +6 -0
- package/lib/types/client/panel/KnowledgeBaseView.d.ts +4 -0
- package/lib/types/client/panel/LiveFeedLog.d.ts +13 -0
- package/lib/types/client/panel/MarketRadarView.d.ts +5 -0
- package/lib/types/client/panel/ModelManager.d.ts +2 -2
- 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/PromptSlotsView.d.ts +11 -0
- package/lib/types/client/panel/SettingsView.d.ts +12 -9
- package/lib/types/client/panel/ShelfView.d.ts +10 -1
- package/lib/types/client/panel/SubPage.d.ts +27 -0
- package/lib/types/client/panel/TensionView.d.ts +18 -0
- package/lib/types/client/panel/TimelineView.d.ts +18 -0
- package/lib/types/client/panel/helpers.d.ts +5 -3
- package/lib/types/client/panel/market-radar-state.d.ts +56 -0
- package/lib/types/client/panel/toast.d.ts +2 -0
- package/lib/types/client/panel/views.d.ts +21 -35
- package/lib/types/client/run-status.d.ts +21 -0
- package/lib/types/client/sidebar-entry.d.ts +0 -2
- package/lib/types/docx-core.d.ts +16 -0
- package/lib/types/docx.d.ts +2 -3
- package/lib/types/engine.d.ts +78 -103
- package/lib/types/global-assets.d.ts +6 -0
- package/lib/types/home.d.ts +11 -0
- package/lib/types/index.d.ts +26 -12
- package/lib/types/llm-live.d.ts +70 -0
- package/lib/types/llm-retry.d.ts +38 -0
- package/lib/types/market-radar-scan.d.ts +18 -0
- package/lib/types/market-radar-sources.d.ts +31 -0
- package/lib/types/migrate-output.d.ts +25 -0
- package/lib/types/model-capability.d.ts +93 -0
- package/lib/types/output-budget.d.ts +86 -0
- package/lib/types/prompt-slots.d.ts +57 -0
- package/lib/types/prompting.d.ts +10 -0
- package/lib/types/protocol.d.ts +816 -601
- package/lib/types/revision.d.ts +76 -0
- package/lib/types/routes.d.ts +9 -3
- package/lib/types/run.d.ts +23 -1
- package/lib/types/settings-persist.d.ts +59 -0
- package/lib/types/snapshots.d.ts +46 -0
- package/lib/types/stage-contract.d.ts +58 -0
- package/lib/types/tension.d.ts +53 -0
- package/lib/types/timeline.d.ts +37 -0
- package/package.json +50 -25
- package/src/action-guard.ts +127 -0
- package/src/ai-scan.ts +0 -24
- package/src/assets.ts +1801 -879
- package/src/assistant.ts +1098 -770
- package/src/author-assets.ts +131 -147
- package/src/bookshelf.ts +193 -167
- package/src/client/api.ts +767 -617
- package/src/client/chip-prefs.ts +121 -0
- package/src/client/docx.ts +32 -69
- package/src/client/index.ts +2 -0
- package/src/client/llmLive.ts +111 -0
- package/src/client/locales.ts +15 -274
- package/src/client/mount.tsx +3 -2
- package/src/client/panel/AdaptModeView.tsx +535 -520
- package/src/client/panel/AiTag.tsx +7 -0
- package/src/client/panel/AssetsTab.tsx +448 -458
- package/src/client/panel/AssistantTab.tsx +355 -301
- package/src/client/panel/AuthorAssetsView.tsx +205 -207
- package/src/client/panel/AuthorHome.tsx +309 -204
- package/src/client/panel/BookAnalysisView.tsx +63 -0
- package/src/client/panel/CmdPalette.tsx +102 -0
- package/src/client/panel/CreateBookView.tsx +366 -352
- package/src/client/panel/DirectorView.tsx +200 -0
- package/src/client/panel/GlobalAssetLibraryView.tsx +168 -172
- package/src/client/panel/IdeaInspirationView.tsx +76 -0
- package/src/client/panel/ImportModal.tsx +302 -302
- package/src/client/panel/KnowledgeBaseView.tsx +125 -0
- package/src/client/panel/LiveFeedLog.tsx +204 -0
- package/src/client/panel/MarketRadarView.tsx +415 -0
- package/src/client/panel/ModelManager.tsx +205 -205
- package/src/client/panel/NovelPanel.tsx +3144 -2205
- package/src/client/panel/PageHeader.tsx +37 -0
- package/src/client/panel/ProgressConsole.tsx +63 -64
- package/src/client/panel/PromptSlotsView.tsx +97 -0
- package/src/client/panel/ReaderView.tsx +306 -306
- package/src/client/panel/RunPanel.tsx +56 -24
- package/src/client/panel/SettingsView.tsx +175 -106
- package/src/client/panel/ShelfView.tsx +63 -39
- package/src/client/panel/SubPage.tsx +42 -0
- package/src/client/panel/TensionView.tsx +212 -0
- package/src/client/panel/TimelineView.tsx +208 -0
- package/src/client/panel/WorldTab.tsx +202 -202
- package/src/client/panel/helpers.ts +7 -5
- package/src/client/panel/market-radar-state.ts +79 -0
- package/src/client/panel/panel.module.css +5767 -4360
- package/src/client/panel/toast.ts +51 -0
- package/src/client/panel/views.tsx +316 -314
- package/src/client/run-status.ts +171 -0
- package/src/client/sidebar-entry.ts +3 -3
- package/src/docx-core.ts +53 -0
- package/src/docx.ts +44 -83
- package/src/engine.ts +1197 -1638
- package/src/global-assets.ts +58 -0
- package/src/home.ts +33 -0
- package/src/index.ts +181 -101
- package/src/llm-live.ts +245 -0
- package/src/llm-retry.ts +77 -0
- package/src/market-radar-scan.ts +35 -0
- package/src/market-radar-sources.ts +173 -0
- package/src/migrate-output.ts +76 -0
- package/src/model-capability.ts +215 -0
- package/src/novel-context.ts +286 -287
- package/src/output-budget.ts +147 -0
- package/src/prompt-slots.ts +125 -0
- package/src/prompting.ts +26 -0
- package/src/protocol.ts +835 -614
- package/src/revision.ts +283 -0
- package/src/routes.ts +1286 -910
- package/src/run.ts +113 -17
- package/src/settings-persist.ts +114 -0
- package/src/snapshots.ts +174 -0
- package/src/stage-contract.ts +186 -0
- package/src/tension.ts +251 -0
- package/src/timeline.ts +159 -0
- package/assets/styles/arcane-thick-3d.webp +0 -0
- package/assets/styles/chinese-ink.webp +0 -0
- package/assets/styles/clay-stopmotion-3d.webp +0 -0
- package/assets/styles/dark-ghibli.webp +0 -0
- package/assets/styles/dark-gothic-3d.webp +0 -0
- package/assets/styles/film-photo.webp +0 -0
- package/assets/styles/guofeng-heavy.webp +0 -0
- package/assets/styles/guoman-2d-gufeng.webp +0 -0
- package/assets/styles/guoman-3d-tianyuan.webp +0 -0
- package/assets/styles/guoman-3d-xuanhuan.webp +0 -0
- package/assets/styles/horror-game-3d.webp +0 -0
- package/assets/styles/hyperreal-3d.webp +0 -0
- package/assets/styles/japan-cel-dark.webp +0 -0
- package/assets/styles/korean-webtoon.webp +0 -0
- package/assets/styles/low-cyberpunk-3d.webp +0 -0
- package/assets/styles/nextgen-cg.webp +0 -0
- package/assets/styles/noir-film.webp +0 -0
- package/assets/styles/openworld-survival.webp +0 -0
- package/assets/styles/paper-cut-3d.webp +0 -0
- package/assets/styles/pixar-adult-3d.webp +0 -0
- package/assets/styles/pop-marvel-2d.webp +0 -0
- package/assets/styles/sandiao-qban.webp +0 -0
- package/assets/styles/shinkai-anime.webp +0 -0
- package/assets/styles/tactical-stealth.webp +0 -0
- package/assets/styles/urban-lightreal-3d.webp +0 -0
- package/assets/styles/vaporwave-3d.webp +0 -0
- package/assets/styles/vhs90.webp +0 -0
- package/lib/types/client/panel/BookshelfBar.d.ts +0 -11
- package/lib/types/client/panel/FlowGuide.d.ts +0 -22
- package/lib/types/client/panel/MangaRoleLibrary.d.ts +0 -20
- package/lib/types/client/panel/MangaWorkspace.d.ts +0 -12
- package/lib/types/client/panel/ModelPicker.d.ts +0 -11
- package/lib/types/client/panel/ProgressHomeView.d.ts +0 -5
- package/lib/types/client/panel/PropLibrary.d.ts +0 -8
- package/lib/types/client/panel/SceneLibrary.d.ts +0 -16
- package/lib/types/client/panel/StoryboardTab.d.ts +0 -19
- package/lib/types/client/panel/StyleCard.d.ts +0 -8
- package/lib/types/client/panel/StyleGallery.d.ts +0 -1
- package/lib/types/comic-presets.d.ts +0 -12
- package/lib/types/manga-genre-rules.d.ts +0 -22
- package/lib/types/prompt-utils.d.ts +0 -35
- package/lib/types/review-policy.d.ts +0 -97
- package/lib/types/shot-language.d.ts +0 -46
- package/lib/types/story-beat-language.d.ts +0 -35
- package/lib/types/style-library.d.ts +0 -33
- package/skills/chapter-batch/SKILL.md +0 -81
- package/src/client/mount.tsx._new +0 -97
- package/src/client/mount.tsx._nf +0 -97
- package/src/client/mount.tsx._tmp +0 -97
- package/src/client/panel/BookshelfBar.tsx +0 -170
- package/src/client/panel/FlowGuide.tsx +0 -84
- package/src/client/panel/MangaRoleLibrary.tsx +0 -1002
- package/src/client/panel/MangaWorkspace.tsx +0 -715
- package/src/client/panel/ProgressHomeView.tsx +0 -87
- package/src/client/panel/PropLibrary.tsx +0 -175
- package/src/client/panel/SceneLibrary.tsx +0 -453
- package/src/client/panel/StoryboardTab.tsx +0 -472
- package/src/client/panel/StyleCard.tsx +0 -90
- package/src/client/panel/StyleGallery.tsx +0 -58
- package/src/comic-presets.ts +0 -67
- package/src/manga-genre-rules.ts +0 -87
- package/src/prompt-utils.ts +0 -73
- package/src/review-policy.ts +0 -161
- package/src/shot-language.ts +0 -181
- package/src/story-beat-language.ts +0 -130
- package/src/style-library.ts +0 -84
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { NovelApi } from '../api.ts';
|
|
2
|
-
import type { BookshelfSnapshot } from '../../protocol.ts';
|
|
3
|
-
/** Props. */
|
|
4
|
-
export interface BookshelfBarProps {
|
|
5
|
-
api: NovelApi;
|
|
6
|
-
shelf: BookshelfSnapshot;
|
|
7
|
-
/** 刷新整个面板(切换书后重新拉状态)。 */
|
|
8
|
-
onSwitch: () => void;
|
|
9
|
-
}
|
|
10
|
-
/** 书架条。 */
|
|
11
|
-
export declare function BookshelfBar({ api, shelf, onSwitch }: BookshelfBarProps): import("react").JSX.Element;
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 漫剧工作台·全流程步骤条(唯一导航,方案X):
|
|
3
|
-
* ①创建方案 → ②一键生成 → ③分镜 → ④角色定妆 → ⑤场景底图 → ⑥导出使用。
|
|
4
|
-
* 每个步骤对应一个独立页面主体;完成度自动判定(读 project 现有字段)。
|
|
5
|
-
*/
|
|
6
|
-
import type { ProjectState } from '../../protocol.ts';
|
|
7
|
-
/** 步骤 → 页面主体的一一对应目标。 */
|
|
8
|
-
export type FlowTarget = 'plan' | 'rules' | 'skeleton' | 'table' | 'import' | 'makeup' | 'prompts' | 'scenes' | 'props' | 'export';
|
|
9
|
-
export interface FlowStep {
|
|
10
|
-
no: number;
|
|
11
|
-
label: string;
|
|
12
|
-
hint: string;
|
|
13
|
-
done: boolean;
|
|
14
|
-
target: FlowTarget;
|
|
15
|
-
}
|
|
16
|
-
/** 依据 project 数据计算 6 步完成度(与存储顺序无关,按生产顺序排列)。 */
|
|
17
|
-
export declare function computeFlowSteps(project: ProjectState | null, exported?: boolean): FlowStep[];
|
|
18
|
-
export declare function FlowGuide({ project, onNavigate, exported, }: {
|
|
19
|
-
project: ProjectState | null;
|
|
20
|
-
onNavigate: (target: FlowTarget) => void;
|
|
21
|
-
exported?: boolean;
|
|
22
|
-
}): import("react").JSX.Element;
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { NovelApi } from '../api.ts';
|
|
2
|
-
import type { ImageModelConfig, ProjectState } from '../../protocol.ts';
|
|
3
|
-
export declare function MangaRoleLibrary({ api, project, refresh, styleId, filterId, focus, showCards, chapterNo: externalChapter, onProgress, }: {
|
|
4
|
-
api: NovelApi;
|
|
5
|
-
project: ProjectState | null;
|
|
6
|
-
refresh: () => void | Promise<void>;
|
|
7
|
-
styleId?: string;
|
|
8
|
-
filterId?: string;
|
|
9
|
-
/** 是否启用生图(豆包等出定妆图)。 */
|
|
10
|
-
imageApiEnabled?: boolean;
|
|
11
|
-
/** 生图模型库(出定妆图时可选择用哪条)。 */
|
|
12
|
-
imageModels?: ImageModelConfig[];
|
|
13
|
-
/** 步骤页聚焦:import=展开并滚动到导入区;cards=滚动到已建漫剧卡列表。 */
|
|
14
|
-
focus?: 'import' | 'cards';
|
|
15
|
-
/** 是否显示「已建漫剧卡」列表(第⑤步导入页可隐藏,只留导入区)。 */
|
|
16
|
-
showCards?: boolean;
|
|
17
|
-
/** 全局当前章节(从工作台顶部导航条传入)。 */
|
|
18
|
-
chapterNo?: number | null;
|
|
19
|
-
onProgress?: (text: string, kind?: 'info' | 'done' | 'error') => void;
|
|
20
|
-
}): import("react").JSX.Element;
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { NovelApi } from '../api.ts';
|
|
2
|
-
import type { ChapterPlan, ProjectState } from '../../protocol.ts';
|
|
3
|
-
export declare function MangaWorkspace({ api, project, chapters, onProjectChanged, onProgress, }: {
|
|
4
|
-
api: NovelApi;
|
|
5
|
-
project: ProjectState | null;
|
|
6
|
-
chapters: ChapterPlan[];
|
|
7
|
-
/** 方案/资产变更已持久化后触发(刷新项目)。 */
|
|
8
|
-
onProjectChanged?: () => void | Promise<void>;
|
|
9
|
-
/** 是否启用生图(漫剧卡出定妆图)。 */
|
|
10
|
-
/** 上报到「AI进度」控制台(漫剧工作台内所有 LLM/方案操作)。 */
|
|
11
|
-
onProgress?: (text: string, kind?: 'info' | 'done' | 'error') => void;
|
|
12
|
-
}): import("react").JSX.Element;
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { NovelApi } from '../api.ts';
|
|
2
|
-
/** 目录不可用时回退的内置预设(历史行为)。 */
|
|
3
|
-
export declare const FALLBACK_MODEL_PRESETS: readonly ["deepseek-v4-flash", "deepseek-v4-pro", "deepseek-chat", "deepseek-reasoner"];
|
|
4
|
-
export interface ModelPickerProps {
|
|
5
|
-
api: NovelApi;
|
|
6
|
-
provider: string;
|
|
7
|
-
model: string;
|
|
8
|
-
onProvider: (provider: string) => void;
|
|
9
|
-
onModel: (model: string) => void;
|
|
10
|
-
}
|
|
11
|
-
export declare function ModelPicker({ api, provider, model, onProvider, onModel }: ModelPickerProps): JSX.Element;
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { NovelApi } from '../api.ts';
|
|
2
|
-
import type { ProjectState } from '../../protocol.ts';
|
|
3
|
-
export declare function PropLibrary({ api, project, refresh, onProgress, }: {
|
|
4
|
-
api: NovelApi;
|
|
5
|
-
project: ProjectState | null;
|
|
6
|
-
refresh: () => void | Promise<void>;
|
|
7
|
-
onProgress?: (text: string, kind?: 'info' | 'done' | 'error') => void;
|
|
8
|
-
}): import("react").JSX.Element;
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { NovelApi } from '../api.ts';
|
|
2
|
-
import type { ProjectState } from '../../protocol.ts';
|
|
3
|
-
export declare function SceneLibrary({ api, project, refresh, styleId, filterId, chapterNo: externalChapter, onProgress, }: {
|
|
4
|
-
api: NovelApi;
|
|
5
|
-
project: ProjectState | null;
|
|
6
|
-
/** 场景库变更已持久化后触发(刷新项目)。 */
|
|
7
|
-
refresh: () => void | Promise<void>;
|
|
8
|
-
/** 提炼场景时的漫剧基底风格 id(提示词按方案风格措辞)。 */
|
|
9
|
-
styleId?: string;
|
|
10
|
-
/** 可选滤镜风格 id。 */
|
|
11
|
-
filterId?: string;
|
|
12
|
-
/** 全局当前章节(从工作台顶部导航条传入)。 */
|
|
13
|
-
chapterNo?: number | null;
|
|
14
|
-
/** 上报到「AI进度」控制台。 */
|
|
15
|
-
onProgress?: (text: string, kind?: 'info' | 'done' | 'error') => void;
|
|
16
|
-
}): import("react").JSX.Element;
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import type { NovelApi } from '../api.ts';
|
|
2
|
-
import type { ChapterPlan, ProjectState } from '../../protocol.ts';
|
|
3
|
-
export declare function StoryboardTab({ api, project, chapters, onProjectChanged, styleId, filterId, mode, onGoStep, onProgress, }: {
|
|
4
|
-
api: NovelApi;
|
|
5
|
-
project: ProjectState | null;
|
|
6
|
-
chapters: ChapterPlan[];
|
|
7
|
-
/** 生成成功且已持久化后触发(刷新项目,切章/重进可恢复)。 */
|
|
8
|
-
onProjectChanged?: () => void | Promise<void>;
|
|
9
|
-
/** 漫剧基底风格 id(画面措辞随风格)。 */
|
|
10
|
-
styleId?: string;
|
|
11
|
-
/** 可选滤镜风格 id。 */
|
|
12
|
-
filterId?: string;
|
|
13
|
-
/** 页面模式:auto=按本章实际进度显示;skeleton/table/prompts=固定显示该步骤(前置不足显示提示,不降级)。 */
|
|
14
|
-
mode?: 'auto' | 'skeleton' | 'table' | 'prompts';
|
|
15
|
-
/** 「下一步」按钮回调(1=骨架→2=分镜表→3=提示词),供外层切步骤页。 */
|
|
16
|
-
onGoStep?: (n: 1 | 2 | 3) => void;
|
|
17
|
-
/** 上报到「AI进度」控制台(分镜三步生成)。 */
|
|
18
|
-
onProgress?: (text: string, kind?: 'info' | 'done' | 'error') => void;
|
|
19
|
-
}): import("react").JSX.Element;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function StyleGallery(): import("react").JSX.Element;
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/** 漫画风格预设:每个预设对应一组出图提示词与排版偏好。 */
|
|
2
|
-
export interface ComicStylePreset {
|
|
3
|
-
id: string;
|
|
4
|
-
label: string;
|
|
5
|
-
/** 注入 imagePrompt 的风格描述词。 */
|
|
6
|
-
prompt: string;
|
|
7
|
-
/** 可选:排版偏好说明。 */
|
|
8
|
-
layoutHint?: string;
|
|
9
|
-
}
|
|
10
|
-
export declare const COMIC_STYLE_PRESETS: ComicStylePreset[];
|
|
11
|
-
export declare function getComicStylePrompt(styleId?: string): string;
|
|
12
|
-
export declare function getComicStyleLabel(styleId?: string): string;
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 题材规则库(可插拔)—— 漫剧分镜/提示词生成的「题材专用规则」。
|
|
3
|
-
* 参考短剧工业导演规范:不同题材用不同的视觉语言 / 打斗 / 特效规则,
|
|
4
|
-
* 按漫剧方案的 genre 注入 generateStoryboardPrompts,避免「一套模板跑所有题材」。
|
|
5
|
-
* 每个题材给一组「生成提示词时必须遵守的题材规则」;未选题材则不注入(走通用规则)。
|
|
6
|
-
*/
|
|
7
|
-
export interface GenreRule {
|
|
8
|
-
/** 题材 id。 */
|
|
9
|
-
id: string;
|
|
10
|
-
/** 题材名(展示)。 */
|
|
11
|
-
label: string;
|
|
12
|
-
/** 该题材的生成提示词核心规则(每行一条,注入生成时)。 */
|
|
13
|
-
rules: string[];
|
|
14
|
-
}
|
|
15
|
-
/** 内置题材(通用题材 default 为空规则)。 */
|
|
16
|
-
export declare const GENRES: GenreRule[];
|
|
17
|
-
/** 通用题材(空规则,走通用生成)。 */
|
|
18
|
-
export declare const DEFAULT_GENRE: GenreRule;
|
|
19
|
-
/** 按题材 id 取规则(未找到返回通用)。 */
|
|
20
|
-
export declare function getGenreRules(id?: string): string[];
|
|
21
|
-
/** 取题材 label(展示用)。 */
|
|
22
|
-
export declare function getGenreLabel(id?: string): string;
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 提示词共享常量与工具
|
|
3
|
-
*
|
|
4
|
-
* 所有 JSON 输出规则、通用约束集中在此,避免各阶段重复定义、互相打架。
|
|
5
|
-
*/
|
|
6
|
-
/** JSON 输出规则(所有要求模型输出 JSON 的地方共用) */
|
|
7
|
-
export declare const JSON_OUTPUT_RULES: string[];
|
|
8
|
-
/** JSON 输出规则的紧凑版(适合放在提示词末尾) */
|
|
9
|
-
export declare const JSON_OUTPUT_RULES_COMPACT: string;
|
|
10
|
-
/**
|
|
11
|
-
* 估算 prompt 的 token 数(粗略估算,用于上下文预算控制)
|
|
12
|
-
* 中文约 1.7 字/token,英文约 4 字符/token
|
|
13
|
-
*/
|
|
14
|
-
export declare function estimateTokens(text: string): number;
|
|
15
|
-
/**
|
|
16
|
-
* 按 token 预算截断文本(保留开头,末尾加省略标记)
|
|
17
|
-
* 用于控制单块上下文不超预算。
|
|
18
|
-
*/
|
|
19
|
-
export declare function truncateByTokens(text: string, maxTokens: number, marker?: string): string;
|
|
20
|
-
/** 通用「只改表达不改情节」约束(润色/局部修订共用) */
|
|
21
|
-
export declare const EXPRESSION_ONLY_RULE: string[];
|
|
22
|
-
/** 章节上下文各阶段的 token 预算(粗略,用于控制注入量) */
|
|
23
|
-
export declare const CONTEXT_BUDGET: {
|
|
24
|
-
/** 章节生成:道藏+角色+大纲+事实+伏笔 */
|
|
25
|
-
readonly writing: 12000;
|
|
26
|
-
/** AI 审稿:道藏+角色+事实+正文 */
|
|
27
|
-
readonly review: 10000;
|
|
28
|
-
/** 修订:原意见+相关事实+目标正文 */
|
|
29
|
-
readonly revise: 8000;
|
|
30
|
-
/** 润色:风格资产+原正文 */
|
|
31
|
-
readonly polish: 6000;
|
|
32
|
-
/** 分章规划:大纲+道藏+角色+近期事实 */
|
|
33
|
-
readonly plan: 15000;
|
|
34
|
-
};
|
|
35
|
-
export type ContextStage = keyof typeof CONTEXT_BUDGET;
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 审稿规则注册表
|
|
3
|
-
*
|
|
4
|
-
* 生成用 compact 模式(核心规则),审稿用 full 模式(全部规则),
|
|
5
|
-
* 消除 reviewSystemPrompt 内嵌规则与 renderAllAssets() 两套打架的问题。
|
|
6
|
-
*/
|
|
7
|
-
/** 通用写作资产规则(兼容 ProjectAssets 中的各类规则) */
|
|
8
|
-
export interface WritingRule {
|
|
9
|
-
name: string;
|
|
10
|
-
description: string;
|
|
11
|
-
type?: string;
|
|
12
|
-
enabled?: boolean;
|
|
13
|
-
}
|
|
14
|
-
export interface ReviewRule {
|
|
15
|
-
id: string;
|
|
16
|
-
/** 规则维度 */
|
|
17
|
-
dimension: 'character' | 'setting' | 'redline' | 'writing' | 'pacing' | 'logic' | 'anti-ai' | 'presentation' | 'compliance';
|
|
18
|
-
/** 严重度 */
|
|
19
|
-
severity: 'high' | 'medium' | 'low';
|
|
20
|
-
/** 规则类型:forbidden=禁止类(命中即问题),encourage=鼓励类(不命中不算错) */
|
|
21
|
-
type: 'forbidden' | 'encourage';
|
|
22
|
-
/** 规则描述 */
|
|
23
|
-
description: string;
|
|
24
|
-
/** 是否启用 */
|
|
25
|
-
enabled: boolean;
|
|
26
|
-
/** 作用域 */
|
|
27
|
-
scope: Array<'writing' | 'review' | 'polish' | 'all'>;
|
|
28
|
-
}
|
|
29
|
-
/** 内置审稿维度(结构化评分用) */
|
|
30
|
-
export declare const REVIEW_DIMENSIONS: readonly [{
|
|
31
|
-
readonly id: "character";
|
|
32
|
-
readonly name: "人设一致性";
|
|
33
|
-
readonly weight: 0.2;
|
|
34
|
-
}, {
|
|
35
|
-
readonly id: "setting";
|
|
36
|
-
readonly name: "设定一致性";
|
|
37
|
-
readonly weight: 0.15;
|
|
38
|
-
}, {
|
|
39
|
-
readonly id: "redline";
|
|
40
|
-
readonly name: "红线检查";
|
|
41
|
-
readonly weight: 0.15;
|
|
42
|
-
}, {
|
|
43
|
-
readonly id: "writing";
|
|
44
|
-
readonly name: "文笔质量";
|
|
45
|
-
readonly weight: 0.15;
|
|
46
|
-
}, {
|
|
47
|
-
readonly id: "pacing";
|
|
48
|
-
readonly name: "节奏与爽点";
|
|
49
|
-
readonly weight: 0.1;
|
|
50
|
-
}, {
|
|
51
|
-
readonly id: "logic";
|
|
52
|
-
readonly name: "逻辑漏洞";
|
|
53
|
-
readonly weight: 0.1;
|
|
54
|
-
}, {
|
|
55
|
-
readonly id: "anti-ai";
|
|
56
|
-
readonly name: "反 AI 规则";
|
|
57
|
-
readonly weight: 0.05;
|
|
58
|
-
}, {
|
|
59
|
-
readonly id: "presentation";
|
|
60
|
-
readonly name: "呈现方式";
|
|
61
|
-
readonly weight: 0.05;
|
|
62
|
-
}, {
|
|
63
|
-
readonly id: "compliance";
|
|
64
|
-
readonly name: "内容合规";
|
|
65
|
-
readonly weight: 0.05;
|
|
66
|
-
}];
|
|
67
|
-
export type ReviewDimensionId = typeof REVIEW_DIMENSIONS[number]['id'];
|
|
68
|
-
/** 维度结构化评分 */
|
|
69
|
-
export interface DimensionScore {
|
|
70
|
-
dimension: ReviewDimensionId;
|
|
71
|
-
score: number;
|
|
72
|
-
note: string;
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* 渲染审稿规则(compact 或 full 模式)
|
|
76
|
-
*
|
|
77
|
-
* compact: 只渲染禁止类 + high 严重度(用于生成时的约束)
|
|
78
|
-
* full: 渲染全部启用规则(用于审稿时的检查清单)
|
|
79
|
-
*/
|
|
80
|
-
export declare function renderReviewRules(assets: WritingRule[] | undefined, mode?: 'compact' | 'full'): string;
|
|
81
|
-
/**
|
|
82
|
-
* 从审稿 issues 中按维度统计
|
|
83
|
-
* 用于结构化评分和 UI 展示。
|
|
84
|
-
*/
|
|
85
|
-
export declare function groupIssuesByDimension(issues: Array<{
|
|
86
|
-
severity: string;
|
|
87
|
-
item: string;
|
|
88
|
-
dimension?: string;
|
|
89
|
-
}>): Record<string, Array<{
|
|
90
|
-
severity: string;
|
|
91
|
-
item: string;
|
|
92
|
-
}>>;
|
|
93
|
-
/**
|
|
94
|
-
* 计算维度加权总分
|
|
95
|
-
* 用于审稿报告的结构化评分。
|
|
96
|
-
*/
|
|
97
|
-
export declare function calculateWeightedScore(dimensions: DimensionScore[]): number;
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 镜头语言词库(分镜规范化基准)。
|
|
3
|
-
* 分镜表的 shot/camera/composition/light 字段统一从这里取值(英文 id),
|
|
4
|
-
* 生成提示词时用 zh 中文词块;适配英文平台时用 en。
|
|
5
|
-
* 目的:让"景别/运镜/构图/光效"有标准词表,避免 LLM 自由发挥导致漂移。
|
|
6
|
-
*/
|
|
7
|
-
/** 景别。 */
|
|
8
|
-
export type ShotSizeId = 'extreme_wide' | 'wide' | 'full' | 'medium' | 'medium_close' | 'close' | 'extreme_close' | 'big_extreme_close';
|
|
9
|
-
/** 运镜/机位。 */
|
|
10
|
-
export type CameraMoveId = 'static' | 'dolly_in' | 'dolly_out' | 'pan_left' | 'pan_right' | 'track_left' | 'track_right' | 'follow' | 'pedestal_up' | 'pedestal_down' | 'orbit' | 'handheld' | 'low_angle' | 'high_angle' | 'over_shoulder';
|
|
11
|
-
/** 构图。 */
|
|
12
|
-
export type CompositionId = 'rule_of_thirds' | 'center' | 'leading_line' | 'foreground' | 'low' | 'overhead' | 'symmetry';
|
|
13
|
-
/** 光效。 */
|
|
14
|
-
export type LightingId = 'front' | 'side' | 'back' | 'top' | 'rembrandt' | 'neon' | 'hard' | 'soft' | 'mood' | 'contrast';
|
|
15
|
-
export interface ShotLangEntry<T extends string> {
|
|
16
|
-
id: T;
|
|
17
|
-
/** 中文(提示词/展示用)。 */
|
|
18
|
-
zh: string;
|
|
19
|
-
/** 英文(英文平台适配用)。 */
|
|
20
|
-
en: string;
|
|
21
|
-
/** 一句话说明(下拉/卡片)。 */
|
|
22
|
-
hint: string;
|
|
23
|
-
}
|
|
24
|
-
export declare const SHOT_SIZES: readonly ShotLangEntry<ShotSizeId>[];
|
|
25
|
-
export declare const CAMERA_MOVES: readonly ShotLangEntry<CameraMoveId>[];
|
|
26
|
-
export declare const COMPOSITIONS: readonly ShotLangEntry<CompositionId>[];
|
|
27
|
-
export declare const LIGHTINGS: readonly ShotLangEntry<LightingId>[];
|
|
28
|
-
export interface ShotLanguage {
|
|
29
|
-
shot?: ShotSizeId;
|
|
30
|
-
camera: CameraMoveId[];
|
|
31
|
-
composition?: CompositionId;
|
|
32
|
-
light: LightingId[];
|
|
33
|
-
}
|
|
34
|
-
/** 从中文文本归一化到景别(处理旧数据/LLM 口语)。未知回退 medium。 */
|
|
35
|
-
export declare function normalizeShotSize(text: string | undefined): ShotSizeId;
|
|
36
|
-
/** 从中文文本归一化到运镜列表。 */
|
|
37
|
-
export declare function normalizeCameras(text: string | undefined): CameraMoveId[];
|
|
38
|
-
/** 从中文文本归一化到构图(可选)。 */
|
|
39
|
-
export declare function normalizeComposition(text: string | undefined): CompositionId | undefined;
|
|
40
|
-
/** 从中文文本归一化到光效列表。 */
|
|
41
|
-
export declare function normalizeLightings(text: string | undefined): LightingId[];
|
|
42
|
-
/** 取词条中文(ZHs 合并句子)。 */
|
|
43
|
-
export declare function sizeZh(id: ShotSizeId | undefined): string;
|
|
44
|
-
export declare function cameraZh(ids: CameraMoveId[] | undefined): string;
|
|
45
|
-
export declare function compoZh(id: CompositionId | undefined): string;
|
|
46
|
-
export declare function lightZh(ids: LightingId[] | undefined): string;
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 编剧词库(剧情骨架规范化基准)。
|
|
3
|
-
* 骨架的 function(叙事功能)与 emotion(情绪走向)统一从这里取值(英文 id),
|
|
4
|
-
* 生成提示词/展示时用 zh 中文。
|
|
5
|
-
* 目的:让"叙事功能/情绪"有标准词表,避免 LLM 自由发挥导致情绪链漂移。
|
|
6
|
-
*/
|
|
7
|
-
/** 叙事功能(对应节拍在剧情中的作用)。 */
|
|
8
|
-
export type StoryFunctionId = 'exposition' | 'conflict' | 'turn' | 'climax' | 'resolve' | 'foreshadow' | 'character';
|
|
9
|
-
/** 情绪词(低→高能量的情绪阶,角色在节拍中的主要情绪点)。 */
|
|
10
|
-
export type EmotionId = 'calm' | 'indifferent' | 'expectant' | 'curious' | 'alert' | 'suppressed' | 'enduring' | 'worried' | 'irritable' | 'uneasy' | 'terrified' | 'angry' | 'collapsing' | 'resolute' | 'grieved' | 'relieved' | 'bittersweet' | 'triumphant' | 'reborn' | 'numb';
|
|
11
|
-
export interface BeatLangEntry<T extends string> {
|
|
12
|
-
id: T;
|
|
13
|
-
/** 中文(展示用)。 */
|
|
14
|
-
zh: string;
|
|
15
|
-
/** 英文(英文平台适配用)。 */
|
|
16
|
-
en: string;
|
|
17
|
-
/** 一句话说明。 */
|
|
18
|
-
hint: string;
|
|
19
|
-
}
|
|
20
|
-
/** 叙事功能词表。 */
|
|
21
|
-
export declare const STORY_FUNCTIONS: readonly BeatLangEntry<StoryFunctionId>[];
|
|
22
|
-
/** 情绪词表(按能量/阶段排序)。 */
|
|
23
|
-
export declare const EMOTIONS: readonly BeatLangEntry<EmotionId>[];
|
|
24
|
-
export interface StoryBeatLanguage {
|
|
25
|
-
function: StoryFunctionId;
|
|
26
|
-
emotion: EmotionId[];
|
|
27
|
-
}
|
|
28
|
-
/** 从中文归一化叙事功能(未知回退 exposition)。 */
|
|
29
|
-
export declare function normalizeStoryFunction(text: string | undefined): StoryFunctionId;
|
|
30
|
-
/** 从中文文本归一化情绪词列表(可含→箭头链)。 */
|
|
31
|
-
export declare function normalizeEmotions(text: string | undefined): EmotionId[];
|
|
32
|
-
/** 叙事功能中文。 */
|
|
33
|
-
export declare function functionZh(id: StoryFunctionId | undefined): string;
|
|
34
|
-
/** 情绪中文(箭头连接)。 */
|
|
35
|
-
export declare function emotionZh(ids: EmotionId[] | undefined): string;
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 风格库(内置模板):漫剧方案的视觉基底 + 可叠加滤镜。
|
|
3
|
-
* 关键词针对即梦 Seedream 优化:画风定义 + 质感描述 + 光影色彩 + 即梦质量词(8K/五官清晰/真实材质)。
|
|
4
|
-
*/
|
|
5
|
-
export type StyleCategory = '3d' | 'game' | '2d' | 'craft' | 'film';
|
|
6
|
-
export interface ArtStyle {
|
|
7
|
-
id: string;
|
|
8
|
-
/** 风格名(用户可见)。 */
|
|
9
|
-
name: string;
|
|
10
|
-
category: StyleCategory;
|
|
11
|
-
/** 特点一句话(卡片展示)。 */
|
|
12
|
-
traits: string;
|
|
13
|
-
/** 风格关键词/前缀(生成角色图/视频提示词时套用)。 */
|
|
14
|
-
keywords: string;
|
|
15
|
-
/** 是否可作为滤镜叠加在基底风格之上(影视实验类;缺省 false)。 */
|
|
16
|
-
stackable?: boolean;
|
|
17
|
-
/** 默认推荐排序权重(0-1,推荐池排序参考)。 */
|
|
18
|
-
weight?: number;
|
|
19
|
-
}
|
|
20
|
-
export declare const STYLE_CATEGORIES: ReadonlyArray<{
|
|
21
|
-
id: StyleCategory;
|
|
22
|
-
label: string;
|
|
23
|
-
icon: string;
|
|
24
|
-
desc: string;
|
|
25
|
-
}>;
|
|
26
|
-
/** 内置风格模板(基底 21 个 + 滤镜 3 个)。 */
|
|
27
|
-
export declare const STYLE_LIBRARY: ArtStyle[];
|
|
28
|
-
/** 按分类取风格。 */
|
|
29
|
-
export declare function stylesByCategory(category: StyleCategory): ArtStyle[];
|
|
30
|
-
/** 取单个风格(未找到返回 undefined)。 */
|
|
31
|
-
export declare function findStyle(id: string): ArtStyle | undefined;
|
|
32
|
-
/** 按基底 + 滤镜拼接风格关键词(角色图/分镜/视频提示词共用;缺省回退 3D 动漫)。 */
|
|
33
|
-
export declare function styleKeywords(styleId?: string, filterId?: string): string;
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: novel-forge-chapter-batch
|
|
3
|
-
description: 批量生成小说章节并值守处理审稿未过的章节。当用户说「生成 N 章」「连写」「批量生成」「继续写章节」「值守处理未过章节」时使用。依赖 dsh-novel-forge 插件(http://127.0.0.1:3080/api/dsh-novel-forge/*)。
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# 小说章节批量生产与值守处理
|
|
7
|
-
|
|
8
|
-
把「批量生成章节 + 审稿未过处理」固化为可复现流程。所有操作通过 dsh-novel-forge 的 HTTP API 执行。
|
|
9
|
-
|
|
10
|
-
## 0. 值守纪律(最高优先级,违反即空转)
|
|
11
|
-
|
|
12
|
-
1. **启动后台任务后立即停下,不轮询、不连续 wait、不做无意义动作**——后台任务(批量生成/处理)由运行时在完成时自动通知。
|
|
13
|
-
2. 只有收到「任务完成」通知或用户新消息时,才继续下一步(读结果 → 处理未过 → 汇报)。
|
|
14
|
-
3. 等待期间可以做「与当前任务无关」的其他有用工作(如用户提出的其他问题、代码改进),但不得为等任务而空转。
|
|
15
|
-
4. 任务因 web 重启中断时:等用户确认或检测到 3080 恢复后,复位卡死章节(generating/error → reset)再续跑,并向用户说明中断原因。
|
|
16
|
-
|
|
17
|
-
## 1. 前置检查(每次必做)
|
|
18
|
-
|
|
19
|
-
1. 确认服务在跑:`GET /api/dsh-novel-forge/status` 返回 200,读 `project.chapters` 数量与书名。
|
|
20
|
-
2. 确认目标章节状态:列出 `pending`(待生成)章节号区间,避免重复生成已 approved/rejected 的章。
|
|
21
|
-
3. **串行执行,禁止并行写 project.json**——多个脚本同时调 API 会造成状态互相覆盖(实测教训:第 166 章曾被并发回滚成 rejected)。
|
|
22
|
-
|
|
23
|
-
## 2. 批量生成
|
|
24
|
-
|
|
25
|
-
对目标章节区间(如 181-205)逐章串行调用:
|
|
26
|
-
|
|
27
|
-
```
|
|
28
|
-
POST /api/dsh-novel-forge/generate
|
|
29
|
-
{ "chapterNo": N, "skipReview": false }
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
- `skipReview: false` 走完整质量门(生成 → 摘要+编年录 → AI 审稿 → 作者复盘)。
|
|
33
|
-
- 响应为 NDJSON 流;找 `{"type":"review","report":{...}}` 帧取 `score`/`passed`。
|
|
34
|
-
- 每章 2-5 分钟;失败自动重试 1 次(间隔 3 秒)。
|
|
35
|
-
- 预估总时长并告知用户(25 章 ≈ 45-75 分钟),放后台跑,期间可并行做别的(只读操作不受影响)。
|
|
36
|
-
- 推荐用 Node 脚本(fetch + 逐章循环),避免 PowerShell 的引号/UTF-8 转义坑;脚本用完删除。
|
|
37
|
-
|
|
38
|
-
## 2. 未过章节处理(分级策略)
|
|
39
|
-
|
|
40
|
-
生成完成后汇总:通过 / 未过(rejected)/ 失败(error)。对未过章按此分级:
|
|
41
|
-
|
|
42
|
-
### A. 无 high(全是 medium/low 主观项)→ 豁免通过
|
|
43
|
-
```
|
|
44
|
-
POST /api/dsh-novel-forge/chapter/approve { "chapterNo": N }
|
|
45
|
-
```
|
|
46
|
-
主观项(套话/节奏/比喻/心理描写)不值得逐章磨,直接通过。
|
|
47
|
-
|
|
48
|
-
### B. 有 high 且问题明确(逻辑/设定/时间线/用词)→ 按意见修订 + 验证模式
|
|
49
|
-
1. 取 review 的 high(无 high 取前 3 条 medium),拼指令:
|
|
50
|
-
`按审稿意见修订(优先处理):\n[severity] item → suggestion`
|
|
51
|
-
2. `POST /rewrite` `{ chapterNo, instructions }` → 产出 pendingDraft
|
|
52
|
-
3. **验证模式**:`POST /chapter/check` `{ chapterNo, text: draft, previousReport: review }`——携带上一轮报告,LLM 只核对原意见是否解决 + 只挑新增 high,不全新找茬
|
|
53
|
-
4. 判定:`passed || issues 全非 high` → 可接受
|
|
54
|
-
5. 可接受 → `POST /draft/apply` `{ chapterNo, report: { score: max(score,70), passed: true, issues: [], ... } }` 落盘为 approved
|
|
55
|
-
6. 不可接受 → 看剩余 high 是否明确可修 → 第二轮修订(指令更精确,如「把第 141 章的养伤时间线统一为七天」)→ 再验证
|
|
56
|
-
7. **每章最多修订 2 轮**,仍不过 → 保留草稿,标记「待人工」,不硬来
|
|
57
|
-
|
|
58
|
-
### C. 有 high 但结构性(修订改不好,如逻辑矛盾被坐实)→ 重新生成
|
|
59
|
-
- `POST /draft/discard` 清草稿 → `POST /generate` 整章重写
|
|
60
|
-
- 重新生成后若只剩主观项 → 走 A 豁免
|
|
61
|
-
|
|
62
|
-
### D. error 状态(无审稿、chars 丢失)→ 重置重生成
|
|
63
|
-
- `POST /chapter/reset` `{ chapterNo }` → `POST /generate`
|
|
64
|
-
|
|
65
|
-
### 特殊注意
|
|
66
|
-
- **approved 但 review 有 high**:已通过状态保留,不折腾(high 可能已被正文处理或审稿误标)。
|
|
67
|
-
- **修订指令越精确越有效**:具体数字/行为(如「茶梗尺寸统一为半截指节」)一次就过;泛泛的「修正矛盾」容易改不到位。
|
|
68
|
-
|
|
69
|
-
## 3. 收尾
|
|
70
|
-
|
|
71
|
-
1. 最终验证:`GET /status` 读目标区间全部 approved。
|
|
72
|
-
2. 可选:对涉及剧情的章节刷新剧情线进度(`POST /plotlines` `{ op:'refresh', id }`)——注意刷新函数只读最近 8 章摘要,早期章节的线可能刷不准,必要时手动 update progress + link 章节。
|
|
73
|
-
3. 向用户汇报:通过/未过/失败数量 + 每章处理方式 + 遗留待人工项。
|
|
74
|
-
|
|
75
|
-
## 4. 已知陷阱(血泪教训)
|
|
76
|
-
|
|
77
|
-
- **并发写 project.json 会互相覆盖**——多个生成/处理任务必须串行。
|
|
78
|
-
- **rewrite 的草稿可能不落盘**(偶发 500 或时序)——应用前检查 `pendingDraft` 是否存在,不存在则重跑 rewrite。
|
|
79
|
-
- **PowerShell 调 API 的引号/中文转义是坑**——统一用 Node 脚本。
|
|
80
|
-
- **status 接口会做数据快照**——以磁盘 novel-project.json 为准复核关键状态。
|
|
81
|
-
- **验证模式 500 偶发**——重试一次即可(LLM 瞬时错误)。
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Panel view mounting — mirrors the family plugins: a container appended
|
|
3
|
-
* inside the conversation grid item, hidden while inactive; toggling is a
|
|
4
|
-
* data attribute on <html>, with cross-plugin activation events.
|
|
5
|
-
*/
|
|
6
|
-
import { createRoot, type Root } from 'react-dom/client'
|
|
7
|
-
import type { NovelApi } from './api.ts'
|
|
8
|
-
import type { PanelController } from './panel/controller.ts'
|
|
9
|
-
import { NovelPanel } from './panel/NovelPanel.tsx'
|
|
10
|
-
import css from './panel/panel.module.css'
|
|
11
|
-
|
|
12
|
-
/** The injected panel container. */
|
|
13
|
-
export const PANEL_VIEW_SELECTOR = '[data-dsh-novelforge-view]'
|
|
14
|
-
|
|
15
|
-
const CONVERSATION_COLUMN_SELECTOR = '[data-pane="conversation"], [class*="centerCol"]'
|
|
16
|
-
const ACTIVE_ATTR = 'data-dsh-novelforge-active'
|
|
17
|
-
/** Sibling panels' activation attributes (evicted when this panel opens). */
|
|
18
|
-
const OTHER_ACTIVE_ATTRS = ['data-dsh-taskboard-active', 'data-dsh-ssh-active']
|
|
19
|
-
/** Cross-plugin activation event; detail is the activating panel name. */
|
|
20
|
-
const ACTIVATE_EVENT = 'dsh-panel-activate'
|
|
21
|
-
const PANEL_NAME = 'novelforge'
|
|
22
|
-
|
|
23
|
-
/** Find the center column, or undefined while the frame is not mounted. */
|
|
24
|
-
function conversationColumn(): HTMLElement | undefined {
|
|
25
|
-
return document.querySelector<HTMLElement>(CONVERSATION_COLUMN_SELECTOR) ?? undefined
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Mount the panel React tree into the center column and bind visibility to
|
|
30
|
-
* the controller.
|
|
31
|
-
*/
|
|
32
|
-
export function mountPanel(controller: PanelController, api: NovelApi): () => void {
|
|
33
|
-
let root: Root | undefined
|
|
34
|
-
let container: HTMLDivElement | undefined
|
|
35
|
-
|
|
36
|
-
const ensure = (): void => {
|
|
37
|
-
if (container !== undefined) {
|
|
38
|
-
if (container.isConnected) return
|
|
39
|
-
root?.unmount()
|
|
40
|
-
root = undefined
|
|
41
|
-
container.remove()
|
|
42
|
-
container = undefined
|
|
43
|
-
}
|
|
44
|
-
const column = conversationColumn()
|
|
45
|
-
if (column === undefined) return
|
|
46
|
-
container = document.createElement('div')
|
|
47
|
-
container.dataset.dshNovelforgeView = 'true'
|
|
48
|
-
container.className = css.view
|
|
49
|
-
column.appendChild(container)
|
|
50
|
-
root = createRoot(container)
|
|
51
|
-
root.render(<NovelPanel controller={controller} api={api} />)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const waitObserver = new MutationObserver(() => { ensure() })
|
|
55
|
-
waitObserver.observe(document.body, { childList: true, subtree: true })
|
|
56
|
-
|
|
57
|
-
const applyActive = (): void => {
|
|
58
|
-
if (controller.getSnapshot().panelOpen) {
|
|
59
|
-
for (const attr of OTHER_ACTIVE_ATTRS) document.documentElement.removeAttribute(attr)
|
|
60
|
-
document.documentElement.setAttribute(ACTIVE_ATTR, '')
|
|
61
|
-
document.dispatchEvent(new CustomEvent(ACTIVATE_EVENT, { detail: PANEL_NAME }))
|
|
62
|
-
} else {
|
|
63
|
-
document.documentElement.removeAttribute(ACTIVE_ATTR)
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
const onOtherActivate = (event: Event): void => {
|
|
67
|
-
const detail = (event as CustomEvent).detail
|
|
68
|
-
if ((detail === 'taskboard' || detail === 'ssh') && controller.getSnapshot().panelOpen) {
|
|
69
|
-
controller.close()
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
// Jump out on sidebar context clicks.
|
|
73
|
-
const SIDEBAR_ROW_SELECTOR = '[class*="sessionRow"], [class*="projectRow"], [class*="searchResultRow"], [class*="searchResultWorkspace"], [class*="newSession"]'
|
|
74
|
-
const onClickSidebarRow = (event: MouseEvent): void => {
|
|
75
|
-
if (!controller.getSnapshot().panelOpen) return
|
|
76
|
-
const target = event.target as HTMLElement | null
|
|
77
|
-
if (target === null) return
|
|
78
|
-
if (target.closest(SIDEBAR_ROW_SELECTOR) !== null) controller.close()
|
|
79
|
-
}
|
|
80
|
-
document.addEventListener('click', onClickSidebarRow, true)
|
|
81
|
-
document.addEventListener(ACTIVATE_EVENT, onOtherActivate)
|
|
82
|
-
const unsubscribe = controller.subscribe(applyActive)
|
|
83
|
-
applyActive()
|
|
84
|
-
ensure()
|
|
85
|
-
|
|
86
|
-
return () => {
|
|
87
|
-
document.removeEventListener('click', onClickSidebarRow, true)
|
|
88
|
-
document.removeEventListener(ACTIVATE_EVENT, onOtherActivate)
|
|
89
|
-
waitObserver.disconnect()
|
|
90
|
-
unsubscribe()
|
|
91
|
-
document.documentElement.removeAttribute(ACTIVE_ATTR)
|
|
92
|
-
root?.unmount()
|
|
93
|
-
root = undefined
|
|
94
|
-
container?.remove()
|
|
95
|
-
container = undefined
|
|
96
|
-
}
|
|
97
|
-
}
|