Apex is a unified Markdown processor that combines the best
features from CommonMark, GitHub Flavored Markdown (GFM),
MultiMarkdown, Kramdown, and Marked. One processor to rule
them all.
Apex Icon
There are so many variations of
Markdown, extending its features in all kinds of ways. But
picking one flavor means giving up the features of another
flavor. So I’m building Apex with the goal of making all of
the most popular features of various processors available in
one tool.
Mode-specific features: Each mode enables appropriate extensions for maximum compatibility
Markdown Extensions
Tables: GitHub Flavored Markdown tables with advanced features (rowspan via ^^, colspan via empty cells/<<, captions before/after tables including Pandoc-style Table: Caption and : Caption syntax, and individual cell alignment using colons :Left, Right:, :Center:)
Table caption positioning: Control caption placement with --captions above or --captions below (default: below)
Table caption IAL: IAL attributes in table captions (e.g., : Caption {#id .class}) are extracted and applied to the table element
Relaxed tables: Support for tables without separator rows (Kramdown-style)
Headerless tables: Support for tables that start with alignment rows (separator rows) without header rows; column alignment is automatically applied
Footnotes: Three syntaxes supported (reference-style, Kramdown inline, MultiMarkdown inline)
Definition lists: Kramdown-style definition lists with Markdown content support
Task lists: GitHub-style checkboxes (- [ ] and - [x])
Strikethrough: ~~text~~ syntax from GFM
Smart typography: Automatic conversion of quotes, dashes, ellipses, and more
Math support: LaTeX math expressions with $...$ (inline) and $$...$$ (display)
Syntax highlighting: External syntax highlighting for fenced code blocks via Pygments or Skylighting with --code-highlight flag.
Supports language-aware highlighting, auto-detection, and line numbers with --code-line-numbers
Wiki links: [[Page Name]], [[Page Name|Display Text]], and [[Page Name#Section]] syntax with configurable link targets via --wikilink-space and --wikilink-extension
Metadata control of options: Control command-line options via metadata
set boolean flags (indices: false, wikilinks: true) and string options (bibliography: refs.bib, title: My Document, wikilink-space: dash, wikilink-extension: html) directly in document metadata for per-document configuration
Table of Contents: Automatic TOC generation with depth control using HTML (<!--TOC-->), MMD ({{TOC}} / {{TOC:2-4}}), and Kramdown {:toc} markers. Headings marked with {:.no_toc} are excluded from the generated TOC.
File includes: Three syntaxes (Marked <<[file], MultiMarkdown {{file}}, iA Writer /file), with support for address ranges and wildcard/glob patterns such as {{file.*}}, {{*.md}}, and {{c?de.py}}.
Markdown combiner (--combine): Concatenate one or more Markdown files into a single Markdown stream, expanding all include syntaxes.
When a SUMMARY.md file is provided, Apex treats it as a GitBook-style index and combines the linked files in order, perfect for building books, multi-file indices, and shared tables of contents that can then be piped back into Apex for final rendering.
MultiMarkdown merge (--mmd-merge): Read one or more mmd_merge-style index files and stitch their referenced documents into a single Markdown stream.
Each non-empty, non-comment line specifies a file to include; indentation with tabs or four-space groups shifts all headings in that file down by one level per indent, mirroring the original mmd_merge.pl behavior.
Output is raw Markdown that can be piped into Apex (e.g., apex --mmd-merge index.txt | apex --mode mmd).
CSV/TSV support: Automatic table conversion from CSV and TSV files
Both formats work in all contexts (block-level, inline, paragraphs, headings, table captions)
Bracketed spans: Convert [text]{IAL} syntax to HTML span elements with attributes, enabled by default in unified mode
Fenced divs: Pandoc-style fenced divs ::::: {#id .class} ... ::::: for creating custom block containers, enabled by default in unified mode.
Supports block type syntax >blocktype to create different HTML elements (e.g., ::: >aside {.sidebar} creates <aside> instead of <div>). Common block types include aside, article, section, details, summary, header, footer, nav, and custom elements
Image IAL support: Inline and reference-style images support IAL syntax with automatic width/height conversion (percentages and non-integer/non-px values convert to style attributes, Xpx values convert to integer width/height attributes, bare integers remain as width/height attributes)
Special markers: Page breaks (<!--BREAK-->), autoscroll pauses (<!--PAUSE:N-->), end-of-block markers
Custom styling: Link multiple external CSS files in standalone mode (use --css multiple times or comma-separated list)
Syntax highlighting: External syntax highlighting via Pygments or Skylighting with --code-highlight flag, includes automatic GitHub-style CSS in standalone mode
Pretty-print: Formatted HTML with proper indentation for readability
Header ID generation: Automatic or manual header IDs with multiple format options (GFM, MMD, Kramdown)
Emoji-to-name conversion: In GFM mode, emojis in headers are converted to their textual names in IDs (e.g., # đ Support â id="smile-support"), matching Pandoc’s GFM behavior
Header anchors: Option to generate <a> anchor tags instead of header IDs
ARIA accessibility: Add ARIA labels and accessibility attributes (--aria) for better screen reader support, including aria-label on TOC navigation, role attributes on figures and tables, and aria-describedby linking tables to their captions
Advanced Features
Hard breaks: Option to treat newlines as hard line breaks
Feature toggles: Granular control to enable/disable specific features (tables, footnotes, math, smart typography, etc.)
Unsafe HTML: Option to allow or block raw HTML in documents
Autolinks: Automatic URL detection and linking
Superscript/Subscript: Support for ^superscript^ and ~subscript~ syntax
Extensibility and Plugins
Apex supports a flexible plugin system that lets you add new syntax and post-processing features in any language while keeping the core parser stable and fast. Plugins are disabled by default so there is no performance impact unless you opt in. Enable them per run with --plugins, or per document with a plugins: true (or enable-plugins: true) key in your metadata.
You can manage plugins from the CLI:
Install plugins with --install-plugin:
From the central directory using an ID: --install-plugin kbd
Directly from a Git URL or GitHub shorthand: --install-plugin https://github.com/user/repo.git or --install-plugin user/repo
Uninstall a local plugin with --uninstall-plugin ID.
See installed and available plugins with --list-plugins.
When installing from a direct Git URL or GitHub repo name,
Apex will prompt with a security warning before cloning,
since plugins execute unverified code.
For a complete guide to writing, installing, and publishing plugins, see the Plugins page in the Apex Wiki.
Installation
Homebrew (macOS/Linux)
brew tap ttscoff/thelab
brew install ttscoff/thelab/apex
Building from Source
git clone https://github.com/ApexMarkdown/apex.git
cd apex
git submodule update --init--recursive
make
The apex binary will be in the build/ directory.
To install the built binary and libraries system-wide:
make install
Note: The default make command runs both cmake -S . -B build (to configure the project) and cmake --build build (to compile). If you prefer to run cmake commands directly, you can use those instead.
Pre-built Binaries
Download pre-built binaries from the latest release. Binaries are available for:
macOS (Universal binary for arm64 and x86_64)
Linux (x86_64 and arm64)
Basic Usage
Command Line
# Process a markdown file
apex input.md
# Output to a file
apex input.md -o output.html
# Generate standalone HTML document
apex input.md --standalone--title"My Document"# Pretty-print HTML output
apex input.md --pretty
Processing Modes
Apex supports multiple compatibility modes:
--mode commonmark - Pure CommonMark specification
--mode gfm - GitHub Flavored Markdown
--mode mmd or --mode multimarkdown - MultiMarkdown compatibility
--mode kramdown - Kramdown compatibility
--mode unified - All features enabled (default)
# Use GFM mode
apex input.md --mode gfm
# Use Kramdown mode with relaxed tables
apex input.md --mode kramdown
Common Options
--pretty - Pretty-print HTML with indentation
--standalone - Generate complete HTML document with <html>, <head>, <body>
--style FILE / --css FILE - Link to CSS file(s) in document head (requires --standalone). Can be used multiple times or with comma-separated list (e.g., --css style.css --css syntax.css or --css style.css,syntax.css)
--embed-css - Embed CSS file contents as inline <style> tags instead of <link> tags (works with multiple stylesheets)
--title TITLE - Document title (requires --standalone)
--spans / --no-spans - Enable/disable bracketed spans [text]{IAL} syntax (enabled by default in unified mode)
--code-highlight TOOL - Use external tool for syntax highlighting (supports pygments/p/pyg or skylighting/s/sky). Automatically includes GitHub-style CSS in standalone mode
--code-line-numbers - Include line numbers in syntax-highlighted code blocks (requires --code-highlight)
All Options
Apex Markdown Processor v0.1.56
One Markdown processor to rule them all
Project homepage: https://github.com/ApexMarkdown/apex
Usage: build/apex [options] [file]
build/apex --combine [files...]
build/apex --mmd-merge [index files...]
Options:
--accept Accept all Critic Markup changes (apply edits)
--[no-]alpha-lists Support alpha list markers (a., b., c. and A., B., C.)
--[no-]autolink Enable autolinking of URLs and email addresses
--base-dir DIR Base directory for resolving relative paths (for images, includes, wiki links)
--bibliography FILE Bibliography file (BibTeX, CSL JSON, or CSL YAML) - can be used multiple times
--captions POSITION Table caption position: above or below (default: below)
--code-highlight TOOL Use external tool for syntax highlighting (pygments, skylighting, or abbreviations p, s)
--code-line-numbers Include line numbers in syntax-highlighted code blocks (requires --code-highlight)
--highlight-language-only Only highlight code blocks that have a language specified (requires --code-highlight)
--combine Concatenate Markdown files (expanding includes) into a single Markdown stream
When a SUMMARY.md file is provided, treat it as a GitBook index and combine
the linked files in order. Output is raw Markdown suitable for piping back into Apex.
--csl FILE Citation style file (CSL format)
--css FILE, --style FILE Link to CSS file(s) in document head (requires --standalone, overrides CSS metadata)
Can be used multiple times or accept comma-separated list (e.g., --css style.css,syntax.css)
--embed-css Embed CSS file contents into a <style> tag in the document head (used with --css)
--embed-images Embed local images as base64 data URLs in HTML output
--hardbreaks Treat newlines as hard breaks
--header-anchors Generate <a> anchor tags instead of header IDs
-h, --help Show this help message
--id-format FORMAT Header ID format: gfm (default), mmd, or kramdown
(modes auto-set format; use this to override in unified mode)
--[no-]includes Enable file inclusion (enabled by default in unified mode)
--indices Enable index processing (mmark and TextIndex syntax)
--install-plugin ID Install plugin by id from directory, or by Git URL/GitHub shorthand (user/repo)
--link-citations Link citations to bibliography entries
--list-plugins List installed plugins and available plugins from the remote directory
--uninstall-plugin ID Uninstall plugin by id
--meta KEY=VALUE Set metadata key-value pair (can be used multiple times, supports quotes and comma-separated pairs)
--meta-file FILE Load metadata from external file (YAML, MMD, or Pandoc format)
--[no-]mixed-lists Allow mixed list markers at same level (inherit type from first item)
--mmd-merge Merge files from one or more mmd_merge-style index files into a single Markdown stream
Index files list document parts line-by-line; indentation controls header level shifting.
-m, --mode MODE Processor mode: commonmark, gfm, mmd, kramdown, unified (default)
--no-bibliography Suppress bibliography output
--no-footnotes Disable footnote support
--no-ids Disable automatic header ID generation
--no-indices Disable index processing
--no-index Suppress index generation (markers still created)
--no-math Disable math support
--aria Add ARIA labels and accessibility attributes to HTML output
--no-plugins Disable external/plugin processing
--no-relaxed-tables Disable relaxed table parsing
--no-smart Disable smart typography
--no-sup-sub Disable superscript/subscript syntax
--[no-]divs Enable or disable Pandoc fenced divs (Unified mode only)
--[no-]spans Enable or disable bracketed spans [text]{IAL} (Pandoc-style, enabled by default in unified mode)
--no-tables Disable table support
--no-transforms Disable metadata variable transforms
--no-unsafe Disable raw HTML in output
--no-wikilinks Disable wiki link syntax
--[no-]emoji-autocorrect Enable/disable emoji name autocorrect (enabled by default in unified mode)
--obfuscate-emails Obfuscate email links/text using HTML entities
-o, --output FILE Write output to FILE instead of stdout
--[no-]progress Show progress indicator during processing (enabled by default for TTY)
--plugins Enable external/plugin processing
--pretty Pretty-print HTML with indentation and whitespace
--reject Reject all Critic Markup changes (revert edits)
--[no-]relaxed-tables Enable or disable relaxed table parsing (no separator rows required)
--[no-]per-cell-alignment Enable or disable per-cell alignment markers (colons at start/end of cells, enabled by default in unified mode)
--script VALUE Inject <script> tags before </body> (standalone) or at end of HTML (snippet).
VALUE can be a path, URL, or shorthand (mermaid, mathjax, katex). Can be used multiple times or as a comma-separated list.
--show-tooltips Show tooltips on citations
-s, --standalone Generate complete HTML document (with <html>, <head>, <body>)
--[no-]sup-sub Enable or disable MultiMarkdown-style superscript (^text^) and subscript (~text~) syntax
--title TITLE Document title (requires --standalone, default: "Document")
--[no-]transforms Enable or disable metadata variable transforms [%key:transform]
--[no-]unsafe Allow or disallow raw HTML in output
--widont Prevent short widows in headings by inserting non-breaking spaces between trailing words
--code-is-poetry Treat unlanguaged code blocks as poetry (adds 'poetry' class, implies --highlight-language-only)
--[no-]markdown-in-html Enable or disable markdown processing inside HTML blocks with markdown attributes
--random-footnote-ids Use hash-based footnote IDs to avoid collisions when combining documents
--hashtags Convert #tags into span-wrapped hashtags
--style-hashtags Use 'mkstyledtag' class instead of 'mkhashtag' for hashtags
--proofreader Treat ==highlight== and ~~delete~~ as CriticMarkup highlight/deletion
--hr-page-break Replace <hr> elements with Marked-style page break divs
--title-from-h1 Use the first H1 as the document title when none is specified
--page-break-before-footnotes Insert a page break before the footnotes section
-v, --version Show version information
--[no-]wikilinks Enable or disable wiki link syntax [[PageName]]
--wikilink-space MODE Space replacement for wiki links: dash, none, underscore, space (default: dash)
--wikilink-extension EXT File extension to append to wiki links (e.g., html, md)
If no file is specified, reads from stdin.
Per-Document Configuration via Metadata
Most command-line options can be controlled via document
metadata, allowing different files to be processed with
different settings when processing batches. Boolean options
accept true/false, yes/no, or 1/0
(case-insensitive). String options use the value directly.
Example:
---indices:falsewikilinks:truebibliography:references.bibtitle:My Research Paperpretty:truestandalone:true---
This allows you to process multiple files with apex *.md and have each file use its own configuration. You can also use --meta-file to specify a shared configuration file that applies to all processed files.
Definition lists now support blank lines between the term and first definition, and between multiple definitions. Terms are preserved across blank lines and definition lists remain open across blank lines instead of closing prematurely.
Improved
Expanded test coverage
Fixed
Definition lists with blank lines between definitions no longer create separate <dl> blocks - all definitions for a term are now grouped in a single definition list.
Terms with blank lines before their definitions are now correctly converted to <dt> tags instead of being rendered as paragraphs.
âhighlight-language-only flag to only highlight code blocks that have a language specified (via ```language or IAL), leaving plain code blocks unhighlighted
Config file support for code-highlight option (accepts pygments, skylighting, or abbreviations p/pyg, s/sky)
Config file support for code-line-numbers option (boolean)
Config file support for highlight-language-only option (boolean)
Display 256-color ANSI art logo alongside version info when running apex --version in a wide terminal (>=70 columns)
Improved
Version output detects terminal capabilities and falls back gracefully (narrow terminal, piped output, NO_COLOR set)
Logo uses transparent background, blending with userâs terminal colors instead of forcing black rectangle
Display ASCII art logo alongside version info when running apex --version in a wide enough terminal (>=80 columns)
âcode-highlight TOOL flag for syntax highlighting via external tools (pygments, skylighting, or abbreviations p, s)
âcode-line-numbers flag to include line numbers in syntax-highlighted code blocks (requires âcode-highlight)
âcss flag now accepts multiple stylesheets via repeated flags or comma-separated list (e.g., âcss style.css âcss syntax.css or âcss style.css,syntax.css)
Automatic GitHub-style syntax highlighting CSS embedded when âcode-highlight is used (covers both Pygments and Skylighting class names)
Add test runner --badge mode that outputs pass/fail count (e.g., â981/981â) for badge generation
Improved
Version output automatically detects terminal capabilities and falls back to text-only when logo cannot display (narrow terminal, piped output, NO_COLOR set)
âembed-css now embeds all specified stylesheets as inline
Test output in errors-only mode (-e) now only prints suite titles for suites with failures
Per-cell alignment is now enabled by default only in unified mode, disabled in all other modes (CommonMark, GFM, MultiMarkdown, Kramdown)
New
Table preprocessing converts consecutive pipes without whitespace (
Â
Â
) to « markers for colspan detection, distinguishing between whitespace-separated empty cells (
Â
Â
) which remain separate and consecutive pipes (
Â
Â
) which create colspans.
Table cells can now specify alignment by adding colons at the start and/or end of cell content: leading colon (:) for left-align, trailing colon (:) for right-align, or both (:content:) for center-align. The colons are stripped from the output and replaced with style=âtext-align: âŠâ attributes.
Added âper-cell-alignment and âno-per-cell-alignment CLI flags to enable or disable per-cell alignment markers in tables
Added tests to verify colspan behavior with consecutive pipes vs empty cells with whitespace
Improved
Colspan attribute injection in HTML post-processing now includes fallback matching that checks nearby rows when position-based matching fails, ensuring colspan attributes are applied correctly even when row indices shift due to removed cells.
HTML post-processing now removes cells containing << markers (entity-encoded «) that were missed by AST-level removal.
Cell text extraction for attribute matching now recursively checks nested text nodes (paragraphs, etc.) to properly match cells with complex content structures.
Colspan merge logic now preserves cell alignment styles when cells are merged together, ensuring alignment attributes are maintained correctly when cells span multiple columns.
Per-cell alignment processing is now conditional and only runs when the feature is enabled, improving performance when disabled
Row-header detection in tables now correctly identifies empty first header cells
Fixed
Colspan detection now only triggers on cells with « markers (from consecutive pipes), not plain empty cells, so whitespace-separated pipes create separate empty cells as intended.
Cells with « markers now always merge with previous cell to create colspan, even when followed by additional content in subsequent cells.
Colspan now only merges consecutive empty cells (
Â
Â
), not empty cells with whitespace between pipes (
Â
)
Email autolinking no longer converts @ symbols in URLs to email addresses (e.g., Mastodon profile links like https://hachyderm.io/@ttscoff)
Email autolinking now requires that the @ symbol is preceded by an alphanumeric character (not space or punctuation)
Email autolinking now requires a TLD (top-level domain) to match, so only [user]@[domain].[tld] format is autolinked
Email autolinking is now skipped inside markdown link URLs text to prevent incorrect conversions
Row-header tables (tables with empty first header cell) now correctly convert first-column body cells to <th scope="row"> even when relaxed_tables is disabled
Support for : Caption syntax before tables, with or without IAL attributes
Caption format now works before tables (previously only worked after tables)
Added tests for : Caption before tables (basic, with IAL, without blank line)
Add regression test to ensure table parsing works correctly when files donât end with a newline, preventing future regressions
Add regression test to ensure table parsing works correctly when files use CR line endings, preventing future regressions with Table: Caption syntax
Improved
Definition list processor now skips : Caption lines that are followed by tables to avoid conflicts
Table caption detection now handles blank lines between captions and tables
Paragraph removal logic now recognizes and removes : Caption format paragraphs from output
Refactored test suite to use centralized test_result() and test_resultf() helper functions instead of scattered printf statements with manual errors_only_output checks
Fixed
Buffer overflow in stdin reading that caused segfaults when piping from pbpaste
Prevent memory leak in is_table_caption by only storing full_text in user_data when caption is confirmed, not before validation
Fix potential crash when processing multiple tables by ensuring full_text is properly freed when caption validation fails
Fix double-free in add_table_caption by checking for existing caption before freeing user_data, preventing use-after-free errors
Fix : Caption lines before tables being incorrectly parsed as definition lists by always treating them as captions when followed by a table, regardless of IAL presence or blank lines
Fix table parsing issue where last row of first table is not parsed when file doesnât end with a newline by normalizing input to always end with a line ending character before preprocessing and parsing
Normalization breaking file includes
Fix table parsing issue where last row of first table is not parsed when file doesnât end with a newline by normalizing input to always end with a line ending character before table preprocessing and final parsing
Fix Table: Caption syntax not being processed when file uses CR line endings by updating table caption preprocessing to handle CR, LF, and CRLF line endings correctly
âerrors-only flag now correctly suppresses all passing test output, including negative tests that pass
Removed rouge_css function from documentation generators (generate_docset.rb, generate_single_html.rb, generate_app_docs.rb) in favor of using the shared stylesheet approach.
New
Added generate_docset.rb script to generate Dash docsets from Apex documentation
Added support for single-page CLI options docset using mmd2cheatset
Added support for multi-page docset from wiki files with full navigation
Added shared_styles.css with common styling for all documentation generators
Added shared_scripts.js with hamburger menu functionality for mobile navigation
Added hamburger menu button for mobile navigation that slides sidebar in from left
Added mobile menu overlay that closes sidebar when clicked
Improved
Documentation generators now use shared CSS and JavaScript files for consistency
Sidebar width increased to 180-250px on larger screens to prevent menu item wrapping
Hamburger menu fades to 0.2 opacity when not hovered, full opacity on hover
Hamburger menu repositions to right of sidebar when menu is open
Hash handling on page load now supports both page IDs and section IDs
Added hashchange event listener to handle URL hash changes after page load
Moved Rouge syntax highlighting CSS to shared stylesheet (shared_styles.css) for better maintainability. All documentation generators now use the centralized GitHub theme CSS instead of generating it dynamically.
Documentation generators now prioritize build/apex over build-release/apex when searching for the Apex binary, ensuring the most recently built version is used for documentation generation.
Fixed
TOC link navigation now uses getAbsoluteTop function for reliable scroll positioning
Hash navigation in single-page HTML files now correctly shows page and scrolls to section
TOC links in C API and other pages now scroll correctly instead of only moving 5px
Definition lists are no longer incorrectly processed inside fenced code blocks. The definition list preprocessor now detects code block boundaries and skips processing when inside a code block, preserving the literal markdown syntax in code examples.
Definition lists are no longer incorrectly processed inside fenced code blocks. The definition list preprocessor now detects code block boundaries and distinguishes between closing fences () and opening fences (markdown). When inside a code block, only closing fences exit the block, while opening fences with language identifiers are treated as content, preventing definition list syntax from being rendered as HTML in code examples.
Fixed double-free memory error in definition list preprocessor by setting ref_definitions to NULL after freeing and adding NULL checks in error paths to prevent attempting to free already-freed memory.
Updated emoji_entry structure to support both unicode and image-based emojis with separate unicode and image_url fields
Table alignment test now accepts both align=âcenterâ and style=âtext-align: centerâ attributes to be more flexible with cmark-gfmâs output format
New
Fenced divs now support specifying different HTML block elements using >blocktype syntax (e.g., ::: >aside {.sidebar} creates <aside> instead of <div>)
Support for common HTML5 block elements: aside, article, section, details, summary, header, footer, nav, and custom elements
Block type syntax works with all attribute types including IDs, classes, and custom attributes
Added comprehensive test coverage for block type feature including nesting, multiple attributes, and edge cases
In GFM format, emojis in headers are converted to their textual names in generated IDs (e.g., # Support -> id=âsmile-supportâ), matching Pandocâs GFM behavior
Added support for all 861 GitHub emojis (expanded from ~200), including 14 image-based emojis like :bowtie:, :octocat:, and :feelsgood:
Added emoji name autocorrect feature using fuzzy matching with Levenshtein distance algorithm to correct typos and formatting errors in emoji names
Added âemoji-autocorrect and âno-emoji-autocorrect command-line flags to control emoji autocorrection
Emoji autocorrect is enabled by default in unified mode and can be enabled in GFM mode
Image-based emojis in headers now use em units (height: 1em) for proper scaling instead of fixed pixel sizes
Improved
Fenced div block types can be nested and mixed with regular divs
Emoji processing now validates that only complete :emoji_name: patterns are processed (requires at least one character and no spaces between colons)
Emoji names are normalized (lowercase, hyphens to underscores) before matching to handle case variations and formatting differences
Table processing now completes in under 30ms for large tables (previously timing out after 5+ seconds) by avoiding expensive string comparisons when no changes are made
Added early exit in table attribute injection to skip processing for simple tables without special attributes, avoiding expensive AST traversal for tables that donât need it
Table post-processing performance significantly improved for large tables (2600+ cells) by implementing lazy cell content extraction, only extracting content when needed for attribute matching or alignment processing
Added timeout protection (10 seconds) to table post-processing loop to prevent hangs on extremely large tables, gracefully exiting and returning processed HTML
Per-cell alignment processing now disabled automatically for tables with more than 1000 cells to avoid timeouts, while column alignment from delimiter rows continues to work (handled by cmark-gfm)
Optimized alignment colon detection to only check first and last non-whitespace characters instead of scanning entire cell content, reducing character comparisons by ~50x
Added early exit for tables with only simple attributes (no rowspan/colspan/data-remove) to skip expensive HTML processing when no complex features are needed
Content-based cell matching now skipped for tables with more than 500 attributes to avoid performance degradation
Added CMARK_OPT_LIBERAL_HTML_TAG option when unsafe mode is enabled to allow cmark-gfm to properly recognize inline HTML tags instead of encoding them
MacOS binaries now use @rpath for libyaml instead of hardcoded Homebrew paths, allowing the binary to work when copied to /usr/local/bin as long as libyaml is installed in /usr/local/lib or /opt/homebrew/lib
Emoji name resolution now prefers longer, more descriptive names (e.g., âthumbsupâ over â+1â)
Header ID generation now normalizes common Latin diacritics (e, a, c, etc.)
Table rowspan matching now extracts cell content for more accurate matching
Fixed
Fixed issue where partial emoji patterns or empty patterns could cause incorrect matches
Emoji replacement now correctly ignores table alignment patterns like :â: and :
: to prevent incorrect emoji processing in table delimiter rows
Emoji patterns (like :bowtie:) inside HTML tag attributes are now correctly ignored and not processed, preventing mangled HTML output when emojis appear in attributes like title=â:emoji:â
Autolink processing now skips URLs inside HTML tag attributes, preventing URLs in attributes like src=âhttps://âŠâ from being converted to markdown links
Inline HTML tags like are no longer HTML-encoded when âunsafe is enabled, preserving raw HTML in paragraphs, blockquotes, and definition lists
Header IDs with emojis now correctly replace cmark-gfmâs auto-generated IDs instead of being skipped, ensuring custom ID formats (like emoji-to-name conversion) are applied
Emoji processing now correctly skips index placeholders ()
Table processing now correctly detects and processes rowspan markers (^^)
Table attribute processing optimization no longer incorrectly skips rowspan/colspan attributes
Table formatting in fixture
Remove unused function
[0.1.43] - 2025-12-31
Changed
Reference image attribute expansion now converts IAL
attributes (ID, classes) to key=value format for
compatibility with inline image parsing
Test suite refactored: split test_runner.c into multiple
Test fixtures reorganized: moved all .md test files from
tests/ to tests/fixtures/ with subdirectories (basic/,
demos/, extensions/, ial/, images/, output/, tables/)
New
Support for Pandoc-style table captions using : Caption syntax (in addition to existing [Caption] and Table: Caption formats)
IAL attributes in table captions are now extracted and applied to the table element (e.g., : Caption {#id .class} applies id and class to the table)
Support for Pandoc-style IAL syntax without colon prefix ({#id .class} in addition to Kramdown {:#id .class} format)
Support Pandoc-style IAL syntax ({#id .class}) in addition
to Kramdown-style ({: #id .class}) for all IAL contexts
including block-level, inline, and paragraph IALs
Extract_ial_from_text function now recognizes both {: and
{# or {. formats when extracting IALs from text
Extract_ial_from_paragraph function now accepts
Pandoc-style IALs for pure IAL paragraphs
Process_span_ial_in_container function now processes
Pandoc-style IALs for inline elements like links, images,
and emphasis
Is_ial_line function now detects Pandoc-style IAL-only
lines in addition to Kramdown format
Support Pandoc-style IAL syntax ({#id .class}) in addition
to Kramdown-style ({: #id .class}) for all IAL contexts
including block-level elements, paragraphs, inline
elements, and headings
Add support for Pandoc fenced divs syntax (::::: {#id
.class} ⊠:::::) in unified mode, enabled by default
Add âdivs and âno-divs command-line flags to control
fenced divs processing
Add comprehensive test suite for Pandoc fenced divs
covering basic divs, nested divs, attributes, and edge
cases
Add bracketed spans feature that converts [text]{IAL}
syntax to HTML span elements with attributes, enabled by
default in unified mode
Add âspans and âno-spans command-line flags to
enable/disable bracketed spans in other modes
Bracketed spans support all IAL attribute types (IDs,
classes, key-value pairs) and process markdown inside
spans
Reference link definitions take precedence over bracketed
spans - if [text] matches a reference link, it remains a
link
Add comprehensive test suite for bracketed spans including
reference link precedence, nested brackets, and markdown
processing
Add bracketed spans examples and documentation
Add automatic width/height attribute conversion:
percentages and non-integer/non-px values convert to style
attributes, Xpx values convert to integer width/height
attributes (strips px suffix), bare integers remain as
width/height attributes
Add support for Pandoc/Kramdown IAL syntax on images:
inline images support IAL after closing paren like
{#id .class width=50%}
Add support for IAL syntax after titles in reference image
Add support for Pandoc-style IAL with space prefix: {
width=50% } syntax works for images
Add comprehensive test suite for width/height conversion
covering percentages, pixels, integers, mixed cases, and
edge cases like decimals and viewport units
Reference image definitions now preserve and include title
attributes from definitions like [ref]: url âtitleâ {#id}
Improved
Table caption preprocessing now converts : Caption
format to [Caption] format before definition list
processing to avoid conflicts
HTML renderer now extracts and injects IAL attributes from
table captions while excluding internal attributes like
data-caption
IAL parsing automatically detects format and adjusts
content offset accordingly (2 chars for {: format, 1 char
for {# or {. format)
HTML markdown extension now uses CMARK_OPT_UNSAFE to allow
raw HTML including nested divs in processed content
Width/height conversion properly merges with existing
style attributes when both are present
IAL detection now properly handles whitespace before IAL
syntax for both inline and reference images
Reference image expansion now correctly skips IAL even
when closing paren is not found
Fixed
Table caption test assertions now correctly match table
tags with attributes by using <table instead of <table>
Extract_ial_from_paragraph now allows newline character
after closing brace in IAL syntax
List items with key:value format (e.g., â- Foo: Barâ) are
no longer incorrectly parsed as MMD metadata in unified
mode
Fenced divs now add markdown=â1â attribute so content
inside divs is properly parsed as markdown
HTML markdown extension now preserves all attributes (id,
class, custom attributes) when processing divs with
markdown=â1â
HTML markdown extension now recursively processes nested
divs with markdown=â1â attributes
HTML markdown extension now adds newline after closing div
tags to ensure following markdown headers and content are
parsed correctly
Bracketed spans now correctly handle nested brackets by
matching outer brackets instead of first closing bracket
Remove test assertions checking for markdown attribute on
bracketed spans which is correctly removed by
html_markdown extension
IAL syntax with spaces (e.g., { width=50% }) now correctly
detected and processed for images
IAL syntax is now properly stripped from output even when
parsing fails, preventing raw IAL from appearing in HTML
Reference image definitions with IAL after URL (no title)
now correctly detected and processed
Test string length issues: replaced hardcoded lengths with
strlen() calls to ensure full IAL syntax is processed in
width/height conversion tests
Removed unused style_attr_index variable to eliminate
compiler warning
[0.1.42] - 2025-12-30
New
ALD references can now be combined with additional
attributes in the same IAL (e.g., {:id .class3} where id
is an ALD reference and .class3 is an additional class)
Improved
When merging ALD attributes with additional attributes,
duplicate key-value pairs are now replaced instead of
duplicated (e.g., if ALD defines rel=âxâ and IAL includes
rel=âyâ, the result is rel=âyâ)
Classes from additional attributes are appended to ALD
classes, and IDs in IALs override ALD IDs when specified
Enhanced merge_attributes function to properly handle
attribute key conflicts by replacing existing values
rather than creating duplicates
[0.1.41] - 2025-12-30
New
Inline Attribute Lists (IALs) can now appear immediately
after inline elements within paragraphs, not just at the
end of paragraphs
IALs can be applied to strong (bold), emphasis (italic),
and code elements in addition to links and images
IALs now work with nested inline elements, allowing
attributes to be applied to italic text inside bold text
and similar nested structures
Added script (tests/generate_ial_demo.sh) to automatically
generate an HTML file with interactive attribute
inspection tooltips
Fixed
IALs (Inline Attribute Lists) are now correctly applied to
the intended link element when multiple links in a
document share the same URL
IALs are now correctly applied to the intended element
when multiple elements share the same URL or content,
using separate element counters for each inline element
type
Block-level HTML elements now correctly include a space
between the tag name and attributes when IAL attributes
are injected
[0.1.40] - 2025-12-23
Changed
Table captions now default to below the table instead of
above
Tfoot row detection in AST now separates the logic for
marking the === row itself versus rows that come after it,
improving accuracy of tfoot section identification.
Disallow using âcombine and âmmd-merge together to avoid
ambiguous multi-file behavior
Update CSV include and inline table handling so both share
the same CSV-to-table conversion and alignment behavior
New
Added âcaptions option to control caption position (above
or below)
Added default CSS styling for figcaption elements in
standalone output (centered, bold, 0.8em)
Added CSS styling for table figures to align captions with
tables (fit-content width)
Support for tables that start with alignment rows
(separator rows) without header rows. Column alignment
specified in the separator row is automatically applied to
all data columns.
Support for individual cell alignment in tables using
colons, similar to Jekyll Spaceship. Cells can be aligned
independently with :Text (left), Text: (right), or :Text:
(center). Colons are removed from output and alignment is
applied via CSS text-align styles.
Support per-cell alignment markers inside table cells
Support multiline table cells using trailing backslash
markers
Support header and footer colspans based on empty cells
Add âcombine CLI mode to concatenate Markdown files with
include expansion and GitBook-style SUMMARY.md index
support.
Add âmmd-merge CLI mode to merge MultiMarkdown index
files into a single Markdown stream
Support indentation-based header level shifting when
merging mmd_merge index entries
Support inline CSV/TSV tables using ```table fenced blocks
with automatic CSV/TSV delimiter detection
Support markers that convert following
CSV/TSV lines into Markdown tables until a blank line
Add âaria command-line option to enable ARIA labels and
accessibility attributes in HTML output
Add aria-label=âTable of contentsâ to TOC navigation
elements when âaria is enabled
Add role=âfigureâ to figure elements when âaria is enabled
Add role=âtableâ to table elements when âaria is enabled
Generate id attributes for figcaption elements in table
figures when âaria is enabled
Add aria-describedby attributes linking tables to their
captions when âaria is enabled
Improved
Removed unused variables to eliminate compiler warnings
Empty thead sections from headerless tables are now
removed from HTML output instead of rendering empty header
cells.
Table row mapping now better handles the relationship
between HTML row indices and AST row indices, accounting
for separator rows that are removed from HTML output.
Added safeguards to prevent rows that should be in tbody
from being skipped, including protection for the first few
rows (header and first two data rows) when a === separator
is present.
Make table captions positionable above or below tables
Center and style figcaptions in standalone HTML output
Support optional alignment keyword rows (left, right,
center, auto) and headless tables for both included CSV
files and inline CSV/TSV data
Preserve ```table fences without commas or tabs by leaving
them as code blocks so users can show literal CSV/TSV
without conversion
Fixed
Removed unused variable ârow_idxâ from advanced_tables.c
to eliminate compiler warnings.
Rows before the === separator are now correctly placed in
tbody instead of being incorrectly placed in tfoot or
skipped entirely. The fix includes HTML position
verification to ensure rows that appear before the === row
in the rendered HTML are always in tbody, regardless of
AST marking.
Prevent === separator rows from appearing as table content
Ensure footer rows render in tfoot without losing body rows
Preserve legitimate empty cells such as missing Q4 values
Apply ^^ rowspans correctly for all table sections
Apply ^^ rowspans correctly across table sections without
leaking into unrelated rows
Support footer colspans so footer cells can span multiple
columns like headers and body rows
Preserve legitimate empty table cells that are not part of
colspans or rowspans
KaTeX auto-render now properly configures delimiters and
manually renders math spans to prevent plain text from
appearing after rendered equations
Relaxed table header conversion now only runs when
relaxed_tables option is enabled
HTML document wrapping now strips existing </body></html>
tags to prevent duplicates when content already contains
them
KaTeX auto-render now properly configures delimiters and
manually renders math spans to prevent plain text from
appearing after rendered equations
Relaxed table header conversion now only runs when
relaxed_tables option is enabled
HTML document wrapping now strips existing </body></html>
tags to prevent duplicates when content already contains
them
Table captions appearing after their tables now correctly
link via aria-describedby attributes
[0.1.39] - 2025-12-19
Changed
Table post-processing now tracks all cells (including
removed ones) to correctly map HTML column positions to
original AST column indices, ensuring attributes are
applied to the correct cells.
Added content-based detection for ^^ marker cells during
HTML post-processing to ensure they are properly removed
even when attribute matching fails.
New
Added content verification to prevent false matches when
cells are covered by rowspans
Added tracking of previous cellâs colspan to detect and
remove empty cells after colspan
Added detection and removal of === marker rows in tfoot
sections
Improved
Rowspan cell tracking now uses a per-column active cell
approach (inspired by Jekyll Spaceship) for more reliable
rowspan calculation across complex table structures.
Cell matching now uses position-based fallback to previous
row when current row match fails
Row mapping accounts for rowspan coverage to correctly
identify visible columns
Tfoot row detection now uses AST row indices instead of
HTML row indices for accuracy
Fixed
Table rowspan rendering now correctly handles rows where
most cells use ^^ markers. Previously, cells like âBetaâ
and âGammaâ in rows with multiple ^^ cells would be
missing or appear in the wrong position. The fix includes
proper mapping between HTML row indices and AST row
indices, accounting for separator rows that are removed
from HTML output.
Missing cells after colspan (e.g., â92.00â cell was
missing when âAbsentâ had colspan=â2â)
Rowspan not applying correctly when HTML row mapping was
off by one
Footer alignment rows (=== markers) appearing in output
instead of being removed
Empty cells after colspan not being removed from rendered
HTML
[0.1.38] - 2025-12-18
Changed
In standalone mode, insert script tags just before </body>
In snippet mode, append script tags at the end of the HTML
fragment
When âembed-css is used with âcss, replace the
stylesheet tag with an inline
New
Support Pandoc-style âTable: Captionâ syntax and
Add âscript CLI flag to inject scripts into HTML output
Support shorthands for common JS libraries (mermaid,
mathjax, katex, highlightjs, prism, htmx, alpine)
Add âembed-css option to inline CSS files into the
standalone document head
Improved
Compress extraneous newlines between HTML elements
Remove unused apex_remote_trim helper to eliminate
compiler warnings
Fixed
Prevent caption paragraphs from being reused across
Skip URL encoding for footnote definitions ([^id]: âŠ) so
footnote
[0.1.37] - 2025-12-17
Changed
Image attributes are mode-dependent: work in Unified and
MultiMarkdown modes only
URL encoding is mode-dependent: works in Unified,
MultiMarkdown, and Kramdown modes
Improved caption detection to check all table rows for
caption markers, not just the last row, to handle cases
where captions come after tfoot rows.
New
Support for MultiMarkdown-style image attributes in
unified and MultiMarkdown modes
Inline image attributes:
Reference-style image attributes: ![][ref] with [ref]: url
width=300
Automatic URL encoding for links with spaces in unified,
MultiMarkdown, and Kramdown modes
URLs with spaces are automatically percent-encoded (e.g.,
âpath with spaces.pngâ becomes âpath%20with%20spaces.pngâ)
Added support for MultiMarkdown-style image attributes in
reference-style images. Reference definitions can now
include attributes: [img1]: image.png width=300
style=âfloat:leftâ
Added support for inline image attributes:
Added automatic URL encoding for all link URLs (images and
regular links). URLs with spaces are automatically
percent-encoded (e.g., âpath to/image.pngâ becomes
âpath%20to/image.pngâ)
Added detection and removal of table alignment separator
rows that were incorrectly being rendered as table rows.
Added test cases for table captions appearing before and
after tables.
Added support for tfoot sections in tables using === row markers. Rows containing === markers are now placed in <tfoot> sections, and all subsequent rows after the first === row are also placed in tfoot.
Added comprehensive table feature tests that validate
rowspan,
Improved
Improved attribute injection in HTML renderer to correctly
place attributes before closing > or /> in img and link
tags
Enhanced URL parsing to distinguish between spaces within
URLs vs spaces before attributes using forward-scanning
pattern detection
Self-closing img tags now consistently use â />â (space
before slash) when attributes are injected, matching the
format used by cmark-gfm for img tags without injected
attributes
Rowspan and colspan attribute handling now properly
appends to existing attributes instead of replacing them,
allowing multiple attributes to coexist on table cells.
Alignment rows (rows containing only ââ characters) are
now detected and marked for removal, preventing them from
appearing in HTML output.
Fixed
Fixed bug where image prefix â![â was incorrectly removed
during preprocessing of expanded reference-style images
URL encoding now only encodes unsafe characters (space,
control chars, non-ASCII). Valid URL characters like /, :,
?, #, ~, etc. are preserved and no longer incorrectly
encoded.
Titles in links and images are now correctly detected and
excluded from URL encoding. Supports quoted titles
(âtitleâ, âtitleâ) and parentheses titles ((title)). URLs
with parentheses (like Wikipedia links) are correctly
distinguished from titles based on whether a space
precedes the opening parenthesis.
Reference-style images with attributes now render
correctly. Reference definitions with image attributes are
removed from output, while those without attributes are
preserved (with URL encoding) so cmark can resolve the
references.
Spacing between attributes in HTML output. Attributes
injected into img and link tags now have proper spacing,
preventing malformed HTML like alt=âtextâwidth=â100â.
Table attributes now render correctly with proper spacing.
Fixed missing space in table tag when id attribute
immediately follows (e.g., <tableid=ââŠâ now renders as
<table id=ââŠâ).
Rowspan and colspan injection now works correctly in all
cases. Fixed bug where table tracking variables werenât
set when fixing missing space in table tag (e.g.,
<tableid=ââŠâ), causing row and cell processing to be
skipped. Table tracking is now properly initialized even
when correcting tag spacing.
Captions after tables were not being detected when tables
had IAL attributes, as IAL processing replaced the caption
data stored in user_data. Added fallback logic to check
for caption paragraphs directly in the AST when user_data
lookup fails.
Rows containing only === markers are now properly
skipped entirely rather than rendering as empty cells in
tfoot sections.
Caption paragraphs before tables are now properly removed,
[0.1.36] - 2025-12-16
Fixed
Resolve CMake error when building framework where
file(GLOB) returns multiple dylib files, causing
semicolon-concatenated paths in file(COPY) command. Now
extracts first file from glob result before copying.
Homebrew installation now correctly links to system
libyaml instead of hardcoded CI path
[0.1.35] - 2025-12-16
Changed
Update Homebrew formula to install from a precompiled
macOS universal binary instead of building from source
with cmake.
Allow âinstall-plugin to accept a Git URL or GitHub
shorthand (user/repo) in addition to directory IDs when
installing plugins.
Improved
Simplify Homebrew installation so users no longer need
cmake or Xcode build tools to install apex.
Add an interactive security confirmation when installing
plugins from a direct Git URL or GitHub repo name,
reminding users that plugins execute unverified code.
[0.1.34] - 2025-12-16
[0.1.33] - 2025-12-16
[0.1.32] - 2025-12-16
[0.1.31] - 2025-12-16
[0.1.30] - 2025-12-16
Changed
Make âlist-plugins show installed plugins before remote
ones.
Prevent remote plugins that are already installed from
being listed under Available Plugins.
Build system now detects libyaml via multiple methods
(yaml-0.1, yaml, libyaml) for better cross-platform
support.
Homebrew formula now includes libyaml as a dependency to
ensure full YAML support.
Suppressed unused-parameter warnings from vendored
cmark-gfm extensions to reduce build noise.
New
Add âuninstall-plugin CLI flag to remove installed plugins.
Run optional post_install command from plugin.yml after
cloning a plugin.
Full YAML parsing support using libyaml for arrays and
nested structures in metadata and plugin manifests.
Plugin bundle support allowing multiple plugins to be
defined in a single plugin.yml manifest.
Expose APEX_FILE_PATH to external plugins so scripts can
see the original input path or base directory when
processing.
Improved
Split() metadata transform now accepts regular expressions
as delimiters (for example split(,\s*)).
YAML arrays are automatically normalized to
comma-separated strings for backward compatibility with
existing metadata transforms.
External plugin environment now includes the source file
path (when available) alongside APEX_PLUGIN_DIR and
APEX_SUPPORT_DIR.
Fixed
Tighten mutual-exclusion checks between install and
uninstall plugin flags.
Ensure CMake policy version is compatible with vendored
cmark-gfm on newer CMake releases.
Install the Apex framework with its public apex.h header
correctly embedded in Apex.framework/Headers for Xcode
use.
Bundle libcmark-gfm and libcmark-gfm-extensions dylibs
into Apex.framework so dependent apps no longer hit
missing library errors at runtime.
[0.1.29] - 2025-12-15
Changed
Make âlist-plugins show installed plugins before remote
ones.
Prevent remote plugins that are already installed from
being listed under Available Plugins.
New
Initial planning for a remote plugin directory and install
features
Add âuninstall-plugin CLI flag to remove installed plugins.
Fixed
Superscript/subscript no longer process content inside
Liquid {% %} tags.
Autolink detection skips Liquid {% %} tags so emails and
URLs are not rewritten there.
Fix directory url for --list-plugins
[0.1.28] - 2025-12-15
Changed
Default wikilink URLs now replace spaces with dashes (e.g.
[[Home Page]] -> href=âHome-Pageâ).
New
Add âwikilink-space and âwikilink-extension flags to
control how [[WikiLink]] hrefs are generated.
Allow wikilink space and extension configuration via
metadata keys wikilink-space and wikilink-extension.
Support Kramdown-style {:toc âŠ} markers mapped to Apex
TOC generation.
Add tests for {:toc} syntaxes
MMD includes support full glob patterns like {{*.md}} and
{{c?de.py}}.
Add plugin discovery from .apex/plugins and
~/.config/apex/plugins.
Allow external handler plugins in any language via JSON
stdin/stdout.
Support declarative regex plugins for pre_parse and
post_render phases.
Add --no-plugins CLI flag to disable all plugins for a
run.
Support plugins: true/false metadata to enable or
disable plugins.
Initial planning for a remote plugin directory and install
features
Improved
Exclude headings with .no_toc class from generated tables
of contents for finer-grained TOC control.
MMD-style {{file.*}} now resolves preferred extensions
before globbing.
Transclusion respects brace-style patterns such as
{{{intro,part1}.md}} where supported.
Provide APEX_PLUGIN_DIR and APEX_SUPPORT_DIR for
plugin code and data.
Add profiling (APEX_PROFILE_PLUGINS=1) for plugins
[0.1.27] - 2025-12-15
Changed
Default wikilink URLs now replace spaces with dashes (e.g.
[[Home Page]] -> href=âHome-Pageâ).
New
Add âwikilink-space and âwikilink-extension flags to
control how [[WikiLink]] hrefs are generated.
Allow wikilink space and extension configuration via
metadata keys wikilink-space and wikilink-extension.
Support Kramdown-style {:toc âŠ} markers mapped to Apex
TOC generation.
Add tests for {:toc} syntaxes
MMD includes support full glob patterns like {{*.md}} and
{{c?de.py}}.
Add plugin discovery from .apex/plugins and
~/.config/apex/plugins.
Allow external handler plugins in any language via JSON
stdin/stdout.
Support declarative regex plugins for pre_parse and
post_render phases.
Add --no-plugins CLI flag to disable all plugins for a
run.
Support plugins: true/false metadata to enable or
disable plugins.
Initial planning for a remote plugin directory and install
features
Improved
Exclude headings with .no_toc class from generated tables
of contents for finer-grained TOC control.
MMD-style {{file.*}} now resolves preferred extensions
before globbing.
Transclusion respects brace-style patterns such as
{{{intro,part1}.md}} where supported.
Provide APEX_PLUGIN_DIR and APEX_SUPPORT_DIR for
plugin code and data.
Add profiling (APEX_PROFILE_PLUGINS=1) for plugins
[0.1.26] - 2025-12-14
Changed
Change --enable-includes to --[no-]includes, allowing
--no-includes to disable includes in unified mode and
shortening the flag
Integrate metadata-to-options application into CLI after
metadata merging
Preserve bibliography files array when metadata mode
resets options structure
New
Add apex_apply_metadata_to_options() function to apply
metadata values to apex_options structure
Support controlling boolean flags via metadata (indices,
with spaces removed (e.g., âHTML Header Levelâ matches
âhtmlheaderlevelâ)
Add index extension supporting mmark syntax (!item),
(!item, subitem), and (!!item, subitem) for primary
entries
Add TextIndex syntax support with {^}, [term]{^}, and
{^params} patterns
Add automatic index generation at end of document or at
marker
Add alphabetical sorting and optional grouping by first
letter for index entries
Add hierarchical sub-item support in generated index
Add âindices CLI flag to enable index processing
Add âno-indices CLI flag to disable index processing
Add âno-index CLI flag to suppress index generation while
keeping markers
Add comprehensive test suite with 40 index tests covering
both syntaxes
Improved
Only process citations when bibliography is actually
provided for better performance
Add comprehensive tests for MultiMarkdown metadata keys
Add comprehensive performance profiling system
(APEX_PROFILE=1) to measure processing time for all
extensions and CLI operations
Add early exit checks for IAL processing when no {:
markers are present
Add early exit checks for index processing when no index
patterns are found
Add early exit checks for citation processing when no
citation patterns are found
Add early exit checks for definition list processing when
no : patterns are found
Optimize alpha lists postprocessing with single-pass
algorithm replacing O(n*m) strstr() loops
Add early exit check for alpha lists postprocessing when
no markers are present
Optimize file I/O by using fwrite() with known length
instead of fputs()
Add markdown syntax detection in definition lists to skip
parser creation for plain text
Optimize definition lists by selectively extracting only
needed reference definitions instead of prepending all
Add profiling instrumentation for all preprocessing,
parsing, rendering, and post-processing steps
Add profiling instrumentation for CLI operations (file
I/O, metadata processing)
Fixed
Prevent autolinking of @ symbols in citation syntax (e.g.,
[@key])
Handle HTML comments in autolinker to preserve citation
placeholders
Fix quote language adjustment to handle Unicode curly
quotes in addition to HTML entities
Fix bibliography_files assignment to remove unnecessary cast
Fix heap-buffer-overflow in html_renderer.c when writing
null terminator (allocate capacity+1)
Fix use-after-free in ial.c by deferring node unlinking
until after iteration completes
Fix buffer overflow in definition_list.c HTML entity
escaping (correct length calculation for & and ")
[0.1.24] - 2025-12-13
New
Add citation processing with support for Pandoc,
MultiMarkdown, and mmark syntaxes
Add bibliography loading from BibTeX, CSL JSON, and CSL
YAML formats
Add âbibliography CLI option to specify bibliography
files (can be used multiple times)
Add âcsl CLI option to specify citation style file
Add âno-bibliography CLI option to suppress bibliography
output
Add âlink-citations CLI option to link citations to
bibliography entries
Add âshow-tooltips CLI option for citation tooltips
Add bibliography generation and insertion at <!â
REFERENCES â> marker
Add support for bibliography specified in document metadata
Improved
Only process citations when bibliography is actually
provided for better performance
Fixed
Raw HTML tags and comments are now preserved in definition
lists by default in unified mode. Previously, HTML content
in definition list definitions was being replaced with
âraw HTML omittedâ even when using âunsafe or in unified
mode.
Unified mode now explicitly sets unsafe=true by default to
ensure raw HTML is allowed.
Prevent autolinking of @ symbols in citation syntax (e.g.,
[@key])
Handle HTML comments in autolinker to preserve citation
placeholders
[0.1.23] - 2025-12-12
Changed
Remove remote image embedding support (curl dependency
removed)
New
Add metadata variable transforms with [%key:transform]
syntax
Add âtransforms and âno-transforms flags to
enable/disable transforms
Add 19 text transforms: upper, lower, title, capitalize,
Add âembed-images flag to embed local images as base64
data URLs in HTML output
Add âbase-dir flag to set base directory for resolving
relative paths (images, includes, wiki links)
Add automatic base directory detection from input file
directory when reading from file
Add base64 encoding utility for image data
Add MIME type detection from file extensions (supports
jpg, png, gif, webp, svg, bmp, ico)
Add image embedding function that processes HTML and
replaces local image src attributes with data URLs
Add test suite for image embedding functionality
Improved
Wiki link scanner now processes all links in a text node
in a single pass instead of recursively processing one at
a time, significantly improving performance for documents
with multiple wiki links per text node.
Added early-exit optimization to skip wiki link AST
traversal entirely when no wiki link markers are present
in the document.
Improve error handling in transform execution to return
original value instead of NULL on failure
Add comprehensive test coverage for all transforms
including edge cases
Relative path resolution for images now uses
base_directory option
Base directory is automatically set from input file
location when not specified
Fixed
Fix bracket handling in regex patterns - properly match
closing brackets in [%âŠ] syntax when patterns contain
brackets
Fix YAML metadata parsing to strip quotes from quoted
string values
Raw HTML tags and comments are now preserved in definition
lists by default in unified mode. Previously, HTML content
in definition list definitions was being replaced with
âraw HTML omittedâ even when using âunsafe or in unified
mode.
Unified mode now explicitly sets unsafe=true by default to
ensure raw HTML is allowed.
[0.1.20] - 2025-12-11
NEW
Added man page generation and installation support. Man
pages can be generated from Markdown source using pandoc
or go-md2man, with pre-generated man pages included in the
repository as fallback. CMake build system now handles man
page installation, and Homebrew formula installs the man
page.
Added comprehensive test suite for MMD 6 features
including multi-line setext headers and link/image titles
with different quote styles (single quotes, double quotes,
parentheses). Tests verify these features work in both
MultiMarkdown and unified modes.
Added build-test man_page_copy target for man page
installation.
Added âobfuscate-emails flag to hex-encode mailto links.
IMPROVED
Superscript processing now stops at sentence terminators
(. , ; : ! ?) instead of including them in the superscript
content. This prevents punctuation from being incorrectly
included in superscripts.
Enhanced subscript and underline detection logic. The
processor now correctly differentiates between subscript
(tildes within a word, e.g., H~2~O) and underline (tildes
at word boundaries, e.g., ~text~) by checking if tildes
are within alphanumeric words or at word boundaries.
Expanded test coverage for superscript, subscript,
underline, strikethrough, and highlight features with
additional edge case tests.
HTML comments now replaced with âraw HTML omittedâ in
CommonMark and GFM modes by default
Added enable_sup_sub flag to apex_options struct
Updated mode configurations to enable sup/sub in
appropriate modes
Added sup_sub.c to CMakeLists.txt build configuration
Removed unused variables to resolve compiler warnings
Tag filter (GFM security feature) now only applies in GFM
mode, not Unified mode, allowing raw HTML and autolinks in
Unified mode as intended.
Autolink extension registration now respects the
enable_autolink option flag.
NEW
Added MultiMarkdown-style superscript (^text^) and
subscript (~text~) syntax support
Added â[no-]sup-sub command-line option to enable/disable
superscript/subscript
Superscript/subscript enabled by default in unified and
MultiMarkdown modes
Created sup_sub extension (sup_sub.c and sup_sub.h) for
processing ^ and ~ syntax
Added â[no-]unsafe command-line option to control raw
HTML handling
Added test_sup_sub() function with 13 tests covering
superscript and
Added test_mixed_lists() function with 10 tests covering
mixed list
Added test_unsafe_mode() function with 8 tests covering
raw HTML
Added preprocessing for angle-bracket autolinks
(http://...) to convert them to explicit markdown links,
ensuring they work correctly with custom rendering paths.
Added â[no-]autolink CLI option to control automatic
linking of URLs and email addresses. Autolinking is
enabled by default in GFM, MultiMarkdown, Kramdown, and
unified modes, and disabled in CommonMark mode.
Added enable_autolink field to apex_options structure to
control autolink behavior programmatically.
Added underline syntax support: ~text~ now renders as
text when thereâs a closing ~ with no space before
it.
IMPROVED
Test suite now includes 36 additional tests, increasing
total test
Autolink preprocessing now skips processing inside code
spans (...) and code blocks (...), preventing URLs
from being converted to links when they appear in code
examples.
Metadata replacement retains HTML edge-case handling and
properly cleans up intermediate buffers.
FIXED
Unified mode now correctly enables mixed list markers and
alpha lists by default when no âmode is specified
^ marker now properly separates lists by creating a
paragraph break instead of just blank lines
Empty paragraphs created by ^ marker are now removed from
final HTML output
Superscript and subscript processing now skips ^ and ~
characters
Superscript processing now skips ^ when part of footnote
reference
Subscript processing now skips ~ when part of critic
markup patterns
Setext headers are no longer broken when followed by
highlight syntax (==text==). Highlight processing now
stops at line breaks to prevent interference with header
parsing.
Metadata parser no longer incorrectly treats URLs and
angle-bracket autolinks as metadata. Lines containing < or
URLs (http://, https://, mailto:) are now skipped during
metadata extraction.
Superscript/subscript processor now correctly
differentiates between ~text~ (underline), ~word
(subscript), and ~~text~~ (strikethrough). Double-tilde
sequences are skipped so strikethrough extension can
handle them.
Subscript processing now stops at sentence terminators (.
, ; : ! ?) instead of including them in the subscript
content.
Metadata variable replacement now runs before autolinking
so [%key] values containing URLs are turned into links
when autolinking is enabled.
MMD metadata parsing no longer incorrectly rejects entries
with URL values; only URL-like keys or â<â characters in
keys are rejected, allowing âURL: https://example.comâ as
valid metadata.
Headers starting with # are now correctly recognized instead of being treated as autolinks. The autolink preprocessor now skips # at the start of a line when followed by whitespace.
Math processor now validates that \(...\) sequences contain actual math content (letters, numbers, or operators) before processing them. This prevents false positives like \(%\) from being treated as math when they only contain special characters.
[0.1.18] - 2025-12-06
Fixed
GitHub Actions workflow now properly builds separate Linux
x86_64 and ARM64 binaries
[0.1.17] - 2025-12-06
Fixed
Relaxed tables now disabled by default for CommonMark,
GFM, and MultiMarkdown modes (only enabled for Kramdown
and Unified modes)
Header ID extraction no longer incorrectly parses metadata
variables like [%title] as MMD-style header IDs
Tables with alignment/separator rows now correctly
generate <thead> even when relaxed table mode is enabled