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
10 changes: 10 additions & 0 deletions .claude/rules/dev-tools/dependency-updates.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
description: "Bundled dependency version bumps (Deno, Pandoc, Dart Sass, Typst, esbuild, veraPDF)"
paths:
- "configuration"
- "src/command/check/check.ts"
---

# Bundled Dependency Updates

Bumping any bundled binary version starts here: see `dev-docs/upgrade-dependencies.md` for the general procedure (version numbers, `check.ts` constraints, the installer signing/notarization dry-run gate before merging). For Pandoc specifically, `dev-docs/upgrade-dependencies.md` links to `dev-docs/update-pandoc-checklist.md` (template resync, schema/lua-types checks, CI smoke-test path).
9 changes: 8 additions & 1 deletion .claude/rules/formats/pandoc-templates.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
---
paths:
- "src/resources/formats/pdf/pandoc/**"
- "src/resources/formats/beamer/pandoc/**"
- "src/resources/formats/typst/pandoc/**"
- "src/resources/formats/html/pandoc/**"
- "src/resources/formats/revealjs/pandoc/**"
- "src/resources/formats/asciidoc/pandoc/**"
- "package/src/common/update-pandoc.ts"
- "dev-docs/update-pandoc-checklist.md"
---

# Pandoc Templates (LaTeX & Typst)
# Pandoc Templates (LaTeX, Typst, HTML, Reveal.js, AsciiDoc)

For how Pandoc's LaTeX templates are copied into Quarto and restructured into a modular form (`latex.template`, `latex.common`, …), see `llm-docs/pandoc-quarto-latex-templates.md`.

For the equivalent Typst template integration (`typst.template`, `template.typst`), see `llm-docs/pandoc-quarto-typst-templates.md`.

Every format's `pandoc/` directory has the same split: a dev-reference-only copy of Pandoc's own template (unreferenced by any TypeScript code — kept only so a maintainer can diff it against a fresh Pandoc checkout) alongside Quarto's actual, wired-up template. `dev-docs/update-pandoc-checklist.md` has the full per-format file list (pdf, beamer, html, revealjs, asciidoc, typst) and the naming gotcha that makes the reference-copy file easy to miss when checking what's in a directory.
9 changes: 9 additions & 0 deletions .github/actions/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Composite Actions

Reusable steps shared across workflows in this repo. See also `.github/workflows/actions/` for another set of composite actions (historical split, no functional difference).

| Action | Purpose |
|--------|---------|
| [`cache-typst`](cache-typst/action.yml) | Configures caching for Typst packages. |
| [`docker`](docker/action.yml) | Builds and pushes a Quarto Docker container to the GitHub Container Registry. |
| [`merge-extension-tests`](merge-extension-tests/action.yml) | Copies test files from extension subtrees into the main test directories. |
13 changes: 13 additions & 0 deletions .github/workflows/actions/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Composite Actions

Reusable steps shared across workflows in this repo. See also `.github/actions/` for another set of composite actions (historical split, no functional difference).

| Action | Purpose |
|--------|---------|
| [`amplitude`](amplitude/action.yml) | Sends an event to Amplitude. |
| [`archive`](archive/action.yml) | Archives a dependency binary to S3. |
| [`keychain`](keychain/action.yml) | Configures a P12 certificate into the keychain for signing. |
| [`pandoc-override`](pandoc-override/action.yml) | Installs an unarchived Pandoc release and points quarto at it via `QUARTO_PANDOC`, for smoke-testing a version before it has been archived to S3. |
| [`prevent-rerun`](prevent-rerun/action.yml) | Fails if the workflow is re-run (`github.run_attempt > 1`). |
| [`quarto-dev`](quarto-dev/action.yml) | Configures the image for Quarto development (runs `configure.sh`/`.cmd`). |
| [`sign-files`](sign-files/action.yml) | Installs and configures the environment to sign files. |
38 changes: 38 additions & 0 deletions .github/workflows/actions/pandoc-override/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: "Pandoc Override"
description: "Installs an unarchived Pandoc release and points quarto at it via QUARTO_PANDOC, for smoke-testing a version before it has been archived to S3. No-op unless pandoc-version is set - the archived/configured binary is used as normal otherwise."

