|
@@ -2,16 +2,14 @@
|
|
|
|
|
|
|
|
import { useState, useCallback, useEffect } from 'react';
|
|
import { useState, useCallback, useEffect } from 'react';
|
|
|
import { X, Check } from 'lucide-react';
|
|
import { X, Check } from 'lucide-react';
|
|
|
-import { WorldStateFile, WorldLoreFile, WritingConstraint, AntiHomogenizationStrategy, NarrativeHook, EventNode, ChapterAllocation } from '@/app/lib/novel-types';
|
|
|
|
|
-import { parseHooksFromJson } from '@/app/lib/novel-utils';
|
|
|
|
|
|
|
+import { WorldStateFile, WorldLoreFile, WritingConstraint, AntiHomogenizationStrategy, EventNode, ChapterAllocation } from '@/app/lib/novel-types';
|
|
|
import Step0ControlCard from './pipeline/Step0ControlCard';
|
|
import Step0ControlCard from './pipeline/Step0ControlCard';
|
|
|
import Step1PlotSelection from './pipeline/Step1PlotSelection';
|
|
import Step1PlotSelection from './pipeline/Step1PlotSelection';
|
|
|
import Step2EmbedHooks from './pipeline/Step2EmbedHooks';
|
|
import Step2EmbedHooks from './pipeline/Step2EmbedHooks';
|
|
|
-import Step3ResolveHooks from './pipeline/Step3ResolveHooks';
|
|
|
|
|
-import Step4ContentEditor from './pipeline/Step4ContentEditor';
|
|
|
|
|
-import Step5ReviewSubmit from './pipeline/Step5ReviewSubmit';
|
|
|
|
|
-import Step6ContinuityCheck from './pipeline/Step6ContinuityCheck';
|
|
|
|
|
-import Step7ReaderFeedback from './pipeline/Step7ReaderFeedback';
|
|
|
|
|
|
|
+import Step3ContentEditor from './pipeline/Step4ContentEditor';
|
|
|
|
|
+import Step4ReviewSubmit from './pipeline/Step5ReviewSubmit';
|
|
|
|
|
+import Step5ContinuityCheck from './pipeline/Step6ContinuityCheck';
|
|
|
|
|
+import Step6ReaderFeedback from './pipeline/Step7ReaderFeedback';
|
|
|
import type { ControlCardData } from './pipeline/Step0ControlCard';
|
|
import type { ControlCardData } from './pipeline/Step0ControlCard';
|
|
|
|
|
|
|
|
interface ApiSettings {
|
|
interface ApiSettings {
|
|
@@ -37,7 +35,7 @@ interface ChapterDraftPipelineProps {
|
|
|
onCancel: () => void;
|
|
onCancel: () => void;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-type PipelineStep = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7;
|
|
|
|
|
|
|
+type PipelineStep = 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
|
|
|
|
|
|
|
interface PipelineState {
|
|
interface PipelineState {
|
|
|
controlCard: ControlCardData | null;
|
|
controlCard: ControlCardData | null;
|
|
@@ -45,7 +43,6 @@ interface PipelineState {
|
|
|
customNote: string;
|
|
customNote: string;
|
|
|
embedHooks: { id: string; type: string; description: string; embedMethod: string }[];
|
|
embedHooks: { id: string; type: string; description: string; embedMethod: string }[];
|
|
|
manualHooks: string[];
|
|
manualHooks: string[];
|
|
|
- resolveHookIds: string[];
|
|
|
|
|
chapterContent: string;
|
|
chapterContent: string;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -53,11 +50,10 @@ const STEPS = [
|
|
|
{ id: 0, label: '控制卡' },
|
|
{ id: 0, label: '控制卡' },
|
|
|
{ id: 1, label: '选择剧情' },
|
|
{ id: 1, label: '选择剧情' },
|
|
|
{ id: 2, label: '埋入钩子' },
|
|
{ id: 2, label: '埋入钩子' },
|
|
|
- { id: 3, label: '解决钩子' },
|
|
|
|
|
- { id: 4, label: '生成内容' },
|
|
|
|
|
- { id: 5, label: '审核提交' },
|
|
|
|
|
- { id: 6, label: '连续性检查' },
|
|
|
|
|
- { id: 7, label: '读者反馈' },
|
|
|
|
|
|
|
+ { id: 3, label: '生成内容' },
|
|
|
|
|
+ { id: 4, label: '审核提交' },
|
|
|
|
|
+ { id: 5, label: '连续性检查' },
|
|
|
|
|
+ { id: 6, label: '读者反馈' },
|
|
|
] as const;
|
|
] as const;
|
|
|
|
|
|
|
|
export default function ChapterDraftPipeline({
|
|
export default function ChapterDraftPipeline({
|
|
@@ -88,7 +84,6 @@ export default function ChapterDraftPipeline({
|
|
|
customNote: parsed.customNote || '',
|
|
customNote: parsed.customNote || '',
|
|
|
embedHooks: parsed.embedHooks || [],
|
|
embedHooks: parsed.embedHooks || [],
|
|
|
manualHooks: parsed.manualHooks || [],
|
|
manualHooks: parsed.manualHooks || [],
|
|
|
- resolveHookIds: parsed.resolveHookIds || [],
|
|
|
|
|
chapterContent: parsed.chapterContent || '',
|
|
chapterContent: parsed.chapterContent || '',
|
|
|
};
|
|
};
|
|
|
}
|
|
}
|
|
@@ -99,7 +94,6 @@ export default function ChapterDraftPipeline({
|
|
|
customNote: '',
|
|
customNote: '',
|
|
|
embedHooks: [],
|
|
embedHooks: [],
|
|
|
manualHooks: [],
|
|
manualHooks: [],
|
|
|
- resolveHookIds: [],
|
|
|
|
|
chapterContent: '',
|
|
chapterContent: '',
|
|
|
};
|
|
};
|
|
|
};
|
|
};
|
|
@@ -125,13 +119,6 @@ export default function ChapterDraftPipeline({
|
|
|
});
|
|
});
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- // 从 worldStateFiles 中提取 open 状态的叙事钩子
|
|
|
|
|
- const openHooks: NarrativeHook[] = (() => {
|
|
|
|
|
- const hooksFile = worldStateFiles.find((f) => f.fileName === 'narrative_hooks.json');
|
|
|
|
|
- if (!hooksFile || !hooksFile.content.trim()) return [];
|
|
|
|
|
- return parseHooksFromJson(hooksFile.content).filter((h) => h.status === 'open');
|
|
|
|
|
- })();
|
|
|
|
|
-
|
|
|
|
|
// 加载事件图谱和防漂移文件
|
|
// 加载事件图谱和防漂移文件
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
|
// Load event graph allocation for this chapter
|
|
// Load event graph allocation for this chapter
|
|
@@ -186,26 +173,14 @@ export default function ChapterDraftPipeline({
|
|
|
[]
|
|
[]
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
- // Step 3 完成(解决钩子)
|
|
|
|
|
- const handleStep3Next = useCallback(
|
|
|
|
|
- (resolveHookIds: string[]) => {
|
|
|
|
|
- updateState((prev) => ({
|
|
|
|
|
- ...prev,
|
|
|
|
|
- resolveHookIds,
|
|
|
|
|
- }));
|
|
|
|
|
- setCurrentStep(4);
|
|
|
|
|
- },
|
|
|
|
|
- []
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- // Step 4 完成
|
|
|
|
|
- const handleStep4Next = useCallback((content: string) => {
|
|
|
|
|
|
|
+ // Step 3 完成(内容生成,原 Step 4)
|
|
|
|
|
+ const handleStep3Next = useCallback((content: string) => {
|
|
|
updateState((prev) => ({ ...prev, chapterContent: content }));
|
|
updateState((prev) => ({ ...prev, chapterContent: content }));
|
|
|
- setCurrentStep(5);
|
|
|
|
|
|
|
+ setCurrentStep(4);
|
|
|
}, []);
|
|
}, []);
|
|
|
|
|
|
|
|
- // Step 5 完成 → 进入连续性检查
|
|
|
|
|
- const handleStep5Complete = useCallback(
|
|
|
|
|
|
|
+ // Step 4 完成 → 进入连续性检查
|
|
|
|
|
+ const handleStep4Complete = useCallback(
|
|
|
(chapterTitle: string) => {
|
|
(chapterTitle: string) => {
|
|
|
// Save the chapter title into control card for use in final completion
|
|
// Save the chapter title into control card for use in final completion
|
|
|
updateState((prev) => ({
|
|
updateState((prev) => ({
|
|
@@ -214,15 +189,15 @@ export default function ChapterDraftPipeline({
|
|
|
? { ...prev.controlCard, workingTitle: prev.controlCard.workingTitle || chapterTitle }
|
|
? { ...prev.controlCard, workingTitle: prev.controlCard.workingTitle || chapterTitle }
|
|
|
: { whatChanges: '', lineToAdvance: '', debtToReturn: '', chapterConflict: '', endingResidue: '', chapterTitleCandidates: [], workingTitle: chapterTitle, rawAIResponse: '' },
|
|
: { whatChanges: '', lineToAdvance: '', debtToReturn: '', chapterConflict: '', endingResidue: '', chapterTitleCandidates: [], workingTitle: chapterTitle, rawAIResponse: '' },
|
|
|
}));
|
|
}));
|
|
|
- setCurrentStep(6);
|
|
|
|
|
|
|
+ setCurrentStep(5);
|
|
|
},
|
|
},
|
|
|
[]
|
|
[]
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
- // Step 6 完成 → 条件性进入 Step 7(每5章一次)或直接完成
|
|
|
|
|
- const handleStep6Complete = useCallback(() => {
|
|
|
|
|
|
|
+ // Step 5 完成 → 条件性进入 Step 6(每5章一次)或直接完成
|
|
|
|
|
+ const handleStep5Complete = useCallback(() => {
|
|
|
if (chapterIndex % 5 === 0) {
|
|
if (chapterIndex % 5 === 0) {
|
|
|
- setCurrentStep(7);
|
|
|
|
|
|
|
+ setCurrentStep(6);
|
|
|
} else {
|
|
} else {
|
|
|
sessionStorage.removeItem(storageKey);
|
|
sessionStorage.removeItem(storageKey);
|
|
|
const title = pipelineState.controlCard?.workingTitle || `第${chapterIndex}章`;
|
|
const title = pipelineState.controlCard?.workingTitle || `第${chapterIndex}章`;
|
|
@@ -230,8 +205,8 @@ export default function ChapterDraftPipeline({
|
|
|
}
|
|
}
|
|
|
}, [onComplete, pipelineState.chapterContent, pipelineState.controlCard, chapterIndex, storageKey]);
|
|
}, [onComplete, pipelineState.chapterContent, pipelineState.controlCard, chapterIndex, storageKey]);
|
|
|
|
|
|
|
|
- // Step 7 完成 → 最终提交
|
|
|
|
|
- const handleStep7Complete = useCallback(() => {
|
|
|
|
|
|
|
+ // Step 6 完成 → 最终提交
|
|
|
|
|
+ const handleStep6Complete = useCallback(() => {
|
|
|
sessionStorage.removeItem(storageKey);
|
|
sessionStorage.removeItem(storageKey);
|
|
|
const title = pipelineState.controlCard?.workingTitle || `第${chapterIndex}章`;
|
|
const title = pipelineState.controlCard?.workingTitle || `第${chapterIndex}章`;
|
|
|
onComplete(title, pipelineState.chapterContent);
|
|
onComplete(title, pipelineState.chapterContent);
|
|
@@ -242,7 +217,7 @@ export default function ChapterDraftPipeline({
|
|
|
onCancel();
|
|
onCancel();
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- // 构建传给 Step4 的完整剧情描述(含补充说明)
|
|
|
|
|
|
|
+ // 构建传给 Step3 的完整剧情描述(含补充说明)
|
|
|
const fullPlotDescription = pipelineState.customNote
|
|
const fullPlotDescription = pipelineState.customNote
|
|
|
? `${pipelineState.selectedPlot}\n\n补充说明:${pipelineState.customNote}`
|
|
? `${pipelineState.selectedPlot}\n\n补充说明:${pipelineState.customNote}`
|
|
|
: pipelineState.selectedPlot;
|
|
: pipelineState.selectedPlot;
|
|
@@ -366,24 +341,13 @@ export default function ChapterDraftPipeline({
|
|
|
/>
|
|
/>
|
|
|
)}
|
|
)}
|
|
|
|
|
|
|
|
- {/* Step 3: 解决钩子 */}
|
|
|
|
|
|
|
+ {/* Step 3: 内容生成 */}
|
|
|
{currentStep === 3 && (
|
|
{currentStep === 3 && (
|
|
|
- <Step3ResolveHooks
|
|
|
|
|
- openHooks={openHooks}
|
|
|
|
|
- savedResolveHookIds={pipelineState.resolveHookIds}
|
|
|
|
|
- onNext={handleStep3Next}
|
|
|
|
|
- onBack={() => setCurrentStep(2)}
|
|
|
|
|
- />
|
|
|
|
|
- )}
|
|
|
|
|
-
|
|
|
|
|
- {/* Step 4: 内容生成 */}
|
|
|
|
|
- {currentStep === 4 && (
|
|
|
|
|
- <Step4ContentEditor
|
|
|
|
|
|
|
+ <Step3ContentEditor
|
|
|
projectPath={projectPath}
|
|
projectPath={projectPath}
|
|
|
selectedPlot={fullPlotDescription}
|
|
selectedPlot={fullPlotDescription}
|
|
|
embedHooks={pipelineState.embedHooks}
|
|
embedHooks={pipelineState.embedHooks}
|
|
|
manualHooks={pipelineState.manualHooks}
|
|
manualHooks={pipelineState.manualHooks}
|
|
|
- resolveHookIds={pipelineState.resolveHookIds}
|
|
|
|
|
savedChapterContent={pipelineState.chapterContent}
|
|
savedChapterContent={pipelineState.chapterContent}
|
|
|
apiSettings={apiSettings}
|
|
apiSettings={apiSettings}
|
|
|
writingConstraints={writingConstraints}
|
|
writingConstraints={writingConstraints}
|
|
@@ -392,14 +356,14 @@ export default function ChapterDraftPipeline({
|
|
|
wordCountMax={wordCountMax}
|
|
wordCountMax={wordCountMax}
|
|
|
recentChaptersCount={recentChaptersCount}
|
|
recentChaptersCount={recentChaptersCount}
|
|
|
ironRules={ironRulesContent}
|
|
ironRules={ironRulesContent}
|
|
|
- onNext={handleStep4Next}
|
|
|
|
|
- onBack={() => setCurrentStep(3)}
|
|
|
|
|
|
|
+ onNext={handleStep3Next}
|
|
|
|
|
+ onBack={() => setCurrentStep(2)}
|
|
|
/>
|
|
/>
|
|
|
)}
|
|
)}
|
|
|
|
|
|
|
|
- {/* Step 5: 审核提交 */}
|
|
|
|
|
- {currentStep === 5 && (
|
|
|
|
|
- <Step5ReviewSubmit
|
|
|
|
|
|
|
+ {/* Step 4: 审核提交 */}
|
|
|
|
|
+ {currentStep === 4 && (
|
|
|
|
|
+ <Step4ReviewSubmit
|
|
|
projectPath={projectPath}
|
|
projectPath={projectPath}
|
|
|
chapterContent={pipelineState.chapterContent}
|
|
chapterContent={pipelineState.chapterContent}
|
|
|
chapterIndex={chapterIndex}
|
|
chapterIndex={chapterIndex}
|
|
@@ -408,34 +372,33 @@ export default function ChapterDraftPipeline({
|
|
|
loreFiles={loreFiles}
|
|
loreFiles={loreFiles}
|
|
|
embedHooks={pipelineState.embedHooks}
|
|
embedHooks={pipelineState.embedHooks}
|
|
|
manualHooks={pipelineState.manualHooks}
|
|
manualHooks={pipelineState.manualHooks}
|
|
|
- resolveHookIds={pipelineState.resolveHookIds}
|
|
|
|
|
- onComplete={handleStep5Complete}
|
|
|
|
|
- onBack={() => setCurrentStep(4)}
|
|
|
|
|
|
|
+ onComplete={handleStep4Complete}
|
|
|
|
|
+ onBack={() => setCurrentStep(3)}
|
|
|
/>
|
|
/>
|
|
|
)}
|
|
)}
|
|
|
|
|
|
|
|
- {/* Step 6: 连续性检查 */}
|
|
|
|
|
- {currentStep === 6 && (
|
|
|
|
|
- <Step6ContinuityCheck
|
|
|
|
|
|
|
+ {/* Step 5: 连续性检查 */}
|
|
|
|
|
+ {currentStep === 5 && (
|
|
|
|
|
+ <Step5ContinuityCheck
|
|
|
projectPath={projectPath}
|
|
projectPath={projectPath}
|
|
|
chapterIndex={chapterIndex}
|
|
chapterIndex={chapterIndex}
|
|
|
chapterContent={pipelineState.chapterContent}
|
|
chapterContent={pipelineState.chapterContent}
|
|
|
apiSettings={apiSettings}
|
|
apiSettings={apiSettings}
|
|
|
chapterEvents={chapterEvents}
|
|
chapterEvents={chapterEvents}
|
|
|
- onComplete={handleStep6Complete}
|
|
|
|
|
- onBack={() => setCurrentStep(5)}
|
|
|
|
|
|
|
+ onComplete={handleStep5Complete}
|
|
|
|
|
+ onBack={() => setCurrentStep(4)}
|
|
|
/>
|
|
/>
|
|
|
)}
|
|
)}
|
|
|
|
|
|
|
|
- {/* Step 7: 读者反馈 */}
|
|
|
|
|
- {currentStep === 7 && (
|
|
|
|
|
- <Step7ReaderFeedback
|
|
|
|
|
|
|
+ {/* Step 6: 读者反馈 */}
|
|
|
|
|
+ {currentStep === 6 && (
|
|
|
|
|
+ <Step6ReaderFeedback
|
|
|
projectPath={projectPath}
|
|
projectPath={projectPath}
|
|
|
chapterIndex={chapterIndex}
|
|
chapterIndex={chapterIndex}
|
|
|
chapterContent={pipelineState.chapterContent}
|
|
chapterContent={pipelineState.chapterContent}
|
|
|
apiSettings={apiSettings}
|
|
apiSettings={apiSettings}
|
|
|
- onComplete={handleStep7Complete}
|
|
|
|
|
- onSkip={handleStep7Complete}
|
|
|
|
|
|
|
+ onComplete={handleStep6Complete}
|
|
|
|
|
+ onSkip={handleStep6Complete}
|
|
|
/>
|
|
/>
|
|
|
)}
|
|
)}
|
|
|
</div>
|
|
</div>
|