diff --git a/skills/baoyu-post-to-wechat/scripts/wechat-article.ts b/skills/baoyu-post-to-wechat/scripts/wechat-article.ts index f6f05d4..0c8d211 100644 --- a/skills/baoyu-post-to-wechat/scripts/wechat-article.ts +++ b/skills/baoyu-post-to-wechat/scripts/wechat-article.ts @@ -315,6 +315,71 @@ async function pressDeleteKey(session: ChromeSession): Promise { await session.cdp.send('Input.dispatchKeyEvent', { type: 'keyUp', key: 'Backspace', code: 'Backspace', windowsVirtualKeyCode: 8 }, { sessionId: session.sessionId }); } +async function removeExtraEmptyLineAfterImage(session: ChromeSession): Promise { + const removed = await evaluate(session, ` + (function() { + const editor = document.querySelector('.ProseMirror'); + if (!editor) return false; + + const sel = window.getSelection(); + if (!sel || sel.rangeCount === 0) return false; + + let node = sel.anchorNode; + if (!node) return false; + let element = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement; + if (!element || !editor.contains(element)) return false; + + const isEmptyParagraph = (el) => { + if (!el || el.tagName !== 'P') return false; + const text = (el.textContent || '').trim(); + if (text.length > 0) return false; + return el.querySelectorAll('img, figure, video, iframe').length === 0; + }; + + const hasImage = (el) => { + if (!el) return false; + return !!el.querySelector('img, figure img, picture img'); + }; + + const placeCursorAfter = (el) => { + if (!el) return; + const range = document.createRange(); + range.setStartAfter(el); + range.collapse(true); + sel.removeAllRanges(); + sel.addRange(range); + }; + + // Case 1: caret is inside an empty paragraph right after an image block. + const emptyPara = element.closest('p'); + if (emptyPara && editor.contains(emptyPara) && isEmptyParagraph(emptyPara)) { + const prev = emptyPara.previousElementSibling; + if (prev && hasImage(prev)) { + emptyPara.remove(); + placeCursorAfter(prev); + return true; + } + } + + // Case 2: caret is on the image block itself; remove the next empty paragraph. + const imageBlock = element.closest('figure, p'); + if (imageBlock && editor.contains(imageBlock) && hasImage(imageBlock)) { + const next = imageBlock.nextElementSibling; + if (next && isEmptyParagraph(next)) { + next.remove(); + placeCursorAfter(imageBlock); + return true; + } + } + + return false; + })() + `); + + if (removed) console.log('[wechat] Removed extra empty line after image.'); + return removed; +} + export async function postArticle(options: ArticleOptions): Promise { const { title, content, htmlFile, markdownFile, theme, author, summary, images = [], submit = false, profileDir, cdpPort } = options; let { contentImages = [] } = options; @@ -454,11 +519,6 @@ export async function postArticle(options: ArticleOptions): Promise { await evaluate(session, `document.querySelector('#author').value = ${JSON.stringify(effectiveAuthor)}; document.querySelector('#author').dispatchEvent(new Event('input', { bubbles: true }));`); } - if (effectiveSummary) { - console.log(`[wechat] Filling summary: ${effectiveSummary}`); - await evaluate(session, `document.querySelector('#js_description').value = ${JSON.stringify(effectiveSummary)}; document.querySelector('#js_description').dispatchEvent(new Event('input', { bubbles: true }));`); - } - await sleep(500); if (effectiveTitle) { @@ -470,15 +530,6 @@ export async function postArticle(options: ArticleOptions): Promise { } } - if (effectiveSummary) { - const actualSummary = await evaluate(session, `document.querySelector('#js_description')?.value || ''`); - if (actualSummary === effectiveSummary) { - console.log('[wechat] Summary verified OK.'); - } else { - console.warn(`[wechat] Summary verification failed. Expected: "${effectiveSummary}", got: "${actualSummary}"`); - } - } - console.log('[wechat] Clicking on editor...'); await clickElement(session, '.ProseMirror'); await sleep(1000); @@ -534,6 +585,7 @@ export async function postArticle(options: ArticleOptions): Promise { console.log('[wechat] Pasting image...'); await pasteFromClipboardInEditor(session); await sleep(3000); + await removeExtraEmptyLineAfterImage(session); } console.log('[wechat] All images inserted.'); } @@ -545,6 +597,7 @@ export async function postArticle(options: ArticleOptions): Promise { await sleep(500); await pasteInEditor(session); await sleep(2000); + await removeExtraEmptyLineAfterImage(session); } } @@ -567,6 +620,30 @@ export async function postArticle(options: ArticleOptions): Promise { } } + if (effectiveSummary) { + console.log(`[wechat] Filling summary (after content paste): ${effectiveSummary}`); + await evaluate(session, ` + (function() { + const el = document.querySelector('#js_description'); + if (!el) return; + el.focus(); + el.select(); + el.value = ${JSON.stringify(effectiveSummary)}; + el.dispatchEvent(new Event('input', { bubbles: true })); + el.dispatchEvent(new Event('change', { bubbles: true })); + el.dispatchEvent(new Event('blur', { bubbles: true })); + })() + `); + await sleep(500); + + const actualSummary = await evaluate(session, `document.querySelector('#js_description')?.value || ''`); + if (actualSummary === effectiveSummary) { + console.log('[wechat] Summary verified OK.'); + } else { + console.warn(`[wechat] Summary verification failed. Expected: "${effectiveSummary}", got: "${actualSummary}"`); + } + } + console.log('[wechat] Saving as draft...'); await evaluate(session, `document.querySelector('#js_submit button').click()`); await sleep(3000);