@plebeiantech/lat.md 0.12.3-fork.13 → 0.12.3-fork.15
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.
- package/README.md +24 -0
- package/dist/src/cli/check-context.d.ts +56 -0
- package/dist/src/cli/check-context.js +215 -0
- package/dist/src/cli/check-coverage.js +9 -5
- package/dist/src/cli/check-frontmatter.js +1 -1
- package/dist/src/cli/check-mode.js +1 -1
- package/dist/src/cli/check-status.js +1 -1
- package/dist/src/cli/check.d.ts +19 -12
- package/dist/src/cli/check.js +259 -206
- package/dist/src/cli/context.js +1 -1
- package/dist/src/cli/expand.d.ts +1 -2
- package/dist/src/cli/expand.js +52 -5
- package/dist/src/cli/external.d.ts +11 -0
- package/dist/src/cli/external.js +185 -0
- package/dist/src/cli/fork-instructions.d.ts +1 -0
- package/dist/src/cli/fork-instructions.js +3 -2
- package/dist/src/cli/hook.d.ts +26 -0
- package/dist/src/cli/hook.js +112 -40
- package/dist/src/cli/index.js +55 -3
- package/dist/src/cli/init.d.ts +1 -0
- package/dist/src/cli/init.js +40 -2
- package/dist/src/cli/locate.js +4 -3
- package/dist/src/cli/refs.d.ts +2 -1
- package/dist/src/cli/refs.js +88 -43
- package/dist/src/cli/reindex.js +2 -1
- package/dist/src/cli/search.d.ts +12 -3
- package/dist/src/cli/search.js +45 -49
- package/dist/src/cli/section.d.ts +3 -1
- package/dist/src/cli/section.js +114 -38
- package/dist/src/code-refs.d.ts +17 -5
- package/dist/src/code-refs.js +246 -65
- package/dist/src/context.d.ts +3 -0
- package/dist/src/document-formats.d.ts +7 -0
- package/dist/src/document-formats.js +39 -0
- package/dist/src/extensions/alert-marker.d.ts +25 -0
- package/dist/src/extensions/alert-marker.js +55 -0
- package/dist/src/external-documents.d.ts +56 -0
- package/dist/src/external-documents.js +404 -0
- package/dist/src/external-sources.d.ts +136 -0
- package/dist/src/external-sources.js +1098 -0
- package/dist/src/fork/antigravity-hook.d.ts +47 -0
- package/dist/src/fork/antigravity-hook.js +424 -0
- package/dist/src/fork/antigravity-init.d.ts +17 -0
- package/dist/src/fork/antigravity-init.js +124 -0
- package/dist/src/fork/frontmatter-fields.d.ts +36 -0
- package/dist/src/fork/frontmatter-fields.js +23 -0
- package/dist/src/format.d.ts +5 -2
- package/dist/src/format.js +8 -3
- package/dist/src/graph-export.js +1 -1
- package/dist/src/lattice-model.d.ts +77 -0
- package/dist/src/lattice-model.js +464 -0
- package/dist/src/lattice.d.ts +9 -104
- package/dist/src/lattice.js +69 -516
- package/dist/src/markdown-analysis-cache.d.ts +20 -0
- package/dist/src/markdown-analysis-cache.js +139 -0
- package/dist/src/markdown-analysis-worker.d.ts +16 -0
- package/dist/src/markdown-analysis-worker.js +33 -0
- package/dist/src/markdown-analysis.d.ts +50 -0
- package/dist/src/markdown-analysis.js +139 -0
- package/dist/src/markdown-analyzer-loader.d.ts +4 -0
- package/dist/src/markdown-analyzer-loader.js +13 -0
- package/dist/src/markdown-validation.d.ts +25 -0
- package/dist/src/markdown-validation.js +104 -0
- package/dist/src/mcp/server.js +27 -11
- package/dist/src/parser-cache.d.ts +17 -0
- package/dist/src/parser-cache.js +65 -0
- package/dist/src/parser-import.d.ts +8 -0
- package/dist/src/parser-import.js +1 -0
- package/dist/src/parser.js +80 -5
- package/dist/src/path.d.ts +2 -0
- package/dist/src/path.js +4 -0
- package/dist/src/profiler.d.ts +16 -0
- package/dist/src/profiler.js +81 -0
- package/dist/src/project-analysis.d.ts +59 -0
- package/dist/src/project-analysis.js +233 -0
- package/dist/src/project-discovery.d.ts +6 -0
- package/dist/src/project-discovery.js +30 -0
- package/dist/src/repository-path.d.ts +11 -0
- package/dist/src/repository-path.js +52 -0
- package/dist/src/search/db.d.ts +1 -1
- package/dist/src/search/db.js +2 -2
- package/dist/src/search/embedder.d.ts +2 -1
- package/dist/src/search/embedder.js +6 -5
- package/dist/src/search/index.d.ts +2 -1
- package/dist/src/search/index.js +10 -8
- package/dist/src/search/query.d.ts +20 -0
- package/dist/src/search/query.js +67 -0
- package/dist/src/search/search.d.ts +3 -1
- package/dist/src/search/search.js +5 -2
- package/dist/src/source-formats.d.ts +7 -0
- package/dist/src/source-formats.js +26 -0
- package/dist/src/source-parser.d.ts +44 -4
- package/dist/src/source-parser.js +494 -57
- package/dist/src/view/client/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/dist/src/view/client/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/dist/src/view/client/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/dist/src/view/client/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/dist/src/view/client/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/dist/src/view/client/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/dist/src/view/client/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/dist/src/view/client/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/dist/src/view/client/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/dist/src/view/client/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/dist/src/view/client/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/dist/src/view/client/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/dist/src/view/client/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/dist/src/view/client/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/dist/src/view/client/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/dist/src/view/client/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/dist/src/view/client/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/dist/src/view/client/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/dist/src/view/client/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/dist/src/view/client/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/dist/src/view/client/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/dist/src/view/client/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/dist/src/view/client/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/dist/src/view/client/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/dist/src/view/client/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/dist/src/view/client/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/dist/src/view/client/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/dist/src/view/client/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/dist/src/view/client/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/dist/src/view/client/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/dist/src/view/client/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/dist/src/view/client/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/dist/src/view/client/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/dist/src/view/client/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/dist/src/view/client/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/dist/src/view/client/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/dist/src/view/client/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/dist/src/view/client/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/dist/src/view/client/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/dist/src/view/client/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/dist/src/view/client/assets/MarkdownEditor-B7quqPb8.js +28 -0
- package/dist/src/view/client/assets/OrbitControls-DwcpQNja.js +1 -0
- package/dist/src/view/client/assets/STLLoader-CLWTKt5i.js +1 -0
- package/dist/src/view/client/assets/abnfDiagram-VCTEODGH-V7-T6qsl.js +1 -0
- package/dist/src/view/client/assets/arc-BnwR8Bu7.js +1 -0
- package/dist/src/view/client/assets/architectureDiagram-5GKGNRK7-u97vDna9.js +36 -0
- package/dist/src/view/client/assets/blockDiagram-I7D4REHJ-DETl8e_H.js +129 -0
- package/dist/src/view/client/assets/c4Diagram-7LVT6UL2-DJRAT1Ve.js +38 -0
- package/dist/src/view/client/assets/channel-BwQbnWCN.js +1 -0
- package/dist/src/view/client/assets/chunk-2Q5K7J3B-C0TmOC1Q.js +1 -0
- package/dist/src/view/client/assets/chunk-5VM5RSS4-dfBOrYE6.js +15 -0
- package/dist/src/view/client/assets/chunk-F27PBJKO-DAnks99K.js +1 -0
- package/dist/src/view/client/assets/chunk-IMKFNOWR-CPjB07if.js +231 -0
- package/dist/src/view/client/assets/chunk-JWPE2WC7-VvU_gZI6.js +1 -0
- package/dist/src/view/client/assets/chunk-POPQ4Y6H-DDUbVF55.js +1 -0
- package/dist/src/view/client/assets/chunk-SVP7TREG-BHROkr6D.js +88 -0
- package/dist/src/view/client/assets/chunk-TICWLB2K-95PzGIOg.js +206 -0
- package/dist/src/view/client/assets/chunk-XXDRQBXY-MdmuLhIs.js +1 -0
- package/dist/src/view/client/assets/classDiagram-ZZMXUADV-HvQKkIS-.js +1 -0
- package/dist/src/view/client/assets/classDiagram-v2-VYDZK3BY-HvQKkIS-.js +1 -0
- package/dist/src/view/client/assets/cose-bilkent-JH36ORCC-CauUf_rN.js +1 -0
- package/dist/src/view/client/assets/cynefin-OW5HDTMX-CeA8IKWo.js +166 -0
- package/dist/src/view/client/assets/cynefinDiagram-5FMLGOSQ-L1rtcT8l.js +62 -0
- package/dist/src/view/client/assets/cytoscape.esm-DzSz-X2X.js +321 -0
- package/dist/src/view/client/assets/dagre-GXQ25YYZ-DLQwTa3H.js +4 -0
- package/dist/src/view/client/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/dist/src/view/client/assets/diagram-S7CK7UJ4-kbKdIyXG.js +30 -0
- package/dist/src/view/client/assets/diagram-UQ7AKVKN-kmzp92RC.js +41 -0
- package/dist/src/view/client/assets/diagram-VSXAHHWV-EXJTU8qq.js +3 -0
- package/dist/src/view/client/assets/diagram-VX7I27RA-DAiIyMCG.js +24 -0
- package/dist/src/view/client/assets/diagram-Z3DM3KII-G9wz50ZV.js +24 -0
- package/dist/src/view/client/assets/ebnfDiagram-PWID7BFC-D2tVRCNA.js +1 -0
- package/dist/src/view/client/assets/erDiagram-RLTQ6QDP-C925J5A3.js +99 -0
- package/dist/src/view/client/assets/flowDiagram-HODETNUW-DzoCa-Nq.js +168 -0
- package/dist/src/view/client/assets/ganttDiagram-EL5Y4UJY-D2ebbg4p.js +292 -0
- package/dist/src/view/client/assets/gitGraphDiagram-WWUBYQGX-B4QratYy.js +106 -0
- package/dist/src/view/client/assets/index-BnENo9bV.js +1 -0
- package/dist/src/view/client/assets/index-D0ajr_Ja.css +1 -0
- package/dist/src/view/client/assets/index-KZcnpXJH.js +321 -0
- package/dist/src/view/client/assets/infoDiagram-27XIBGKW-DL4mtHkt.js +2 -0
- package/dist/src/view/client/assets/init-Gi6I4Gst.js +1 -0
- package/dist/src/view/client/assets/ishikawaDiagram-5VMMS53U-CW4mto2d.js +70 -0
- package/dist/src/view/client/assets/journeyDiagram-3NMN7TZE-8R4eacFW.js +139 -0
- package/dist/src/view/client/assets/kanban-definition-UXKFOSKX-Cy9ioXEJ.js +89 -0
- package/dist/src/view/client/assets/katex-HP8lGamR.js +257 -0
- package/dist/src/view/client/assets/layout-C0MFuhth.js +1 -0
- package/dist/src/view/client/assets/linear-Dv3N4dQR.js +1 -0
- package/dist/src/view/client/assets/logo-small-RR_KBOAT.svg +149 -0
- package/dist/src/view/client/assets/maplibre-gl-DnqZgfUS.js +800 -0
- package/dist/src/view/client/assets/mermaid.core-bds9A7e2.js +310 -0
- package/dist/src/view/client/assets/mindmap-definition-YA3MSWOX-DjmGW355.js +96 -0
- package/dist/src/view/client/assets/ordinal-Cboi1Yqb.js +1 -0
- package/dist/src/view/client/assets/pegDiagram-XKGWAZYB-BWsZp0ab.js +1 -0
- package/dist/src/view/client/assets/pieDiagram-E7YTZNPT-CCH2gGR-.js +39 -0
- package/dist/src/view/client/assets/quadrantDiagram-AXDQQJYC-CjocPJFz.js +7 -0
- package/dist/src/view/client/assets/railroadDiagram-O6MQD6OU-BjLeb2KF.js +1 -0
- package/dist/src/view/client/assets/requirementDiagram-BXWQKSXE-Ct6AoXRr.js +84 -0
- package/dist/src/view/client/assets/sankeyDiagram-P5KCCOFB-CIpgGN_w.js +40 -0
- package/dist/src/view/client/assets/sequenceDiagram-WJ2MYXX4-DrmLFj6w.js +162 -0
- package/dist/src/view/client/assets/sizeCapture-INFHLROL-BdFMe2Lr.js +1 -0
- package/dist/src/view/client/assets/stateDiagram-D77RDMKH-Zd9F1goW.js +1 -0
- package/dist/src/view/client/assets/stateDiagram-v2-MP3YSRHH-ByIKCftl.js +1 -0
- package/dist/src/view/client/assets/swimlanes-42K2YHIH-a7VFwi00.js +1 -0
- package/dist/src/view/client/assets/swimlanesDiagram-VR7AAH4N-CzxaUHB-.js +8 -0
- package/dist/src/view/client/assets/three.module-PDSP0dbZ.js +4108 -0
- package/dist/src/view/client/assets/timeline-definition-24CTP7MA-BExPu6X5.js +120 -0
- package/dist/src/view/client/assets/vennDiagram-4TSXK5OY-PC1MU3si.js +34 -0
- package/dist/src/view/client/assets/wardleyDiagram-VM6X3IG4-DNWqU3fa.js +78 -0
- package/dist/src/view/client/assets/xychartDiagram-S5SC5T6Z-E1uA28_D.js +7 -0
- package/dist/src/view/client/index.html +2 -2
- package/dist/src/view/client-shell.d.ts +2 -0
- package/dist/src/view/client-shell.js +5 -0
- package/dist/src/view/diagnostics.d.ts +3 -2
- package/dist/src/view/diagnostics.js +49 -58
- package/dist/src/view/document-edit.d.ts +8 -0
- package/dist/src/view/document-edit.js +25 -0
- package/dist/src/view/document-route.d.ts +12 -0
- package/dist/src/view/document-route.js +93 -0
- package/dist/src/view/document-tree.d.ts +18 -0
- package/dist/src/view/document-tree.js +139 -0
- package/dist/src/view/external-document-tree.d.ts +3 -0
- package/dist/src/view/external-document-tree.js +674 -0
- package/dist/src/view/git-diff.js +187 -0
- package/dist/src/view/git.js +1 -1
- package/dist/src/view/graph.d.ts +3 -2
- package/dist/src/view/graph.js +94 -7
- package/dist/src/view/highlight.d.ts +10 -2
- package/dist/src/view/highlight.js +4540 -1360
- package/dist/src/view/markdown.d.ts +3 -2
- package/dist/src/view/markdown.js +214 -53
- package/dist/src/view/protocol.d.ts +59 -6
- package/dist/src/view/references.d.ts +10 -25
- package/dist/src/view/references.js +117 -107
- package/dist/src/view/repository.d.ts +13 -3
- package/dist/src/view/repository.js +234 -15
- package/dist/src/view/search.d.ts +1 -1
- package/dist/src/view/search.js +3 -7
- package/dist/src/view/server.d.ts +11 -2
- package/dist/src/view/server.js +212 -70
- package/dist/src/view/source-target.js +5 -8
- package/dist/src/view/static-build.d.ts +1 -0
- package/dist/src/view/static-build.js +245 -42
- package/dist/src/view/static-protocol.d.ts +19 -2
- package/dist/src/view/store.d.ts +16 -3
- package/dist/src/view/store.js +228 -36
- package/dist/src/view/table-of-contents.d.ts +2 -2
- package/dist/src/view/table-of-contents.js +2 -22
- package/dist/src/walk.d.ts +5 -3
- package/dist/src/walk.js +102 -8
- package/dist/vendor/lat.md-server/cli.d.ts +2 -0
- package/dist/vendor/lat.md-server/cli.js +12 -0
- package/dist/vendor/lat.md-server/index.d.ts +38 -0
- package/dist/vendor/lat.md-server/index.js +128 -0
- package/package.json +52 -12
- package/plugins/lat-md/README.md +61 -0
- package/plugins/lat-md/hooks.json +38 -0
- package/plugins/lat-md/plugin.json +9 -0
- package/plugins/lat-md/rules/AGENTS.md +118 -0
- package/plugins/lat-md/skills/lat-md/SKILL.md +178 -0
- package/plugins/lat-md/skills/lat-md-conventions/SKILL.md +182 -0
- package/templates/AGENTS.md +15 -3
- package/templates/cursor-rules.md +12 -3
- package/templates/skill/SKILL.md +22 -1
- package/dist/src/view/client/assets/index-C3iaZyYT.css +0 -1
- package/dist/src/view/client/assets/index-KWi67IMz.js +0 -334
- 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 {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
106
|
-
|
|
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.
|
|
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
|
|
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
|
-
})).
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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 {
|
|
5
|
-
import {
|
|
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 {
|
|
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
|
-
|
|
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
|
-
!
|
|
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
|
-
|
|
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 = {
|