Skip to content

Commit 364ee3b

Browse files
authored
fix: place code annotation highlight bands from the line positions (#13429)
Selecting a code annotation drew a band that spilled out to the left of the code block and sat off its lines (regression introduced in v1.8.15). Both bands were placed from offsetTop, which is relative to the code element, while each band is positioned against a different containing block. They are now placed from the line client rects, converted to the CSS pixels of their own container (Reveal.js scales slides) and corrected for the container's scroll, and the band spans the code block. Playwright tests measure the bands in html, dashboard and revealjs, with and without line numbers, without code copy, and inside a scrolled container. Closes #13448
1 parent cd95d21 commit 364ee3b

11 files changed

Lines changed: 441 additions & 57 deletions

‎news/changelog-1.11.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ All changes included in 1.11:
7171
- ([#12650](https://github.com/quarto-dev/quarto-cli/issues/12650)): Fix markdown formatting and HTML entities in a document title appearing literally in the appendix citation, the BibTeX citation, and the Google Scholar `citation_title`. (author: @jolars)
7272
- ([#13444](https://github.com/quarto-dev/quarto-cli/issues/13444)): Fix the code copy button covering the code annotation marker on the first line of an annotated code block, which stopped the annotation from showing on hover or click (regression introduced in v1.8.15).
7373
- ([#13446](https://github.com/quarto-dev/quarto-cli/issues/13446)): Fix the code copy button sitting above the code frame, and the extra space around the code, when a code block starts or ends a callout (regression introduced in v1.8.15).
74+
- ([#13448](https://github.com/quarto-dev/quarto-cli/issues/13448)): Fix the code annotation line highlight spilling out to the left of the code block, and sitting above the gutter highlight, in `html`, `dashboard` and `revealjs` output (regression introduced in v1.8.15). (author: @mcanouil)
7475
- ([#14684](https://github.com/quarto-dev/quarto-cli/issues/14684)): Add a "Skip to main content" link to Bootstrap-themed HTML output (documents, websites, books, dashboards) so keyboard users can bypass the navbar and sidebars.
7576
- ([#14973](https://github.com/quarto-dev/quarto-cli/issues/14973)): Fix Safari sometimes opening pages with a light and dark theme at the top instead of at the URL fragment target, and toggling the color scheme scrolling back to the top of the page.
7677
- ([#14976](https://github.com/quarto-dev/quarto-cli/pull/14976)): Fix KaTeX math not rendering on pages with Jupyter widgets when `html-math-method` is set with the object form (`method: katex` and `url`).

‎src/resources/formats/html/bootstrap/_bootstrap-rules.scss‎

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2163,6 +2163,7 @@ pre.code-annotation-code {
21632163
}
21642164
}
21652165

2166+
#code-annotation-line-highlight,
21662167
#code-annotation-line-highlight-gutter {
21672168
width: 100%;
21682169
border-top: solid $code-annotation-higlight-color 1px;
@@ -2171,15 +2172,6 @@ pre.code-annotation-code {
21712172
background-color: $code-annotation-higlight-bg;
21722173
}
21732174

2174-
#code-annotation-line-highlight {
2175-
margin-left: -4em;
2176-
width: calc(100% + 4em);
2177-
border-top: solid $code-annotation-higlight-color 1px;
2178-
border-bottom: solid $code-annotation-higlight-color 1px;
2179-
z-index: 2;
2180-
background-color: $code-annotation-higlight-bg;
2181-
}
2182-
21832175
code.sourceCode .code-annotation-anchor.code-annotation-active {
21842176
background-color: var(--quarto-hl-normal-color, #aaaaaa);
21852177
border: solid var(--quarto-hl-normal-color, #aaaaaa) 1px;

‎src/resources/formats/html/templates/quarto-html-after-body.ejs‎

Lines changed: 49 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -572,48 +572,58 @@
572572
return targetCell + "-" + line;
573573
})
574574
575-
let top = null;
576-
let height = null;
577-
let parent = null;
575+
// Each band is positioned against its own containing block, which is
576+
// not the offsetParent of the lines, so place it from client rects.
577+
const placeBand = (band, firstEl, lastEl, codeBlock) => {
578+
const container = band.offsetParent;
579+
if (container === null) {
580+
return;
581+
}
582+
const containerRect = container.getBoundingClientRect();
583+
// Reveal.js scales slides with a transform, so convert client
584+
// pixels back to the CSS pixels of the container.
585+
const scale = containerRect.height / container.offsetHeight || 1;
586+
const firstRect = firstEl.getBoundingClientRect();
587+
const lastRect = lastEl.getBoundingClientRect();
588+
const top = (firstRect.top - containerRect.top) / scale - container.clientTop + container.scrollTop;
589+
const height = (lastRect.bottom - firstRect.top) / scale;
590+
band.style.top = top - 2 + "px";
591+
band.style.height = height + 4 + "px";
592+
if (codeBlock) {
593+
// Without the copy button wrapper, the containing block is wider
594+
// than the code block, so span the code block explicitly.
595+
const blockRect = codeBlock.getBoundingClientRect();
596+
band.style.left = (blockRect.left - containerRect.left) / scale - container.clientLeft + container.scrollLeft + "px";
597+
band.style.width = blockRect.width / scale + "px";
598+
}
599+
};
600+
578601
if (lineIds.length > 0) {
579-
//compute the position of the single el (top and bottom and make a div)
580-
const el = window.document.getElementById(lineIds[0]);
581-
top = el.offsetTop;
582-
height = el.offsetHeight;
583-
parent = el.parentElement.parentElement;
584-
585-
if (lineIds.length > 1) {
586-
const lastEl = window.document.getElementById(lineIds[lineIds.length - 1]);
587-
const bottom = lastEl.offsetTop + lastEl.offsetHeight;
588-
height = bottom - top;
602+
const el = window.document.getElementById(lineIds[0]);
603+
const lastEl = window.document.getElementById(lineIds[lineIds.length - 1]);
604+
const parent = el.parentElement.parentElement;
605+
606+
// cook up a div (if necessary) and position it
607+
let div = window.document.getElementById("code-annotation-line-highlight");
608+
if (div === null) {
609+
div = window.document.createElement("div");
610+
div.setAttribute("id", "code-annotation-line-highlight");
611+
div.style.position = 'absolute';
612+
parent.appendChild(div);
589613
}
590-
591-
if (top !== null && height !== null && parent !== null) {
592-
// cook up a div (if necessary) and position it
593-
let div = window.document.getElementById("code-annotation-line-highlight");
594-
if (div === null) {
595-
div = window.document.createElement("div");
596-
div.setAttribute("id", "code-annotation-line-highlight");
597-
div.style.position = 'absolute';
598-
parent.appendChild(div);
599-
}
600-
div.style.top = top - 2 + "px";
601-
div.style.height = height + 4 + "px";
602-
div.style.left = 0;
603-
604-
let gutterDiv = window.document.getElementById("code-annotation-line-highlight-gutter");
605-
if (gutterDiv === null) {
606-
gutterDiv = window.document.createElement("div");
607-
gutterDiv.setAttribute("id", "code-annotation-line-highlight-gutter");
608-
gutterDiv.style.position = 'absolute';
609-
const codeCell = window.document.getElementById(targetCell);
610-
const gutter = codeCell.querySelector('.code-annotation-gutter');
611-
gutter.appendChild(gutterDiv);
612-
}
613-
614-
gutterDiv.style.top = top - 2 + "px";
615-
gutterDiv.style.height = height + 4 + "px";
614+
placeBand(div, el, lastEl, parent.closest("div.sourceCode") || parent);
615+
616+
let gutterDiv = window.document.getElementById("code-annotation-line-highlight-gutter");
617+
if (gutterDiv === null) {
618+
gutterDiv = window.document.createElement("div");
619+
gutterDiv.setAttribute("id", "code-annotation-line-highlight-gutter");
620+
gutterDiv.style.position = 'absolute';
621+
const codeCell = window.document.getElementById(targetCell);
622+
const gutter = codeCell.querySelector('.code-annotation-gutter');
623+
gutter.appendChild(gutterDiv);
616624
}
625+
placeBand(gutterDiv, el, lastEl);
626+
617627
selectedAnnoteEl = annoteEl;
618628
}
619629
};

‎src/resources/formats/revealjs/quarto.scss‎

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -769,22 +769,15 @@ div.cell-output-display div.pagedtable-wrapper table.table {
769769
left: auto;
770770
}
771771

772+
.reveal #code-annotation-line-highlight,
772773
.reveal #code-annotation-line-highlight-gutter {
774+
box-sizing: border-box;
773775
width: 100%;
774776
border-top: solid var(--quarto-hl-co-color) 1px;
775777
border-bottom: solid var(--quarto-hl-co-color) 1px;
776778
z-index: 2;
777779
}
778780

779-
.reveal #code-annotation-line-highlight {
780-
margin-left: -8em;
781-
width: calc(100% + 4em);
782-
border-top: solid var(--quarto-hl-co-color) 1px;
783-
border-bottom: solid var(--quarto-hl-co-color) 1px;
784-
z-index: 2;
785-
margin-bottom: -2px;
786-
}
787-
788781
.reveal code.sourceCode .code-annotation-anchor.code-annotation-active {
789782
background-color: var(--quarto-hl-normal-color, #aaaaaa);
790783
border: solid var(--quarto-hl-normal-color, #aaaaaa) 1px;
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
title: "Code annotation line highlight"
3+
format: dashboard
4+
code-annotations: below
5+
---
6+
7+
## Row
8+
9+
```{.python}
10+
# Some comment
11+
# to avoid overlapping copy button and annotation marker
12+
import polars as pl # <1>
13+
df = pl.DataFrame({"a": [1, 2]}) # <2>
14+
df.head() # <2>
15+
```
16+
17+
1. Import the library.
18+
2. Build and show a data frame.
19+
20+
```{.python code-line-numbers="true"}
21+
# Some comment
22+
# to avoid overlapping copy button and annotation marker
23+
import polars as pl # <1>
24+
df = pl.DataFrame({"a": [1, 2]}) # <2>
25+
df.head() # <2>
26+
```
27+
28+
1. Import the library.
29+
2. Build and show a data frame.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
title: "Code annotation line highlight contrast"
3+
format:
4+
html:
5+
theme:
6+
light: cosmo
7+
dark: darkly
8+
respect-user-color-scheme: true
9+
code-annotations: below
10+
---
11+
12+
```{.python}
13+
# Some comment
14+
# to avoid overlapping copy button and annotation marker
15+
import polars as pl # <1>
16+
df = pl.DataFrame({"a": [1, 2]}) # <2>
17+
df.head() # <2>
18+
```
19+
20+
1. Import the library.
21+
2. Build and show a data frame.
22+
23+
```{.python code-line-numbers="true"}
24+
# Some comment
25+
# to avoid overlapping copy button and annotation marker
26+
import polars as pl # <1>
27+
df = pl.DataFrame({"a": [1, 2]}) # <2>
28+
df.head() # <2>
29+
```
30+
31+
1. Import the library.
32+
2. Build and show a data frame.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
title: "Code annotation line highlight no copy"
3+
format:
4+
html:
5+
code-copy: false
6+
code-annotations: below
7+
---
8+
9+
```{.python}
10+
# Some comment
11+
# to avoid overlapping copy button and annotation marker
12+
import polars as pl # <1>
13+
df = pl.DataFrame({"a": [1, 2]}) # <2>
14+
df.head() # <2>
15+
```
16+
17+
1. Import the library.
18+
2. Build and show a data frame.
19+
20+
```{.python code-line-numbers="true"}
21+
# Some comment
22+
# to avoid overlapping copy button and annotation marker
23+
import polars as pl # <1>
24+
df = pl.DataFrame({"a": [1, 2]}) # <2>
25+
df.head() # <2>
26+
```
27+
28+
1. Import the library.
29+
2. Build and show a data frame.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
---
2+
title: "Code annotation line highlight in a scrolled container"
3+
format:
4+
html:
5+
code-copy: false
6+
code-annotations: below
7+
---
8+
9+
::: {#scroll-box style="position: relative; height: 200px; overflow: auto;"}
10+
11+
Filler paragraph so the code block starts below the visible area of the box.
12+
13+
Filler paragraph so the code block starts below the visible area of the box.
14+
15+
Filler paragraph so the code block starts below the visible area of the box.
16+
17+
Filler paragraph so the code block starts below the visible area of the box.
18+
19+
```{.python}
20+
# Some comment
21+
# to avoid overlapping copy button and annotation marker
22+
import polars as pl # <1>
23+
df = pl.DataFrame({"a": [1, 2]}) # <2>
24+
df.head() # <2>
25+
```
26+
27+
1. Import the library.
28+
2. Build and show a data frame.
29+
30+
Filler paragraph after the annotations.
31+
32+
Filler paragraph after the annotations.
33+
34+
:::
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
title: "Code annotation line highlight"
3+
format: html
4+
code-annotations: below
5+
---
6+
7+
```{.python}
8+
# Some comment
9+
# to avoid overlapping copy button and annotation marker
10+
import polars as pl # <1>
11+
df = pl.DataFrame({"a": [1, 2]}) # <2>
12+
df.head() # <2>
13+
```
14+
15+
1. Import the library.
16+
2. Build and show a data frame.
17+
18+
```{.python code-line-numbers="true"}
19+
# Some comment
20+
# to avoid overlapping copy button and annotation marker
21+
import polars as pl # <1>
22+
df = pl.DataFrame({"a": [1, 2]}) # <2>
23+
df.head() # <2>
24+
```
25+
26+
1. Import the library.
27+
2. Build and show a data frame.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
title: "Code annotation line highlight"
3+
format: revealjs
4+
code-annotations: below
5+
---
6+
7+
## Without line numbers
8+
9+
```{.python code-line-numbers="false"}
10+
# Some comment
11+
# to avoid overlapping copy button and annotation marker
12+
import polars as pl # <1>
13+
df = pl.DataFrame({"a": [1, 2]}) # <2>
14+
df.head() # <2>
15+
```
16+
17+
1. Import the library.
18+
2. Build and show a data frame.
19+
20+
21+
## With line numbers
22+
23+
```{.python code-line-numbers="true"}
24+
# Some comment
25+
# to avoid overlapping copy button and annotation marker
26+
import polars as pl # <1>
27+
df = pl.DataFrame({"a": [1, 2]}) # <2>
28+
df.head() # <2>
29+
```
30+
31+
1. Import the library.
32+
2. Build and show a data frame.

0 commit comments

Comments
 (0)