Skip to content

Commit 4c389e5

Browse files
refactor(migrate): diff visualizer
1 parent 2ead597 commit 4c389e5

File tree

2 files changed

+64
-10
lines changed

2 files changed

+64
-10
lines changed

migrate/migrate-bot.ts

Lines changed: 59 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
import { Octokit } from "@octokit/rest";
22
import { JSDOM } from "jsdom";
33
import fs, { readFile } from "fs/promises";
4-
import path from "path";
4+
import path, { join } from "path";
55
import { fileURLToPath } from "url";
66
import { execSync, spawnSync } from "child_process";
7+
import { visualizeTextDiff } from "./text-diff-visualizer";
78

89
const __dirname = path.dirname(fileURLToPath(import.meta.url));
910

1011
const GITHUB_TOKEN = process.env.GITHUB_TOKEN;
1112
const OPENROUTER_API_KEY = process.env.OPENROUTER_API_KEY;
13+
const IMGBB_API_KEY = process.env.IMGBB_API_KEY || "";
1214
const REPO_OWNER = process.env.GITHUB_REPOSITORY_OWNER || "owner";
1315
const REPO_NAME = process.env.GITHUB_REPOSITORY?.split("/")[1] || "cppdoc";
1416
const LABEL = "migrate-cppref-page";
@@ -51,7 +53,7 @@ function hasPRReference(title: string): boolean {
5153
return /\[#\d+\]/.test(title);
5254
}
5355

54-
async function fetchPageContent(url: string): Promise<{ html: string; title: string; url: string }> {
56+
async function fetchPageContent(url: string): Promise<{ html: string; title: string; url: string; innerText: string }> {
5557
const response = await fetch(url);
5658
if (!response.ok) {
5759
throw new Error(`Failed to fetch ${url}: ${response.status}`);
@@ -67,6 +69,7 @@ async function fetchPageContent(url: string): Promise<{ html: string; title: str
6769
html: contentElement.innerHTML,
6870
title: headingElement?.textContent?.trim() || "",
6971
url,
72+
innerText: contentElement.textContent?.trim() || "",
7073
};
7174
}
7275

@@ -196,7 +199,7 @@ ${html}
196199
}
197200

198201
// https://cppreference.com/w/cpp/comments => src/content/docs/cpp/comments.mdx
199-
function getRelativePath(url: string): string {
202+
function getRelativeMDXPath(url: string): string {
200203
const match = url.match(/https?:\/\/.*?cppreference\.com\/w\/(.+)\.html$/);
201204
if (!match) {
202205
throw new Error(`无法从URL解析路径: ${url}`);
@@ -205,10 +208,19 @@ function getRelativePath(url: string): string {
205208
return `src/content/docs/${relative}.mdx`;
206209
}
207210

208-
function getLocalPath(url: string): string {
211+
function getRelativeHTMLPath(url: string): string {
212+
const match = url.match(/https?:\/\/.*?cppreference\.com\/w\/(.+)\.html$/);
213+
if (!match) {
214+
throw new Error(`无法从URL解析路径: ${url}`);
215+
}
216+
const relative = match[1]; // "cpp/comments"
217+
return `dist/${relative}/index.html`;
218+
}
219+
220+
function getLocalMDXPath(url: string): string {
209221
return path.join(
210222
__dirname,
211-
"..", getRelativePath(url)
223+
"..", getRelativeMDXPath(url)
212224
);
213225
}
214226

@@ -223,17 +235,54 @@ description: Auto‑generated from cppreference
223235
console.log(`写入 ${filePath}`);
224236
}
225237

226-
async function createPullRequest(issue: { number: number; title: string }, filePath: string, url: string): Promise<number> {
238+
// curl --location --request POST "https://api.imgbb.com/1/upload?expiration=600&key=YOUR_CLIENT_API_KEY" --form "image=R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
239+
async function uploadImageToImgBB(imageBuffer: Buffer): Promise<string> {
240+
const formData = new FormData();
241+
formData.append("image", new Blob([new Uint8Array(imageBuffer)]), "diff.svg");
242+
243+
const response = await fetch(`https://api.imgbb.com/1/upload?expiration=600&key=${IMGBB_API_KEY}`, {
244+
method: "POST",
245+
body: formData,
246+
});
247+
248+
if (!response.ok) {
249+
const error = await response.text();
250+
throw new Error(`ImgBB API error: ${error}`);
251+
}
252+
253+
const data = await response.json() as { data: { url: string } };
254+
return data.data.url;
255+
}
256+
257+
async function createPullRequest(issue: { number: number; title: string }, filePath: string, url: string, originalInnerText: string): Promise<number> {
227258
const branchName = `migrate/${issue.number}-${Date.now().toString(36)}`;
228259
const page = url.split("/w/").pop();
229260
const pageName = page ? page.replace(".html", "") : "unknown";
230261
const prTitle = `feat: migrate ${pageName} from cppref [#${issue.number}]`;
231262
const commitMessage = prTitle;
263+
264+
const newInnerText = await readFile(getRelativeHTMLPath(url), "utf8").then((data) => {
265+
const dom = new JSDOM(data);
266+
const contentElement = dom.window.document.querySelector("main");
267+
return contentElement?.textContent?.trim() || "";
268+
}).catch(() => "");
269+
270+
let imageUrl = null
271+
if (originalInnerText && newInnerText) {
272+
const svg = visualizeTextDiff(originalInnerText, newInnerText);
273+
if (svg) {
274+
imageUrl = await uploadImageToImgBB(svg);
275+
console.log(`上传文本差异图像到 ImgBB: ${imageUrl}`);
276+
}
277+
}
278+
232279
const prBody = `> 由 ${MODEL_NAME}${url} 自动迁移
233280
>
234-
> 📝 [编辑此页面](${getRelativePath(url)})
281+
> 📝 [编辑此页面](${getRelativeMDXPath(url)})
235282
236283
<small>Close #${issue.number}</small>
284+
285+
${imageUrl ? `![Text Diff](${imageUrl})` : "(无文本差异图像)"}
237286
`;
238287

239288
const { execSync } = await import("child_process");
@@ -322,12 +371,12 @@ async function main() {
322371
}
323372

324373
console.log(` 获取 ${url}`);
325-
const { html, title } = await retry(() => fetchPageContent(url), 3, 2000);
374+
const { html, title, innerText } = await retry(() => fetchPageContent(url), 3, 2000);
326375

327376
console.log(` 转换HTML为MDX...`);
328377
const mdx = await retry(() => convertToMDX(html, title, url), 3, 2000);
329378

330-
const filePath = getLocalPath(url);
379+
const filePath = getLocalMDXPath(url);
331380
console.log(` 写入 ${filePath}`);
332381
await writeMDXFile(filePath, mdx, title);
333382

@@ -338,7 +387,7 @@ async function main() {
338387
}
339388

340389
console.log(` 创建PR...`);
341-
const prNumber = await createPullRequest(issue, filePath, url);
390+
const prNumber = await createPullRequest(issue, filePath, url, innerText);
342391

343392
console.log(` 更新issue...`);
344393
await updateIssue(issue, prNumber);

migrate/text-diff-visualizer.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,3 +351,8 @@ export function visualizeTextDiff(textA: string, textB: string) {
351351
const buffer = canvas.toBuffer();
352352
return buffer
353353
}
354+
355+
356+
const svg = visualizeTextDiff("This is a sample text.\nIt has multiple lines.\nSome words are different.", "This is a sample text.\nIt has several lines.\nSome words differ.");
357+
import { writeFileSync } from 'fs';
358+
writeFileSync('text-diff.svg', svg);

0 commit comments

Comments
 (0)