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,79 @@
1
+ /**
2
+ * ApexPlugins.swift
3
+ * Swift API for plugin catalog and installation.
4
+ */
5
+
6
+ import Foundation
7
+ @_exported import ApexObjC
8
+
9
+ /// Metadata for one Apex plugin (available or installed).
10
+ public struct ApexPluginInfo: Identifiable, Hashable, Sendable {
11
+ public let id: String
12
+ public let title: String?
13
+ public let description: String?
14
+ public let author: String?
15
+ public let homepage: String?
16
+ public let repo: String?
17
+
18
+ init(_ objc: ApexObjC.ApexPluginInfo) {
19
+ self.id = objc.pluginId
20
+ self.title = objc.title
21
+ self.description = objc.pluginDescription
22
+ self.author = objc.author
23
+ self.homepage = objc.homepage
24
+ self.repo = objc.repo
25
+ }
26
+ }
27
+
28
+ /// Fetch, list, install, and uninstall Apex plugins from app code.
29
+ public enum ApexPluginManager {
30
+ /// Default `apex-plugins.json` URL.
31
+ public static var defaultDirectoryURL: String {
32
+ ApexPluginCatalog.defaultDirectoryURL
33
+ }
34
+
35
+ /// User-global plugins directory (`~/.config/apex/plugins` or XDG equivalent).
36
+ public static var globalPluginsDirectory: String? {
37
+ ApexPluginCatalog.globalPluginsDirectory()
38
+ }
39
+
40
+ /// Fetch plugins from the central Apex directory.
41
+ public static func fetchAvailable() throws -> [ApexPluginInfo] {
42
+ let items = try ApexPluginCatalog.fetchAvailablePlugins()
43
+ return items.map(ApexPluginInfo.init)
44
+ }
45
+
46
+ /// Fetch plugins from a custom directory JSON URL.
47
+ public static func fetchAvailable(from url: String) throws -> [ApexPluginInfo] {
48
+ let items = try ApexPluginCatalog.fetchAvailablePlugins(fromURL: url)
49
+ return items.map(ApexPluginInfo.init)
50
+ }
51
+
52
+ /// List installed plugins (project-scoped + user-global).
53
+ public static func installed(baseDirectory: String? = nil) -> [ApexPluginInfo] {
54
+ ApexPluginCatalog.installedPlugins(withBaseDirectory: baseDirectory)
55
+ .map(ApexPluginInfo.init)
56
+ }
57
+
58
+ /// Install a curated plugin by id from the central directory.
59
+ public static func install(id: String) throws {
60
+ try install(idOrRepository: id, allowUntrustedSource: false)
61
+ }
62
+
63
+ /// Install by directory id, Git URL, or GitHub `user/repo` shorthand.
64
+ public static func install(idOrRepository: String, allowUntrustedSource: Bool = false) throws {
65
+ if allowUntrustedSource {
66
+ try ApexPluginCatalog.installPlugin(
67
+ fromRepository: idOrRepository,
68
+ allowUntrustedSource: true
69
+ )
70
+ } else {
71
+ try ApexPluginCatalog.installPlugin(withId: idOrRepository)
72
+ }
73
+ }
74
+
75
+ /// Remove a plugin from the user-global plugins directory.
76
+ public static func uninstall(id: String) throws {
77
+ try ApexPluginCatalog.uninstallPlugin(withId: id)
78
+ }
79
+ }
@@ -16,6 +16,7 @@ extern NSString * const ApexModeGFM;
16
16
  extern NSString * const ApexModeMultiMarkdown;
17
17
  extern NSString * const ApexModeKramdown;
18
18
  extern NSString * const ApexModeUnified;
19
+ extern NSString * const ApexModeQuarto;
19
20
 
20
21
  @interface NSString (Apex)
21
22
 
