@plebeiantech/lat.md 0.12.3-fork.13 → 0.12.3-fork.14

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 (274) hide show
  1. package/README.md +24 -0
  2. package/dist/src/cli/check-context.d.ts +56 -0
  3. package/dist/src/cli/check-context.js +215 -0
  4. package/dist/src/cli/check-coverage.js +9 -5
  5. package/dist/src/cli/check-frontmatter.js +1 -1
  6. package/dist/src/cli/check-mode.js +1 -1
  7. package/dist/src/cli/check-status.js +1 -1
  8. package/dist/src/cli/check.d.ts +19 -12
  9. package/dist/src/cli/check.js +259 -206
  10. package/dist/src/cli/context.js +1 -1
  11. package/dist/src/cli/expand.d.ts +1 -2
  12. package/dist/src/cli/expand.js +52 -5
  13. package/dist/src/cli/external.d.ts +11 -0
  14. package/dist/src/cli/external.js +185 -0
  15. package/dist/src/cli/fork-instructions.d.ts +1 -0
  16. package/dist/src/cli/fork-instructions.js +3 -2
  17. package/dist/src/cli/hook.d.ts +26 -0
  18. package/dist/src/cli/hook.js +112 -40
  19. package/dist/src/cli/index.js +55 -3
  20. package/dist/src/cli/init.d.ts +1 -0
  21. package/dist/src/cli/init.js +40 -2
  22. package/dist/src/cli/locate.js +4 -3
  23. package/dist/src/cli/refs.d.ts +2 -1
  24. package/dist/src/cli/refs.js +88 -43
  25. package/dist/src/cli/reindex.js +2 -1
  26. package/dist/src/cli/search.d.ts +12 -3
  27. package/dist/src/cli/search.js +45 -49
  28. package/dist/src/cli/section.d.ts +3 -1
  29. package/dist/src/cli/section.js +114 -38
  30. package/dist/src/code-refs.d.ts +17 -5
  31. package/dist/src/code-refs.js +246 -65
  32. package/dist/src/context.d.ts +3 -0
  33. package/dist/src/document-formats.d.ts +7 -0
  34. package/dist/src/document-formats.js +39 -0
  35. package/dist/src/extensions/alert-marker.d.ts +25 -0
  36. package/dist/src/extensions/alert-marker.js +55 -0
  37. package/dist/src/external-documents.d.ts +56 -0
  38. package/dist/src/external-documents.js +404 -0
  39. package/dist/src/external-sources.d.ts +136 -0
  40. package/dist/src/external-sources.js +1098 -0
  41. package/dist/src/fork/antigravity-hook.d.ts +47 -0
  42. package/dist/src/fork/antigravity-hook.js +424 -0
  43. package/dist/src/fork/antigravity-init.d.ts +17 -0
  44. package/dist/src/fork/antigravity-init.js +124 -0
  45. package/dist/src/fork/frontmatter-fields.d.ts +36 -0
  46. package/dist/src/fork/frontmatter-fields.js +23 -0
  47. package/dist/src/format.d.ts +5 -2
  48. package/dist/src/format.js +8 -3
  49. package/dist/src/graph-export.js +1 -1
  50. package/dist/src/lattice-model.d.ts +77 -0
  51. package/dist/src/lattice-model.js +464 -0
  52. package/dist/src/lattice.d.ts +9 -104
  53. package/dist/src/lattice.js +69 -516
  54. package/dist/src/markdown-analysis-cache.d.ts +20 -0
  55. package/dist/src/markdown-analysis-cache.js +139 -0
  56. package/dist/src/markdown-analysis-worker.d.ts +16 -0
  57. package/dist/src/markdown-analysis-worker.js +33 -0
  58. package/dist/src/markdown-analysis.d.ts +50 -0
  59. package/dist/src/markdown-analysis.js +139 -0
  60. package/dist/src/markdown-analyzer-loader.d.ts +4 -0
  61. package/dist/src/markdown-analyzer-loader.js +13 -0
  62. package/dist/src/markdown-validation.d.ts +25 -0
  63. package/dist/src/markdown-validation.js +104 -0
  64. package/dist/src/mcp/server.js +27 -11
  65. package/dist/src/parser-cache.d.ts +17 -0
  66. package/dist/src/parser-cache.js +65 -0
  67. package/dist/src/parser-import.d.ts +8 -0
  68. package/dist/src/parser-import.js +1 -0
  69. package/dist/src/parser.js +80 -5
  70. package/dist/src/path.d.ts +2 -0
  71. package/dist/src/path.js +4 -0
  72. package/dist/src/profiler.d.ts +16 -0
  73. package/dist/src/profiler.js +81 -0
  74. package/dist/src/project-analysis.d.ts +59 -0
  75. package/dist/src/project-analysis.js +233 -0
  76. package/dist/src/project-discovery.d.ts +6 -0
  77. package/dist/src/project-discovery.js +30 -0
  78. package/dist/src/repository-path.d.ts +11 -0
  79. package/dist/src/repository-path.js +52 -0
  80. package/dist/src/search/db.d.ts +1 -1
  81. package/dist/src/search/db.js +2 -2
  82. package/dist/src/search/embedder.d.ts +2 -1
  83. package/dist/src/search/embedder.js +6 -5
  84. package/dist/src/search/index.d.ts +2 -1
  85. package/dist/src/search/index.js +10 -8
  86. package/dist/src/search/query.d.ts +20 -0
  87. package/dist/src/search/query.js +67 -0
  88. package/dist/src/search/search.d.ts +3 -1
  89. package/dist/src/search/search.js +5 -2
  90. package/dist/src/source-formats.d.ts +7 -0
  91. package/dist/src/source-formats.js +26 -0
  92. package/dist/src/source-parser.d.ts +44 -4
  93. package/dist/src/source-parser.js +494 -57
  94. package/dist/src/view/client/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  95. package/dist/src/view/client/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  96. package/dist/src/view/client/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  97. package/dist/src/view/client/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  98. package/dist/src/view/client/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  99. package/dist/src/view/client/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  100. package/dist/src/view/client/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  101. package/dist/src/view/client/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  102. package/dist/src/view/client/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  103. package/dist/src/view/client/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  104. package/dist/src/view/client/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  105. package/dist/src/view/client/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  106. package/dist/src/view/client/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  107. package/dist/src/view/client/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  108. package/dist/src/view/client/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  109. package/dist/src/view/client/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  110. package/dist/src/view/client/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  111. package/dist/src/view/client/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  112. package/dist/src/view/client/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  113. package/dist/src/view/client/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  114. package/dist/src/view/client/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  115. package/dist/src/view/client/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  116. package/dist/src/view/client/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  117. package/dist/src/view/client/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  118. package/dist/src/view/client/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  119. package/dist/src/view/client/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  120. package/dist/src/view/client/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  121. package/dist/src/view/client/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  122. package/dist/src/view/client/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  123. package/dist/src/view/client/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  124. package/dist/src/view/client/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  125. package/dist/src/view/client/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  126. package/dist/src/view/client/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  127. package/dist/src/view/client/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  128. package/dist/src/view/client/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  129. package/dist/src/view/client/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  130. package/dist/src/view/client/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  131. package/dist/src/view/client/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  132. package/dist/src/view/client/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  133. package/dist/src/view/client/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  134. package/dist/src/view/client/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  135. package/dist/src/view/client/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  136. package/dist/src/view/client/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  137. package/dist/src/view/client/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  138. package/dist/src/view/client/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  139. package/dist/src/view/client/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  140. package/dist/src/view/client/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  141. package/dist/src/view/client/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  142. package/dist/src/view/client/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  143. package/dist/src/view/client/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  144. package/dist/src/view/client/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  145. package/dist/src/view/client/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  146. package/dist/src/view/client/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  147. package/dist/src/view/client/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  148. package/dist/src/view/client/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  149. package/dist/src/view/client/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  150. package/dist/src/view/client/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  151. package/dist/src/view/client/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  152. package/dist/src/view/client/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  153. package/dist/src/view/client/assets/MarkdownEditor-B7quqPb8.js +28 -0
  154. package/dist/src/view/client/assets/OrbitControls-DwcpQNja.js +1 -0
  155. package/dist/src/view/client/assets/STLLoader-CLWTKt5i.js +1 -0
  156. package/dist/src/view/client/assets/abnfDiagram-VCTEODGH-V7-T6qsl.js +1 -0
  157. package/dist/src/view/client/assets/arc-BnwR8Bu7.js +1 -0
  158. package/dist/src/view/client/assets/architectureDiagram-5GKGNRK7-u97vDna9.js +36 -0
  159. package/dist/src/view/client/assets/blockDiagram-I7D4REHJ-DETl8e_H.js +129 -0
  160. package/dist/src/view/client/assets/c4Diagram-7LVT6UL2-DJRAT1Ve.js +38 -0
  161. package/dist/src/view/client/assets/channel-BwQbnWCN.js +1 -0
  162. package/dist/src/view/client/assets/chunk-2Q5K7J3B-C0TmOC1Q.js +1 -0
  163. package/dist/src/view/client/assets/chunk-5VM5RSS4-dfBOrYE6.js +15 -0
  164. package/dist/src/view/client/assets/chunk-F27PBJKO-DAnks99K.js +1 -0
  165. package/dist/src/view/client/assets/chunk-IMKFNOWR-CPjB07if.js +231 -0
  166. package/dist/src/view/client/assets/chunk-JWPE2WC7-VvU_gZI6.js +1 -0
  167. package/dist/src/view/client/assets/chunk-POPQ4Y6H-DDUbVF55.js +1 -0
  168. package/dist/src/view/client/assets/chunk-SVP7TREG-BHROkr6D.js +88 -0
  169. package/dist/src/view/client/assets/chunk-TICWLB2K-95PzGIOg.js +206 -0
  170. package/dist/src/view/client/assets/chunk-XXDRQBXY-MdmuLhIs.js +1 -0
  171. package/dist/src/view/client/assets/classDiagram-ZZMXUADV-HvQKkIS-.js +1 -0
  172. package/dist/src/view/client/assets/classDiagram-v2-VYDZK3BY-HvQKkIS-.js +1 -0
  173. package/dist/src/view/client/assets/cose-bilkent-JH36ORCC-CauUf_rN.js +1 -0
  174. package/dist/src/view/client/assets/cynefin-OW5HDTMX-CeA8IKWo.js +166 -0
  175. package/dist/src/view/client/assets/cynefinDiagram-5FMLGOSQ-L1rtcT8l.js +62 -0
  176. package/dist/src/view/client/assets/cytoscape.esm-DzSz-X2X.js +321 -0
  177. package/dist/src/view/client/assets/dagre-GXQ25YYZ-DLQwTa3H.js +4 -0
  178. package/dist/src/view/client/assets/defaultLocale-DX6XiGOO.js +1 -0
  179. package/dist/src/view/client/assets/diagram-S7CK7UJ4-kbKdIyXG.js +30 -0
  180. package/dist/src/view/client/assets/diagram-UQ7AKVKN-kmzp92RC.js +41 -0
  181. package/dist/src/view/client/assets/diagram-VSXAHHWV-EXJTU8qq.js +3 -0
  182. package/dist/src/view/client/assets/diagram-VX7I27RA-DAiIyMCG.js +24 -0
  183. package/dist/src/view/client/assets/diagram-Z3DM3KII-G9wz50ZV.js +24 -0
  184. package/dist/src/view/client/assets/ebnfDiagram-PWID7BFC-D2tVRCNA.js +1 -0
  185. package/dist/src/view/client/assets/erDiagram-RLTQ6QDP-C925J5A3.js +99 -0
  186. package/dist/src/view/client/assets/flowDiagram-HODETNUW-DzoCa-Nq.js +168 -0
  187. package/dist/src/view/client/assets/ganttDiagram-EL5Y4UJY-D2ebbg4p.js +292 -0
  188. package/dist/src/view/client/assets/gitGraphDiagram-WWUBYQGX-B4QratYy.js +106 -0
  189. package/dist/src/view/client/assets/index-BnENo9bV.js +1 -0
  190. package/dist/src/view/client/assets/index-D0ajr_Ja.css +1 -0
  191. package/dist/src/view/client/assets/index-KZcnpXJH.js +321 -0
  192. package/dist/src/view/client/assets/infoDiagram-27XIBGKW-DL4mtHkt.js +2 -0
  193. package/dist/src/view/client/assets/init-Gi6I4Gst.js +1 -0
  194. package/dist/src/view/client/assets/ishikawaDiagram-5VMMS53U-CW4mto2d.js +70 -0
  195. package/dist/src/view/client/assets/journeyDiagram-3NMN7TZE-8R4eacFW.js +139 -0
  196. package/dist/src/view/client/assets/kanban-definition-UXKFOSKX-Cy9ioXEJ.js +89 -0
  197. package/dist/src/view/client/assets/katex-HP8lGamR.js +257 -0
  198. package/dist/src/view/client/assets/layout-C0MFuhth.js +1 -0
  199. package/dist/src/view/client/assets/linear-Dv3N4dQR.js +1 -0
  200. package/dist/src/view/client/assets/logo-small-RR_KBOAT.svg +149 -0
  201. package/dist/src/view/client/assets/maplibre-gl-DnqZgfUS.js +800 -0
  202. package/dist/src/view/client/assets/mermaid.core-bds9A7e2.js +310 -0
  203. package/dist/src/view/client/assets/mindmap-definition-YA3MSWOX-DjmGW355.js +96 -0
  204. package/dist/src/view/client/assets/ordinal-Cboi1Yqb.js +1 -0
  205. package/dist/src/view/client/assets/pegDiagram-XKGWAZYB-BWsZp0ab.js +1 -0
  206. package/dist/src/view/client/assets/pieDiagram-E7YTZNPT-CCH2gGR-.js +39 -0
  207. package/dist/src/view/client/assets/quadrantDiagram-AXDQQJYC-CjocPJFz.js +7 -0
  208. package/dist/src/view/client/assets/railroadDiagram-O6MQD6OU-BjLeb2KF.js +1 -0
  209. package/dist/src/view/client/assets/requirementDiagram-BXWQKSXE-Ct6AoXRr.js +84 -0
  210. package/dist/src/view/client/assets/sankeyDiagram-P5KCCOFB-CIpgGN_w.js +40 -0
  211. package/dist/src/view/client/assets/sequenceDiagram-WJ2MYXX4-DrmLFj6w.js +162 -0
  212. package/dist/src/view/client/assets/sizeCapture-INFHLROL-BdFMe2Lr.js +1 -0
  213. package/dist/src/view/client/assets/stateDiagram-D77RDMKH-Zd9F1goW.js +1 -0
  214. package/dist/src/view/client/assets/stateDiagram-v2-MP3YSRHH-ByIKCftl.js +1 -0
  215. package/dist/src/view/client/assets/swimlanes-42K2YHIH-a7VFwi00.js +1 -0
  216. package/dist/src/view/client/assets/swimlanesDiagram-VR7AAH4N-CzxaUHB-.js +8 -0
  217. package/dist/src/view/client/assets/three.module-PDSP0dbZ.js +4108 -0
  218. package/dist/src/view/client/assets/timeline-definition-24CTP7MA-BExPu6X5.js +120 -0
  219. package/dist/src/view/client/assets/vennDiagram-4TSXK5OY-PC1MU3si.js +34 -0
  220. package/dist/src/view/client/assets/wardleyDiagram-VM6X3IG4-DNWqU3fa.js +78 -0
  221. package/dist/src/view/client/assets/xychartDiagram-S5SC5T6Z-E1uA28_D.js +7 -0
  222. package/dist/src/view/client/index.html +2 -2
  223. package/dist/src/view/client-shell.d.ts +2 -0
  224. package/dist/src/view/client-shell.js +5 -0
  225. package/dist/src/view/diagnostics.d.ts +3 -2
  226. package/dist/src/view/diagnostics.js +49 -58
  227. package/dist/src/view/document-edit.d.ts +8 -0
  228. package/dist/src/view/document-edit.js +25 -0
  229. package/dist/src/view/document-route.d.ts +12 -0
  230. package/dist/src/view/document-route.js +93 -0
  231. package/dist/src/view/document-tree.d.ts +18 -0
  232. package/dist/src/view/document-tree.js +139 -0
  233. package/dist/src/view/external-document-tree.d.ts +3 -0
  234. package/dist/src/view/external-document-tree.js +674 -0
  235. package/dist/src/view/git-diff.js +187 -0
  236. package/dist/src/view/git.js +1 -1
  237. package/dist/src/view/graph.d.ts +3 -2
  238. package/dist/src/view/graph.js +94 -7
  239. package/dist/src/view/highlight.d.ts +10 -2
  240. package/dist/src/view/highlight.js +4540 -1360
  241. package/dist/src/view/markdown.d.ts +3 -2
  242. package/dist/src/view/markdown.js +214 -53
  243. package/dist/src/view/protocol.d.ts +59 -6
  244. package/dist/src/view/references.d.ts +10 -25
  245. package/dist/src/view/references.js +117 -107
  246. package/dist/src/view/repository.d.ts +13 -3
  247. package/dist/src/view/repository.js +234 -15
  248. package/dist/src/view/search.d.ts +1 -1
  249. package/dist/src/view/search.js +3 -7
  250. package/dist/src/view/server.d.ts +11 -2
  251. package/dist/src/view/server.js +212 -70
  252. package/dist/src/view/source-target.js +5 -8
  253. package/dist/src/view/static-build.d.ts +1 -0
  254. package/dist/src/view/static-build.js +245 -42
  255. package/dist/src/view/static-protocol.d.ts +19 -2
  256. package/dist/src/view/store.d.ts +16 -3
  257. package/dist/src/view/store.js +228 -36
  258. package/dist/src/view/table-of-contents.d.ts +2 -2
  259. package/dist/src/view/table-of-contents.js +2 -22
  260. package/dist/src/walk.d.ts +5 -3
  261. package/dist/src/walk.js +102 -8
  262. package/package.json +46 -8
  263. package/plugins/lat-md/README.md +61 -0
  264. package/plugins/lat-md/hooks.json +38 -0
  265. package/plugins/lat-md/plugin.json +9 -0
  266. package/plugins/lat-md/rules/AGENTS.md +118 -0
  267. package/plugins/lat-md/skills/lat-md/SKILL.md +178 -0
  268. package/plugins/lat-md/skills/lat-md-conventions/SKILL.md +182 -0
  269. package/templates/AGENTS.md +15 -3
  270. package/templates/cursor-rules.md +12 -3
  271. package/templates/skill/SKILL.md +22 -1
  272. package/dist/src/view/client/assets/index-C3iaZyYT.css +0 -1
  273. package/dist/src/view/client/assets/index-KWi67IMz.js +0 -334
  274. package/dist/src/view/client/assets/logo-CNF8zs47.svg +0 -182
