Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
### Bug Fixes

- fix: Style inline code in a Typst theorem title like inline code anywhere else. (#78)
- fix: Keep the title bar on the same page as the first lines of its code in Typst, so it never sits alone at the bottom of a page. The Typst bundled with Quarto 1.10.19 still breaks a window taller than a page from the current page, which 1.2.1 could not rely on. (#80)

## 1.6.0 (2026-10-05)

Expand Down
1 change: 1 addition & 0 deletions _extensions/code-window/code-window.lua
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,7 @@ local function build_typst_function_def(has_hotfixes)
below: 0pt,
radius: 0pt,
stroke: (bottom: 1pt + border-colour),
sticky: true,
title-bar,
)
// Strip code block chrome so content fills flush against the window body.
Expand Down
104 changes: 104 additions & 0 deletions tests/fixtures/page-break-title.qmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
---
title: "A title bar kept with its code across a page break"
filters:
- code-window
format:
typst:
keep-typ: true
---

The first two windows start 3em above the bottom of a page.
That leaves room for the title bar but not for the first line of code.

```{=typst}
#set page(height: 10cm, margin: 1cm)
#v(8cm - 3em)
```

```{.python filename="plain.py"}
plain_first = 1
plain_second = 2
```

```{=typst}
#pagebreak()
#v(8cm - 3em)
```

```{.python filename="annotated.py"}
annotated_first = 1 # <1>
annotated_second = 2
```

1. The first line.

```{=typst}
#pagebreak()
```

Text above the tall window.

## Tall heading

```{.python filename="tall.py"}
tall_0 = 0
tall_1 = 1
tall_2 = 2
tall_3 = 3
tall_4 = 4
tall_5 = 5
tall_6 = 6
tall_7 = 7
tall_8 = 8
tall_9 = 9
tall_10 = 10
tall_11 = 11
tall_12 = 12
tall_13 = 13
tall_14 = 14
tall_15 = 15
tall_16 = 16
tall_17 = 17
tall_18 = 18
tall_19 = 19
tall_20 = 20
tall_21 = 21
tall_22 = 22
tall_23 = 23
tall_24 = 24
tall_25 = 25
tall_26 = 26
tall_27 = 27
tall_28 = 28
tall_29 = 29
tall_30 = 30
tall_31 = 31
tall_32 = 32
tall_33 = 33
tall_34 = 34
tall_35 = 35
tall_36 = 36
tall_37 = 37
tall_38 = 38
tall_39 = 39
tall_40 = 40
tall_41 = 41
tall_42 = 42
tall_43 = 43
tall_44 = 44
tall_45 = 45
tall_46 = 46
tall_47 = 47
tall_48 = 48
tall_49 = 49
tall_50 = 50
tall_51 = 51
tall_52 = 52
tall_53 = 53
tall_54 = 54
tall_55 = 55
tall_56 = 56
tall_57 = 57
tall_58 = 58
tall_59 = 59
```
65 changes: 65 additions & 0 deletions tests/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ set -euo pipefail
# fixtures rather than linked. docs/_scripts/sync-extension.sh solves the same
# problem for the documentation site.
#
# Requires: quarto, and pdftotext from poppler for the page-break test.
#
# Usage: tests/run.sh

tests_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
Expand Down Expand Up @@ -54,6 +56,15 @@ count_typst_windows() {
grep -c '^#code-window(filename' "$1" || true
}

# Succeed when one page of page-break-title.txt holds both strings. pdftotext
# ends each page with a form feed, so awk reads one page per record.
# $1 first string. $2 second string.
same_page() {
awk -v RS='\f' -v first="$1" -v second="$2" \
'index($0, first) && index($0, second) { found = 1 } END { exit !found }' \
"${work_dir}/page-break-title.txt"
}

# Answer the classes of every highlighted block in a rendered HTML file. The
# injected script names its marker classes in a selector, never in a tag, so
# reading the tags alone keeps the script out of the answer.
Expand Down Expand Up @@ -390,6 +401,60 @@ else
"a box(fill: call inside the #theorem(title: [...]) line of theorem-title.typ"
fi

# ============================================================================
# A title bar never sits alone at the bottom of a page
# ============================================================================

# Each window in the fixture starts where the title bar fits on the page but
# the first line of code does not. The title bar has to move to the next page
# with its code. The plain window and the annotated window take different
# content paths in the wrapper, so both are checked. The test reads the page
# text of the PDF, so it needs pdftotext from poppler.
if command -v pdftotext >/dev/null 2>&1; then
render page-break-title typst
pdftotext "${work_dir}/page-break-title.pdf" "${work_dir}/page-break-title.txt"

for window in plain annotated; do
if same_page "${window}.py" "${window}_first = 1"; then
report pass "page-break-title: the ${window} title bar stays with its code"
else
report fail "page-break-title: the ${window} title bar stays with its code" \
"${window}.py and ${window}_first = 1 on the same page of page-break-title.pdf"
fi
done

# A window taller than a page has to start on the page where it is placed
# and break across pages. The sticky title bar must not push it whole to
# the next page and leave a near-empty page behind.
if same_page "Text above the tall window." "tall_0 = 0"; then
report pass "page-break-title: a window taller than a page starts on the current page"
else
report fail "page-break-title: a window taller than a page starts on the current page" \
"the text above tall.py and tall_0 = 0 on the same page of page-break-title.pdf"
fi

# Without the sticky title bar, the same fixture has to leave the title bar
# alone on its page. This proves that the fixture still forces the break,
# so the checks above test the fix and not a layout that fits anyway.
lua_file="${work_dir}/_extensions/code-window/code-window.lua"
sed -i.aside '/sticky: true,/d' "${lua_file}"
render page-break-title typst
mv "${lua_file}.aside" "${lua_file}"
pdftotext "${work_dir}/page-break-title.pdf" "${work_dir}/page-break-title.txt"

for window in plain annotated; do
if same_page "${window}.py" "${window}_first = 1"; then
report fail "page-break-title: the fixture orphans the ${window} title bar without the fix" \
"${window}.py and ${window}_first = 1 on different pages without sticky: true"
else
report pass "page-break-title: the fixture orphans the ${window} title bar without the fix"
fi
done
else
report fail "page-break-title: the page text can be read" \
"pdftotext on PATH (brew install poppler)"
fi

# ============================================================================

printf '\n%s passed, %s failed\n' "${passed}" "${failed}"
Expand Down