-
-
Notifications
You must be signed in to change notification settings - Fork 5
Plugins
Apex supports a lightweight plugin system that lets you add new syntax and post‑processing behavior without patching the core. Plugins can be small scripts (Ruby, Python, etc.) or simple declarative regex rules defined in YAML.
This page covers:
- Where plugins live
- How to enable/disable plugins
- Processing phases
- Plugin manifest format (
plugin.yml) - External handler plugins (scripts/commands)
- Declarative regex plugins (pure YAML)
- Plugin bundles (
bundlekey) - Support directory and environment variables
- Plugin metadata for directories and installers
- Installing plugins from the directory or a Git URL
- Examples:
kbdliquid tag and:emoji:spans
If you are using plugins from Swift/Xcode, see Xcode Integration for guidance on when to use high-level ApexOptions vs low-level apex_options and required imports (Apex + ApexC) for direct C struct field access.
By design, plugins are disabled by default so Apex’s performance and behavior are unchanged unless you explicitly opt in.
-
CLI flags
-
Enable plugins:
apex --plugins input.md -
Disable plugins:
apex --no-plugins input.md
-
Enable plugins:
-
Metadata keys (in the document’s front matter)
-
Any of these keys are recognized (case-insensitive):
pluginsenable-pluginsenable_plugins
-
Example:
--- title: Plugin demo plugins: true ---
-
-
Precedence
- If metadata enables or disables plugins, you can still override it from the CLI:
-
--pluginsforces plugins on -
--no-pluginsforces plugins off
-
- CLI flags always win over metadata.
- If metadata enables or disables plugins, you can still override it from the CLI:
If you never set plugins: true or pass --plugins, Apex will not load or run any plugins.
Plugins are discovered from project and user locations, in this order:
-
Project-local plugins (current directory)
- Directory:
.apex/plugins/under the current working directory. - Structure: one subdirectory per plugin, for example:
.apex/plugins/kbd/plugin.yml.apex/plugins/kbd/kbd_plugin.rb.apex/plugins/emoji/plugin.yml
- Directory:
-
Project-local plugins (base directory)
- If Apex knows a
base_directory(from--base-dir, metadata, or the input file’s directory), it also looks in:<base_directory>/.apex/plugins/
- If Apex knows a
-
Project-local plugins (Git repository root)
- If the current directory is inside a Git repository and
git rev-parse --show-toplevelsucceeds, Apex will also look in:<git-top-level>/.apex/plugins/
- This is only used when the Git top-level is a parent of (or equal to) the current directory.
- If the current directory is inside a Git repository and
-
Global (user) plugins
- Directory (XDG-aware):
- If
$XDG_CONFIG_HOMEis set:$XDG_CONFIG_HOME/apex/plugins/
- Otherwise:
~/.config/apex/plugins/
- If
- Same structure: one subdirectory per plugin:
~/.config/apex/plugins/kbd/plugin.yml~/.config/apex/plugins/emoji/plugin.yml
- Directory (XDG-aware):
Plugin IDs must be unique. If a project plugin and a global plugin share the same id, the project plugin wins and the global one is ignored.
Sometimes you may want to disable a plugin that is installed globally, but only for a specific project. Apex’s lookup order makes this easy: you can shadow a global plugin with a no-op project plugin that has the same id.
Key points:
- Project plugins are loaded before global plugins.
- If two plugins share the same
id, the first one wins and the later one is ignored. - A plugin that has no handler and no regex is a true no-op: it is loaded, but never changes the text.
To disable a global plugin called emoji-span just for one project:
-
Create a project plugins directory:
.apex/ plugins/ emoji-span/ plugin.yml -
Add a minimal no-op manifest that declares the same
idand a phase, but no handler or pattern:--- id: emoji-span phase: post_render # or pre_parse, depending on the original plugin # No handler.command, pattern, or replacement on purpose: # this plugin will be loaded, but it does nothing. ---
When you run Apex with plugins enabled in that project:
- The project-local
emoji-spanplugin is discovered first. - Because a plugin with id
emoji-spanalready exists, the globalemoji-spanplugin is skipped. - Since the project version has no handler or regex, it is effectively a no-op, which means the global plugin is disabled for this project only.
Apex exposes several phases in its pipeline. Plugins can hook into one or more phases; for now, two are wired up:
-
pre_parse- Runs on the raw Markdown text before it is parsed.
- Good for:
- Custom syntax (e.g.
{% kbd ... %}) - Textual rewrites
- Adding/removing markup before Apex sees it
- Custom syntax (e.g.
-
post_render- Runs on the final HTML output after Apex finishes rendering.
- Good for:
- Wrapping elements in spans/divs
- Adding CSS classes
- Simple HTML post-processing (e.g. turning
:emoji:into<span>)
Internally, plugins for each phase are run in a deterministic order:
- Sorted by priority (lower numbers first; default is
100). - Ties broken by plugin
id(lexicographically).
Each plugin is defined by a manifest file:
-
File name:
plugin.yml -
Location: inside the plugin’s directory, e.g.:
.apex/plugins/kbd/plugin.yml ~/.config/apex/plugins/emoji/plugin.yml
At minimum, a plugin needs:
---
id: my-plugin
# Optional, but strongly recommended metadata:
# title: Human-friendly name (if omitted, tools may fall back to id)
# author: Name or handle of the plugin author
# description: Short, human-readable description of what the plugin does
# homepage: URL for the plugin landing page or README
# repo: Canonical Git repository URL used for installation/updates
phase: pre_parse # or post_render
priority: 100 # optional, lower runs earlier
timeout_ms: 0 # optional, 0 = no extra timeout logic (best-effort)
---From there, you choose one of two plugin types:
-
External handler plugin
- Runs an external command (Ruby, Python, shell, etc.).
- Declared with a
handler.commandfield.
-
Declarative regex plugin
- No external code; in-process regex search/replace.
- Declared with
patternandreplacementfields.
You can’t mix both styles in a single plugin; if handler.command is present, the plugin is treated as external.
To support plugin directories, automatic installation, and future auto-update tools, Apex understands several optional metadata fields in plugin.yml:
-
title: Short, human-friendly name for the plugin (e.g.Keyboard Shortcuts). -
author: Free-form author string (your name, handle, or organization). -
description: One–two sentence description of what the plugin does. This is already used in examples and is what directory listings will usually display. -
homepage: Informational URL where users can learn more about the plugin—often a README, documentation site, or the GitHub project page. This field is not used for cloning, but may be shown in--list-pluginsoutput. -
repo: Canonical Git URL for the plugin repository, used by Apex when installing plugins from the central directory (for example,https://github.com/ApexMarkdown/apex-kbd-plugin.git). The repository must contain theplugin.ymlmanifest (and any supporting files) at its root. -
post_install: Optional command that Apex will run after cloning the plugin during--install-plugin. See “Post-install hooks” below.
Only id, phase, and either handler.command (for external plugins) or pattern/replacement (for declarative plugins) are required for execution—everything else is optional metadata used by tools.
External plugins can define an optional post_install command in plugin.yml that Apex will run immediately after cloning the plugin during --install-plugin:
---
id: kbd
# ... other fields ...
post_install: ./post_install.sh
---When you run:
apex --install-plugin kbdApex will:
- Clone the plugin repository into your user plugin directory (e.g.
~/.config/apex/plugins/kbd). - Look for
plugin.yml(orplugin.yaml) in the root of that directory. - If it finds a
post_installkey, run the value as a shell command from the plugin directory (equivalent tocd <plugin_dir> && <post_install>).
This hook is useful for:
- Creating initial configuration files or support data under
APEX_SUPPORT_DIR. - Printing a welcome or usage message via
echo. - Running an interactive setup script that asks the user questions and writes config.
If the post_install command exits with a non-zero status, Apex prints a warning but considers the plugin installed successfully (the clone is not rolled back).
Sometimes it is convenient for a single repository to provide multiple related plugins as a bundle—for example, a “documentation” bundle that contains:
-
kbd– expands{% kbd @3 %}keyboard shortcuts -
prefpane– expands{% prefspane Advanced, Processor %} -
menubar– expands{% menubar File, Open %}
Instead of creating a separate repo (and plugin.yml) for each, Apex supports a bundle syntax in plugin.yml when built with full YAML (libyaml) support.
A bundle manifest has:
- Top-level metadata that applies to the bundle as a whole.
- A
bundle:key whose value is a YAML sequence (array) of per-plugin configs.
Example:
---
id: documentation
title: Documentation helpers
author: Brett Terpstra
description: A bundle of documentation-related helpers (kbd, menubar, prefpane).
homepage: https://github.com/ApexMarkdown/apex-plugin-documentation
repo: https://github.com/ApexMarkdown/apex-plugin-documentation.git
# Each entry in bundle defines a child plugin
bundle:
- id: kbd
title: Keyboard Shortcuts
description: Render {% kbd ... %} key combos to HTML <kbd> elements
phase: pre_parse
priority: 100
handler:
command: "ruby kbd_plugin.rb"
- id: menubar
title: Menubar Paths
description: Render {% menubar File, Open %} to a styled menu path
phase: pre_parse
handler:
command: "ruby menubar_plugin.rb"
- id: prefspane
title: Preferences Pane
description: Render {% prefspane Advanced, Processor %} to a styled preference path
phase: pre_parse
handler:
command: "ruby prefspane_plugin.rb"
---Apex will treat this as three distinct plugins:
kbdmenubarprefspane
all sourced from the same repository and manifest.
When loading a bundle, Apex:
- Parses the top-level mapping (the “bundle header”), and the
bundle:sequence, using full YAML. - For each entry in
bundle:- Starts with the top-level metadata.
- Applies the child entry’s fields as overrides.
Concretely:
- Top-level
author,homepage, andrepoare used as defaults for every child plugin. - Each child must define its own:
idphase- and either:
-
handler.command(external plugin), or -
pattern+replacement(declarative plugin)
-
- A child may also define its own:
titledescriptionprioritytimeout_msflags
- If a child defines
author,homepage, orrepo, those values override the bundle-level defaults for that child only.
After merging, each child is turned into a normal internal plugin record, just like if it had its own standalone plugin.yml.
-
Discovery:
- Bundle manifests live in the same places as normal plugins:
- Project-local:
.apex/plugins/documentation/plugin.yml - Global:
~/.config/apex/plugins/documentation/plugin.yml
- Project-local:
- Bundle manifests live in the same places as normal plugins:
-
Phases and IDs:
- Each child plugin’s
idandphaseare used when registering and running plugins. - All normal rules apply:
- IDs must be unique across all loaded plugins.
- Phase must be
pre_parseorpost_render(for now).
- Each child plugin’s
-
libyaml requirement:
- The
bundlesyntax relies on full YAML parsing vialibyaml. - If Apex is built without libyaml support,
bundle:is not recognized as a special structure; in that case, you should fall back to separate manifests for each plugin if you need cross-platform behavior on builds that omit libyaml.
- The
An external handler plugin defines a command to run, which receives JSON on stdin and writes the transformed text to stdout.
---
id: kbd
title: Keyboard Shortcuts
author: Brett Terpstra
description: Render {% kbd ... %} key combos to HTML
homepage: https://github.com/ApexMarkdown/apex-kbd-plugin
repo: https://github.com/ApexMarkdown/apex-kbd-plugin.git
phase: pre_parse
priority: 100
timeout_ms: 0 # optional
handler:
command: "ruby kbd_plugin.rb"
----
id: unique identifier (no spaces recommended). -
title/author/description/homepage/repo: optional metadata used by listing/installation tools and plugin directories. -
phase:pre_parseorpost_render. -
priority: integer; lower runs first. -
timeout_ms: optional; host may use this as a soft cap. -
handler.command:- Executed via
sh -cfrom the plugin’s directory (APEX_PLUGIN_DIR). - Must be runnable in your environment (e.g. Ruby installed if you use
ruby).
- Executed via
For text phases (pre_parse, post_render), Apex sends your command a JSON object on stdin:
{
"version": 1,
"plugin_id": "kbd",
"phase": "pre_parse",
"text": "raw or rendered text here"
}Your plugin should:
- Read all of stdin.
- Parse the JSON.
- Transform the
textfield. - Print the new text only to stdout (no extra JSON, headers, or logging).
If your plugin fails, times out, or prints nothing, Apex will treat it as a no-op and continue gracefully.
For many cases, you don’t need a script at all. A declarative regex plugin uses regex.h inside Apex for fast in-process search/replace.
---
id: emoji-span
title: Emoji span wrapper
author: Brett Terpstra
description: Wrap :emoji: markers in a span for styling
homepage: https://github.com/ApexMarkdown/apex-emoji-plugin
repo: https://github.com/ApexMarkdown/apex-emoji-plugin.git
phase: post_render
pattern: "(:[a-zA-Z0-9_+-]+:)"
replacement: "<span class=\"emoji\">$1</span>"
flags: "i" # optional: e.g. i, m, s
priority: 200
timeout_ms: 0
----
pattern: POSIX regular expression (compiled viaregcomp). -
replacement:- Replacement string with capture groups like
$1,$2, etc. - Runs repeatedly across the text until no more matches.
- Replacement string with capture groups like
-
flags(optional):- Currently supports:
-
i– case-insensitive -
m– multi-line -
s– dot matches newline (if supported by underlying regex flavor)
-
- Currently supports:
This is ideal when:
- You only need straightforward pattern substitution.
- Performance matters and you want to avoid
fork/exec.
Plugins sometimes need a place to store cache/data files or know which file is being processed. To keep plugin code directories clean and give plugins enough context, Apex exposes a support directory and several environment variables.
Base path:
- If
$XDG_CONFIG_HOMEis set:$XDG_CONFIG_HOME/apex/support/
- Otherwise:
~/.config/apex/support/
For each plugin, Apex creates:
APEX_SUPPORT_DIR/<plugin-id>/
You can safely write caches, logs, or temporary files there.
When Apex runs an external handler plugin, it sets:
-
APEX_PLUGIN_DIR- Filesystem path to the plugin’s directory (where
plugin.ymllives). - Useful for loading sidecar files, templates, etc.
- Filesystem path to the plugin’s directory (where
-
APEX_SUPPORT_DIR- Base support directory as described above.
- You’ll usually combine it with your plugin id, e.g.
$APEX_SUPPORT_DIR/kbd.
-
APEX_FILE_PATH- When Apex is invoked on a file (for example
apex path/to/file.md), this is the original path that was passed on the command line. Plugins can use this to make decisions based on path, directory, or extension. - When Apex reads from stdin (for example
cat file.md | apex),APEX_FILE_PATHis set to:- The current
base_directory(if one was set via--base-diror metadata), or - An empty string (
"") if no base directory is known.
- The current
- An empty
APEX_FILE_PATHis a clear signal that there is no concrete source file path.
- When Apex is invoked on a file (for example
All of these variables apply only during the external command’s execution and are restored afterward.
Apex can install plugins directly from a central directory hosted in the ApexMarkdown/apex-plugins GitHub repository. The directory is published as a JSON file:
-
https://github.com/ApexMarkdown/apex-plugins(repository) -
https://raw.githubusercontent.com/ApexMarkdown/apex-plugins/refs/heads/main/apex-plugins.json(JSON index)
Each entry in apex-plugins.json looks like this:
{
"plugins": [
{
"id": "kbd",
"title": "Keyboard Shortcuts",
"description": "Renders {% kbd %} tags as <kbd> elements.",
"author": "Brett Terpstra",
"homepage": "https://github.com/ApexMarkdown/apex-plugin-kbd",
"repo": "https://github.com/ApexMarkdown/apex-plugin-kbd"
}
]
}-
idmust match the pluginiddeclared in that repo’splugin.yml. -
repois the canonical Git URL that Apex will pass togit clone. -
title,author,description, andhomepageare used for human-readable listings.
To see what plugins are available in the central directory, use:
apex --list-pluginsThis command:
- Fetches
apex-plugins.jsonusingcurl. - Parses the
pluginsarray. - Prints a concise listing to stdout, for example:
## Installed Plugins
kbd - Keyboard Shortcuts (author: Brett Terpstra)
Renders {% kbd %} tags as <kbd> elements.
homepage: https://github.com/ApexMarkdown/apex-kbd-plugin
---
## Available Plugins
emoji-span - Emoji span wrapper (author: Brett Terpstra)
Wrap :emoji: markers in a span for styling.
homepage: https://github.com/ApexMarkdown/apex-emoji-plugin
The --install-plugin command accepts three types of arguments:
-
Plugin ID from the directory (recommended for curated plugins):
apex --install-plugin kbd
This will:
- Fetch
apex-plugins.jsonfrom the central directory. - Find the entry whose
idmatcheskbd. - Read the
repofield and clone the repository. - Install into
$XDG_CONFIG_HOME/apex/plugins/kbdor~/.config/apex/plugins/kbd.
- Fetch
-
Full Git URL (for plugins not in the directory):
apex --install-plugin https://github.com/ttscoff/apex-plugin-kbd.git
Also works with SSH URLs:
apex --install-plugin git@github.com:ttscoff/apex-plugin-kbd.git
-
GitHub shorthand (
user/repoformat):apex --install-plugin ttscoff/apex-plugin-kbd
This is automatically expanded to
https://github.com/ttscoff/apex-plugin-kbd.git.
When installing from a direct Git URL or GitHub shorthand (i.e., anything outside the curated directory), Apex will prompt:
Apex plugins execute unverified code. Only install plugins from trusted sources.
Continue? (y/n)
- Answer
yorYto proceed with the installation. - Any other response (including
n,N, or just pressing Enter) aborts the install.
Directory ID installs (like --install-plugin kbd from the curated list) do not show this prompt, as those plugins have been reviewed and included in the central directory.
For all installation methods, Apex will:
-
Create (if necessary) the user plugin directory:
-
$XDG_CONFIG_HOME/apex/pluginsif$XDG_CONFIG_HOMEis set, or -
~/.config/apex/pluginsotherwise.
-
-
For direct URL/shorthand installs:
- Clone the repository into a temporary directory.
- Read
plugin.yml(orplugin.yaml) from the cloned repo to determine the pluginid. - Move the cloned directory to the final location:
~/.config/apex/plugins/<id>/.
-
For directory ID installs:
- Clone directly into the final location:
~/.config/apex/plugins/<id>/.
- Clone directly into the final location:
-
If the target directory already exists, Apex will refuse to overwrite it and print an error. You can remove or rename the existing directory and rerun
--install-pluginto reinstall. -
After successful cloning, look for a
post_installhook inplugin.ymland run it if present.
Security note: Apex plugins execute unverified code. Only install plugins from trusted sources. When installing from a direct Git URL or GitHub repo name, you will be prompted to confirm before the plugin is cloned.
Once installed, the plugin is just like any other user-global plugin:
- It will be discovered from
$XDG_CONFIG_HOME/apex/pluginsor~/.config/apex/plugins. - You still need to enable plugins via
--pluginsor metadata (plugins: true).
If you are authoring a plugin for inclusion in the directory:
- Make sure your
plugin.ymlhas at leastid,phase, and eitherhandler.commandorpattern/replacement. - Add
title,author,description,homepage, andrepotoplugin.ymlso directory tools and installers have complete metadata. - Ensure the
plugin.ymland any scripts live at the root of the Git repository referenced byrepo.
To remove a locally installed plugin, use:
apex --uninstall-plugin kbdThe --uninstall-plugin command:
- Verifies that the plugin directory exists under the user plugin path (
$XDG_CONFIG_HOME/apex/pluginsor~/.config/apex/plugins). - Prompts for confirmation before deleting the plugin directory.
- Removes only the plugin’s directory; support data under
.../apex/support/<plugin-id>/is left intact.
This command only works for plugins installed in the user plugin directory. Project-local plugins (in .apex/plugins/) must be removed manually by deleting the plugin directory.
To request that your plugin be added to the central directory:
-
Fork the
ApexMarkdown/apex-pluginsrepository on GitHub to your own account or organization. -
Clone your fork locally and create a new branch for your plugin entry (for example,
add-kbd-plugin). - Open the
apex-plugins.jsonfile in the root of your fork and add a new object to thepluginsarray with the fields:-
id(matching theidin your plugin’splugin.yml), -
title, -
description, -
author, -
homepage, -
repo(canonical Git URL for the plugin repo).
-
- Commit your changes and push the branch to your fork on GitHub.
- From your fork, open a pull request against the
mainbranch ofApexMarkdown/apex-plugins, briefly describing your plugin and confirming that theidandrepomatch your published plugin repository. - Once the pull request is reviewed and merged, your plugin will show up in
apex --list-pluginsand be installable viaapex --install-plugin <id>.
This example shows how to support a liquid-style {% kbd ... %} syntax, turning key combos into <kbd> markup.
Project-local example:
.apex/
plugins/
kbd/
plugin.yml
kbd_plugin.rb
Global example:
~/.config/apex/plugins/
kbd/
plugin.yml
kbd_plugin.rb
---
id: kbd
title: Keyboard Shortcuts
author: Brett Terpstra
description: Render {% kbd ... %} key combos to HTML <kbd> elements
homepage: https://github.com/ApexMarkdown/apex-kbd-plugin
repo: https://github.com/ApexMarkdown/apex-kbd-plugin.git
phase: pre_parse
priority: 100
timeout_ms: 0
handler:
command: "ruby kbd_plugin.rb"
---The full script lives in the examples directory of the Apex repo as a reference. Conceptually, it:
- Reads JSON from stdin.
- Extracts
text. - Replaces each
{% kbd ... %}occurrence with properly formatted<kbd>HTML. - Prints the full transformed text to stdout.
A very abridged sketch:
#!/usr/bin/env ruby
require "json"
payload = JSON.parse($stdin.read)
text = payload["text"] || ""
# ... helper methods for normalizing modifier names, etc. ...
def render_kbd(markup)
# convert markup like "^~@r" to HTML
# returning something like:
# <kbd>⌃</kbd>+<kbd>⌥</kbd>+<kbd>⌘</kbd>+<kbd>R</kbd>
end
result = text.gsub(/\{%\s*kbd\s+([^%]+)%\}/) do
render_kbd(Regexp.last_match(1))
end
print resultYou can customize this script as you like; the only requirement is that it obey the stdin JSON / stdout contract.
This plugin turns :emoji: tokens in the final HTML into <span class="emoji">:emoji:</span>.
~/.config/apex/plugins/
emoji/
plugin.yml
---
id: emoji-span
title: Emoji span wrapper
author: Brett Terpstra
description: Wrap :emoji: markers in a span for styling
homepage: https://github.com/ApexMarkdown/apex-emoji-plugin
repo: https://github.com/ApexMarkdown/apex-emoji-plugin.git
phase: post_render
pattern: "(:[a-zA-Z0-9_+-]+:)"
replacement: "<span class=\"emoji\">$1</span>"
flags: "i"
priority: 200
timeout_ms: 0
---Because this is a declarative plugin, no external command is run. Apex compiles the regex and runs the replacements internally.
A typical workflow:
-
Create a plugin directory
- Project-local:
.apex/plugins/my-plugin/ - Or global:
~/.config/apex/plugins/my-plugin/
- Project-local:
-
Add
plugin.yml- Choose
phase(pre_parseorpost_render). - Choose plugin type:
- External: add
handler.command. - Declarative: add
pattern,replacement, and optionalflags.
- External: add
- Optionally, add metadata fields (
title,author,description,homepage,repo) so your plugin shows up nicely in directories and can be auto-installed. - For bundles, use
bundle:to define multiple related child plugins in one manifest.
- Choose
-
Add code (if external)
- Put your script alongside
plugin.yml. - Implement the JSON stdin → text stdout contract.
- Put your script alongside
-
Enable plugins
- Either:
- Add
plugins: true(orenable-plugins: true) to your document metadata, or - Run with
--pluginson the command line.
- Add
- Either:
-
Test
- Run Apex on a sample document that exercises your plugin syntax.
- If something goes wrong, log to a file under
$APEX_SUPPORT_DIR/<id>/rather than printing debugging output to stdout.
Once you’re comfortable, you can share the plugin.yml plus any scripts with others; they just need to drop the directory into their .apex/plugins/ or ~/.config/apex/plugins/ and enable plugins as needed.
Copyright 2025 Brett Terpstra, All Rights Reserved | MIT License
- Getting Started - Your first steps with Apex
- Installation - How to build and install Apex
- Usage - Basic usage examples
- Syntax - Complete syntax reference for unified mode
- Tables - Complete table syntax reference including rowspan, colspan, alignment, and captions
- Inline Attribute Lists - IALs and ALDs guide with examples
- Modes - Understanding processor modes
- Command Line Options - All CLI flags explained
-
Rendering Markdown in the Terminal - Terminal output formats (
-t terminal,-t terminal256),--width,--theme, and theming -
Generating Man Pages - Generate man pages from Markdown using
apex -t man -
Multi-file Documents - Combining multiple files with
--combine,--mmd-merge, and includes - Citations - Citations and bibliography guide
- Indices - Index generation with mmark and TextIndex syntax
-
Metadata Transforms - Transform metadata values with
[%key:transform] - Integrating with Pandoc - Use Apex with Pandoc for DOCX, PDF, and more
- Using Apex with Jekyll - Use the apex-ruby gem as Jekyll’s Markdown converter (untested; feedback welcome)
- Header IDs - How header IDs are generated
- C API - Programmatic API documentation
- Writing Tests - How to add tests for new features
- Xcode Integration - Using Apex in Xcode projects
- Examples - Practical usage examples
- Plugins - Plugin system, examples, and recipes
- Filters - AST filters (Pandoc-style JSON), examples, and usage
- Troubleshooting - Common issues and solutions
- Credits - Acknowledgments and links to related projects