From e8ead934c92bcd7009e7aa01a20d0c47bd17240e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Canouil?= <8896044+mcanouil@users.noreply.github.com> Date: Mon, 9 Mar 2026 21:24:05 +0100 Subject: [PATCH 1/5] feat(quarto): add quarto-lua skill for Lua shortcodes and filters Add a new skill for writing Lua shortcodes and filters in Quarto. Covers handler patterns, Lua style conventions, Quarto-specific APIs, and delegates to Quarto's .llms.md pages for detailed API reference. --- .claude-plugin/marketplace.json | 3 +- README.md | 1 + quarto/README.md | 32 ++++--- quarto/quarto-lua/SKILL.md | 164 ++++++++++++++++++++++++++++++++ 4 files changed, 188 insertions(+), 12 deletions(-) create mode 100644 quarto/quarto-lua/SKILL.md diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 4edc581..8bb7ca4 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -74,7 +74,8 @@ "skills": [ "./brand-yml", "./quarto/quarto-authoring", - "./quarto/quarto-alt-text" + "./quarto/quarto-alt-text", + "./quarto/quarto-lua" ] } ] diff --git a/README.md b/README.md index f882fac..ba7558d 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,7 @@ Skills for Quarto document creation and publishing. - **[brand-yml](./brand-yml/)** - Create and apply brand.yml files for consistent styling across Quarto projects, supporting HTML documents, dashboards, RevealJS presentations, Typst PDFs, and websites with automatic brand discovery and theme layering - **[authoring](quarto/README.md#quarto-authoring-skill)** - Comprehensive guidance for Quarto document authoring and R Markdown migration. Write new Quarto documents with best practices, convert R Markdown files, migrate bookdown/blogdown/xaringan/distill projects, and use Quarto-specific features like hashpipe syntax, cross-references, callouts, and extensions - **[quarto-alt-text](./quarto/quarto-alt-text/)** - Generate accessible alt text for figures in Quarto documents using Amy Cesal's three-part formula (chart type, data description, key insight). Supports code-generated plots and static images +- **[quarto-lua](./quarto/quarto-lua/)** - Write Lua shortcodes and filters for Quarto, with runtime access to Quarto's LLM-optimised API documentation ## Installation diff --git a/quarto/README.md b/quarto/README.md index 61a853e..9cc15c5 100644 --- a/quarto/README.md +++ b/quarto/README.md @@ -76,10 +76,11 @@ Comprehensive guidance for Quarto document authoring and R Markdown migration. W Create and use `_brand.yml` files for consistent branding across Quarto documents and Shiny applications. Use when working with brand styling, corporate identity, colors, fonts, or logos in Quarto projects. **Organization**: Main skill file includes workflows and decision tree. Reference files provide framework-specific integration guides: -- `brand-yml-spec.md` - Complete brand.yml specification -- `shiny-r.md` - Shiny for R integration with bslib -- `shiny-python.md` - Shiny for Python integration with ui.Theme -- `quarto.md` - Quarto integration for all formats (HTML, dashboards, RevealJS presentations, Typst PDFs, websites) + +- `brand-yml-spec.md` - Complete brand.yml specification. +- `shiny-r.md` - Shiny for R integration with bslib. +- `shiny-python.md` - Shiny for Python integration with ui.Theme. +- `quarto.md` - Quarto integration for all formats (HTML, dashboards, RevealJS presentations, Typst PDFs, websites). **Note**: This skill is also registered in the shiny category since brand.yml works across both Shiny and Quarto projects. @@ -92,6 +93,16 @@ Create and use `_brand.yml` files for consistent branding across Quarto document --- +### `lua` + +Write Lua shortcodes and filters for Quarto. Covers shortcode handlers, Pandoc AST filters, Lua coding conventions, Quarto-specific Lua APIs, and common patterns. Includes runtime access to Quarto's LLM-optimised documentation (`.llms.md` pages) for detailed API reference. + +#### Authors + +- [Mickaël CANOUIL](https://github.com/mcanouil) + +--- + ### `quarto-alt-text` Generate accessible alt text for data visualizations in Quarto documents. Use when adding, improving, or reviewing `fig-alt` for figures in `.qmd` files, or when making documents more accessible for screen readers. @@ -106,13 +117,12 @@ Generate accessible alt text for data visualizations in Quarto documents. Use wh This category could include skills for: -- Publishing workflows -- Extension development -- Template creation -- Multi-format output -- Parameterized reports -- Website and book publishing -- Presentation design +- Publishing workflows. +- Template creation. +- Multi-format output. +- Parameterized reports. +- Website and book publishing. +- Presentation design. ## Contributing diff --git a/quarto/quarto-lua/SKILL.md b/quarto/quarto-lua/SKILL.md new file mode 100644 index 0000000..e212f74 --- /dev/null +++ b/quarto/quarto-lua/SKILL.md @@ -0,0 +1,164 @@ +--- +name: quarto-lua +description: > + Write Lua shortcodes and filters for Quarto. + Use when creating, debugging, or modifying Lua code that runs inside + Quarto, including shortcode handlers, Quarto Lua filters, and + Quarto-specific Lua APIs. +metadata: + author: Mickaël Canouil (@mcanouil) + version: "1.0" +license: MIT +--- + +# Quarto Lua + +Write Lua shortcodes and filters for Quarto. + +> This skill is based on Quarto CLI v1.8.26. + +## When to Use What + +Task: Write a shortcode -> "Writing a Shortcode" below +Task: Write a filter -> "Writing a Filter" below +Task: Pandoc Lua API (constructors, types, methods) -> WebFetch `https://quarto.org/docs/extensions/lua-api.llms.md` +Task: Debug Lua / tooling -> WebFetch `https://quarto.org/docs/extensions/lua.llms.md` +Task: Shortcode details (args, raw output) -> WebFetch `https://quarto.org/docs/extensions/shortcodes.llms.md` +Task: Filter details (AST traversal, multi-pass) -> WebFetch `https://quarto.org/docs/extensions/filters.llms.md` +Task: Metadata / project filters -> WebFetch `https://quarto.org/docs/extensions/metadata.llms.md` + +Fetch only pages relevant to the current task. +The fetched `.llms.md` pages may contain `.llms.md` URLs for external sites (e.g., `lua.org`, `pandoc.org`). +These are broken links. Only `https://quarto.org` URLs have valid `.llms.md` pages; replace with `.html` for any other domain. + +## Writing a Shortcode + +A shortcode exports a function called whenever `{{< name ... >}}` appears in `.qmd`. +Register under `shortcodes:` in `_extension.yml` or document YAML. +Add a file header (see "Lua File Header Convention"), then: + +```lua +return function(args, kwargs, meta, raw_args) + local name = pandoc.utils.stringify(args[1] or "world") + return pandoc.Str("Hello, " .. name .. "!") +end +``` + +Parameters: `args` (positional, 1-indexed), `kwargs` (named), `meta` (document metadata), `raw_args` (unparsed strings). +Both `args` and `kwargs` contain `pandoc.Inlines`; use `pandoc.utils.stringify()` to get strings. +Return `pandoc.Inlines` or `pandoc.Blocks`. Use `pandoc.RawInline`/`pandoc.RawBlock` for format-specific output. +Verify the exact handler signature against the shortcodes `.llms.md` page when targeting a specific Quarto version. + +## Writing a Filter + +A filter returns a list of handler tables mapping AST element types to transform functions. +Register under `filters:` in `_extension.yml` or document YAML. +Add a file header (see "Lua File Header Convention"), then: + +```lua +local function convert_emph(el) + return pandoc.SmallCaps(el.content) +end + +return { + { Emph = convert_emph } +} +``` + +Each table is a separate traversal pass. Handlers return a replacement element, a list, or `nil` (or nothing) to skip. +Use a `Pandoc(doc)` handler to process the entire document, or `Meta(meta)` to read/modify metadata. +Multiple passes: `return { { Header = fix_headers }, { Link = fix_links } }`. + +## Lua File Header Convention + +Every `.lua` file must start with: + +```lua +--- name - Short description +--- @module name.lua +--- @license MIT +--- @copyright 2026 Author Name +--- @author Author Name +--- @version 0.1.0 +--- @brief One-line summary. +--- @description Longer explanation of purpose and behaviour. +--- Wrap at ~72 chars, indent continuation with two spaces. +``` + +Fields: `@module` (filename), `@license`, `@copyright`, `@author`, `@version` (semver), `@brief` (one-liner), `@description` (multi-line). +Always generate for new files. Update `@version`/`@description` when modifying. + +## Lua Style and Conventions + +- **Naming**: `snake_case` for variables/functions, `PascalCase` for module-level tables only. +- **Indentation**: 2 spaces. +- **Strings**: double quotes for user-facing text, single quotes for identifiers/keys. +- **Scoping**: always `local` unless intentionally global. +- **Errors**: fail fast with `error("context: what went wrong")`. +- **Docs**: `---` comment blocks above functions (LDoc-compatible): + +```lua +--- Convert a Pandoc inline element to plain text. +--- @param el pandoc.Inline The inline element to convert. +--- @return string The plain text representation. +local function stringify_inline(el) + return pandoc.utils.stringify(el) +end +``` + +## Common Patterns + +### `pandoc.utils.stringify()` + +Converts any AST element to plain text. Use for shortcode arguments and metadata fields. + +### Format Detection + +Check the output format before emitting format-specific content: + +```lua +if quarto.doc.is_format("html") then + -- HTML-only logic +end +``` + +### Quarto Document APIs + +```lua +-- HTML only; no-op for PDF/Typst +quarto.doc.add_html_dependency({ + name = "my-dep", version = "0.1.0", + stylesheets = { "style.css" }, scripts = { "script.js" } +}) + +-- Works for all formats (HTML, LaTeX/PDF, Typst) +quarto.doc.include_text("in-header", "...") +-- Positions: "in-header", "before-body", "after-body" +``` + +### Debugging + +```lua +quarto.log.output("my-var:", my_var) +``` + +### Multi-file Modules + +```lua +local utils = require("utils") +``` + +Quarto resolves `require` paths relative to the calling script's directory. + +### Testing + +```bash +quarto render example.qmd +``` + +## Resources + +- [Quarto Lua API](https://quarto.org/docs/extensions/lua-api.html) +- [Pandoc Lua Filters reference](https://pandoc.org/lua-filters.html) +- [Pandoc community Lua filters](https://github.com/pandoc/lua-filters) +- [LuaRocks style guide](https://github.com/luarocks/lua-style-guide) From 564f18ceb64a32cc1d0cb265dadc543c0cb5394e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Canouil?= <8896044+mcanouil@users.noreply.github.com> Date: Mon, 9 Mar 2026 21:26:20 +0100 Subject: [PATCH 2/5] fix(quarto-lua): remove broken links note to external .llms.md pages --- quarto/quarto-lua/SKILL.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/quarto/quarto-lua/SKILL.md b/quarto/quarto-lua/SKILL.md index e212f74..d4c6cd7 100644 --- a/quarto/quarto-lua/SKILL.md +++ b/quarto/quarto-lua/SKILL.md @@ -28,8 +28,6 @@ Task: Filter details (AST traversal, multi-pass) -> WebFetch `https://quarto.org Task: Metadata / project filters -> WebFetch `https://quarto.org/docs/extensions/metadata.llms.md` Fetch only pages relevant to the current task. -The fetched `.llms.md` pages may contain `.llms.md` URLs for external sites (e.g., `lua.org`, `pandoc.org`). -These are broken links. Only `https://quarto.org` URLs have valid `.llms.md` pages; replace with `.html` for any other domain. ## Writing a Shortcode From d28f2b10c7ce5b91e872cef4b498d9846532261e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Canouil?= <8896044+mcanouil@users.noreply.github.com> Date: Mon, 9 Mar 2026 21:43:24 +0100 Subject: [PATCH 3/5] chore: update Quarto CLI version in SKILL.md --- quarto/quarto-lua/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quarto/quarto-lua/SKILL.md b/quarto/quarto-lua/SKILL.md index d4c6cd7..7e88ee3 100644 --- a/quarto/quarto-lua/SKILL.md +++ b/quarto/quarto-lua/SKILL.md @@ -15,7 +15,7 @@ license: MIT Write Lua shortcodes and filters for Quarto. -> This skill is based on Quarto CLI v1.8.26. +> This skill is based on Quarto CLI v1.9.30. ## When to Use What From 0d275b8b4b9e5637cd45f3db0c8a739079918246 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Canouil?= <8896044+mcanouil@users.noreply.github.com> Date: Mon, 9 Mar 2026 21:55:51 +0100 Subject: [PATCH 4/5] chore: add date of last check --- quarto/quarto-lua/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quarto/quarto-lua/SKILL.md b/quarto/quarto-lua/SKILL.md index 7e88ee3..d00aef1 100644 --- a/quarto/quarto-lua/SKILL.md +++ b/quarto/quarto-lua/SKILL.md @@ -15,7 +15,7 @@ license: MIT Write Lua shortcodes and filters for Quarto. -> This skill is based on Quarto CLI v1.9.30. +> This skill is based on Quarto CLI v1.9.30 (2026-03-09). ## When to Use What From ce6bdca9807e8765e504a5b518a480ab38fef58a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Canouil?= <8896044+mcanouil@users.noreply.github.com> Date: Mon, 9 Mar 2026 23:17:38 +0100 Subject: [PATCH 5/5] feat(quarto-lua): add custom AST nodes section and references Include details on custom AST nodes and filter timing in the documentation. This enhances the understanding of Quarto's capabilities for users working with Lua shortcodes and filters. --- quarto/quarto-lua/SKILL.md | 19 +++++ .../quarto-lua/references/custom-ast-nodes.md | 84 +++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 quarto/quarto-lua/references/custom-ast-nodes.md diff --git a/quarto/quarto-lua/SKILL.md b/quarto/quarto-lua/SKILL.md index d00aef1..7cd7d5a 100644 --- a/quarto/quarto-lua/SKILL.md +++ b/quarto/quarto-lua/SKILL.md @@ -26,6 +26,7 @@ Task: Debug Lua / tooling -> WebFetch `https://quarto.org/docs/extensions/lua.ll Task: Shortcode details (args, raw output) -> WebFetch `https://quarto.org/docs/extensions/shortcodes.llms.md` Task: Filter details (AST traversal, multi-pass) -> WebFetch `https://quarto.org/docs/extensions/filters.llms.md` Task: Metadata / project filters -> WebFetch `https://quarto.org/docs/extensions/metadata.llms.md` +Task: Custom AST nodes / filter timing -> Read `references/custom-ast-nodes.md` in this skill directory Fetch only pages relevant to the current task. @@ -154,6 +155,24 @@ Quarto resolves `require` paths relative to the calling script's directory. quarto render example.qmd ``` +## Custom AST Nodes + +Quarto extends Pandoc's AST with custom node types that filters can match by name. + +**Block-level:** Callout, ConditionalBlock, Tabset, PanelLayout, FloatRefTarget, DecoratedCodeBlock, Theorem, Proof. + +**Inline-level:** Shortcode. + +**Other:** LatexEnvironment, LatexInlineCommand, HtmlTag. + +Constructors exist for: `quarto.Callout(tbl)`, `quarto.ConditionalBlock(tbl)`, `quarto.Tabset(tbl)`, `quarto.Tab(tbl)`. + +Cross-referenceable elements (figures, tables, listings) are represented as `FloatRefTarget` nodes. + +Filter timing supports eight phases (`pre-ast` through `post-finalize`) via the `at` property in `_extension.yml`. + +For full constructor signatures and filter timing details, read `references/custom-ast-nodes.md`. + ## Resources - [Quarto Lua API](https://quarto.org/docs/extensions/lua-api.html) diff --git a/quarto/quarto-lua/references/custom-ast-nodes.md b/quarto/quarto-lua/references/custom-ast-nodes.md new file mode 100644 index 0000000..92dbc01 --- /dev/null +++ b/quarto/quarto-lua/references/custom-ast-nodes.md @@ -0,0 +1,84 @@ +# Custom AST Nodes + +Quarto extends Pandoc's AST with custom node types. +Filters match these by name in handler tables (e.g., `{ Callout = handle_callout }`). + +## Available Node Types + +**Block-level:** Callout, ConditionalBlock, Tabset, PanelLayout, FloatRefTarget, DecoratedCodeBlock, Theorem, Proof. + +**Inline-level:** Shortcode. + +**Other:** LatexEnvironment, LatexInlineCommand, HtmlTag. + +Note: `_quarto.ast.add_handler()` is internal to Quarto. +Extension authors interact via standard filter handlers. + +## Constructor Signatures + +### `quarto.Callout(tbl)` + +- `type`: `string` - callout type (note, caution, warning, tip, important). +- `title`: `pandoc.Inlines` or `string` (optional). +- `icon`: `boolean` or `string` (optional, defaults based on type). +- `appearance`: `string` - "minimal", "simple", or "default" (optional). +- `collapse`: collapse state (optional). +- `content`: `pandoc.Blocks` or list (optional, defaults to empty Blocks). +- `attr`: `pandoc.Attr` (optional, defaults to empty Attr). + +### `quarto.ConditionalBlock(tbl)` + +- `node`: `pandoc.Div` - the div holding the content. +- `behavior`: `string` - "content-visible" or "content-hidden". +- `condition`: list of 2-element lists (optional, defaults to `{}`). + Each sublist: `{"when-format"|"unless-format"|"when-profile"|"unless-profile", value}`. + +### `quarto.Tabset(tbl)` + +- `tabs`: list of `quarto.Tab` objects (optional, defaults to empty list). +- `level`: `number` - heading level for tabs (optional, default `2`). +- `attr`: `pandoc.Attr` (optional, defaults to `pandoc.Attr("", {"panel-tabset"})`). + +### `quarto.Tab(tbl)` + +- `title`: `pandoc.Inlines` or `string` (required). +- `content`: `pandoc.Blocks` or `string` (optional, string parsed as markdown). +- `active`: `boolean` (optional, default `false`). + +## Filter Timing + +Eight phases available via the `at` property in `_extension.yml` or document YAML: + +1. `pre-ast` +2. `post-ast` +3. `pre-quarto` +4. `post-quarto` +5. `pre-render` +6. `post-render` +7. `pre-finalize` +8. `post-finalize` + +Syntax in `_extension.yml` or document YAML: + +```yaml +filters: + - path: my-filter.lua + at: pre-quarto +``` + +Default (no `at`): filters listed before a "quarto" marker get `pre-quarto`, filters listed after get `post-render`. + +## FloatRefTarget + +Cross-referenceable elements (figures, tables, listings) are represented as `FloatRefTarget` custom AST nodes. +Filters operating on these should use the `FloatRefTarget` handler: + +```lua +return { + { FloatRefTarget = function(el) + -- el.caption, el.content, el.identifier, etc. + return el + end + } +} +```