feat(blog): add copyable highlighted code blocks
This commit is contained in:
52
src/lib/markdown-headings.ts
Normal file
52
src/lib/markdown-headings.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
export type MarkdownHeading = {
|
||||
id: string;
|
||||
level: 1 | 2 | 3;
|
||||
text: string;
|
||||
};
|
||||
|
||||
function plainHeadingText(value: string) {
|
||||
return value
|
||||
.replace(/`([^`]+)`/g, "$1")
|
||||
.replace(/\*\*([^*]+)\*\*/g, "$1")
|
||||
.replace(/\*([^*]+)\*/g, "$1")
|
||||
.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1")
|
||||
.replace(/<[^>]*>/g, " ")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim();
|
||||
}
|
||||
|
||||
function headingIdBase(text: string) {
|
||||
const normalized = text
|
||||
.normalize("NFKC")
|
||||
.toLowerCase()
|
||||
.replace(/[^\p{L}\p{N}\s_-]/gu, "")
|
||||
.trim()
|
||||
.replace(/\s+/g, "-")
|
||||
.slice(0, 80);
|
||||
|
||||
return normalized || "section";
|
||||
}
|
||||
|
||||
export function extractMarkdownHeadings(content?: string): MarkdownHeading[] {
|
||||
const counters = new Map<string, number>();
|
||||
|
||||
return (content || "")
|
||||
.split(/\r?\n/)
|
||||
.map((line) => {
|
||||
const match = /^(#{1,3})\s+(.+?)\s*#*$/.exec(line.trim());
|
||||
if (!match) return null;
|
||||
|
||||
const level = match[1].length as 1 | 2 | 3;
|
||||
const text = plainHeadingText(match[2]);
|
||||
const base = headingIdBase(text);
|
||||
const nextCount = (counters.get(base) || 0) + 1;
|
||||
counters.set(base, nextCount);
|
||||
|
||||
return {
|
||||
id: nextCount === 1 ? base : `${base}-${nextCount}`,
|
||||
level,
|
||||
text,
|
||||
};
|
||||
})
|
||||
.filter(Boolean) as MarkdownHeading[];
|
||||
}
|
||||
Reference in New Issue
Block a user