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:
@@ -127,6 +127,46 @@ function parseJianshuErrorMessage(error: unknown, fallback: string): string {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
function extractHtmlAttribute(sourceTag: string, attributeName: string): string | null {
|
||||
const matcher = new RegExp(`\\s${attributeName}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|([^\\s"'>]+))`, "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 ``;
|
||||
}
|
||||
|
||||
export function renderJianshuHtmlImagesAsMarkdown(content: string): string {
|
||||
if (!/<img\b/i.test(content)) {
|
||||
return content;
|
||||
}
|
||||
|
||||
let next = content.replace(
|
||||
/<p\b[^>]*>\s*(<img\b[^>]*\/?>)\s*<\/p>/gi,
|
||||
(_match, imgTag: string) => `\n\n${markdownImageFromHtmlTag(imgTag)}\n\n`,
|
||||
);
|
||||
|
||||
next = next.replace(
|
||||
/<img\b[^>]*\/?>/gi,
|
||||
(imgTag) => `\n\n${markdownImageFromHtmlTag(imgTag)}\n\n`,
|
||||
);
|
||||
|
||||
return next.replace(/\n{3,}/g, "\n\n").trim();
|
||||
}
|
||||
|
||||
export async function fetchJianshuMediaSnapshot(
|
||||
fetchJson: <T>(input: string, init?: RequestInit) => Promise<T>,
|
||||
): Promise<JianshuMediaSnapshot | null> {
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user