apex-ruby 1.0.17 → 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,169 @@
1
+ /**
2
+ * NSString+ApexPlugins.m
3
+ */
4
+
5
+ #import "NSString+ApexPlugins.h"
6
+ #import <apex/plugins.h>
7
+
8
+ NSErrorDomain const ApexPluginErrorDomain = @"ApexPluginErrorDomain";
9
+
10
+ @interface ApexPluginInfo ()
11
+ @property (nonatomic, copy, readwrite) NSString *pluginId;
12
+ @property (nonatomic, copy, readwrite, nullable) NSString *title;
13
+ @property (nonatomic, copy, readwrite, nullable) NSString *pluginDescription;
14
+ @property (nonatomic, copy, readwrite, nullable) NSString *author;
15
+ @property (nonatomic, copy, readwrite, nullable) NSString *homepage;
16
+ @property (nonatomic, copy, readwrite, nullable) NSString *repo;
17
+ @end
18
+
19
+ @implementation ApexPluginInfo
20
+
21
+ + (instancetype)infoWithCStruct:(const apex_plugin_info *)info {
22
+ if (!info || !info->id) return nil;
23
+ ApexPluginInfo *obj = [[self alloc] initPrivate];
24
+ obj.pluginId = [NSString stringWithUTF8String:info->id];
25
+ if (info->title) obj.title = [NSString stringWithUTF8String:info->title];
26
+ if (info->description) obj.pluginDescription = [NSString stringWithUTF8String:info->description];
27
+ if (info->author) obj.author = [NSString stringWithUTF8String:info->author];
28
+ if (info->homepage) obj.homepage = [NSString stringWithUTF8String:info->homepage];
29
+ if (info->repo) obj.repo = [NSString stringWithUTF8String:info->repo];
30
+ return obj;
31
+ }
32
+
33
+ - (instancetype)initPrivate {
34
+ return [super init];
35
+ }
36
+
37
+ @end
38
+
39
+ static NSArray<ApexPluginInfo *> *ApexPluginInfosFromCatalog(apex_plugin_catalog *catalog) {
40
+ if (!catalog) return @[];
41
+
42
+ NSMutableArray<ApexPluginInfo *> *items = [NSMutableArray arrayWithCapacity:catalog->count];
43
+ for (size_t i = 0; i < catalog->count; i++) {
44
+ ApexPluginInfo *info = [ApexPluginInfo infoWithCStruct:&catalog->items[i]];
45
+ if (info) [items addObject:info];
46
+ }
47
+ return [items copy];
48
+ }
49
+
50
+ static NSError *ApexPluginMakeError(ApexPluginError code, const char *message) {
51
+ NSString *desc = message ? [NSString stringWithUTF8String:message] : @"Unknown plugin error";
52
+ return [NSError errorWithDomain:ApexPluginErrorDomain
53
+ code:code
54
+ userInfo:@{NSLocalizedDescriptionKey: desc}];
55
+ }
56
+
57
+ @implementation ApexPluginCatalog
58
+
59
+ + (NSString *)defaultDirectoryURL {
60
+ return [NSString stringWithUTF8String:APEX_PLUGIN_DIRECTORY_URL];
61
+ }
62
+
63
+ + (nullable NSArray<ApexPluginInfo *> *)fetchAvailablePlugins:(NSError **)error {
64
+ return [self fetchAvailablePluginsFromURL:self.defaultDirectoryURL error:error];
65
+ }
66
+
67
+ + (nullable NSArray<ApexPluginInfo *> *)fetchAvailablePluginsFromURL:(NSString *)url
68
+ error:(NSError **)error {
69
+ if (!url.length) {
70
+ if (error) {
71
+ *error = ApexPluginMakeError(ApexPluginErrorFetchFailed, "URL is required");
72
+ }
73
+ return nil;
74
+ }
75
+
76
+ apex_plugin_catalog *catalog = apex_plugin_catalog_fetch_url(url.UTF8String);
77
+ if (!catalog) {
78
+ if (error) {
79
+ *error = ApexPluginMakeError(ApexPluginErrorFetchFailed,
80
+ "failed to fetch plugin directory");
81
+ }
82
+ return nil;
83
+ }
84
+
85
+ NSArray<ApexPluginInfo *> *items = ApexPluginInfosFromCatalog(catalog);
86
+ apex_plugin_catalog_free(catalog);
87
+ return items;
88
+ }
89
+
90
+ + (NSArray<ApexPluginInfo *> *)installedPluginsWithBaseDirectory:(NSString *)baseDirectory {
91
+ apex_plugin_discovery_options opts;
92
+ memset(&opts, 0, sizeof(opts));
93
+ opts.include_project = true;
94
+ opts.include_user_global = true;
95
+ if (baseDirectory.length > 0) {
96
+ opts.base_directory = baseDirectory.UTF8String;
97
+ }
98
+
99
+ apex_plugin_catalog *catalog = apex_plugins_list_installed(&opts);
100
+ if (!catalog) return @[];
101
+
102
+ NSArray<ApexPluginInfo *> *items = ApexPluginInfosFromCatalog(catalog);
103
+ apex_plugin_catalog_free(catalog);
104
+ return items;
105
+ }
106
+
107
+ + (BOOL)installPluginWithId:(NSString *)pluginId error:(NSError **)error {
108
+ return [self installPluginFromRepository:pluginId allowUntrustedSource:NO error:error];
109
+ }
110
+
111
+ + (BOOL)installPluginFromRepository:(NSString *)idOrRepo
112
+ allowUntrustedSource:(BOOL)allowUntrusted
113
+ error:(NSError **)error {
114
+ if (!idOrRepo.length) {
115
+ if (error) {
116
+ *error = ApexPluginMakeError(ApexPluginErrorInstallFailed, "plugin id is required");
117
+ }
118
+ return NO;
119
+ }
120
+
121
+ char errbuf[512];
122
+ errbuf[0] = '\0';
123
+
124
+ apex_plugin_install_options opts;
125
+ memset(&opts, 0, sizeof(opts));
126
+ opts.allow_untrusted_repo = allowUntrusted;
127
+ opts.run_post_install = true;
128
+
129
+ int rc = apex_plugin_install(idOrRepo.UTF8String, &opts, errbuf, sizeof(errbuf));
130
+ if (rc != 0) {
131
+ if (error) {
132
+ *error = ApexPluginMakeError(ApexPluginErrorInstallFailed,
133
+ errbuf[0] ? errbuf : "install failed");
134
+ }
135
+ return NO;
136
+ }
137
+ return YES;
138
+ }
139
+
140
+ + (BOOL)uninstallPluginWithId:(NSString *)pluginId error:(NSError **)error {
141
+ if (!pluginId.length) {
142
+ if (error) {
143
+ *error = ApexPluginMakeError(ApexPluginErrorUninstallFailed, "plugin id is required");
144
+ }
145
+ return NO;
146
+ }
147
+
148
+ char errbuf[512];
149
+ errbuf[0] = '\0';
150
+ int rc = apex_plugin_uninstall(pluginId.UTF8String, errbuf, sizeof(errbuf));
151
+ if (rc != 0) {
152
+ if (error) {
153
+ *error = ApexPluginMakeError(ApexPluginErrorUninstallFailed,
154
+ errbuf[0] ? errbuf : "uninstall failed");
155
+ }
156
+ return NO;
157
+ }
158
+ return YES;
159
+ }
160
+
161
+ + (nullable NSString *)globalPluginsDirectory {
162
+ char buf[1024];
163
+ if (apex_plugin_global_directory(buf, sizeof(buf)) != 0) {
164
+ return nil;
165
+ }
166
+ return [NSString stringWithUTF8String:buf];
167
+ }
168
+
169
+ @end
@@ -0,0 +1,87 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <meta name="generator" content="Apex 0.1.38">
7
+ <title>Timbuktu</title>
8
+ <style>
9
+ body {
10
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
11
+ line-height: 1.6;
12
+ max-width: 800px;
13
+ margin: 2rem auto;
14
+ padding: 0 1rem;
15
+ color: #333;
16
+ }
17
+ pre { background: #f5f5f5; padding: 1rem; overflow-x: auto; }
18
+ code { background: #f0f0f0; padding: 0.2em 0.4em; border-radius: 3px; }
19
+ blockquote { border-left: 4px solid #ddd; margin: 0; padding-left: 1rem; color: #666; }
20
+ table { border-collapse: collapse; width: 100%%; }
21
+ th, td { border: 1px solid #ddd; padding: 0.5rem; }
22
+ th { background: #f5f5f5; }
23
+ .page-break { page-break-after: always; }
24
+ .callout { padding: 1rem; margin: 1rem 0; border-left: 4px solid; }
25
+ .callout-note { border-color: #3b82f6; background: #eff6ff; }
26
+ .callout-warning { border-color: #f59e0b; background: #fffbeb; }
27
+ .callout-tip { border-color: #10b981; background: #f0fdf4; }
28
+ .callout-danger { border-color: #ef4444; background: #fef2f2; }
29
+ ins { background: #d4fcbc; text-decoration: none; }
30
+ del { background: #fbb6c2; text-decoration: line-through; }
31
+ mark { background: #fff3cd; }
32
+ .critic.comment { background: #e7e7e7; color: #666; font-style: italic; }
33
+ </style>
34
+ </head>
35
+ <body>
36
+
37
+ <h1 id="basic-heading">Basic Heading</h1>
38
+ <h2 id="heading-with-spaces">Heading with Spaces</h2>
39
+ <h3 id="multiple-spaces-here">Multiple Spaces Here</h3>
40
+ <h4 id="heading-with-dash">Heading-with-dash</h4>
41
+ <h5 id="headingwithunderscore">Heading_with_underscore</h5>
42
+ <h6 id="headingwithdots">Heading.with.dots</h6>
43
+ <h1 id="leading-space-test">Leading Space Test</h1>
44
+ <h2 id="trailing-space-test">Trailing Space Test</h2>
45
+ <h3 id="mixed-case-heading">Mixed Case Heading</h3>
46
+ <h4 id="all-caps-heading">ALL CAPS HEADING</h4>
47
+ <h1 id="punctuation-test-here">Punctuation Test, Here!</h1>
48
+ <h2 id="more-punctuation-colons-semicolons">More Punctuation: Colons; Semicolons?</h2>
49
+ <h3 id="special-characters">Special Characters @#$%^&amp;*</h3>
50
+ <h4id="test">Parentheses (and brackets)</h4>
51
+ <h5 id="test">Quotes “double” and ‘single’</h5>
52
+ <h6 id="quotes-adoublea-and-asinglea">Backticks <code>code</code> in heading</h6>
53
+ <h1 id="backticks-code-in-heading">Em Dash — Test</h1>
54
+ <h2 id="em-dash-test">En Dash – Test</h2>
55
+ <h3 id="en-dash-test">Mixed Dashes — and – here</h3>
56
+ <h1 id="mixed-dashes-and-here">Diacritics Émoji Support</h1>
57
+ <h2 id="diacritics-amoji-support">More Diacritics: Café, naïve, résumé</h2>
58
+ <h3 id="more-diacritics-cafa-naave-rasuma">Accented Characters: àáâãäå</h3>
59
+ <h4 id="accented-characters-aaaaaa">Cyrillic: Привет</h4>
60
+ <h5 id="cyrillic-nn">Chinese: 你好</h5>
61
+ <h6 id="chinese-aa">Japanese: こんにちは</h6>
62
+ <h1 id="japanese-aaaaa">Numbers 123 in Heading</h1>
63
+ <h2 id="numbers-123-in-heading">Math Symbols: 2+2=4</h2>
64
+ <h3 id="math-symbols-224">Currency: $100, €50, £25</h3>
65
+ <h1 id="currency-100-a50-a25">Leading Dash -Test</h1>
66
+ <h2 id="leading-dash-test">Trailing Dash Test-</h2>
67
+ <h3 id="trailing-dash-test">Multiple Dashes – Here</h3>
68
+ <h4 id="multiple-dashes-here">Triple Dash — Test</h4>
69
+ <h1 id="triple-dash-test">Empty After Processing</h1>
70
+ <h2 id="empty-after-processing">[!@#<span class="math inline">\(%^&*()](mailto:!@#\)</span>%%5E&amp;*())</h2>
71
+ <h3 id="5e">Only Special Characters</h3>
72
+ <h1 id="only-special-characters">Very Long Heading That Should Still Generate a Valid ID Even When It Contains Many Words and Characters</h1>
73
+ <h2 id="very-long-heading-that-should-still-generate-a-valid-id-even-when-it-contains-many-words-and-characters">Heading with URL: <a href="https://example.com/path">https://example.com/path</a></h2>
74
+ <h3 id="heading-with-url">Email in heading: <a href="mailto:user@example.com">user@example.com</a></h3>
75
+ <h1 id="email-in-heading">Heading with Markdown <em>bold</em> and <em>italic</em></h1>
76
+ <h2 id="heading-with-markdown-and">Heading with <code>code</code> span</h2>
77
+ <h3 id="heading-with-code-span">Heading with <a href="url">link</a></h3>
78
+ <h4 id="heading-with">Heading with <img src="img.png" alt="image" /></h4>
79
+ <h1 id="heading-with">Heading with HTML <tag></h1>
80
+ <h2 id="heading-with-html">Mixed: Heading—with—dashes and spaces</h2>
81
+ <h3id="2024">Complex: Hello, World! (Test)</h3>
82
+ <h4 id="mixed-headingwithdashes-and-spaces">Edge Case: Multiple Spaces Everywhere</h4>
83
+ <h5 id="2024">Another: Test—Multiple—Dashes</h5>
84
+ <h6 id="edge-case-multiple-spaces-everywhere">Final: [Special@#<span class="math inline">\(%Chars](mailto:Special@#\)</span>%Chars)</h6>
85
+
86
+ </body>
87
+ </html>
@@ -0,0 +1,442 @@
1
+ # Plan: `--mode quarto` for Apex
2
+
3
+ ## Goal
4
+
5
+ Add a **Quarto/Pandoc markdown compatibility mode** optimized for HTML rendering — not a Quarto runtime. Users should be able to point Apex at `.qmd`-style documents and get sensible HTML without running the full Quarto toolchain.
6
+
7
+ **In scope:** Pandoc markdown syntax from [Quarto Markdown Basics](https://quarto.org/docs/authoring/markdown-basics.html)
8
+ **Out of scope:** cell execution, cross-refs (`@fig-…`), CSL/bibliography pipeline, multi-format output (PDF/Typst/Word), project/site rendering, conditional content, computed shortcodes
9
+
10
+ ---
11
+
12
+ ## Design principle
13
+
14
+ **Quarto mode = unified + Pandoc preprocessors + Quarto defaults**
15
+
16
+ Rather than a from-scratch mode, inherit `APEX_MODE_UNIFIED` and layer Quarto-specific preprocessing behind a master flag. That keeps maintenance manageable and avoids duplicating unified’s feature matrix.
17
+
18
+ ```c
19
+ typedef enum {
20
+ /* ... existing ... */
21
+ APEX_MODE_UNIFIED = 4,
22
+ APEX_MODE_QUARTO = 5 /* Pandoc/Quarto markdown for HTML */
23
+ } apex_mode_t;
24
+ ```
25
+
26
+ Add a convenience flag:
27
+
28
+ ```c
29
+ bool enable_quarto_extensions; /* Master switch for Pandoc/Quarto preprocessors */
30
+ ```
31
+
32
+ Quarto mode sets this `true`; individual preprocessors can still be toggled via metadata/CLI for testing.
33
+
34
+ ---
35
+
36
+ ## Mode preset: `apex_options_for_mode(APEX_MODE_QUARTO)`
37
+
38
+ Start from unified, then override:
39
+
40
+
41
+ | Option | Quarto value | Rationale |
42
+ | ------------------------------ | ------------ | ------------------------------ |
43
+ | `enable_quarto_extensions` | `true` | Enables new preprocessors |
44
+ | `enable_quarto_callouts` | `true` | Core Quarto authoring |
45
+ | `enable_divs` / `enable_spans` | `true` | Pandoc div/span syntax |
46
+ | `enable_alpha_lists` | `true` | `a.` / `A.` in Quarto examples |
47
+ | `allow_mixed_list_markers` | `true` | Nested list markers |
48
+ | `enable_definition_lists` | `true` | Pandoc definition lists |
49
+ | `enable_footnotes` | `true` | Reference + inline `^[...]` |
50
+ | `enable_math` | `true` | `$...$` / `$$...$$` |
51
+ | `enable_smart_typography` | `true` | `--`, `---` |
52
+ | `enable_task_lists` | `true` | GFM task lists in Quarto |
53
+ | `enable_image_captions` | `true` | Figure captions |
54
+ | `unsafe` | `true` | Raw HTML passthrough |
55
+ | `hardbreaks` | `false` | Pandoc soft breaks, not GFM |
56
+ | `enable_wiki_links` | `false` | Not Quarto |
57
+ | `enable_marked_extensions` | `false` | Marked-specific syntax off |
58
+ | `enable_py_callouts` | `false` | Quarto uses `:::`, not `!!!` |
59
+ | `enable_indices` | `false` | Not Pandoc/Quarto |
60
+ | `id_format` | GFM | Quarto default header IDs |
61
+
62
+
63
+ **Refactor needed:** Many code paths gate on `mode == APEX_MODE_UNIFIED` (fenced divs, spans, autolink, etc.). Introduce helpers:
64
+
65
+ ```c
66
+ static inline bool apex_mode_is_unified_family(apex_mode_t m) {
67
+ return m == APEX_MODE_UNIFIED || m == APEX_MODE_QUARTO;
68
+ }
69
+ ```
70
+
71
+ Replace hard-coded unified checks for divs, spans, fenced-div preprocessing, and similar features.
72
+
73
+ ---
74
+
75
+ ## Preprocessing pipeline
76
+
77
+ New steps run **only when `enable_quarto_extensions`** is true. Proposed order (relative to existing pipeline in `apex.c`):
78
+
79
+ ```
80
+ … existing preprocessors (metadata, includes, tables, def lists) …
81
+
82
+ 1. apex_preprocess_raw_content() # {=format} blocks + inline
83
+ 2. apex_preprocess_code_fence_attrs() # ```{.python filename="..."}
84
+ 3. apex_preprocess_example_lists() # (@) example list markers
85
+ 4. apex_preprocess_line_blocks() # | line blocks
86
+ 5. apex_preprocess_roman_lists() # i) ii) iii)
87
+ 6. apex_preprocess_quarto_shortcodes() # {{< ... >}} (optional/plugin)
88
+ 7. apex_preprocess_quarto_callouts() # existing
89
+ 8. apex_process_fenced_divs() # existing (extend mode check)
90
+ … parse …
91
+ 9. apex_postprocess_fig_alt() # fig-alt → alt attribute
92
+ 10. apex_postprocess_diagrams() # mermaid/graphviz wrappers
93
+ 11. apex_postprocess_semantic_spans() # .smallcaps, .underline, .mark CSS
94
+ ```
95
+
96
+ Raw content and code-fence attribute normalization must run **before** cmark sees fenced code blocks.
97
+
98
+ ---
99
+
100
+ ## Phase 0 — Infrastructure (1–2 days)
101
+
102
+ **Deliverables**
103
+
104
+ - [ ] Add `APEX_MODE_QUARTO` to `apex.h`, Ruby bindings, docs
105
+ - [ ] `apex_options_for_mode(APEX_MODE_QUARTO)` preset
106
+ - [ ] CLI: `--mode quarto`; metadata `mode: quarto`
107
+ - [ ] `apex_mode_is_unified_family()` and update mode gates in `apex.c`, `ial.c`
108
+ - [ ] `tests/fixtures/quarto/` + smoke test: `apex --mode quarto fixtures/quarto/smoke.md`
109
+ - [ ] Wiki page: `Quarto-Mode.md`; update `Modes.md`
110
+
111
+ **Acceptance:** `apex --mode quarto doc.md` runs with quarto callouts enabled and divs/spans working.
112
+
113
+ ---
114
+
115
+ ## Phase 1 — Prerequisite fixes + low-hanging fruit (1–2 days)
116
+
117
+ ### 1a. Fix class-only bracketed spans
118
+
119
+ Current bug: `[text]{.smallcaps}` → `<spanclass="smallcaps">`.
120
+
121
+ **Fix:** In `attributes_to_html()`, ensure a leading space before `class=` when it’s the first attribute and no `id` precedes it (likely in the `first_attr` branch for classes).
122
+
123
+ ### 1b. `fig-alt` mapping
124
+
125
+ Quarto: `![Caption](img.png){fig-alt="Alt text"}`
126
+
127
+ **HTML output:**
128
+
129
+ ```html
130
+ <figure>
131
+ <img src="img.png" alt="Alt text" />
132
+ <figcaption>Caption</figcaption>
133
+ </figure>
134
+ ```
135
+
136
+ Implement in image caption post-processing (`html_renderer.c` or IAL image attrs).
137
+
138
+ ### 1c. Semantic span classes
139
+
140
+ Map Pandoc classes to CSS (inline or class-only):
141
+
142
+
143
+ | Class | HTML/CSS |
144
+ | ------------ | ---------------------------------------- |
145
+ | `.smallcaps` | `font-variant: small-caps` |
146
+ | `.underline` | `text-decoration: underline` |
147
+ | `.mark` | `background-color: yellow` (or `<mark>`) |
148
+
149
+
150
+ Could be a small post-render pass or bundled CSS in `--standalone` when quarto mode is active.
151
+
152
+ **Acceptance:** Quarto “Other Spans” examples render correctly.
153
+
154
+ ---
155
+
156
+ ## Phase 2 — Raw content `{=format}` (2–3 days)
157
+
158
+ **Syntax**
159
+
160
+ Block:
161
+
162
+ ```markdown
163
+ ```{=html}
164
+ <iframe src="..."></iframe>
165
+ ```
166
+ ```
167
+
168
+ Inline:
169
+
170
+ ```markdown
171
+ `<a>html</a>`{=html}
172
+ ```
173
+
174
+ **Behavior (HTML output)**
175
+
176
+
177
+ | Format | Action |
178
+ | ----------------------- | ----------------------------------------------------------------- |
179
+ | `html` | Passthrough raw (requires `unsafe: true`) |
180
+ | `latex`, `typst`, `tex` | Omit or wrap in `<!-- raw format=typst -->…<!-- /raw -->` comment |
181
+ | unknown | Same as non-html: preserve as HTML comment for debugging |
182
+
183
+
184
+ **Implementation:** New `src/extensions/raw_content.c`
185
+
186
+ - Scan fenced blocks; if info string matches `^=\w+$`, extract body and emit raw HTML block (or placeholder replaced post-parse)
187
+ - Scan inline: backtick span immediately followed by `{=format}`
188
+
189
+ **Tests:** `{=html}`, `{=latex}`, `{=typst}`, inline `{=html}`, ensure no processing inside `{=html}` blocks
190
+
191
+ ---
192
+
193
+ ## Phase 3 — List extensions (2–3 days)
194
+
195
+ ### 3a. Example lists `(@)`
196
+
197
+ Pandoc `example_lists` extension — `(@)` is a **list marker**, not a standalone continuation line. See [Pandoc example lists](https://pandoc.org/MANUAL.html#extension-example_lists).
198
+
199
+ ```markdown
200
+ (@) My first example will be numbered (1).
201
+ (@) My second example will be numbered (2).
202
+
203
+ Explanation of examples.
204
+
205
+ (@) My third example will be numbered (3).
206
+ ```
207
+
208
+ Labeled markers `(@good)` are also supported; labels are stripped and items are numbered sequentially.
209
+
210
+ **Implementation:** Preprocessor converts `(@)` / `(@label)` line-start markers to `1.`, `2.`, `3.`, … with document-wide sequential numbering. Interrupted lists rely on cmark's `start` attribute on continued `<ol>` blocks.
211
+
212
+ **Not yet implemented:** repeating a prior example by reusing its label (`(@foo)` twice).
213
+
214
+ ### 3b. Roman numeral markers `i)`, `ii)`, `I)`, etc.
215
+
216
+ Extend the alpha-list preprocessor (`apex.c` ~line 2649) with a third style:
217
+
218
+ ```html
219
+ <ol style="list-style-type: lower-roman">
220
+ ```
221
+
222
+ Detect `i)`, `ii)`, `iii)` (and uppercase) at list-item starts.
223
+
224
+ ### 3c. Line blocks
225
+
226
+ ```markdown
227
+ | Line one
228
+ | preserved spaces
229
+ | Line three
230
+ ```
231
+
232
+ Pandoc line blocks: lines starting with `|` + space, continuation lines indented or also `|`.
233
+
234
+ **Output:** `<div class="line-block">` with `<br />` or nested divs preserving spacing.
235
+
236
+ **Tests:** Quarto’s nested list example (`1. / i) / A.`), line block example, `(@)` continuation
237
+
238
+ ---
239
+
240
+ ## Phase 4 — Code fence attributes (2 days)
241
+
242
+ Quarto/Pandoc extended fences:
243
+
244
+ ```markdown
245
+ ```{.python filename="run.py" linenos=true}
246
+ print("hello")
247
+ ```
248
+ ```
249
+
250
+ **Preprocessor** normalizes to something cmark + Apex highlighting understand:
251
+
252
+ ```markdown
253
+ ```python
254
+ print("hello")
255
+ ```
256
+ ```
257
+
258
+ …plus metadata preserved for HTML output:
259
+
260
+ ```html
261
+ <pre class="python" data-filename="run.py" data-linenos="true">
262
+ ```
263
+
264
+ Or use existing code-line-numbers when `linenos` / `line-numbers` is present.
265
+
266
+ **Parse rules:** Info string `{.class key="val" key2=val2}` — class becomes language if `.python`-style; remaining keys become data attributes on `<pre>`/`<code>`.
267
+
268
+ ---
269
+
270
+ ## Phase 5 — Diagrams (2–3 days)
271
+
272
+ Quarto native diagram fences:
273
+
274
+ ```markdown
275
+ ```{mermaid}
276
+ flowchart LR
277
+ A --> B
278
+ ```
279
+ ```
280
+
281
+ **HTML strategy (pragmatic):**
282
+
283
+ ```html
284
+ <pre class="mermaid">
285
+ flowchart LR
286
+ A --> B
287
+ </pre>
288
+ ```
289
+
290
+ When `--standalone` + quarto mode, auto-suggest/include mermaid.js (you already have `--script mermaid`).
291
+
292
+ Graphviz `{dot}` / `{graphviz}`: similar, or convert to `<img>` if a dot renderer is unavailable (document limitation).
293
+
294
+ **Optional:** `enable_quarto_diagrams` flag; off = plain code block (current behavior).
295
+
296
+ ---
297
+
298
+ ## Phase 6 — Shortcodes (plugin-first, 2–4 days)
299
+
300
+ Quarto shortcodes are a large surface area. Recommend **plugin, not core**:
301
+
302
+
303
+ | Shortcode | Apex approach |
304
+ | ------------------- | --------------------------------------------------- |
305
+ | `{{< kbd >}}` | Extend `apex-plugin-kbd` to accept Quarto syntax |
306
+ | `{{< pagebreak >}}` | Map to existing `{::pagebreak /}` or `<!--BREAK-->` |
307
+ | `{{< video >}}` | New small plugin → `<video>` / embed iframe |
308
+ | `{{< include >}}` | Delegate to existing includes if possible |
309
+
310
+
311
+ **Core shim:** Optional lightweight preprocessor converts `{{< name args >}}` → plugin hook or known expansions; unknown shortcodes pass through with a warning in verbose mode.
312
+
313
+ ---
314
+
315
+ ## Phase 7 — Polish & compatibility toggles (ongoing)
316
+
317
+
318
+ | Feature | Approach |
319
+ | ----------------------------- | -------------------------------------------------------------------------------------------- |
320
+ | `::: {.hidden}` | Add `.hidden { display: none }` in quarto standalone CSS; math macros stay user’s problem |
321
+ | Empty div list reset `::: {}` | Verify list numbering; fix if cmark merges lists incorrectly |
322
+ | Strict blank-line-before-list | Optional `quarto_strict_lists` flag (off by default; Apex stays permissive unless requested) |
323
+ | Cross-ref syntax `@fig-x` | Pass through as text, or wrap in `<span class="quarto-xref">` for future filter |
324
+ | Citations `@key` | Out of scope unless CSL integration is planned |
325
+
326
+
327
+ ---
328
+
329
+ ## File layout
330
+
331
+ ```
332
+ src/extensions/
333
+ raw_content.c / .h
334
+ quarto_lists.c / .h # (@), roman lists, line blocks
335
+ code_fence_attrs.c / .h
336
+ quarto_diagrams.c / .h # optional
337
+ quarto_shortcodes.c / .h # optional thin shim
338
+
339
+ tests/
340
+ fixtures/quarto/
341
+ raw-content.md
342
+ lists.md
343
+ code-attrs.md
344
+ diagrams.md
345
+ callouts.md
346
+ spans.md
347
+ test_quarto_mode.c # or section in test_extensions.c
348
+ ```
349
+
350
+ ---
351
+
352
+ ## CLI & metadata surface
353
+
354
+ ```bash
355
+ apex doc.qmd --mode quarto
356
+ apex doc.qmd --mode quarto --standalone --script mermaid
357
+ ```
358
+
359
+ Metadata (in `.qmd` YAML):
360
+
361
+ ```yaml
362
+ ---
363
+ title: My Doc
364
+ mode: quarto
365
+ quarto-callouts: true
366
+ ---
367
+ ```
368
+
369
+ Individual toggles (for debugging / partial compatibility):
370
+
371
+ ```yaml
372
+ quarto-raw: true
373
+ quarto-list-continuation: true
374
+ quarto-line-blocks: true
375
+ quarto-roman-lists: true
376
+ quarto-code-attrs: true
377
+ quarto-diagrams: true
378
+ quarto-shortcodes: false # plugin handles it
379
+ ```
380
+
381
+ ---
382
+
383
+ ## Testing strategy
384
+
385
+ 1. **Fixture suite** — one `.md` per Quarto basics section; golden HTML snapshots
386
+ 2. **Regression** — existing unified tests must not break when quarto flags are off
387
+ 3. **Round-trip** — optional: quarto fixtures through `--to markdown` (future)
388
+ 4. **Live samples** — copy 2–3 snippets from quarto.org docs into fixtures
389
+
390
+ Example acceptance test for Phase 2:
391
+
392
+ ```bash
393
+ build/apex --mode quarto - <<'EOF'
394
+ ```{=html}
395
+ <strong>raw</strong>
396
+ ```
397
+
398
+ EOF
399
+
400
+ # expect: **raw** not ```
401
+
402
+ ```
403
+
404
+ ```
405
+
406
+ ---
407
+
408
+ ## Suggested release milestones
409
+
410
+ | Version | Phases | User-visible value |
411
+ |---------|--------|-------------------|
412
+ | **v1.2** | 0 + 1 | `--mode quarto` exists; callouts, spans, fig-alt work |
413
+ | **v1.3** | 2 + 3 | Raw `{=typst}`/`{=html}`, `(@)`, roman lists, line blocks |
414
+ | **v1.4** | 4 + 5 | Code fence attrs, mermaid fences |
415
+ | **v1.5** | 6 + 7 | Shortcode plugins, polish |
416
+
417
+ ---
418
+
419
+ ## Risks & decisions
420
+
421
+ 1. **cmark limitations** — Line blocks and list continuation may need AST surgery, not just preprocessing. Budget time for post-parse fixes.
422
+ 2. **Raw `{=typst}` in HTML** — Document clearly: Apex preserves/emits comments; it does not compile Typst. Users targeting Typst still need Quarto/Pandoc.
423
+ 3. **Mode proliferation** — Keep quarto as unified-family; avoid a separate options matrix per feature.
424
+ 4. **Shortcodes vs plugins** — Core should not reimplement Quarto’s shortcode engine; plugins + thin shim is the right boundary.
425
+ 5. **Span bug** — Fix in unified too; not quarto-only.
426
+
427
+ ---
428
+
429
+ ## Recommended first PR (minimal viable quarto mode)
430
+
431
+ Single PR touching:
432
+
433
+ 1. `APEX_MODE_QUARTO` + CLI + metadata
434
+ 2. `apex_mode_is_unified_family()` refactor
435
+ 3. Enable `--quarto-callouts` in quarto preset
436
+ 4. Fix bracketed span `class=` bug
437
+ 5. `fig-alt` mapping
438
+ 6. `tests/fixtures/quarto/smoke.md` + tests
439
+
440
+ That gives a real `--mode quarto` users can try immediately, with raw content and list features as follow-up PRs.
441
+ ```
442
+