|
1 | 1 | import matter from "gray-matter"; |
2 | 2 | import hljs from "highlight.js"; |
3 | 3 | import MarkdownIt from "markdown-it"; |
| 4 | +import MarkdownItAnchor from "markdown-it-anchor"; |
4 | 5 | import type {RuleCore} from "markdown-it/lib/parser_core.js"; |
5 | 6 | import type {RuleInline} from "markdown-it/lib/parser_inline.js"; |
6 | 7 | import type {RenderRule} from "markdown-it/lib/renderer.js"; |
@@ -197,6 +198,7 @@ export function parseMarkdown(source: string, root: string): ParseResult { |
197 | 198 | return ""; // defaults to escapeHtml(str) |
198 | 199 | } |
199 | 200 | }); |
| 201 | + md.use(MarkdownItAnchor, {permalink: MarkdownItAnchor.permalink.headerLink({class: "observablehq-header-anchor"})}); |
200 | 202 | md.inline.ruler.push("placeholder", transformPlaceholderInline); |
201 | 203 | md.core.ruler.before("linkify", "placeholder", transformPlaceholderCore); |
202 | 204 | md.renderer.rules.placeholder = makePlaceholderRenderer(root); |
@@ -225,8 +227,14 @@ function findTitle(tokens: ReturnType<MarkdownIt["parse"]>): string | undefined |
225 | 227 | for (const [i, token] of tokens.entries()) { |
226 | 228 | if (token.type === "heading_open" && token.tag === "h1") { |
227 | 229 | const next = tokens[i + 1]; |
228 | | - if (next?.type === "inline" && next.children?.[0]?.type === "text") { |
229 | | - return next.content; |
| 230 | + if (next?.type === "inline") { |
| 231 | + const text = next.children |
| 232 | + ?.filter((t) => t.type === "text") |
| 233 | + .map((t) => t.content) |
| 234 | + .join(""); |
| 235 | + if (text) { |
| 236 | + return text; |
| 237 | + } |
230 | 238 | } |
231 | 239 | } |
232 | 240 | } |
|
0 commit comments