Skip to content
Draft
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 NEWS.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
7 changes: 7 additions & 0 deletions altdoc/_extensions/d-morrison/div-anchors/_extension.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
title: Div Anchors
author: d-morrison
version: 1.0.0
quarto-required: ">=1.3.0"
contributes:
filters:
- div-anchors.lua
36 changes: 36 additions & 0 deletions altdoc/_extensions/d-morrison/div-anchors/div-anchors.css
Original file line number Diff line number Diff line change
@@ -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;
}
84 changes: 84 additions & 0 deletions altdoc/_extensions/d-morrison/div-anchors/div-anchors.js
Original file line number Diff line number Diff line change
@@ -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();
}
})();
24 changes: 24 additions & 0 deletions altdoc/_extensions/d-morrison/div-anchors/div-anchors.lua
Original file line number Diff line number Diff line change
@@ -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
7 changes: 7 additions & 0 deletions altdoc/_extensions/d-morrison/equation-anchors/_extension.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
title: Equation Anchors
author: d-morrison
version: 1.0.0
quarto-required: ">=1.2.0"
contributes:
filters:
- equation-anchors.lua
196 changes: 196 additions & 0 deletions altdoc/_extensions/d-morrison/equation-anchors/equation-anchors.lua
Original file line number Diff line number Diff line change
@@ -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 [[
<script>
function getQuartoHeadingAnchorIcon() {
const afterBodyScript = document.getElementById("quarto-html-after-body");
if (!afterBodyScript) {
return null;
}

const match = afterBodyScript.textContent.match(/const icon = ["']([^"']+)["']/);
return match ? match[1] : null;
}

// Quarto chain-link glyph from Bootstrap Icons.
const chainLinkIconFallback = "\ue9cb";
const headingAnchorIcon = getQuartoHeadingAnchorIcon();
const defaultAnchorIconFallback =
headingAnchorIcon && headingAnchorIcon !== "#" ? headingAnchorIcon : chainLinkIconFallback;

function getDefaultAnchorTemplate() {
// Prefer Quarto heading anchors first so the visible chain-link icon is reused.
const defaultAnchor =
document.querySelector(".anchored > a.anchorjs-link") ||
document.querySelector("a.anchorjs-link[data-anchorjs-icon]");
if (!defaultAnchor) {
return null;
}

return {
// Detect icon value from the template anchor's attribute or text.
icon: defaultAnchor.getAttribute("data-anchorjs-icon") || defaultAnchor.textContent.trim(),
hasDataIcon: defaultAnchor.hasAttribute("data-anchorjs-icon"),
style: defaultAnchor.getAttribute("style")
};
}

function normalizeAnchorIcon(icon) {
if (!icon || icon === "#") {
return defaultAnchorIconFallback;
}

return icon;
}

function createChainLinkSvgIcon() {
const svgNs = "http://www.w3.org/2000/svg";
const svg = document.createElementNS(svgNs, "svg");
svg.setAttribute("viewBox", "0 0 16 16");
svg.setAttribute("fill", "currentColor");
svg.setAttribute("aria-hidden", "true");
svg.setAttribute("focusable", "false");
svg.setAttribute("class", "equation-anchor-icon");

[
"M6.354 5.5H4a3 3 0 0 0 0 6h3a3 3 0 0 0 2.83-2H11a4 4 0 0 1-4 3H4a4 4 0 0 1 0-8h2.354z",
"M9.646 10.5H12a3 3 0 1 0 0-6H9a3 3 0 0 0-2.83 2H5a4 4 0 0 1 4-3h3a4 4 0 0 1 0 8H9.646z",
"M5.5 8a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 0 1H6a.5.5 0 0 1-.5-.5"
].forEach(function (pathData) {
const path = document.createElementNS(svgNs, "path");
path.setAttribute("d", pathData);
svg.appendChild(path);
});

return svg;
}

function alignEquationAnchorWithDefault(anchor, template) {
if (!anchor) {
return;
}

anchor.classList.remove("external");
anchor.classList.add("no-external");

const normalizedIcon = normalizeAnchorIcon(template && template.icon);
if (normalizedIcon === chainLinkIconFallback) {
anchor.removeAttribute("data-anchorjs-icon");
anchor.textContent = "";
anchor.appendChild(createChainLinkSvgIcon());
} else if (template && template.hasDataIcon) {
anchor.setAttribute("data-anchorjs-icon", normalizedIcon);
anchor.textContent = normalizedIcon;
} else {
anchor.removeAttribute("data-anchorjs-icon");
anchor.textContent = normalizedIcon;
}

if (template && template.style) {
anchor.setAttribute("style", template.style);
} else {
anchor.removeAttribute("style");
}
}

function alignEquationAnchorsWithDefault() {
const template = getDefaultAnchorTemplate();
document.querySelectorAll("a.equation-anchor").forEach(function (anchor) {
alignEquationAnchorWithDefault(anchor, template);
});
}

function ensureEquationAnchorStyles() {
if (document.getElementById("equation-anchor-styles")) {
return;
}

const style = document.createElement("style");
style.id = "equation-anchor-styles";
style.textContent = `
.equation-anchor-target {
--equation-anchor-offset: 1.2rem;
position: relative;
display: block;
}
.equation-anchor {
position: absolute;
left: calc(-1 * var(--equation-anchor-offset));
top: 50%;
transform: translateY(-50%);
text-decoration: none;
opacity: 0.7;
font-size: 0.9em;
}
.equation-anchor:hover {
opacity: 1;
}
.equation-anchor-icon {
width: 0.9em;
height: 0.9em;
vertical-align: -0.125em;
}
`;
document.head.appendChild(style);
}

document.addEventListener("DOMContentLoaded", function () {
ensureEquationAnchorStyles();

let counter = 0;

document.querySelectorAll(".math.display").forEach(function (mathEl) {
// For labeled equations, Quarto wraps the math display span in an eq-* span.
// For unlabeled equations, use the parent element (typically a <p>).
const labeledContainer = mathEl.closest("[id^='eq-']");
const target = labeledContainer || mathEl.parentElement;

if (!target) {
return;
}

// Skip if already processed.
if (target.querySelector(".equation-anchor")) {
return;
}

// Use the labeled container's id, or assign an auto-generated id.
let id = labeledContainer ? labeledContainer.id : target.id;
if (!id) {
counter++;
id = "eq-anchor-" + counter;
target.id = id;
}

target.classList.add("equation-anchor-target");

const anchor = document.createElement("a");
anchor.className = "equation-anchor anchorjs-link";
anchor.href = "#" + id;
anchor.setAttribute("aria-label", "Permalink to this equation");
target.appendChild(anchor);
});

alignEquationAnchorsWithDefault();
});

window.addEventListener("load", alignEquationAnchorsWithDefault);
</script>
]]
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
7 changes: 7 additions & 0 deletions altdoc/_extensions/sun123zxy/callouty-theorem/_extension.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
title: callouty-theorem
author: sun123zxy
version: 1.1.0
quarto-required: ">=1.6.0"
contributes:
filters:
- callouty-theorem.lua
Loading
Loading