From 977598d5aef625fe578b0e8758bf2e5a7e44f829 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jim=20Liu=20=E5=AE=9D=E7=8E=89?= Date: Sun, 25 Jan 2026 21:05:04 -0600 Subject: [PATCH] feat(baoyu-post-to-wechat): add theme selection, HTML preview, and simplify image placeholders --- skills/baoyu-post-to-wechat/SKILL.md | 19 +++++- .../references/article-posting.md | 4 +- .../scripts/md-to-wechat.ts | 4 +- .../scripts/wechat-article.ts | 62 +++++++++---------- 4 files changed, 50 insertions(+), 39 deletions(-) diff --git a/skills/baoyu-post-to-wechat/SKILL.md b/skills/baoyu-post-to-wechat/SKILL.md index 93b3384..83eb074 100644 --- a/skills/baoyu-post-to-wechat/SKILL.md +++ b/skills/baoyu-post-to-wechat/SKILL.md @@ -56,8 +56,25 @@ npx -y bun ${SKILL_DIR}/scripts/wechat-browser.ts --title "标题" --content " ### Article (文章) +Before posting, ask user to choose a theme using AskUserQuestion: + +| Theme | Description | +|-------|-------------| +| `default` | 经典主题 - 传统排版,标题居中带底边,二级标题白字彩底 | +| `grace` | 优雅主题 - 文字阴影,圆角卡片,精致引用块 (by @brzhang) | +| `simple` | 简洁主题 - 现代极简风,不对称圆角,清爽留白 (by @okooo5km) | + +Default: `default`. If user has already specified a theme, skip the question. + +**Workflow**: + +1. Generate HTML preview and print the full `htmlPath` from JSON output so user can click to preview: ```bash -npx -y bun ${SKILL_DIR}/scripts/wechat-article.ts --markdown article.md --theme grace +npx -y bun ${SKILL_DIR}/scripts/md-to-wechat.ts article.md --theme +``` +2. Post to WeChat: +```bash +npx -y bun ${SKILL_DIR}/scripts/wechat-article.ts --markdown article.md --theme ``` ## Detailed References diff --git a/skills/baoyu-post-to-wechat/references/article-posting.md b/skills/baoyu-post-to-wechat/references/article-posting.md index 7555ec3..8204c8a 100644 --- a/skills/baoyu-post-to-wechat/references/article-posting.md +++ b/skills/baoyu-post-to-wechat/references/article-posting.md @@ -53,7 +53,7 @@ Regular paragraph with **bold** and *italic*. ## Image Handling -1. **Parse**: Images in markdown are replaced with `[[IMAGE_PLACEHOLDER_N]]` +1. **Parse**: Images in markdown are replaced with `WECHATIMGPH_N` 2. **Render**: HTML is generated with placeholders in text 3. **Paste**: HTML content is pasted into WeChat editor 4. **Replace**: For each placeholder: @@ -81,7 +81,7 @@ Claude: 3. Opens Chrome, navigates to WeChat editor 4. Pastes HTML content 5. For each image: - - Selects [[IMAGE_PLACEHOLDER_1]] + - Selects WECHATIMGPH_1 - Scrolls into view - Presses Backspace to delete - Pastes image diff --git a/skills/baoyu-post-to-wechat/scripts/md-to-wechat.ts b/skills/baoyu-post-to-wechat/scripts/md-to-wechat.ts index 26e65ee..d4d9be6 100644 --- a/skills/baoyu-post-to-wechat/scripts/md-to-wechat.ts +++ b/skills/baoyu-post-to-wechat/scripts/md-to-wechat.ts @@ -156,7 +156,7 @@ export async function convertMarkdown(markdownPath: string, options?: { title?: let imageCounter = 0; const modifiedBody = body.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, (match, alt, src) => { - const placeholder = `[[IMAGE_PLACEHOLDER_${++imageCounter}]]`; + const placeholder = `WECHATIMGPH_${++imageCounter}`; images.push({ src, placeholder }); return placeholder; }); @@ -224,7 +224,7 @@ Output JSON format: "htmlPath": "/tmp/wechat-article-images/temp-article.html", "contentImages": [ { - "placeholder": "[[IMAGE_PLACEHOLDER_1]]", + "placeholder": "WECHATIMGPH_1", "localPath": "/tmp/wechat-image/img.png", "originalPath": "imgs/image.png" } diff --git a/skills/baoyu-post-to-wechat/scripts/wechat-article.ts b/skills/baoyu-post-to-wechat/scripts/wechat-article.ts index a4ce55b..73463eb 100644 --- a/skills/baoyu-post-to-wechat/scripts/wechat-article.ts +++ b/skills/baoyu-post-to-wechat/scripts/wechat-article.ts @@ -80,6 +80,30 @@ async function pasteInEditor(session: ChromeSession): Promise { await session.cdp.send('Input.dispatchKeyEvent', { type: 'keyUp', key: 'v', code: 'KeyV', modifiers, windowsVirtualKeyCode: 86 }, { sessionId: session.sessionId }); } +async function sendCopy(cdp?: CdpConnection, sessionId?: string): Promise { + if (process.platform === 'darwin') { + spawnSync('osascript', ['-e', 'tell application "System Events" to keystroke "c" using command down']); + } else if (process.platform === 'linux') { + spawnSync('xdotool', ['key', 'ctrl+c']); + } else if (cdp && sessionId) { + await cdp.send('Input.dispatchKeyEvent', { type: 'keyDown', key: 'c', code: 'KeyC', modifiers: 2, windowsVirtualKeyCode: 67 }, { sessionId }); + await sleep(50); + await cdp.send('Input.dispatchKeyEvent', { type: 'keyUp', key: 'c', code: 'KeyC', modifiers: 2, windowsVirtualKeyCode: 67 }, { sessionId }); + } +} + +async function sendPaste(cdp?: CdpConnection, sessionId?: string): Promise { + if (process.platform === 'darwin') { + spawnSync('osascript', ['-e', 'tell application "System Events" to keystroke "v" using command down']); + } else if (process.platform === 'linux') { + spawnSync('xdotool', ['key', 'ctrl+v']); + } else if (cdp && sessionId) { + await cdp.send('Input.dispatchKeyEvent', { type: 'keyDown', key: 'v', code: 'KeyV', modifiers: 2, windowsVirtualKeyCode: 86 }, { sessionId }); + await sleep(50); + await cdp.send('Input.dispatchKeyEvent', { type: 'keyUp', key: 'v', code: 'KeyV', modifiers: 2, windowsVirtualKeyCode: 86 }, { sessionId }); + } +} + async function copyHtmlFromBrowser(cdp: CdpConnection, htmlFilePath: string): Promise { const absolutePath = path.isAbsolute(htmlFilePath) ? htmlFilePath : path.resolve(process.cwd(), htmlFilePath); const fileUrl = `file://${absolutePath}`; @@ -110,23 +134,8 @@ async function copyHtmlFromBrowser(cdp: CdpConnection, htmlFilePath: string): Pr }, { sessionId }); await sleep(300); - console.log('[wechat] Copying with CDP keyboard event...'); - const modifiers = process.platform === 'darwin' ? 4 : 2; - await cdp.send('Input.dispatchKeyEvent', { - type: 'keyDown', - key: 'c', - code: 'KeyC', - modifiers, - windowsVirtualKeyCode: 67 - }, { sessionId }); - await sleep(50); - await cdp.send('Input.dispatchKeyEvent', { - type: 'keyUp', - key: 'c', - code: 'KeyC', - modifiers, - windowsVirtualKeyCode: 67 - }, { sessionId }); + console.log('[wechat] Copying content...'); + await sendCopy(cdp, sessionId); await sleep(1000); console.log('[wechat] Closing HTML tab...'); @@ -134,23 +143,8 @@ async function copyHtmlFromBrowser(cdp: CdpConnection, htmlFilePath: string): Pr } async function pasteFromClipboardInEditor(session: ChromeSession): Promise { - console.log('[wechat] Pasting with CDP keyboard event...'); - const modifiers = process.platform === 'darwin' ? 4 : 2; - await session.cdp.send('Input.dispatchKeyEvent', { - type: 'keyDown', - key: 'v', - code: 'KeyV', - modifiers, - windowsVirtualKeyCode: 86 - }, { sessionId: session.sessionId }); - await sleep(50); - await session.cdp.send('Input.dispatchKeyEvent', { - type: 'keyUp', - key: 'v', - code: 'KeyV', - modifiers, - windowsVirtualKeyCode: 86 - }, { sessionId: session.sessionId }); + console.log('[wechat] Pasting content...'); + await sendPaste(session.cdp, session.sessionId); await sleep(1000); }