|
| 1 | +--- |
| 2 | +export interface Props { |
| 3 | + content: string; |
| 4 | +} |
| 5 | +
|
| 6 | +const { content } = Astro.props; |
| 7 | +
|
| 8 | +function processCallouts(htmlContent: string): string { |
| 9 | + // Define callout types and mapping |
| 10 | + const CALLOUT_TYPES = "NOTE|TIP|WARNING|IMPORTANT|CAUTION"; |
| 11 | + const CALLOUT_MAP: Record<string, string> = { |
| 12 | + NOTE: "note", |
| 13 | + TIP: "tip", |
| 14 | + WARNING: "caution", |
| 15 | + IMPORTANT: "note", |
| 16 | + CAUTION: "danger", |
| 17 | + }; |
| 18 | +
|
| 19 | + // Helper function to generate Starlight aside HTML |
| 20 | + const createAside = (type: string, content: string): string => { |
| 21 | + const starlightType = CALLOUT_MAP[type.toUpperCase()] ?? "note"; |
| 22 | + return `<div class="starlight-aside starlight-aside--${starlightType}"><p class="starlight-aside__title">${type}</p><div class="starlight-aside__content"><p>${content}</p></div></div>`; |
| 23 | + }; |
| 24 | +
|
| 25 | + // Helper function to clean HTML content |
| 26 | + const cleanHtmlContent = (match: string): string => |
| 27 | + match |
| 28 | + .replace(/<blockquote[^>]*>/gi, "") |
| 29 | + .replace(/<\/blockquote>/gi, "") |
| 30 | + .replace( |
| 31 | + new RegExp(`<p>\\s*\\[!(${CALLOUT_TYPES})\\]\\s*<\\/p>`, "gi"), |
| 32 | + "", |
| 33 | + ) |
| 34 | + .replace(new RegExp(`\\[!(${CALLOUT_TYPES})\\]`, "gi"), "") |
| 35 | + .replace(/<p>(.*?)<\/p>/gs, "$1 ") |
| 36 | + .replace(/<(?!code|\/code)[^>]*>/g, "") // Preserve <code> tags |
| 37 | + .replace(/\s+/g, " ") |
| 38 | + .trim(); |
| 39 | +
|
| 40 | + // Helper function to clean markdown content |
| 41 | + const cleanMarkdownContent = (content: string): string => |
| 42 | + content |
| 43 | + .split("\n") |
| 44 | + .map((line: string) => line.replace(/^>\s*/, "").trim()) |
| 45 | + .filter((line: string) => line.length > 0) |
| 46 | + .join(" "); |
| 47 | +
|
| 48 | + let processedContent = htmlContent; |
| 49 | +
|
| 50 | + // Process blockquote-based callouts |
| 51 | + processedContent = processedContent.replace( |
| 52 | + new RegExp( |
| 53 | + `<blockquote[^>]*>[\\s\\S]*?\\[!(${CALLOUT_TYPES})\\][\\s\\S]*?<\\/blockquote>`, |
| 54 | + "gi", |
| 55 | + ), |
| 56 | + (match: string) => { |
| 57 | + const typeMatch = match.match( |
| 58 | + new RegExp(`\\[!(${CALLOUT_TYPES})\\]`, "i"), |
| 59 | + ); |
| 60 | + const type = typeMatch?.[1] ?? "NOTE"; |
| 61 | + const cleanContent = cleanHtmlContent(match); |
| 62 | + return createAside(type, cleanContent); |
| 63 | + }, |
| 64 | + ); |
| 65 | +
|
| 66 | + // Process markdown-style callouts |
| 67 | + processedContent = processedContent.replace( |
| 68 | + new RegExp( |
| 69 | + `>\\s*\\[!(${CALLOUT_TYPES})\\]\\s*\\n((?:>\\s*.*(?:\\n|$))*)`, |
| 70 | + "gim", |
| 71 | + ), |
| 72 | + (match: string, type: string, content: string) => { |
| 73 | + const cleanContent = cleanMarkdownContent(content); |
| 74 | + return createAside(type, cleanContent); |
| 75 | + }, |
| 76 | + ); |
| 77 | +
|
| 78 | + return processedContent; |
| 79 | +} |
| 80 | +
|
| 81 | +const processedContent = processCallouts(content); |
| 82 | +--- |
| 83 | + |
| 84 | +<div set:html={processedContent} /> |
0 commit comments