Repository navigation
fix(typst): add rendering for proof, remark, and solution environments #14312
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
2e0e918
fix: add Typst rendering for proof, remark, and solution environments…
mcanouil c95dc07
refactor: move theorems.lua to a require() module to avoid Lua local …
mcanouil d07fb27
test: check unnumbered proofs and titles with Typst markup
mcanouil 0ce9f5b
fix(typst): leave unnumbered proofs out of the frame counter and esca…
mcanouil 3504d4b
docs: add changelog entry for #14290
mcanouil f0bdff4
test: check a title-only proof keeps its label and the bold unnumbere…
mcanouil d5252e2
fix(typst): keep title-only proofs and style the unnumbered prefix
mcanouil f92794c
test: check italic proof headers in the simple appearance and styled …
mcanouil e18d96f
fix(typst): keep italic proof headers in the simple appearance and ke…
mcanouil 01da667
refactor(typst): avoid shadowed names in the theorems module
mcanouil 6b81598
test: check proof frames in the fancy appearance
mcanouil 29ff13c
test: check a styled theorem keeps its attributes in Typst
mcanouil 0d8d511
fix(typst): keep block attributes on theorems as on proofs
mcanouil 4e40149
docs: mention theorem and proof style attributes in the #14290 entry
mcanouil File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,156 @@ | ||
| -- theorems.lua | ||
| -- Shared Typst theorem/proof setup logic using theorion | ||
| -- Copyright (C) 2026 Posit Software, PBC | ||
|
|
||
| local typst_theorem_appearance_imported = false | ||
| local typst_theorem_like_frames = {} | ||
| local typst_simple_renderers = {} | ||
|
|
||
| local function appearance() | ||
| local val = option("theorem-appearance", "simple") | ||
| if type(val) == "table" then | ||
| val = pandoc.utils.stringify(val) | ||
| end | ||
| return val or "simple" | ||
| end | ||
|
|
||
| local function ensure_appearance_imports() | ||
| local val = appearance() | ||
| if typst_theorem_appearance_imported then | ||
| return val | ||
| end | ||
|
|
||
| typst_theorem_appearance_imported = true | ||
| if val == "fancy" then | ||
| quarto.doc.include_text("in-header", [[ | ||
| #import "@preview/theorion:0.4.1": make-frame, cosmos | ||
| #import cosmos.fancy: fancy-box, set-primary-border-color, set-primary-body-color, set-secondary-border-color, set-secondary-body-color, set-tertiary-border-color, set-tertiary-body-color, get-primary-border-color, get-primary-body-color, get-secondary-border-color, get-secondary-body-color, get-tertiary-border-color, get-tertiary-body-color | ||
| ]]) | ||
| quarto.doc.include_text("before-body", [[ | ||
| #set-primary-border-color(brand-color.at("primary", default: green.darken(30%))) | ||
| #set-primary-body-color(brand-color.at("primary", default: green).lighten(90%)) | ||
| #set-secondary-border-color(brand-color.at("secondary", default: orange)) | ||
| #set-secondary-body-color(brand-color.at("secondary", default: orange).lighten(90%)) | ||
| #set-tertiary-border-color(brand-color.at("tertiary", default: blue.darken(30%))) | ||
| #set-tertiary-body-color(brand-color.at("tertiary", default: blue).lighten(90%)) | ||
| ]]) | ||
| elseif val == "clouds" then | ||
| quarto.doc.include_text("in-header", [[ | ||
| #import "@preview/theorion:0.4.1": make-frame, cosmos | ||
| #import cosmos.clouds: render-fn as clouds-render | ||
| ]]) | ||
| elseif val == "rainbow" then | ||
| quarto.doc.include_text("in-header", [[ | ||
| #import "@preview/theorion:0.4.1": make-frame, cosmos | ||
| #import cosmos.rainbow: render-fn as rainbow-render | ||
| ]]) | ||
| else | ||
| quarto.doc.include_text("in-header", [[ | ||
| #import "@preview/theorion:0.4.1": make-frame | ||
| ]]) | ||
| end | ||
|
|
||
| return val | ||
| end | ||
|
|
||
| -- Defines a simple renderer once. The "theorem" style writes a bold header and | ||
| -- an italic body; the "proof" style writes an italic header and an upright | ||
| -- body, with the name in brackets, as amsthm and the HTML output do. | ||
| local function ensure_simple_render(render_name, style) | ||
| if typst_simple_renderers[render_name] then | ||
| return | ||
| end | ||
|
|
||
| typst_simple_renderers[render_name] = true | ||
| if style == "proof" then | ||
| quarto.doc.include_text("in-header", "#let " .. render_name .. [[(prefix: none, title: "", full-title: auto, body) = { | ||
| emph(prefix) | ||
| if title != "" and title != none [ (#title)] | ||
| [. ] | ||
| body | ||
| parbreak() | ||
| } | ||
| ]]) | ||
| return | ||
| end | ||
|
|
||
| quarto.doc.include_text("in-header", "#let " .. render_name .. [[(prefix: none, title: "", full-title: auto, body) = { | ||
| if full-title != "" and full-title != auto and full-title != none { | ||
| strong[#full-title.] | ||
| h(0.5em) | ||
| } | ||
| emph(body) | ||
| parbreak() | ||
| } | ||
| ]]) | ||
| end | ||
|
|
||
| -- Writes text as a Typst string literal, so that titles from the language | ||
| -- files reach the page as written instead of being read as Typst markup. | ||
| local function typst_string(text) | ||
| return '"' .. text:gsub("\\", "\\\\"):gsub('"', '\\"') .. '"' | ||
| end | ||
|
|
||
| -- Returns the `render:` argument of make-frame for the theorem appearance. | ||
| -- opts.color_scheme: fancy color set (primary, secondary or tertiary) | ||
| -- opts.color: clouds and rainbow fill color | ||
| -- opts.simple_render, opts.simple_style: name and style ("theorem" or "proof") | ||
| -- of the simple renderer | ||
| local function render_code(appearance_name, opts) | ||
| if appearance_name == "fancy" then | ||
| return " render: fancy-box.with(\n" .. | ||
| " get-border-color: get-" .. opts.color_scheme .. "-border-color,\n" .. | ||
| " get-body-color: get-" .. opts.color_scheme .. "-body-color,\n" .. | ||
| " get-symbol: loc => none,\n" .. | ||
| " ),\n" | ||
| elseif appearance_name == "clouds" then | ||
| return " render: clouds-render.with(fill: " .. opts.color .. ".lighten(85%)),\n" | ||
| elseif appearance_name == "rainbow" then | ||
| return " render: rainbow-render.with(fill: " .. opts.color .. ".darken(20%)),\n" | ||
| end | ||
| ensure_simple_render(opts.simple_render, opts.simple_style) | ||
| return " render: " .. opts.simple_render .. ",\n" | ||
| end | ||
|
|
||
| -- Returns the Typst code for a frame header: bold, or plain for blocks whose | ||
| -- renderer styles the header itself. | ||
| local function header_code(title, bold) | ||
| if bold then | ||
| return 'text(weight: "bold", ' .. typst_string(title) .. ')' | ||
| end | ||
| return typst_string(title) | ||
| end | ||
|
|
||
| local function ensure_frame(env_name, title, render_arg, bold) | ||
| if typst_theorem_like_frames[env_name] then | ||
| return | ||
| end | ||
|
|
||
| typst_theorem_like_frames[env_name] = true | ||
| quarto.doc.include_text("in-header", "#let (" .. env_name .. "-counter, " .. env_name .. "-box, " .. | ||
| env_name .. ", show-" .. env_name .. ") = make-frame(\n" .. | ||
| " \"" .. env_name .. "\",\n" .. | ||
| " " .. header_code(title, bold) .. ",\n" .. | ||
| " inherited-levels: theorem-inherited-levels,\n" .. | ||
| " numbering: theorem-numbering,\n" .. | ||
| render_arg .. | ||
| ")") | ||
| quarto.doc.include_text("in-header", "#show: show-" .. env_name) | ||
| end | ||
|
|
||
| -- Keeps the block's attributes, such as `style`, on a wrapper div, so the Typst | ||
| -- CSS support turns them into a block around the frame, as for other divs. | ||
| local function keep_attributes(frame, attributes) | ||
| if #attributes > 0 then | ||
| return pandoc.Div({frame}, pandoc.Attr("", {}, attributes)) | ||
| end | ||
| return frame | ||
| end | ||
|
|
||
| return { | ||
| ensure_appearance_imports = ensure_appearance_imports, | ||
| ensure_frame = ensure_frame, | ||
| header_code = header_code, | ||
| keep_attributes = keep_attributes, | ||
| render_code = render_code, | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.