@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,5 +1,6 @@
|
|
|
1
|
-
import { type Section } from '../lattice.js';
|
|
1
|
+
import { type Section } from '../lattice-model.js';
|
|
2
|
+
import type { ExternalResolver } from '../external-sources.js';
|
|
2
3
|
import type { ViewDocumentError } from './protocol.js';
|
|
3
4
|
import type { ViewCodeReferenceFile, ViewParsedMarkdownFile } from './references.js';
|
|
4
5
|
/** Build per-document validation errors entirely from the current view snapshot. */
|
|
5
|
-
export declare function buildViewDiagnostics(markdownFiles: Iterable<ViewParsedMarkdownFile>, codeFiles: Iterable<ViewCodeReferenceFile>, allSections: Section[], projectRoot: string): Promise<ReadonlyMap<string, readonly ViewDocumentError[]>>;
|
|
6
|
+
export declare function buildViewDiagnostics(markdownFiles: Iterable<ViewParsedMarkdownFile>, codeFiles: Iterable<ViewCodeReferenceFile>, allSections: Section[], projectRoot: string, external?: ExternalResolver, latDir?: string): Promise<ReadonlyMap<string, readonly ViewDocumentError[]>>;
|
|
@@ -1,41 +1,10 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import { extname, relative, resolve, dirname } from 'node:path';
|
|
3
|
-
import { ambiguousRefMessage,
|
|
4
|
-
import { buildFileIndex, buildSectionSlugIndex, flattenSections,
|
|
5
|
-
import {
|
|
6
|
-
import { toPosix } from '../
|
|
7
|
-
|
|
8
|
-
function decodeLinkPart(value) {
|
|
9
|
-
try {
|
|
10
|
-
return decodeURIComponent(value);
|
|
11
|
-
}
|
|
12
|
-
catch {
|
|
13
|
-
return value;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
function localLinkTarget(url) {
|
|
17
|
-
const value = url.trim();
|
|
18
|
-
if (value.startsWith('/'))
|
|
19
|
-
return null;
|
|
20
|
-
const windowsDrivePath = /^[a-zA-Z]:\\/.test(value);
|
|
21
|
-
if (!windowsDrivePath && /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(value)) {
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
24
|
-
const queryAt = value.indexOf('?');
|
|
25
|
-
const fragmentAt = value.indexOf('#');
|
|
26
|
-
const pathEnd = Math.min(queryAt === -1 ? value.length : queryAt, fragmentAt === -1 ? value.length : fragmentAt);
|
|
27
|
-
const rawPath = value.slice(0, pathEnd);
|
|
28
|
-
const path = rawPath === '' ? null : decodeLinkPart(rawPath);
|
|
29
|
-
const fragment = fragmentAt === -1 ? null : decodeLinkPart(value.slice(fragmentAt + 1));
|
|
30
|
-
if (rawPath === '' && fragment === null)
|
|
31
|
-
return null;
|
|
32
|
-
if (path?.includes('\\'))
|
|
33
|
-
return { kind: 'invalid-backslash' };
|
|
34
|
-
return { kind: 'target', path, fragment };
|
|
35
|
-
}
|
|
36
|
-
function bodyTextLength(body) {
|
|
37
|
-
return body.replace(/\[\[[^\]]*\]\]/g, '').length;
|
|
38
|
-
}
|
|
3
|
+
import { ambiguousRefMessage, repositoryRefError } from '../cli/check.js';
|
|
4
|
+
import { buildFileIndex, buildSectionSlugIndex, flattenSections, resolveRef, } from '../lattice-model.js';
|
|
5
|
+
import { SourceParserRuntime } from '../source-parser.js';
|
|
6
|
+
import { toPosix } from '../path.js';
|
|
7
|
+
import { parseLocalMarkdownTarget } from '../markdown-validation.js';
|
|
39
8
|
function error(line, target, message, options = {}) {
|
|
40
9
|
return {
|
|
41
10
|
anchor: options.anchor || `user-content-markdown-error-${line}`,
|
|
@@ -64,12 +33,11 @@ function addError(errors, path, diagnostic) {
|
|
|
64
33
|
errors.set(path, fileErrors);
|
|
65
34
|
}
|
|
66
35
|
async function markdownLinkError(file, filesByAbsolutePath, link, projectRoot) {
|
|
67
|
-
const target =
|
|
36
|
+
const target = parseLocalMarkdownTarget(link.url);
|
|
68
37
|
if (target === null)
|
|
69
38
|
return null;
|
|
70
|
-
if (target.kind === 'invalid-backslash')
|
|
71
|
-
return
|
|
72
|
-
}
|
|
39
|
+
if (target.kind === 'invalid-backslash')
|
|
40
|
+
return null;
|
|
73
41
|
const absolutePath = target.path
|
|
74
42
|
? resolve(dirname(file.absolutePath), target.path)
|
|
75
43
|
: file.absolutePath;
|
|
@@ -90,8 +58,8 @@ async function markdownLinkError(file, filesByAbsolutePath, link, projectRoot) {
|
|
|
90
58
|
return error(link.line, link.url, `broken link (${link.url}) — heading "#${target.fragment}" not found in "${destination.path}"`);
|
|
91
59
|
}
|
|
92
60
|
/** Build per-document validation errors entirely from the current view snapshot. */
|
|
93
|
-
export async function buildViewDiagnostics(markdownFiles, codeFiles, allSections, projectRoot) {
|
|
94
|
-
|
|
61
|
+
export async function buildViewDiagnostics(markdownFiles, codeFiles, allSections, projectRoot, external, latDir = resolve(projectRoot, 'lat.md')) {
|
|
62
|
+
const sourceParserRuntime = new SourceParserRuntime();
|
|
95
63
|
const files = [...markdownFiles];
|
|
96
64
|
const errors = new Map();
|
|
97
65
|
const sections = flattenSections(allSections);
|
|
@@ -99,25 +67,58 @@ export async function buildViewDiagnostics(markdownFiles, codeFiles, allSections
|
|
|
99
67
|
const fileIndex = buildFileIndex(allSections);
|
|
100
68
|
const slugIndex = buildSectionSlugIndex(allSections);
|
|
101
69
|
const filesByAbsolutePath = new Map(files.map((file) => [resolve(file.absolutePath), file]));
|
|
70
|
+
if (external) {
|
|
71
|
+
const root = files.find((file) => file.path === 'lat.md') ?? files[0];
|
|
72
|
+
if (root) {
|
|
73
|
+
for (const configError of external.snapshot.errors) {
|
|
74
|
+
addError(errors, root.path, error(1, 'external-sources', configError.message, {
|
|
75
|
+
marker: 'line',
|
|
76
|
+
}));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
102
80
|
for (const file of files) {
|
|
81
|
+
for (const diagnostic of file.diagnostics) {
|
|
82
|
+
addError(errors, file.path, error(diagnostic.line, diagnostic.target, diagnostic.message, {
|
|
83
|
+
anchor: diagnostic.anchor,
|
|
84
|
+
marker: diagnostic.marker,
|
|
85
|
+
}));
|
|
86
|
+
}
|
|
103
87
|
for (const ref of file.wikiRefs) {
|
|
88
|
+
if (external) {
|
|
89
|
+
try {
|
|
90
|
+
if (external.parse(ref.target)) {
|
|
91
|
+
await external.resolve(ref.target);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch (externalError) {
|
|
96
|
+
addError(errors, file.path, error(ref.line, ref.target, externalError.message));
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
const unknownExternal = external.unknownTargetMessage(ref.target);
|
|
100
|
+
if (unknownExternal) {
|
|
101
|
+
addError(errors, file.path, error(ref.line, ref.target, unknownExternal));
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
104
105
|
const resolved = resolveRef(ref.target, sectionIds, fileIndex, slugIndex);
|
|
105
106
|
let message = null;
|
|
106
107
|
if (resolved.ambiguous) {
|
|
107
108
|
message = ambiguousRefMessage(ref.target, resolved.ambiguous, resolved.suggested);
|
|
108
109
|
}
|
|
109
110
|
else if (!sectionIds.has(resolved.resolved.toLowerCase())) {
|
|
110
|
-
message = await
|
|
111
|
+
message = await repositoryRefError(ref.target, projectRoot, {
|
|
112
|
+
latDir,
|
|
113
|
+
runtime: sourceParserRuntime,
|
|
114
|
+
});
|
|
111
115
|
}
|
|
112
116
|
if (message)
|
|
113
117
|
addError(errors, file.path, error(ref.line, ref.target, message));
|
|
114
118
|
}
|
|
115
119
|
const links = new Map();
|
|
116
120
|
for (const link of file.validationLinks) {
|
|
117
|
-
if ('identifier' in link) {
|
|
118
|
-
addError(errors, file.path, error(link.line, link.identifier, `undefined ${link.kind === 'imageReference' ? 'image' : 'link'} reference (${link.source}) — definition "[${link.identifier}]" not found`, { marker: 'line' }));
|
|
119
|
-
}
|
|
120
|
-
else if (link.kind !== 'definition') {
|
|
121
|
+
if (!('identifier' in link) && link.kind !== 'definition') {
|
|
121
122
|
links.set(`${link.kind}:${link.line}:${link.url}`, {
|
|
122
123
|
kind: link.kind,
|
|
123
124
|
line: link.line,
|
|
@@ -133,16 +134,6 @@ export async function buildViewDiagnostics(markdownFiles, codeFiles, allSections
|
|
|
133
134
|
if (diagnostic)
|
|
134
135
|
addError(errors, file.path, diagnostic);
|
|
135
136
|
}
|
|
136
|
-
for (const section of flattenSections(file.sections)) {
|
|
137
|
-
if (!section.firstParagraph) {
|
|
138
|
-
addError(errors, file.path, error(section.startLine, section.id, `section "${section.id}" has no leading paragraph. Every section must start with a brief overview (≤${MAX_BODY_LENGTH} chars).`, { anchor: section.githubSlug, marker: 'heading' }));
|
|
139
|
-
continue;
|
|
140
|
-
}
|
|
141
|
-
const length = bodyTextLength(section.firstParagraph);
|
|
142
|
-
if (length > MAX_BODY_LENGTH) {
|
|
143
|
-
addError(errors, file.path, error(section.startLine, section.id, `section "${section.id}" leading paragraph is ${length} characters (max ${MAX_BODY_LENGTH}, excluding [[wiki links]]).`, { anchor: section.githubSlug, marker: 'heading' }));
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
137
|
}
|
|
147
138
|
const mentionedSections = new Set();
|
|
148
139
|
for (const file of codeFiles) {
|
|
@@ -155,7 +146,7 @@ export async function buildViewDiagnostics(markdownFiles, codeFiles, allSections
|
|
|
155
146
|
}
|
|
156
147
|
}
|
|
157
148
|
for (const file of files) {
|
|
158
|
-
if (!
|
|
149
|
+
if (!file.frontmatter.requireCodeMention)
|
|
159
150
|
continue;
|
|
160
151
|
for (const section of flattenSections(file.sections)) {
|
|
161
152
|
if (section.children.length === 0 &&
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare class ViewDocumentConflictError extends Error {
|
|
2
|
+
}
|
|
3
|
+
export type AppliedDocumentEdit = {
|
|
4
|
+
content: string;
|
|
5
|
+
merged: boolean;
|
|
6
|
+
};
|
|
7
|
+
/** Apply the user's base-to-edited patch to the latest content on disk. */
|
|
8
|
+
export declare function applyDocumentEdit(baseContent: string, editedContent: string, currentContent: string): AppliedDocumentEdit;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { applyPatch, createPatch } from 'diff';
|
|
2
|
+
const PATCH_CONTEXT_LINES = 3;
|
|
3
|
+
const PATCH_FUZZ_LINES = 2;
|
|
4
|
+
export class ViewDocumentConflictError extends Error {
|
|
5
|
+
}
|
|
6
|
+
/** Apply the user's base-to-edited patch to the latest content on disk. */
|
|
7
|
+
export function applyDocumentEdit(baseContent, editedContent, currentContent) {
|
|
8
|
+
if (baseContent === editedContent) {
|
|
9
|
+
return { content: currentContent, merged: currentContent !== baseContent };
|
|
10
|
+
}
|
|
11
|
+
if (currentContent === editedContent) {
|
|
12
|
+
return { content: currentContent, merged: currentContent !== baseContent };
|
|
13
|
+
}
|
|
14
|
+
if (currentContent === baseContent) {
|
|
15
|
+
return { content: editedContent, merged: false };
|
|
16
|
+
}
|
|
17
|
+
const patch = createPatch('document.md', baseContent, editedContent, undefined, undefined, { context: PATCH_CONTEXT_LINES });
|
|
18
|
+
const content = applyPatch(currentContent, patch, {
|
|
19
|
+
fuzzFactor: PATCH_FUZZ_LINES,
|
|
20
|
+
});
|
|
21
|
+
if (content === false) {
|
|
22
|
+
throw new ViewDocumentConflictError('Could not save because this file changed in the same area. Your edits are still in the editor.');
|
|
23
|
+
}
|
|
24
|
+
return { content, merged: true };
|
|
25
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Build the canonical, extensionless browser route for a Markdown file. */
|
|
2
|
+
export declare function documentUrl(path: string, fragment?: string): string;
|
|
3
|
+
/** Resolve an extensionless browser route back to its Markdown file path. */
|
|
4
|
+
export declare function documentPath(pathname: string): string | null;
|
|
5
|
+
/** Resolve an explicit `.md` route to the raw Markdown file it requests. */
|
|
6
|
+
export declare function rawDocumentPath(pathname: string): string | null;
|
|
7
|
+
/** Build the browser route for a non-Markdown file stored in the vault. */
|
|
8
|
+
export declare function documentResourceUrl(path: string): string;
|
|
9
|
+
/** Resolve a browser resource route to a safe vault-relative file path. */
|
|
10
|
+
export declare function documentResourcePath(pathname: string): string | null;
|
|
11
|
+
/** Keep relative Markdown-to-Markdown links inside the rendered document UI. */
|
|
12
|
+
export declare function rewriteDocumentLink(value: string, sourcePath: string): string;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
const DOCUMENT_PREFIX = '/docs/';
|
|
2
|
+
const RESOURCE_PREFIX = '/resources/';
|
|
3
|
+
const MARKDOWN_EXTENSION = '.md';
|
|
4
|
+
function encodePath(path) {
|
|
5
|
+
return path.split('/').map(encodeURIComponent).join('/');
|
|
6
|
+
}
|
|
7
|
+
function decodeDocumentPath(pathname) {
|
|
8
|
+
if (!pathname.startsWith(DOCUMENT_PREFIX))
|
|
9
|
+
return null;
|
|
10
|
+
try {
|
|
11
|
+
const path = pathname
|
|
12
|
+
.slice(DOCUMENT_PREFIX.length)
|
|
13
|
+
.split('/')
|
|
14
|
+
.map(decodeURIComponent)
|
|
15
|
+
.join('/');
|
|
16
|
+
return path && !path.endsWith('/') ? path : null;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/** Build the canonical, extensionless browser route for a Markdown file. */
|
|
23
|
+
export function documentUrl(path, fragment = '') {
|
|
24
|
+
const routePath = path.endsWith(MARKDOWN_EXTENSION)
|
|
25
|
+
? path.slice(0, -MARKDOWN_EXTENSION.length)
|
|
26
|
+
: path;
|
|
27
|
+
return `${DOCUMENT_PREFIX}${encodePath(routePath)}${fragment ? `#${encodeURIComponent(fragment)}` : ''}`;
|
|
28
|
+
}
|
|
29
|
+
/** Resolve an extensionless browser route back to its Markdown file path. */
|
|
30
|
+
export function documentPath(pathname) {
|
|
31
|
+
const path = decodeDocumentPath(pathname);
|
|
32
|
+
if (!path || path.toLowerCase().endsWith(MARKDOWN_EXTENSION))
|
|
33
|
+
return null;
|
|
34
|
+
return `${path}${MARKDOWN_EXTENSION}`;
|
|
35
|
+
}
|
|
36
|
+
/** Resolve an explicit `.md` route to the raw Markdown file it requests. */
|
|
37
|
+
export function rawDocumentPath(pathname) {
|
|
38
|
+
const path = decodeDocumentPath(pathname);
|
|
39
|
+
return path?.toLowerCase().endsWith(MARKDOWN_EXTENSION) ? path : null;
|
|
40
|
+
}
|
|
41
|
+
/** Build the browser route for a non-Markdown file stored in the vault. */
|
|
42
|
+
export function documentResourceUrl(path) {
|
|
43
|
+
return `${RESOURCE_PREFIX}${encodePath(path)}`;
|
|
44
|
+
}
|
|
45
|
+
/** Resolve a browser resource route to a safe vault-relative file path. */
|
|
46
|
+
export function documentResourcePath(pathname) {
|
|
47
|
+
if (!pathname.startsWith(RESOURCE_PREFIX))
|
|
48
|
+
return null;
|
|
49
|
+
try {
|
|
50
|
+
const segments = pathname
|
|
51
|
+
.slice(RESOURCE_PREFIX.length)
|
|
52
|
+
.split('/')
|
|
53
|
+
.map(decodeURIComponent);
|
|
54
|
+
if (segments.length === 0 ||
|
|
55
|
+
segments.some((segment) => !segment ||
|
|
56
|
+
segment === '.' ||
|
|
57
|
+
segment === '..' ||
|
|
58
|
+
segment.includes('\\'))) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
return segments.join('/');
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/** Keep relative Markdown-to-Markdown links inside the rendered document UI. */
|
|
68
|
+
export function rewriteDocumentLink(value, sourcePath) {
|
|
69
|
+
if (!value ||
|
|
70
|
+
value.startsWith('#') ||
|
|
71
|
+
value.startsWith('/') ||
|
|
72
|
+
value.startsWith('//') ||
|
|
73
|
+
/^[a-z][a-z\d+.-]*:/i.test(value)) {
|
|
74
|
+
return value;
|
|
75
|
+
}
|
|
76
|
+
let resolved;
|
|
77
|
+
try {
|
|
78
|
+
resolved = new URL(value, `http://lat.local${DOCUMENT_PREFIX}${encodePath(sourcePath)}`);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return value;
|
|
82
|
+
}
|
|
83
|
+
if (resolved.origin !== 'http://lat.local')
|
|
84
|
+
return value;
|
|
85
|
+
const path = rawDocumentPath(resolved.pathname);
|
|
86
|
+
if (path) {
|
|
87
|
+
return `${documentUrl(path)}${resolved.search}${resolved.hash}`;
|
|
88
|
+
}
|
|
89
|
+
const resourcePath = decodeDocumentPath(resolved.pathname);
|
|
90
|
+
if (!resourcePath)
|
|
91
|
+
return value;
|
|
92
|
+
return `${documentResourceUrl(resourcePath)}${resolved.search}${resolved.hash}`;
|
|
93
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ViewDocumentElement, ViewDocumentTree } from './protocol.js';
|
|
2
|
+
type HastLikeNode = {
|
|
3
|
+
type?: unknown;
|
|
4
|
+
value?: unknown;
|
|
5
|
+
tagName?: unknown;
|
|
6
|
+
properties?: unknown;
|
|
7
|
+
children?: unknown;
|
|
8
|
+
};
|
|
9
|
+
/** Strip a sanitized HAST tree to the stable JSON wire representation. */
|
|
10
|
+
export declare function toViewDocumentTree(tree: HastLikeNode): ViewDocumentTree;
|
|
11
|
+
export declare function documentTreeUrls(tree: ViewDocumentTree): string[];
|
|
12
|
+
export declare function visitDocumentElements(tree: ViewDocumentTree, visitor: (element: ViewDocumentElement) => void): void;
|
|
13
|
+
/** Apply parser-neutral presentation to links that leave the current site. */
|
|
14
|
+
export declare function decorateExternalSiteLinks(tree: ViewDocumentTree): ViewDocumentTree;
|
|
15
|
+
/** Clone a document tree while structurally rewriting navigable URLs. */
|
|
16
|
+
export declare function rewriteDocumentTreeUrls(tree: ViewDocumentTree, rewrite: (value: string) => string): ViewDocumentTree;
|
|
17
|
+
export declare function textDocumentTree(value: string): ViewDocumentTree;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
function propertyValue(value) {
|
|
2
|
+
if (value === null ||
|
|
3
|
+
typeof value === 'string' ||
|
|
4
|
+
typeof value === 'number' ||
|
|
5
|
+
typeof value === 'boolean') {
|
|
6
|
+
return value;
|
|
7
|
+
}
|
|
8
|
+
if (Array.isArray(value)) {
|
|
9
|
+
return value.filter((item) => typeof item === 'string' || typeof item === 'number');
|
|
10
|
+
}
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
function documentNode(node) {
|
|
14
|
+
if (node.type === 'text' && typeof node.value === 'string') {
|
|
15
|
+
return { type: 'text', value: node.value };
|
|
16
|
+
}
|
|
17
|
+
if (node.type !== 'element' || typeof node.tagName !== 'string')
|
|
18
|
+
return null;
|
|
19
|
+
const properties = {};
|
|
20
|
+
if (node.properties && typeof node.properties === 'object') {
|
|
21
|
+
for (const [name, value] of Object.entries(node.properties)) {
|
|
22
|
+
const normalized = propertyValue(value);
|
|
23
|
+
if (normalized !== undefined)
|
|
24
|
+
properties[name] = normalized;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const children = Array.isArray(node.children)
|
|
28
|
+
? node.children
|
|
29
|
+
.map((child) => documentNode(child))
|
|
30
|
+
.filter((child) => child !== null)
|
|
31
|
+
: [];
|
|
32
|
+
return {
|
|
33
|
+
type: 'element',
|
|
34
|
+
tagName: node.tagName,
|
|
35
|
+
properties,
|
|
36
|
+
children,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/** Strip a sanitized HAST tree to the stable JSON wire representation. */
|
|
40
|
+
export function toViewDocumentTree(tree) {
|
|
41
|
+
if (tree.type !== 'root' || !Array.isArray(tree.children)) {
|
|
42
|
+
throw new Error('Document adapter did not produce a root node');
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
version: 1,
|
|
46
|
+
type: 'root',
|
|
47
|
+
children: tree.children
|
|
48
|
+
.map((child) => documentNode(child))
|
|
49
|
+
.filter((child) => child !== null),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export function documentTreeUrls(tree) {
|
|
53
|
+
const urls = [];
|
|
54
|
+
visitDocumentElements(tree, (element) => {
|
|
55
|
+
for (const name of ['href', 'src']) {
|
|
56
|
+
const value = element.properties[name];
|
|
57
|
+
if (typeof value === 'string')
|
|
58
|
+
urls.push(value);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
return urls;
|
|
62
|
+
}
|
|
63
|
+
export function visitDocumentElements(tree, visitor) {
|
|
64
|
+
const visit = (node) => {
|
|
65
|
+
if (node.type !== 'element')
|
|
66
|
+
return;
|
|
67
|
+
visitor(node);
|
|
68
|
+
for (const child of node.children)
|
|
69
|
+
visit(child);
|
|
70
|
+
};
|
|
71
|
+
for (const child of tree.children)
|
|
72
|
+
visit(child);
|
|
73
|
+
}
|
|
74
|
+
function classNames(value) {
|
|
75
|
+
if (Array.isArray(value))
|
|
76
|
+
return value.map(String);
|
|
77
|
+
return typeof value === 'string' ? value.split(/\s+/).filter(Boolean) : [];
|
|
78
|
+
}
|
|
79
|
+
function isExternalSiteUrl(value) {
|
|
80
|
+
return /^(?:https?:)?\/\//i.test(value);
|
|
81
|
+
}
|
|
82
|
+
function containsElementTag(element, tagName) {
|
|
83
|
+
return element.children.some((child) => child.type === 'element' &&
|
|
84
|
+
(child.tagName === tagName || containsElementTag(child, tagName)));
|
|
85
|
+
}
|
|
86
|
+
/** Apply parser-neutral presentation to links that leave the current site. */
|
|
87
|
+
export function decorateExternalSiteLinks(tree) {
|
|
88
|
+
visitDocumentElements(tree, (element) => {
|
|
89
|
+
const href = element.properties.href;
|
|
90
|
+
if (element.tagName !== 'a' ||
|
|
91
|
+
typeof href !== 'string' ||
|
|
92
|
+
!isExternalSiteUrl(href)) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const classes = classNames(element.properties.className);
|
|
96
|
+
if (!classes.includes('external-link'))
|
|
97
|
+
classes.push('external-link');
|
|
98
|
+
element.properties.className = classes;
|
|
99
|
+
if (containsElementTag(element, 'img'))
|
|
100
|
+
return;
|
|
101
|
+
const alreadyDecorated = element.children.some((child) => child.type === 'element' &&
|
|
102
|
+
child.tagName === 'span' &&
|
|
103
|
+
classNames(child.properties.className).includes('external-link-icon'));
|
|
104
|
+
if (!alreadyDecorated) {
|
|
105
|
+
element.children.push({
|
|
106
|
+
type: 'element',
|
|
107
|
+
tagName: 'span',
|
|
108
|
+
properties: {
|
|
109
|
+
ariaHidden: 'true',
|
|
110
|
+
className: ['external-link-icon'],
|
|
111
|
+
},
|
|
112
|
+
children: [],
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
return tree;
|
|
117
|
+
}
|
|
118
|
+
/** Clone a document tree while structurally rewriting navigable URLs. */
|
|
119
|
+
export function rewriteDocumentTreeUrls(tree, rewrite) {
|
|
120
|
+
const cloneNode = (node) => {
|
|
121
|
+
if (node.type === 'text')
|
|
122
|
+
return { ...node };
|
|
123
|
+
const properties = { ...node.properties };
|
|
124
|
+
for (const name of ['href', 'src']) {
|
|
125
|
+
const value = properties[name];
|
|
126
|
+
if (typeof value === 'string')
|
|
127
|
+
properties[name] = rewrite(value);
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
...node,
|
|
131
|
+
properties,
|
|
132
|
+
children: node.children.map(cloneNode),
|
|
133
|
+
};
|
|
134
|
+
};
|
|
135
|
+
return { ...tree, children: tree.children.map(cloneNode) };
|
|
136
|
+
}
|
|
137
|
+
export function textDocumentTree(value) {
|
|
138
|
+
return { version: 1, type: 'root', children: [{ type: 'text', value }] };
|
|
139
|
+
}
|