@@ -1,112 +1,21 @@
1
1
  import { relative, resolve } from 'node:path';
2
- import { visit } from 'unist-util-visit';
3
- import { buildFileIndex, buildSectionSlugIndex, extractRefs, extractLinks, flattenSections, parseSections, resolveRef, } from '../lattice.js';
4
- import { parse } from '../parser.js';
5
- import { toPosix } from '../walk.js';
2
+ import { buildFileIndex, buildSectionSlugIndex, flattenSections, resolveRef, } from '../lattice-model.js';
3
+ import { analyzeMarkdownFile, } from '../markdown-analysis.js';
4
+ import { toPosix } from '../path.js';
6
5
  import { renderMarkdown } from './markdown.js';
7
6
  import { viewSourceTarget } from './source-target.js';
8
- function inlineText(node) {
9
- if (node.type === 'wikiLink')
10
- return node.data.alias ?? node.value;
11
- if ('value' in node && typeof node.value === 'string')
12
- return node.value;
13
- if (node.type === 'image')
14
- return node.alt ?? '';
15
- if (!('children' in node) || !Array.isArray(node.children))
16
- return '';
17
- return node.children
18
- .map((child) => inlineText(child))
19
- .join('');
20
- }
21
- function paragraphs(content, tree) {
22
- const byLine = new Map();
23
- visit(tree, 'paragraph', (node) => {
24
- const start = node.position?.start.line;
25
- const end = node.position?.end.line;
26
- if (!start || !end)
27
- return;
28
- const text = inlineText(node).replace(/\s+/g, ' ').trim();
29
- const startOffset = node.position?.start.offset;
30
- const endOffset = node.position?.end.offset;
31
- const paragraph = {
32
- markdown: startOffset === undefined || endOffset === undefined
33
- ? text
34
- : content.slice(startOffset, endOffset),
35
- startLine: start,
36
- text,
37
- };
38
- for (let line = start; line <= end; line++)
39
- byLine.set(line, paragraph);
40
- });
41
- return byLine;
42
- }
43
- function markdownLinks(tree) {
44
- const definitions = new Map();
45
- visit(tree, 'definition', (node) => {
46
- definitions.set(node.identifier.toLowerCase(), node.url);
47
- });
48
- const links = [];
49
- visit(tree, (node) => {
50
- if (node.type !== 'link' &&
51
- node.type !== 'image' &&
52
- node.type !== 'linkReference' &&
53
- node.type !== 'imageReference') {
54
- return;
55
- }
56
- const line = node.position?.start.line;
57
- if (!line)
58
- return;
59
- const url = node.type === 'link' || node.type === 'image'
60
- ? node.url
61
- : definitions.get(node.identifier.toLowerCase());
62
- if (url) {
63
- links.push({
64
- kind: node.type === 'image' || node.type === 'imageReference'
65
- ? 'image'
66
- : 'link',
67
- line,
68
- url,
69
- });
70
- }
71
- });
72
- return links;
73
- }
7
+ import { documentUrl as routeDocumentUrl, rewriteDocumentLink, } from './document-route.js';
74
8
  /** Parse one Markdown file once into every structure needed by the view store. */
