feat(publish): render jianshu html images as markdown

Jianshu's HTML editor strips uploaded <img> tags during save, so previously
uploaded images vanished from the rendered article. After image upload,
convert each <img> (and surrounding <p> wrappers) to a markdown image block
in both the desktop and extension publish paths so the references survive.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-27 11:02:50 +08:00
parent 63abe403c0
commit dbce8515e7
3 changed files with 69 additions and 2 deletions
@@ -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<PlatformPublishR
const htmlProcessed = useMarkdownMode
? null
: await uploadHtmlImages(normalizeArticleHtml(context.article), async (sourceUrl) => 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<PlatformPublishR
id: noteId,
autosave_control: 1,
title: context.article.title,
content: useMarkdownMode ? (markdownProcessed?.markdown ?? rawMarkdown) : (htmlProcessed?.html ?? ""),
content: useMarkdownMode ? (markdownProcessed?.markdown ?? rawMarkdown) : htmlContent,
}),
});
@@ -0,0 +1,25 @@
import { describe, expect, it } from "vitest";
import { renderJianshuHtmlImagesAsMarkdown } from "../../../../../packages/publisher-platforms/src/jianshu";
describe("jianshu article content", () => {
it("converts editor image paragraphs to markdown image syntax", () => {
const result = renderJianshuHtmlImagesAsMarkdown(
'<p class="article-editor-image article-editor-image--center" align="center"><img src="https://upload-images.jianshu.io/upload_images/2370478-707abbd68000b249.png" alt="" width="365" height="521" data-asset-id="14" /></p>',
);
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(
'<p>正文段落</p><img src="https://upload-images.jianshu.io/upload_images/2370478-0e05fd986e33dae9.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="微信图片_2026-04-18_141100_730.jpg">',
);
expect(result).toContain("<p>正文段落</p>");
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("<img");
});
});