@@ -40,7 +41,7 @@ extern NSString * const ApexModeUnified;
40
41
  /**
41
42
  * Convert Markdown to HTML using Apex with specific processor mode
42
43
  * @param inputString The markdown text to convert
43
- * @param mode Processor mode: Use ApexMode* constants (ApexModeCommonmark, ApexModeGFM, ApexModeMultiMarkdown, ApexModeKramdown, or ApexModeUnified) or string values: "commonmark", "gfm", "multimarkdown", "kramdown", or "unified"
44
+ * @param mode Processor mode: Use ApexMode* constants (ApexModeCommonmark, ApexModeGFM, ApexModeMultiMarkdown, ApexModeKramdown, ApexModeUnified, or ApexModeQuarto) or string values: "commonmark", "gfm", "multimarkdown", "kramdown", "unified", or "quarto"
44
45
  * @return HTML string
45
46
  */
46
47
  + (NSString *)convertWithApex:(NSString *)inputString mode:(NSString *)mode;
@@ -86,7 +87,10 @@ extern NSString * const ApexModeUnified;
86
87
  * - @"headerAnchors": NSNumber (BOOL) - Generate anchor tags instead of IDs
87
88
  * - @"obfuscateEmails": NSNumber (BOOL) - Obfuscate email links
88
89
  * - @"embedImages": NSNumber (BOOL) - Embed images as base64 data URLs
89
- * @return HTML string
90
+ * - @"enablePlugins": NSNumber (BOOL) - Enable external plugin processing
91
+ * - @"enableAutolink": NSNumber (BOOL) - Autolink bare URLs and emails
92
+ * - @"outputFormat": NSString - @"html" (default) or @"rtf"
93
+ * @return HTML or RTF string depending on outputFormat
90
94
  */
91
95
  + (NSString *)convertWithApex:(NSString *)inputString
92
96
  mode:(NSString *)mode
@@ -104,9 +108,30 @@ extern NSString * const ApexModeUnified;
104
108
  */
105
109
  + (NSString *)convertWithApex:(NSString *)inputString
106
110
  mode:(NSString *)mode
107
- generateHeaderIDs:(BOOL)generateHeaderIDs
108
- hardBreaks:(BOOL)hardBreaks
109
- pretty:(BOOL)pretty;
111
+ generateHeaderIDs:(BOOL)generateHeaderIDs
112
+ hardBreaks:(BOOL)hardBreaks
113
+ pretty:(BOOL)pretty;
114
+
115
+ /**
116
+ * Convert Markdown to HTML using Apex with source file URL for includes.
117
+ * Does not embed local images (callers may embed after security-scoped access).
118
+ * @param inputString The markdown text to convert
119
+ * @param mode Processor mode
120
+ * @param sourceURL File URL used for include resolution and base directory
121
+ * @return HTML string
122
+ */
123
+ + (NSString *)convertWithApex:(NSString *)inputString
124
+ mode:(NSString *)mode
125
+ sourceURL:(NSURL *)sourceURL;
126
+
127
+ /**
128
+ * Same as convertWithApex:mode:sourceURL: with explicit autolink control.
129
+ * Disabling autolink avoids a costly scan on large URL-heavy documents.
130
+ */
131
+ + (NSString *)convertWithApex:(NSString *)inputString
132
+ mode:(NSString *)mode
133
+ sourceURL:(NSURL *)sourceURL
134
+ enableAutolink:(BOOL)enableAutolink;
110
135
 
111
136
  /**
112
137
  * Convert this string (as Markdown) to HTML using Apex in unified mode
@@ -123,6 +148,59 @@ extern NSString * const ApexModeUnified;
123
148
  */
124
149
  - (NSString *)apexHTMLWithMode:(NSString *)mode;
125
150
 