inputs:
pandoc-version:
description: "Pandoc release tag to install and use instead of the archived/configured version (e.g. 3.10.1). Leave empty to skip."
required: false
default: ""

runs:
using: "composite"
steps:
- name: Install Pandoc override
if: inputs.pandoc-version != ''
uses: r-lib/actions/setup-pandoc@v2
with:
pandoc-version: ${{ inputs.pandoc-version }}

- name: Point QUARTO_PANDOC at override (Linux)
if: runner.os == 'Linux' && inputs.pandoc-version != ''
shell: bash
run: echo "QUARTO_PANDOC=$(command -v pandoc)" >> "$GITHUB_ENV"

- name: Point QUARTO_PANDOC at override (Windows)
if: runner.os == 'Windows' && inputs.pandoc-version != ''
shell: pwsh
run: |
"QUARTO_PANDOC=$((Get-Command pandoc).Source)" >> $env:GITHUB_ENV

- name: Confirm Pandoc override is live (Linux)
if: runner.os == 'Linux' && inputs.pandoc-version != ''
shell: bash
run: quarto pandoc --version

- name: Confirm Pandoc override is live (Windows)
if: runner.os == 'Windows' && inputs.pandoc-version != ''
shell: pwsh
run: quarto pandoc --version
9 changes: 9 additions & 0 deletions .github/workflows/test-smokes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ on:
required: false
type: string
default: ""
pandoc-override-version:
description: "Pandoc release tag to download from GitHub and use via QUARTO_PANDOC instead of the archived/configured version (e.g. 3.10.1) - for smoke-testing a version before it has been archived to S3. Leave empty for normal behavior."
required: false
type: string
default: ""
schedule:
- cron: "0 6 * * *"

Expand Down Expand Up @@ -229,6 +234,10 @@ jobs:

- uses: ./.github/workflows/actions/quarto-dev

- uses: ./.github/workflows/actions/pandoc-override
with:
pandoc-version: ${{ inputs.pandoc-override-version }}

- name: Install Tinytex
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand Down
60 changes: 54 additions & 6 deletions dev-docs/update-pandoc-checklist.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,35 @@
## Updating the bundled version of Pandoc

Carlos needs to run this:
Updating Pandoc happens in two phases. Phase 1 (prep) needs no S3 access and can be done by anyone; Phase 2 (archival) is done by whoever holds S3 write credentials, right before merge.

- [ ] Ensure archives are upgraded
- [ ] Run `AWS_PROFILE=... ./package/src/quarto-bld update-pandoc PANDOC_VERSION`
- [ ] look at `git diff`, specifically for changes in Pandoc templates, and adjust as needed.
### Phase 1 - prep (no S3, anyone)

