Skip to content

Commit daa9f8f

Browse files
authored
Add the block id as an id attribute to all rendered blocks (#8)
* Add id to headings blocks * Add id to divider blocks * Add id to paragraph blocks The decision to link to the wrapper div instead of the paragraph itself was made because currently when a link to a paragraph which has children is taken on Notion, Notion highlights both the paragraph and its children. I went back and forth on this decision however so if linking to the paragraph directly proves more useful we should go for it. Linking to paragraphs directly feels more intuitive to me anyway * Add id to quote blocks * Add id to code blocks * Add id to lists blocks The decision to put the id on the list items instead of the lists themselves is a fairly obvious one to me. No idea how would one put them on the lists anyway * Add id to image blocks I put the id on the figure instead of the img tag because I think they act as one entity and linking to the whole thing makes most sense This is as opposed to lists where each item acts independently * Add id to callout blocks Same as images here, the whole thing is one unified thing * Add id to unsupported blocks
1 parent ce41bb3 commit daa9f8f

File tree

2 files changed

+47
-32
lines changed

2 files changed

+47
-32
lines changed

src/highlight/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ const HIGHLIGHTS: [&str; 14] = [
2222
"variable",
2323
];
2424

25-
pub fn highlight(lang: &Language, code: &str) -> Result<Markup> {
25+
pub fn highlight(lang: &Language, code: &str, id: &str) -> Result<Markup> {
2626
// This converts the language because to serde_json::Value and since we are confident
2727
// that it's a Value of variant `Value::String` we call .as_str to get the content of
2828
// the string.
@@ -57,7 +57,7 @@ pub fn highlight(lang: &Language, code: &str) -> Result<Markup> {
5757
.context("Failed to render code")?;
5858

5959
Ok(html! {
60-
pre class=(lang_name) {
60+
pre id=(id) class=(lang_name) {
6161
code class=(lang_name) {
6262
@for line in renderer.lines() {
6363
// TreeSitter HtmlRenderer already handles escaping

src/render.rs

+45-30
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ fn render_list(
103103
let list = list.into_iter().map(|item| {
104104
if let (Some(text), Some(children)) = (item.get_text(), item.get_children()) {
105105
Ok::<_, anyhow::Error>(html! {
106-
li {
106+
li id=(item.id.replace("-", "")) {
107107
(render_rich_text(text))
108108
@for block in downloadables.extract(render_blocks(children, class)) {
109109
(block?)
@@ -141,35 +141,35 @@ fn render_block(block: &Block, class: Option<&str>) -> Result<(Markup, Downloada
141141

142142
let result = match &block.ty {
143143
BlockType::HeadingOne { text } => Ok(html! {
144-
h1 class=[class] {
144+
h1 id=(block.id.replace("-", "")) class=[class] {
145145
(render_rich_text(text))
146146
}
147147
}),
148148
BlockType::HeadingTwo { text } => Ok(html! {
149-
h2 class=[class] {
149+
h2 id=(block.id.replace("-", "")) class=[class] {
150150
(render_rich_text(text))
151151
}
152152
}),
153153
BlockType::HeadingThree { text } => Ok(html! {
154-
h3 class=[class] {
154+
h3 id=(block.id.replace("-", "")) class=[class] {
155155
(render_rich_text(text))
156156
}
157157
}),
158158
BlockType::Divider {} => Ok(html! {
159-
hr;
159+
hr id=(block.id.replace("-", ""));
160160
}),
161161
BlockType::Paragraph { text, children } => {
162162
if children.is_empty() {
163163
Ok(html! {
164-
p class=[class] {
164+
p id=(block.id.replace("-", "")) class=[class] {
165165
(render_rich_text(text))
166166
}
167167
})
168168
} else {
169169
eprintln!("WARNING: Rendering a paragraph with children doesn't make sense as far as I am aware at least for the English language.\nThe HTML spec is strictly against it (rendering a <p> inside of a <p> is forbidden) but it's part of Notion's spec so we support it but emit this warning.\n\nRendering a paragraph with children doesn't give any indication to accessibility tools that anything about the children of this paragraph are special so it causes accessibility information loss.\n\nIf you have an actual use case for paragraphs inside of paragraphs please open an issue, I would love to be convinced of reasons to remove this warning or of good HTML ways to render paragraphs inside of paragraphs!");
170170

171171
Ok(html! {
172-
div class=[class] {
172+
div id=(block.id.replace("-", "")) class=[class] {
173173
p {
174174
(render_rich_text(text))
175175
}
@@ -181,7 +181,7 @@ fn render_block(block: &Block, class: Option<&str>) -> Result<(Markup, Downloada
181181
}
182182
}
183183
BlockType::Quote { text, children } => Ok(html! {
184-
blockquote {
184+
blockquote id=(block.id.replace("-", "")) {
185185
(render_rich_text(text))
186186
@for child in downloadables.extract(render_blocks(children, Some("indent"))) {
187187
(child?)
@@ -194,12 +194,13 @@ fn render_block(block: &Block, class: Option<&str>) -> Result<(Markup, Downloada
194194
.get(0)
195195
.context("Code block's RichText is empty")?
196196
.plain_text,
197+
&block.id.replace("-", ""),
197198
),
198199
// The list items should only be reachable below if a block wasn't coalesced, thus it's
199200
// a list made of one item so we can safely render a list of one item
200201
BlockType::BulletedListItem { text, children } => Ok(html! {
201202
ul {
202-
li {
203+
li id=(block.id.replace("-", "")) {
203204
(render_rich_text(text))
204205
@for child in downloadables.extract(render_blocks(children, Some("indent"))) {
205206
(child?)
@@ -209,7 +210,7 @@ fn render_block(block: &Block, class: Option<&str>) -> Result<(Markup, Downloada
209210
}),
210211
BlockType::NumberedListItem { text, children } => Ok(html! {
211212
ol {
212-
li {
213+
li id=(block.id.replace("-", "")) {
213214
(render_rich_text(text))
214215
@for child in downloadables.extract(render_blocks(children, Some("indent"))) {
215216
(child?)
@@ -229,7 +230,7 @@ fn render_block(block: &Block, class: Option<&str>) -> Result<(Markup, Downloada
229230
// Lack of alt text can be explained here
230231
// https://stackoverflow.com/a/58468470/3018913
231232
html! {
232-
figure {
233+
figure id=(block.id.replace("-", "")) {
233234
img src=(src);
234235
figcaption {
235236
(caption)
@@ -240,7 +241,7 @@ fn render_block(block: &Block, class: Option<&str>) -> Result<(Markup, Downloada
240241
eprintln!("WARNING: Rendering image without caption text is not accessibility friendly for users who use screen readers");
241242

242243
html! {
243-
img src=(src);
244+
img id=(block.id.replace("-", "")) src=(src);
244245
}
245246
};
246247

@@ -261,7 +262,7 @@ fn render_block(block: &Block, class: Option<&str>) -> Result<(Markup, Downloada
261262
emoji::lookup_by_glyph::lookup(&emoji.emoji).map(|emoji| emoji.name);
262263

263264
Ok(html! {
264-
figure class="callout" {
265+
figure id=(block.id.replace("-", "")) class="callout" {
265266
div {
266267
span role="img" aria-label=[label] {
267268
(emoji.emoji)
@@ -283,7 +284,7 @@ fn render_block(block: &Block, class: Option<&str>) -> Result<(Markup, Downloada
283284
let src = path.to_str().unwrap();
284285

285286
let markup = html! {
286-
figure class="callout" {
287+
figure id=(block.id.replace("-", "")) class="callout" {
287288
div {
288289
img src=(src);
289290
}
@@ -303,7 +304,7 @@ fn render_block(block: &Block, class: Option<&str>) -> Result<(Markup, Downloada
303304
}
304305
}
305306
_ => Ok(html! {
306-
h4 style="color: red;" class=[class] {
307+
h4 id=(block.id.replace("-", "")) style="color: red;" class=[class] {
307308
"UNSUPPORTED FEATURE: " (block.name())
308309
}
309310
}),
@@ -448,7 +449,7 @@ mod tests {
448449
.unwrap();
449450
assert_eq!(
450451
markup,
451-
r#"<h4 style="color: red;">UNSUPPORTED FEATURE: table_of_contents</h4>"#
452+
r#"<h4 id="eb39a20e10364469b750a9df8f4f18df" style="color: red;">UNSUPPORTED FEATURE: table_of_contents</h4>"#
452453
);
453454
assert_eq!(downloadables, vec![]);
454455
}
@@ -484,7 +485,10 @@ mod tests {
484485
let (markup, downloadables) = render_block(&block, None)
485486
.map(|(markup, downloadables)| (markup.into_string(), downloadables.list))
486487
.unwrap();
487-
assert_eq!(markup, "<h1>Cool test</h1>");
488+
assert_eq!(
489+
markup,
490+
r#"<h1 id="8cac60c274b9408cacbd0895cfd7b7f8">Cool test</h1>"#
491+
);
488492
assert_eq!(downloadables, vec![]);
489493

490494
let block = Block {
@@ -516,7 +520,10 @@ mod tests {
516520
let (markup, downloadables) = render_block(&block, None)
517521
.map(|(markup, downloadables)| (markup.into_string(), downloadables.list))
518522
.unwrap();
519-
assert_eq!(markup, "<h2>Cooler test</h2>");
523+
assert_eq!(
524+
markup,
525+
r#"<h2 id="8042c69c49e7420ba49839b9d61c43d0">Cooler test</h2>"#
526+
);
520527
assert_eq!(downloadables, vec![]);
521528

522529
let block = Block {
@@ -548,7 +555,10 @@ mod tests {
548555
let (markup, downloadables) = render_block(&block, None)
549556
.map(|(markup, downloadables)| (markup.into_string(), downloadables.list))
550557
.unwrap();
551-
assert_eq!(markup, "<h3>Coolest test</h3>");
558+
assert_eq!(
559+
markup,
560+
r#"<h3 id="7f54fffa61084a49b8e9587afe7ac08f">Coolest test</h3>"#
561+
);
552562
assert_eq!(downloadables, vec![]);
553563
}
554564

@@ -567,7 +577,7 @@ mod tests {
567577
let (markup, downloadables) = render_block(&block, None)
568578
.map(|(markup, downloadables)| (markup.into_string(), downloadables.list))
569579
.unwrap();
570-
assert_eq!(markup, "<hr>");
580+
assert_eq!(markup, r#"<hr id="5e845049255f423296fd6f20449be0bc">"#);
571581
assert_eq!(downloadables, vec![]);
572582
}
573583

@@ -603,7 +613,10 @@ mod tests {
603613
let (markup, downloadables) = render_block(&block, None)
604614
.map(|(markup, downloadables)| (markup.into_string(), downloadables.list))
605615
.unwrap();
606-
assert_eq!(markup, "<p>Cool test</p>");
616+
assert_eq!(
617+
markup,
618+
r#"<p id="64740ca63a0646948845401688334ef5">Cool test</p>"#
619+
);
607620
assert_eq!(downloadables, vec![]);
608621

609622
let block = Block {
@@ -701,7 +714,7 @@ mod tests {
701714
.unwrap();
702715
assert_eq!(
703716
markup,
704-
r#"<div><p>Or you can just leave an empty line in between if you want it to leave extra breathing room.</p><div class="indent"><p>You can also create these rather interesting nested paragraphs</p><p class="indent">Possibly more than once too!</p></div></div>"#
717+
r#"<div id="4f2efd79ae9a4684827c6b69743d6c5d"><p>Or you can just leave an empty line in between if you want it to leave extra breathing room.</p><div id="4fb9dd792fc745b1b3a28efae49992ed" class="indent"><p>You can also create these rather interesting nested paragraphs</p><p id="817c0ca1721a4565ac54eedbbe471f0b" class="indent">Possibly more than once too!</p></div></div>"#
705718
);
706719
assert_eq!(downloadables, vec![]);
707720
}
@@ -743,7 +756,8 @@ mod tests {
743756
.unwrap();
744757
assert_eq!(
745758
markup,
746-
"<blockquote>If you think you can do a thing or think you can’t do a thing, you’re right.\n—Henry Ford</blockquote>"
759+
r#"<blockquote id="191b3d44a37f40c4bb4f3477359022fd">If you think you can do a thing or think you can’t do a thing, you’re right.
760+
—Henry Ford</blockquote>"#
747761
);
748762
assert_eq!(downloadables, vec![]);
749763
}
@@ -785,7 +799,8 @@ mod tests {
785799
.unwrap();
786800
assert_eq!(
787801
markup,
788-
r#"<pre class="rust"><code class="rust">"#.to_string()
802+
r#"<pre id="bf0128fd3b854d85aadae500dcbcda35" class="rust"><code class="rust">"#
803+
.to_string()
789804
+ r#"<span class="keyword">struct</span> <span class="type">Magic</span><span class="punctuation">&lt;</span><span class="type">T</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span>"#
790805
+ "\n"
791806
+ r#" <span class="variable">value</span>: <span class="type">T</span>"#
@@ -952,7 +967,7 @@ mod tests {
952967
.unwrap();
953968
assert_eq!(
954969
markup,
955-
r#"<ul><li>This is some cool list<ol><li>It can even contain other lists inside of it<ul><li>And those lists can contain OTHER LISTS!<ol class="indent"><li>Listception</li><li>Listception</li></ol></li></ul></li></ol></li></ul>"#
970+
r#"<ul><li id="844b3fdf56884f6c91e897b4f0e436cd">This is some cool list<ol><li id="c3e9c471d4b347dcab6a6ecd4dda161a">It can even contain other lists inside of it<ul><li id="55d7294249f649f98adee3d049f682e5">And those lists can contain OTHER LISTS!<ol class="indent"><li id="100116e20a4749038b794ac9cc3a7870">Listception</li><li id="c1a5555a8359499980dc10241d262071">Listception</li></ol></li></ul></li></ol></li></ul>"#
956971
);
957972
assert_eq!(downloadables, vec![]);
958973
}
@@ -1023,8 +1038,8 @@ mod tests {
10231038
assert_eq!(
10241039
markup,
10251040
vec![
1026-
r#"<figure><img src="media/5ac94d7e-25de-4fa3-a781-0a43aac9d5c4.png"><figcaption>Circle rendered in Bevy</figcaption></figure>"#,
1027-
r#"<img src="media/d1e5e2c5-4351-4b8e-83a3-20ef532967a7">"#
1041+
r#"<figure id="5ac94d7e25de4fa3a7810a43aac9d5c4"><img src="media/5ac94d7e-25de-4fa3-a781-0a43aac9d5c4.png"><figcaption>Circle rendered in Bevy</figcaption></figure>"#,
1042+
r#"<img id="d1e5e2c543514b8e83a320ef532967a7" src="media/d1e5e2c5-4351-4b8e-83a3-20ef532967a7">"#
10281043
]
10291044
);
10301045
assert_eq!(
@@ -1154,9 +1169,9 @@ mod tests {
11541169
assert_eq!(
11551170
markup,
11561171
vec![
1157-
r#"<figure class="callout"><div><span role="img" aria-label="warning">⚠️</span></div><div>Some really spooky callout.</div></figure>"#,
1158-
r#"<figure class="callout"><div><img src="media/28c719a3-9845-4f08-9e87-1fe78e50e92b.gif"></div><div>Some really spooky callout.</div></figure>"#,
1159-
r#"<figure class="callout"><div><img src="media/66ea7370-1a3b-4f4e-ada5-3be2f7e6ef73"></div><div>Some really spooky callout.</div></figure>"#
1172+
r#"<figure id="b7363fedd7cd4abaa86ff51763f4ce91" class="callout"><div><span role="img" aria-label="warning">⚠️</span></div><div>Some really spooky callout.</div></figure>"#,
1173+
r#"<figure id="28c719a398454f089e871fe78e50e92b" class="callout"><div><img src="media/28c719a3-9845-4f08-9e87-1fe78e50e92b.gif"></div><div>Some really spooky callout.</div></figure>"#,
1174+
r#"<figure id="66ea73701a3b4f4eada53be2f7e6ef73" class="callout"><div><img src="media/66ea7370-1a3b-4f4e-ada5-3be2f7e6ef73"></div><div>Some really spooky callout.</div></figure>"#
11601175
]
11611176
);
11621177
assert_eq!(

0 commit comments

Comments
 (0)