apex-ruby 1.0.18 → 1.0.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +17 -3
  3. data/ext/apex_ext/apex_ext.c +260 -91
  4. data/ext/apex_ext/apex_src/CHANGELOG.md +440 -0
  5. data/ext/apex_ext/apex_src/CMakeLists.txt +15 -1
  6. data/ext/apex_ext/apex_src/Formula/apex.rb +7 -2
  7. data/ext/apex_ext/apex_src/Package.swift +15 -4
  8. data/ext/apex_ext/apex_src/README.md +94 -35
  9. data/ext/apex_ext/apex_src/VERSION +1 -1
  10. data/ext/apex_ext/apex_src/apex-debug.log +4 -0
  11. data/ext/apex_ext/apex_src/apex.code-workspace +38 -0
  12. data/ext/apex_ext/apex_src/apex.taskpaper +12 -0
  13. data/ext/apex_ext/apex_src/apex_deflist_debug.log +25 -0
  14. data/ext/apex_ext/apex_src/buildnotes.md +1435 -0
  15. data/ext/apex_ext/apex_src/cli/main.c +262 -33
  16. data/ext/apex_ext/apex_src/debug.log +7 -0
  17. data/ext/apex_ext/apex_src/docs/CITATIONS_EXAMPLES.md +274 -0
  18. data/ext/apex_ext/apex_src/docs/CITATIONS_IMPLEMENTATION_PLAN.md +749 -0
  19. data/ext/apex_ext/apex_src/docs/CITATIONS_PLAN.md +744 -0
  20. data/ext/apex_ext/apex_src/docs/INDEX_IMPLEMENTATION_ANALYSIS.md +382 -0
  21. data/ext/apex_ext/apex_src/docs/plans/2025-02-27-man-page-output-implementation.md +351 -0
  22. data/ext/apex_ext/apex_src/docs/superpowers/plans/2026-07-12-toc-output-format.md +745 -0
  23. data/ext/apex_ext/apex_src/docs/superpowers/plans/2026-07-18-bear-image-attributes.md +829 -0
  24. data/ext/apex_ext/apex_src/docs/superpowers/plans/2026-07-19-apex-node-npm.md +1148 -0
  25. data/ext/apex_ext/apex_src/docs/superpowers/specs/2026-07-12-toc-output-format-design.md +161 -0
  26. data/ext/apex_ext/apex_src/docs/superpowers/specs/2026-07-18-bear-image-attributes-design.md +184 -0
  27. data/ext/apex_ext/apex_src/docs/superpowers/specs/2026-07-19-apex-node-npm-design.md +211 -0
  28. data/ext/apex_ext/apex_src/documentation/app-settings-table.md +67 -0
  29. data/ext/apex_ext/apex_src/documentation/app-transformed/Citations.md +340 -0
  30. data/ext/apex_ext/apex_src/documentation/app-transformed/Credits.md +11 -0
  31. data/ext/apex_ext/apex_src/documentation/app-transformed/Header-IDs.md +234 -0
  32. data/ext/apex_ext/apex_src/documentation/app-transformed/Inline-Attribute-Lists.md +516 -0
  33. data/ext/apex_ext/apex_src/documentation/app-transformed/Metadata-Transforms.md +461 -0
  34. data/ext/apex_ext/apex_src/documentation/app-transformed/Modes.md +193 -0
  35. data/ext/apex_ext/apex_src/documentation/app-transformed/Multi-File-Documents.md +201 -0
  36. data/ext/apex_ext/apex_src/documentation/app-transformed/Plugins.md +678 -0
  37. data/ext/apex_ext/apex_src/documentation/app-transformed/Syntax.md +1367 -0
  38. data/ext/apex_ext/apex_src/documentation/docsets/Apex Command Line Options.cheatsheet.txt +6 -0
  39. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/C-API.html +774 -300
  40. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Callouts.html +1194 -0
  41. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Citations.html +416 -216
  42. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Command-Line-Options.html +3544 -1094
  43. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Configuration.html +371 -97
  44. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Credits.html +153 -23
  45. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Examples.html +358 -124
  46. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Filters.html +1656 -0
  47. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Generating-Man-Pages.html +1226 -0
  48. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Getting-Started.html +201 -56
  49. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Header-IDs.html +279 -109
  50. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Home.html +200 -53
  51. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Indices.html +2327 -0
  52. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Inline-Attribute-Lists.html +690 -271
  53. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Installation.html +201 -55
  54. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Limitations-and-Roadmap.html +238 -68
  55. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Metadata-Transforms.html +421 -221
  56. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Modes.html +340 -78
  57. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Multi-File-Documents.html +251 -86
  58. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Multi-Format-Images.html +1362 -0
  59. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Pandoc-Integration.html +277 -90
  60. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Plugins.html +673 -346
  61. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Quarto-Mode.html +1405 -0
  62. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Rendering-Markdown-In-Terminal.html +1902 -0
  63. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Syntax.html +1939 -1698
  64. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Tables.html +1781 -0
  65. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Troubleshooting.html +315 -140
  66. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Usage.html +307 -113
  67. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Using-Apex-with-Jekyll.html +1132 -0
  68. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Writing-Tests.html +1754 -0
  69. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Xcode-Integration.html +872 -376
  70. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/docSet.dsidx +0 -0
  71. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/optimizedIndex.dsidx +0 -0
  72. data/ext/apex_ext/apex_src/documentation/html/apex-app-docs.html +8746 -0
  73. data/ext/apex_ext/apex_src/documentation/shared_styles.css +20 -15
  74. data/ext/apex_ext/apex_src/documentation/transform_for_app.md +55 -0
  75. data/ext/apex_ext/apex_src/escaping-repro.md +103 -0
  76. data/ext/apex_ext/apex_src/examples/quick-actions/Markdown to RTF.workflow/Contents/Info.plist +8 -0
  77. data/ext/apex_ext/apex_src/examples/quick-actions/Markdown to RTF.workflow/Contents/QuickLook/Preview.png +0 -0
  78. data/ext/apex_ext/apex_src/examples/quick-actions/Markdown to RTF.workflow/Contents/document.wflow +547 -0
  79. data/ext/apex_ext/apex_src/examples/quick-actions/Markdown-to-RTF.workflow.zip +0 -0
  80. data/ext/apex_ext/apex_src/examples/quick-actions/README.md +33 -0
  81. data/ext/apex_ext/apex_src/include/apex/apex.h +92 -6
  82. data/ext/apex_ext/apex_src/include/apex/ast_rtf.h +23 -0
  83. data/ext/apex_ext/apex_src/include/apex/ast_terminal.h +6 -0
  84. data/ext/apex_ext/apex_src/include/apex/module.modulemap +5 -0
  85. data/ext/apex_ext/apex_src/include/apex/plugins.h +105 -0
  86. data/ext/apex_ext/apex_src/include/apexc.h +1 -0
  87. data/ext/apex_ext/apex_src/man/apex.1 +82 -78
  88. data/ext/apex_ext/apex_src/man/apex.1.md +81 -4
  89. data/ext/apex_ext/apex_src/objc/Apex.swift +73 -0
  90. data/ext/apex_ext/apex_src/objc/ApexPlugins.swift +79 -0
  91. data/ext/apex_ext/apex_src/objc/NSString+Apex.h +83 -5
  92. data/ext/apex_ext/apex_src/objc/NSString+Apex.m +244 -1
  93. data/ext/apex_ext/apex_src/objc/NSString+ApexPlugins.h +76 -0
  94. data/ext/apex_ext/apex_src/objc/NSString+ApexPlugins.m +169 -0
  95. data/ext/apex_ext/apex_src/output.html +87 -0
  96. data/ext/apex_ext/apex_src/plans/quarto-mode.md +442 -0
  97. data/ext/apex_ext/apex_src/src/_README.md +85 -32
  98. data/ext/apex_ext/apex_src/src/apex.c +770 -200
  99. data/ext/apex_ext/apex_src/src/ast_rtf.c +848 -0
  100. data/ext/apex_ext/apex_src/src/ast_terminal.c +184 -7
  101. data/ext/apex_ext/apex_src/src/extensions/abbreviations.c +10 -3
  102. data/ext/apex_ext/apex_src/src/extensions/advanced_tables.c +7 -11
  103. data/ext/apex_ext/apex_src/src/extensions/bear_image_attrs.c +467 -0
  104. data/ext/apex_ext/apex_src/src/extensions/bear_image_attrs.h +27 -0
  105. data/ext/apex_ext/apex_src/src/extensions/callouts.c +400 -4
  106. data/ext/apex_ext/apex_src/src/extensions/callouts.h +15 -5
  107. data/ext/apex_ext/apex_src/src/extensions/citations.c +26 -5
  108. data/ext/apex_ext/apex_src/src/extensions/code_fence_attrs.c +632 -0
  109. data/ext/apex_ext/apex_src/src/extensions/code_fence_attrs.h +22 -0
  110. data/ext/apex_ext/apex_src/src/extensions/concordance.c +632 -0
  111. data/ext/apex_ext/apex_src/src/extensions/definition_list.c +129 -15
  112. data/ext/apex_ext/apex_src/src/extensions/grid_tables.c +1050 -121
  113. data/ext/apex_ext/apex_src/src/extensions/html_markdown.c +569 -16
  114. data/ext/apex_ext/apex_src/src/extensions/ial.c +502 -52
  115. data/ext/apex_ext/apex_src/src/extensions/ial.h +2 -1
  116. data/ext/apex_ext/apex_src/src/extensions/includes.c +532 -33
  117. data/ext/apex_ext/apex_src/src/extensions/includes.h +7 -1
  118. data/ext/apex_ext/apex_src/src/extensions/index.c +1427 -140
  119. data/ext/apex_ext/apex_src/src/extensions/index.h +28 -0
  120. data/ext/apex_ext/apex_src/src/extensions/metadata.c +200 -161
  121. data/ext/apex_ext/apex_src/src/extensions/quarto_diagrams.c +251 -0
  122. data/ext/apex_ext/apex_src/src/extensions/quarto_diagrams.h +22 -0
  123. data/ext/apex_ext/apex_src/src/extensions/quarto_lists.c +624 -0
  124. data/ext/apex_ext/apex_src/src/extensions/quarto_lists.h +35 -0
  125. data/ext/apex_ext/apex_src/src/extensions/quarto_polish.c +264 -0
  126. data/ext/apex_ext/apex_src/src/extensions/quarto_polish.h +23 -0
  127. data/ext/apex_ext/apex_src/src/extensions/quarto_shortcodes.c +307 -0
  128. data/ext/apex_ext/apex_src/src/extensions/quarto_shortcodes.h +21 -0
  129. data/ext/apex_ext/apex_src/src/extensions/raw_content.c +351 -0
  130. data/ext/apex_ext/apex_src/src/extensions/raw_content.h +20 -0
  131. data/ext/apex_ext/apex_src/src/extensions/relaxed_tables.c +117 -3
  132. data/ext/apex_ext/apex_src/src/extensions/special_markers.c +53 -0
  133. data/ext/apex_ext/apex_src/src/extensions/syntax_highlight.c +23 -1
  134. data/ext/apex_ext/apex_src/src/extensions/table_html_postprocess.c +83 -92
  135. data/ext/apex_ext/apex_src/src/extensions/toc.c +446 -25
  136. data/ext/apex_ext/apex_src/src/extensions/toc.h +26 -1
  137. data/ext/apex_ext/apex_src/src/extensions/wiki_links.c +28 -0
  138. data/ext/apex_ext/apex_src/src/html_renderer.c +387 -17
  139. data/ext/apex_ext/apex_src/src/plugin_catalog.c +517 -0
  140. data/ext/apex_ext/apex_src/src/plugins_remote.c +88 -14
  141. data/ext/apex_ext/apex_src/src/plugins_remote.h +34 -0
  142. data/ext/apex_ext/apex_src/src/pretty_html.c +6 -1
  143. data/ext/apex_ext/apex_src/tests/fixtures/comprehensive_test.md +21 -0
  144. data/ext/apex_ext/apex_src/tests/fixtures/images/bear_image_attributes.md +10 -0
  145. data/ext/apex_ext/apex_src/tests/fixtures/includes/sections.md +19 -0
  146. data/ext/apex_ext/apex_src/tests/fixtures/includes/sections.txt +11 -0
  147. data/ext/apex_ext/apex_src/tests/fixtures/minimal_pb.md +2 -0
  148. data/ext/apex_ext/apex_src/tests/fixtures/obsidian-callouts.md +46 -0
  149. data/ext/apex_ext/apex_src/tests/fixtures/pagebreak_html_repro.md +5 -0
  150. data/ext/apex_ext/apex_src/tests/fixtures/python-callouts.md +47 -0
  151. data/ext/apex_ext/apex_src/tests/fixtures/quarto/callouts.md +3 -0
  152. data/ext/apex_ext/apex_src/tests/fixtures/quarto/code-attrs.md +3 -0
  153. data/ext/apex_ext/apex_src/tests/fixtures/quarto/diagrams.md +8 -0
  154. data/ext/apex_ext/apex_src/tests/fixtures/quarto/lists.md +19 -0
  155. data/ext/apex_ext/apex_src/tests/fixtures/quarto/raw-content.md +5 -0
  156. data/ext/apex_ext/apex_src/tests/fixtures/quarto/shortcodes.md +7 -0
  157. data/ext/apex_ext/apex_src/tests/fixtures/quarto/smoke.md +81 -0
  158. data/ext/apex_ext/apex_src/tests/fixtures/quarto/spans.md +5 -0
  159. data/ext/apex_ext/apex_src/tests/fixtures/quarto-callouts.md +47 -0
  160. data/ext/apex_ext/apex_src/tests/fixtures/speed.md +192 -0
  161. data/ext/apex_ext/apex_src/tests/fixtures/tables/grid-tables.md +37 -0
  162. data/ext/apex_ext/apex_src/tests/paginate_cli_test.sh +16 -0
  163. data/ext/apex_ext/apex_src/tests/test_bear_image_attrs.c +301 -0
  164. data/ext/apex_ext/apex_src/tests/test_concordance.tsv +4 -0
  165. data/ext/apex_ext/apex_src/tests/test_escaping_repro.c +82 -0
  166. data/ext/apex_ext/apex_src/tests/test_extensions.c +821 -1
  167. data/ext/apex_ext/apex_src/tests/test_ial.c +7 -0
  168. data/ext/apex_ext/apex_src/tests/test_marked_integration.c +25 -0
  169. data/ext/apex_ext/apex_src/tests/test_metadata.c +129 -0
  170. data/ext/apex_ext/apex_src/tests/test_output.c +568 -2
  171. data/ext/apex_ext/apex_src/tests/test_runner.c +11 -0
  172. data/ext/apex_ext/apex_src/tests/test_tables.c +420 -0
  173. data/ext/apex_ext/apex_src/vendor/cmark-gfm/CMakeLists.txt +1 -1
  174. data/ext/apex_ext/apex_src/vendor/cmark-gfm/extensions/autolink.c +77 -1
  175. data/ext/apex_ext/apex_src/vendor/cmark-gfm/extensions/table.c +135 -5
  176. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/blocks.c +56 -1
  177. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/config.h +38 -0
  178. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/main.c +1 -1
  179. data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/__pycache__/cmark.cpython-314.pyc +0 -0
  180. data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/__pycache__/normalize.cpython-314.pyc +0 -0
  181. data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/extensions.txt +69 -0
  182. data/ext/apex_ext/apex_src/xcode_build/CMakeScripts/ReRunCMake.make +74 -0
  183. data/ext/apex_ext/apex_src/xcode_build/CTestTestfile.cmake +22 -0
  184. data/ext/apex_ext/apex_src/xcode_build/Debug/Apex.framework/Versions/A/Apex +0 -0
  185. data/ext/apex_ext/apex_src/xcode_build/Debug/Apex.framework/Versions/A/Resources/Info.plist +44 -0
  186. data/ext/apex_ext/apex_src/xcode_build/Debug/Apex.framework/Versions/A/_CodeSignature/CodeResources +128 -0
  187. data/ext/apex_ext/apex_src/xcode_build/apex.xcodeproj/project.pbxproj +5118 -0
  188. data/ext/apex_ext/apex_src/xcode_build/apex.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +8 -0
  189. data/ext/apex_ext/apex_src/xcode_build/apex.xcodeproj/project.xcworkspace/xcuserdata/ttscoff.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  190. data/ext/apex_ext/apex_src/xcode_build/apex.xcodeproj/project.xcworkspace/xcuserdata/ttscoff.xcuserdatad/WorkspaceSettings.xcsettings +8 -0
  191. data/ext/apex_ext/apex_src/xcode_build/apex.xcodeproj/xcuserdata/ttscoff.xcuserdatad/xcschemes/xcschememanagement.plist +99 -0
  192. data/ext/apex_ext/apex_src/xcode_build/cmake_install.cmake +452 -0
  193. data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/CMakeScripts/ReRunCMake.make +52 -0
  194. data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/cmake_install.cmake +55 -0
  195. data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/cmark-gfm.xcodeproj/project.pbxproj +2455 -0
  196. data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/cmark-gfm.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +8 -0
  197. data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/extensions/cmake_install.cmake +171 -0
  198. data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/man/cmake_install.cmake +55 -0
  199. data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/src/cmake_install.cmake +220 -0
  200. data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/src/cmark-gfm_export.h +43 -0
  201. data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/src/cmark-gfm_version.h +7 -0
  202. data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/src/config.h +76 -0
  203. data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/src/libcmark-gfm.pc +10 -0
  204. data/ext/apex_ext/extconf.rb +35 -0
  205. data/lib/apex/version.rb +2 -2
  206. metadata +117 -2
  207. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/tempOptimizedIndex.dsidx +0 -0
