Skip to content

Commit

Permalink
heading anchors
Browse files Browse the repository at this point in the history
  • Loading branch information
mbostock committed Oct 10, 2023
1 parent 4b1e30d commit 30bb912
Show file tree
Hide file tree
Showing 13 changed files with 29 additions and 10 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ TODO
- implicitly await ${…} expressions before calling display?
- promote img, video, audio, and picture elements to file attachments
- add rel="nofollow noindex" to external links by default
- ✅ automatic anchor links for heading elements
- themes
- ✅ default light/dark theme
- dashboard theme for wide layout
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"gray-matter": "^4.0.3",
"highlight.js": "^11.8.0",
"markdown-it": "^13.0.2",
"markdown-it-anchor": "^8.6.7",
"mime": "^3.0.0",
"send": "^0.18.0",
"tsx": "^3.13.0",
Expand Down
4 changes: 4 additions & 0 deletions public/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,10 @@ figcaption {
color: var(--syntax-unknown-variable);
}

a[href].observablehq-header-anchor {
color: inherit;
}

.observablehq--block {
margin: 1rem 0;
}
Expand Down
12 changes: 10 additions & 2 deletions src/markdown.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import matter from "gray-matter";
import hljs from "highlight.js";
import MarkdownIt from "markdown-it";
import MarkdownItAnchor from "markdown-it-anchor";
import type {RuleCore} from "markdown-it/lib/parser_core.js";
import type {RuleInline} from "markdown-it/lib/parser_inline.js";
import type {RenderRule} from "markdown-it/lib/renderer.js";
Expand Down Expand Up @@ -197,6 +198,7 @@ export function parseMarkdown(source: string, root: string): ParseResult {
return ""; // defaults to escapeHtml(str)
}
});
md.use(MarkdownItAnchor, {permalink: MarkdownItAnchor.permalink.headerLink({class: "observablehq-header-anchor"})});
md.inline.ruler.push("placeholder", transformPlaceholderInline);
md.core.ruler.before("linkify", "placeholder", transformPlaceholderCore);
md.renderer.rules.placeholder = makePlaceholderRenderer(root);
Expand Down Expand Up @@ -225,8 +227,14 @@ function findTitle(tokens: ReturnType<MarkdownIt["parse"]>): string | undefined
for (const [i, token] of tokens.entries()) {
if (token.type === "heading_open" && token.tag === "h1") {
const next = tokens[i + 1];
if (next?.type === "inline" && next.children?.[0]?.type === "text") {
return next.content;
if (next?.type === "inline") {
const text = next.children
?.filter((t) => t.type === "text")
.map((t) => t.content)
.join("");
if (text) {
return text;
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion test/output/embedded-expression.html
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
<h1>Embedded expression</h1>
<h1 id="embedded-expression" tabindex="-1"><a class="observablehq-header-anchor" href="#embedded-expression">Embedded expression</a></h1>
<p>One plus two is <span id="cell-1"></span>.</p>
2 changes: 1 addition & 1 deletion test/output/fenced-code.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<h1>Fenced code</h1>
<h1 id="fenced-code" tabindex="-1"><a class="observablehq-header-anchor" href="#fenced-code">Fenced code</a></h1>
<div id="cell-1" class="observablehq observablehq--block"></div>
<pre><code class="language-js"><span class="hljs-keyword">function</span> <span class="hljs-title function_">add</span>(<span class="hljs-params">a, b</span>) {
<span class="hljs-keyword">return</span> a + b;
Expand Down
2 changes: 1 addition & 1 deletion test/output/heading-expression.html
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<h1><span id="cell-1"></span></h1>
<h1 id="" tabindex="-1"><a class="observablehq-header-anchor" href="#"><span id="cell-1"></span></a></h1>
2 changes: 1 addition & 1 deletion test/output/hello-world.html
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
<h1>Hello, world!</h1>
<h1 id="hello%2C-world!" tabindex="-1"><a class="observablehq-header-anchor" href="#hello%2C-world!">Hello, world!</a></h1>
<p>This is a test.</p>
2 changes: 1 addition & 1 deletion test/output/script-expression.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<h1>Script expression</h1>
<h1 id="script-expression" tabindex="-1"><a class="observablehq-header-anchor" href="#script-expression">Script expression</a></h1>
<script type="module">

const subject = "world";
Expand Down
2 changes: 1 addition & 1 deletion test/output/tex-expression.html
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
<h1>Hello, <span id="cell-1"></span></h1>
<h1 id="hello%2C" tabindex="-1"><a class="observablehq-header-anchor" href="#hello%2C">Hello, <span id="cell-1"></span></a></h1>
<p>My favorite equation is <span id="cell-2"></span>.</p>
2 changes: 1 addition & 1 deletion test/output/tex-expression.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"data": null,
"title": "Hello, ${tex`\\KaTeX`}",
"title": "Hello, ",
"files": [],
"imports": []
}
2 changes: 1 addition & 1 deletion test/output/yaml-frontmatter.html
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
<h1>YAML frontmatter</h1>
<h1 id="yaml-frontmatter" tabindex="-1"><a class="observablehq-header-anchor" href="#yaml-frontmatter">YAML frontmatter</a></h1>
<p>This page has some YAML configuration.</p>
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1322,6 +1322,11 @@ lru-cache@^6.0.0:
dependencies:
yallist "^4.0.0"

markdown-it-anchor@^8.6.7:
version "8.6.7"
resolved "https://registry.yarnpkg.com/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz#ee6926daf3ad1ed5e4e3968b1740eef1c6399634"
integrity sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==

markdown-it@^13.0.2:
version "13.0.2"
resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-13.0.2.tgz#1bc22e23379a6952e5d56217fbed881e0c94d536"
Expand Down

0 comments on commit 30bb912

Please sign in to comment.