151
+ /**
152
+ * Convert this string (as Markdown) to RTF using Apex in unified mode.
153
+ */
154
+ - (NSString *)apexRTF;
155
+
156
+ /**
157
+ * Convert this string (as Markdown) to RTF using Apex with specific mode.
158
+ */
159
+ - (NSString *)apexRTFWithMode:(NSString *)mode;
160
+
161
+ /**
162
+ * Convert this string (as Markdown) to RTF with mode and options dictionary.
163
+ * Sets outputFormat to rtf automatically.
164
+ */
165
+ - (NSString *)apexRTFWithMode:(NSString *)mode
166
+ options:(NSDictionary<NSString *, id> * _Nullable)options;
167
+
168
+ #if __has_include(<AppKit/AppKit.h>) || __has_include(<UIKit/UIKit.h>)
169
+ /**
170
+ * Build an NSAttributedString from Markdown via Apex RTF output (preferred).
171
+ */
172
+ - (NSAttributedString * _Nullable)apexAttributedString;
173
+
174
+ /**
175
+ * Build an NSAttributedString from Markdown using RTF or HTML document type.
176
+ * @param useHTML If YES, use NSHTMLTextDocumentType; if NO, use NSRTFTextDocumentType.
177
+ */
178
+ - (NSAttributedString * _Nullable)apexAttributedStringUsingHTML:(BOOL)useHTML;
179
+
180
+ /**
181
+ * Build an NSAttributedString with mode/options, preferring RTF unless useHTML is YES.
182
+ */
183
+ - (NSAttributedString * _Nullable)apexAttributedStringWithMode:(NSString *)mode
184
+ options:(NSDictionary<NSString *, id> * _Nullable)options
185
+ usingHTML:(BOOL)useHTML;
186
+ #endif
187
+
188
+ /**
189
+ * Extract a flat table of contents for outline / table-view UIs.
190
+ * Each dictionary has keys: @"level" (NSNumber 1-6), @"text", @"id".
191
+ * Nesting for collapsible sections is derived from level (same rules as -t toc).
192
+ */
193
+ + (NSArray<NSDictionary<NSString *, id> *> *)tableOfContentsWithApex:(NSString *)inputString;
194
+
195
+ + (NSArray<NSDictionary<NSString *, id> *> *)tableOfContentsWithApex:(NSString *)inputString
196
+ mode:(NSString *)mode
197
+ options:(NSDictionary<NSString *, id> * _Nullable)options;
198
+
199
+ - (NSArray<NSDictionary<NSString *, id> *> *)apexTableOfContents;
200
+
201
+ - (NSArray<NSDictionary<NSString *, id> *> *)apexTableOfContentsWithMode:(NSString *)mode
202
+ options:(NSDictionary<NSString *, id> * _Nullable)options;
203
+
126
204
  @end
127
205
 
128
206
  NS_ASSUME_NONNULL_END
@@ -6,6 +6,12 @@
6
6
  #import "NSString+Apex.h"
7
7
  #import <apex/apex.h>
8
8
 
9
+ #if __has_include(<AppKit/AppKit.h>)
10
+ #import <AppKit/AppKit.h>
11
+ #elif __has_include(<UIKit/UIKit.h>)
12
+ #import <UIKit/UIKit.h>
13
+ #endif
14
+
9
15
  /**
10
16
  * Apex mode constants
11
17
  */
@@ -14,6 +20,7 @@ NSString *const ApexModeGFM = @"gfm";
14
20
  NSString *const ApexModeMultiMarkdown = @"multimarkdown";
15
21
  NSString *const ApexModeKramdown = @"kramdown";
16
22
  NSString *const ApexModeUnified = @"unified";
23
+ NSString *const ApexModeQuarto = @"quarto";
17
24
 
18
25
  @implementation NSString (Apex)
19
26
 
@@ -41,6 +48,8 @@ NSString *const ApexModeUnified = @"unified";
41
48
  return APEX_MODE_MULTIMARKDOWN;
42
49
  } else if ([mode isEqualToString:@"kramdown"]) {
43
50
  return APEX_MODE_KRAMDOWN;
51
+ } else if ([mode isEqualToString:@"quarto"]) {
52
+ return APEX_MODE_QUARTO;
44
53
  } else {
45
54
  return APEX_MODE_UNIFIED; /* Default to unified */
46
55
  }