As a reminder, our templates are kept in the same directories as Pandoc's templates, but with different names. `git diff` will show the diff in Pandoc's template; we have to manually patch
ours. (We can't just use `patch` because the templates have diverged too much)
Patch Quarto's wired-up template files and regenerate the dev-reference copies + `src/core/pandoc/format-extension.ts`, then verify rendering - all while `configuration`'s `export PANDOC=` line stays pinned at the currently-archived version, so plain `./configure.sh`/`.cmd` keeps working for everyone else.

Run the template/variant regeneration with `--skip-archive`, pointing `QUARTO_PANDOC` at a real binary of the target version:

```bash
QUARTO_PANDOC=/path/to/pandoc-3.10.1/bin/pandoc \
./package/src/quarto-bld update-pandoc 3.10.1 --skip-archive
```

`--skip-archive` skips the S3 archival, the `configureDependency` download, and the `configuration` rewrite. It still runs `writePandocTemplates` (regenerates every mapped template + dev-reference copy) and `writeVariants` (regenerates `format-extension.ts`). `QUARTO_PANDOC` is **required** with `--skip-archive`: `writeVariants` shells out to that binary to enumerate formats/extensions, and without the override it would silently regenerate `format-extension.ts` from the old configured Pandoc. The command leaves everything as an uncommitted working-tree diff for you to review and commit yourself; it never commits.

Running `update-pandoc ... --skip-archive` is itself a safety-net checkpoint: if it produces a diff on a template or dev-reference file that Phase 1 was supposed to have already hand-patched, Phase 1 missed that file - exactly the naming trap described below (this bit twice in one session, on `latex.common` and on the real `styles.html`). A diff limited to `format-extension.ts` is a normal generated change to review, not a miss. The same checkpoint applies to the full Phase 2 run.

Rendering changes can also be smoke-tested before archival: dispatch `.github/workflows/test-smokes.yml` with the `pandoc-override-version` input set to the new release tag (e.g. `3.10.1`). It downloads that Pandoc release directly from GitHub, points `quarto` at it via `QUARTO_PANDOC` for that run only, and leaves the archived/configured version untouched - no S3 access needed. Scope `buckets` to the relevant smoke-all dirs (e.g. `typst`, `latex`, `table`) for a faster signal.

### Phase 2 - archival (S3 credential holder, before merge)

Whoever holds S3 write credentials does this last, right before merging:

- [ ] Bump `configuration`'s `export PANDOC=` line and the `Pandoc` entry in `src/command/check/check.ts`'s `versionConstraints` array to the new version (this constraint tracks the bundled version exactly).
- [ ] Ensure archives are upgraded.
- [ ] Run `AWS_PROFILE=... ./package/src/quarto-bld update-pandoc PANDOC_VERSION` (without `--skip-archive`) to archive the binary to S3, configure it locally, and regenerate templates/variants.
- [ ] Look at `git diff`, specifically for changes in Pandoc templates, and adjust as needed. As in Phase 1, a diff on an already-patched file means something was missed.
- [ ] Run the `create-release.yml` dry-run signing/notarization gate (see `dev-docs/upgrade-dependencies.md`), then merge.

As a reminder, our templates are kept in the same directories as Pandoc's templates, but with different names. `git diff` will show the diff in Pandoc's template; we have to manually patch ours. (We can't just use `patch` because the templates have diverged too much)

### Pandoc templates

Expand All @@ -25,6 +47,32 @@ Partials:
- Ours:
- src/resources/formats/beamer/pandoc/common.latex

#### pdf / latex

- Pandoc's: src/resources/formats/pdf/pandoc/latex.template
- Ours: src/resources/formats/pdf/pandoc/template.tex (see `llm-docs/pandoc-quarto-latex-templates.md` for the full partial breakdown, including `latex.common`/`common.latex`)

#### html

- Pandoc's: src/resources/formats/html/pandoc/html.template, src/resources/formats/html/pandoc/html.styles
- Ours: src/resources/formats/html/pandoc/template.html, src/resources/formats/html/pandoc/styles.html

#### revealjs

- Pandoc's: src/resources/formats/revealjs/pandoc/revealjs.template
- Ours: src/resources/formats/revealjs/pandoc/template.html

#### asciidoc

- Pandoc's: src/resources/formats/asciidoc/pandoc/asciidoc.template
- Ours: src/resources/formats/asciidoc/pandoc/template.asciidoc

#### typst

See `llm-docs/pandoc-quarto-typst-templates.md` - typst has an extra wrinkle where Pandoc's own `template.typst` partial is also kept verbatim (same filename) alongside the renamed `typst.template`.

**All of the "Pandoc's" files above are dev-reference-only**: none of them are read by any TypeScript code path (confirmed via `grep -rn "<filename>" src/ --include="*.ts"` returning zero matches for each, across every format listed here). They exist purely so `git diff` against a fresh Pandoc checkout shows what changed upstream - patch the corresponding "Ours" file (or its own sub-partials, e.g. `tables.tex`, `toc.tex`) to actually change rendered behavior, and update the "Pandoc's" copy too so the next resync's diff stays meaningful. It's easy to update one and miss the other (or vice versa) since the filenames differ only by which segment comes first - use an unfiltered `ls`/`Glob **/*` on the directory when checking what's there, not an extension-filtered glob (e.g. `*.latex` silently excludes `latex.common`, which ends in `.common`). Because `writePandocTemplates` (in `package/src/common/update-pandoc.ts`) overwrites every "Pandoc's" copy wholesale on each run, never hand-fix an upstream bug in one of those reference copies - the change would silently vanish on the next resync, and the reference copy must stay byte-identical to upstream so the diff stays meaningful. (This session deliberately preserved a real upstream typo in `typst.template`'s divider-fallback polyfill for exactly this reason.) Behavioral fixes belong only in the corresponding "Ours" file (or its sub-partials).

## Manual steps

- [ ] Update schemas by inspecting [their changelog](https://github.com/jgm/pandoc/blob/main/changelog.md) for new commands, deprecation removals, etc
Expand Down
4 changes: 3 additions & 1 deletion dev-docs/upgrade-dependencies.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
Change version numbers in `./configuration` to correspond to new versions.

Update hardcoded version strings in `src/command/check/check.ts` (`versionConstraints` array, ~line 249) so that they match the new versions in `configuration`. The `configuration` file warns about this in a comment.
Update hardcoded version strings in `src/command/check/check.ts` (`versionConstraints` array, ~line 249) so that they match the new versions in `configuration`. The `configuration` file warns about this in a comment. This constraint tracks the bundled version exactly (verified via history: `3.8.3` → `3.10.0` for Pandoc alongside the same PR's Dart Sass/Typst bumps), not a looser minimum floor.

**Pandoc specifically** has its own checklist: [update-pandoc-checklist.md](update-pandoc-checklist.md) - template resync (with a naming gotcha that's easy to miss), schema/lua-types checks, and a `QUARTO_PANDOC`-based CI path for smoke-testing a version before it's archived to S3.

## Verify installer signing & notarization before merging (bundled binaries)

Expand Down
23 changes: 12 additions & 11 deletions llm-docs/localization-architecture.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
main_commit: eca40cdab
analyzed_date: 2026-05-21
main_commit: d30cdbb9e
analyzed_date: 2026-07-23
key_files:
- src/resources/language/_language.yml
- src/resources/language/_language-fr.yml
Expand All @@ -16,14 +16,13 @@ key_files:
- src/resources/filters/modules/authors.lua
- src/resources/filters/modules/callouts.lua
- src/resources/filters/layout/meta.lua
- src/resources/formats/html/pandoc/html.template
- src/resources/formats/html/pandoc/template.html
- src/resources/formats/html/pandoc/toc.html
- src/resources/formats/html/pandoc/title-block.html
- src/resources/formats/html/templates/title-metadata.html
- src/resources/formats/pdf/pandoc/latex.template
- src/resources/formats/pdf/pandoc/babel-lang.tex
- src/resources/formats/pdf/pandoc/toc.tex
- src/resources/formats/beamer/pandoc/beamer.template
- src/resources/formats/typst/pandoc/typst.template
- src/resources/formats/beamer/pandoc/toc.tex
- src/resources/formats/typst/pandoc/quarto/typst-template.typ
- src/resources/formats/typst/pandoc/quarto/typst-show.typ
- src/resources/extension-subtrees/orange-book/_extensions/orange-book/typst-show.typ
Expand Down Expand Up @@ -151,7 +150,9 @@ These bypass both Pandoc metadata and Lua filters. The string lands directly in

`quarto.*` is an internal namespace. The user-facing override path for localized strings is the top-level `language:` YAML key (resolved by `formatLanguage`, already merged into `options.format.language` before the builder runs). The schema does not advertise `variables.quarto.*` as a user option, but the merge in `generateDefaults` honors a user-set value on collision: it deep-merges via `mergeConfigs` (`src/core/config.ts`) so user-supplied keys win at any nesting depth while all other localized values remain available. For example, a user setting `variables.quarto.language.crossref-ch-prefix: Bouquin` overrides only that one leaf — `$quarto.language.toc-title-document$` still resolves to the localized fallback. (Same helper is used in `src/core/language.ts:formatLanguage` to merge user-supplied `language:` onto `_language.yml` defaults.) A non-object `variables.quarto` (string, number, array) is ignored defensively. New contributors to this file should design around the `language:` path, not around the escape hatch.

From any Pandoc template — built-in (`html.template`, `latex.template`, `typst-template.typ`), extension partial, or custom user template — every localized key is then accessible as:
From any Pandoc template — built-in (`template.html`, `template.tex`, `typst-template.typ`), extension partial, or custom user template — every localized key is then accessible as:

> **Naming note**: Quarto's actual, rendered templates are `template.html` (HTML), `template.tex` (LaTeX/PDF and Beamer), and `quarto/typst-template.typ` (Typst). The similarly-named `html.template`, `latex.template`, `beamer.template`, `typst.template`, `revealjs.template`, and `asciidoc.template` files elsewhere in these `pandoc/` directories are dev-reference-only copies of Pandoc's own default templates (zero references in `src/**/*.ts` — confirmed via grep), kept solely so a maintainer can diff them against a fresh Pandoc checkout during a version bump. Citations below point at the real, rendered files.

```pandoc
$quarto.language.<key>$
Expand All @@ -173,9 +174,9 @@ It does **not** replace 2a — Lua filters still need `param("key")` because Pan

Localization paths used:

- **Document `lang` attribute**: `_language.yml` lookup not involved. `lang:` flows as Pandoc-native metadata, template renders `<html lang="$lang$" xml:lang="$lang$">` (`html.template:2`).
- **TOC title**: `$toc-title$` resolved via channel 2b. Used in `html.template:60`, `toc.html:3`.
- **Abstract title**: `$abstract-title$` resolved via 2b. Used in `html.template:51`, `title-block.html:15`.
- **Document `lang` attribute**: `_language.yml` lookup not involved. `lang:` flows as Pandoc-native metadata, template renders `<html$if(lang)$ lang="$lang$" xml:lang="$lang$"$endif$>` (`template.html:2`).
- **TOC title**: `$toc-title$` resolved via channel 2b. Used in `toc.html:3`.
- **Abstract title**: `$abstract-title$` resolved via 2b. Used in `title-block.html:15`.
- **Title block labels** (`Authors`, `Affiliations`, `Published`, `Modified`, `Doi`, `Abstract`, `Keywords`): `$labels.*$` written into meta by `modules/authors.lua:854-913` (`computeLabels`). Templates: `title-metadata.html:3,4,32,43,52,61,74,83`, `manuscript/title-metadata.html:6,7,37,48,57,66,83,92`.
- **Crossref text** (`Figure 1.1`, `Table 2.1`): assembled in Lua by `crossref/format.lua` using `title()` / `refPrefix()` which call `param("crossref-<type>-title"/"-prefix")`. Written as inline text directly into the AST. By the time Pandoc renders HTML, the localized prefix is already document content.
- **Callout titles** (`Tip`, `Note`, etc.): `modules/callouts.lua:15,185` reads `param("callout-<type>-title", default)`, writes into the callout node.
Expand All @@ -191,7 +192,7 @@ Three localization paths layered:

Other Lua filters use `metaInjectLatex` for non-language LaTeX customization (`crossref/custom.lua:78`, `layout/meta.lua`, `quarto-post/landscape.lua`, etc. — they inject packages or styling, not localized strings).

3. **Pandoc template `$var$`** — `pdf/pandoc/toc.tex:3` and `pdf/pandoc/latex.template:93-94` use `$toc-title$` to set `\contentsname`. Beamer templates (`beamer/pandoc/beamer.template:146-151`, `beamer/pandoc/toc.tex:3`) likewise.
3. **Pandoc template `$var$`** — `pdf/pandoc/toc.tex:2-3` uses `$toc-title$` to set `\contentsname`. Beamer's `beamer/pandoc/toc.tex:2-3` does the same.

PDF-side TS code does not read `format.language` directly except `src/format/pdf/format-pdf.ts:242` which registers `"babel-lang"` as a Quarto partial.

Expand Down
Loading
Loading