From bcdfdc753d1546934bbc03b08d49ab2c5c5e6226 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 30 Apr 2026 06:12:10 +0000 Subject: [PATCH 1/2] Initial plan From d2f5f2b1aadf8f5f04455dafd088889a41eff012 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 30 Apr 2026 06:25:15 +0000 Subject: [PATCH 2/2] Install Quarto extensions: div-anchors, equation-anchors, callouty-theorem Agent-Logs-Url: https://github.com/UCD-SERG/rpt/sessions/288d9f12-6032-449d-9d50-0088c6fb2a87 Co-authored-by: d-morrison <2474437+d-morrison@users.noreply.github.com> --- NEWS.md | 1 + .../d-morrison/div-anchors/_extension.yml | 7 + .../d-morrison/div-anchors/div-anchors.css | 36 +++ .../d-morrison/div-anchors/div-anchors.js | 84 +++++++ .../d-morrison/div-anchors/div-anchors.lua | 24 ++ .../equation-anchors/_extension.yml | 7 + .../equation-anchors/equation-anchors.lua | 196 ++++++++++++++++ .../sun123zxy/callouty-theorem/_extension.yml | 7 + .../callouty-theorem/callouty-theorem.lua | 211 ++++++++++++++++++ altdoc/quarto_website.yml | 66 ++++++ 10 files changed, 639 insertions(+) create mode 100644 altdoc/_extensions/d-morrison/div-anchors/_extension.yml create mode 100644 altdoc/_extensions/d-morrison/div-anchors/div-anchors.css create mode 100644 altdoc/_extensions/d-morrison/div-anchors/div-anchors.js create mode 100644 altdoc/_extensions/d-morrison/div-anchors/div-anchors.lua create mode 100644 altdoc/_extensions/d-morrison/equation-anchors/_extension.yml create mode 100644 altdoc/_extensions/d-morrison/equation-anchors/equation-anchors.lua create mode 100644 altdoc/_extensions/sun123zxy/callouty-theorem/_extension.yml create mode 100644 altdoc/_extensions/sun123zxy/callouty-theorem/callouty-theorem.lua diff --git a/NEWS.md b/NEWS.md index db48a7fa..f287dae5 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,6 @@ # rpt (development version) +* Installed Quarto extensions for enhanced documentation: `d-morrison/div-anchors` (permalink anchors for theorem/proof divs), `d-morrison/equation-anchors` (permalink anchors for equations), and `sun123zxy/callouty-theorem` (render theorems as callout blocks). Added callouty-theorem YAML settings based on `d-morrison/rme`. * Switched from pkgdown to altdoc for documentation generation. Now using Quarto Website for documentation with native math equation support via MathJax. * Removed pkgdown-specific configurations and workflows. * Retained RevealJS multi-format support for Quarto vignettes and articles. diff --git a/altdoc/_extensions/d-morrison/div-anchors/_extension.yml b/altdoc/_extensions/d-morrison/div-anchors/_extension.yml new file mode 100644 index 00000000..783ef8fb --- /dev/null +++ b/altdoc/_extensions/d-morrison/div-anchors/_extension.yml @@ -0,0 +1,7 @@ +title: Div Anchors +author: d-morrison +version: 1.0.0 +quarto-required: ">=1.3.0" +contributes: + filters: + - div-anchors.lua diff --git a/altdoc/_extensions/d-morrison/div-anchors/div-anchors.css b/altdoc/_extensions/d-morrison/div-anchors/div-anchors.css new file mode 100644 index 00000000..1c33e33f --- /dev/null +++ b/altdoc/_extensions/d-morrison/div-anchors/div-anchors.css @@ -0,0 +1,36 @@ +/* div-anchors.css + * Visual URL anchor links for theorem and proof divs. + * Mirrors the behavior of Quarto's built-in `anchor-sections` option, + * which shows a small anchor icon on hover for HTML headings. + */ + +/* Shared spacing value for theorem/proof permalink anchors. */ +:root { + --div-anchor-spacing: 0.375em; +} + +.div-anchor { + opacity: 0; + font-size: 0.875em; + font-weight: 400; + margin-left: var(--div-anchor-spacing); + text-decoration: none; + color: inherit; + vertical-align: middle; + transition: opacity 0.2s ease; +} + +.div-anchor:hover, +.div-anchor:focus { + opacity: 1; + text-decoration: none; +} + +/* Show the anchor when hovering over any theorem or proof div. + * .theorem covers all theorem-type environments (lemma, corollary, + * proposition, conjecture, definition, example, exercise, algorithm). + * Proof-type environments are listed separately. */ +:is(.theorem, .proof, .remark, .solution):hover .div-anchor, +:is(.theorem, .proof, .remark, .solution):focus-within .div-anchor { + opacity: 1; +} diff --git a/altdoc/_extensions/d-morrison/div-anchors/div-anchors.js b/altdoc/_extensions/d-morrison/div-anchors/div-anchors.js new file mode 100644 index 00000000..02fb83ef --- /dev/null +++ b/altdoc/_extensions/d-morrison/div-anchors/div-anchors.js @@ -0,0 +1,84 @@ +/** + * div-anchors.js + * + * Adds visual URL anchor links to Quarto theorem and proof divs, + * similarly to the `anchor-sections` option for HTML headings. + * + * After the DOM is ready, finds all theorem/proof divs that have an `id` + * attribute and inserts an anchor link (ยง) right after the theorem-title + * or proof-title span. The anchor is hidden by default and appears on hover, + * controlled by div-anchors.css. + */ + +(function () { + "use strict"; + + /** + * CSS selector matching all Quarto theorem and proof div environments. + * + * Quarto adds the `.theorem` class to all theorem-type divs + * (theorem, lemma, corollary, proposition, conjecture, definition, + * example, exercise, algorithm), so `.theorem[id]` covers all of them. + * Proof-type environments (.proof, .remark, .solution) are listed + * separately because they do not carry the `.theorem` class. + */ + var THEOREM_SELECTOR = [ + ".theorem[id]", /* covers: theorem, lemma, corollary, proposition, + conjecture, definition, example, + exercise, algorithm */ + ".proof[id]", + ".remark[id]", + ".solution[id]", + ].join(", "); + + /** + * Create a styled anchor element pointing to the given id. + * @param {string} id - The target element's id attribute value. + * @returns {HTMLAnchorElement} + */ + function createAnchor(id) { + var a = document.createElement("a"); + a.href = "#" + id; + a.className = "div-anchor"; + a.setAttribute("aria-label", "Permalink to this block"); + var icon = document.createElement("span"); + icon.setAttribute("aria-hidden", "true"); + icon.textContent = "\u00A7"; + a.appendChild(icon); + return a; + } + + /** + * Add anchor links to all theorem/proof divs with ids on the page. + */ + function addDivAnchors() { + var divs = document.querySelectorAll(THEOREM_SELECTOR); + divs.forEach(function (div) { + var id = div.id; + if (!id) { + return; + } + + // Find the theorem-title or proof-title span + var titleSpan = div.querySelector(".theorem-title, .proof-title"); + var anchor = createAnchor(id); + + if (titleSpan) { + // Insert anchor immediately after the title span + titleSpan.insertAdjacentElement("afterend", anchor); + } else { + // Fallback: prepend to the first paragraph + var firstPara = div.querySelector("p"); + if (firstPara) { + firstPara.insertAdjacentElement("afterbegin", anchor); + } + } + }); + } + + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", addDivAnchors); + } else { + addDivAnchors(); + } +})(); diff --git a/altdoc/_extensions/d-morrison/div-anchors/div-anchors.lua b/altdoc/_extensions/d-morrison/div-anchors/div-anchors.lua new file mode 100644 index 00000000..cb16aa81 --- /dev/null +++ b/altdoc/_extensions/d-morrison/div-anchors/div-anchors.lua @@ -0,0 +1,24 @@ +-- div-anchors.lua +-- Registers a JavaScript dependency that adds visual URL anchor links +-- to theorem and proof divs for HTML output, similarly to the +-- `anchor-sections` option for headings. +-- +-- This approach uses JavaScript rather than a Lua AST filter because +-- Quarto processes theorem divs as custom AST nodes that are not visible +-- to Lua element filters at extension filter time. + +function Meta(meta) + if quarto.doc.isFormat("html") then + quarto.doc.addHtmlDependency({ + name = "div-anchors", + version = "1.0.0", + scripts = { + { path = "div-anchors.js" } + }, + stylesheets = { + { path = "div-anchors.css" } + } + }) + end + return meta +end diff --git a/altdoc/_extensions/d-morrison/equation-anchors/_extension.yml b/altdoc/_extensions/d-morrison/equation-anchors/_extension.yml new file mode 100644 index 00000000..1df1d68c --- /dev/null +++ b/altdoc/_extensions/d-morrison/equation-anchors/_extension.yml @@ -0,0 +1,7 @@ +title: Equation Anchors +author: d-morrison +version: 1.0.0 +quarto-required: ">=1.2.0" +contributes: + filters: + - equation-anchors.lua diff --git a/altdoc/_extensions/d-morrison/equation-anchors/equation-anchors.lua b/altdoc/_extensions/d-morrison/equation-anchors/equation-anchors.lua new file mode 100644 index 00000000..191e274a --- /dev/null +++ b/altdoc/_extensions/d-morrison/equation-anchors/equation-anchors.lua @@ -0,0 +1,196 @@ +local function in_html_output() + if quarto and quarto.doc and quarto.doc.is_format then + return quarto.doc.is_format("html") or quarto.doc.is_format("revealjs") + end + + return false +end + +local function anchor_script() + return [[ + +]] +end + +function Pandoc(doc) + if not in_html_output() then + return nil + end + + table.insert(doc.blocks, pandoc.RawBlock("html", anchor_script())) + return doc +end diff --git a/altdoc/_extensions/sun123zxy/callouty-theorem/_extension.yml b/altdoc/_extensions/sun123zxy/callouty-theorem/_extension.yml new file mode 100644 index 00000000..1da7f50f --- /dev/null +++ b/altdoc/_extensions/sun123zxy/callouty-theorem/_extension.yml @@ -0,0 +1,7 @@ +title: callouty-theorem +author: sun123zxy +version: 1.1.0 +quarto-required: ">=1.6.0" +contributes: + filters: + - callouty-theorem.lua diff --git a/altdoc/_extensions/sun123zxy/callouty-theorem/callouty-theorem.lua b/altdoc/_extensions/sun123zxy/callouty-theorem/callouty-theorem.lua new file mode 100644 index 00000000..e61db564 --- /dev/null +++ b/altdoc/_extensions/sun123zxy/callouty-theorem/callouty-theorem.lua @@ -0,0 +1,211 @@ +---- some internal ultilities of Quarto's Lua filter +---- can't be directly called, thus copied here + +-- copied from quarto-cli/src/resources/filters/common/refs.lua +local refType = function (id) + if not id then + return nil + end + local match = string.match(id, "^(%a+)%-") + if match then + return pandoc.text.lower(match) + else + return nil + end +end + +-- copied from quarto-cli/src/resources/filters/customnodes/theorem.lua +local theorem_types = { + thm = { + env = "theorem", + style = "plain", + title = "Theorem" + }, + lem = { + env = "lemma", + style = "plain", + title = "Lemma" + }, + cor = { + env = "corollary", + style = "plain", + title = "Corollary", + }, + prp = { + env = "proposition", + style = "plain", + title = "Proposition", + }, + cnj = { + env = "conjecture", + style = "plain", + title = "Conjecture" + }, + def = { + env = "definition", + style = "definition", + title = "Definition", + }, + exm = { + env = "example", + style = "definition", + title = "Example", + }, + exr = { + env = "exercise", + style = "definition", + title = "Exercise" + }, + alg = { + env = "algorithm", + style = "plain", + title = "Algorithm" + } +} + +-- copied from quarto-cli/src/resources/filters/customnodes/proof.lua +local proof_types = { + proof = { + env = 'proof', + title = 'Proof' + }, + remark = { + env = 'remark', + title = 'Remark' + }, + solution = { + env = 'solution', + title = 'Solution' + } +} + +---- meta reading stuff + +-- recursively iterates through the metadata, stringifying all pandoc.Str elements +local function stringify_meta(meta) + if type(meta) == 'table' then + -- check if is a table with only one pandoc.Str element + if #meta == 1 and meta[1].t == 'Str' then + return pandoc.utils.stringify(meta) + end + for k, v in pairs(meta) do + meta[k] = stringify_meta(v) + end + end + return meta +end + +local callouty_meta = nil + +-- reads the metadata +local function read_meta(meta) + callouty_meta = stringify_meta(meta['callouty-theorem']) +end + +---- main logic + +local function spawn_callout_title(type_title, name) + local my_name = pandoc.List({type_title}) + if name then + my_name:insert(" (") + my_name:extend(pandoc.utils.blocks_to_inlines(pandoc.Blocks(name))) + my_name:insert(")") + end + return my_name +end + +local function shallow_copy(tbl) + local copy = {} + for k, v in pairs(tbl) do + copy[k] = v + end + return copy +end + +local function parse_boolean_attr(value, field) + if value == nil then + return nil + end + if type(value) == "boolean" then + return value + end + if type(value) == "string" then + local lowered = pandoc.text.lower(value) + if lowered == "true" then + return true + elseif lowered == "false" then + return false + end + end + quarto.log.warning("callouty-theorem: unrecognized " .. field .. " value '" .. pandoc.utils.stringify(value) .. "'; expected 'true' or 'false'") + return nil +end + +local function apply_div_callout_overrides(callout_tbl, source_attr) + if not source_attr or not source_attr.attributes then + return + end + + local attrs = source_attr.attributes + for _, key in ipairs({"type", "appearance"}) do + if attrs[key] ~= nil then + callout_tbl[key] = attrs[key] + end + end + + for _, key in ipairs({"collapse", "icon"}) do + local parsed = parse_boolean_attr(attrs[key], key) + if parsed ~= nil then + callout_tbl[key] = parsed + end + end +end + +local function calloutify(el, is_proof) + local typ, my_types, theorem_ctor + if is_proof then + typ = el.type:lower() + my_types = proof_types + theorem_ctor = quarto.Proof + else + typ = refType(el.identifier) + my_types = theorem_types + theorem_ctor = quarto.Theorem + end + + if not typ then + return el + end + + if not callouty_meta or not callouty_meta[typ] then -- metadata not given, return as is + return el + end + + local override_title = false + local callout_tbl = {type = "note"} + if type(callouty_meta[typ]) == "table" then + override_title = callouty_meta[typ]["override-title"] or override_title + if type(callouty_meta[typ]["callout"]) == "table" then + callout_tbl = shallow_copy(callouty_meta[typ]["callout"]) + end + end + + local source_attr = (el.div and el.div.attr) or el.attr + if override_title and my_types[typ] then + callout_tbl.title = spawn_callout_title(my_types[typ].title, el.name) + end + + apply_div_callout_overrides(callout_tbl, source_attr) + + callout_tbl.content = theorem_ctor(el) + return quarto.Callout(callout_tbl) +end + +-- Run in two passes so we process metadata +-- and then process the divs +return { + { Meta = read_meta }, + { Theorem = function(el) return calloutify(el, false) end, + Proof = function(el) return calloutify(el, true) end + } +} diff --git a/altdoc/quarto_website.yml b/altdoc/quarto_website.yml index 3a4362cf..8672cd92 100644 --- a/altdoc/quarto_website.yml +++ b/altdoc/quarto_website.yml @@ -81,5 +81,71 @@ format: filters: - slidebreak + - d-morrison/div-anchors + - d-morrison/equation-anchors + - sun123zxy/callouty-theorem - quarto +callouty-theorem: + thm: + override-title: false + callout: + type: note + appearance: minimal + lem: + override-title: false + callout: + type: note + appearance: minimal + cor: + override-title: false + callout: + type: note + appearance: minimal + prp: + override-title: false + callout: + type: note + appearance: minimal + cnj: + override-title: false + callout: + type: note + appearance: minimal + def: + override-title: false + callout: + type: tip + appearance: minimal + exm: + override-title: false + callout: + type: tip + appearance: minimal + exr: + override-title: false + callout: + type: tip + appearance: minimal + proof: + override-title: true + callout: + type: note + appearance: default + collapse: true + icon: true + solution: + override-title: true + callout: + type: note + appearance: default + collapse: true + icon: false + remark: + override-title: true + callout: + type: note + appearance: default + collapse: false + icon: true +