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
3 changes: 2 additions & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@
"skills": [
"./brand-yml",
"./quarto/quarto-authoring",
"./quarto/quarto-alt-text"
"./quarto/quarto-alt-text",
"./quarto/quarto-lua"
]
}
]
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
32 changes: 21 additions & 11 deletions quarto/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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.
Expand All @@ -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

Expand Down
181 changes: 181 additions & 0 deletions quarto/quarto-lua/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
---
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.9.30 (2026-03-09).

## 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`
Task: Custom AST nodes / filter timing -> Read `references/custom-ast-nodes.md` in this skill directory

Fetch only pages relevant to the current task.

## 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
```

## 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)
- [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)
84 changes: 84 additions & 0 deletions quarto/quarto-lua/references/custom-ast-nodes.md
Original file line number Diff line number Diff line change
@@ -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
}
}
```