@@ -267,9 +276,32 @@ NSString *const ApexModeUnified = @"unified";
267
276
  if (embedImagesValue && [embedImagesValue isKindOfClass:[NSNumber class]]) {
268
277
  options.embed_images = [embedImagesValue boolValue];
269
278
  }
279
+
280
+ /* Plugins */
281
+ id enablePluginsValue = optionsDict[@"enablePlugins"];
282
+ if (enablePluginsValue && [enablePluginsValue isKindOfClass:[NSNumber class]]) {
283
+ options.enable_plugins = [enablePluginsValue boolValue];
284
+ }
285
+
286
+ /* Autolink bare URLs / emails */
287
+ id enableAutolinkValue = optionsDict[@"enableAutolink"];
288
+ if (enableAutolinkValue && [enableAutolinkValue isKindOfClass:[NSNumber class]]) {
289
+ options.enable_autolink = [enableAutolinkValue boolValue];
290
+ }
291
+
292
+ /* Output format: html (default) or rtf */
293
+ id outputFormatValue = optionsDict[@"outputFormat"];
294
+ if (outputFormatValue && [outputFormatValue isKindOfClass:[NSString class]]) {
295
+ NSString *fmt = [(NSString *)outputFormatValue lowercaseString];
296
+ if ([fmt isEqualToString:@"rtf"]) {
297
+ options.output_format = APEX_OUTPUT_RTF;
298
+ } else {
299
+ options.output_format = APEX_OUTPUT_HTML;
300
+ }
301
+ }
270
302
  }
271
303
 
272
- /* Convert to HTML */
304
+ /* Convert to HTML or RTF */
273
305
  char *html_c = apex_markdown_to_html(markdown, strlen(markdown), &options);
274
306
 
275
307
  if (!html_c) {
@@ -324,6 +356,69 @@ NSString *const ApexModeUnified = @"unified";
324
356
  return html ? html : @"";
325
357
  }
326
358
 
359
+ /**
360
+ * Convert Markdown to HTML using Apex with source file URL for includes.
361
+ * Leaves local images as paths; sandboxed callers (e.g. Marked Quick Look)
362
+ * embed after security-scoped folder grants.
363
+ */
364
+ + (NSString *)convertWithApex:(NSString *)inputString
365
+ mode:(NSString *)modeString
366
+ sourceURL:(NSURL *)sourceURL {
367
+ return [self convertWithApex:inputString
368
+ mode:modeString
369
+ sourceURL:sourceURL
370
+ enableAutolink:YES];
371
+ }
372
+
373
+ + (NSString *)convertWithApex:(NSString *)inputString
374
+ mode:(NSString *)modeString
375
+ sourceURL:(NSURL *)sourceURL
376
+ enableAutolink:(BOOL)enableAutolink {
377
+ if (!inputString || [inputString length] == 0) {
378
+ return @"";
379
+ }
380
+
381
+ const char *markdown = [inputString UTF8String];
382
+ if (!markdown) {
383
+ return @"";
384
+ }
385
+
386
+ apex_mode_t mode = [self apexModeFromString:modeString];
387
+ apex_options options = apex_options_for_mode(mode);
388
+
389
+ options.unsafe = true;
390
+ options.generate_header_ids = true;
391
+ options.enable_critic_markup = true;
392
+ options.critic_mode = 2; /* CRITIC_MARKUP: show markup with classes */
393
+ /* Do not embed images here: fopen/stat under Dropbox/CloudStorage can stall
394
+ * sandboxed Quick Look. Callers (Marked Quick Look) embed after security-scoped
395
+ * folder grants with a non-blocking Swift pass. */
396
+ options.embed_images = false;
397
+ options.enable_autolink = enableAutolink;
398
+
399
+ if (sourceURL && sourceURL.isFileURL) {
400
+ NSString *path = sourceURL.path;
401
+ if (path.length > 0) {
402
+ options.input_file_path = [path UTF8String];
403
+ NSString *baseDir = [path stringByDeletingLastPathComponent];
404
+ if (baseDir.length > 0) {
405
+ options.base_directory = [baseDir UTF8String];
406
+ }
407
+ }
408
+ }
409
+
410
+ char *html_c = apex_markdown_to_html(markdown, strlen(markdown), &options);
411
+
412
+ if (!html_c) {
413
+ return @"";
414
+ }
415
+
416
+ NSString *html = [NSString stringWithUTF8String:html_c];
417
+ apex_free_string(html_c);
418
+
419
+ return html ? html : @"";
420
+ }
421
+
327
422
  /**
328
423
  * Convert this string (as Markdown) to HTML using Apex in unified mode
329
424
  */