75
9
  export function parseViewMarkdownFile(absolutePath, content, latDir, projectRoot) {
76
- const tree = parse(content);
77
- return {
78
- absolutePath,
79
- content,
80
- path: toPosix(relative(latDir, absolutePath)),
81
- projectPath: toPosix(relative(projectRoot, absolutePath)),
82
- tree,
83
- sections: parseSections(absolutePath, content, projectRoot, tree),
84
- wikiRefs: extractRefs(absolutePath, content, projectRoot, tree),
85
- paragraphs: paragraphs(content, tree),
86
- markdownLinks: markdownLinks(tree),
87
- validationLinks: extractLinks(content, tree),
88
- };
10
+ return analyzeMarkdownFile(absolutePath, content, latDir, projectRoot);
89
11
  }
90
12
  function contextMarkdownLink(requestedPath, url) {
91
- if (url.startsWith('/') ||
92
- url.startsWith('//') ||
93
- /^[a-z][a-z\d+.-]*:/i.test(url)) {
94
- return url;
95
- }
96
- const encodedPath = requestedPath
97
- .split('/')
98
- .map(encodeURIComponent)
99
- .join('/');
100
- const resolved = new URL(url, `http://lat.local/docs/${encodedPath}`);
101
- return `${resolved.pathname}${resolved.search}${resolved.hash}`;
13
+ return rewriteDocumentLink(url, requestedPath);
102
14
  }
