feat(baoyu-post-to-wechat): internalize markdown conversion with modular renderer and color support

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jim Liu 宝玉
2026-03-01 00:01:05 -06:00
parent 226d501e9e
commit 8787cbe85b
90 changed files with 3273 additions and 847 deletions
@@ -19,6 +19,7 @@ interface ArticleOptions {
htmlFile?: string;
markdownFile?: string;
theme?: string;
color?: string;
author?: string;
summary?: string;
images?: string[];
@@ -181,12 +182,13 @@ async function pasteFromClipboardInEditor(session: ChromeSession): Promise<void>
await sleep(1000);
}
async function parseMarkdownWithPlaceholders(markdownPath: string, theme?: string): Promise<{ title: string; author: string; summary: string; htmlPath: string; contentImages: ImageInfo[] }> {
async function parseMarkdownWithPlaceholders(markdownPath: string, theme?: string, color?: string): Promise<{ title: string; author: string; summary: string; htmlPath: string; contentImages: ImageInfo[] }> {
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const mdToWechatScript = path.join(__dirname, 'md-to-wechat.ts');
const args = ['-y', 'bun', mdToWechatScript, markdownPath];
if (theme) args.push('--theme', theme);
if (color) args.push('--color', color);
const result = spawnSync('npx', args, { stdio: ['inherit', 'pipe', 'pipe'] });
if (result.status !== 0) {
@@ -381,7 +383,7 @@ async function removeExtraEmptyLineAfterImage(session: ChromeSession): Promise<b
}
export async function postArticle(options: ArticleOptions): Promise<void> {
const { title, content, htmlFile, markdownFile, theme, author, summary, images = [], submit = false, profileDir, cdpPort } = options;
const { title, content, htmlFile, markdownFile, theme, color, author, summary, images = [], submit = false, profileDir, cdpPort } = options;
let { contentImages = [] } = options;
let effectiveTitle = title || '';
let effectiveAuthor = author || '';
@@ -390,7 +392,7 @@ export async function postArticle(options: ArticleOptions): Promise<void> {
if (markdownFile) {
console.log(`[wechat] Parsing markdown: ${markdownFile}`);
const parsed = await parseMarkdownWithPlaceholders(markdownFile, theme);
const parsed = await parseMarkdownWithPlaceholders(markdownFile, theme, color);
effectiveTitle = effectiveTitle || parsed.title;
effectiveAuthor = effectiveAuthor || parsed.author;
effectiveSummary = effectiveSummary || parsed.summary;
@@ -674,6 +676,7 @@ Options:
--html <path> HTML file to paste (alternative to --content)
--markdown <path> Markdown file to convert and post (recommended)
--theme <name> Theme for markdown (default, grace, simple, modern)
--color <name|hex> Primary color (blue, green, vermilion, etc. or hex)
--author <name> Author name
--summary <text> Article summary
--image <path> Content image, can repeat (only with --content)
@@ -705,6 +708,7 @@ async function main(): Promise<void> {
let htmlFile: string | undefined;
let markdownFile: string | undefined;
let theme: string | undefined;
let color: string | undefined;
let author: string | undefined;
let summary: string | undefined;
let submit = false;
@@ -718,6 +722,7 @@ async function main(): Promise<void> {
else if (arg === '--html' && args[i + 1]) htmlFile = args[++i];
else if (arg === '--markdown' && args[i + 1]) markdownFile = args[++i];
else if (arg === '--theme' && args[i + 1]) theme = args[++i];
else if (arg === '--color' && args[i + 1]) color = args[++i];
else if (arg === '--author' && args[i + 1]) author = args[++i];
else if (arg === '--summary' && args[i + 1]) summary = args[++i];
else if (arg === '--image' && args[i + 1]) images.push(args[++i]!);
@@ -729,7 +734,7 @@ async function main(): Promise<void> {
if (!markdownFile && !htmlFile && !title) { console.error('Error: --title is required (or use --markdown/--html)'); process.exit(1); }
if (!markdownFile && !htmlFile && !content) { console.error('Error: --content, --html, or --markdown is required'); process.exit(1); }
await postArticle({ title: title || '', content, htmlFile, markdownFile, theme, author, summary, images, submit, profileDir, cdpPort });
await postArticle({ title: title || '', content, htmlFile, markdownFile, theme, color, author, summary, images, submit, profileDir, cdpPort });
}
await main().then(() => {