1import type { PullRequestBodySegment } from "@t3tools/client-runtime/pull-request-markdown";
2import type { MarkdownNode } from "../../lib/markdownDocument";
3
4function isWebUrl(url: string): boolean {
5 try {
6 const parsed = new URL(url);
7 return parsed.protocol === "https:" || parsed.protocol === "http:";
8 } catch {
9 return false;
10 }
11}
12
13export function pullRequestDescriptionContent(
14 document: MarkdownNode,
15 media: readonly PullRequestBodySegment[] = [],
16) {
17 const attachments = new Map<
18 string,
19 { readonly uri: string; readonly caption: string; readonly kind: "image" | "video" | "unknown" }
20 >();
21 const text: string[] = [];
22 const visit = (node: MarkdownNode) => {
23 if (node.type === "image") {
24 if (node.url && isWebUrl(node.url) && !attachments.has(node.url)) {
25 attachments.set(node.url, {
26 uri: node.url,
27 caption: node.alt || node.title || "Image",
28 kind: "image",
29 });
30 }
31 return;
32 }
33 if (node.type === "html") return;
34 if ("value" in node) text.push(node.value);
35 if ("children" in node) node.children.forEach(visit);
36 if (["paragraph", "heading", "listItem", "code"].includes(node.type)) text.push("\n");
37 if (node.type === "break") text.push(" ");
38 };
39 visit(document);
40 const collect = (segment: PullRequestBodySegment) => {
41 if (segment.kind === "table") {
42 segment.rows.flat().forEach((cell) => cell.segments.forEach(collect));
43 return;
44 }
45 if (segment.kind === "attachment" && !attachments.has(segment.url)) {
46 const name = new URL(segment.url).pathname.split("/").pop();
47 attachments.set(segment.url, {
48 uri: segment.url,
49 caption: segment.media === "video" ? "Video" : name || "Image",
50 kind: segment.media,
51 });
52 }
53 };
54 media.forEach(collect);
55 return { preview: text.join("").trim(), attachments: [...attachments.values()] };
56}