103
15
  function documentUrl(latDir, projectRoot, section) {
104
16
  const path = toPosix(relative(latDir, resolve(projectRoot, section.filePath)));
105
- const encoded = path.split('/').map(encodeURIComponent).join('/');
106
- const fragment = section.githubSlug
107
- ? `#${encodeURIComponent(section.githubSlug)}`
108
- : '';
109
- return `/docs/${encoded}${fragment}`;
17
+ const fragment = section.githubSlug ? section.githubSlug : '';
18
+ return routeDocumentUrl(path, fragment);
110
19
  }
111
20
  function breadcrumbs(latDir, projectRoot, section) {
112
21
  const path = toPosix(relative(latDir, resolve(projectRoot, section.filePath))).replace(/\.md$/i, '');
@@ -155,14 +64,15 @@ export function linkedSection(url, sourcePath, sectionsByPath) {
155
64
  return (sections.find((section) => section.githubSlug?.toLowerCase() === fragment.toLowerCase()) ?? null);
156
65
  }
157
66
  function paragraphFor(file, section, line) {
158
- return (file.paragraphs.get(line) ?? {
67
+ return (file.paragraphs.find((paragraph) => line >= paragraph.startLine && line <= paragraph.endLine) ?? {
159
68
  markdown: section.firstParagraph,
160
69
  startLine: line,
70
+ endLine: line,
161
71
  text: section.firstParagraph,
162
72
  });
163
73
  }
164
74
  /** Resolve cached outgoing occurrences into direct section and source indexes. */
165
- export function buildViewReferenceIndex(markdownFiles, codeFiles, allSections) {
75
+ export function buildViewReferenceIndex(markdownFiles, codeFiles, allSections, resolveExternalTarget) {
166
76
  const files = [...markdownFiles].sort((a, b) => a.path.localeCompare(b.path));
167
77
  const sections = flattenSections(allSections);
168
78
  const sectionIds = new Set(sections.map((section) => section.id.toLowerCase()));
@@ -175,6 +85,7 @@ export function buildViewReferenceIndex(markdownFiles, codeFiles, allSections) {
175
85
  }
176
86
  const incoming = new Map();
177
87
  const sourceByTarget = new Map();
88
+ const externalByTarget = new Map();
178
89
  const addIncoming = (targetId, key, reference) => {
179
90
  const target = targetId.toLowerCase();
180
91
  let references = incoming.get(target);
@@ -194,6 +105,15 @@ export function buildViewReferenceIndex(markdownFiles, codeFiles, allSections) {
194
105
  if (!references.has(key))
195
106
  references.set(key, reference);
196
107
  };
108
+ const addExternalReference = (target, key, reference) => {
109
+ let references = externalByTarget.get(target);
110
+ if (!references) {
111
+ references = new Map();
112
+ externalByTarget.set(target, references);
113
+ }
114
+ if (!references.has(key))
115
+ references.set(key, reference);
116
+ };
197
117
  for (const file of files) {
198
118
  const fileSections = flattenSections(file.sections).sort((a, b) => a.startLine - b.startLine);
199
119
  for (const ref of file.wikiRefs) {
@@ -209,6 +129,10 @@ export function buildViewReferenceIndex(markdownFiles, codeFiles, allSections) {
209
129
  activeWikiLink: ref.target,
210
130
  };
211
131
  const locationKey = `markdown:${section.filePath}:${reference.paragraph.startLine}`;
132
+ const externalTarget = resolveExternalTarget?.(ref.target);
133
+ if (externalTarget) {
134
+ addExternalReference(externalTarget, locationKey, reference);
135
+ }
212
136
  const source = viewSourceTarget(ref.target);
213
137
  if (source) {
214
138
  addSourceReference(source.key, locationKey, reference);
@@ -247,6 +171,16 @@ export function buildViewReferenceIndex(markdownFiles, codeFiles, allSections) {
247
171
  }
248
172
  for (const file of [...codeFiles].sort((a, b) => a.path.localeCompare(b.path))) {
249
173
  for (const ref of [...file.refs].sort((a, b) => a.line - b.line)) {
174
+ const externalTarget = resolveExternalTarget?.(ref.target);
175
+ if (externalTarget) {
176
+ addExternalReference(externalTarget, `code:${ref.file}:${ref.line}`, {
177
+ kind: 'code',
178
+ path: ref.file,
179
+ line: ref.line,
180
+ snippet: file.lines[ref.line - 1]?.trim() ?? '',
181
+ url: sourceLineUrl(ref.file, ref.line),
182
+ });
183
+ }
250
184
  const resolved = resolveRef(ref.target, sectionIds, fileIndex, slugIndex);
251
185
  if (resolved.ambiguous ||
252
186
  !sectionById.has(resolved.resolved.toLowerCase())) {
@@ -274,21 +208,99 @@ export function buildViewReferenceIndex(markdownFiles, codeFiles, allSections) {
274
208
  target,
275
209
  references.size,
276
210
  ])),
211
+ externalByTarget: new Map([...externalByTarget].map(([target, references]) => [
212
+ target,
213
+ [...references.values()],
214
+ ])),
277
215
  };
278
216
  }
217
+ async function renderExternalReference(reference, latDir, projectRoot, createWikiLinkResolver) {
218
+ if (reference.kind === 'code')
219
+ return reference;
220
+ const resolver = createWikiLinkResolver
221
+ ? await createWikiLinkResolver(reference.sourcePath)
222
+ : undefined;
223
+ return renderIndexedMarkdownReference(reference, latDir, projectRoot, resolver);
224
+ }
225
+ /** Render exact local Markdown and code backlinks for external targets. */
226
+ export async function renderExternalSectionBackReferences(index, targets, latDir, projectRoot, createWikiLinkResolver) {
227
+ const sections = new Map();
228
+ for (const [target, headingId] of targets) {
229
+ let section = sections.get(headingId);
230
+ if (!section) {
231
+ section = { sectionId: target, references: new Map() };
232
+ sections.set(headingId, section);
233
+ }
234
+ const lowerTarget = target.toLowerCase();
235
+ const indexed = [...index.externalByTarget]
236
+ .filter(([candidate]) => candidate.toLowerCase() === lowerTarget)
237
+ .flatMap(([, references]) => references);
238
+ for (const reference of indexed) {
239
+ const key = reference.kind === 'markdown'
240
+ ? `markdown:${reference.sourcePath}:${reference.paragraph.startLine}`
241
+ : `code:${reference.path}:${reference.line}`;
242
+ if (!section.references.has(key))
243
+ section.references.set(key, reference);
244
+ }
245
+ }
246
+ const result = [];
247
+ for (const [headingId, section] of sections) {
248
+ const references = await Promise.all([...section.references.values()].map((reference) => renderExternalReference(reference, latDir, projectRoot, createWikiLinkResolver)));
249
+ result.push({ sectionId: section.sectionId, headingId, references });
250
+ }
251
+ return result;
252
+ }
253
+ /** Adapt exact external backlinks to the source context presentation. */
254
+ export async function renderExternalSourceReferences(index, target, latDir, projectRoot, createWikiLinkResolver) {
255
+ const indexed = index.externalByTarget.get(target) ?? [];
256
+ const result = [];
257
+ for (const reference of indexed) {
258
+ const rendered = await renderExternalReference(reference, latDir, projectRoot, createWikiLinkResolver);
259
+ if (rendered.kind === 'markdown') {
260
+ result.push({
261
+ sectionId: rendered.sectionId,
262
+ breadcrumbs: rendered.breadcrumbs,
263
+ paragraph: rendered.paragraph,
264
+ paragraphTree: rendered.paragraphTree,
265
+ url: rendered.url,
266
+ });
267
+ }
268
+ else {
269
+ result.push({
270
+ sectionId: `code:${rendered.path}:${rendered.line}`,
271
+ breadcrumbs: [...rendered.path.split('/'), `line ${rendered.line}`],
272
+ paragraph: rendered.snippet,
273
+ paragraphTree: {
274
+ version: 1,
275
+ type: 'root',
276
+ children: [
277
+ {
278
+ type: 'element',
279
+ tagName: 'code',
280
+ properties: {},
281
+ children: [{ type: 'text', value: rendered.snippet }],
282
+ },
283
+ ],
284
+ },
285
+ url: rendered.url,
286
+ });
287
+ }
288
+ }
289
+ return result;
290
+ }
279
291
  async function renderIndexedMarkdownReference(reference, latDir, projectRoot, resolveWikiLink) {
280
- const paragraphHtml = (await renderMarkdown(reference.paragraph.markdown, reference.sourcePath, resolveWikiLink, {
292
+ const paragraphTree = (await renderMarkdown(reference.paragraph.markdown, reference.sourcePath, resolveWikiLink, {
281
293
  activeWikiLink: reference.activeWikiLink,
282
294
  activeMarkdownLink: reference.activeMarkdownLink,
283
295
  lineOffset: reference.paragraph.startLine - 1,
284
296
  rewriteMarkdownLink: (url) => contextMarkdownLink(reference.sourcePath, url),
285
- })).html;
297
+ })).tree;
286
298
  return {
287
299
  kind: 'markdown',
288
300
  sectionId: reference.section.id,
289
301
  breadcrumbs: breadcrumbs(latDir, projectRoot, reference.section),
290
302
  paragraph: reference.paragraph.text,
291
- paragraphHtml,
303
+ paragraphTree,
292
304
  url: documentUrl(latDir, projectRoot, reference.section),
293
305
  };
294
306
  }
@@ -308,8 +320,6 @@ export async function renderSectionBackReferences(index, visibleSections, latDir
308
320
  const result = [];
309
321
  for (const section of flattenSections(visibleSections)) {
310
322
  const indexed = index.incomingBySection.get(section.id.toLowerCase()) ?? [];
311
- if (indexed.length === 0)
312
- continue;
313
323
  const references = [];
314
324
  for (const reference of indexed) {
315
325
  references.push(reference.kind === 'code'
@@ -345,7 +355,7 @@ export async function renderSourceReferenceContext(index, target, origin, latDir
345
355
  sectionId: rendered.sectionId,
346
356
  breadcrumbs: rendered.breadcrumbs,
347
357
  paragraph: rendered.paragraph,
348
- paragraphHtml: rendered.paragraphHtml,
358
+ paragraphTree: rendered.paragraphTree,
349
359
  url: rendered.url,
350
360
  },
351
361
  });
@@ -1,15 +1,25 @@
1
- import { type Section } from '../lattice.js';
2
- import type { ViewSourceDocument } from './protocol.js';
1
+ import { type Section } from '../lattice-model.js';
2
+ import { type ExternalResolver } from '../external-sources.js';
3
+ import type { ExternalTarget } from '../external-sources.js';
4
+ import { renderExternalDocumentTree } from './external-document-tree.js';
5
+ import type { ViewExternalDocument, ViewSourceDocument } from './protocol.js';
3
6
  import { type SourceReferenceOrigin, type ViewReferenceIndex } from './references.js';
4
7
  export declare class ViewDocumentNotFoundError extends Error {
5
8
  }
6
9
  export declare class ViewSourceNotFoundError extends Error {
7
10
  }
8
- export declare function createMarkdownWikiLinkResolver(latDir: string, requestedPath: string, loadedSections: Section[], referenceIndex?: ViewReferenceIndex): Promise<(target: string, context: {
11
+ export declare class ViewExternalNotFoundError extends Error {
12
+ }
13
+ export declare function externalUrl(target: string): string;
14
+ /** Make repository-relative external-document links reflect pulled files. */
15
+ export declare function resolveExternalDocumentLinks(tree: Awaited<ReturnType<typeof renderExternalDocumentTree>>, current: ExternalTarget, external: ExternalResolver, referenceIndex: ViewReferenceIndex): typeof tree;
16
+ export declare function createMarkdownWikiLinkResolver(latDir: string, requestedPath: string, loadedSections: Section[], referenceIndex?: ViewReferenceIndex, externalResolver?: ExternalResolver): Promise<(target: string, context: {
9
17
  line: number;
10
18
  }) => Promise<{
11
19
  href: string;
12
20
  referenceCount: number;
13
21
  } | null>>;
22
+ /** Resolve and render one configured external file through the normal view models. */
23
+ export declare function getViewExternal(latDir: string, projectRoot: string, targetValue: string, external: ExternalResolver, allSections: Section[], referenceIndex: ViewReferenceIndex): Promise<ViewExternalDocument>;
14
24
  /** Read and highlight a source file after constraining it to the project root. */
15
25
  export declare function getViewSource(latDir: string, projectRoot: string, requestedPath: string, requestedSymbol?: string, origin?: SourceReferenceOrigin, requestedLine?: number, allSections?: Section[], referenceIndex?: ViewReferenceIndex): Promise<ViewSourceDocument>;
@@ -1,23 +1,30 @@
1
1
  import { readFile, realpath } from 'node:fs/promises';
2
- import { dirname, extname, isAbsolute, relative, resolve, sep, } from 'node:path';
3
- import { buildFileIndex, buildSectionSlugIndex, flattenSections, resolveRef, } from '../lattice.js';
4
- import { resolveSourceSymbol, SOURCE_EXTENSIONS, } from '../source-parser.js';
5
- import { toPosix } from '../walk.js';
2
+ import { dirname, extname, isAbsolute, join, posix, relative, resolve, sep, } from 'node:path';
3
+ import { buildFileIndex, buildSectionSlugIndex, flattenSections, resolveRef, } from '../lattice-model.js';
4
+ import { createExternalResolver, } from '../external-sources.js';
5
+ import { addExternalDocumentAliasAnchors, externalDocumentSections, } from '../external-documents.js';
6
+ import { resolveSourceSymbol, SourceParserRuntime, } from '../source-parser.js';
7
+ import { isSourceFileExtension } from '../source-formats.js';
8
+ import { toPosix } from '../path.js';
9
+ import { renderExternalDocumentTree } from './external-document-tree.js';
10
+ import { visitDocumentElements } from './document-tree.js';
6
11
  import { highlightSource } from './highlight.js';
7
- import { renderSourceReferenceContext, } from './references.js';
12
+ import { renderMarkdown } from './markdown.js';
13
+ import { renderExternalSectionBackReferences, renderExternalSourceReferences, renderSourceReferenceContext, } from './references.js';
14
+ import { buildViewTableOfContents } from './table-of-contents.js';
8
15
  import { viewSourceTarget } from './source-target.js';
16
+ import { documentUrl } from './document-route.js';
9
17
  export class ViewDocumentNotFoundError extends Error {
10
18
  }
11
19
  export class ViewSourceNotFoundError extends Error {
12
20
  }
21
+ export class ViewExternalNotFoundError extends Error {
22
+ }
13
23
  function isInside(root, candidate) {
14
24
  const rel = relative(root, candidate);
15
25
  return (rel === '' ||
16
26
  (!isAbsolute(rel) && rel !== '..' && !rel.startsWith(`..${sep}`)));
17
27
  }
18
- function documentUrl(path) {
19
- return `/docs/${path.split('/').map(encodeURIComponent).join('/')}`;
20
- }
21
28
  function sourceUrl(path, symbol, origin) {
22
29
  const encodedPath = path.split('/').map(encodeURIComponent).join('/');
23
30
  const query = new URLSearchParams();
@@ -29,6 +36,128 @@ function sourceUrl(path, symbol, origin) {
29
36
  const fragment = symbol ? `#${encodeURIComponent(symbol)}` : '';
30
37
  return `/code/${encodedPath}${search}${fragment}`;
31
38
  }
39
+ export function externalUrl(target) {
40
+ const colon = target.indexOf(':');
41
+ const hash = target.indexOf('#', colon + 1);
42
+ const handle = target.slice(0, colon);
43
+ const path = hash === -1 ? target.slice(colon + 1) : target.slice(colon + 1, hash);
44
+ const fragment = hash === -1 ? '' : target.slice(hash + 1);
45
+ const encodedPath = path.split('/').map(encodeURIComponent).join('/');
46
+ return `/external/${encodeURIComponent(handle)}/${encodedPath}${fragment ? `#${encodeURIComponent(fragment)}` : ''}`;
47
+ }
48
+ function baseExternalTarget(identity) {
49
+ const hash = identity.indexOf('#');
50
+ return hash === -1 ? identity : identity.slice(0, hash);
51
+ }
52
+ function externalFileKey(handle, path) {
53
+ return `${handle}\0${path}`;
54
+ }
55
+ function availableExternalFiles(external, referenceIndex, current) {
56
+ const files = new Map();
57
+ for (const target of referenceIndex.externalByTarget.keys()) {
58
+ try {
59
+ const parsed = external.parse(target);
60
+ if (!parsed)
61
+ continue;
62
+ files.set(externalFileKey(parsed.handle, parsed.resolvedPath), baseExternalTarget(parsed.identity));
63
+ }
64
+ catch {
65
+ // Invalid external references remain diagnostics and are not navigable.
66
+ }
67
+ }
68
+ files.set(externalFileKey(current.handle, current.resolvedPath), baseExternalTarget(current.identity));
69
+ return files;
70
+ }
71
+ function externalDocumentLink(href, current, external, available) {
72
+ if (href.startsWith('#') ||
73
+ href.startsWith('?') ||
74
+ href.startsWith('//') ||
75
+ /^[a-z][a-z\d+.-]*:/i.test(href) ||
76
+ /^\/(?:code|docs|external)(?:\/|$)/.test(href)) {
77
+ return undefined;
78
+ }
79
+ let destination;
80
+ try {
81
+ const encodedPath = current.resolvedPath
82
+ .split('/')
83
+ .map(encodeURIComponent)
84
+ .join('/');
85
+ destination = new URL(href, `http://lat.external/${encodedPath}`);
86
+ }
87
+ catch {
88
+ return null;
89
+ }
90
+ if (destination.origin !== 'http://lat.external')
91
+ return undefined;
92
+ let path;
93
+ let fragment;
94
+ try {
95
+ path = destination.pathname
96
+ .slice(1)
97
+ .split('/')
98
+ .map(decodeURIComponent)
99
+ .join('/');
100
+ fragment = decodeURIComponent(destination.hash.slice(1));
101
+ }
102
+ catch {
103
+ return null;
104
+ }
105
+ path = posix.normalize(path);
106
+ if (!path || path === '.' || path === '..' || path.startsWith('../')) {
107
+ return null;
108
+ }
109
+ let parsed;
110
+ try {
111
+ parsed = external.parse(`${current.handle}:${path}${fragment ? `#${fragment}` : ''}`);
112
+ }
113
+ catch {
114
+ return null;
115
+ }
116
+ if (!parsed)
117
+ return null;
118
+ const availableTarget = available.get(externalFileKey(parsed.handle, parsed.resolvedPath));
119
+ if (!availableTarget)
120
+ return null;
121
+ const route = externalUrl(`${availableTarget}${fragment ? `#${fragment}` : ''}`);
122
+ if (!destination.search)
123
+ return route;
124
+ const hash = route.indexOf('#');
125
+ return hash === -1
126
+ ? `${route}${destination.search}`
127
+ : `${route.slice(0, hash)}${destination.search}${route.slice(hash)}`;
128
+ }
129
+ /** Make repository-relative external-document links reflect pulled files. */
130
+ export function resolveExternalDocumentLinks(tree, current, external, referenceIndex) {
131
+ const available = availableExternalFiles(external, referenceIndex, current);
132
+ visitDocumentElements(tree, (element) => {
133
+ if (element.tagName !== 'a')
134
+ return;
135
+ const href = element.properties.href;
136
+ if (typeof href !== 'string')
137
+ return;
138
+ const resolved = externalDocumentLink(href, current, external, available);
139
+ if (resolved === undefined)
140
+ return;
141
+ if (resolved !== null) {
142
+ element.properties.href = resolved;
143
+ return;
144
+ }
145
+ const currentClasses = element.properties.className;
146
+ const classes = Array.isArray(currentClasses)
147
+ ? currentClasses.map(String)
148
+ : typeof currentClasses === 'string'
149
+ ? currentClasses.split(/\s+/).filter(Boolean)
150
+ : [];
151
+ element.tagName = 'span';
152
+ element.properties = {
153
+ ...element.properties,
154
+ className: [...classes, 'external-source-link-unavailable'],
155
+ title: 'Linked file is not included in this Lat project',
156
+ };
157
+ delete element.properties.href;
158
+ });
159
+ return tree;
160
+ }
32
161
  function matchingSymbol(symbols, symbolPath) {
33
162
  const parts = symbolPath.split('#');
34
163
  if (parts.length === 1) {
@@ -39,7 +168,8 @@ function matchingSymbol(symbols, symbolPath) {
39
168
  }
40
169
  return undefined;
41
170
  }
42
- export async function createMarkdownWikiLinkResolver(latDir, requestedPath, loadedSections, referenceIndex) {
171
+ export async function createMarkdownWikiLinkResolver(latDir, requestedPath, loadedSections, referenceIndex, externalResolver) {
172
+ const external = externalResolver ?? (await createExternalResolver(latDir, dirname(latDir)));
43
173
  const projectRoot = dirname(latDir);
44
174
  const flat = flattenSections(loadedSections);
45
175
  const sectionIds = new Set(flat.map((section) => section.id.toLowerCase()));
@@ -51,6 +181,18 @@ export async function createMarkdownWikiLinkResolver(latDir, requestedPath, load
51
181
  .filter((section) => section.filePath === currentFile)
52
182
  .sort((a, b) => a.startLine - b.startLine);
53
183
  return async (target, context) => {
184
+ try {
185
+ const parsed = external.parse(target);
186
+ if (parsed) {
187
+ return {
188
+ href: externalUrl(parsed.identity),
189
+ referenceCount: referenceIndex?.externalByTarget.get(parsed.identity)?.length ?? 0,
190
+ };
191
+ }
192
+ }
193
+ catch {
194
+ return null;
195
+ }
54
196
  const result = resolveRef(target, sectionIds, fileIndex, slugIndex);
55
197
  if (result.ambiguous)
56
198
  return null;
@@ -71,7 +213,7 @@ export async function createMarkdownWikiLinkResolver(latDir, requestedPath, load
71
213
  if (!source)
72
214
  return null;
73
215
  try {
74
- await readViewSource(projectRoot, source.path, source.symbol);
216
+ await readViewSource(latDir, projectRoot, source.path, source.symbol);
75
217
  let section;
76
218
  for (const candidate of currentSections) {
77
219
  if (candidate.startLine > context.line)
@@ -93,11 +235,88 @@ export async function createMarkdownWikiLinkResolver(latDir, requestedPath, load
93
235
  }
94
236
  };
95
237
  }
96
- async function readViewSource(projectRoot, requestedPath, requestedSymbol = '', requestedLine = 0) {
238
+ /** Resolve and render one configured external file through the normal view models. */
239
+ export async function getViewExternal(latDir, projectRoot, targetValue, external, allSections, referenceIndex) {
240
+ let resolved;
241
+ try {
242
+ resolved = await external.resolve(targetValue);
243
+ }
244
+ catch (error) {
245
+ throw new ViewExternalNotFoundError(error.message);
246
+ }
247
+ const createResolver = (path) => createMarkdownWikiLinkResolver(latDir, path, allSections, referenceIndex, external);
248
+ if (resolved.kind === 'document') {
249
+ const virtualPath = join(projectRoot, 'lat.md', '.external', resolved.target.handle, resolved.target.resolvedPath);
250
+ const analysis = resolved.document;
251
+ const sections = externalDocumentSections(virtualPath, analysis);
252
+ const resolver = await createResolver(resolved.target.resolvedPath);
253
+ const rendered = analysis.format === 'markdown'
254
+ ? await renderMarkdown(resolved.fullContent, resolved.target.resolvedPath, resolver, {})
255
+ : {
256
+ title: analysis.title,
257
+ tree: await renderExternalDocumentTree(analysis.format, resolved.fullContent),
258
+ };
259
+ resolveExternalDocumentLinks(rendered.tree, resolved.target, external, referenceIndex);
260
+ const fragmentIndex = resolved.target.identity.indexOf('#');
261
+ const baseTarget = fragmentIndex === -1
262
+ ? resolved.target.identity
263
+ : resolved.target.identity.slice(0, fragmentIndex);
264
+ const targetHeadings = new Map();
265
+ targetHeadings.set(baseTarget, analysis.sections[0]?.anchor ?? '');
266
+ for (const section of analysis.sections) {
267
+ for (const alias of section.aliases) {
268
+ const target = `${baseTarget}#${alias}`;
269
+ if (!targetHeadings.has(target)) {
270
+ targetHeadings.set(target, section.anchor);
271
+ }
272
+ }
273
+ }
274
+ return {
275
+ kind: 'markdown',
276
+ target: resolved.target.identity,
277
+ document: {
278
+ path: baseTarget,
279
+ ...rendered,
280
+ tree: addExternalDocumentAliasAnchors(rendered.tree, analysis),
281
+ gitTree: null,
282
+ graphNodeIds: {},
283
+ tableOfContents: buildViewTableOfContents(sections, analysis.sections.map((section) => section.title), {
284
+ errors: [],
285
+ gitTree: null,
286
+ }),
287
+ errors: [],
288
+ backReferences: await renderExternalSectionBackReferences(referenceIndex, targetHeadings, latDir, projectRoot, createResolver),
289
+ frontmatter: { requireCodeMention: false },
290
+ },
291
+ };
292
+ }
293
+ const references = await renderExternalSourceReferences(referenceIndex, resolved.target.identity, latDir, projectRoot, createResolver);
294
+ return {
295
+ kind: 'source',
296
+ target: resolved.target.identity,
297
+ source: {
298
+ path: `${resolved.target.handle}:${resolved.target.authoredPath}`,
299
+ content: resolved.fullContent,
300
+ highlightedLines: highlightSource(resolved.target.resolvedPath, resolved.fullContent),
301
+ focus: resolved.target.fragment
302
+ ? {
303
+ symbol: resolved.target.fragment,
304
+ kind: 'external',
305
+ signature: resolved.signature ?? resolved.target.fragment,
306
+ startLine: resolved.startLine,
307
+ endLine: resolved.endLine,
308
+ }
309
+ : null,
310
+ context: null,
311
+ otherReferences: references,
312
+ },
313
+ };
314
+ }
315
+ async function readViewSource(latDir, projectRoot, requestedPath, requestedSymbol = '', requestedLine = 0) {
97
316
  if (!requestedPath ||
98
317
  requestedPath.includes('\\') ||
99
318
  isAbsolute(requestedPath) ||
100
- !SOURCE_EXTENSIONS.has(extname(requestedPath))) {
319
+ !isSourceFileExtension(extname(requestedPath))) {
101
320
  throw new ViewSourceNotFoundError('Source document not found');
102
321
  }
103
322
  const candidate = resolve(projectRoot, requestedPath);
@@ -135,7 +354,7 @@ async function readViewSource(projectRoot, requestedPath, requestedSymbol = '',
135
354
  },
136
355
  };
137
356
  }
138
- const resolved = await resolveSourceSymbol(requestedPath, requestedSymbol, projectRoot);
357
+ const resolved = await resolveSourceSymbol(requestedPath, requestedSymbol, projectRoot, { latDir, runtime: new SourceParserRuntime() });
139
358
  const symbol = resolved.found
140
359
  ? matchingSymbol(resolved.symbols, requestedSymbol)
141
360
  : undefined;
@@ -156,13 +375,13 @@ async function readViewSource(projectRoot, requestedPath, requestedSymbol = '',
156
375
  }
157
376
  /** Read and highlight a source file after constraining it to the project root. */
158
377
  export async function getViewSource(latDir, projectRoot, requestedPath, requestedSymbol = '', origin, requestedLine = 0, allSections = [], referenceIndex) {
159
- const source = await readViewSource(projectRoot, requestedPath, requestedSymbol, requestedLine);
378
+ const source = await readViewSource(latDir, projectRoot, requestedPath, requestedSymbol, requestedLine);
160
379
  const references = referenceIndex
161
380
  ? await renderSourceReferenceContext(referenceIndex, `${source.path}${requestedSymbol ? `#${requestedSymbol}` : ''}`, origin, latDir, projectRoot, (path) => createMarkdownWikiLinkResolver(latDir, path, allSections, referenceIndex))
162
381
  : { context: null, otherReferences: [] };
163
382
  return {
164
383
  ...source,
165
- highlightedHtmlLines: highlightSource(source.path, source.content),
384
+ highlightedLines: highlightSource(source.path, source.content),
166
385
  ...references,
167
386
  };
168
387
  }
@@ -1,4 +1,4 @@
1
- import type { SectionMatch } from '../lattice.js';
1
+ import type { SectionMatch } from '../lattice-model.js';
2
2
  import type { ViewSearchResponse } from './protocol.js';
3
3
  export type ViewSearch = (query: string) => Promise<ViewSearchResponse>;
4
4
  export type ViewSearchDependencies = {