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
@@ -8,6 +8,7 @@
8
8
  #include <stdlib.h>
9
9
  #include <ctype.h>
10
10
  #include <strings.h>
11
+ #include <stdio.h>
11
12
 
12
13
  /**
13
14
  * Detect callout type from string (case-insensitive)
@@ -66,7 +67,7 @@ static const char *callout_type_name(callout_type_t type) {
66
67
  * Check if a blockquote is a Bear/Obsidian style callout
67
68
  * Pattern: > [!TYPE] Title or > [!TYPE]+ Title or > [!TYPE]- Title
68
69
  */
69
- static bool is_bear_callout(cmark_node *blockquote, callout_type_t *type,
70
+ static bool is_bear_callout(cmark_node *blockquote, bool enable_py_callouts, callout_type_t *type,
70
71
  char **title, bool *collapsible, bool *default_open) {
71
72
  if (cmark_node_get_type(blockquote) != CMARK_NODE_BLOCK_QUOTE) return false;
72
73
 
@@ -81,6 +82,34 @@ static bool is_bear_callout(cmark_node *blockquote, callout_type_t *type,
81
82
  const char *text = cmark_node_get_literal(text_node);
82
83
  if (!text) return false;
83
84
 
85
+ /* markdown-callouts collapsed syntax in blockquote: ? TYPE: Title */
86
+ if (enable_py_callouts && text[0] == '?' && text[1] == ' ') {
87
+ const char *type_start = text + 2;
88
+ const char *p = type_start;
89
+ while (*p && (isalnum((unsigned char)*p) || *p == '-' || *p == '_')) p++;
90
+ if (*p == ':') {
91
+ int type_len = (int)(p - type_start);
92
+ if (type_len > 0) {
93
+ *type = detect_callout_type(type_start, type_len);
94
+ if (*type != CALLOUT_NONE) {
95
+ *collapsible = true;
96
+ *default_open = false;
97
+ const char *title_start = p + 1;
98
+ while (*title_start == ' ' || *title_start == '\t') title_start++;
99
+ if (*title_start) {
100
+ const char *title_end = strchr(title_start, '\n');
101
+ if (title_end) {
102
+ *title = strndup(title_start, title_end - title_start);
103
+ } else {
104
+ *title = strdup(title_start);
105
+ }
106
+ }
107
+ return true;
108
+ }
109
+ }
110
+ }
111
+ }
112
+
84
113
  /* Check for [!TYPE] pattern */
85
114
  if (text[0] != '[' || text[1] != '!') return false;
86
115
 
@@ -188,7 +217,7 @@ static void convert_blockquote_to_callout(cmark_node *blockquote, callout_type_t
188
217
  /**
189
218
  * Process callouts in AST
190
219
  */
191
- void apex_process_callouts_in_tree(cmark_node *node) {
220
+ void apex_process_callouts_in_tree(cmark_node *node, bool enable_py_callouts) {
192
221
  if (!node) return;
193
222
 
194
223
  /* Check if current node is a blockquote callout */
@@ -197,7 +226,7 @@ void apex_process_callouts_in_tree(cmark_node *node) {
197
226
  char *title = NULL;
198
227
  bool collapsible, default_open;
199
228
 
200
- if (is_bear_callout(node, &type, &title, &collapsible, &default_open)) {
229
+ if (is_bear_callout(node, enable_py_callouts, &type, &title, &collapsible, &default_open)) {
201
230
  convert_blockquote_to_callout(node, type, title, collapsible, default_open);
202
231
  free(title);
203
232
  return; /* Don't recurse into modified node */
@@ -208,8 +237,375 @@ void apex_process_callouts_in_tree(cmark_node *node) {
208
237
  cmark_node *child = cmark_node_first_child(node);
209
238
  while (child) {
210
239
  cmark_node *next = cmark_node_next(child);
211
- apex_process_callouts_in_tree(child);
240
+ apex_process_callouts_in_tree(child, enable_py_callouts);
212
241
  child = next;
213
242
  }
214
243
  }
215
244
 
245
+ static bool append_chunk(char **out, size_t *len, size_t *cap, const char *chunk, size_t chunk_len) {
246
+ if (!out || !len || !cap || !chunk) return false;
247
+ if (*len + chunk_len + 1 > *cap) {
248
+ size_t new_cap = *cap ? *cap : 256;
249
+ while (*len + chunk_len + 1 > new_cap) {
250
+ new_cap *= 2;
251
+ }
252
+ char *new_out = realloc(*out, new_cap);
253
+ if (!new_out) return false;
254
+ *out = new_out;
255
+ *cap = new_cap;
256
+ }
257
+ memcpy(*out + *len, chunk, chunk_len);
258
+ *len += chunk_len;
259
+ (*out)[*len] = '\0';
260
+ return true;
261
+ }
262
+
263
+ static bool append_str(char **out, size_t *len, size_t *cap, const char *str) {
264
+ if (!str) return true;
265
+ return append_chunk(out, len, cap, str, strlen(str));
266
+ }
267
+
268
+ static char *trim_ascii_whitespace(char *s) {
269
+ if (!s) return s;
270
+ while (*s && (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n')) s++;
271
+ char *end = s + strlen(s);
272
+ while (end > s && (end[-1] == ' ' || end[-1] == '\t' || end[-1] == '\r' || end[-1] == '\n')) {
273
+ end--;
274
+ }
275
+ *end = '\0';
276
+ return s;
277
+ }
278
+
279
+ char *apex_preprocess_py_callouts(const char *text) {
280
+ if (!text) return NULL;
281
+
282
+ size_t cap = strlen(text) * 2 + 256;
283
+ char *out = malloc(cap);
284
+ if (!out) return NULL;
285
+ size_t len = 0;
286
+ out[0] = '\0';
287
+
288
+ bool in_fenced_code = false;
289
+ const char *p = text;
290
+ while (*p) {
291
+ const char *line_start = p;
292
+ const char *line_end = strchr(p, '\n');
293
+ bool has_nl = line_end != NULL;
294
+ size_t line_len = has_nl ? (size_t)(line_end - line_start) : strlen(line_start);
295
+
296
+ const char *content = line_start;
297
+ while ((size_t)(content - line_start) < line_len && (*content == ' ' || *content == '\t')) content++;
298
+ size_t content_len = line_len - (size_t)(content - line_start);
299
+
300
+ if (content_len >= 3 && strncmp(content, "```", 3) == 0) {
301
+ in_fenced_code = !in_fenced_code;
302
+ }
303
+
304
+ if (!in_fenced_code) {
305
+ if (content_len > 4 && strncmp(content, ">? ", 3) == 0) {
306
+ const char *spec = content + 3;
307
+ const char *spec_end = content + content_len;
308
+ const char *type_start = spec;
309
+ while (spec < spec_end && (isalnum((unsigned char)*spec) || *spec == '-' || *spec == '_')) spec++;
310
+ if (spec > type_start && spec < spec_end && *spec == ':') {
311
+ int type_len = (int)(spec - type_start);
312
+ callout_type_t ct = detect_callout_type(type_start, type_len);
313
+ if (ct != CALLOUT_NONE) {
314
+ spec++; /* skip : */
315
+ while (spec < spec_end && *spec == ' ') spec++;
316
+
317
+ char header[640];
318
+ if (spec < spec_end) {
319
+ snprintf(header, sizeof(header), "> [!%.*s]- %.*s",
320
+ type_len, type_start, (int)(spec_end - spec), spec);
321
+ } else {
322
+ snprintf(header, sizeof(header), "> [!%.*s]-", type_len, type_start);
323
+ }
324
+ if (!append_str(&out, &len, &cap, header)) goto fail;
325
+ if (has_nl && !append_chunk(&out, &len, &cap, "\n", 1)) goto fail;
326
+ p = has_nl ? line_end + 1 : line_start + line_len;
327
+ continue;
328
+ }
329
+ }
330
+ }
331
+
332
+ const char *spec = content;
333
+ const char *spec_end = content + content_len;
334
+ const char *type_start = spec;
335
+ while (spec < spec_end && (isalnum((unsigned char)*spec) || *spec == '-' || *spec == '_')) spec++;
336
+ if (spec > type_start && spec < spec_end && *spec == ':') {
337
+ int type_len = (int)(spec - type_start);
338
+ callout_type_t ct = detect_callout_type(type_start, type_len);
339
+ if (ct != CALLOUT_NONE) {
340
+ spec++; /* skip : */
341
+ while (spec < spec_end && *spec == ' ') spec++;
342
+
343
+ char header[128];
344
+ snprintf(header, sizeof(header), "> [!%.*s]", type_len, type_start);
345
+ if (!append_str(&out, &len, &cap, header)) goto fail;
346
+ if (!append_chunk(&out, &len, &cap, "\n", 1)) goto fail;
347
+
348
+ if (!append_str(&out, &len, &cap, "> ")) goto fail;
349
+ if (spec < spec_end && !append_chunk(&out, &len, &cap, spec, (size_t)(spec_end - spec))) goto fail;
350
+ if (has_nl && !append_chunk(&out, &len, &cap, "\n", 1)) goto fail;
351
+
352
+ p = has_nl ? line_end + 1 : line_start + line_len;
353
+ if (!append_chunk(&out, &len, &cap, "\n", 1)) goto fail;
354
+ while (*p) {
355
+ const char *body_start = p;
356
+ const char *body_end = strchr(p, '\n');
357
+ bool body_nl = body_end != NULL;
358
+ size_t body_len = body_nl ? (size_t)(body_end - body_start) : strlen(body_start);
359
+ bool blank = true;
360
+ for (size_t i = 0; i < body_len; i++) {
361
+ if (body_start[i] != ' ' && body_start[i] != '\t' && body_start[i] != '\r') {
362
+ blank = false;
363
+ break;
364
+ }
365
+ }
366
+ if (blank) break;
367
+ if (!append_str(&out, &len, &cap, "> ")) goto fail;
368
+ if (!append_chunk(&out, &len, &cap, body_start, body_len)) goto fail;
369
+ if (body_nl && !append_chunk(&out, &len, &cap, "\n", 1)) goto fail;
370
+ p = body_nl ? body_end + 1 : body_start + body_len;
371
+ }
372
+ if (!append_chunk(&out, &len, &cap, "\n", 1)) goto fail;
373
+ continue;
374
+ }
375
+ }
376
+ }
377
+
378
+ if (!in_fenced_code && content_len >= 4 && strncmp(content, "!!! ", 4) == 0) {
379
+ const char *spec = content + 4;
380
+ const char *spec_end = content + content_len;
381
+ while (spec < spec_end && *spec == ' ') spec++;
382
+ const char *type_start = spec;
383
+ while (spec < spec_end && (isalnum((unsigned char)*spec) || *spec == '-' || *spec == '_')) spec++;
384
+ if (spec > type_start) {
385
+ int type_len = (int)(spec - type_start);
386
+ callout_type_t ct = detect_callout_type(type_start, type_len);
387
+ if (ct != CALLOUT_NONE) {
388
+ char title_buf[512];
389
+ title_buf[0] = '\0';
390
+ while (spec < spec_end && *spec == ' ') spec++;
391
+ if (spec < spec_end) {
392
+ if (*spec == '"' || *spec == '\'') {
393
+ char quote = *spec++;
394
+ const char *title_start = spec;
395
+ while (spec < spec_end && *spec != quote) spec++;
396
+ size_t tlen = (size_t)(spec - title_start);
397
+ if (tlen >= sizeof(title_buf)) tlen = sizeof(title_buf) - 1;
398
+ memcpy(title_buf, title_start, tlen);
399
+ title_buf[tlen] = '\0';
400
+ } else {
401
+ size_t tlen = (size_t)(spec_end - spec);
402
+ if (tlen >= sizeof(title_buf)) tlen = sizeof(title_buf) - 1;
403
+ memcpy(title_buf, spec, tlen);
404
+ title_buf[tlen] = '\0';
405
+ trim_ascii_whitespace(title_buf);
406
+ }
407
+ }
408
+
409
+ char header[640];
410
+ if (title_buf[0]) {
411
+ snprintf(header, sizeof(header), "> [!%.*s] %s", type_len, type_start, title_buf);
412
+ } else {
413
+ snprintf(header, sizeof(header), "> [!%.*s]", type_len, type_start);
414
+ }
415
+ if (!append_str(&out, &len, &cap, header)) goto fail;
416
+ if (has_nl && !append_chunk(&out, &len, &cap, "\n", 1)) goto fail;
417
+
418
+ p = has_nl ? line_end + 1 : line_start + line_len;
419
+ while (*p) {
420
+ const char *body_start = p;
421
+ const char *body_end = strchr(p, '\n');
422
+ bool body_nl = body_end != NULL;
423
+ size_t body_len = body_nl ? (size_t)(body_end - body_start) : strlen(body_start);
424
+
425
+ size_t indent = 0;
426
+ while (indent < body_len && body_start[indent] == ' ') indent++;
427
+ bool blank = true;
428
+ for (size_t i = 0; i < body_len; i++) {
429
+ if (body_start[i] != ' ' && body_start[i] != '\t' && body_start[i] != '\r') {
430
+ blank = false;
431
+ break;
432
+ }
433
+ }
434
+
435
+ if (!blank && indent < 4) break;
436
+
437
+ if (!append_str(&out, &len, &cap, ">")) goto fail;
438
+ if (!blank) {
439
+ if (!append_str(&out, &len, &cap, " ")) goto fail;
440
+ size_t strip = indent >= 4 ? 4 : indent;
441
+ if (!append_chunk(&out, &len, &cap, body_start + strip, body_len - strip)) goto fail;
442
+ }
443
+ if (body_nl && !append_chunk(&out, &len, &cap, "\n", 1)) goto fail;
444
+
445
+ p = body_nl ? body_end + 1 : body_start + body_len;
446
+ }
447
+ if (!append_chunk(&out, &len, &cap, "\n", 1)) goto fail;
448
+ continue;
449
+ }
450
+ }
451
+ }
452
+
453
+ if (!append_chunk(&out, &len, &cap, line_start, line_len)) goto fail;
454
+ if (has_nl && !append_chunk(&out, &len, &cap, "\n", 1)) goto fail;
455
+ p = has_nl ? line_end + 1 : line_start + line_len;
456
+ }
457
+
458
+ return out;
459
+
460
+ fail:
461
+ free(out);
462
+ return NULL;
463
+ }
464
+
465
+ char *apex_preprocess_quarto_callouts(const char *text) {
466
+ if (!text) return NULL;
467
+
468
+ size_t cap = strlen(text) * 2 + 256;
469
+ char *out = malloc(cap);
470
+ if (!out) return NULL;
471
+ size_t len = 0;
472
+ out[0] = '\0';
473
+
474
+ bool in_fenced_code = false;
475
+ const char *p = text;
476
+ while (*p) {
477
+ const char *line_start = p;
478
+ const char *line_end = strchr(p, '\n');
479
+ bool has_nl = line_end != NULL;
480
+ size_t line_len = has_nl ? (size_t)(line_end - line_start) : strlen(line_start);
481
+
482
+ const char *content = line_start;
483
+ while ((size_t)(content - line_start) < line_len && (*content == ' ' || *content == '\t')) content++;
484
+ size_t content_len = line_len - (size_t)(content - line_start);
485
+
486
+ if (content_len >= 3 && strncmp(content, "```", 3) == 0) {
487
+ in_fenced_code = !in_fenced_code;
488
+ }
489
+
490
+ if (!in_fenced_code && content_len >= 3 && strncmp(content, ":::", 3) == 0) {
491
+ const char *after = content + 3;
492
+ while (after < content + content_len && *after == ' ') after++;
493
+ if (after < content + content_len && *after == '{') {
494
+ const char *close = content + content_len - 1;
495
+ while (close > after && *close != '}') close--;
496
+ if (*close == '}') {
497
+ size_t attrs_len = (size_t)(close - (after + 1));
498
+ char attrs[768];
499
+ if (attrs_len >= sizeof(attrs)) attrs_len = sizeof(attrs) - 1;
500
+ memcpy(attrs, after + 1, attrs_len);
501
+ attrs[attrs_len] = '\0';
502
+
503
+ char *attrs_work = strdup(attrs);
504
+ if (!attrs_work) goto fail;
505
+ char *token = strtok(attrs_work, " \t");
506
+ char type_buf[64] = {0};
507
+ char title_buf[256] = {0};
508
+ bool collapsible = false;
509
+ bool default_open = true;
510
+ while (token) {
511
+ if (strncmp(token, ".callout-", 9) == 0) {
512
+ strncpy(type_buf, token + 9, sizeof(type_buf) - 1);
513
+ } else if (strncmp(token, "title=", 6) == 0) {
514
+ const char *v = token + 6;
515
+ size_t vlen = strlen(v);
516
+ if (vlen >= 2 && ((v[0] == '"' && v[vlen - 1] == '"') || (v[0] == '\'' && v[vlen - 1] == '\''))) {
517
+ v++;
518
+ vlen -= 2;
519
+ }
520
+ if (vlen >= sizeof(title_buf)) vlen = sizeof(title_buf) - 1;
521
+ memcpy(title_buf, v, vlen);
522
+ title_buf[vlen] = '\0';
523
+ } else if (strncmp(token, "collapse=", 9) == 0) {
524
+ const char *v = token + 9;
525
+ if (strcasecmp(v, "\"true\"") == 0 || strcasecmp(v, "true") == 0) {
526
+ collapsible = true;
527
+ default_open = false;
528
+ } else if (strcasecmp(v, "\"false\"") == 0 || strcasecmp(v, "false") == 0) {
529
+ collapsible = true;
530
+ default_open = true;
531
+ }
532
+ }
533
+ token = strtok(NULL, " \t");
534
+ }
535
+ free(attrs_work);
536
+
537
+ if (type_buf[0]) {
538
+ callout_type_t ct = detect_callout_type(type_buf, (int)strlen(type_buf));
539
+ if (ct != CALLOUT_NONE) {
540
+ char header[512];
541
+ char marker = collapsible ? (default_open ? '+' : '-') : ']';
542
+ if (marker == ']') {
543
+ if (title_buf[0]) snprintf(header, sizeof(header), "> [!%s] %s", type_buf, title_buf);
544
+ else snprintf(header, sizeof(header), "> [!%s]", type_buf);
545
+ } else {
546
+ if (title_buf[0]) snprintf(header, sizeof(header), "> [!%s]%c %s", type_buf, marker, title_buf);
547
+ else snprintf(header, sizeof(header), "> [!%s]%c", type_buf, marker);
548
+ }
549
+ if (!append_str(&out, &len, &cap, header)) goto fail;
550
+ if (has_nl && !append_chunk(&out, &len, &cap, "\n", 1)) goto fail;
551
+
552
+ p = has_nl ? line_end + 1 : line_start + line_len;
553
+ bool had_title_heading = false;
554
+ bool emitted_body_content = false;
555
+ while (*p) {
556
+ const char *body_start = p;
557
+ const char *body_end = strchr(p, '\n');
558
+ bool body_nl = body_end != NULL;
559
+ size_t body_len = body_nl ? (size_t)(body_end - body_start) : strlen(body_start);
560
+
561
+ const char *body_content = body_start;
562
+ while ((size_t)(body_content - body_start) < body_len && (*body_content == ' ' || *body_content == '\t')) body_content++;
563
+ size_t body_content_len = body_len - (size_t)(body_content - body_start);
564
+
565
+ if (body_content_len >= 3 && strncmp(body_content, ":::", 3) == 0) {
566
+ p = body_nl ? body_end + 1 : body_start + body_len;
567
+ break;
568
+ }
569
+
570
+ if (!title_buf[0] && !had_title_heading && body_content_len > 2 &&
571
+ body_content[0] == '#' && body_content[1] == '#') {
572
+ had_title_heading = true;
573
+ if (body_nl) p = body_end + 1;
574
+ else p = body_start + body_len;
575
+ continue;
576
+ }
577
+
578
+ /* Avoid emitting an empty leading paragraph after heading/title removal. */
579
+ if (body_len == 0 && !emitted_body_content) {
580
+ p = body_nl ? body_end + 1 : body_start + body_len;
581
+ continue;
582
+ }
583
+
584
+ if (!append_str(&out, &len, &cap, ">")) goto fail;
585
+ if (body_len > 0) {
586
+ if (!append_str(&out, &len, &cap, " ")) goto fail;
587
+ if (!append_chunk(&out, &len, &cap, body_start, body_len)) goto fail;
588
+ emitted_body_content = true;
589
+ }
590
+ if (body_nl && !append_chunk(&out, &len, &cap, "\n", 1)) goto fail;
591
+ p = body_nl ? body_end + 1 : body_start + body_len;
592
+ }
593
+ continue;
594
+ }
595
+ }
596
+ }
597
+ }
598
+ }
599
+
600
+ if (!append_chunk(&out, &len, &cap, line_start, line_len)) goto fail;
601
+ if (has_nl && !append_chunk(&out, &len, &cap, "\n", 1)) goto fail;
602
+ p = has_nl ? line_end + 1 : line_start + line_len;
603
+ }
604
+
605
+ return out;
606
+
607
+ fail:
608
+ free(out);
609
+ return NULL;
610
+ }
611
+
@@ -1,12 +1,9 @@
1
1
  /**
2
2
  * Callouts Extension for Apex
3
3
  *
4
- * Supports Bear/Obsidian and Xcode Playground callout syntax:
4
+ * Supports Bear/Obsidian callout syntax:
5
5
  * > [!NOTE] Title
6
6
  * > Content
7
- *
8
- * - Attention: Title
9
- * Content
10
7
  */
11
8
 
12
9
  #ifndef APEX_CALLOUTS_H
@@ -43,7 +40,20 @@ typedef enum {
43
40
  /**
44
41
  * Process callouts in AST (postprocessing)
45
42
  */
46
- void apex_process_callouts_in_tree(cmark_node *document);
43
+ void apex_process_callouts_in_tree(cmark_node *document, bool enable_py_callouts);
44
+
45
+ /**
46
+ * Preprocess Python-Markdown callouts into Obsidian-style blockquotes.
47
+ * Pattern: !!! type "Optional title" followed by indented content.
48
+ * Returns newly allocated string or NULL on allocation failure.
49
+ */
50
+ char *apex_preprocess_py_callouts(const char *text);
51
+
52
+ /**
53
+ * Preprocess Quarto ::: {.callout-*} callouts into Obsidian-style blockquotes.
54
+ * Returns newly allocated string or NULL on allocation failure.
55
+ */
56
+ char *apex_preprocess_quarto_callouts(const char *text);
47
57
 
48
58
  #ifdef __cplusplus
49
59
  }
@@ -107,12 +107,29 @@ static bool is_mmark_pattern(const char *text, int pos, int len) {
107
107
  return false;
108
108
  }
109
109
 
110
+ /**
111
+ * Quarto cross-ref keys (fig-/sec-/tbl-/eq-) use bare @ syntax and must not be
112
+ * parsed as author-in-text citations when quarto-xrefs is enabled.
113
+ */
114
+ static bool apex_citation_key_is_quarto_xref(const char *key) {
115
+ if (!key) {
116
+ return false;
117
+ }
118
+ static const char *prefixes[] = { "fig-", "sec-", "tbl-", "eq-", NULL };
119
+ for (int i = 0; prefixes[i]; i++) {
120
+ size_t n = strlen(prefixes[i]);
121
+ if (strncmp(key, prefixes[i], n) == 0) {
122
+ return true;
123
+ }
124
+ }
125
+ return false;
126
+ }
127
+
110
128
  /**
111
129
  * Parse Pandoc citation: [@key] or @key or [see @key, pp. 33-35]
112
130
  */
113
131
  static int parse_pandoc_citation(const char *text, int pos, int len,
114
132
  apex_citation **citation_out, const apex_options *options) {
115
- (void)options; /* Unused for now */
116
133
  if (pos >= len) return 0;
117
134
 
118
135
  const char *p = text + pos;
@@ -128,6 +145,10 @@ static int parse_pandoc_citation(const char *text, int pos, int len,
128
145
  p++;
129
146
  int key_len = extract_citation_key(text, apex_ptrdiff_to_int(p - text), len, &key);
130
147
  if (key_len > 0 && key) {
148
+ if (options && options->enable_quarto_xrefs && apex_citation_key_is_quarto_xref(key)) {
149
+ free(key);
150
+ return 0;
151
+ }
131
152
  /* Check if there's a locator after: @key [p. 33] */
132
153
  const char *after_key = p + key_len;
133
154
  while (after_key < text + len && isspace(*after_key)) after_key++;
@@ -418,7 +439,7 @@ char *apex_process_citations(const char *text, apex_citation_registry *registry,
418
439
 
419
440
  /* Citations only enabled in certain modes */
420
441
  if (options->mode != APEX_MODE_MULTIMARKDOWN &&
421
- options->mode != APEX_MODE_UNIFIED) {
442
+ !apex_mode_is_unified_family(options->mode)) {
422
443
  return NULL; /* Citations not enabled in this mode */
423
444
  }
424
445
 
@@ -487,17 +508,17 @@ char *apex_process_citations(const char *text, apex_citation_registry *registry,
487
508
  int consumed = 0;
488
509
 
489
510
  /* Try mmark first (most specific pattern) */
490
- if (options->mode == APEX_MODE_UNIFIED) {
511
+ if (apex_mode_is_unified_family(options->mode)) {
491
512
  consumed = parse_mmark_citation(text, pos, apex_size_to_int(len), &citation, options);
492
513
  }
493
514
 
494
515
  /* Try MultiMarkdown */
495
- if (!citation && (options->mode == APEX_MODE_MULTIMARKDOWN || options->mode == APEX_MODE_UNIFIED)) {
516
+ if (!citation && (options->mode == APEX_MODE_MULTIMARKDOWN || apex_mode_is_unified_family(options->mode))) {
496
517
  consumed = parse_mmd_citation(text, pos, apex_size_to_int(len), &citation, options);
497
518
  }
498
519
 
499
520
  /* Try Pandoc (most common) */
500
- if (!citation && (options->mode == APEX_MODE_UNIFIED)) {
521
+ if (!citation && apex_mode_is_unified_family(options->mode)) {
501
522
  consumed = parse_pandoc_citation(text, pos, apex_size_to_int(len), &citation, options);
502
523
  }
503
524