From 5e653900dea7751353c49fe383adc84567284d32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Canouil?= <8896044+mcanouil@users.noreply.github.com> Date: Wed, 23 Sep 2026 11:08:16 +0200 Subject: [PATCH 1/6] fix: stand the Skylighting hot-fix down with the filter The hot-fix filters asked hotfix_skylighting alone, while the pass that declares the Typst colour helpers asks whether the render draws chrome at all. With the filter off, the helpers were never declared and the override that calls them still went into the document, so Typst stopped on _cw-page-bg and a document setting enabled: false failed to compile. The hot-fix now asks draws_chrome, like every other pass that exists only to serve the chrome. --- CHANGELOG.md | 1 + _extensions/code-window/main.lua | 11 +++++++++- .../fixtures/filter-disabled-no-language.qmd | 3 +++ tests/fixtures/filter-disabled.qmd | 3 +++ tests/run.sh | 20 +++++++++++++++++++ 5 files changed, 37 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3303d50..b54996d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ ### Bug Fixes +- fix: Stand the Skylighting hot-fix down when the filter is off, so a Typst document that sets `enabled: false` compiles. The hot-fix still wrote an override calling the colour helpers, and the pass that declares them does not run with the filter off, so Typst stopped on a name nothing had declared. - fix: Keep a block's own language where the filter derives no name for it, which means the filter is off, or `auto-filename` is off, or the output format is neither HTML nor Typst. The block was relabelled as `default` before, and the language the author wrote was lost. (#66) - fix: Keep inline code in a Typst document as a code element, so Pandoc writes the syntax highlighting definitions for it. A document with inline code and no code block failed to compile before. (#65) - fix: Remove the code-window attributes from a block that sets code-window-enabled="false", and from a block that sets code-window-lines when lines-label is off. They reached the HTML output as data-code-window-* attributes before. (#63) diff --git a/_extensions/code-window/main.lua b/_extensions/code-window/main.lua index 3f691a4..89f1874 100644 --- a/_extensions/code-window/main.lua +++ b/_extensions/code-window/main.lua @@ -126,12 +126,21 @@ local filters = { local skylighting_mod = load_skylighting_hotfix_module() +-- The hot-fix exists only to serve the chrome, so it asks draws_chrome like +-- every other pass that does. Its Skylighting override and its inline box both +-- call the colour helpers, and the pass that defines them is code_window.Pandoc, +-- which returns before it writes anything with the filter off. Asking +-- hotfix_skylighting alone left the override in a document that declared no +-- helpers, and Typst stopped on the first name it could not resolve. for _, subfilter in ipairs(skylighting_mod.filters or {}) do local wrapped = {} for element_type, handler in pairs(subfilter) do wrapped[element_type] = function(...) + if not code_window.draws_chrome() then + return nil + end local cfg = code_window.CONFIG() - if not cfg or not cfg.hotfix_skylighting then + if not cfg.hotfix_skylighting then return nil end if skylighting_mod.set_wrapper then diff --git a/tests/fixtures/filter-disabled-no-language.qmd b/tests/fixtures/filter-disabled-no-language.qmd index 4f9e3e6..3a58640 100644 --- a/tests/fixtures/filter-disabled-no-language.qmd +++ b/tests/fixtures/filter-disabled-no-language.qmd @@ -5,6 +5,9 @@ filters: extensions: code-window: enabled: false +format: + typst: + keep-typ: true --- A block with no language gains none while the filter is off. The block below is diff --git a/tests/fixtures/filter-disabled.qmd b/tests/fixtures/filter-disabled.qmd index cc8693e..9ddf7f4 100644 --- a/tests/fixtures/filter-disabled.qmd +++ b/tests/fixtures/filter-disabled.qmd @@ -5,6 +5,9 @@ filters: extensions: code-window: enabled: false +format: + typst: + keep-typ: true --- A filter that is off leaves every block as Quarto writes it. The block below diff --git a/tests/run.sh b/tests/run.sh index 7b060b5..c8571ec 100755 --- a/tests/run.sh +++ b/tests/run.sh @@ -203,6 +203,26 @@ else report pass "filter-disabled-no-language: the block gains no class" fi +# The Skylighting override calls the colour helpers, and the pass that defines +# them does not run with the filter off. So the hot-fix has to stand down there +# too, or the Typst document names a variable nothing declared and the compile +# fails. +render filter-disabled typst +if grep -q 'skylighting-typst-fix override' "${work_dir}/filter-disabled.typ"; then + report fail "filter-disabled: the hot-fix stands down with the filter" \ + "no skylighting-typst-fix override in filter-disabled.typ" +else + report pass "filter-disabled: the hot-fix stands down with the filter" +fi + +render filter-disabled-no-language typst +if grep -q 'skylighting-typst-fix override' "${work_dir}/filter-disabled-no-language.typ"; then + report fail "filter-disabled-no-language: the hot-fix stands down with the filter" \ + "no skylighting-typst-fix override in filter-disabled-no-language.typ" +else + report pass "filter-disabled-no-language: the hot-fix stands down with the filter" +fi + # A render with no derived name to build reads no label either, whatever the # format, so the pass has no reader there. render auto-filename-off html From 6b58d3e686dc0661c8efa71fdb66865a8a19629f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Canouil?= <8896044+mcanouil@users.noreply.github.com> Date: Wed, 23 Sep 2026 11:11:21 +0200 Subject: [PATCH 2/6] refactor: fold the new gate and tests into the file idioms The hot-fix gate becomes the one-line form the two passes above it use, and the two new tests become one loop over both fixtures. --- _extensions/code-window/main.lua | 16 +++++----------- tests/run.sh | 24 +++++++++--------------- 2 files changed, 14 insertions(+), 26 deletions(-) diff --git a/_extensions/code-window/main.lua b/_extensions/code-window/main.lua index 89f1874..19af81c 100644 --- a/_extensions/code-window/main.lua +++ b/_extensions/code-window/main.lua @@ -127,24 +127,18 @@ local filters = { local skylighting_mod = load_skylighting_hotfix_module() -- The hot-fix exists only to serve the chrome, so it asks draws_chrome like --- every other pass that does. Its Skylighting override and its inline box both --- call the colour helpers, and the pass that defines them is code_window.Pandoc, --- which returns before it writes anything with the filter off. Asking --- hotfix_skylighting alone left the override in a document that declared no --- helpers, and Typst stopped on the first name it could not resolve. +-- every other pass that does. Its Skylighting override calls the colour helpers, +-- and code_window.Pandoc is what declares them, so asking hotfix_skylighting +-- alone left the override in a document that declared none. for _, subfilter in ipairs(skylighting_mod.filters or {}) do local wrapped = {} for element_type, handler in pairs(subfilter) do wrapped[element_type] = function(...) - if not code_window.draws_chrome() then - return nil - end - local cfg = code_window.CONFIG() - if not cfg.hotfix_skylighting then + if not code_window.draws_chrome() or not code_window.CONFIG().hotfix_skylighting then return nil end if skylighting_mod.set_wrapper then - skylighting_mod.set_wrapper(cfg.typst_wrapper) + skylighting_mod.set_wrapper(code_window.CONFIG().typst_wrapper) end return handler(...) end diff --git a/tests/run.sh b/tests/run.sh index c8571ec..d3514f3 100755 --- a/tests/run.sh +++ b/tests/run.sh @@ -207,21 +207,15 @@ fi # them does not run with the filter off. So the hot-fix has to stand down there # too, or the Typst document names a variable nothing declared and the compile # fails. -render filter-disabled typst -if grep -q 'skylighting-typst-fix override' "${work_dir}/filter-disabled.typ"; then - report fail "filter-disabled: the hot-fix stands down with the filter" \ - "no skylighting-typst-fix override in filter-disabled.typ" -else - report pass "filter-disabled: the hot-fix stands down with the filter" -fi - -render filter-disabled-no-language typst -if grep -q 'skylighting-typst-fix override' "${work_dir}/filter-disabled-no-language.typ"; then - report fail "filter-disabled-no-language: the hot-fix stands down with the filter" \ - "no skylighting-typst-fix override in filter-disabled-no-language.typ" -else - report pass "filter-disabled-no-language: the hot-fix stands down with the filter" -fi +for fixture in filter-disabled filter-disabled-no-language; do + render "${fixture}" typst + if grep -q 'skylighting-typst-fix override' "${work_dir}/${fixture}.typ"; then + report fail "${fixture}: the hot-fix stands down with the filter" \ + "no skylighting-typst-fix override in ${fixture}.typ" + else + report pass "${fixture}: the hot-fix stands down with the filter" + fi +done # A render with no derived name to build reads no label either, whatever the # format, so the pass has no reader there. From 608becaffab77202684c1755b52f0ef9c2873b5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Canouil?= <8896044+mcanouil@users.noreply.github.com> Date: Wed, 23 Sep 2026 11:15:41 +0200 Subject: [PATCH 3/6] fix: widen the hot-fix test and correct the hot-fix docs The test greps for the "_cw-" prefix rather than the Skylighting override alone, so the annotation rule that can replace it and the inline-code fallback are covered too. The documentation said typst-title was the hot-fix that follows enabled. All three follow it, so a document with the filter off takes none of them. --- CHANGELOG.md | 2 +- _extensions/code-window/main.lua | 8 ++++++-- docs/examples.qmd | 2 +- docs/reference.qmd | 1 + tests/run.sh | 14 ++++++++------ 5 files changed, 17 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b54996d..42e22fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ ### Bug Fixes -- fix: Stand the Skylighting hot-fix down when the filter is off, so a Typst document that sets `enabled: false` compiles. The hot-fix still wrote an override calling the colour helpers, and the pass that declares them does not run with the filter off, so Typst stopped on a name nothing had declared. +- fix: Stand the Skylighting hot-fix down when the filter is off, so a Typst document that sets `enabled: false` compiles. It failed on an undeclared name before. - fix: Keep a block's own language where the filter derives no name for it, which means the filter is off, or `auto-filename` is off, or the output format is neither HTML nor Typst. The block was relabelled as `default` before, and the language the author wrote was lost. (#66) - fix: Keep inline code in a Typst document as a code element, so Pandoc writes the syntax highlighting definitions for it. A document with inline code and no code block failed to compile before. (#65) - fix: Remove the code-window attributes from a block that sets code-window-enabled="false", and from a block that sets code-window-lines when lines-label is off. They reached the HTML output as data-code-window-* attributes before. (#63) diff --git a/_extensions/code-window/main.lua b/_extensions/code-window/main.lua index 19af81c..59727d4 100644 --- a/_extensions/code-window/main.lua +++ b/_extensions/code-window/main.lua @@ -134,11 +134,15 @@ for _, subfilter in ipairs(skylighting_mod.filters or {}) do local wrapped = {} for element_type, handler in pairs(subfilter) do wrapped[element_type] = function(...) - if not code_window.draws_chrome() or not code_window.CONFIG().hotfix_skylighting then + if not code_window.draws_chrome() then + return nil + end + local cfg = code_window.CONFIG() + if not cfg.hotfix_skylighting then return nil end if skylighting_mod.set_wrapper then - skylighting_mod.set_wrapper(code_window.CONFIG().typst_wrapper) + skylighting_mod.set_wrapper(cfg.typst_wrapper) end return handler(...) end diff --git a/docs/examples.qmd b/docs/examples.qmd index ebb5c37..e8c678a 100644 --- a/docs/examples.qmd +++ b/docs/examples.qmd @@ -261,7 +261,7 @@ Two pairs are linked. `code-annotations` and `skylighting` work together: the first resolves the annotations, and the second supplies the rule that draws the circled marker beside an annotated line. Turn `skylighting` off, and the numbered list stays under the block while every marker inside it goes, along with the block styling and the inline code backgrounds the same module supplies. -`typst-title` follows `enabled`: with the filter off, theorem titles stay as Quarto writes them. +Every hot-fix follows `enabled`: with the filter off, none of them acts, so the block styling, the inline code backgrounds and the theorem titles all stay as Quarto writes them. See [Hot-fixes](reference.qmd#hot-fixes) for what each one corrects. diff --git a/docs/reference.qmd b/docs/reference.qmd index 27720df..a726b2c 100644 --- a/docs/reference.qmd +++ b/docs/reference.qmd @@ -90,6 +90,7 @@ In Typst the chrome is a call to the `wrapper` function, emitted into the docume Typst output needs a few corrections that Quarto does not yet make itself. Each is on by default and each can be given a version at which it switches itself off, so an upgrade retires it without a code change. +All of them follow `enabled`, so a document that turns the filter off takes no hot-fix at all. | Hot-fix | What it corrects | | --- | --- | diff --git a/tests/run.sh b/tests/run.sh index d3514f3..7c3214f 100755 --- a/tests/run.sh +++ b/tests/run.sh @@ -203,15 +203,17 @@ else report pass "filter-disabled-no-language: the block gains no class" fi -# The Skylighting override calls the colour helpers, and the pass that defines -# them does not run with the filter off. So the hot-fix has to stand down there -# too, or the Typst document names a variable nothing declared and the compile -# fails. +# Everything the hot-fix writes calls the colour helpers or the annotation +# state, and the pass that declares those does not run with the filter off. So +# the hot-fix has to stand down there too, or the Typst document names a +# variable nothing declared and the compile fails. The test greps for the "_cw-" +# prefix the helpers share, which covers the Skylighting override, the +# annotation rule that replaces it, and the inline-code fallback alike. for fixture in filter-disabled filter-disabled-no-language; do render "${fixture}" typst - if grep -q 'skylighting-typst-fix override' "${work_dir}/${fixture}.typ"; then + if grep -q '_cw-' "${work_dir}/${fixture}.typ"; then report fail "${fixture}: the hot-fix stands down with the filter" \ - "no skylighting-typst-fix override in ${fixture}.typ" + "no _cw- helper call in ${fixture}.typ" else report pass "${fixture}: the hot-fix stands down with the filter" fi From 3b74891f40e3b362a383a19a81ab01846ffafd45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Canouil?= <8896044+mcanouil@users.noreply.github.com> Date: Wed, 23 Sep 2026 11:20:57 +0200 Subject: [PATCH 4/6] test: observe both hot-fix passes standing down The hot-fix contributes a block pass and an inline pass, and the inline one writes a box that names no helper where the theme gives a background colour. Both disabled fixtures gain inline code, and the loop asserts the absence of that box alongside the absence of the helpers. The loop moves under its own banner, since it was splitting the section on block languages. --- .../fixtures/filter-disabled-no-language.qmd | 3 ++ tests/fixtures/filter-disabled.qmd | 3 ++ tests/run.sh | 43 ++++++++++++------- 3 files changed, 33 insertions(+), 16 deletions(-) diff --git a/tests/fixtures/filter-disabled-no-language.qmd b/tests/fixtures/filter-disabled-no-language.qmd index 3a58640..e72479b 100644 --- a/tests/fixtures/filter-disabled-no-language.qmd +++ b/tests/fixtures/filter-disabled-no-language.qmd @@ -13,4 +13,7 @@ format: A block with no language gains none while the filter is off. The block below is indented rather than fenced, which gives the same code block with no language. +The inline code in `x = 1` keeps the shape Quarto gives it too, because the +hot-fix that boxes it stands down with the filter. + x = 1 diff --git a/tests/fixtures/filter-disabled.qmd b/tests/fixtures/filter-disabled.qmd index 9ddf7f4..8728c9a 100644 --- a/tests/fixtures/filter-disabled.qmd +++ b/tests/fixtures/filter-disabled.qmd @@ -14,6 +14,9 @@ A filter that is off leaves every block as Quarto writes it. The block below keeps the language its author gave it, even though Pandoc cannot highlight it and the filter would relabel it when it is on. +The inline code in `x = 1` keeps the shape Quarto gives it too, because the +hot-fix that boxes it stands down with the filter. + ```foo x = 1 ``` diff --git a/tests/run.sh b/tests/run.sh index 7c3214f..3ea0655 100755 --- a/tests/run.sh +++ b/tests/run.sh @@ -203,22 +203,6 @@ else report pass "filter-disabled-no-language: the block gains no class" fi -# Everything the hot-fix writes calls the colour helpers or the annotation -# state, and the pass that declares those does not run with the filter off. So -# the hot-fix has to stand down there too, or the Typst document names a -# variable nothing declared and the compile fails. The test greps for the "_cw-" -# prefix the helpers share, which covers the Skylighting override, the -# annotation rule that replaces it, and the inline-code fallback alike. -for fixture in filter-disabled filter-disabled-no-language; do - render "${fixture}" typst - if grep -q '_cw-' "${work_dir}/${fixture}.typ"; then - report fail "${fixture}: the hot-fix stands down with the filter" \ - "no _cw- helper call in ${fixture}.typ" - else - report pass "${fixture}: the hot-fix stands down with the filter" - fi -done - # A render with no derived name to build reads no label either, whatever the # format, so the pass has no reader there. render auto-filename-off html @@ -251,6 +235,33 @@ else "a default class, a cw-auto class, and data-filename=\"foo\" on the block" fi +# ============================================================================ +# The Typst hot-fix stands down with the filter +# ============================================================================ + +# The hot-fix contributes two passes, and both write Typst that only the +# filter's own preamble declares. That preamble is not written with the filter +# off, so both passes have to stand down there. The block pass is caught by the +# "_cw-" prefix the colour helpers and the annotation state share. The inline +# pass takes a different shape where the theme gives a background colour, and +# names no helper at all, so the box itself is the thing to look for. +for fixture in filter-disabled filter-disabled-no-language; do + render "${fixture}" typst + if grep -q '_cw-' "${work_dir}/${fixture}.typ"; then + report fail "${fixture}: the block pass stands down with the filter" \ + "no _cw- helper call in ${fixture}.typ" + else + report pass "${fixture}: the block pass stands down with the filter" + fi + + if grep -q 'box(fill: rgb(' "${work_dir}/${fixture}.typ"; then + report fail "${fixture}: the inline pass stands down with the filter" \ + "no box(fill: rgb( call in ${fixture}.typ" + else + report pass "${fixture}: the inline pass stands down with the filter" + fi +done + # ============================================================================ # The schema is where an option's default is written down # ============================================================================ From 2d7ff47dbabd00115e7c39fa2bd3ed372e952570 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Canouil?= <8896044+mcanouil@users.noreply.github.com> Date: Wed, 23 Sep 2026 11:26:14 +0200 Subject: [PATCH 5/6] test: check the Typst source exists before reading it Both hot-fix assertions are negative, so a missing file answered them the same way a clean render does. The loop now checks for the file first. The examples page also loses a count that no longer matched the text under it, and stops listing effects as though they were all of them. --- docs/examples.qmd | 4 ++-- tests/run.sh | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/docs/examples.qmd b/docs/examples.qmd index e8c678a..0abce96 100644 --- a/docs/examples.qmd +++ b/docs/examples.qmd @@ -256,12 +256,12 @@ The extension compares that string with the running Quarto. At or above the named version the hot-fix stays out, whatever `enabled` says. An upgrade thus retires the hot-fix with no further edit. -Two pairs are linked. +`code-annotations` and `skylighting` are linked. `code-annotations` and `skylighting` work together: the first resolves the annotations, and the second supplies the rule that draws the circled marker beside an annotated line. Turn `skylighting` off, and the numbered list stays under the block while every marker inside it goes, along with the block styling and the inline code backgrounds the same module supplies. -Every hot-fix follows `enabled`: with the filter off, none of them acts, so the block styling, the inline code backgrounds and the theorem titles all stay as Quarto writes them. +Every hot-fix follows `enabled`: with the filter off, none of them acts, and the Typst document comes out as Quarto writes it. See [Hot-fixes](reference.qmd#hot-fixes) for what each one corrects. diff --git a/tests/run.sh b/tests/run.sh index 3ea0655..8ae852c 100755 --- a/tests/run.sh +++ b/tests/run.sh @@ -247,6 +247,17 @@ fi # names no helper at all, so the box itself is the thing to look for. for fixture in filter-disabled filter-disabled-no-language; do render "${fixture}" typst + + # Both assertions below are negative, and a missing file would answer them + # the same way a clean render does. So the file is checked for first. + if [ -f "${work_dir}/${fixture}.typ" ]; then + report pass "${fixture}: the render keeps its Typst source" + else + report fail "${fixture}: the render keeps its Typst source" \ + "a ${fixture}.typ in the work directory" + continue + fi + if grep -q '_cw-' "${work_dir}/${fixture}.typ"; then report fail "${fixture}: the block pass stands down with the filter" \ "no _cw- helper call in ${fixture}.typ" From a4da63c30413a327b4da21aeba9d2efd9c3d6918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Canouil?= <8896044+mcanouil@users.noreply.github.com> Date: Wed, 23 Sep 2026 11:27:27 +0200 Subject: [PATCH 6/6] docs: add the pull request reference to the changelog entry --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 42e22fc..f3d3860 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ ### Bug Fixes -- fix: Stand the Skylighting hot-fix down when the filter is off, so a Typst document that sets `enabled: false` compiles. It failed on an undeclared name before. +- fix: Stand the Skylighting hot-fix down when the filter is off, so a Typst document that sets `enabled: false` compiles. It failed on an undeclared name before. (#71) - fix: Keep a block's own language where the filter derives no name for it, which means the filter is off, or `auto-filename` is off, or the output format is neither HTML nor Typst. The block was relabelled as `default` before, and the language the author wrote was lost. (#66) - fix: Keep inline code in a Typst document as a code element, so Pandoc writes the syntax highlighting definitions for it. A document with inline code and no code block failed to compile before. (#65) - fix: Remove the code-window attributes from a block that sets code-window-enabled="false", and from a block that sets code-window-lines when lines-label is off. They reached the HTML output as data-code-window-* attributes before. (#63)