article-writer-cn
Version:
AI 驱动的智能写作系统 - 专注公众号/自媒体文章创作
461 lines (456 loc) • 14.7 kB
JavaScript
#!/usr/bin/env node
/**
* 交互式微信格式化配置器
* 允许用户通过 CLI 交互界面定制样式
*/
import fs from 'fs';
import path from 'path';
import inquirer from 'inquirer';
import { themeOptions, fontFamilyOptions, fontSizeOptions, colorPresets, getColorPresetName, isValidHexColor, } from '../utils/style-options.js';
/**
* 读取当前配置
*/
function loadCurrentConfig() {
const configPath = path.join(process.cwd(), '.content', 'config.json');
if (!fs.existsSync(configPath)) {
console.error('❌ 错误: 未找到 .content/config.json');
console.error('请确保在项目根目录运行此命令');
return null;
}
try {
const configContent = fs.readFileSync(configPath, 'utf-8');
const config = JSON.parse(configContent);
return config.formatting || null;
}
catch (error) {
console.error('❌ 读取配置文件失败:', error);
return null;
}
}
/**
* 保存配置
*/
function saveConfig(formattingConfig) {
const configPath = path.join(process.cwd(), '.content', 'config.json');
try {
const configContent = fs.readFileSync(configPath, 'utf-8');
const config = JSON.parse(configContent);
config.formatting = formattingConfig;
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
return true;
}
catch (error) {
console.error('❌ 保存配置失败:', error);
return false;
}
}
/**
* 显示当前配置
*/
function displayCurrentConfig(config) {
console.log('\n📋 当前配置:');
console.log(` 主题: ${config.theme}`);
console.log(` 主题色: ${getColorPresetName(config.primaryColor)}`);
console.log(` 字号: ${config.fontSize}`);
console.log(` 字体: ${config.fontFamily.split(',')[0]}...`);
console.log(` 首行缩进: ${config.isUseIndent ? '是' : '否'}`);
console.log(` 两端对齐: ${config.isUseJustify ? '是' : '否'}`);
console.log(` 代码行号: ${config.isShowLineNumber ? '是' : '否'}`);
console.log(` 链接转脚注: ${config.citeStatus ? '是' : '否'}`);
console.log(` 自动预览: ${config.autoPreview ? '是' : '否'}\n`);
}
/**
* 主函数:交互式配置流程
*/
export async function runFormatConfig() {
console.log('🎨 微信格式化配置器\n');
// 1. 读取当前配置
const currentConfig = loadCurrentConfig();
if (!currentConfig) {
process.exit(1);
}
displayCurrentConfig(currentConfig);
// 2. 确认是否要修改
const { shouldModify } = await inquirer.prompt([
{
type: 'confirm',
name: 'shouldModify',
message: '是否要修改配置?',
default: true,
},
]);
if (!shouldModify) {
console.log('✓ 已取消');
return;
}
// 3. 主题选择
const { theme } = await inquirer.prompt([
{
type: 'list',
name: 'theme',
message: '选择主题',
choices: themeOptions.map(opt => ({
value: opt.value,
name: `${opt.name} - ${opt.description}`,
})),
default: currentConfig.theme,
},
]);
// 4. 主题色选择
const colorChoices = [
...colorPresets.map(opt => ({
value: opt.value,
name: `${opt.name} (${opt.description})`,
})),
{
value: 'custom',
name: '自定义(输入十六进制)',
},
];
let { primaryColor } = await inquirer.prompt([
{
type: 'list',
name: 'primaryColor',
message: '选择主题色',
choices: colorChoices,
default: currentConfig.primaryColor,
},
]);
// 如果选择自定义颜色
if (primaryColor === 'custom') {
let customColor = '';
let isValid = false;
while (!isValid) {
const result = await inquirer.prompt([
{
type: 'input',
name: 'customColor',
message: '输入十六进制颜色值(如 #3f51b5)',
default: currentConfig.primaryColor,
},
]);
customColor = result.customColor;
if (isValidHexColor(customColor)) {
isValid = true;
primaryColor = customColor;
}
else {
console.log('❌ 无效的颜色格式,请使用 #RRGGBB 格式');
}
}
}
// 5. 字号选择
const { fontSize } = await inquirer.prompt([
{
type: 'list',
name: 'fontSize',
message: '选择字号',
choices: fontSizeOptions.map(opt => ({
value: opt.value,
name: `${opt.name} (${opt.description})`,
})),
default: currentConfig.fontSize,
},
]);
// 6. 字体选择
const { fontFamily } = await inquirer.prompt([
{
type: 'list',
name: 'fontFamily',
message: '选择字体',
choices: fontFamilyOptions.map(opt => ({
value: opt.value,
name: `${opt.name} - ${opt.description}`,
})),
default: currentConfig.fontFamily,
},
]);
// 7. 开关选项
const answers = await inquirer.prompt([
{
type: 'confirm',
name: 'isUseIndent',
message: '段落首行缩进?',
default: currentConfig.isUseIndent,
},
{
type: 'confirm',
name: 'isUseJustify',
message: '文本两端对齐?',
default: currentConfig.isUseJustify,
},
{
type: 'confirm',
name: 'isShowLineNumber',
message: '代码块显示行号?',
default: currentConfig.isShowLineNumber,
},
{
type: 'confirm',
name: 'citeStatus',
message: '链接转为脚注?',
default: currentConfig.citeStatus,
},
{
type: 'confirm',
name: 'autoPreview',
message: '自动打开浏览器预览?',
default: currentConfig.autoPreview,
},
]);
const { isUseIndent, isUseJustify, isShowLineNumber, citeStatus, autoPreview } = answers;
// 8. 构建新配置
const newConfig = {
theme,
primaryColor,
fontSize,
fontFamily,
isUseIndent,
isUseJustify,
isShowLineNumber,
citeStatus,
autoPreview,
};
// 9. 显示新配置并确认
console.log('\n✨ 新配置预览:');
displayCurrentConfig(newConfig);
const { confirmSave } = await inquirer.prompt([
{
type: 'confirm',
name: 'confirmSave',
message: '确认保存此配置?',
default: true,
},
]);
if (!confirmSave) {
console.log('✓ 已取消保存');
return;
}
// 10. 保存配置
if (saveConfig(newConfig)) {
console.log('\n✓ 配置已保存到 .content/config.json');
console.log('✓ 下次运行 /publish 时将使用新配置');
// 11. 可选:生成预览
if (newConfig.autoPreview) {
console.log('\n📝 提示: autoPreview 已启用');
console.log(' 下次运行 /publish wechat 时会自动打开浏览器预览');
}
// 12. 提示下一步操作
console.log('\n💡 下一步:');
console.log(' 1. 运行 /publish wechat 生成格式化 HTML');
console.log(' 2. 在浏览器中预览效果');
console.log(' 3. 满意后复制到微信公众号编辑器\n');
}
else {
process.exit(1);
}
}
/**
* 保存当前配置为预设
*/
export async function savePreset() {
console.log('💾 保存配置预设\n');
const currentConfig = loadCurrentConfig();
if (!currentConfig) {
process.exit(1);
}
displayCurrentConfig(currentConfig);
const { presetName } = await inquirer.prompt([
{
type: 'input',
name: 'presetName',
message: '为此预设命名(如 tech, life, design):',
validate: (input) => {
if (!input.trim()) {
return '预设名称不能为空';
}
if (!/^[a-z0-9_-]+$/i.test(input)) {
return '预设名称只能包含字母、数字、下划线和连字符';
}
return true;
},
},
]);
const presetPath = path.join(process.cwd(), '.content', `config-${presetName}.json`);
if (fs.existsSync(presetPath)) {
const { overwrite } = await inquirer.prompt([
{
type: 'confirm',
name: 'overwrite',
message: `预设 "${presetName}" 已存在,是否覆盖?`,
default: false,
},
]);
if (!overwrite) {
console.log('✓ 已取消保存');
return;
}
}
try {
const configContent = fs.readFileSync(path.join(process.cwd(), '.content', 'config.json'), 'utf-8');
fs.writeFileSync(presetPath, configContent);
console.log(`\n✓ 已保存预设: ${presetName}`);
console.log(`✓ 文件位置: .content/config-${presetName}.json\n`);
}
catch (error) {
console.error('❌ 保存预设失败:', error);
process.exit(1);
}
}
/**
* 加载已保存的预设
*/
export async function loadPreset() {
console.log('📂 加载配置预设\n');
const contentDir = path.join(process.cwd(), '.content');
if (!fs.existsSync(contentDir)) {
console.error('❌ 错误: 未找到 .content/ 目录');
process.exit(1);
}
// 查找所有预设文件
const files = fs.readdirSync(contentDir);
const presetFiles = files.filter(f => f.startsWith('config-') && f.endsWith('.json') && f !== 'config.json');
if (presetFiles.length === 0) {
console.log('暂无保存的预设。');
console.log('提示: 使用 /format-config --save-preset 保存当前配置为预设\n');
return;
}
console.log(`找到 ${presetFiles.length} 个预设:\n`);
const presetChoices = presetFiles.map(f => {
const presetName = f.replace('config-', '').replace('.json', '');
return {
value: f,
name: presetName,
};
});
const { selectedPreset } = await inquirer.prompt([
{
type: 'list',
name: 'selectedPreset',
message: '选择要加载的预设:',
choices: [
...presetChoices,
{ value: 'cancel', name: '取消' },
],
},
]);
if (selectedPreset === 'cancel') {
console.log('✓ 已取消');
return;
}
// 读取预设内容
const presetPath = path.join(contentDir, selectedPreset);
const presetContent = fs.readFileSync(presetPath, 'utf-8');
const presetConfig = JSON.parse(presetContent);
console.log('\n预设配置:');
displayCurrentConfig(presetConfig.formatting);
const { confirmLoad } = await inquirer.prompt([
{
type: 'confirm',
name: 'confirmLoad',
message: '确认加载此预设?',
default: true,
},
]);
if (!confirmLoad) {
console.log('✓ 已取消加载');
return;
}
// 将预设内容写入主配置文件
const configPath = path.join(contentDir, 'config.json');
fs.writeFileSync(configPath, presetContent);
console.log('\n✓ 已加载预设配置');
console.log('✓ 下次运行 /publish 时将使用此配置\n');
}
/**
* 重置配置为默认值
*/
export async function resetFormatConfig() {
console.log('🔄 重置微信格式化配置\n');
const currentConfig = loadCurrentConfig();
if (!currentConfig) {
process.exit(1);
}
console.log('⚠️ 将恢复为默认配置:');
const defaultConfig = {
theme: 'default',
primaryColor: '#3f51b5',
fontSize: '16px',
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
isUseIndent: false,
isUseJustify: false,
isShowLineNumber: false,
citeStatus: true,
autoPreview: false,
};
displayCurrentConfig(defaultConfig);
const { confirmReset } = await inquirer.prompt([
{
type: 'confirm',
name: 'confirmReset',
message: '确认重置为默认配置?',
default: false,
},
]);
if (!confirmReset) {
console.log('✓ 已取消重置');
return;
}
if (saveConfig(defaultConfig)) {
console.log('\n✓ 已重置为默认配置');
}
else {
process.exit(1);
}
}
// 如果直接运行此文件
if (import.meta.url === `file://${process.argv[1]}`) {
// 检查命令行参数
const args = process.argv.slice(2);
if (args.includes('--reset')) {
resetFormatConfig().catch(error => {
console.error('❌ 运行出错:', error);
process.exit(1);
});
}
else if (args.includes('--save-preset')) {
savePreset().catch(error => {
console.error('❌ 运行出错:', error);
process.exit(1);
});
}
else if (args.includes('--load-preset')) {
loadPreset().catch(error => {
console.error('❌ 运行出错:', error);
process.exit(1);
});
}
else if (args.includes('--help') || args.includes('-h')) {
console.log(`
🎨 微信格式化配置器
用法:
/format-config 交互式配置样式
/format-config --reset 重置为默认配置
/format-config --save-preset 保存当前配置为预设
/format-config --load-preset 加载已保存的预设
/format-config --help 显示此帮助信息
示例:
# 启动交互式配置
/format-config
# 保存当前配置为"技术文章"预设
/format-config --save-preset
> 为此预设命名: tech
# 加载"技术文章"预设
/format-config --load-preset
> 选择: tech
详细文档: templates/commands/format-config.md
`);
}
else {
runFormatConfig().catch(error => {
console.error('❌ 运行出错:', error);
process.exit(1);
});
}
}
//# sourceMappingURL=format-config.js.map