novel-writer-cn
Version:
AI 驱动的中文小说创作工具 - 基于结构化工作流的智能写作助手
442 lines • 16.9 kB
JavaScript
/**
* 方法自动转换工具
* 在不同写作方法之间转换现有内容
*/
export class MethodConverter {
/**
* 转换方法
*/
convert(content, targetMethod) {
const sourceMethod = content.currentMethod;
// 根据源方法和目标方法选择转换策略
const converterKey = `${sourceMethod}_to_${targetMethod}`;
switch (converterKey) {
case 'three-act_to_hero-journey':
return this.threeActToHeroJourney(content);
case 'three-act_to_seven-point':
return this.threeActToSevenPoint(content);
case 'hero-journey_to_three-act':
return this.heroJourneyToThreeAct(content);
case 'hero-journey_to_story-circle':
return this.heroJourneyToStoryCircle(content);
case 'story-circle_to_three-act':
return this.storyCircleToThreeAct(content);
case 'seven-point_to_three-act':
return this.sevenPointToThreeAct(content);
default:
return this.genericConversion(content, sourceMethod, targetMethod);
}
}
/**
* 三幕结构转英雄之旅
*/
threeActToHeroJourney(content) {
const totalChapters = content.chapters.length;
const act1End = Math.floor(totalChapters * 0.25);
const act2End = Math.floor(totalChapters * 0.75);
const mapping = [
{
original: [1, Math.floor(act1End * 0.4)],
target: '1. 平凡世界',
description: '将第一幕前40%映射为平凡世界'
},
{
original: [Math.floor(act1End * 0.4) + 1, Math.floor(act1End * 0.6)],
target: '2. 冒险召唤',
description: '触发事件映射为召唤'
},
{
original: [Math.floor(act1End * 0.6) + 1, Math.floor(act1End * 0.8)],
target: '3. 拒绝召唤',
description: '主角的犹豫期'
},
{
original: [Math.floor(act1End * 0.8) + 1, act1End],
target: '4. 遇见导师',
description: '获得帮助或指引'
},
{
original: [act1End + 1, act1End + Math.floor((act2End - act1End) * 0.1)],
target: '5. 跨越门槛',
description: '进入第二幕映射为跨越门槛'
},
{
original: [act1End + Math.floor((act2End - act1End) * 0.1) + 1, act1End + Math.floor((act2End - act1End) * 0.4)],
target: '6. 试炼、盟友与敌人',
description: '第二幕前半部分的冲突'
},
{
original: [act1End + Math.floor((act2End - act1End) * 0.4) + 1, act1End + Math.floor((act2End - act1End) * 0.5)],
target: '7. 接近最深的洞穴',
description: '准备面对最大挑战'
},
{
original: [act1End + Math.floor((act2End - act1End) * 0.5) + 1, act1End + Math.floor((act2End - act1End) * 0.6)],
target: '8. 磨难',
description: '中点危机映射为磨难'
},
{
original: [act1End + Math.floor((act2End - act1End) * 0.6) + 1, act1End + Math.floor((act2End - act1End) * 0.8)],
target: '9. 获得奖赏',
description: '克服磨难后的收获'
},
{
original: [act1End + Math.floor((act2End - act1End) * 0.8) + 1, act2End],
target: '10. 归途',
description: '第二幕后期的返回过程'
},
{
original: [act2End + 1, act2End + Math.floor((totalChapters - act2End) * 0.6)],
target: '11. 复活',
description: '第三幕高潮映射为复活'
},
{
original: [act2End + Math.floor((totalChapters - act2End) * 0.6) + 1, totalChapters],
target: '12. 带着灵药归来',
description: '结局和新平衡'
}
];
const notes = [
'英雄之旅强调角色的内在成长,需要加强心理描写',
'可能需要明确或添加导师角色',
'磨难阶段需要体现"死亡与重生"的主题'
];
const recommendations = [
'检查是否有明确的导师角色,如无需要添加',
'强化主角的内在转变弧线',
'确保每个阶段都推进主题'
];
const warnings = [
'三幕结构的线性发展可能不完全符合英雄之旅的循环性',
'可能需要补充一些过渡章节'
];
return { chapters: mapping, structuralNotes: notes, recommendations, warnings };
}
/**
* 三幕结构转七点结构
*/
threeActToSevenPoint(content) {
const totalChapters = content.chapters.length;
const mapping = [
{
original: [1, 3],
target: '1. 钩子 (Hook)',
description: '开头章节作为钩子'
},
{
original: [Math.floor(totalChapters * 0.25) - 1, Math.floor(totalChapters * 0.25) + 1],
target: '2. 第一情节点 (PP1)',
description: '第一幕结尾作为PP1'
},
{
original: [Math.floor(totalChapters * 0.375) - 1, Math.floor(totalChapters * 0.375) + 1],
target: '3. 第一收紧点 (Pinch1)',
description: '第二幕前段压力点'
},
{
original: [Math.floor(totalChapters * 0.5) - 1, Math.floor(totalChapters * 0.5) + 1],
target: '4. 中点 (Midpoint)',
description: '故事中点'
},
{
original: [Math.floor(totalChapters * 0.625) - 1, Math.floor(totalChapters * 0.625) + 1],
target: '5. 第二收紧点 (Pinch2)',
description: '第二幕后段压力点'
},
{
original: [Math.floor(totalChapters * 0.75) - 1, Math.floor(totalChapters * 0.75) + 1],
target: '6. 第二情节点 (PP2)',
description: '第二幕结尾作为PP2'
},
{
original: [Math.floor(totalChapters * 0.95), totalChapters],
target: '7. 结局 (Resolution)',
description: '第三幕结尾作为结局'
}
];
const notes = [
'七点结构需要明确的节奏控制点',
'收紧点(Pinch Points)需要展示对手的力量',
'中点必须是真正的转折'
];
const recommendations = [
'确保每个节点都有明确的功能',
'检查中点是否真正改变了故事走向',
'增强收紧点的压力感'
];
const warnings = [
'可能需要调整某些章节的位置以符合七点结构',
'节奏可能需要重新调整'
];
return { chapters: mapping, structuralNotes: notes, recommendations, warnings };
}
/**
* 英雄之旅转三幕结构
*/
heroJourneyToThreeAct(content) {
const mapping = [
{
original: [1, 5], // 阶段1-5
target: '第一幕',
description: '平凡世界到跨越门槛合并为第一幕'
},
{
original: [6, 10], // 阶段6-10
target: '第二幕',
description: '试炼到归途合并为第二幕'
},
{
original: [11, 12], // 阶段11-12
target: '第三幕',
description: '复活和归来合并为第三幕'
}
];
const notes = [
'简化12阶段为3幕',
'可能失去一些细节深度',
'需要重新组织过渡'
];
const recommendations = [
'保留关键的成长节点',
'确保三幕比例合适(25%-50%-25%)',
'合并相似的阶段'
];
const warnings = [
'可能失去英雄之旅的仪式感',
'角色成长弧线可能被简化'
];
return { chapters: mapping, structuralNotes: notes, recommendations, warnings };
}
/**
* 英雄之旅转故事圈
*/
heroJourneyToStoryCircle(content) {
const mapping = [
{
original: [1], // 平凡世界
target: '1. 你(舒适区)',
description: '平凡世界 = 舒适区'
},
{
original: [2, 3], // 召唤+拒绝
target: '2. 需要',
description: '召唤产生需求'
},
{
original: [4, 5], // 导师+门槛
target: '3. 进入',
description: '跨越门槛进入新世界'
},
{
original: [6, 7], // 试炼+洞穴
target: '4. 搜索',
description: '试炼过程就是搜索'
},
{
original: [8, 9], // 磨难+奖赏
target: '5. 找到',
description: '通过磨难获得所需'
},
{
original: [10], // 归途
target: '6. 付出代价',
description: '归途中的代价'
},
{
original: [11], // 复活
target: '7. 回归',
description: '复活后的回归'
},
{
original: [12], // 带着灵药归来
target: '8. 改变',
description: '彻底的改变'
}
];
const notes = [
'故事圈更强调循环性',
'简化了英雄之旅的复杂性',
'更适合短篇或系列故事'
];
const recommendations = [
'强调角色的内在需求',
'明确每一步的因果关系',
'考虑循环的可能性'
];
const warnings = [
'可能需要简化某些复杂情节',
'史诗感可能减弱'
];
return { chapters: mapping, structuralNotes: notes, recommendations, warnings };
}
/**
* 故事圈转三幕结构
*/
storyCircleToThreeAct(content) {
const mapping = [
{
original: [1, 2], // 你+需要
target: '第一幕',
description: '建立和触发'
},
{
original: [3, 4, 5, 6], // 进入+搜索+找到+代价
target: '第二幕',
description: '冒险和冲突'
},
{
original: [7, 8], // 回归+改变
target: '第三幕',
description: '解决和新平衡'
}
];
const notes = [
'将8步简化为3幕',
'保持核心冲突',
'调整节奏分配'
];
const recommendations = [
'确保第二幕不拖沓',
'保留关键转变时刻',
'明确三幕的功能'
];
const warnings = [
'可能失去循环结构的特色',
'需要重新设计节奏'
];
return { chapters: mapping, structuralNotes: notes, recommendations, warnings };
}
/**
* 七点结构转三幕结构
*/
sevenPointToThreeAct(content) {
const mapping = [
{
original: [1, 2], // Hook + PP1
target: '第一幕',
description: '钩子到第一情节点'
},
{
original: [3, 4, 5, 6], // Pinch1 + Mid + Pinch2 + PP2
target: '第二幕',
description: '所有中间节点'
},
{
original: [7], // Resolution
target: '第三幕',
description: '结局部分'
}
];
const notes = [
'保留关键转折点',
'简化节点为幕',
'调整过渡'
];
const recommendations = [
'确保转折点依然明确',
'不要失去节奏感',
'保持张力曲线'
];
const warnings = [
'可能失去精确的节奏控制',
'需要补充过渡内容'
];
return { chapters: mapping, structuralNotes: notes, recommendations, warnings };
}
/**
* 通用转换(当没有特定转换规则时)
*/
genericConversion(content, source, target) {
const totalChapters = content.chapters.length;
// 基于百分比的通用映射
const mapping = [
{
original: [1, Math.floor(totalChapters * 0.25)],
target: '开端/建立',
description: '故事的建立阶段'
},
{
original: [Math.floor(totalChapters * 0.25) + 1, Math.floor(totalChapters * 0.75)],
target: '发展/冲突',
description: '主要冲突和发展'
},
{
original: [Math.floor(totalChapters * 0.75) + 1, totalChapters],
target: '高潮/结局',
description: '高潮和解决'
}
];
const notes = [
`从${this.getMethodName(source)}转换到${this.getMethodName(target)}`,
'这是基于通用规则的转换',
'可能需要手动调整'
];
const recommendations = [
'仔细检查转换后的结构',
'根据目标方法的特点调整',
'考虑是否需要添加或删除内容'
];
const warnings = [
'自动转换可能不够精确',
'建议人工审核和调整',
'某些特色可能在转换中丢失'
];
return { chapters: mapping, structuralNotes: notes, recommendations, warnings };
}
/**
* 生成转换报告
*/
generateConversionReport(content, targetMethod) {
const conversionMap = this.convert(content, targetMethod);
let report = `# 📝 方法转换报告\n\n`;
report += `## 转换概要\n`;
report += `- **源方法**:${this.getMethodName(content.currentMethod)}\n`;
report += `- **目标方法**:${this.getMethodName(targetMethod)}\n`;
report += `- **总章节数**:${content.chapters.length}章\n`;
report += `- **总字数**:${content.chapters.reduce((sum, ch) => sum + ch.wordCount, 0)}字\n\n`;
report += `## 📊 章节映射\n\n`;
report += `| 原章节 | 目标结构 | 说明 |\n`;
report += `|--------|----------|------|\n`;
conversionMap.chapters.forEach(mapping => {
const range = mapping.original.length === 2
? `${mapping.original[0]}-${mapping.original[1]}`
: `${mapping.original[0]}`;
report += `| 第${range}章 | ${mapping.target} | ${mapping.description} |\n`;
});
report += `\n## 📌 结构说明\n`;
conversionMap.structuralNotes.forEach(note => {
report += `- ${note}\n`;
});
report += `\n## ✅ 建议事项\n`;
conversionMap.recommendations.forEach(rec => {
report += `- ${rec}\n`;
});
if (conversionMap.warnings.length > 0) {
report += `\n## ⚠️ 注意事项\n`;
conversionMap.warnings.forEach(warning => {
report += `- ${warning}\n`;
});
}
report += `\n## 🔧 后续步骤\n`;
report += `1. 审查章节映射是否合理\n`;
report += `2. 根据新结构调整章节内容\n`;
report += `3. 补充或删除必要的过渡内容\n`;
report += `4. 确保新结构的完整性\n`;
report += `5. 测试阅读体验\n`;
return report;
}
/**
* 获取方法中文名
*/
getMethodName(method) {
const names = {
'three-act': '三幕结构',
'hero-journey': '英雄之旅',
'story-circle': '故事圈',
'seven-point': '七点结构',
'pixar-formula': '皮克斯公式'
};
return names[method] || method;
}
}
//# sourceMappingURL=method-converter.js.map