@@ -338,4 +433,152 @@ NSString *const ApexModeUnified = @"unified";
338
433
  return [NSString convertWithApex:self mode:mode];
339
434
  }
340
435
 
436
+ - (NSString *)apexRTF {
437
+ return [self apexRTFWithMode:ApexModeUnified options:nil];
438
+ }
439
+
440
+ - (NSString *)apexRTFWithMode:(NSString *)mode {
441
+ return [self apexRTFWithMode:mode options:nil];
442
+ }
443
+
444
+ - (NSString *)apexRTFWithMode:(NSString *)mode
445
+ options:(NSDictionary<NSString *, id> *_Nullable)options {
446
+ NSMutableDictionary *dict =
447
+ options ? [options mutableCopy] : [NSMutableDictionary dictionary];
448
+ dict[@"outputFormat"] = @"rtf";
449
+ return [NSString convertWithApex:self mode:mode options:dict];
450
+ }
451
+
452
+ #if __has_include(<AppKit/AppKit.h>) || __has_include(<UIKit/UIKit.h>)
453
+ - (NSAttributedString *)apexAttributedString {
454
+ return [self apexAttributedStringWithMode:ApexModeUnified options:nil usingHTML:NO];
455
+ }
456
+
457
+ - (NSAttributedString *)apexAttributedStringUsingHTML:(BOOL)useHTML {
458
+ return [self apexAttributedStringWithMode:ApexModeUnified options:nil usingHTML:useHTML];
459
+ }
460
+
461
+ - (NSAttributedString *)apexAttributedStringWithMode:(NSString *)mode
462
+ options:(NSDictionary<NSString *, id> *_Nullable)options
463
+ usingHTML:(BOOL)useHTML {
464
+ NSString *source = nil;
465
+ NSAttributedStringDocumentType docType;
466
+ if (useHTML) {
467
+ source = [NSString convertWithApex:self mode:mode options:options];
468
+ docType = NSHTMLTextDocumentType;
469
+ } else {
470
+ source = [self apexRTFWithMode:mode options:options];
471
+ docType = NSRTFTextDocumentType;
472
+ }
473
+ if (!source || [source length] == 0) {
474
+ return [[NSAttributedString alloc] initWithString:@""];
475
+ }
476
+ NSData *data = [source dataUsingEncoding:NSUTF8StringEncoding];
477
+ if (!data) {
478
+ return nil;
479
+ }
480
+ NSDictionary *attrs = @{
481
+ NSDocumentTypeDocumentAttribute : docType,
482
+ NSCharacterEncodingDocumentAttribute : @(NSUTF8StringEncoding)
483
+ };
484
+ NSError *error = nil;
485
+ NSAttributedString *attr =
486
+ [[NSAttributedString alloc] initWithData:data options:attrs documentAttributes:nil error:&error];
487
+ (void)error;
488
+ return attr;
489
+ }
490
+ #endif
491
+
492
+ /**
493
+ * Extract a flat table of contents as dictionaries suitable for Swift OutlineGroup.
494
+ * Each entry: @{ @"level": NSNumber, @"text": NSString, @"id": NSString }
495
+ * Optional options keys: @"idFormat" (@"gfm"/@"mmd"/@"kramdown"),
496
+ * @"tocMinMax" (@"2,4" or @[@2, @4]).
497
+ */
498
+ + (NSArray<NSDictionary<NSString *, id> *> *)tableOfContentsWithApex:(NSString *)inputString
499
+ mode:(NSString *)modeString
500
+ options:(NSDictionary<NSString *, id> *_Nullable)optionsDict {
501
+ if (!inputString || [inputString length] == 0) {
502
+ return @[];
503
+ }
504
+
505
+ const char *markdown = [inputString UTF8String];
506
+ if (!markdown) {
507
+ return @[];
508
+ }
509
+
510
+ apex_mode_t mode = [self apexModeFromString:modeString ?: ApexModeUnified];
511
+ apex_options options = apex_options_for_mode(mode);
512
+
513
+ if (optionsDict) {
514
+ id idFormatVal = optionsDict[@"idFormat"];
515
+ if ([idFormatVal isKindOfClass:[NSString class]]) {
516
+ NSString *fmt = [(NSString *)idFormatVal lowercaseString];
517
+ if ([fmt isEqualToString:@"mmd"]) {
518
+ options.id_format = 1;
519
+ } else if ([fmt isEqualToString:@"kramdown"]) {
520
+ options.id_format = 2;
521
+ } else {
522
+ options.id_format = 0;
523
+ }
524
+ }
525
+ id tocMinMaxVal = optionsDict[@"tocMinMax"];
526
+ if ([tocMinMaxVal isKindOfClass:[NSString class]]) {
527
+ int min = 0, max = 0;
528
+ if (sscanf([(NSString *)tocMinMaxVal UTF8String], "%d,%d", &min, &max) == 2 &&
529
+ min >= 1 && max <= 6 && min <= max) {
530
+ options.toc_min = min;
531
+ options.toc_max = max;
532
+ }
533
+ } else if ([tocMinMaxVal isKindOfClass:[NSArray class]] &&
534
+ [(NSArray *)tocMinMaxVal count] >= 2) {
535
+ int min = [[(NSArray *)tocMinMaxVal objectAtIndex:0] intValue];
536
+ int max = [[(NSArray *)tocMinMaxVal objectAtIndex:1] intValue];
537
+ if (min >= 1 && max <= 6 && min <= max) {
538
+ options.toc_min = min;
539
+ options.toc_max = max;
540
+ }
541
+ }
542
+ }
543
+
544
+ size_t count = 0;
545
+ apex_toc_entry *entries =
546
+ apex_markdown_to_toc_entries(markdown, strlen(markdown), &options, &count);
547
+ if (!entries || count == 0) {
548
+ apex_toc_entries_free(entries, count);
549
+ return @[];
550
+ }
551
+
552
+ NSMutableArray<NSDictionary<NSString *, id> *> *result =
553
+ [NSMutableArray arrayWithCapacity:count];
554
+ for (size_t i = 0; i < count; i++) {
555
+ NSString *text = entries[i].text
556
+ ? [NSString stringWithUTF8String:entries[i].text]
557
+ : @"";
558
+ NSString *ident = entries[i].id
559
+ ? [NSString stringWithUTF8String:entries[i].id]
560
+ : @"";
561
+ [result addObject:@{
562
+ @"level" : @(entries[i].level),
563
+ @"text" : text ?: @"",
564
+ @"id" : ident ?: @""
565
+ }];
566
+ }
567
+ apex_toc_entries_free(entries, count);
568
+ return result;
569
+ }
570
+
571
+ + (NSArray<NSDictionary<NSString *, id> *> *)tableOfContentsWithApex:(NSString *)inputString {
572
+ return [self tableOfContentsWithApex:inputString mode:ApexModeUnified options:nil];
573
+ }
574
+
575
+ - (NSArray<NSDictionary<NSString *, id> *> *)apexTableOfContents {
576
+ return [NSString tableOfContentsWithApex:self];
577
+ }
578
+
579
+ - (NSArray<NSDictionary<NSString *, id> *> *)apexTableOfContentsWithMode:(NSString *)mode
580
+ options:(NSDictionary<NSString *, id> *_Nullable)options {
581
+ return [NSString tableOfContentsWithApex:self mode:mode options:options];
582
+ }
583
+
341
584
  @end