@@ -0,0 +1,678 @@
1
+ ## Plugins
2
+
3
+ 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.
4
+
5
+ This page covers:
6
+
7
+ - **Where plugins live**
8
+ - **How to enable/disable plugins**
9
+ - **Processing phases**
10
+ - **Plugin manifest format (`plugin.yml`)**
11
+ - **External handler plugins (scripts/commands)**
12
+ - **Declarative regex plugins (pure YAML)**
13
+ - **Plugin bundles (`bundle` key)**
14
+ - **Support directory and environment variables**
15
+ - **Plugin metadata for directories and installers**
16
+ - **Installing plugins from the directory or a Git URL**
17
+ - **Examples: `kbd` liquid tag and `:emoji:` spans**
18
+
19
+ ---
20
+
21
+ ## Enabling and disabling plugins
22
+
23
+ By design, **plugins are disabled by default** so Apex's performance and behavior are unchanged unless you explicitly opt in.
24
+
25
+ Enable plugins by checking **Settings->Processor->Include Plugins**.
26
+
27
+ You can also enable plugins in document metadata (in the document's front matter):
28
+
29
+ ```yaml
30
+ ---
31
+ title: Plugin demo
32
+ plugins: true
33
+ ---
34
+ ```
35
+
36
+ If you never enable plugins in settings or set `plugins: true` in metadata, Apex will not load or run any plugins.
37
+
38
+ ---
39
+
40
+ ## Where plugins live
41
+
42
+ Plugins are discovered from **two locations**, in this order:
43
+
44
+ - **Project-local plugins**
45
+ - Directory: `.apex/plugins/` in the same project as your documents
46
+ - Structure: **one subdirectory per plugin**, for example:
47
+ - `.apex/plugins/kbd/plugin.yml`
48
+ - `.apex/plugins/kbd/kbd_plugin.rb`
49
+ - `.apex/plugins/emoji/plugin.yml`
50
+
51
+ - **Global (user) plugins**
52
+ - Directory (XDG-aware):
53
+ - If `$XDG_CONFIG_HOME` is set:
54
+ - `$XDG_CONFIG_HOME/apex/plugins/`
55
+ - Otherwise:
56
+ - `~/.config/apex/plugins/`
57
+ - Same structure: one subdirectory per plugin:
58
+ - `~/.config/apex/plugins/kbd/plugin.yml`
59
+ - `~/.config/apex/plugins/emoji/plugin.yml`
60
+
61
+ **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.
62
+
63
+ ---
64
+
65
+ ## Installing plugins in Marked
66
+
67
+ Marked provides an **Install Plugins** option that allows you to select a plugin directory on your disk and have it automatically installed to `~/.config/apex/plugins`. This makes it easy to install plugins from local directories or cloned repositories.
68
+
69
+ Marked will list available plugins in a **Plugins panel** within the Processor settings. From there, you can:
70
+
71
+ - View all installed plugins
72
+ - Enable or disable individual plugins
73
+ - Install new plugins from the central directory
74
+ - Remove installed plugins
75
+
76
+ ---
77
+
78
+ ## Processing phases
79
+
80
+ Apex exposes several **phases** in its pipeline. Plugins can hook into one or more phases; for now, two are wired up:
81
+
82
+ - **`pre_parse`**
83
+ - Runs on the **raw Markdown text** *before* it is parsed.
84
+ - Good for:
85
+ - Custom syntax (e.g. `{% kbd ... %}`)
86
+ - Textual rewrites
87
+ - Adding/removing markup before Apex sees it
88
+
89
+ - **`post_render`**
90
+ - Runs on the **final HTML output** after Apex finishes rendering.
91
+ - Good for:
92
+ - Wrapping elements in spans/divs
93
+ - Adding CSS classes
94
+ - Simple HTML post-processing (e.g. turning `:emoji:` into `<span>`)
95
+
96
+ Internally, plugins for each phase are run in a **deterministic order**:
97
+
98
+ 1. Sorted by **priority** (lower numbers first; default is `100`).
99
+ 2. Ties broken by **plugin `id`** (lexicographically).
100
+
101
+ ---
102
+
103
+ ## Plugin manifest: `plugin.yml`
104
+
105
+ Each plugin is defined by a **manifest** file:
106
+
107
+ - **File name**: `plugin.yml`
108
+ - **Location**: inside the plugin's directory, e.g.:
109
+
110
+ ```text
111
+ .apex/plugins/kbd/plugin.yml
112
+ ~/.config/apex/plugins/emoji/plugin.yml
113
+ ```
114
+
115
+ At minimum, a plugin needs:
116
+
117
+ ```yaml
118
+ ---
119
+ id: my-plugin
120
+ # Optional, but strongly recommended metadata:
121
+ # title: Human-friendly name (if omitted, tools may fall back to id)
122
+ # author: Name or handle of the plugin author
123
+ # description: Short, human-readable description of what the plugin does
124
+ # homepage: URL for the plugin landing page or README
125
+ # repo: Canonical Git repository URL used for installation/updates
126
+ phase: pre_parse # or post_render
127
+ priority: 100 # optional, lower runs earlier
128
+ timeout_ms: 0 # optional, 0 = no extra timeout logic (best-effort)
129
+ ---
130
+ ```
131
+
132
+ From there, you choose **one of two plugin types**:
133
+
134
+ - **External handler plugin**
135
+ - Runs an external command (Ruby, Python, shell, etc.).
136
+ - Declared with a `handler.command` field.
137
+ - **Declarative regex plugin**
138
+ - No external code; in-process regex search/replace.
139
+ - Declared with `pattern` and `replacement` fields.
140
+
141
+ You can't mix both styles in a single plugin; if `handler.command` is present, the plugin is treated as external.
142
+
143
+ ### Metadata fields for directories and installers
144
+
145
+ To support plugin directories, automatic installation, and future auto-update tools, Apex understands several **optional metadata fields** in `plugin.yml`:
146
+
147
+ - **`title`**: Short, human-friendly name for the plugin (e.g. `Keyboard Shortcuts`).
148
+ - **`author`**: Free-form author string (your name, handle, or organization).
149
+ - **`description`**: One–two sentence description of what the plugin does. This is already used in examples and is what directory listings will usually display.
150
+ - **`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 plugin listings.
151
+ - **`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 the `plugin.yml` manifest (and any supporting files) at its root.
152
+ - **`post_install`**: Optional command that Apex will run after cloning the plugin during installation. See "Post-install hooks" below.
153
+
154
+ 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.
155
+
156
+ ### Post-install hooks (`post_install`)
157
+
158
+ External plugins can define an optional `post_install` command in `plugin.yml` that Apex will run immediately after installing the plugin:
159
+
160
+ ```yaml
161
+ ---
162
+ id: kbd
163
+ # ... other fields ...
164
+ post_install: ./post_install.sh
165
+ ---
166
+ ```
167
+
168
+ When you install a plugin, Apex will:
169
+
170
+ 1. Clone or copy the plugin into your user plugin directory (e.g. `~/.config/apex/plugins/kbd`).
171
+ 2. Look for `plugin.yml` (or `plugin.yaml`) in the root of that directory.
172
+ 3. If it finds a `post_install` key, run the value as a shell command from the plugin directory (equivalent to `cd <plugin_dir> && <post_install>`).
173
+
174
+ This hook is useful for:
175
+
176
+ - Creating initial configuration files or support data under `APEX_SUPPORT_DIR`.
177
+ - Printing a welcome or usage message via `echo`.
178
+ - Running an interactive setup script that asks the user questions and writes config.
179
+
180
+ If the `post_install` command exits with a non-zero status, Apex prints a warning but considers the plugin installed successfully (the installation is not rolled back).
181
+
182
+ ---
183
+
184
+ ## Plugin bundles (`bundle` key)
185
+
186
+ Sometimes it is convenient for a single repository to provide **multiple related plugins** as a bundle—for example, a "documentation" bundle that contains:
187
+
188
+ - `kbd` – expands `{% kbd @3 %}` keyboard shortcuts
189
+ - `prefpane` – expands `{% prefspane Advanced, Processor %}`
190
+ - `menubar` – expands `{% menubar File, Open %}`
191
+
192
+ 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.
193
+
194
+ ### Bundle structure
195
+
196
+ A **bundle manifest** has:
197
+
198
+ - Top-level metadata that applies to the bundle as a whole.
199
+ - A `bundle:` key whose value is a YAML sequence (array) of per-plugin configs.
200
+
201
+ Example:
202
+
203
+ ```yaml
204
+ ---
205
+ id: documentation
206
+ title: Documentation helpers
207
+ author: Brett Terpstra
208
+ description: A bundle of documentation-related helpers (kbd, menubar, prefpane).
209
+ homepage: https://github.com/ApexMarkdown/apex-plugin-documentation
210
+ repo: https://github.com/ApexMarkdown/apex-plugin-documentation.git
211
+
212
+ # Each entry in bundle defines a child plugin
213
+ bundle:
214
+ - id: kbd
215
+ title: Keyboard Shortcuts
216
+ description: Render {% kbd ... %} key combos to HTML <kbd> elements
217
+ phase: pre_parse
218
+ priority: 100
219
+ handler:
220
+ command: "ruby kbd_plugin.rb"
221
+
222
+ - id: menubar
223
+ title: Menubar Paths
224
+ description: Render {% menubar File, Open %} to a styled menu path
225
+ phase: pre_parse
226
+ handler:
227
+ command: "ruby menubar_plugin.rb"
228
+
229
+ - id: prefspane
230
+ title: Preferences Pane
231
+ description: Render {% prefspane Advanced, Processor %} to a styled preference path
232
+ phase: pre_parse
233
+ handler:
234
+ command: "ruby prefspane_plugin.rb"
235
+ ---
236
+ ```
237
+
238
+ Apex will treat this as **three distinct plugins**:
239
+
240
+ - `kbd`
241
+ - `menubar`
242
+ - `prefspane`
243
+
244
+ all sourced from the same repository and manifest.
245
+
246
+ ### Inheritance and overrides
247
+
248
+ When loading a bundle, Apex:
249
+
250
+ 1. Parses the top-level mapping (the "bundle header"), and the `bundle:` sequence, using full YAML.
251
+ 2. For each entry in `bundle`:
252
+ - Starts with the **top-level metadata**.
253
+ - Applies the child entry's fields as **overrides**.
254
+
255
+ Concretely:
256
+
257
+ - Top-level `author`, `homepage`, and `repo` are used as defaults for every child plugin.
258
+ - Each child **must** define its own:
259
+ - `id`
260
+ - `phase`
261
+ - and either:
262
+ - `handler.command` (external plugin), or
263
+ - `pattern` + `replacement` (declarative plugin)
264
+ - A child **may** also define its own:
265
+ - `title`
266
+ - `description`
267
+ - `priority`
268
+ - `timeout_ms`
269
+ - `flags`
270
+ - If a child defines `author`, `homepage`, or `repo`, those values **override** the bundle-level defaults for that child only.
271
+
272
+ After merging, each child is turned into a normal internal plugin record, just like if it had its own standalone `plugin.yml`.
273
+
274
+ ### Behavior and limitations
275
+
276
+ - **Discovery**:
277
+ - Bundle manifests live in the same places as normal plugins:
278
+ - Project-local: `.apex/plugins/documentation/plugin.yml`
279
+ - Global: `~/.config/apex/plugins/documentation/plugin.yml`
280
+ - **Phases and IDs**:
281
+ - Each child plugin's `id` and `phase` are used when registering and running plugins.
282
+ - All normal rules apply:
283
+ - IDs must be unique across all loaded plugins.
284
+ - Phase must be `pre_parse` or `post_render` (for now).
285
+ - **libyaml requirement**:
286
+ - The `bundle` syntax relies on full YAML parsing via `libyaml`.
287
+ - 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.
288
+
289
+ ---
290
+
291
+ ## External handler plugins (scripts/commands)
292
+
293
+ An external handler plugin defines a **command** to run, which receives JSON on stdin and writes the transformed text to stdout.
294
+
295
+ ### Manifest fields
296
+
297
+ ```yaml
298
+ ---
299
+ id: kbd
300
+ title: Keyboard Shortcuts
301
+ author: Brett Terpstra
302
+ description: Render {% kbd ... %} key combos to HTML
303
+ homepage: https://github.com/ApexMarkdown/apex-kbd-plugin
304
+ repo: https://github.com/ApexMarkdown/apex-kbd-plugin.git
305
+ phase: pre_parse
306
+ priority: 100
307
+ timeout_ms: 0 # optional
308
+ handler:
309
+ command: "ruby kbd_plugin.rb"
310
+ ---
311
+ ```
312
+
313
+ - **`id`**: unique identifier (no spaces recommended).
314
+ - **`title`**/`author`/`description`/`homepage`/`repo`**: optional metadata used by listing/installation tools and plugin directories.
315
+ - **`phase`**: `pre_parse` or `post_render`.
316
+ - **`priority`**: integer; lower runs first.
317
+ - **`timeout_ms`**: optional; host may use this as a soft cap.
318
+ - **`handler.command`**:
319
+ - Executed via `sh -c` from the plugin's directory (`APEX_PLUGIN_DIR`).
320
+ - Must be runnable in your environment (e.g. Ruby installed if you use `ruby`).
321
+
322
+ ### JSON protocol
323
+
324
+ For text phases (`pre_parse`, `post_render`), Apex sends your command a JSON object on **stdin**:
325
+
326
+ ```json
327
+ {
328
+ "version": 1,
329
+ "plugin_id": "kbd",
330
+ "phase": "pre_parse",
331
+ "text": "raw or rendered text here"
332
+ }
333
+ ```
334
+
335
+ Your plugin should:
336
+
337
+ 1. Read all of stdin.
338
+ 2. Parse the JSON.
339
+ 3. Transform the `text` field.
340
+ 4. Print the **new text only** to stdout (no extra JSON, headers, or logging).
341
+
342
+ If your plugin fails, times out, or prints nothing, Apex will treat it as a no-op and continue gracefully.
343
+
344
+ ---
345
+
346
+ ## Declarative regex plugins (no scripting)
347
+
348
+ 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.
349
+
350
+ ### Manifest fields
351
+
352
+ ```yaml
353
+ ---
354
+ id: emoji-span
355
+ title: Emoji span wrapper
356
+ author: Brett Terpstra
357
+ description: Wrap :emoji: markers in a span for styling
358
+ homepage: https://github.com/ApexMarkdown/apex-emoji-plugin
359
+ repo: https://github.com/ApexMarkdown/apex-emoji-plugin.git
360
+ phase: post_render
361
+ pattern: "(:[a-zA-Z0-9_+-]+:)"
362
+ replacement: "<span class=\"emoji\">$1</span>"
363
+ flags: "i" # optional: e.g. i, m, s
364
+ priority: 200
365
+ timeout_ms: 0
366
+ ---
367
+ ```
368
+
369
+ - **`pattern`**: POSIX regular expression (compiled via `regcomp`).
370
+ - **`replacement`**:
371
+ - Replacement string with capture groups like `$1`, `$2`, etc.
372
+ - Runs repeatedly across the text until no more matches.
373
+ - **`flags`** (optional):
374
+ - Currently supports:
375
+ - `i` – case-insensitive
376
+ - `m` – multi-line
377
+ - `s` – dot matches newline (if supported by underlying regex flavor)
378
+
379
+ This is ideal when:
380
+
381
+ - You only need straightforward pattern substitution.
382
+ - Performance matters and you want to avoid `fork/exec`.
383
+
384
+ ---
385
+
386
+ ## Support directory and environment variables
387
+
388
+ 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.
389
+
390
+ ### Support directory (`APEX_SUPPORT_DIR`)
391
+
392
+ Base path:
393
+
394
+ - If `$XDG_CONFIG_HOME` is set:
395
+ - `$XDG_CONFIG_HOME/apex/support/`
396
+ - Otherwise:
397
+ - `~/.config/apex/support/`
398
+
399
+ For each plugin, Apex creates:
400
+
401
+ ```text
402
+ APEX_SUPPORT_DIR/<plugin-id>/
403
+ ```
404
+
405
+ You can safely write caches, logs, or temporary files there.
406
+
407
+ ### Environment variables for external plugins
408
+
409
+ When Apex runs an external handler plugin, it sets:
410
+
411
+ - **`APEX_PLUGIN_DIR`**
412
+ - Filesystem path to the plugin's directory (where `plugin.yml` lives).
413
+ - Useful for loading sidecar files, templates, etc.
414
+
415
+ - **`APEX_SUPPORT_DIR`**
416
+ - Base support directory as described above.
417
+ - You'll usually combine it with your plugin id, e.g. `$APEX_SUPPORT_DIR/kbd`.
418
+
419
+ - **`APEX_FILE_PATH`**
420
+ - When Apex is invoked on a file, this is the **original path** that was passed. Plugins can use this to make decisions based on path, directory, or extension.
421
+ - When Apex reads from stdin, `APEX_FILE_PATH` is set to:
422
+ - The current `base_directory` (if one was set), or
423
+ - An empty string (`""`) if no base directory is known.
424
+ - An empty `APEX_FILE_PATH` is a clear signal that there is no concrete source file path.
425
+
426
+ All of these variables apply only during the external command's execution and are restored afterward.
427
+
428
+ ---
429
+
430
+ ## Installing plugins from the directory
431
+
432
+ 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:
433
+
434
+ - `https://github.com/ApexMarkdown/apex-plugins` (repository)
435
+ - `https://raw.githubusercontent.com/ApexMarkdown/apex-plugins/refs/heads/main/apex-plugins.json` (JSON index)
436
+
437
+ Each entry in `apex-plugins.json` looks like this:
438
+
439
+ ```json
440
+ {
441
+ "plugins": [
442
+ {
443
+ "id": "kbd",
444
+ "title": "Keyboard Shortcuts",
445
+ "description": "Renders {% kbd %} tags as <kbd> elements.",
446
+ "author": "Brett Terpstra",
447
+ "homepage": "https://github.com/ApexMarkdown/apex-plugin-kbd",
448
+ "repo": "https://github.com/ApexMarkdown/apex-plugin-kbd"
449
+ }
450
+ ]
451
+ }
452
+ ```
453
+
454
+ - **`id`** must match the plugin `id` declared in that repo's `plugin.yml`.
455
+ - **`repo`** is the canonical Git URL that Apex will pass to `git clone`.
456
+ - **`title`**, **`author`**, **`description`**, and **`homepage`** are used for human-readable listings.
457
+
458
+ ### Listing available plugins
459
+
460
+ Marked lists available plugins in the **Plugins panel** within Processor settings. You can see:
461
+
462
+ - Installed plugins (with their status)
463
+ - Available plugins from the central directory
464
+ - Plugin descriptions and metadata
465
+
466
+ ### Installing a plugin
467
+
468
+ In Marked, you can install plugins in several ways:
469
+
470
+ 1. **From the Plugins panel**: Browse available plugins and install them with a single click.
471
+ 2. **Using Install Plugins**: Select a plugin directory on your disk, and Marked will automatically install it to `~/.config/apex/plugins`.
472
+ 3. **From a Git URL**: If you have a plugin repository URL, you can install it directly.
473
+
474
+ When installing from the central directory, Apex will:
475
+
476
+ 1. Fetch `apex-plugins.json` from the central directory.
477
+ 2. Find the entry whose `id` matches the plugin you want.
478
+ 3. Read the `repo` field and clone the repository.
479
+ 4. Install into `$XDG_CONFIG_HOME/apex/plugins/<id>` or `~/.config/apex/plugins/<id>`.
480
+
481
+ When installing from a direct URL or local directory:
482
+
483
+ 1. Clone or copy the repository into a temporary directory.
484
+ 2. Read `plugin.yml` (or `plugin.yaml`) from the cloned repo to determine the plugin `id`.
485
+ 3. Move the cloned directory to the final location: `~/.config/apex/plugins/<id>/`.
486
+
487
+ If the target directory already exists, Apex will refuse to overwrite it and show an error. You can remove or rename the existing directory and try again.
488
+
489
+ After successful installation, look for a `post_install` hook in `plugin.yml` and run it if present.
490
+
491
+ > **Security note:** Apex plugins execute unverified code. Only install plugins from trusted sources.
492
+
493
+ Once installed, the plugin is just like any other user-global plugin:
494
+
495
+ - It will be discovered from `$XDG_CONFIG_HOME/apex/plugins` or `~/.config/apex/plugins`.
496
+ - You still need to enable plugins via **Settings->Processor->Include Plugins** or metadata (`plugins: true`).
497
+
498
+ If you are authoring a plugin for inclusion in the directory:
499
+
500
+ - Make sure your `plugin.yml` has at least `id`, `phase`, and either `handler.command` or `pattern`/`replacement`.
501
+ - Add `title`, `author`, `description`, `homepage`, and `repo` to `plugin.yml` so directory tools and installers have complete metadata.
502
+ - Ensure the `plugin.yml` and any scripts live at the **root** of the Git repository referenced by `repo`.
503
+
504
+ ### Uninstalling a plugin
505
+
506
+ To remove a locally installed plugin, use the Plugins panel in Processor settings. You can remove plugins from there, which will:
507
+
508
+ - Verify that the plugin directory exists under the user plugin path (`$XDG_CONFIG_HOME/apex/plugins` or `~/.config/apex/plugins`).
509
+ - Prompt for confirmation before deleting the plugin directory.
510
+ - Remove only the plugin's directory; support data under `.../apex/support/<plugin-id>/` is left intact.
511
+
512
+ This 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.
513
+
514
+ ---
515
+
516
+ ## Share Your Plugin
517
+
518
+ To request that your plugin be added to the central directory:
519
+
520
+ 1. **Fork** the `ApexMarkdown/apex-plugins` repository on GitHub to your own account or organization.
521
+ 2. **Clone** your fork locally and create a new branch for your plugin entry (for example, `add-kbd-plugin`).
522
+ 3. Open the `apex-plugins.json` file in the root of your fork and add a new object to the `plugins` array with the fields:
523
+ - `id` (matching the `id` in your plugin's `plugin.yml`),
524
+ - `title`,
525
+ - `description`,
526
+ - `author`,
527
+ - `homepage`,
528
+ - `repo` (canonical Git URL for the plugin repo).
529
+ 4. Commit your changes and **push** the branch to your fork on GitHub.
530
+ 5. From your fork, open a **pull request** against the `main` branch of `ApexMarkdown/apex-plugins`, briefly describing your plugin and confirming that the `id` and `repo` match your published plugin repository.
531
+ 6. Once the pull request is reviewed and merged, your plugin will show up in the Plugins panel and be installable via the interface.
532
+
533
+ ---
534
+
535
+ ## Example: `kbd` liquid tag plugin
536
+
537
+ This example shows how to support a liquid-style `{% kbd ... %}` syntax, turning key combos into `<kbd>` markup.
538
+
539
+ ### Directory layout
540
+
541
+ Project-local example:
542
+
543
+ ```text
544
+ .apex/
545
+ plugins/
546
+ kbd/
547
+ plugin.yml
548
+ kbd_plugin.rb
549
+ ```
550
+
551
+ Global example:
552
+
553
+ ```text
554
+ ~/.config/apex/plugins/
555
+ kbd/
556
+ plugin.yml
557
+ kbd_plugin.rb
558
+ ```
559
+
560
+ ### `plugin.yml`
561
+
562
+ ```yaml
563
+ ---
564
+ id: kbd
565
+ title: Keyboard Shortcuts
566
+ author: Brett Terpstra
567
+ description: Render {% kbd ... %} key combos to HTML <kbd> elements
568
+ homepage: https://github.com/ApexMarkdown/apex-kbd-plugin
569
+ repo: https://github.com/ApexMarkdown/apex-kbd-plugin.git
570
+ phase: pre_parse
571
+ priority: 100
572
+ timeout_ms: 0
573
+ handler:
574
+ command: "ruby kbd_plugin.rb"
575
+ ---
576
+ ```
577
+
578
+ ### `kbd_plugin.rb` (simplified shape)
579
+
580
+ The full script lives in the `examples` directory of the Apex repo as a reference. Conceptually, it:
581
+
582
+ - Reads JSON from stdin.
583
+ - Extracts `text`.
584
+ - Replaces each `{% kbd ... %}` occurrence with properly formatted `<kbd>` HTML.
585
+ - Prints the full transformed text to stdout.
586
+
587
+ A very abridged sketch:
588
+
589
+ ```ruby
590
+ #!/usr/bin/env ruby
591
+ require "json"
592
+
593
+ payload = JSON.parse($stdin.read)
594
+ text = payload["text"] || ""
595
+
596
+ # ... helper methods for normalizing modifier names, etc. ...
597
+
598
+ def render_kbd(markup)
599
+ # convert markup like "^~@r" to HTML
600
+ # returning something like:
601
+ # <kbd>⌃</kbd>+<kbd>⌥</kbd>+<kbd>⌘</kbd>+<kbd>R</kbd>
602
+ end
603
+
604
+ result = text.gsub(/\{%\s*kbd\s+([^%]+)%\}/) do
605
+ render_kbd(Regexp.last_match(1))
606
+ end
607
+
608
+ print result
609
+ ```
610
+
611
+ You can customize this script as you like; the only requirement is that it obey the stdin JSON / stdout contract.
612
+
613
+ ---
614
+
615
+ ## Example: `:emoji:` span plugin (declarative)
616
+
617
+ This plugin turns `:emoji:` tokens in the final HTML into `<span class="emoji">:emoji:</span>`.
618
+
619
+ ### Directory layout
620
+
621
+ ```text
622
+ ~/.config/apex/plugins/
623
+ emoji/
624
+ plugin.yml
625
+ ```
626
+
627
+ ### `plugin.yml`
628
+
629
+ ```yaml
630
+ ---
631
+ id: emoji-span
632
+ title: Emoji span wrapper
633
+ author: Brett Terpstra
634
+ description: Wrap :emoji: markers in a span for styling
635
+ homepage: https://github.com/ApexMarkdown/apex-emoji-plugin
636
+ repo: https://github.com/ApexMarkdown/apex-emoji-plugin.git
637
+ phase: post_render
638
+ pattern: "(:[a-zA-Z0-9_+-]+:)"
639
+ replacement: "<span class=\"emoji\">$1</span>"
640
+ flags: "i"
641
+ priority: 200
642
+ timeout_ms: 0
643
+ ---
644
+ ```
645
+
646
+ Because this is a declarative plugin, **no external command is run**. Apex compiles the regex and runs the replacements internally.
647
+
648
+ ---
649
+
650
+ ## Putting it all together
651
+
652
+ A typical workflow:
653
+
654
+ 1. **Create a plugin directory**
655
+ - Project-local: `.apex/plugins/my-plugin/`
656
+ - Or global: `~/.config/apex/plugins/my-plugin/`
657
+
658
+ 2. **Add `plugin.yml`**
659
+ - Choose `phase` (`pre_parse` or `post_render`).
660
+ - Choose plugin type:
661
+ - External: add `handler.command`.
662
+ - Declarative: add `pattern`, `replacement`, and optional `flags`.
663
+ - Optionally, add metadata fields (`title`, `author`, `description`, `homepage`, `repo`) so your plugin shows up nicely in directories and can be auto-installed.
664
+ - For bundles, use `bundle:` to define multiple related child plugins in one manifest.
665
+
666
+ 3. **Add code (if external)**
667
+ - Put your script alongside `plugin.yml`.
668
+ - Implement the JSON stdin → text stdout contract.
669
+
670
+ 4. **Enable plugins**
671
+ - Enable **Settings->Processor->Include Plugins**, **or**
672
+ - Add `plugins: true` (or `enable-plugins: true`) to your document metadata.
673
+
674
+ 5. **Test**
675
+ - Open a sample document that exercises your plugin syntax in Marked.
676
+ - If something goes wrong, log to a file under `$APEX_SUPPORT_DIR/<id>/` rather than printing debugging output to stdout.
677
+
678
+ 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.