diff --git a/apps/browser-extension/src/adapters/jianshu.ts b/apps/browser-extension/src/adapters/jianshu.ts index 09ecf99..5e91a1a 100644 --- a/apps/browser-extension/src/adapters/jianshu.ts +++ b/apps/browser-extension/src/adapters/jianshu.ts @@ -1,4 +1,5 @@ import type { StoredPlatformState } from "../platforms"; +import { renderJianshuHtmlImagesAsMarkdown } from "../../../../packages/publisher-platforms/src/jianshu"; import { articleMarkdown, @@ -146,6 +147,7 @@ async function publishJianshu(context: AdapterContext): Promise uploadImage(noteId, sourceUrl)); + const htmlContent = htmlProcessed ? renderJianshuHtmlImagesAsMarkdown(htmlProcessed.html) : ""; await fetchJson(`https://www.jianshu.com/author/notes/${noteId}`, { method: "PUT", @@ -154,7 +156,7 @@ async function publishJianshu(context: AdapterContext): Promise { + it("converts editor image paragraphs to markdown image syntax", () => { + const result = renderJianshuHtmlImagesAsMarkdown( + '

', + ); + + expect(result).toBe("![](https://upload-images.jianshu.io/upload_images/2370478-707abbd68000b249.png)"); + }); + + it("keeps normal text while replacing raw image tags", () => { + const result = renderJianshuHtmlImagesAsMarkdown( + '

正文段落

微信图片_2026-04-18_141100_730.jpg', + ); + + expect(result).toContain("

正文段落

"); + expect(result).toContain( + "![微信图片_2026-04-18_141100_730.jpg](https://upload-images.jianshu.io/upload_images/2370478-0e05fd986e33dae9.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)", + ); + expect(result).not.toContain("]+))`, "i"); + const match = matcher.exec(sourceTag); + const value = match?.[1] ?? match?.[2] ?? match?.[3] ?? ""; + const trimmed = value.trim(); + return trimmed ? trimmed : null; +} + +function escapeMarkdownAlt(value: string): string { + return value.replace(/\\/g, "\\\\").replace(/\]/g, "\\]"); +} + +function markdownImageFromHtmlTag(imgTag: string): string { + const src = extractHtmlAttribute(imgTag, "src"); + if (!src) { + return imgTag; + } + + const alt = extractHtmlAttribute(imgTag, "alt") ?? ""; + return `![${escapeMarkdownAlt(alt)}](${src})`; +} + +export function renderJianshuHtmlImagesAsMarkdown(content: string): string { + if (!/]*>\s*(]*\/?>)\s*<\/p>/gi, + (_match, imgTag: string) => `\n\n${markdownImageFromHtmlTag(imgTag)}\n\n`, + ); + + next = next.replace( + /]*\/?>/gi, + (imgTag) => `\n\n${markdownImageFromHtmlTag(imgTag)}\n\n`, + ); + + return next.replace(/\n{3,}/g, "\n\n").trim(); +} + export async function fetchJianshuMediaSnapshot( fetchJson: (input: string, init?: RequestInit) => Promise, ): Promise { @@ -265,7 +305,7 @@ async function processContentImages( for (const [from, to] of replacements.entries()) { next = next.split(from).join(to); } - return next; + return renderJianshuHtmlImagesAsMarkdown(next); } async function attachCoverImage(