@@ -0,0 +1,76 @@
1
+ /**
2
+ * NSString+ApexPlugins.h
3
+ * Objective-C API for Apex plugin catalog and installation.
4
+ */
5
+
6
+ #import <Foundation/Foundation.h>
7
+
8
+ NS_ASSUME_NONNULL_BEGIN
9
+
10
+ /** Metadata for one plugin (catalog or installed). */
11
+ @interface ApexPluginInfo : NSObject
12
+
13
+ @property (nonatomic, copy, readonly) NSString *pluginId;
14
+ @property (nonatomic, copy, readonly, nullable) NSString *title;
15
+ @property (nonatomic, copy, readonly, nullable) NSString *pluginDescription;
16
+ @property (nonatomic, copy, readonly, nullable) NSString *author;
17
+ @property (nonatomic, copy, readonly, nullable) NSString *homepage;
18
+ @property (nonatomic, copy, readonly, nullable) NSString *repo;
19
+
20
+ - (instancetype)init NS_UNAVAILABLE;
21
+
22
+ @end
23
+
24
+ /** Plugin catalog and install helpers for app UIs. */
25
+ @interface ApexPluginCatalog : NSObject
26
+
27
+ /** Default apex-plugins.json URL. */
28
+ @property (class, nonatomic, readonly) NSString *defaultDirectoryURL;
29
+
30
+ /**
31
+ * Fetch available plugins from the central directory.
32
+ * @param error Set on failure (network, parse, curl missing).
33
+ */
34
+ + (nullable NSArray<ApexPluginInfo *> *)fetchAvailablePlugins:(NSError **)error;
35
+
36
+ /**
37
+ * Fetch available plugins from a custom directory JSON URL.
38
+ */
39
+ + (nullable NSArray<ApexPluginInfo *> *)fetchAvailablePluginsFromURL:(NSString *)url
40
+ error:(NSError **)error;
41
+
42
+ /**
43
+ * List installed plugins (project + user-global, same order as runtime loading).
44
+ * @param baseDirectory Optional document base for .apex/plugins lookup.
45
+ */
46
+ + (NSArray<ApexPluginInfo *> *)installedPluginsWithBaseDirectory:(NSString * _Nullable)baseDirectory;
47
+
48
+ /**
49
+ * Install a plugin by directory id into ~/.config/apex/plugins.
50
+ */
51
+ + (BOOL)installPluginWithId:(NSString *)pluginId error:(NSError **)error;
52
+
53
+ /**
54
+ * Install from a Git URL or GitHub shorthand (user/repo). Requires explicit opt-in.
55
+ */
56
+ + (BOOL)installPluginFromRepository:(NSString *)idOrRepo
57
+ allowUntrustedSource:(BOOL)allowUntrusted
58
+ error:(NSError **)error;
59
+
60
+ /** Remove a plugin from the user-global plugins directory. */
61
+ + (BOOL)uninstallPluginWithId:(NSString *)pluginId error:(NSError **)error;
62
+
63
+ /** User-global plugins directory path. */
64
+ + (nullable NSString *)globalPluginsDirectory;
65
+
66
+ @end
67
+
68
+ FOUNDATION_EXPORT NSErrorDomain const ApexPluginErrorDomain;
69
+
70
+ typedef NS_ERROR_ENUM(ApexPluginErrorDomain, ApexPluginError) {
71
+ ApexPluginErrorFetchFailed = 1,
72
+ ApexPluginErrorInstallFailed = 2,
73
+ ApexPluginErrorUninstallFailed = 3,
74
+ };
75
+
76
+ NS_ASSUME_NONNULL_END