mirror of
https://github.com/jackyzha0/quartz.git
synced 2026-03-24 15:05:42 -05:00
fix type issues
This commit is contained in:
parent
16c4b476e8
commit
388ab1f228
@ -23,10 +23,6 @@
|
|||||||
"check": "tsc --noEmit && npx prettier . --check",
|
"check": "tsc --noEmit && npx prettier . --check",
|
||||||
"format": "npx prettier . --write"
|
"format": "npx prettier . --write"
|
||||||
},
|
},
|
||||||
"engines": {
|
|
||||||
"npm": ">=9.3.1",
|
|
||||||
"node": "20 || >=22"
|
|
||||||
},
|
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"site generator",
|
"site generator",
|
||||||
"ssg",
|
"ssg",
|
||||||
@ -108,5 +104,8 @@
|
|||||||
"workerpool": "^9.1.3",
|
"workerpool": "^9.1.3",
|
||||||
"ws": "^8.17.1",
|
"ws": "^8.17.1",
|
||||||
"yargs": "^17.7.2"
|
"yargs": "^17.7.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@types/mdast": "^4.0.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
61
pnpm-lock.yaml
generated
61
pnpm-lock.yaml
generated
@ -4,6 +4,11 @@ settings:
|
|||||||
autoInstallPeers: true
|
autoInstallPeers: true
|
||||||
excludeLinksFromLockfile: false
|
excludeLinksFromLockfile: false
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
'@types/mdast':
|
||||||
|
specifier: ^4.0.4
|
||||||
|
version: 4.0.4
|
||||||
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@clack/prompts':
|
'@clack/prompts':
|
||||||
specifier: ^0.7.0
|
specifier: ^0.7.0
|
||||||
@ -1117,11 +1122,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-rHusx08LCg92WJxrsM3SPjvLTSvK5C+gealtSuhKbEOcUZfWlwigaFoPLf6Dfxhg4oryN5qP9Sj7zOQ4HYXINw==}
|
resolution: {integrity: sha512-rHusx08LCg92WJxrsM3SPjvLTSvK5C+gealtSuhKbEOcUZfWlwigaFoPLf6Dfxhg4oryN5qP9Sj7zOQ4HYXINw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@types/mdast@4.0.3:
|
/@types/mdast@4.0.4:
|
||||||
resolution: {integrity: sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==}
|
resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/unist': 3.0.2
|
'@types/unist': 3.0.2
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@types/ms@0.7.34:
|
/@types/ms@0.7.34:
|
||||||
resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==}
|
resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==}
|
||||||
@ -1155,7 +1159,6 @@ packages:
|
|||||||
|
|
||||||
/@types/unist@3.0.2:
|
/@types/unist@3.0.2:
|
||||||
resolution: {integrity: sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==}
|
resolution: {integrity: sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==}
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@types/ws@8.5.10:
|
/@types/ws@8.5.10:
|
||||||
resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==}
|
resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==}
|
||||||
@ -2507,7 +2510,7 @@ packages:
|
|||||||
/mdast-util-find-and-replace@3.0.1:
|
/mdast-util-find-and-replace@3.0.1:
|
||||||
resolution: {integrity: sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==}
|
resolution: {integrity: sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/mdast': 4.0.3
|
'@types/mdast': 4.0.4
|
||||||
escape-string-regexp: 5.0.0
|
escape-string-regexp: 5.0.0
|
||||||
unist-util-is: 6.0.0
|
unist-util-is: 6.0.0
|
||||||
unist-util-visit-parents: 6.0.1
|
unist-util-visit-parents: 6.0.1
|
||||||
@ -2516,7 +2519,7 @@ packages:
|
|||||||
/mdast-util-from-markdown@2.0.0:
|
/mdast-util-from-markdown@2.0.0:
|
||||||
resolution: {integrity: sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA==}
|
resolution: {integrity: sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/mdast': 4.0.3
|
'@types/mdast': 4.0.4
|
||||||
'@types/unist': 3.0.2
|
'@types/unist': 3.0.2
|
||||||
decode-named-character-reference: 1.0.2
|
decode-named-character-reference: 1.0.2
|
||||||
devlop: 1.1.0
|
devlop: 1.1.0
|
||||||
@ -2535,7 +2538,7 @@ packages:
|
|||||||
/mdast-util-frontmatter@2.0.1:
|
/mdast-util-frontmatter@2.0.1:
|
||||||
resolution: {integrity: sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA==}
|
resolution: {integrity: sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/mdast': 4.0.3
|
'@types/mdast': 4.0.4
|
||||||
devlop: 1.1.0
|
devlop: 1.1.0
|
||||||
escape-string-regexp: 5.0.0
|
escape-string-regexp: 5.0.0
|
||||||
mdast-util-from-markdown: 2.0.0
|
mdast-util-from-markdown: 2.0.0
|
||||||
@ -2548,7 +2551,7 @@ packages:
|
|||||||
/mdast-util-gfm-autolink-literal@2.0.0:
|
/mdast-util-gfm-autolink-literal@2.0.0:
|
||||||
resolution: {integrity: sha512-FyzMsduZZHSc3i0Px3PQcBT4WJY/X/RCtEJKuybiC6sjPqLv7h1yqAkmILZtuxMSsUyaLUWNp71+vQH2zqp5cg==}
|
resolution: {integrity: sha512-FyzMsduZZHSc3i0Px3PQcBT4WJY/X/RCtEJKuybiC6sjPqLv7h1yqAkmILZtuxMSsUyaLUWNp71+vQH2zqp5cg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/mdast': 4.0.3
|
'@types/mdast': 4.0.4
|
||||||
ccount: 2.0.1
|
ccount: 2.0.1
|
||||||
devlop: 1.1.0
|
devlop: 1.1.0
|
||||||
mdast-util-find-and-replace: 3.0.1
|
mdast-util-find-and-replace: 3.0.1
|
||||||
@ -2558,7 +2561,7 @@ packages:
|
|||||||
/mdast-util-gfm-footnote@2.0.0:
|
/mdast-util-gfm-footnote@2.0.0:
|
||||||
resolution: {integrity: sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ==}
|
resolution: {integrity: sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/mdast': 4.0.3
|
'@types/mdast': 4.0.4
|
||||||
devlop: 1.1.0
|
devlop: 1.1.0
|
||||||
mdast-util-from-markdown: 2.0.0
|
mdast-util-from-markdown: 2.0.0
|
||||||
mdast-util-to-markdown: 2.1.0
|
mdast-util-to-markdown: 2.1.0
|
||||||
@ -2570,7 +2573,7 @@ packages:
|
|||||||
/mdast-util-gfm-strikethrough@2.0.0:
|
/mdast-util-gfm-strikethrough@2.0.0:
|
||||||
resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==}
|
resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/mdast': 4.0.3
|
'@types/mdast': 4.0.4
|
||||||
mdast-util-from-markdown: 2.0.0
|
mdast-util-from-markdown: 2.0.0
|
||||||
mdast-util-to-markdown: 2.1.0
|
mdast-util-to-markdown: 2.1.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@ -2580,7 +2583,7 @@ packages:
|
|||||||
/mdast-util-gfm-table@2.0.0:
|
/mdast-util-gfm-table@2.0.0:
|
||||||
resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==}
|
resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/mdast': 4.0.3
|
'@types/mdast': 4.0.4
|
||||||
devlop: 1.1.0
|
devlop: 1.1.0
|
||||||
markdown-table: 3.0.3
|
markdown-table: 3.0.3
|
||||||
mdast-util-from-markdown: 2.0.0
|
mdast-util-from-markdown: 2.0.0
|
||||||
@ -2592,7 +2595,7 @@ packages:
|
|||||||
/mdast-util-gfm-task-list-item@2.0.0:
|
/mdast-util-gfm-task-list-item@2.0.0:
|
||||||
resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==}
|
resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/mdast': 4.0.3
|
'@types/mdast': 4.0.4
|
||||||
devlop: 1.1.0
|
devlop: 1.1.0
|
||||||
mdast-util-from-markdown: 2.0.0
|
mdast-util-from-markdown: 2.0.0
|
||||||
mdast-util-to-markdown: 2.1.0
|
mdast-util-to-markdown: 2.1.0
|
||||||
@ -2618,7 +2621,7 @@ packages:
|
|||||||
resolution: {integrity: sha512-Tl9GBNeG/AhJnQM221bJR2HPvLOSnLE/T9cJI9tlc6zwQk2nPk/4f0cHkOdEixQPC/j8UtKDdITswvLAy1OZ1w==}
|
resolution: {integrity: sha512-Tl9GBNeG/AhJnQM221bJR2HPvLOSnLE/T9cJI9tlc6zwQk2nPk/4f0cHkOdEixQPC/j8UtKDdITswvLAy1OZ1w==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/hast': 3.0.4
|
'@types/hast': 3.0.4
|
||||||
'@types/mdast': 4.0.3
|
'@types/mdast': 4.0.4
|
||||||
devlop: 1.1.0
|
devlop: 1.1.0
|
||||||
longest-streak: 3.1.0
|
longest-streak: 3.1.0
|
||||||
mdast-util-from-markdown: 2.0.0
|
mdast-util-from-markdown: 2.0.0
|
||||||
@ -2633,7 +2636,7 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@types/estree-jsx': 1.0.5
|
'@types/estree-jsx': 1.0.5
|
||||||
'@types/hast': 3.0.4
|
'@types/hast': 3.0.4
|
||||||
'@types/mdast': 4.0.3
|
'@types/mdast': 4.0.4
|
||||||
devlop: 1.1.0
|
devlop: 1.1.0
|
||||||
mdast-util-from-markdown: 2.0.0
|
mdast-util-from-markdown: 2.0.0
|
||||||
mdast-util-to-markdown: 2.1.0
|
mdast-util-to-markdown: 2.1.0
|
||||||
@ -2646,7 +2649,7 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@types/estree-jsx': 1.0.5
|
'@types/estree-jsx': 1.0.5
|
||||||
'@types/hast': 3.0.4
|
'@types/hast': 3.0.4
|
||||||
'@types/mdast': 4.0.3
|
'@types/mdast': 4.0.4
|
||||||
'@types/unist': 3.0.2
|
'@types/unist': 3.0.2
|
||||||
ccount: 2.0.1
|
ccount: 2.0.1
|
||||||
devlop: 1.1.0
|
devlop: 1.1.0
|
||||||
@ -2666,7 +2669,7 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@types/estree-jsx': 1.0.5
|
'@types/estree-jsx': 1.0.5
|
||||||
'@types/hast': 3.0.4
|
'@types/hast': 3.0.4
|
||||||
'@types/mdast': 4.0.3
|
'@types/mdast': 4.0.4
|
||||||
devlop: 1.1.0
|
devlop: 1.1.0
|
||||||
mdast-util-from-markdown: 2.0.0
|
mdast-util-from-markdown: 2.0.0
|
||||||
mdast-util-to-markdown: 2.1.0
|
mdast-util-to-markdown: 2.1.0
|
||||||
@ -2677,14 +2680,14 @@ packages:
|
|||||||
/mdast-util-newline-to-break@2.0.0:
|
/mdast-util-newline-to-break@2.0.0:
|
||||||
resolution: {integrity: sha512-MbgeFca0hLYIEx/2zGsszCSEJJ1JSCdiY5xQxRcLDDGa8EPvlLPupJ4DSajbMPAnC0je8jfb9TiUATnxxrHUog==}
|
resolution: {integrity: sha512-MbgeFca0hLYIEx/2zGsszCSEJJ1JSCdiY5xQxRcLDDGa8EPvlLPupJ4DSajbMPAnC0je8jfb9TiUATnxxrHUog==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/mdast': 4.0.3
|
'@types/mdast': 4.0.4
|
||||||
mdast-util-find-and-replace: 3.0.1
|
mdast-util-find-and-replace: 3.0.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/mdast-util-phrasing@4.1.0:
|
/mdast-util-phrasing@4.1.0:
|
||||||
resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==}
|
resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/mdast': 4.0.3
|
'@types/mdast': 4.0.4
|
||||||
unist-util-is: 6.0.0
|
unist-util-is: 6.0.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
@ -2692,7 +2695,7 @@ packages:
|
|||||||
resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==}
|
resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/hast': 3.0.4
|
'@types/hast': 3.0.4
|
||||||
'@types/mdast': 4.0.3
|
'@types/mdast': 4.0.4
|
||||||
'@ungap/structured-clone': 1.2.0
|
'@ungap/structured-clone': 1.2.0
|
||||||
devlop: 1.1.0
|
devlop: 1.1.0
|
||||||
micromark-util-sanitize-uri: 2.0.0
|
micromark-util-sanitize-uri: 2.0.0
|
||||||
@ -2705,7 +2708,7 @@ packages:
|
|||||||
/mdast-util-to-markdown@2.1.0:
|
/mdast-util-to-markdown@2.1.0:
|
||||||
resolution: {integrity: sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==}
|
resolution: {integrity: sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/mdast': 4.0.3
|
'@types/mdast': 4.0.4
|
||||||
'@types/unist': 3.0.2
|
'@types/unist': 3.0.2
|
||||||
longest-streak: 3.1.0
|
longest-streak: 3.1.0
|
||||||
mdast-util-phrasing: 4.1.0
|
mdast-util-phrasing: 4.1.0
|
||||||
@ -2718,7 +2721,7 @@ packages:
|
|||||||
/mdast-util-to-string@4.0.0:
|
/mdast-util-to-string@4.0.0:
|
||||||
resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==}
|
resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/mdast': 4.0.3
|
'@types/mdast': 4.0.4
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/mdn-data@2.0.30:
|
/mdn-data@2.0.30:
|
||||||
@ -3348,7 +3351,7 @@ packages:
|
|||||||
/remark-breaks@4.0.0:
|
/remark-breaks@4.0.0:
|
||||||
resolution: {integrity: sha512-IjEjJOkH4FuJvHZVIW0QCDWxcG96kCq7An/KVH2NfJe6rKZU2AsHeB3OEjPNRxi4QC34Xdx7I2KGYn6IpT7gxQ==}
|
resolution: {integrity: sha512-IjEjJOkH4FuJvHZVIW0QCDWxcG96kCq7An/KVH2NfJe6rKZU2AsHeB3OEjPNRxi4QC34Xdx7I2KGYn6IpT7gxQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/mdast': 4.0.3
|
'@types/mdast': 4.0.4
|
||||||
mdast-util-newline-to-break: 2.0.0
|
mdast-util-newline-to-break: 2.0.0
|
||||||
unified: 11.0.5
|
unified: 11.0.5
|
||||||
dev: true
|
dev: true
|
||||||
@ -3356,7 +3359,7 @@ packages:
|
|||||||
/remark-frontmatter@5.0.0:
|
/remark-frontmatter@5.0.0:
|
||||||
resolution: {integrity: sha512-XTFYvNASMe5iPN0719nPrdItC9aU0ssC4v14mH1BCi1u0n1gAocqcujWUrByftZTbLhRtiKRyjYTSIOcr69UVQ==}
|
resolution: {integrity: sha512-XTFYvNASMe5iPN0719nPrdItC9aU0ssC4v14mH1BCi1u0n1gAocqcujWUrByftZTbLhRtiKRyjYTSIOcr69UVQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/mdast': 4.0.3
|
'@types/mdast': 4.0.4
|
||||||
mdast-util-frontmatter: 2.0.1
|
mdast-util-frontmatter: 2.0.1
|
||||||
micromark-extension-frontmatter: 2.0.0
|
micromark-extension-frontmatter: 2.0.0
|
||||||
unified: 11.0.5
|
unified: 11.0.5
|
||||||
@ -3367,7 +3370,7 @@ packages:
|
|||||||
/remark-gfm@4.0.0:
|
/remark-gfm@4.0.0:
|
||||||
resolution: {integrity: sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==}
|
resolution: {integrity: sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/mdast': 4.0.3
|
'@types/mdast': 4.0.4
|
||||||
mdast-util-gfm: 3.0.0
|
mdast-util-gfm: 3.0.0
|
||||||
micromark-extension-gfm: 3.0.0
|
micromark-extension-gfm: 3.0.0
|
||||||
remark-parse: 11.0.0
|
remark-parse: 11.0.0
|
||||||
@ -3380,7 +3383,7 @@ packages:
|
|||||||
/remark-math@6.0.0:
|
/remark-math@6.0.0:
|
||||||
resolution: {integrity: sha512-MMqgnP74Igy+S3WwnhQ7kqGlEerTETXMvJhrUzDikVZ2/uogJCb+WHUg97hK9/jcfc0dkD73s3LN8zU49cTEtA==}
|
resolution: {integrity: sha512-MMqgnP74Igy+S3WwnhQ7kqGlEerTETXMvJhrUzDikVZ2/uogJCb+WHUg97hK9/jcfc0dkD73s3LN8zU49cTEtA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/mdast': 4.0.3
|
'@types/mdast': 4.0.4
|
||||||
mdast-util-math: 3.0.0
|
mdast-util-math: 3.0.0
|
||||||
micromark-extension-math: 3.0.0
|
micromark-extension-math: 3.0.0
|
||||||
unified: 11.0.5
|
unified: 11.0.5
|
||||||
@ -3391,7 +3394,7 @@ packages:
|
|||||||
/remark-parse@11.0.0:
|
/remark-parse@11.0.0:
|
||||||
resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==}
|
resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/mdast': 4.0.3
|
'@types/mdast': 4.0.4
|
||||||
mdast-util-from-markdown: 2.0.0
|
mdast-util-from-markdown: 2.0.0
|
||||||
micromark-util-types: 2.0.0
|
micromark-util-types: 2.0.0
|
||||||
unified: 11.0.5
|
unified: 11.0.5
|
||||||
@ -3403,7 +3406,7 @@ packages:
|
|||||||
resolution: {integrity: sha512-z3tJrAs2kIs1AqIIy6pzHmAHlF1hWQ+OdY4/hv+Wxe35EhyLKcajL33iUEn3ScxtFox9nUvRufR/Zre8Q08H/g==}
|
resolution: {integrity: sha512-z3tJrAs2kIs1AqIIy6pzHmAHlF1hWQ+OdY4/hv+Wxe35EhyLKcajL33iUEn3ScxtFox9nUvRufR/Zre8Q08H/g==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/hast': 3.0.4
|
'@types/hast': 3.0.4
|
||||||
'@types/mdast': 4.0.3
|
'@types/mdast': 4.0.4
|
||||||
mdast-util-to-hast: 13.2.0
|
mdast-util-to-hast: 13.2.0
|
||||||
unified: 11.0.5
|
unified: 11.0.5
|
||||||
vfile: 6.0.1
|
vfile: 6.0.1
|
||||||
@ -3421,7 +3424,7 @@ packages:
|
|||||||
/remark-stringify@11.0.0:
|
/remark-stringify@11.0.0:
|
||||||
resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==}
|
resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/mdast': 4.0.3
|
'@types/mdast': 4.0.4
|
||||||
mdast-util-to-markdown: 2.1.0
|
mdast-util-to-markdown: 2.1.0
|
||||||
unified: 11.0.5
|
unified: 11.0.5
|
||||||
dev: true
|
dev: true
|
||||||
@ -3429,7 +3432,7 @@ packages:
|
|||||||
/remark@15.0.1:
|
/remark@15.0.1:
|
||||||
resolution: {integrity: sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A==}
|
resolution: {integrity: sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/mdast': 4.0.3
|
'@types/mdast': 4.0.4
|
||||||
remark-parse: 11.0.0
|
remark-parse: 11.0.0
|
||||||
remark-stringify: 11.0.0
|
remark-stringify: 11.0.0
|
||||||
unified: 11.0.5
|
unified: 11.0.5
|
||||||
|
|||||||
@ -35,6 +35,7 @@ const config: QuartzConfig = {
|
|||||||
secondary: "#284B63", // #091217
|
secondary: "#284B63", // #091217
|
||||||
tertiary: "#84A59D", // #AA336A
|
tertiary: "#84A59D", // #AA336A
|
||||||
highlight: "#8F9FA925",
|
highlight: "#8F9FA925",
|
||||||
|
textHighlight: "#fff23688",
|
||||||
},
|
},
|
||||||
darkMode: {
|
darkMode: {
|
||||||
light: "#1E1E2E", // background // #161618
|
light: "#1E1E2E", // background // #161618
|
||||||
@ -45,6 +46,7 @@ const config: QuartzConfig = {
|
|||||||
secondary: "#9BE895", // links, nodes // #7B97AA
|
secondary: "#9BE895", // links, nodes // #7B97AA
|
||||||
tertiary: "#C072C4", // hover states, visited links // #84A59D
|
tertiary: "#C072C4", // hover states, visited links // #84A59D
|
||||||
highlight: "#8F9FA925", // internal link background
|
highlight: "#8F9FA925", // internal link background
|
||||||
|
textHighlight: "#b3aa0288",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
137
quartz/build.ts
137
quartz/build.ts
@ -1,24 +1,24 @@
|
|||||||
import sourceMapSupport from "source-map-support"
|
import sourceMapSupport from "source-map-support"
|
||||||
sourceMapSupport.install(options)
|
sourceMapSupport.install(options)
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import {PerfTimer} from "./util/perf"
|
import { PerfTimer } from "./util/perf"
|
||||||
import {rimraf} from "rimraf"
|
import { rimraf } from "rimraf"
|
||||||
import {GlobbyFilterFunction, isGitIgnored} from "globby"
|
import { GlobbyFilterFunction, isGitIgnored } from "globby"
|
||||||
import chalk from "chalk"
|
import chalk from "chalk"
|
||||||
import {parseMarkdown} from "./processors/parse"
|
import { parseMarkdown } from "./processors/parse"
|
||||||
import {filterContent} from "./processors/filter"
|
import { filterContent } from "./processors/filter"
|
||||||
import {emitContent} from "./processors/emit"
|
import { emitContent } from "./processors/emit"
|
||||||
import cfg from "../quartz.config"
|
import cfg from "../quartz.config"
|
||||||
import {FilePath, FullSlug, joinSegments, slugifyFilePath} from "./util/path"
|
import { FilePath, FullSlug, joinSegments, slugifyFilePath } from "./util/path"
|
||||||
import chokidar from "chokidar"
|
import chokidar from "chokidar"
|
||||||
import {ProcessedContent} from "./plugins/vfile"
|
import { ProcessedContent } from "./plugins/vfile"
|
||||||
import {Argv, BuildCtx} from "./util/ctx"
|
import { Argv, BuildCtx } from "./util/ctx"
|
||||||
import {glob, toPosixPath} from "./util/glob"
|
import { glob, toPosixPath } from "./util/glob"
|
||||||
import {trace} from "./util/trace"
|
import { trace } from "./util/trace"
|
||||||
import {options} from "./util/sourcemap"
|
import { options } from "./util/sourcemap"
|
||||||
import {Mutex} from "async-mutex"
|
import { Mutex } from "async-mutex"
|
||||||
import DepGraph from "./depgraph"
|
import DepGraph from "./depgraph"
|
||||||
import {getStaticResourcesFromPlugins} from "./plugins"
|
import { getStaticResourcesFromPlugins } from "./plugins"
|
||||||
|
|
||||||
type Dependencies = Record<string, DepGraph<FilePath> | null>
|
type Dependencies = Record<string, DepGraph<FilePath> | null>
|
||||||
|
|
||||||
@ -65,25 +65,17 @@ async function buildQuartz(argv: Argv, mut: Mutex, clientRefresh: () => void) {
|
|||||||
|
|
||||||
const release = await mut.acquire()
|
const release = await mut.acquire()
|
||||||
perf.addEvent("clean")
|
perf.addEvent("clean")
|
||||||
await rimraf(path.join(output, "*"), {glob: true})
|
await rimraf(path.join(output, "*"), { glob: true })
|
||||||
console.log(
|
console.log(`Cleaned output directory \`${output}\` in ${perf.timeSince("clean")}`)
|
||||||
`Cleaned output directory \`${output}\` in ${perf.timeSince("clean")}`,
|
|
||||||
)
|
|
||||||
|
|
||||||
perf.addEvent("glob")
|
perf.addEvent("glob")
|
||||||
const allFiles = await glob(
|
const allFiles = await glob("**/*.*", argv.directory, cfg.configuration.ignorePatterns)
|
||||||
"**/*.*",
|
|
||||||
argv.directory,
|
|
||||||
cfg.configuration.ignorePatterns,
|
|
||||||
)
|
|
||||||
const fps = allFiles.filter((fp) => fp.endsWith(".md")).sort()
|
const fps = allFiles.filter((fp) => fp.endsWith(".md")).sort()
|
||||||
console.log(
|
console.log(
|
||||||
`Found ${fps.length} input files from \`${argv.directory}\` in ${perf.timeSince("glob")}`,
|
`Found ${fps.length} input files from \`${argv.directory}\` in ${perf.timeSince("glob")}`,
|
||||||
)
|
)
|
||||||
|
|
||||||
const filePaths = fps.map(
|
const filePaths = fps.map((fp) => joinSegments(argv.directory, fp) as FilePath)
|
||||||
(fp) => joinSegments(argv.directory, fp) as FilePath,
|
|
||||||
)
|
|
||||||
ctx.allSlugs = allFiles.map((fp) => slugifyFilePath(fp as FilePath))
|
ctx.allSlugs = allFiles.map((fp) => slugifyFilePath(fp as FilePath))
|
||||||
|
|
||||||
const parsedFiles = await parseMarkdown(ctx, filePaths)
|
const parsedFiles = await parseMarkdown(ctx, filePaths)
|
||||||
@ -96,18 +88,12 @@ async function buildQuartz(argv: Argv, mut: Mutex, clientRefresh: () => void) {
|
|||||||
const staticResources = getStaticResourcesFromPlugins(ctx)
|
const staticResources = getStaticResourcesFromPlugins(ctx)
|
||||||
for (const emitter of cfg.plugins.emitters) {
|
for (const emitter of cfg.plugins.emitters) {
|
||||||
dependencies[emitter.name] =
|
dependencies[emitter.name] =
|
||||||
(await emitter.getDependencyGraph?.(
|
(await emitter.getDependencyGraph?.(ctx, filteredContent, staticResources)) ?? null
|
||||||
ctx,
|
|
||||||
filteredContent,
|
|
||||||
staticResources,
|
|
||||||
)) ?? null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await emitContent(ctx, filteredContent)
|
await emitContent(ctx, filteredContent)
|
||||||
console.log(
|
console.log(chalk.green(`Done processing ${fps.length} files in ${perf.timeSince()}`))
|
||||||
chalk.green(`Done processing ${fps.length} files in ${perf.timeSince()}`),
|
|
||||||
)
|
|
||||||
release()
|
release()
|
||||||
|
|
||||||
if (argv.serve) {
|
if (argv.serve) {
|
||||||
@ -123,7 +109,7 @@ async function startServing(
|
|||||||
clientRefresh: () => void,
|
clientRefresh: () => void,
|
||||||
dependencies: Dependencies, // emitter name: dep graph
|
dependencies: Dependencies, // emitter name: dep graph
|
||||||
) {
|
) {
|
||||||
const {argv} = ctx
|
const { argv } = ctx
|
||||||
|
|
||||||
// cache file parse results
|
// cache file parse results
|
||||||
const contentMap = new Map<FilePath, ProcessedContent>()
|
const contentMap = new Map<FilePath, ProcessedContent>()
|
||||||
@ -151,17 +137,11 @@ async function startServing(
|
|||||||
ignoreInitial: true,
|
ignoreInitial: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
const buildFromEntry = argv.fastRebuild
|
const buildFromEntry = argv.fastRebuild ? partialRebuildFromEntrypoint : rebuildFromEntrypoint
|
||||||
? partialRebuildFromEntrypoint
|
|
||||||
: rebuildFromEntrypoint
|
|
||||||
watcher
|
watcher
|
||||||
.on("add", (fp) => buildFromEntry(fp, "add", clientRefresh, buildData))
|
.on("add", (fp) => buildFromEntry(fp, "add", clientRefresh, buildData))
|
||||||
.on("change", (fp) =>
|
.on("change", (fp) => buildFromEntry(fp, "change", clientRefresh, buildData))
|
||||||
buildFromEntry(fp, "change", clientRefresh, buildData),
|
.on("unlink", (fp) => buildFromEntry(fp, "delete", clientRefresh, buildData))
|
||||||
)
|
|
||||||
.on("unlink", (fp) =>
|
|
||||||
buildFromEntry(fp, "delete", clientRefresh, buildData),
|
|
||||||
)
|
|
||||||
|
|
||||||
return async () => {
|
return async () => {
|
||||||
await watcher.close()
|
await watcher.close()
|
||||||
@ -174,8 +154,8 @@ async function partialRebuildFromEntrypoint(
|
|||||||
clientRefresh: () => void,
|
clientRefresh: () => void,
|
||||||
buildData: BuildData, // note: this function mutates buildData
|
buildData: BuildData, // note: this function mutates buildData
|
||||||
) {
|
) {
|
||||||
const {ctx, ignored, dependencies, contentMap, mut, toRemove} = buildData
|
const { ctx, ignored, dependencies, contentMap, mut, toRemove } = buildData
|
||||||
const {argv, cfg} = ctx
|
const { argv, cfg } = ctx
|
||||||
|
|
||||||
// don't do anything for gitignored files
|
// don't do anything for gitignored files
|
||||||
if (ignored(filepath)) {
|
if (ignored(filepath)) {
|
||||||
@ -204,18 +184,12 @@ async function partialRebuildFromEntrypoint(
|
|||||||
case "add":
|
case "add":
|
||||||
// add to cache when new file is added
|
// add to cache when new file is added
|
||||||
processedFiles = await parseMarkdown(ctx, [fp])
|
processedFiles = await parseMarkdown(ctx, [fp])
|
||||||
processedFiles.forEach(([tree, vfile]) =>
|
processedFiles.forEach(([tree, vfile]) => contentMap.set(vfile.data.filePath!, [tree, vfile]))
|
||||||
contentMap.set(vfile.data.filePath!, [tree, vfile]),
|
|
||||||
)
|
|
||||||
|
|
||||||
// update the dep graph by asking all emitters whether they depend on this file
|
// update the dep graph by asking all emitters whether they depend on this file
|
||||||
for (const emitter of cfg.plugins.emitters) {
|
for (const emitter of cfg.plugins.emitters) {
|
||||||
const emitterGraph =
|
const emitterGraph =
|
||||||
(await emitter.getDependencyGraph?.(
|
(await emitter.getDependencyGraph?.(ctx, processedFiles, staticResources)) ?? null
|
||||||
ctx,
|
|
||||||
processedFiles,
|
|
||||||
staticResources,
|
|
||||||
)) ?? null
|
|
||||||
|
|
||||||
if (emitterGraph) {
|
if (emitterGraph) {
|
||||||
const existingGraph = dependencies[emitter.name]
|
const existingGraph = dependencies[emitter.name]
|
||||||
@ -231,29 +205,20 @@ async function partialRebuildFromEntrypoint(
|
|||||||
case "change":
|
case "change":
|
||||||
// invalidate cache when file is changed
|
// invalidate cache when file is changed
|
||||||
processedFiles = await parseMarkdown(ctx, [fp])
|
processedFiles = await parseMarkdown(ctx, [fp])
|
||||||
processedFiles.forEach(([tree, vfile]) =>
|
processedFiles.forEach(([tree, vfile]) => contentMap.set(vfile.data.filePath!, [tree, vfile]))
|
||||||
contentMap.set(vfile.data.filePath!, [tree, vfile]),
|
|
||||||
)
|
|
||||||
|
|
||||||
// only content files can have added/removed dependencies because of transclusions
|
// only content files can have added/removed dependencies because of transclusions
|
||||||
if (path.extname(fp) === ".md") {
|
if (path.extname(fp) === ".md") {
|
||||||
for (const emitter of cfg.plugins.emitters) {
|
for (const emitter of cfg.plugins.emitters) {
|
||||||
// get new dependencies from all emitters for this file
|
// get new dependencies from all emitters for this file
|
||||||
const emitterGraph =
|
const emitterGraph =
|
||||||
(await emitter.getDependencyGraph?.(
|
(await emitter.getDependencyGraph?.(ctx, processedFiles, staticResources)) ?? null
|
||||||
ctx,
|
|
||||||
processedFiles,
|
|
||||||
staticResources,
|
|
||||||
)) ?? null
|
|
||||||
|
|
||||||
// only update the graph if the emitter plugin uses the changed file
|
// only update the graph if the emitter plugin uses the changed file
|
||||||
// eg. Assets plugin ignores md files, so we skip updating the graph
|
// eg. Assets plugin ignores md files, so we skip updating the graph
|
||||||
if (emitterGraph?.hasNode(fp)) {
|
if (emitterGraph?.hasNode(fp)) {
|
||||||
// merge the new dependencies into the dep graph
|
// merge the new dependencies into the dep graph
|
||||||
dependencies[emitter.name]?.updateIncomingEdgesForNode(
|
dependencies[emitter.name]?.updateIncomingEdgesForNode(emitterGraph, fp)
|
||||||
emitterGraph,
|
|
||||||
fp,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -316,11 +281,7 @@ async function partialRebuildFromEntrypoint(
|
|||||||
.filter((file) => !toRemove.has(file))
|
.filter((file) => !toRemove.has(file))
|
||||||
.map((file) => contentMap.get(file)!)
|
.map((file) => contentMap.get(file)!)
|
||||||
|
|
||||||
const emittedFps = await emitter.emit(
|
const emittedFps = await emitter.emit(ctx, upstreamContent, staticResources)
|
||||||
ctx,
|
|
||||||
upstreamContent,
|
|
||||||
staticResources,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (ctx.argv.verbose) {
|
if (ctx.argv.verbose) {
|
||||||
for (const file of emittedFps) {
|
for (const file of emittedFps) {
|
||||||
@ -332,9 +293,7 @@ async function partialRebuildFromEntrypoint(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(
|
console.log(`Emitted ${emittedFiles} files to \`${argv.output}\` in ${perf.timeSince("rebuild")}`)
|
||||||
`Emitted ${emittedFiles} files to \`${argv.output}\` in ${perf.timeSince("rebuild")}`,
|
|
||||||
)
|
|
||||||
|
|
||||||
// CLEANUP
|
// CLEANUP
|
||||||
const destinationsToDelete = new Set<FilePath>()
|
const destinationsToDelete = new Set<FilePath>()
|
||||||
@ -369,18 +328,10 @@ async function rebuildFromEntrypoint(
|
|||||||
clientRefresh: () => void,
|
clientRefresh: () => void,
|
||||||
buildData: BuildData, // note: this function mutates buildData
|
buildData: BuildData, // note: this function mutates buildData
|
||||||
) {
|
) {
|
||||||
const {
|
const { ctx, ignored, mut, initialSlugs, contentMap, toRebuild, toRemove, trackedAssets } =
|
||||||
ctx,
|
buildData
|
||||||
ignored,
|
|
||||||
mut,
|
|
||||||
initialSlugs,
|
|
||||||
contentMap,
|
|
||||||
toRebuild,
|
|
||||||
toRemove,
|
|
||||||
trackedAssets,
|
|
||||||
} = buildData
|
|
||||||
|
|
||||||
const {argv} = ctx
|
const { argv } = ctx
|
||||||
|
|
||||||
// don't do anything for gitignored files
|
// don't do anything for gitignored files
|
||||||
if (ignored(fp)) {
|
if (ignored(fp)) {
|
||||||
@ -422,16 +373,6 @@ async function rebuildFromEntrypoint(
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const filesToRebuild = [...toRebuild].filter((fp) => !toRemove.has(fp))
|
const filesToRebuild = [...toRebuild].filter((fp) => !toRemove.has(fp))
|
||||||
|
|
||||||
const trackedSlugs = [
|
|
||||||
...new Set([...contentMap.keys(), ...toRebuild, ...trackedAssets]),
|
|
||||||
]
|
|
||||||
.filter((fp) => !toRemove.has(fp))
|
|
||||||
.map((fp) =>
|
|
||||||
slugifyFilePath(path.posix.relative(argv.directory, fp) as FilePath),
|
|
||||||
)
|
|
||||||
|
|
||||||
ctx.allSlugs = [...new Set([...initialSlugs, ...trackedSlugs])]
|
|
||||||
const parsedContent = await parseMarkdown(ctx, filesToRebuild)
|
const parsedContent = await parseMarkdown(ctx, filesToRebuild)
|
||||||
for (const content of parsedContent) {
|
for (const content of parsedContent) {
|
||||||
const [_tree, vfile] = content
|
const [_tree, vfile] = content
|
||||||
@ -454,13 +395,11 @@ async function rebuildFromEntrypoint(
|
|||||||
|
|
||||||
// TODO: we can probably traverse the link graph to figure out what's safe to delete here
|
// TODO: we can probably traverse the link graph to figure out what's safe to delete here
|
||||||
// instead of just deleting everything
|
// instead of just deleting everything
|
||||||
await rimraf(path.join(argv.output, ".*"), {glob: true})
|
await rimraf(path.join(argv.output, ".*"), { glob: true })
|
||||||
await emitContent(ctx, filteredContent)
|
await emitContent(ctx, filteredContent)
|
||||||
console.log(chalk.green(`Done rebuilding in ${perf.timeSince()}`))
|
console.log(chalk.green(`Done rebuilding in ${perf.timeSince()}`))
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(
|
console.log(chalk.yellow(`Rebuild failed. Waiting on a change to fix the error...`))
|
||||||
chalk.yellow(`Rebuild failed. Waiting on a change to fix the error...`),
|
|
||||||
)
|
|
||||||
if (argv.verbose) {
|
if (argv.verbose) {
|
||||||
console.log(chalk.red(err))
|
console.log(chalk.red(err))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,7 @@
|
|||||||
import {
|
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
||||||
QuartzComponent,
|
import { classNames } from "../util/lang"
|
||||||
QuartzComponentConstructor,
|
|
||||||
QuartzComponentProps,
|
|
||||||
} from "./types"
|
|
||||||
import {classNames} from "../util/lang"
|
|
||||||
|
|
||||||
const ArticleTitle: QuartzComponent = ({
|
const ArticleTitle: QuartzComponent = ({ fileData, displayClass }: QuartzComponentProps) => {
|
||||||
fileData,
|
|
||||||
displayClass,
|
|
||||||
}: QuartzComponentProps) => {
|
|
||||||
const title = fileData.frontmatter?.title
|
const title = fileData.frontmatter?.title
|
||||||
if (title) {
|
if (title) {
|
||||||
return <h1 class={classNames(displayClass, "article-title")}>{title}</h1>
|
return <h1 class={classNames(displayClass, "article-title")}>{title}</h1>
|
||||||
|
|||||||
@ -1,12 +1,8 @@
|
|||||||
import {
|
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
||||||
QuartzComponent,
|
|
||||||
QuartzComponentConstructor,
|
|
||||||
QuartzComponentProps,
|
|
||||||
} from "./types"
|
|
||||||
import style from "./styles/backlinks.scss"
|
import style from "./styles/backlinks.scss"
|
||||||
import {resolveRelative, simplifySlug} from "../util/path"
|
import { resolveRelative, simplifySlug } from "../util/path"
|
||||||
import {i18n} from "../i18n"
|
import { i18n } from "../i18n"
|
||||||
import {classNames} from "../util/lang"
|
import { classNames } from "../util/lang"
|
||||||
|
|
||||||
const Backlinks: QuartzComponent = ({
|
const Backlinks: QuartzComponent = ({
|
||||||
fileData,
|
fileData,
|
||||||
@ -23,9 +19,7 @@ const Backlinks: QuartzComponent = ({
|
|||||||
{backlinkFiles.length > 0 ? (
|
{backlinkFiles.length > 0 ? (
|
||||||
backlinkFiles.map((f) => (
|
backlinkFiles.map((f) => (
|
||||||
<li>
|
<li>
|
||||||
<a
|
<a href={resolveRelative(fileData.slug!, f.slug!)} class="internal">
|
||||||
href={resolveRelative(fileData.slug!, f.slug!)}
|
|
||||||
class="internal">
|
|
||||||
{f.frontmatter?.title}
|
{f.frontmatter?.title}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@ -1,13 +1,9 @@
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import clipboardScript from "./scripts/clipboard.inline"
|
import clipboardScript from "./scripts/clipboard.inline"
|
||||||
import clipboardStyle from "./styles/clipboard.scss"
|
import clipboardStyle from "./styles/clipboard.scss"
|
||||||
import {
|
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
||||||
QuartzComponent,
|
|
||||||
QuartzComponentConstructor,
|
|
||||||
QuartzComponentProps,
|
|
||||||
} from "./types"
|
|
||||||
|
|
||||||
const Body: QuartzComponent = ({children}: QuartzComponentProps) => {
|
const Body: QuartzComponent = ({ children }: QuartzComponentProps) => {
|
||||||
return <div id="quartz-body">{children}</div>
|
return <div id="quartz-body">{children}</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,12 +1,8 @@
|
|||||||
import {
|
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
||||||
QuartzComponent,
|
|
||||||
QuartzComponentConstructor,
|
|
||||||
QuartzComponentProps,
|
|
||||||
} from "./types"
|
|
||||||
import breadcrumbsStyle from "./styles/breadcrumbs.scss"
|
import breadcrumbsStyle from "./styles/breadcrumbs.scss"
|
||||||
import {FullSlug, SimpleSlug, joinSegments, resolveRelative} from "../util/path"
|
import { FullSlug, SimpleSlug, joinSegments, resolveRelative } from "../util/path"
|
||||||
import {QuartzPluginData} from "../plugins/vfile"
|
import { QuartzPluginData } from "../plugins/vfile"
|
||||||
import {classNames} from "../util/lang"
|
import { classNames } from "../util/lang"
|
||||||
|
|
||||||
type CrumbData = {
|
type CrumbData = {
|
||||||
displayName: string
|
displayName: string
|
||||||
@ -44,11 +40,7 @@ const defaultOptions: BreadcrumbOptions = {
|
|||||||
showCurrentPage: true,
|
showCurrentPage: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatCrumb(
|
function formatCrumb(displayName: string, baseSlug: FullSlug, currentSlug: SimpleSlug): CrumbData {
|
||||||
displayName: string,
|
|
||||||
baseSlug: FullSlug,
|
|
||||||
currentSlug: SimpleSlug,
|
|
||||||
): CrumbData {
|
|
||||||
return {
|
return {
|
||||||
displayName: displayName.replaceAll("-", " "),
|
displayName: displayName.replaceAll("-", " "),
|
||||||
path: resolveRelative(baseSlug, currentSlug),
|
path: resolveRelative(baseSlug, currentSlug),
|
||||||
@ -57,7 +49,7 @@ function formatCrumb(
|
|||||||
|
|
||||||
export default ((opts?: Partial<BreadcrumbOptions>) => {
|
export default ((opts?: Partial<BreadcrumbOptions>) => {
|
||||||
// Merge options with defaults
|
// Merge options with defaults
|
||||||
const options: BreadcrumbOptions = {...defaultOptions, ...opts}
|
const options: BreadcrumbOptions = { ...defaultOptions, ...opts }
|
||||||
|
|
||||||
// computed index of folder name to its associated file data
|
// computed index of folder name to its associated file data
|
||||||
let folderIndex: Map<string, QuartzPluginData> | undefined
|
let folderIndex: Map<string, QuartzPluginData> | undefined
|
||||||
@ -73,11 +65,7 @@ export default ((opts?: Partial<BreadcrumbOptions>) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Format entry for root element
|
// Format entry for root element
|
||||||
const firstEntry = formatCrumb(
|
const firstEntry = formatCrumb(options.rootName, fileData.slug!, "/" as SimpleSlug)
|
||||||
options.rootName,
|
|
||||||
fileData.slug!,
|
|
||||||
"/" as SimpleSlug,
|
|
||||||
)
|
|
||||||
const crumbs: CrumbData[] = [firstEntry]
|
const crumbs: CrumbData[] = [firstEntry]
|
||||||
|
|
||||||
if (!folderIndex && options.resolveFrontmatterTitle) {
|
if (!folderIndex && options.resolveFrontmatterTitle) {
|
||||||
@ -104,9 +92,7 @@ export default ((opts?: Partial<BreadcrumbOptions>) => {
|
|||||||
let curPathSegment = slugParts[i]
|
let curPathSegment = slugParts[i]
|
||||||
|
|
||||||
// Try to resolve frontmatter folder title
|
// Try to resolve frontmatter folder title
|
||||||
const currentFile = folderIndex?.get(
|
const currentFile = folderIndex?.get(slugParts.slice(0, i + 1).join("/"))
|
||||||
slugParts.slice(0, i + 1).join("/"),
|
|
||||||
)
|
|
||||||
if (currentFile) {
|
if (currentFile) {
|
||||||
const title = currentFile.frontmatter!.title
|
const title = currentFile.frontmatter!.title
|
||||||
if (title !== "index") {
|
if (title !== "index") {
|
||||||
@ -137,15 +123,11 @@ export default ((opts?: Partial<BreadcrumbOptions>) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav
|
<nav class={classNames(displayClass, "breadcrumb-container")} aria-label="breadcrumbs">
|
||||||
class={classNames(displayClass, "breadcrumb-container")}
|
|
||||||
aria-label="breadcrumbs">
|
|
||||||
{crumbs.map((crumb, index) => (
|
{crumbs.map((crumb, index) => (
|
||||||
<div class="breadcrumb-element">
|
<div class="breadcrumb-element">
|
||||||
<a href={crumb.path}>{crumb.displayName}</a>
|
<a href={crumb.path}>{crumb.displayName}</a>
|
||||||
{index !== crumbs.length - 1 && (
|
{index !== crumbs.length - 1 && <p>{` ${options.spacerSymbol} `}</p>}
|
||||||
<p>{` ${options.spacerSymbol} `}</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import {formatDate, getDate} from "./Date"
|
import { formatDate, getDate } from "./Date"
|
||||||
import {QuartzComponentConstructor, QuartzComponentProps} from "./types"
|
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
||||||
import readingTime from "reading-time"
|
import readingTime from "reading-time"
|
||||||
import {classNames} from "../util/lang"
|
import { classNames } from "../util/lang"
|
||||||
import {i18n} from "../i18n"
|
import { i18n } from "../i18n"
|
||||||
import {JSX} from "preact"
|
import { JSX } from "preact"
|
||||||
import style from "./styles/contentMeta.scss"
|
import style from "./styles/contentMeta.scss"
|
||||||
|
|
||||||
interface ContentMetaOptions {
|
interface ContentMetaOptions {
|
||||||
@ -21,13 +21,9 @@ const defaultOptions: ContentMetaOptions = {
|
|||||||
|
|
||||||
export default ((opts?: Partial<ContentMetaOptions>) => {
|
export default ((opts?: Partial<ContentMetaOptions>) => {
|
||||||
// Merge options with defaults
|
// Merge options with defaults
|
||||||
const options: ContentMetaOptions = {...defaultOptions, ...opts}
|
const options: ContentMetaOptions = { ...defaultOptions, ...opts }
|
||||||
|
|
||||||
function ContentMetadata({
|
function ContentMetadata({ cfg, fileData, displayClass }: QuartzComponentProps) {
|
||||||
cfg,
|
|
||||||
fileData,
|
|
||||||
displayClass,
|
|
||||||
}: QuartzComponentProps) {
|
|
||||||
const text = fileData.text
|
const text = fileData.text
|
||||||
|
|
||||||
if (text) {
|
if (text) {
|
||||||
@ -39,10 +35,8 @@ export default ((opts?: Partial<ContentMetaOptions>) => {
|
|||||||
|
|
||||||
// Display reading time if enabled
|
// Display reading time if enabled
|
||||||
if (options.showReadingTime) {
|
if (options.showReadingTime) {
|
||||||
const {minutes, words: _words} = readingTime(text)
|
const { minutes, words: _words } = readingTime(text)
|
||||||
const displayedTime = i18n(
|
const displayedTime = i18n(cfg.locale).components.contentMeta.readingTime({
|
||||||
cfg.locale,
|
|
||||||
).components.contentMeta.readingTime({
|
|
||||||
minutes: Math.ceil(minutes),
|
minutes: Math.ceil(minutes),
|
||||||
})
|
})
|
||||||
segments.push(displayedTime)
|
segments.push(displayedTime)
|
||||||
@ -51,9 +45,7 @@ export default ((opts?: Partial<ContentMetaOptions>) => {
|
|||||||
const segmentsElements = segments.map((segment) => <span>{segment}</span>)
|
const segmentsElements = segments.map((segment) => <span>{segment}</span>)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<p
|
<p show-comma={options.showComma} class={classNames(displayClass, "content-meta")}>
|
||||||
show-comma={options.showComma}
|
|
||||||
class={classNames(displayClass, "content-meta")}>
|
|
||||||
{segmentsElements}
|
{segmentsElements}
|
||||||
</p>
|
</p>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -3,26 +3,14 @@
|
|||||||
// see: https://v8.dev/features/modules#defer
|
// see: https://v8.dev/features/modules#defer
|
||||||
import darkmodeScript from "./scripts/darkmode.inline"
|
import darkmodeScript from "./scripts/darkmode.inline"
|
||||||
import styles from "./styles/darkmode.scss"
|
import styles from "./styles/darkmode.scss"
|
||||||
import {
|
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
||||||
QuartzComponent,
|
import { i18n } from "../i18n"
|
||||||
QuartzComponentConstructor,
|
import { classNames } from "../util/lang"
|
||||||
QuartzComponentProps,
|
|
||||||
} from "./types"
|
|
||||||
import {i18n} from "../i18n"
|
|
||||||
import {classNames} from "../util/lang"
|
|
||||||
|
|
||||||
const Darkmode: QuartzComponent = ({
|
const Darkmode: QuartzComponent = ({ displayClass, cfg }: QuartzComponentProps) => {
|
||||||
displayClass,
|
|
||||||
cfg,
|
|
||||||
}: QuartzComponentProps) => {
|
|
||||||
return (
|
return (
|
||||||
<div class={classNames(displayClass, "darkmode")}>
|
<div class={classNames(displayClass, "darkmode")}>
|
||||||
<input
|
<input class="toggle" id="darkmode-toggle" type="checkbox" tabIndex={-1} />
|
||||||
class="toggle"
|
|
||||||
id="darkmode-toggle"
|
|
||||||
type="checkbox"
|
|
||||||
tabIndex={-1}
|
|
||||||
/>
|
|
||||||
<label id="toggle-label-light" for="darkmode-toggle" tabIndex={-1}>
|
<label id="toggle-label-light" for="darkmode-toggle" tabIndex={-1}>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
@ -33,7 +21,8 @@ const Darkmode: QuartzComponent = ({
|
|||||||
y="0px"
|
y="0px"
|
||||||
viewBox="0 0 35 35"
|
viewBox="0 0 35 35"
|
||||||
style="enable-background:new 0 0 35 35"
|
style="enable-background:new 0 0 35 35"
|
||||||
xmlSpace="preserve">
|
xmlSpace="preserve"
|
||||||
|
>
|
||||||
<title>{i18n(cfg.locale).components.themeToggle.darkMode}</title>
|
<title>{i18n(cfg.locale).components.themeToggle.darkMode}</title>
|
||||||
<path d="M6,17.5C6,16.672,5.328,16,4.5,16h-3C0.672,16,0,16.672,0,17.5 S0.672,19,1.5,19h3C5.328,19,6,18.328,6,17.5z M7.5,26c-0.414,0-0.789,0.168-1.061,0.439l-2,2C4.168,28.711,4,29.086,4,29.5 C4,30.328,4.671,31,5.5,31c0.414,0,0.789-0.168,1.06-0.44l2-2C8.832,28.289,9,27.914,9,27.5C9,26.672,8.329,26,7.5,26z M17.5,6 C18.329,6,19,5.328,19,4.5v-3C19,0.672,18.329,0,17.5,0S16,0.672,16,1.5v3C16,5.328,16.671,6,17.5,6z M27.5,9 c0.414,0,0.789-0.168,1.06-0.439l2-2C30.832,6.289,31,5.914,31,5.5C31,4.672,30.329,4,29.5,4c-0.414,0-0.789,0.168-1.061,0.44 l-2,2C26.168,6.711,26,7.086,26,7.5C26,8.328,26.671,9,27.5,9z M6.439,8.561C6.711,8.832,7.086,9,7.5,9C8.328,9,9,8.328,9,7.5 c0-0.414-0.168-0.789-0.439-1.061l-2-2C6.289,4.168,5.914,4,5.5,4C4.672,4,4,4.672,4,5.5c0,0.414,0.168,0.789,0.439,1.06 L6.439,8.561z M33.5,16h-3c-0.828,0-1.5,0.672-1.5,1.5s0.672,1.5,1.5,1.5h3c0.828,0,1.5-0.672,1.5-1.5S34.328,16,33.5,16z M28.561,26.439C28.289,26.168,27.914,26,27.5,26c-0.828,0-1.5,0.672-1.5,1.5c0,0.414,0.168,0.789,0.439,1.06l2,2 C28.711,30.832,29.086,31,29.5,31c0.828,0,1.5-0.672,1.5-1.5c0-0.414-0.168-0.789-0.439-1.061L28.561,26.439z M17.5,29 c-0.829,0-1.5,0.672-1.5,1.5v3c0,0.828,0.671,1.5,1.5,1.5s1.5-0.672,1.5-1.5v-3C19,29.672,18.329,29,17.5,29z M17.5,7 C11.71,7,7,11.71,7,17.5S11.71,28,17.5,28S28,23.29,28,17.5S23.29,7,17.5,7z M17.5,25c-4.136,0-7.5-3.364-7.5-7.5 c0-4.136,3.364-7.5,7.5-7.5c4.136,0,7.5,3.364,7.5,7.5C25,21.636,21.636,25,17.5,25z"></path>
|
<path d="M6,17.5C6,16.672,5.328,16,4.5,16h-3C0.672,16,0,16.672,0,17.5 S0.672,19,1.5,19h3C5.328,19,6,18.328,6,17.5z M7.5,26c-0.414,0-0.789,0.168-1.061,0.439l-2,2C4.168,28.711,4,29.086,4,29.5 C4,30.328,4.671,31,5.5,31c0.414,0,0.789-0.168,1.06-0.44l2-2C8.832,28.289,9,27.914,9,27.5C9,26.672,8.329,26,7.5,26z M17.5,6 C18.329,6,19,5.328,19,4.5v-3C19,0.672,18.329,0,17.5,0S16,0.672,16,1.5v3C16,5.328,16.671,6,17.5,6z M27.5,9 c0.414,0,0.789-0.168,1.06-0.439l2-2C30.832,6.289,31,5.914,31,5.5C31,4.672,30.329,4,29.5,4c-0.414,0-0.789,0.168-1.061,0.44 l-2,2C26.168,6.711,26,7.086,26,7.5C26,8.328,26.671,9,27.5,9z M6.439,8.561C6.711,8.832,7.086,9,7.5,9C8.328,9,9,8.328,9,7.5 c0-0.414-0.168-0.789-0.439-1.061l-2-2C6.289,4.168,5.914,4,5.5,4C4.672,4,4,4.672,4,5.5c0,0.414,0.168,0.789,0.439,1.06 L6.439,8.561z M33.5,16h-3c-0.828,0-1.5,0.672-1.5,1.5s0.672,1.5,1.5,1.5h3c0.828,0,1.5-0.672,1.5-1.5S34.328,16,33.5,16z M28.561,26.439C28.289,26.168,27.914,26,27.5,26c-0.828,0-1.5,0.672-1.5,1.5c0,0.414,0.168,0.789,0.439,1.06l2,2 C28.711,30.832,29.086,31,29.5,31c0.828,0,1.5-0.672,1.5-1.5c0-0.414-0.168-0.789-0.439-1.061L28.561,26.439z M17.5,29 c-0.829,0-1.5,0.672-1.5,1.5v3c0,0.828,0.671,1.5,1.5,1.5s1.5-0.672,1.5-1.5v-3C19,29.672,18.329,29,17.5,29z M17.5,7 C11.71,7,7,11.71,7,17.5S11.71,28,17.5,28S28,23.29,28,17.5S23.29,7,17.5,7z M17.5,25c-4.136,0-7.5-3.364-7.5-7.5 c0-4.136,3.364-7.5,7.5-7.5c4.136,0,7.5,3.364,7.5,7.5C25,21.636,21.636,25,17.5,25z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
@ -48,7 +37,8 @@ const Darkmode: QuartzComponent = ({
|
|||||||
y="0px"
|
y="0px"
|
||||||
viewBox="0 0 100 100"
|
viewBox="0 0 100 100"
|
||||||
style="enable-background:new 0 0 100 100"
|
style="enable-background:new 0 0 100 100"
|
||||||
xmlSpace="preserve">
|
xmlSpace="preserve"
|
||||||
|
>
|
||||||
<title>{i18n(cfg.locale).components.themeToggle.lightMode}</title>
|
<title>{i18n(cfg.locale).components.themeToggle.lightMode}</title>
|
||||||
<path d="M96.76,66.458c-0.853-0.852-2.15-1.064-3.23-0.534c-6.063,2.991-12.858,4.571-19.655,4.571 C62.022,70.495,50.88,65.88,42.5,57.5C29.043,44.043,25.658,23.536,34.076,6.47c0.532-1.08,0.318-2.379-0.534-3.23 c-0.851-0.852-2.15-1.064-3.23-0.534c-4.918,2.427-9.375,5.619-13.246,9.491c-9.447,9.447-14.65,22.008-14.65,35.369 c0,13.36,5.203,25.921,14.65,35.368s22.008,14.65,35.368,14.65c13.361,0,25.921-5.203,35.369-14.65 c3.872-3.871,7.064-8.328,9.491-13.246C97.826,68.608,97.611,67.309,96.76,66.458z"></path>
|
<path d="M96.76,66.458c-0.853-0.852-2.15-1.064-3.23-0.534c-6.063,2.991-12.858,4.571-19.655,4.571 C62.022,70.495,50.88,65.88,42.5,57.5C29.043,44.043,25.658,23.536,34.076,6.47c0.532-1.08,0.318-2.379-0.534-3.23 c-0.851-0.852-2.15-1.064-3.23-0.534c-4.918,2.427-9.375,5.619-13.246,9.491c-9.447,9.447-14.65,22.008-14.65,35.369 c0,13.36,5.203,25.921,14.65,35.368s22.008,14.65,35.368,14.65c13.361,0,25.921-5.203,35.369-14.65 c3.872-3.871,7.064-8.328,9.491-13.246C97.826,68.608,97.611,67.309,96.76,66.458z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import {GlobalConfiguration} from "../cfg"
|
import { GlobalConfiguration } from "../cfg"
|
||||||
import {ValidLocale} from "../i18n"
|
import { ValidLocale } from "../i18n"
|
||||||
import {QuartzPluginData} from "../plugins/vfile"
|
import { QuartzPluginData } from "../plugins/vfile"
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
date: Date
|
date: Date
|
||||||
@ -9,10 +9,7 @@ interface Props {
|
|||||||
|
|
||||||
export type ValidDateType = keyof Required<QuartzPluginData>["dates"]
|
export type ValidDateType = keyof Required<QuartzPluginData>["dates"]
|
||||||
|
|
||||||
export function getDate(
|
export function getDate(cfg: GlobalConfiguration, data: QuartzPluginData): Date | undefined {
|
||||||
cfg: GlobalConfiguration,
|
|
||||||
data: QuartzPluginData,
|
|
||||||
): Date | undefined {
|
|
||||||
if (!cfg.defaultDateType) {
|
if (!cfg.defaultDateType) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Field 'defaultDateType' was not set in the configuration object of quartz.config.ts. See https://quartz.jzhao.xyz/configuration#general-configuration for more details.`,
|
`Field 'defaultDateType' was not set in the configuration object of quartz.config.ts. See https://quartz.jzhao.xyz/configuration#general-configuration for more details.`,
|
||||||
@ -29,6 +26,6 @@ export function formatDate(d: Date, locale: ValidLocale = "en-US"): string {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Date({date, locale}: Props) {
|
export function Date({ date, locale }: Props) {
|
||||||
return <>{formatDate(date, locale)}</>
|
return <>{formatDate(date, locale)}</>
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,4 @@
|
|||||||
import {
|
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
||||||
QuartzComponent,
|
|
||||||
QuartzComponentConstructor,
|
|
||||||
QuartzComponentProps,
|
|
||||||
} from "./types"
|
|
||||||
|
|
||||||
export default ((component?: QuartzComponent) => {
|
export default ((component?: QuartzComponent) => {
|
||||||
if (component) {
|
if (component) {
|
||||||
|
|||||||
@ -1,16 +1,12 @@
|
|||||||
import {
|
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
||||||
QuartzComponent,
|
|
||||||
QuartzComponentConstructor,
|
|
||||||
QuartzComponentProps,
|
|
||||||
} from "./types"
|
|
||||||
import explorerStyle from "./styles/explorer.scss"
|
import explorerStyle from "./styles/explorer.scss"
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import script from "./scripts/explorer.inline"
|
import script from "./scripts/explorer.inline"
|
||||||
import {ExplorerNode, FileNode, Options} from "./ExplorerNode"
|
import { ExplorerNode, FileNode, Options } from "./ExplorerNode"
|
||||||
import {QuartzPluginData} from "../plugins/vfile"
|
import { QuartzPluginData } from "../plugins/vfile"
|
||||||
import {classNames} from "../util/lang"
|
import { classNames } from "../util/lang"
|
||||||
import {i18n} from "../i18n"
|
import { i18n } from "../i18n"
|
||||||
|
|
||||||
// Options interface defined in `ExplorerNode` to avoid circular dependency
|
// Options interface defined in `ExplorerNode` to avoid circular dependency
|
||||||
const defaultOptions = {
|
const defaultOptions = {
|
||||||
@ -43,7 +39,7 @@ const defaultOptions = {
|
|||||||
|
|
||||||
export default ((userOpts?: Partial<Options>) => {
|
export default ((userOpts?: Partial<Options>) => {
|
||||||
// Parse config
|
// Parse config
|
||||||
const opts: Options = {...defaultOptions, ...userOpts}
|
const opts: Options = { ...defaultOptions, ...userOpts }
|
||||||
|
|
||||||
// memoized
|
// memoized
|
||||||
let fileTree: FileNode
|
let fileTree: FileNode
|
||||||
@ -72,9 +68,7 @@ export default ((userOpts?: Partial<Options>) => {
|
|||||||
|
|
||||||
// Get all folders of tree. Initialize with collapsed state
|
// Get all folders of tree. Initialize with collapsed state
|
||||||
// Stringify to pass json tree as data attribute ([data-tree])
|
// Stringify to pass json tree as data attribute ([data-tree])
|
||||||
const folders = fileTree.getFolderPaths(
|
const folders = fileTree.getFolderPaths(opts.folderDefaultState === "collapsed")
|
||||||
opts.folderDefaultState === "collapsed",
|
|
||||||
)
|
|
||||||
jsonTree = JSON.stringify(folders)
|
jsonTree = JSON.stringify(folders)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,8 +92,11 @@ export default ((userOpts?: Partial<Options>) => {
|
|||||||
data-behavior={opts.folderClickBehavior}
|
data-behavior={opts.folderClickBehavior}
|
||||||
data-collapsed={opts.folderDefaultState}
|
data-collapsed={opts.folderDefaultState}
|
||||||
data-savestate={opts.useSavedState}
|
data-savestate={opts.useSavedState}
|
||||||
data-tree={jsonTree}>
|
data-tree={jsonTree}
|
||||||
<h1>{opts.title ?? i18n(cfg.locale).components.explorer.title}</h1>
|
aria-controls="explorer-content"
|
||||||
|
aria-expanded={opts.folderDefaultState === "open"}
|
||||||
|
>
|
||||||
|
<h2>{opts.title ?? i18n(cfg.locale).components.explorer.title}</h2>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
width="14"
|
width="14"
|
||||||
@ -110,7 +107,8 @@ export default ((userOpts?: Partial<Options>) => {
|
|||||||
stroke-width="2"
|
stroke-width="2"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
class="fold">
|
class="fold"
|
||||||
|
>
|
||||||
<polyline points="6 9 12 15 18 9"></polyline>
|
<polyline points="6 9 12 15 18 9"></polyline>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import {QuartzPluginData} from "../plugins/vfile"
|
import { QuartzPluginData } from "../plugins/vfile"
|
||||||
import {
|
import {
|
||||||
joinSegments,
|
joinSegments,
|
||||||
resolveRelative,
|
resolveRelative,
|
||||||
@ -32,10 +32,7 @@ export type FolderState = {
|
|||||||
collapsed: boolean
|
collapsed: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPathSegment(
|
function getPathSegment(fp: FilePath | undefined, idx: number): string | undefined {
|
||||||
fp: FilePath | undefined,
|
|
||||||
idx: number,
|
|
||||||
): string | undefined {
|
|
||||||
if (!fp) {
|
if (!fp) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
@ -51,12 +48,7 @@ export class FileNode {
|
|||||||
file: QuartzPluginData | null
|
file: QuartzPluginData | null
|
||||||
depth: number
|
depth: number
|
||||||
|
|
||||||
constructor(
|
constructor(slugSegment: string, displayName?: string, file?: QuartzPluginData, depth?: number) {
|
||||||
slugSegment: string,
|
|
||||||
displayName?: string,
|
|
||||||
file?: QuartzPluginData,
|
|
||||||
depth?: number,
|
|
||||||
) {
|
|
||||||
this.children = []
|
this.children = []
|
||||||
this.name = slugSegment
|
this.name = slugSegment
|
||||||
this.displayName = displayName ?? file?.frontmatter?.title ?? slugSegment
|
this.displayName = displayName ?? file?.frontmatter?.title ?? slugSegment
|
||||||
@ -81,9 +73,7 @@ export class FileNode {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// direct child
|
// direct child
|
||||||
this.children.push(
|
this.children.push(new FileNode(nextSegment, undefined, fileData.file, this.depth + 1))
|
||||||
new FileNode(nextSegment, undefined, fileData.file, this.depth + 1),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -109,7 +99,7 @@ export class FileNode {
|
|||||||
|
|
||||||
// Add new file to tree
|
// Add new file to tree
|
||||||
add(file: QuartzPluginData) {
|
add(file: QuartzPluginData) {
|
||||||
this.insert({file: file, path: simplifySlug(file.slug!).split("/")})
|
this.insert({ file: file, path: simplifySlug(file.slug!).split("/") })
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -143,7 +133,7 @@ export class FileNode {
|
|||||||
if (!node.file) {
|
if (!node.file) {
|
||||||
const folderPath = joinSegments(currentPath, node.name)
|
const folderPath = joinSegments(currentPath, node.name)
|
||||||
if (folderPath !== "") {
|
if (folderPath !== "") {
|
||||||
folderPaths.push({path: folderPath, collapsed})
|
folderPaths.push({ path: folderPath, collapsed })
|
||||||
}
|
}
|
||||||
|
|
||||||
node.children.forEach((child) => traverse(child, folderPath))
|
node.children.forEach((child) => traverse(child, folderPath))
|
||||||
@ -172,19 +162,13 @@ type ExplorerNodeProps = {
|
|||||||
fullPath?: string
|
fullPath?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ExplorerNode({
|
export function ExplorerNode({ node, opts, fullPath, fileData }: ExplorerNodeProps) {
|
||||||
node,
|
|
||||||
opts,
|
|
||||||
fullPath,
|
|
||||||
fileData,
|
|
||||||
}: ExplorerNodeProps) {
|
|
||||||
// Get options
|
// Get options
|
||||||
const folderBehavior = opts.folderClickBehavior
|
const folderBehavior = opts.folderClickBehavior
|
||||||
const isDefaultOpen = opts.folderDefaultState === "open"
|
const isDefaultOpen = opts.folderDefaultState === "open"
|
||||||
|
|
||||||
// Calculate current folderPath
|
// Calculate current folderPath
|
||||||
const folderPath =
|
const folderPath = node.name !== "" ? joinSegments(fullPath ?? "", node.name) : ""
|
||||||
node.name !== "" ? joinSegments(fullPath ?? "", node.name) : ""
|
|
||||||
const href = resolveRelative(fileData.slug!, folderPath as SimpleSlug) + "/"
|
const href = resolveRelative(fileData.slug!, folderPath as SimpleSlug) + "/"
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -192,9 +176,7 @@ export function ExplorerNode({
|
|||||||
{node.file ? (
|
{node.file ? (
|
||||||
// Single file node
|
// Single file node
|
||||||
<li key={node.file.slug}>
|
<li key={node.file.slug}>
|
||||||
<a
|
<a href={resolveRelative(fileData.slug!, node.file.slug!)} data-for={node.file.slug}>
|
||||||
href={resolveRelative(fileData.slug!, node.file.slug!)}
|
|
||||||
data-for={node.file.slug}>
|
|
||||||
{node.displayName}
|
{node.displayName}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@ -214,19 +196,14 @@ export function ExplorerNode({
|
|||||||
stroke-width="2"
|
stroke-width="2"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
class="folder-icon">
|
class="folder-icon"
|
||||||
|
>
|
||||||
<polyline points="6 9 12 15 18 9"></polyline>
|
<polyline points="6 9 12 15 18 9"></polyline>
|
||||||
</svg>
|
</svg>
|
||||||
{/* render <a> tag if folderBehavior is "link", otherwise render <button> with collapse click event */}
|
{/* render <a> tag if folderBehavior is "link", otherwise render <button> with collapse click event */}
|
||||||
<div key={node.name} data-folderpath={folderPath}>
|
<div key={node.name} data-folderpath={folderPath}>
|
||||||
{folderBehavior === "link" ? (
|
{folderBehavior === "link" ? (
|
||||||
<a
|
<a href={href} data-for={node.name} class="folder-title">
|
||||||
href={resolveRelative(
|
|
||||||
fileData.slug!,
|
|
||||||
folderPath as SimpleSlug,
|
|
||||||
)}
|
|
||||||
data-for={node.name}
|
|
||||||
class="folder-title">
|
|
||||||
{node.displayName}
|
{node.displayName}
|
||||||
</a>
|
</a>
|
||||||
) : (
|
) : (
|
||||||
@ -238,15 +215,15 @@ export function ExplorerNode({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{/* Recursively render children of folder */}
|
{/* Recursively render children of folder */}
|
||||||
<div
|
<div class={`folder-outer ${node.depth === 0 || isDefaultOpen ? "open" : ""}`}>
|
||||||
class={`folder-outer ${node.depth === 0 || isDefaultOpen ? "open" : ""}`}>
|
|
||||||
<ul
|
<ul
|
||||||
// Inline style for left folder paddings
|
// Inline style for left folder paddings
|
||||||
style={{
|
style={{
|
||||||
paddingLeft: node.name !== "" ? "1.4rem" : "0",
|
paddingLeft: node.name !== "" ? "1.4rem" : "0",
|
||||||
}}
|
}}
|
||||||
class="content"
|
class="content"
|
||||||
data-folderul={folderPath}>
|
data-folderul={folderPath}
|
||||||
|
>
|
||||||
{node.children.map((childNode, i) => (
|
{node.children.map((childNode, i) => (
|
||||||
<ExplorerNode
|
<ExplorerNode
|
||||||
node={childNode}
|
node={childNode}
|
||||||
|
|||||||
@ -1,28 +1,21 @@
|
|||||||
import {
|
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
||||||
QuartzComponent,
|
|
||||||
QuartzComponentConstructor,
|
|
||||||
QuartzComponentProps,
|
|
||||||
} from "./types"
|
|
||||||
import style from "./styles/footer.scss"
|
import style from "./styles/footer.scss"
|
||||||
import {version} from "../../package.json"
|
import { version } from "../../package.json"
|
||||||
import {i18n} from "../i18n"
|
import { i18n } from "../i18n"
|
||||||
|
|
||||||
interface Options {
|
interface Options {
|
||||||
links: Record<string, string>
|
links: Record<string, string>
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ((opts?: Options) => {
|
export default ((opts?: Options) => {
|
||||||
const Footer: QuartzComponent = ({
|
const Footer: QuartzComponent = ({ displayClass, cfg }: QuartzComponentProps) => {
|
||||||
displayClass,
|
|
||||||
cfg,
|
|
||||||
}: QuartzComponentProps) => {
|
|
||||||
const year = new Date().getFullYear()
|
const year = new Date().getFullYear()
|
||||||
const links = opts?.links ?? []
|
const links = opts?.links ?? []
|
||||||
return (
|
return (
|
||||||
<footer class={`${displayClass ?? ""}`}>
|
<footer class={`${displayClass ?? ""}`}>
|
||||||
<p>
|
<p>
|
||||||
© {year} Miguel Pimentel · Created with{" "}
|
{i18n(cfg.locale).components.footer.createdWith}{" "}
|
||||||
<a href="https://quartz.jzhao.xyz/">Quartz</a>.
|
<a href="https://quartz.jzhao.xyz/">Quartz v{version}</a> © {year}
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
{Object.entries(links).map(([text, link]) => (
|
{Object.entries(links).map(([text, link]) => (
|
||||||
|
|||||||
@ -1,13 +1,9 @@
|
|||||||
import {
|
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
||||||
QuartzComponent,
|
|
||||||
QuartzComponentConstructor,
|
|
||||||
QuartzComponentProps,
|
|
||||||
} from "./types"
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import script from "./scripts/graph.inline"
|
import script from "./scripts/graph.inline"
|
||||||
import style from "./styles/graph.scss"
|
import style from "./styles/graph.scss"
|
||||||
import {i18n} from "../i18n"
|
import { i18n } from "../i18n"
|
||||||
import {classNames} from "../util/lang"
|
import { classNames } from "../util/lang"
|
||||||
|
|
||||||
export interface D3Config {
|
export interface D3Config {
|
||||||
drag: boolean
|
drag: boolean
|
||||||
@ -61,18 +57,12 @@ const defaultOptions: GraphOptions = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default ((opts?: GraphOptions) => {
|
export default ((opts?: GraphOptions) => {
|
||||||
const Graph: QuartzComponent = ({
|
const Graph: QuartzComponent = ({ displayClass, cfg }: QuartzComponentProps) => {
|
||||||
displayClass,
|
const localGraph = { ...defaultOptions.localGraph, ...opts?.localGraph }
|
||||||
cfg,
|
const globalGraph = { ...defaultOptions.globalGraph, ...opts?.globalGraph }
|
||||||
}: QuartzComponentProps) => {
|
|
||||||
const localGraph = {...defaultOptions.localGraph, ...opts?.localGraph}
|
|
||||||
const globalGraph = {
|
|
||||||
...defaultOptions.globalGraph,
|
|
||||||
...opts?.globalGraph,
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<div class={classNames(displayClass, "graph")}>
|
<div class={classNames(displayClass, "graph")}>
|
||||||
<h2>{i18n(cfg.locale).components.graph.title}</h2>
|
<h3>{i18n(cfg.locale).components.graph.title}</h3>
|
||||||
<div class="graph-outer">
|
<div class="graph-outer">
|
||||||
<div id="graph-container" data-cfg={JSON.stringify(localGraph)}></div>
|
<div id="graph-container" data-cfg={JSON.stringify(localGraph)}></div>
|
||||||
<svg
|
<svg
|
||||||
@ -84,7 +74,8 @@ export default ((opts?: GraphOptions) => {
|
|||||||
y="0px"
|
y="0px"
|
||||||
viewBox="0 0 55 55"
|
viewBox="0 0 55 55"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
xmlSpace="preserve">
|
xmlSpace="preserve"
|
||||||
|
>
|
||||||
<path
|
<path
|
||||||
d="M49,0c-3.309,0-6,2.691-6,6c0,1.035,0.263,2.009,0.726,2.86l-9.829,9.829C32.542,17.634,30.846,17,29,17
|
d="M49,0c-3.309,0-6,2.691-6,6c0,1.035,0.263,2.009,0.726,2.86l-9.829,9.829C32.542,17.634,30.846,17,29,17
|
||||||
s-3.542,0.634-4.898,1.688l-7.669-7.669C16.785,10.424,17,9.74,17,9c0-2.206-1.794-4-4-4S9,6.794,9,9s1.794,4,4,4
|
s-3.542,0.634-4.898,1.688l-7.669-7.669C16.785,10.424,17,9.74,17,9c0-2.206-1.794-4-4-4S9,6.794,9,9s1.794,4,4,4
|
||||||
@ -101,9 +92,7 @@ export default ((opts?: GraphOptions) => {
|
|||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div id="global-graph-outer">
|
<div id="global-graph-outer">
|
||||||
<div
|
<div id="global-graph-container" data-cfg={JSON.stringify(globalGraph)}></div>
|
||||||
id="global-graph-container"
|
|
||||||
data-cfg={JSON.stringify(globalGraph)}></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,25 +1,15 @@
|
|||||||
import {i18n} from "../i18n"
|
import { i18n } from "../i18n"
|
||||||
import {FullSlug, joinSegments, pathToRoot} from "../util/path"
|
import { FullSlug, joinSegments, pathToRoot } from "../util/path"
|
||||||
import {JSResourceToScriptElement} from "../util/resources"
|
import { JSResourceToScriptElement } from "../util/resources"
|
||||||
import {googleFontHref} from "../util/theme"
|
import { googleFontHref } from "../util/theme"
|
||||||
import {
|
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
||||||
QuartzComponent,
|
|
||||||
QuartzComponentConstructor,
|
|
||||||
QuartzComponentProps,
|
|
||||||
} from "./types"
|
|
||||||
|
|
||||||
export default (() => {
|
export default (() => {
|
||||||
const Head: QuartzComponent = ({
|
const Head: QuartzComponent = ({ cfg, fileData, externalResources }: QuartzComponentProps) => {
|
||||||
cfg,
|
const title = fileData.frontmatter?.title ?? i18n(cfg.locale).propertyDefaults.title
|
||||||
fileData,
|
|
||||||
externalResources,
|
|
||||||
}: QuartzComponentProps) => {
|
|
||||||
const title =
|
|
||||||
fileData.frontmatter?.title ?? i18n(cfg.locale).propertyDefaults.title
|
|
||||||
const description =
|
const description =
|
||||||
fileData.description?.trim() ??
|
fileData.description?.trim() ?? i18n(cfg.locale).propertyDefaults.description
|
||||||
i18n(cfg.locale).propertyDefaults.description
|
const { css, js } = externalResources
|
||||||
const {css, js} = externalResources
|
|
||||||
|
|
||||||
const url = new URL(`https://${cfg.baseUrl ?? "example.com"}`)
|
const url = new URL(`https://${cfg.baseUrl ?? "example.com"}`)
|
||||||
const path = url.pathname as FullSlug
|
const path = url.pathname as FullSlug
|
||||||
@ -49,13 +39,7 @@ export default (() => {
|
|||||||
<meta name="description" content={description} />
|
<meta name="description" content={description} />
|
||||||
<meta name="generator" content="Quartz" />
|
<meta name="generator" content="Quartz" />
|
||||||
{css.map((href) => (
|
{css.map((href) => (
|
||||||
<link
|
<link key={href} href={href} rel="stylesheet" type="text/css" spa-preserve />
|
||||||
key={href}
|
|
||||||
href={href}
|
|
||||||
rel="stylesheet"
|
|
||||||
type="text/css"
|
|
||||||
spa-preserve
|
|
||||||
/>
|
|
||||||
))}
|
))}
|
||||||
{js
|
{js
|
||||||
.filter((resource) => resource.loadTime === "beforeDOMReady")
|
.filter((resource) => resource.loadTime === "beforeDOMReady")
|
||||||
|
|||||||
@ -1,10 +1,6 @@
|
|||||||
import {
|
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
||||||
QuartzComponent,
|
|
||||||
QuartzComponentConstructor,
|
|
||||||
QuartzComponentProps,
|
|
||||||
} from "./types"
|
|
||||||
|
|
||||||
const Header: QuartzComponent = ({children}: QuartzComponentProps) => {
|
const Header: QuartzComponent = ({ children }: QuartzComponentProps) => {
|
||||||
return children.length > 0 ? <header>{children}</header> : null
|
return children.length > 0 ? <header>{children}</header> : null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,4 @@
|
|||||||
import {
|
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
||||||
QuartzComponent,
|
|
||||||
QuartzComponentConstructor,
|
|
||||||
QuartzComponentProps,
|
|
||||||
} from "./types"
|
|
||||||
|
|
||||||
export default ((component?: QuartzComponent) => {
|
export default ((component?: QuartzComponent) => {
|
||||||
if (component) {
|
if (component) {
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import {FullSlug, resolveRelative} from "../util/path"
|
import { FullSlug, resolveRelative } from "../util/path"
|
||||||
import {QuartzPluginData} from "../plugins/vfile"
|
import { QuartzPluginData } from "../plugins/vfile"
|
||||||
import {Date, getDate} from "./Date"
|
import { Date, getDate } from "./Date"
|
||||||
import {QuartzComponent, QuartzComponentProps} from "./types"
|
import { QuartzComponent, QuartzComponentProps } from "./types"
|
||||||
import {GlobalConfiguration} from "../cfg"
|
import { GlobalConfiguration } from "../cfg"
|
||||||
|
|
||||||
export type SortFn = (f1: QuartzPluginData, f2: QuartzPluginData) => number
|
export type SortFn = (f1: QuartzPluginData, f2: QuartzPluginData) => number
|
||||||
|
|
||||||
@ -30,13 +30,9 @@ type Props = {
|
|||||||
sort?: SortFn
|
sort?: SortFn
|
||||||
} & QuartzComponentProps
|
} & QuartzComponentProps
|
||||||
|
|
||||||
export const PageList: QuartzComponent = ({
|
export const PageList: QuartzComponent = ({ cfg, fileData, allFiles, limit, sort }: Props) => {
|
||||||
cfg,
|
const sorter = sort ?? byDateAndAlphabetical(cfg)
|
||||||
fileData,
|
let list = allFiles.sort(sorter)
|
||||||
allFiles,
|
|
||||||
limit,
|
|
||||||
}: Props) => {
|
|
||||||
let list = allFiles.sort(byDateAndAlphabetical(cfg))
|
|
||||||
if (limit) {
|
if (limit) {
|
||||||
list = list.slice(0, limit)
|
list = list.slice(0, limit)
|
||||||
}
|
}
|
||||||
@ -57,9 +53,7 @@ export const PageList: QuartzComponent = ({
|
|||||||
)}
|
)}
|
||||||
<div class="desc">
|
<div class="desc">
|
||||||
<h3>
|
<h3>
|
||||||
<a
|
<a href={resolveRelative(fileData.slug!, page.slug!)} class="internal">
|
||||||
href={resolveRelative(fileData.slug!, page.slug!)}
|
|
||||||
class="internal">
|
|
||||||
{title}
|
{title}
|
||||||
</a>
|
</a>
|
||||||
</h3>
|
</h3>
|
||||||
@ -69,10 +63,8 @@ export const PageList: QuartzComponent = ({
|
|||||||
<li>
|
<li>
|
||||||
<a
|
<a
|
||||||
class="internal tag-link"
|
class="internal tag-link"
|
||||||
href={resolveRelative(
|
href={resolveRelative(fileData.slug!, `tags/${tag}` as FullSlug)}
|
||||||
fileData.slug!,
|
>
|
||||||
`tags/${tag}` as FullSlug,
|
|
||||||
)}>
|
|
||||||
{tag}
|
{tag}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@ -1,17 +1,9 @@
|
|||||||
import {pathToRoot} from "../util/path"
|
import { pathToRoot } from "../util/path"
|
||||||
import {
|
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
||||||
QuartzComponent,
|
import { classNames } from "../util/lang"
|
||||||
QuartzComponentConstructor,
|
import { i18n } from "../i18n"
|
||||||
QuartzComponentProps,
|
|
||||||
} from "./types"
|
|
||||||
import {classNames} from "../util/lang"
|
|
||||||
import {i18n} from "../i18n"
|
|
||||||
|
|
||||||
const PageTitle: QuartzComponent = ({
|
const PageTitle: QuartzComponent = ({ fileData, cfg, displayClass }: QuartzComponentProps) => {
|
||||||
fileData,
|
|
||||||
cfg,
|
|
||||||
displayClass,
|
|
||||||
}: QuartzComponentProps) => {
|
|
||||||
const title = cfg?.pageTitle ?? i18n(cfg.locale).propertyDefaults.title
|
const title = cfg?.pageTitle ?? i18n(cfg.locale).propertyDefaults.title
|
||||||
const baseDir = pathToRoot(fileData.slug!)
|
const baseDir = pathToRoot(fileData.slug!)
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,16 +1,12 @@
|
|||||||
import {
|
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
||||||
QuartzComponent,
|
import { FullSlug, SimpleSlug, resolveRelative } from "../util/path"
|
||||||
QuartzComponentConstructor,
|
import { QuartzPluginData } from "../plugins/vfile"
|
||||||
QuartzComponentProps,
|
import { byDateAndAlphabetical } from "./PageList"
|
||||||
} from "./types"
|
|
||||||
import {FullSlug, SimpleSlug, resolveRelative} from "../util/path"
|
|
||||||
import {QuartzPluginData} from "../plugins/vfile"
|
|
||||||
import {byDateAndAlphabetical} from "./PageList"
|
|
||||||
import style from "./styles/recentNotes.scss"
|
import style from "./styles/recentNotes.scss"
|
||||||
import {Date, getDate} from "./Date"
|
import { Date, getDate } from "./Date"
|
||||||
import {GlobalConfiguration} from "../cfg"
|
import { GlobalConfiguration } from "../cfg"
|
||||||
import {i18n} from "../i18n"
|
import { i18n } from "../i18n"
|
||||||
import {classNames} from "../util/lang"
|
import { classNames } from "../util/lang"
|
||||||
|
|
||||||
interface Options {
|
interface Options {
|
||||||
title?: string
|
title?: string
|
||||||
@ -36,7 +32,7 @@ export default ((userOpts?: Partial<Options>) => {
|
|||||||
displayClass,
|
displayClass,
|
||||||
cfg,
|
cfg,
|
||||||
}: QuartzComponentProps) => {
|
}: QuartzComponentProps) => {
|
||||||
const opts = {...defaultOptions(cfg), ...userOpts}
|
const opts = { ...defaultOptions(cfg), ...userOpts }
|
||||||
const pages = allFiles.filter(opts.filter).sort(opts.sort)
|
const pages = allFiles.filter(opts.filter).sort(opts.sort)
|
||||||
const remaining = Math.max(0, pages.length - opts.limit)
|
const remaining = Math.max(0, pages.length - opts.limit)
|
||||||
return (
|
return (
|
||||||
@ -44,8 +40,7 @@ export default ((userOpts?: Partial<Options>) => {
|
|||||||
<h3>{opts.title ?? i18n(cfg.locale).components.recentNotes.title}</h3>
|
<h3>{opts.title ?? i18n(cfg.locale).components.recentNotes.title}</h3>
|
||||||
<ul class="recent-ul">
|
<ul class="recent-ul">
|
||||||
{pages.slice(0, opts.limit).map((page) => {
|
{pages.slice(0, opts.limit).map((page) => {
|
||||||
const title =
|
const title = page.frontmatter?.title ?? i18n(cfg.locale).propertyDefaults.title
|
||||||
page.frontmatter?.title ?? i18n(cfg.locale).propertyDefaults.title
|
|
||||||
const tags = page.frontmatter?.tags ?? []
|
const tags = page.frontmatter?.tags ?? []
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -53,9 +48,7 @@ export default ((userOpts?: Partial<Options>) => {
|
|||||||
<div class="section">
|
<div class="section">
|
||||||
<div class="desc">
|
<div class="desc">
|
||||||
<h3>
|
<h3>
|
||||||
<a
|
<a href={resolveRelative(fileData.slug!, page.slug!)} class="internal">
|
||||||
href={resolveRelative(fileData.slug!, page.slug!)}
|
|
||||||
class="internal">
|
|
||||||
{title}
|
{title}
|
||||||
</a>
|
</a>
|
||||||
</h3>
|
</h3>
|
||||||
@ -65,20 +58,20 @@ export default ((userOpts?: Partial<Options>) => {
|
|||||||
<Date date={getDate(cfg, page)!} locale={cfg.locale} />
|
<Date date={getDate(cfg, page)!} locale={cfg.locale} />
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
<ul class="tags">
|
{opts.showTags && (
|
||||||
{tags.map((tag) => (
|
<ul class="tags">
|
||||||
<li>
|
{tags.map((tag) => (
|
||||||
<a
|
<li>
|
||||||
class="internal tag-link"
|
<a
|
||||||
href={resolveRelative(
|
class="internal tag-link"
|
||||||
fileData.slug!,
|
href={resolveRelative(fileData.slug!, `tags/${tag}` as FullSlug)}
|
||||||
`tags/${tag}` as FullSlug,
|
>
|
||||||
)}>
|
{tag}
|
||||||
{tag}
|
</a>
|
||||||
</a>
|
</li>
|
||||||
</li>
|
))}
|
||||||
))}
|
</ul>
|
||||||
</ul>
|
)}
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
)
|
)
|
||||||
@ -87,9 +80,7 @@ export default ((userOpts?: Partial<Options>) => {
|
|||||||
{opts.linkToMore && remaining > 0 && (
|
{opts.linkToMore && remaining > 0 && (
|
||||||
<p>
|
<p>
|
||||||
<a href={resolveRelative(fileData.slug!, opts.linkToMore)}>
|
<a href={resolveRelative(fileData.slug!, opts.linkToMore)}>
|
||||||
{i18n(cfg.locale).components.recentNotes.seeRemainingMore({
|
{i18n(cfg.locale).components.recentNotes.seeRemainingMore({ remaining })}
|
||||||
remaining,
|
|
||||||
})}
|
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -1,13 +1,9 @@
|
|||||||
import {
|
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
||||||
QuartzComponent,
|
|
||||||
QuartzComponentConstructor,
|
|
||||||
QuartzComponentProps,
|
|
||||||
} from "./types"
|
|
||||||
import style from "./styles/search.scss"
|
import style from "./styles/search.scss"
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import script from "./scripts/search.inline"
|
import script from "./scripts/search.inline"
|
||||||
import {classNames} from "../util/lang"
|
import { classNames } from "../util/lang"
|
||||||
import {i18n} from "../i18n"
|
import { i18n } from "../i18n"
|
||||||
|
|
||||||
export interface SearchOptions {
|
export interface SearchOptions {
|
||||||
enablePreview: boolean
|
enablePreview: boolean
|
||||||
@ -18,26 +14,15 @@ const defaultOptions: SearchOptions = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default ((userOpts?: Partial<SearchOptions>) => {
|
export default ((userOpts?: Partial<SearchOptions>) => {
|
||||||
const Search: QuartzComponent = ({
|
const Search: QuartzComponent = ({ displayClass, cfg }: QuartzComponentProps) => {
|
||||||
displayClass,
|
const opts = { ...defaultOptions, ...userOpts }
|
||||||
cfg,
|
const searchPlaceholder = i18n(cfg.locale).components.search.searchBarPlaceholder
|
||||||
}: QuartzComponentProps) => {
|
|
||||||
const opts = {...defaultOptions, ...userOpts}
|
|
||||||
const searchPlaceholder = i18n(cfg.locale).components.search
|
|
||||||
.searchBarPlaceholder
|
|
||||||
return (
|
return (
|
||||||
<div class={classNames(displayClass, "search")}>
|
<div class={classNames(displayClass, "search")}>
|
||||||
<div id="search-icon">
|
<button class="search-button" id="search-button">
|
||||||
{/* <p>Search</p> */}
|
<p>{i18n(cfg.locale).components.search.title}</p>
|
||||||
<div></div>
|
<svg role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19.9 19.7">
|
||||||
<svg
|
<title>Search</title>
|
||||||
tabIndex={0}
|
|
||||||
aria-labelledby="title desc"
|
|
||||||
role="img"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 19.9 19.7">
|
|
||||||
<title id="title">Search</title>
|
|
||||||
<desc id="desc">Search</desc>
|
|
||||||
<g class="search-path" fill="none">
|
<g class="search-path" fill="none">
|
||||||
<path stroke-linecap="square" d="M18.5 18.3l-5.4-5.4" />
|
<path stroke-linecap="square" d="M18.5 18.3l-5.4-5.4" />
|
||||||
<circle cx="8" cy="8" r="7" />
|
<circle cx="8" cy="8" r="7" />
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import {QuartzComponentConstructor, QuartzComponentProps} from "./types"
|
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
||||||
import {classNames} from "../util/lang"
|
import { classNames } from "../util/lang"
|
||||||
|
|
||||||
function Spacer({displayClass}: QuartzComponentProps) {
|
function Spacer({ displayClass }: QuartzComponentProps) {
|
||||||
return <div class={classNames(displayClass, "spacer")}></div>
|
return <div class={classNames(displayClass, "spacer")}></div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,15 +1,11 @@
|
|||||||
import {
|
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
||||||
QuartzComponent,
|
|
||||||
QuartzComponentConstructor,
|
|
||||||
QuartzComponentProps,
|
|
||||||
} from "./types"
|
|
||||||
import legacyStyle from "./styles/legacyToc.scss"
|
import legacyStyle from "./styles/legacyToc.scss"
|
||||||
import modernStyle from "./styles/toc.scss"
|
import modernStyle from "./styles/toc.scss"
|
||||||
import {classNames} from "../util/lang"
|
import { classNames } from "../util/lang"
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import script from "./scripts/toc.inline"
|
import script from "./scripts/toc.inline"
|
||||||
import {i18n} from "../i18n"
|
import { i18n } from "../i18n"
|
||||||
|
|
||||||
interface Options {
|
interface Options {
|
||||||
layout: "modern" | "legacy"
|
layout: "modern" | "legacy"
|
||||||
@ -33,7 +29,10 @@ const TableOfContents: QuartzComponent = ({
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
id="toc"
|
id="toc"
|
||||||
class={fileData.collapseToc ? "collapsed" : ""}>
|
class={fileData.collapseToc ? "collapsed" : ""}
|
||||||
|
aria-controls="toc-content"
|
||||||
|
aria-expanded={!fileData.collapseToc}
|
||||||
|
>
|
||||||
<h3>{i18n(cfg.locale).components.tableOfContents.title}</h3>
|
<h3>{i18n(cfg.locale).components.tableOfContents.title}</h3>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
@ -45,7 +44,8 @@ const TableOfContents: QuartzComponent = ({
|
|||||||
stroke-width="2"
|
stroke-width="2"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
class="fold">
|
class="fold"
|
||||||
|
>
|
||||||
<polyline points="6 9 12 15 18 9"></polyline>
|
<polyline points="6 9 12 15 18 9"></polyline>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
@ -66,10 +66,7 @@ const TableOfContents: QuartzComponent = ({
|
|||||||
TableOfContents.css = modernStyle
|
TableOfContents.css = modernStyle
|
||||||
TableOfContents.afterDOMLoaded = script
|
TableOfContents.afterDOMLoaded = script
|
||||||
|
|
||||||
const LegacyTableOfContents: QuartzComponent = ({
|
const LegacyTableOfContents: QuartzComponent = ({ fileData, cfg }: QuartzComponentProps) => {
|
||||||
fileData,
|
|
||||||
cfg,
|
|
||||||
}: QuartzComponentProps) => {
|
|
||||||
if (!fileData.toc) {
|
if (!fileData.toc) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,15 +1,8 @@
|
|||||||
import {pathToRoot, slugTag} from "../util/path"
|
import { pathToRoot, slugTag } from "../util/path"
|
||||||
import {
|
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
||||||
QuartzComponent,
|
import { classNames } from "../util/lang"
|
||||||
QuartzComponentConstructor,
|
|
||||||
QuartzComponentProps,
|
|
||||||
} from "./types"
|
|
||||||
import {classNames} from "../util/lang"
|
|
||||||
|
|
||||||
const TagList: QuartzComponent = ({
|
const TagList: QuartzComponent = ({ fileData, displayClass }: QuartzComponentProps) => {
|
||||||
fileData,
|
|
||||||
displayClass,
|
|
||||||
}: QuartzComponentProps) => {
|
|
||||||
const tags = fileData.frontmatter?.tags
|
const tags = fileData.frontmatter?.tags
|
||||||
const baseDir = pathToRoot(fileData.slug!)
|
const baseDir = pathToRoot(fileData.slug!)
|
||||||
if (tags && tags.length > 0) {
|
if (tags && tags.length > 0) {
|
||||||
|
|||||||
@ -1,11 +1,7 @@
|
|||||||
import {i18n} from "../../i18n"
|
import { i18n } from "../../i18n"
|
||||||
import {
|
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "../types"
|
||||||
QuartzComponent,
|
|
||||||
QuartzComponentConstructor,
|
|
||||||
QuartzComponentProps,
|
|
||||||
} from "../types"
|
|
||||||
|
|
||||||
const NotFound: QuartzComponent = ({cfg}: QuartzComponentProps) => {
|
const NotFound: QuartzComponent = ({ cfg }: QuartzComponentProps) => {
|
||||||
// If baseUrl contains a pathname after the domain, use this as the home link
|
// If baseUrl contains a pathname after the domain, use this as the home link
|
||||||
const url = new URL(`https://${cfg.baseUrl ?? "example.com"}`)
|
const url = new URL(`https://${cfg.baseUrl ?? "example.com"}`)
|
||||||
const baseDir = url.pathname
|
const baseDir = url.pathname
|
||||||
|
|||||||
@ -1,11 +1,7 @@
|
|||||||
import {htmlToJsx} from "../../util/jsx"
|
import { htmlToJsx } from "../../util/jsx"
|
||||||
import {
|
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "../types"
|
||||||
QuartzComponent,
|
|
||||||
QuartzComponentConstructor,
|
|
||||||
QuartzComponentProps,
|
|
||||||
} from "../types"
|
|
||||||
|
|
||||||
const Content: QuartzComponent = ({fileData, tree}: QuartzComponentProps) => {
|
const Content: QuartzComponent = ({ fileData, tree }: QuartzComponentProps) => {
|
||||||
const content = htmlToJsx(fileData.filePath!, tree)
|
const content = htmlToJsx(fileData.filePath!, tree)
|
||||||
const classes: string[] = fileData.frontmatter?.cssclasses ?? []
|
const classes: string[] = fileData.frontmatter?.cssclasses ?? []
|
||||||
const classString = ["popover-hint", ...classes].join(" ")
|
const classString = ["popover-hint", ...classes].join(" ")
|
||||||
|
|||||||
@ -1,16 +1,12 @@
|
|||||||
import {
|
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "../types"
|
||||||
QuartzComponent,
|
|
||||||
QuartzComponentConstructor,
|
|
||||||
QuartzComponentProps,
|
|
||||||
} from "../types"
|
|
||||||
import path from "path"
|
import path from "path"
|
||||||
|
|
||||||
import style from "../styles/listPage.scss"
|
import style from "../styles/listPage.scss"
|
||||||
import {PageList} from "../PageList"
|
import { PageList, SortFn } from "../PageList"
|
||||||
import {stripSlashes, simplifySlug} from "../../util/path"
|
import { stripSlashes, simplifySlug } from "../../util/path"
|
||||||
import {Root} from "hast"
|
import { Root } from "hast"
|
||||||
import {htmlToJsx} from "../../util/jsx"
|
import { htmlToJsx } from "../../util/jsx"
|
||||||
import {i18n} from "../../i18n"
|
import { i18n } from "../../i18n"
|
||||||
|
|
||||||
interface FolderContentOptions {
|
interface FolderContentOptions {
|
||||||
/**
|
/**
|
||||||
@ -25,15 +21,14 @@ const defaultOptions: FolderContentOptions = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default ((opts?: Partial<FolderContentOptions>) => {
|
export default ((opts?: Partial<FolderContentOptions>) => {
|
||||||
const options: FolderContentOptions = {...defaultOptions, ...opts}
|
const options: FolderContentOptions = { ...defaultOptions, ...opts }
|
||||||
|
|
||||||
const FolderContent: QuartzComponent = (props: QuartzComponentProps) => {
|
const FolderContent: QuartzComponent = (props: QuartzComponentProps) => {
|
||||||
const {tree, fileData, allFiles, cfg} = props
|
const { tree, fileData, allFiles, cfg } = props
|
||||||
const folderSlug = stripSlashes(simplifySlug(fileData.slug!))
|
const folderSlug = stripSlashes(simplifySlug(fileData.slug!))
|
||||||
const allPagesInFolder = allFiles.filter((file) => {
|
const allPagesInFolder = allFiles.filter((file) => {
|
||||||
const fileSlug = stripSlashes(simplifySlug(file.slug!))
|
const fileSlug = stripSlashes(simplifySlug(file.slug!))
|
||||||
const prefixed =
|
const prefixed = fileSlug.startsWith(folderSlug) && fileSlug !== folderSlug
|
||||||
fileSlug.startsWith(folderSlug) && fileSlug !== folderSlug
|
|
||||||
const folderParts = folderSlug.split(path.posix.sep)
|
const folderParts = folderSlug.split(path.posix.sep)
|
||||||
const fileParts = fileSlug.split(path.posix.sep)
|
const fileParts = fileSlug.split(path.posix.sep)
|
||||||
const isDirectChild = fileParts.length === folderParts.length + 1
|
const isDirectChild = fileParts.length === folderParts.length + 1
|
||||||
|
|||||||
@ -1,100 +1,100 @@
|
|||||||
import {
|
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "../types"
|
||||||
QuartzComponent,
|
|
||||||
QuartzComponentConstructor,
|
|
||||||
QuartzComponentProps,
|
|
||||||
} from "../types"
|
|
||||||
import style from "../styles/listPage.scss"
|
import style from "../styles/listPage.scss"
|
||||||
import {PageList} from "../PageList"
|
import { PageList, SortFn } from "../PageList"
|
||||||
import {FullSlug, getAllSegmentPrefixes, simplifySlug} from "../../util/path"
|
import { FullSlug, getAllSegmentPrefixes, simplifySlug } from "../../util/path"
|
||||||
import {QuartzPluginData} from "../../plugins/vfile"
|
import { QuartzPluginData } from "../../plugins/vfile"
|
||||||
import {Root} from "hast"
|
import { Root } from "hast"
|
||||||
import {htmlToJsx} from "../../util/jsx"
|
import { htmlToJsx } from "../../util/jsx"
|
||||||
import {i18n} from "../../i18n"
|
import { i18n } from "../../i18n"
|
||||||
|
|
||||||
const numPages = 10
|
interface TagContentOptions {
|
||||||
const TagContent: QuartzComponent = (props: QuartzComponentProps) => {
|
sort?: SortFn
|
||||||
const {tree, fileData, allFiles, cfg} = props
|
numPages: number
|
||||||
const slug = fileData.slug
|
}
|
||||||
|
|
||||||
if (!(slug?.startsWith("tags/") || slug === "tags")) {
|
const defaultOptions: TagContentOptions = {
|
||||||
throw new Error(
|
numPages: 10,
|
||||||
`Component "TagContent" tried to render a non-tag page: ${slug}`,
|
}
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const tag = simplifySlug(slug.slice("tags/".length) as FullSlug)
|
export default ((opts?: Partial<TagContentOptions>) => {
|
||||||
const allPagesWithTag = (tag: string) =>
|
const options: TagContentOptions = { ...defaultOptions, ...opts }
|
||||||
allFiles.filter((file) =>
|
|
||||||
(file.frontmatter?.tags ?? [])
|
|
||||||
.flatMap(getAllSegmentPrefixes)
|
|
||||||
.includes(tag),
|
|
||||||
)
|
|
||||||
|
|
||||||
const content =
|
const TagContent: QuartzComponent = (props: QuartzComponentProps) => {
|
||||||
(tree as Root).children.length === 0
|
const { tree, fileData, allFiles, cfg } = props
|
||||||
? fileData.description
|
const slug = fileData.slug
|
||||||
: htmlToJsx(fileData.filePath!, tree)
|
|
||||||
const cssClasses: string[] = fileData.frontmatter?.cssclasses ?? []
|
if (!(slug?.startsWith("tags/") || slug === "tags")) {
|
||||||
const classes = ["popover-hint", ...cssClasses].join(" ")
|
throw new Error(`Component "TagContent" tried to render a non-tag page: ${slug}`)
|
||||||
if (tag === "/") {
|
|
||||||
const tags = [
|
|
||||||
...new Set(
|
|
||||||
allFiles
|
|
||||||
.flatMap((data) => data.frontmatter?.tags ?? [])
|
|
||||||
.flatMap(getAllSegmentPrefixes),
|
|
||||||
),
|
|
||||||
].sort((a, b) => a.localeCompare(b))
|
|
||||||
const tagItemMap: Map<string, QuartzPluginData[]> = new Map()
|
|
||||||
for (const tag of tags) {
|
|
||||||
tagItemMap.set(tag, allPagesWithTag(tag))
|
|
||||||
}
|
}
|
||||||
return (
|
|
||||||
<div class={classes}>
|
|
||||||
<article>
|
|
||||||
<p>{content}</p>
|
|
||||||
</article>
|
|
||||||
<p>
|
|
||||||
{i18n(cfg.locale).pages.tagContent.totalTags({
|
|
||||||
count: tags.length,
|
|
||||||
})}
|
|
||||||
</p>
|
|
||||||
<div>
|
|
||||||
{tags.map((tag) => {
|
|
||||||
const pages = tagItemMap.get(tag)!
|
|
||||||
const listProps = {
|
|
||||||
...props,
|
|
||||||
allFiles: pages,
|
|
||||||
}
|
|
||||||
|
|
||||||
const contentPage = allFiles.filter(
|
const tag = simplifySlug(slug.slice("tags/".length) as FullSlug)
|
||||||
(file) => file.slug === `tags/${tag}`,
|
const allPagesWithTag = (tag: string) =>
|
||||||
)[0]
|
allFiles.filter((file) =>
|
||||||
const content = contentPage?.description
|
(file.frontmatter?.tags ?? []).flatMap(getAllSegmentPrefixes).includes(tag),
|
||||||
return (
|
)
|
||||||
<div>
|
|
||||||
<h2>
|
const content =
|
||||||
<a class="internal tag-link" href={`../tags/${tag}`}>
|
(tree as Root).children.length === 0
|
||||||
{tag}
|
? fileData.description
|
||||||
</a>
|
: htmlToJsx(fileData.filePath!, tree)
|
||||||
</h2>
|
const cssClasses: string[] = fileData.frontmatter?.cssclasses ?? []
|
||||||
{content && <p>{content}</p>}
|
const classes = ["popover-hint", ...cssClasses].join(" ")
|
||||||
<div class="page-listing">
|
if (tag === "/") {
|
||||||
<p>
|
const tags = [
|
||||||
{i18n(cfg.locale).pages.tagContent.itemsUnderTag({
|
...new Set(
|
||||||
count: pages.length,
|
allFiles.flatMap((data) => data.frontmatter?.tags ?? []).flatMap(getAllSegmentPrefixes),
|
||||||
})}
|
),
|
||||||
{pages.length > numPages && (
|
].sort((a, b) => a.localeCompare(b))
|
||||||
<>
|
const tagItemMap: Map<string, QuartzPluginData[]> = new Map()
|
||||||
{" "}
|
for (const tag of tags) {
|
||||||
<span>
|
tagItemMap.set(tag, allPagesWithTag(tag))
|
||||||
{i18n(cfg.locale).pages.tagContent.showingFirst({
|
}
|
||||||
count: numPages,
|
return (
|
||||||
})}
|
<div class={classes}>
|
||||||
</span>
|
<article>
|
||||||
</>
|
<p>{content}</p>
|
||||||
)}
|
</article>
|
||||||
</p>
|
<p>{i18n(cfg.locale).pages.tagContent.totalTags({ count: tags.length })}</p>
|
||||||
<PageList limit={numPages} {...listProps} />
|
<div>
|
||||||
|
{tags.map((tag) => {
|
||||||
|
const pages = tagItemMap.get(tag)!
|
||||||
|
const listProps = {
|
||||||
|
...props,
|
||||||
|
allFiles: pages,
|
||||||
|
}
|
||||||
|
|
||||||
|
const contentPage = allFiles.filter((file) => file.slug === `tags/${tag}`).at(0)
|
||||||
|
|
||||||
|
const root = contentPage?.htmlAst
|
||||||
|
const content =
|
||||||
|
!root || root?.children.length === 0
|
||||||
|
? contentPage?.description
|
||||||
|
: htmlToJsx(contentPage.filePath!, root)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h2>
|
||||||
|
<a class="internal tag-link" href={`../tags/${tag}`}>
|
||||||
|
{tag}
|
||||||
|
</a>
|
||||||
|
</h2>
|
||||||
|
{content && <p>{content}</p>}
|
||||||
|
<div class="page-listing">
|
||||||
|
<p>
|
||||||
|
{i18n(cfg.locale).pages.tagContent.itemsUnderTag({ count: pages.length })}
|
||||||
|
{pages.length > options.numPages && (
|
||||||
|
<>
|
||||||
|
{" "}
|
||||||
|
<span>
|
||||||
|
{i18n(cfg.locale).pages.tagContent.showingFirst({
|
||||||
|
count: options.numPages,
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<PageList limit={options.numPages} {...listProps} sort={opts?.sort} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
@ -108,17 +108,14 @@ const TagContent: QuartzComponent = (props: QuartzComponentProps) => {
|
|||||||
allFiles: pages,
|
allFiles: pages,
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class={classes}>
|
<div class={classes}>
|
||||||
<article>{content}</article>
|
<article>{content}</article>
|
||||||
<div class="page-listing">
|
<div class="page-listing">
|
||||||
<p>
|
<p>{i18n(cfg.locale).pages.tagContent.itemsUnderTag({ count: pages.length })}</p>
|
||||||
{i18n(cfg.locale).pages.tagContent.itemsUnderTag({
|
<div>
|
||||||
count: pages.length,
|
<PageList {...listProps} />
|
||||||
})}
|
</div>
|
||||||
</p>
|
|
||||||
<div>
|
|
||||||
<PageList {...listProps} />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,19 +1,13 @@
|
|||||||
import {render} from "preact-render-to-string"
|
import { render } from "preact-render-to-string"
|
||||||
import {QuartzComponent, QuartzComponentProps} from "./types"
|
import { QuartzComponent, QuartzComponentProps } from "./types"
|
||||||
import HeaderConstructor from "./Header"
|
import HeaderConstructor from "./Header"
|
||||||
import BodyConstructor from "./Body"
|
import BodyConstructor from "./Body"
|
||||||
import {JSResourceToScriptElement, StaticResources} from "../util/resources"
|
import { JSResourceToScriptElement, StaticResources } from "../util/resources"
|
||||||
import {
|
import { clone, FullSlug, RelativeURL, joinSegments, normalizeHastElement } from "../util/path"
|
||||||
clone,
|
import { visit } from "unist-util-visit"
|
||||||
FullSlug,
|
import { Root, Element, ElementContent } from "hast"
|
||||||
RelativeURL,
|
import { GlobalConfiguration } from "../cfg"
|
||||||
joinSegments,
|
import { i18n } from "../i18n"
|
||||||
normalizeHastElement,
|
|
||||||
} from "../util/path"
|
|
||||||
import {visit} from "unist-util-visit"
|
|
||||||
import {Root, Element, ElementContent} from "hast"
|
|
||||||
import {GlobalConfiguration} from "../cfg"
|
|
||||||
import {i18n} from "../i18n"
|
|
||||||
|
|
||||||
interface RenderComponents {
|
interface RenderComponents {
|
||||||
head: QuartzComponent
|
head: QuartzComponent
|
||||||
@ -77,9 +71,7 @@ export function renderPage(
|
|||||||
if (classNames.includes("transclude")) {
|
if (classNames.includes("transclude")) {
|
||||||
const inner = node.children[0] as Element
|
const inner = node.children[0] as Element
|
||||||
const transcludeTarget = inner.properties["data-slug"] as FullSlug
|
const transcludeTarget = inner.properties["data-slug"] as FullSlug
|
||||||
const page = componentData.allFiles.find(
|
const page = componentData.allFiles.find((f) => f.slug === transcludeTarget)
|
||||||
(f) => f.slug === transcludeTarget,
|
|
||||||
)
|
|
||||||
if (!page) {
|
if (!page) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -104,16 +96,9 @@ export function renderPage(
|
|||||||
{
|
{
|
||||||
type: "element",
|
type: "element",
|
||||||
tagName: "a",
|
tagName: "a",
|
||||||
properties: {
|
properties: { href: inner.properties?.href, class: ["internal", "transclude-src"] },
|
||||||
href: inner.properties?.href,
|
|
||||||
class: ["internal", "transclude-src"],
|
|
||||||
},
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{ type: "text", value: i18n(cfg.locale).components.transcludes.linkToOriginal },
|
||||||
type: "text",
|
|
||||||
value: i18n(cfg.locale).components.transcludes
|
|
||||||
.linkToOriginal,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@ -126,8 +111,7 @@ export function renderPage(
|
|||||||
let endIdx = undefined
|
let endIdx = undefined
|
||||||
for (const [i, el] of page.htmlAst.children.entries()) {
|
for (const [i, el] of page.htmlAst.children.entries()) {
|
||||||
// skip non-headers
|
// skip non-headers
|
||||||
if (!(el.type === "element" && el.tagName.match(headerRegex)))
|
if (!(el.type === "element" && el.tagName.match(headerRegex))) continue
|
||||||
continue
|
|
||||||
const depth = Number(el.tagName.substring(1))
|
const depth = Number(el.tagName.substring(1))
|
||||||
|
|
||||||
// lookin for our blockref
|
// lookin for our blockref
|
||||||
@ -149,23 +133,15 @@ export function renderPage(
|
|||||||
}
|
}
|
||||||
|
|
||||||
node.children = [
|
node.children = [
|
||||||
...(
|
...(page.htmlAst.children.slice(startIdx, endIdx) as ElementContent[]).map((child) =>
|
||||||
page.htmlAst.children.slice(startIdx, endIdx) as ElementContent[]
|
|
||||||
).map((child) =>
|
|
||||||
normalizeHastElement(child as Element, slug, transcludeTarget),
|
normalizeHastElement(child as Element, slug, transcludeTarget),
|
||||||
),
|
),
|
||||||
{
|
{
|
||||||
type: "element",
|
type: "element",
|
||||||
tagName: "a",
|
tagName: "a",
|
||||||
properties: {
|
properties: { href: inner.properties?.href, class: ["internal", "transclude-src"] },
|
||||||
href: inner.properties?.href,
|
|
||||||
class: ["internal", "transclude-src"],
|
|
||||||
},
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{ type: "text", value: i18n(cfg.locale).components.transcludes.linkToOriginal },
|
||||||
type: "text",
|
|
||||||
value: i18n(cfg.locale).components.transcludes.linkToOriginal,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@ -193,15 +169,9 @@ export function renderPage(
|
|||||||
{
|
{
|
||||||
type: "element",
|
type: "element",
|
||||||
tagName: "a",
|
tagName: "a",
|
||||||
properties: {
|
properties: { href: inner.properties?.href, class: ["internal", "transclude-src"] },
|
||||||
href: inner.properties?.href,
|
|
||||||
class: ["internal", "transclude-src"],
|
|
||||||
},
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{ type: "text", value: i18n(cfg.locale).components.transcludes.linkToOriginal },
|
||||||
type: "text",
|
|
||||||
value: i18n(cfg.locale).components.transcludes.linkToOriginal,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@ -242,12 +212,9 @@ export function renderPage(
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
const lang =
|
const lang = componentData.fileData.frontmatter?.lang ?? cfg.locale?.split("-")[0] ?? "en"
|
||||||
componentData.fileData.frontmatter?.lang ??
|
|
||||||
cfg.locale?.split("-")[0] ??
|
|
||||||
"en"
|
|
||||||
const doc = (
|
const doc = (
|
||||||
<html lang="en">
|
<html lang={lang}>
|
||||||
<Head {...componentData} />
|
<Head {...componentData} />
|
||||||
<body data-slug={slug}>
|
<body data-slug={slug}>
|
||||||
<div id="quartz-root" class="page">
|
<div id="quartz-root" class="page">
|
||||||
|
|||||||
@ -14,9 +14,7 @@ function toggleCallout(this: HTMLElement) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const collapsed = parent.classList.contains("is-collapsed")
|
const collapsed = parent.classList.contains("is-collapsed")
|
||||||
const height = collapsed
|
const height = collapsed ? parent.scrollHeight : parent.scrollHeight + current.scrollHeight
|
||||||
? parent.scrollHeight
|
|
||||||
: parent.scrollHeight + current.scrollHeight
|
|
||||||
parent.style.maxHeight = height + "px"
|
parent.style.maxHeight = height + "px"
|
||||||
|
|
||||||
current = parent
|
current = parent
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {getFullSlug} from "../../util/path"
|
import { getFullSlug } from "../../util/path"
|
||||||
|
|
||||||
const checkboxId = (index: number) => `${getFullSlug(window)}-checkbox-${index}`
|
const checkboxId = (index: number) => `${getFullSlug(window)}-checkbox-${index}`
|
||||||
|
|
||||||
@ -10,9 +10,7 @@ document.addEventListener("nav", () => {
|
|||||||
const elId = checkboxId(index)
|
const elId = checkboxId(index)
|
||||||
|
|
||||||
const switchState = (e: Event) => {
|
const switchState = (e: Event) => {
|
||||||
const newCheckboxState = (e.target as HTMLInputElement)?.checked
|
const newCheckboxState = (e.target as HTMLInputElement)?.checked ? "true" : "false"
|
||||||
? "true"
|
|
||||||
: "false"
|
|
||||||
localStorage.setItem(elId, newCheckboxState)
|
localStorage.setItem(elId, newCheckboxState)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -63,5 +63,8 @@ document.addEventListener("nav", () => {
|
|||||||
giscusContainer.appendChild(giscusScript)
|
giscusContainer.appendChild(giscusScript)
|
||||||
|
|
||||||
document.addEventListener("themechange", changeTheme)
|
document.addEventListener("themechange", changeTheme)
|
||||||
window.addCleanup(() => document.removeEventListener("themechange", changeTheme))
|
// window.addCleanup(() => document.removeEventListener("themechange", changeTheme))
|
||||||
|
window.addCleanup(() =>
|
||||||
|
document.removeEventListener("themechange", changeTheme as EventListener)
|
||||||
|
);
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,12 +1,10 @@
|
|||||||
const userPref = window.matchMedia("(prefers-color-scheme: light)").matches
|
const userPref = window.matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark"
|
||||||
? "light"
|
|
||||||
: "dark"
|
|
||||||
const currentTheme = localStorage.getItem("theme") ?? userPref
|
const currentTheme = localStorage.getItem("theme") ?? userPref
|
||||||
document.documentElement.setAttribute("saved-theme", currentTheme)
|
document.documentElement.setAttribute("saved-theme", currentTheme)
|
||||||
|
|
||||||
const emitThemeChangeEvent = (theme: "light" | "dark") => {
|
const emitThemeChangeEvent = (theme: "light" | "dark") => {
|
||||||
const event: CustomEventMap["themechange"] = new CustomEvent("themechange", {
|
const event: CustomEventMap["themechange"] = new CustomEvent("themechange", {
|
||||||
detail: {theme},
|
detail: { theme },
|
||||||
})
|
})
|
||||||
document.dispatchEvent(event)
|
document.dispatchEvent(event)
|
||||||
}
|
}
|
||||||
@ -28,23 +26,15 @@ document.addEventListener("nav", () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Darkmode toggle
|
// Darkmode toggle
|
||||||
const toggleSwitch = document.querySelector(
|
const toggleSwitch = document.querySelector("#darkmode-toggle") as HTMLInputElement
|
||||||
"#darkmode-toggle",
|
|
||||||
) as HTMLInputElement
|
|
||||||
toggleSwitch.addEventListener("change", switchTheme)
|
toggleSwitch.addEventListener("change", switchTheme)
|
||||||
window.addCleanup(() =>
|
window.addCleanup(() => toggleSwitch.removeEventListener("change", switchTheme))
|
||||||
toggleSwitch.removeEventListener("change", switchTheme),
|
|
||||||
)
|
|
||||||
if (currentTheme === "dark") {
|
if (currentTheme === "dark") {
|
||||||
toggleSwitch.checked = true
|
toggleSwitch.checked = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Listen for changes in prefers-color-scheme
|
// Listen for changes in prefers-color-scheme
|
||||||
const colorSchemeMediaQuery = window.matchMedia(
|
const colorSchemeMediaQuery = window.matchMedia("(prefers-color-scheme: dark)")
|
||||||
"(prefers-color-scheme: dark)",
|
|
||||||
)
|
|
||||||
colorSchemeMediaQuery.addEventListener("change", themeChange)
|
colorSchemeMediaQuery.addEventListener("change", themeChange)
|
||||||
window.addCleanup(() =>
|
window.addCleanup(() => colorSchemeMediaQuery.removeEventListener("change", themeChange))
|
||||||
colorSchemeMediaQuery.removeEventListener("change", themeChange),
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {FolderState} from "../ExplorerNode"
|
import { FolderState } from "../ExplorerNode"
|
||||||
|
|
||||||
type MaybeHTMLElement = HTMLElement | undefined
|
type MaybeHTMLElement = HTMLElement | undefined
|
||||||
let currentExplorerState: FolderState[]
|
let currentExplorerState: FolderState[]
|
||||||
@ -25,8 +25,7 @@ function toggleExplorer(this: HTMLElement) {
|
|||||||
if (!content) return
|
if (!content) return
|
||||||
|
|
||||||
content.classList.toggle("collapsed")
|
content.classList.toggle("collapsed")
|
||||||
content.style.maxHeight =
|
content.style.maxHeight = content.style.maxHeight === "0px" ? content.scrollHeight + "px" : "0px"
|
||||||
content.style.maxHeight === "0px" ? content.scrollHeight + "px" : "0px"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleFolder(evt: MouseEvent) {
|
function toggleFolder(evt: MouseEvent) {
|
||||||
@ -83,26 +82,20 @@ function setupExplorer() {
|
|||||||
const useSavedFolderState = explorer?.dataset.savestate === "true"
|
const useSavedFolderState = explorer?.dataset.savestate === "true"
|
||||||
const oldExplorerState: FolderState[] =
|
const oldExplorerState: FolderState[] =
|
||||||
storageTree && useSavedFolderState ? JSON.parse(storageTree) : []
|
storageTree && useSavedFolderState ? JSON.parse(storageTree) : []
|
||||||
const oldIndex = new Map(
|
const oldIndex = new Map(oldExplorerState.map((entry) => [entry.path, entry.collapsed]))
|
||||||
oldExplorerState.map((entry) => [entry.path, entry.collapsed]),
|
|
||||||
)
|
|
||||||
const newExplorerState: FolderState[] = explorer.dataset.tree
|
const newExplorerState: FolderState[] = explorer.dataset.tree
|
||||||
? JSON.parse(explorer.dataset.tree)
|
? JSON.parse(explorer.dataset.tree)
|
||||||
: []
|
: []
|
||||||
currentExplorerState = []
|
currentExplorerState = []
|
||||||
for (const {path, collapsed} of newExplorerState) {
|
for (const { path, collapsed } of newExplorerState) {
|
||||||
currentExplorerState.push({
|
currentExplorerState.push({ path, collapsed: oldIndex.get(path) ?? collapsed })
|
||||||
path,
|
|
||||||
collapsed: oldIndex.get(path) ?? collapsed,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
currentExplorerState.map((folderState) => {
|
currentExplorerState.map((folderState) => {
|
||||||
const folderLi = document.querySelector(
|
const folderLi = document.querySelector(
|
||||||
`[data-folderpath='${folderState.path}']`,
|
`[data-folderpath='${folderState.path}']`,
|
||||||
) as MaybeHTMLElement
|
) as MaybeHTMLElement
|
||||||
const folderUl = folderLi?.parentElement
|
const folderUl = folderLi?.parentElement?.nextElementSibling as MaybeHTMLElement
|
||||||
?.nextElementSibling as MaybeHTMLElement
|
|
||||||
if (folderUl) {
|
if (folderUl) {
|
||||||
setFolderState(folderUl, folderState.collapsed)
|
setFolderState(folderUl, folderState.collapsed)
|
||||||
}
|
}
|
||||||
@ -127,9 +120,7 @@ document.addEventListener("nav", () => {
|
|||||||
* @param collapsed if folder should be set to collapsed or not
|
* @param collapsed if folder should be set to collapsed or not
|
||||||
*/
|
*/
|
||||||
function setFolderState(folderElement: HTMLElement, collapsed: boolean) {
|
function setFolderState(folderElement: HTMLElement, collapsed: boolean) {
|
||||||
return collapsed
|
return collapsed ? folderElement.classList.remove("open") : folderElement.classList.add("open")
|
||||||
? folderElement.classList.remove("open")
|
|
||||||
: folderElement.classList.add("open")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -1,16 +1,7 @@
|
|||||||
import type {
|
import type { ContentDetails } from "../../plugins/emitters/contentIndex"
|
||||||
ContentDetails,
|
|
||||||
ContentIndex,
|
|
||||||
} from "../../plugins/emitters/contentIndex"
|
|
||||||
import * as d3 from "d3"
|
import * as d3 from "d3"
|
||||||
import {registerEscapeHandler, removeAllChildren} from "./util"
|
import { registerEscapeHandler, removeAllChildren } from "./util"
|
||||||
import {
|
import { FullSlug, SimpleSlug, getFullSlug, resolveRelative, simplifySlug } from "../../util/path"
|
||||||
FullSlug,
|
|
||||||
SimpleSlug,
|
|
||||||
getFullSlug,
|
|
||||||
resolveRelative,
|
|
||||||
simplifySlug,
|
|
||||||
} from "../../util/path"
|
|
||||||
|
|
||||||
type NodeData = {
|
type NodeData = {
|
||||||
id: SimpleSlug
|
id: SimpleSlug
|
||||||
@ -71,7 +62,7 @@ async function renderGraph(container: string, fullSlug: FullSlug) {
|
|||||||
|
|
||||||
for (const dest of outgoing) {
|
for (const dest of outgoing) {
|
||||||
if (validLinks.has(dest)) {
|
if (validLinks.has(dest)) {
|
||||||
links.push({source: source, target: dest})
|
links.push({ source: source, target: dest })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,7 +74,7 @@ async function renderGraph(container: string, fullSlug: FullSlug) {
|
|||||||
tags.push(...localTags.filter((tag) => !tags.includes(tag)))
|
tags.push(...localTags.filter((tag) => !tags.includes(tag)))
|
||||||
|
|
||||||
for (const tag of localTags) {
|
for (const tag of localTags) {
|
||||||
links.push({source: source, target: tag})
|
links.push({ source: source, target: tag })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -101,10 +92,7 @@ async function renderGraph(container: string, fullSlug: FullSlug) {
|
|||||||
neighbourhood.add(cur)
|
neighbourhood.add(cur)
|
||||||
const outgoing = links.filter((l) => l.source === cur)
|
const outgoing = links.filter((l) => l.source === cur)
|
||||||
const incoming = links.filter((l) => l.target === cur)
|
const incoming = links.filter((l) => l.target === cur)
|
||||||
wl.push(
|
wl.push(...outgoing.map((l) => l.target), ...incoming.map((l) => l.source))
|
||||||
...outgoing.map((l) => l.target),
|
|
||||||
...incoming.map((l) => l.source),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -112,20 +100,16 @@ async function renderGraph(container: string, fullSlug: FullSlug) {
|
|||||||
if (showTags) tags.forEach((tag) => neighbourhood.add(tag))
|
if (showTags) tags.forEach((tag) => neighbourhood.add(tag))
|
||||||
}
|
}
|
||||||
|
|
||||||
const graphData: {nodes: NodeData[]; links: LinkData[]} = {
|
const graphData: { nodes: NodeData[]; links: LinkData[] } = {
|
||||||
nodes: [...neighbourhood].map((url) => {
|
nodes: [...neighbourhood].map((url) => {
|
||||||
const text = url.startsWith("tags/")
|
const text = url.startsWith("tags/") ? "#" + url.substring(5) : (data.get(url)?.title ?? url)
|
||||||
? "#" + url.substring(5)
|
|
||||||
: data.get(url)?.title ?? url
|
|
||||||
return {
|
return {
|
||||||
id: url,
|
id: url,
|
||||||
text: text,
|
text: text,
|
||||||
tags: data.get(url)?.tags ?? [],
|
tags: data.get(url)?.tags ?? [],
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
links: links.filter(
|
links: links.filter((l) => neighbourhood.has(l.source) && neighbourhood.has(l.target)),
|
||||||
(l) => neighbourhood.has(l.source) && neighbourhood.has(l.target),
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const simulation: d3.Simulation<NodeData, LinkData> = d3
|
const simulation: d3.Simulation<NodeData, LinkData> = d3
|
||||||
@ -148,12 +132,7 @@ async function renderGraph(container: string, fullSlug: FullSlug) {
|
|||||||
.append("svg")
|
.append("svg")
|
||||||
.attr("width", width)
|
.attr("width", width)
|
||||||
.attr("height", height)
|
.attr("height", height)
|
||||||
.attr("viewBox", [
|
.attr("viewBox", [-width / 2 / scale, -height / 2 / scale, width / scale, height / scale])
|
||||||
-width / 2 / scale,
|
|
||||||
-height / 2 / scale,
|
|
||||||
width / scale,
|
|
||||||
height / scale,
|
|
||||||
])
|
|
||||||
|
|
||||||
// draw links between nodes
|
// draw links between nodes
|
||||||
const link = svg
|
const link = svg
|
||||||
@ -166,12 +145,7 @@ async function renderGraph(container: string, fullSlug: FullSlug) {
|
|||||||
.attr("stroke-width", 1)
|
.attr("stroke-width", 1)
|
||||||
|
|
||||||
// svg groups
|
// svg groups
|
||||||
const graphNode = svg
|
const graphNode = svg.append("g").selectAll("g").data(graphData.nodes).enter().append("g")
|
||||||
.append("g")
|
|
||||||
.selectAll("g")
|
|
||||||
.data(graphData.nodes)
|
|
||||||
.enter()
|
|
||||||
.append("g")
|
|
||||||
|
|
||||||
// calculate color
|
// calculate color
|
||||||
const color = (d: NodeData) => {
|
const color = (d: NodeData) => {
|
||||||
@ -212,9 +186,7 @@ async function renderGraph(container: string, fullSlug: FullSlug) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function nodeRadius(d: NodeData) {
|
function nodeRadius(d: NodeData) {
|
||||||
const numLinks = links.filter(
|
const numLinks = links.filter((l: any) => l.source.id === d.id || l.target.id === d.id).length
|
||||||
(l: any) => l.source.id === d.id || l.target.id === d.id,
|
|
||||||
).length
|
|
||||||
return 2 + Math.sqrt(numLinks)
|
return 2 + Math.sqrt(numLinks)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,15 +208,11 @@ async function renderGraph(container: string, fullSlug: FullSlug) {
|
|||||||
const currentId = d.id
|
const currentId = d.id
|
||||||
const linkNodes = d3
|
const linkNodes = d3
|
||||||
.selectAll(".link")
|
.selectAll(".link")
|
||||||
.filter(
|
.filter((d: any) => d.source.id === currentId || d.target.id === currentId)
|
||||||
(d: any) => d.source.id === currentId || d.target.id === currentId,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (focusOnHover) {
|
if (focusOnHover) {
|
||||||
// fade out non-neighbour nodes
|
// fade out non-neighbour nodes
|
||||||
connectedNodes = linkNodes
|
connectedNodes = linkNodes.data().flatMap((d: any) => [d.source.id, d.target.id])
|
||||||
.data()
|
|
||||||
.flatMap((d: any) => [d.source.id, d.target.id])
|
|
||||||
|
|
||||||
d3.selectAll<HTMLElement, NodeData>(".link")
|
d3.selectAll<HTMLElement, NodeData>(".link")
|
||||||
.transition()
|
.transition()
|
||||||
@ -270,11 +238,7 @@ async function renderGraph(container: string, fullSlug: FullSlug) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// highlight links
|
// highlight links
|
||||||
linkNodes
|
linkNodes.transition().duration(200).attr("stroke", "var(--gray)").attr("stroke-width", 1)
|
||||||
.transition()
|
|
||||||
.duration(200)
|
|
||||||
.attr("stroke", "var(--gray)")
|
|
||||||
.attr("stroke-width", 1)
|
|
||||||
|
|
||||||
const bigFont = fontSize * 1.5
|
const bigFont = fontSize * 1.5
|
||||||
|
|
||||||
@ -291,21 +255,19 @@ async function renderGraph(container: string, fullSlug: FullSlug) {
|
|||||||
})
|
})
|
||||||
.on("mouseleave", function (_, d) {
|
.on("mouseleave", function (_, d) {
|
||||||
if (focusOnHover) {
|
if (focusOnHover) {
|
||||||
d3.selectAll<HTMLElement, NodeData>(".link")
|
d3.selectAll<HTMLElement, NodeData>(".link").transition().duration(200).style("opacity", 1)
|
||||||
.transition()
|
d3.selectAll<HTMLElement, NodeData>(".node").transition().duration(200).style("opacity", 1)
|
||||||
.duration(200)
|
|
||||||
.style("opacity", 1)
|
|
||||||
d3.selectAll<HTMLElement, NodeData>(".node")
|
d3.selectAll<HTMLElement, NodeData>(".node")
|
||||||
.transition()
|
.filter((d) => !connectedNodes.includes(d.id))
|
||||||
.duration(200)
|
.nodes()
|
||||||
.style("opacity", 1)
|
.map((it) => d3.select(it.parentNode as HTMLElement).select("text"))
|
||||||
|
.forEach((it) => it.transition().duration(200).style("opacity", it.attr("opacityOld")))
|
||||||
}
|
}
|
||||||
const currentId = d.id
|
const currentId = d.id
|
||||||
const linkNodes = d3
|
const linkNodes = d3
|
||||||
.selectAll(".link")
|
.selectAll(".link")
|
||||||
.filter(
|
.filter((d: any) => d.source.id === currentId || d.target.id === currentId)
|
||||||
(d: any) => d.source.id === currentId || d.target.id === currentId,
|
|
||||||
)
|
|
||||||
|
|
||||||
linkNodes.transition().duration(200).attr("stroke", "var(--lightgray)")
|
linkNodes.transition().duration(200).attr("stroke", "var(--lightgray)")
|
||||||
|
|
||||||
@ -351,7 +313,7 @@ async function renderGraph(container: string, fullSlug: FullSlug) {
|
|||||||
[width, height],
|
[width, height],
|
||||||
])
|
])
|
||||||
.scaleExtent([0.25, 4])
|
.scaleExtent([0.25, 4])
|
||||||
.on("zoom", ({transform}) => {
|
.on("zoom", ({ transform }) => {
|
||||||
link.attr("transform", transform)
|
link.attr("transform", transform)
|
||||||
node.attr("transform", transform)
|
node.attr("transform", transform)
|
||||||
const scale = transform.k * opacityScale
|
const scale = transform.k * opacityScale
|
||||||
@ -404,7 +366,5 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
|
|||||||
|
|
||||||
const containerIcon = document.getElementById("global-graph-icon")
|
const containerIcon = document.getElementById("global-graph-icon")
|
||||||
containerIcon?.addEventListener("click", renderGlobalGraph)
|
containerIcon?.addEventListener("click", renderGlobalGraph)
|
||||||
window.addCleanup(() =>
|
window.addCleanup(() => containerIcon?.removeEventListener("click", renderGlobalGraph))
|
||||||
containerIcon?.removeEventListener("click", renderGlobalGraph),
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,3 +0,0 @@
|
|||||||
// import Plausible from "plausible-tracker"
|
|
||||||
// const { trackPageview } = Plausible()
|
|
||||||
// document.addEventListener("nav", () => trackPageview(), { passive: true })
|
|
||||||
@ -1,10 +1,10 @@
|
|||||||
import {computePosition, flip, inline, shift} from "@floating-ui/dom"
|
import { computePosition, flip, inline, shift } from "@floating-ui/dom"
|
||||||
import {normalizeRelativeURLs} from "../../util/path"
|
import { normalizeRelativeURLs } from "../../util/path"
|
||||||
|
|
||||||
const p = new DOMParser()
|
const p = new DOMParser()
|
||||||
async function mouseEnterHandler(
|
async function mouseEnterHandler(
|
||||||
this: HTMLLinkElement,
|
this: HTMLAnchorElement,
|
||||||
{clientX, clientY}: {clientX: number; clientY: number},
|
{ clientX, clientY }: { clientX: number; clientY: number },
|
||||||
) {
|
) {
|
||||||
const link = this
|
const link = this
|
||||||
if (link.dataset.noPopover === "true") {
|
if (link.dataset.noPopover === "true") {
|
||||||
@ -12,8 +12,8 @@ async function mouseEnterHandler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function setPosition(popoverElement: HTMLElement) {
|
async function setPosition(popoverElement: HTMLElement) {
|
||||||
const {x, y} = await computePosition(link, popoverElement, {
|
const { x, y } = await computePosition(link, popoverElement, {
|
||||||
middleware: [inline({x: clientX, y: clientY}), shift(), flip()],
|
middleware: [inline({ x: clientX, y: clientY }), shift(), flip()],
|
||||||
})
|
})
|
||||||
Object.assign(popoverElement.style, {
|
Object.assign(popoverElement.style, {
|
||||||
left: `${x}px`,
|
left: `${x}px`,
|
||||||
@ -94,22 +94,15 @@ async function mouseEnterHandler(
|
|||||||
const heading = popoverInner.querySelector(hash) as HTMLElement | null
|
const heading = popoverInner.querySelector(hash) as HTMLElement | null
|
||||||
if (heading) {
|
if (heading) {
|
||||||
// leave ~12px of buffer when scrolling to a heading
|
// leave ~12px of buffer when scrolling to a heading
|
||||||
popoverInner.scroll({
|
popoverInner.scroll({ top: heading.offsetTop - 12, behavior: "instant" })
|
||||||
top: heading.offsetTop - 12,
|
|
||||||
behavior: "instant",
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener("nav", () => {
|
document.addEventListener("nav", () => {
|
||||||
const links = [
|
const links = [...document.getElementsByClassName("internal")] as HTMLAnchorElement[]
|
||||||
...document.getElementsByClassName("internal"),
|
|
||||||
] as HTMLLinkElement[]
|
|
||||||
for (const link of links) {
|
for (const link of links) {
|
||||||
link.addEventListener("mouseenter", mouseEnterHandler)
|
link.addEventListener("mouseenter", mouseEnterHandler)
|
||||||
window.addCleanup(() =>
|
window.addCleanup(() => link.removeEventListener("mouseenter", mouseEnterHandler))
|
||||||
link.removeEventListener("mouseenter", mouseEnterHandler),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import FlexSearch from "flexsearch"
|
import FlexSearch from "flexsearch"
|
||||||
import {ContentDetails} from "../../plugins/emitters/contentIndex"
|
import { ContentDetails } from "../../plugins/emitters/contentIndex"
|
||||||
import {registerEscapeHandler, removeAllChildren} from "./util"
|
import { registerEscapeHandler, removeAllChildren } from "./util"
|
||||||
import {FullSlug, normalizeRelativeURLs, resolveRelative} from "../../util/path"
|
import { FullSlug, normalizeRelativeURLs, resolveRelative } from "../../util/path"
|
||||||
|
|
||||||
interface Item {
|
interface Item {
|
||||||
id: number
|
id: number
|
||||||
@ -15,8 +15,7 @@ interface Item {
|
|||||||
type SearchType = "basic" | "tags"
|
type SearchType = "basic" | "tags"
|
||||||
let searchType: SearchType = "basic"
|
let searchType: SearchType = "basic"
|
||||||
let currentSearchTerm: string = ""
|
let currentSearchTerm: string = ""
|
||||||
const encoder = (str: string) =>
|
const encoder = (str: string) => str.toLowerCase().split(/([^a-z]|[^\x00-\x7F])/)
|
||||||
str.toLowerCase().split(/([^a-z]|[^\x00-\x7F])/)
|
|
||||||
let index = new FlexSearch.Document<Item>({
|
let index = new FlexSearch.Document<Item>({
|
||||||
charset: "latin:extra",
|
charset: "latin:extra",
|
||||||
encode: encoder,
|
encode: encoder,
|
||||||
@ -66,18 +65,12 @@ function highlight(searchTerm: string, text: string, trim?: boolean) {
|
|||||||
let endIndex = tokenizedText.length - 1
|
let endIndex = tokenizedText.length - 1
|
||||||
if (trim) {
|
if (trim) {
|
||||||
const includesCheck = (tok: string) =>
|
const includesCheck = (tok: string) =>
|
||||||
tokenizedTerms.some((term) =>
|
tokenizedTerms.some((term) => tok.toLowerCase().startsWith(term.toLowerCase()))
|
||||||
tok.toLowerCase().startsWith(term.toLowerCase()),
|
|
||||||
)
|
|
||||||
const occurrencesIndices = tokenizedText.map(includesCheck)
|
const occurrencesIndices = tokenizedText.map(includesCheck)
|
||||||
|
|
||||||
let bestSum = 0
|
let bestSum = 0
|
||||||
let bestIndex = 0
|
let bestIndex = 0
|
||||||
for (
|
for (let i = 0; i < Math.max(tokenizedText.length - contextWindowWords, 0); i++) {
|
||||||
let i = 0;
|
|
||||||
i < Math.max(tokenizedText.length - contextWindowWords, 0);
|
|
||||||
i++
|
|
||||||
) {
|
|
||||||
const window = occurrencesIndices.slice(i, i + contextWindowWords)
|
const window = occurrencesIndices.slice(i, i + contextWindowWords)
|
||||||
const windowSum = window.reduce((total, cur) => total + (cur ? 1 : 0), 0)
|
const windowSum = window.reduce((total, cur) => total + (cur ? 1 : 0), 0)
|
||||||
if (windowSum >= bestSum) {
|
if (windowSum >= bestSum) {
|
||||||
@ -87,10 +80,7 @@ function highlight(searchTerm: string, text: string, trim?: boolean) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
startIndex = Math.max(bestIndex - contextWindowWords, 0)
|
startIndex = Math.max(bestIndex - contextWindowWords, 0)
|
||||||
endIndex = Math.min(
|
endIndex = Math.min(startIndex + 2 * contextWindowWords, tokenizedText.length - 1)
|
||||||
startIndex + 2 * contextWindowWords,
|
|
||||||
tokenizedText.length - 1,
|
|
||||||
)
|
|
||||||
tokenizedText = tokenizedText.slice(startIndex, endIndex)
|
tokenizedText = tokenizedText.slice(startIndex, endIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,21 +124,15 @@ function highlightHTML(searchTerm: string, el: HTMLElement) {
|
|||||||
let lastIndex = 0
|
let lastIndex = 0
|
||||||
for (const match of matches) {
|
for (const match of matches) {
|
||||||
const matchIndex = nodeText.indexOf(match, lastIndex)
|
const matchIndex = nodeText.indexOf(match, lastIndex)
|
||||||
spanContainer.appendChild(
|
spanContainer.appendChild(document.createTextNode(nodeText.slice(lastIndex, matchIndex)))
|
||||||
document.createTextNode(nodeText.slice(lastIndex, matchIndex)),
|
|
||||||
)
|
|
||||||
spanContainer.appendChild(createHighlightSpan(match))
|
spanContainer.appendChild(createHighlightSpan(match))
|
||||||
lastIndex = matchIndex + match.length
|
lastIndex = matchIndex + match.length
|
||||||
}
|
}
|
||||||
spanContainer.appendChild(
|
spanContainer.appendChild(document.createTextNode(nodeText.slice(lastIndex)))
|
||||||
document.createTextNode(nodeText.slice(lastIndex)),
|
|
||||||
)
|
|
||||||
node.parentNode?.replaceChild(spanContainer, node)
|
node.parentNode?.replaceChild(spanContainer, node)
|
||||||
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
||||||
if ((node as HTMLElement).classList.contains("highlight")) return
|
if ((node as HTMLElement).classList.contains("highlight")) return
|
||||||
Array.from(node.childNodes).forEach((child) =>
|
Array.from(node.childNodes).forEach((child) => highlightTextNodes(child, term))
|
||||||
highlightTextNodes(child, term),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,10 +148,8 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
|
|||||||
const data = await fetchData
|
const data = await fetchData
|
||||||
const container = document.getElementById("search-container")
|
const container = document.getElementById("search-container")
|
||||||
const sidebar = container?.closest(".sidebar") as HTMLElement
|
const sidebar = container?.closest(".sidebar") as HTMLElement
|
||||||
const searchIcon = document.getElementById("search-icon")
|
const searchButton = document.getElementById("search-button")
|
||||||
const searchBar = document.getElementById(
|
const searchBar = document.getElementById("search-bar") as HTMLInputElement | null
|
||||||
"search-bar",
|
|
||||||
) as HTMLInputElement | null
|
|
||||||
const searchLayout = document.getElementById("search-layout")
|
const searchLayout = document.getElementById("search-layout")
|
||||||
const idDataMap = Object.keys(data) as FullSlug[]
|
const idDataMap = Object.keys(data) as FullSlug[]
|
||||||
|
|
||||||
@ -230,11 +212,7 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
|
|||||||
const searchBarOpen = container?.classList.contains("active")
|
const searchBarOpen = container?.classList.contains("active")
|
||||||
searchBarOpen ? hideSearch() : showSearch("basic")
|
searchBarOpen ? hideSearch() : showSearch("basic")
|
||||||
return
|
return
|
||||||
} else if (
|
} else if (e.shiftKey && (e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "k") {
|
||||||
e.shiftKey &&
|
|
||||||
(e.ctrlKey || e.metaKey) &&
|
|
||||||
e.key.toLowerCase() === "k"
|
|
||||||
) {
|
|
||||||
// Hotkey to open tag search
|
// Hotkey to open tag search
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
const searchBarOpen = container?.classList.contains("active")
|
const searchBarOpen = container?.classList.contains("active")
|
||||||
@ -259,9 +237,7 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
|
|||||||
await displayPreview(active)
|
await displayPreview(active)
|
||||||
active.click()
|
active.click()
|
||||||
} else {
|
} else {
|
||||||
const anchor = document.getElementsByClassName(
|
const anchor = document.getElementsByClassName("result-card")[0] as HTMLInputElement | null
|
||||||
"result-card",
|
|
||||||
)[0] as HTMLInputElement | null
|
|
||||||
if (!anchor || anchor?.classList.contains("no-match")) return
|
if (!anchor || anchor?.classList.contains("no-match")) return
|
||||||
await displayPreview(anchor)
|
await displayPreview(anchor)
|
||||||
anchor.click()
|
anchor.click()
|
||||||
@ -273,8 +249,7 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
|
|||||||
const currentResult = currentHover
|
const currentResult = currentHover
|
||||||
? currentHover
|
? currentHover
|
||||||
: (document.activeElement as HTMLInputElement | null)
|
: (document.activeElement as HTMLInputElement | null)
|
||||||
const prevResult =
|
const prevResult = currentResult?.previousElementSibling as HTMLInputElement | null
|
||||||
currentResult?.previousElementSibling as HTMLInputElement | null
|
|
||||||
currentResult?.classList.remove("focus")
|
currentResult?.classList.remove("focus")
|
||||||
prevResult?.focus()
|
prevResult?.focus()
|
||||||
if (prevResult) currentHover = prevResult
|
if (prevResult) currentHover = prevResult
|
||||||
@ -287,11 +262,8 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
|
|||||||
if (document.activeElement === searchBar || currentHover !== null) {
|
if (document.activeElement === searchBar || currentHover !== null) {
|
||||||
const firstResult = currentHover
|
const firstResult = currentHover
|
||||||
? currentHover
|
? currentHover
|
||||||
: (document.getElementsByClassName(
|
: (document.getElementsByClassName("result-card")[0] as HTMLInputElement | null)
|
||||||
"result-card",
|
const secondResult = firstResult?.nextElementSibling as HTMLInputElement | null
|
||||||
)[0] as HTMLInputElement | null)
|
|
||||||
const secondResult =
|
|
||||||
firstResult?.nextElementSibling as HTMLInputElement | null
|
|
||||||
firstResult?.classList.remove("focus")
|
firstResult?.classList.remove("focus")
|
||||||
secondResult?.focus()
|
secondResult?.focus()
|
||||||
if (secondResult) currentHover = secondResult
|
if (secondResult) currentHover = secondResult
|
||||||
@ -305,10 +277,7 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
|
|||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
slug,
|
slug,
|
||||||
title:
|
title: searchType === "tags" ? data[slug].title : highlight(term, data[slug].title ?? ""),
|
||||||
searchType === "tags"
|
|
||||||
? data[slug].title
|
|
||||||
: highlight(term, data[slug].title ?? ""),
|
|
||||||
content: highlight(term, data[slug].content ?? "", true),
|
content: highlight(term, data[slug].content ?? "", true),
|
||||||
tags: highlightTags(term.substring(1), data[slug].tags),
|
tags: highlightTags(term.substring(1), data[slug].tags),
|
||||||
}
|
}
|
||||||
@ -334,9 +303,8 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
|
|||||||
return new URL(resolveRelative(currentSlug, slug), location.toString())
|
return new URL(resolveRelative(currentSlug, slug), location.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
const resultToHTML = ({slug, title, content, tags}: Item) => {
|
const resultToHTML = ({ slug, title, content, tags }: Item) => {
|
||||||
const htmlTags =
|
const htmlTags = tags.length > 0 ? `<ul class="tags">${tags.join("")}</ul>` : ``
|
||||||
tags.length > 0 ? `<ul class="tags">${tags.join("")}</ul>` : ``
|
|
||||||
const itemTile = document.createElement("a")
|
const itemTile = document.createElement("a")
|
||||||
itemTile.classList.add("result-card")
|
itemTile.classList.add("result-card")
|
||||||
itemTile.id = slug
|
itemTile.id = slug
|
||||||
@ -345,14 +313,12 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
|
|||||||
enablePreview && window.innerWidth > 600 ? "" : `<p>${content}</p>`
|
enablePreview && window.innerWidth > 600 ? "" : `<p>${content}</p>`
|
||||||
}`
|
}`
|
||||||
itemTile.addEventListener("click", (event) => {
|
itemTile.addEventListener("click", (event) => {
|
||||||
if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey)
|
if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) return
|
||||||
return
|
|
||||||
hideSearch()
|
hideSearch()
|
||||||
})
|
})
|
||||||
|
|
||||||
const handler = (event: MouseEvent) => {
|
const handler = (event: MouseEvent) => {
|
||||||
if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey)
|
if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) return
|
||||||
return
|
|
||||||
hideSearch()
|
hideSearch()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -363,9 +329,7 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
itemTile.addEventListener("mouseenter", onMouseEnter)
|
itemTile.addEventListener("mouseenter", onMouseEnter)
|
||||||
window.addCleanup(() =>
|
window.addCleanup(() => itemTile.removeEventListener("mouseenter", onMouseEnter))
|
||||||
itemTile.removeEventListener("mouseenter", onMouseEnter),
|
|
||||||
)
|
|
||||||
itemTile.addEventListener("click", handler)
|
itemTile.addEventListener("click", handler)
|
||||||
window.addCleanup(() => itemTile.removeEventListener("click", handler))
|
window.addCleanup(() => itemTile.removeEventListener("click", handler))
|
||||||
|
|
||||||
@ -422,9 +386,7 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
|
|||||||
if (!searchLayout || !enablePreview || !el || !preview) return
|
if (!searchLayout || !enablePreview || !el || !preview) return
|
||||||
const slug = el.id as FullSlug
|
const slug = el.id as FullSlug
|
||||||
const innerDiv = await fetchContent(slug).then((contents) =>
|
const innerDiv = await fetchContent(slug).then((contents) =>
|
||||||
contents.flatMap((el) => [
|
contents.flatMap((el) => [...highlightHTML(currentSearchTerm, el as HTMLElement).children]),
|
||||||
...highlightHTML(currentSearchTerm, el as HTMLElement).children,
|
|
||||||
]),
|
|
||||||
)
|
)
|
||||||
previewInner = document.createElement("div")
|
previewInner = document.createElement("div")
|
||||||
previewInner.classList.add("preview-inner")
|
previewInner.classList.add("preview-inner")
|
||||||
@ -435,7 +397,7 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
|
|||||||
const highlights = [...preview.querySelectorAll(".highlight")].sort(
|
const highlights = [...preview.querySelectorAll(".highlight")].sort(
|
||||||
(a, b) => b.innerHTML.length - a.innerHTML.length,
|
(a, b) => b.innerHTML.length - a.innerHTML.length,
|
||||||
)
|
)
|
||||||
highlights[0]?.scrollIntoView({block: "start"})
|
highlights[0]?.scrollIntoView({ block: "start" })
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onType(e: HTMLElementEventMap["input"]) {
|
async function onType(e: HTMLElementEventMap["input"]) {
|
||||||
@ -492,20 +454,14 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
|
|||||||
...getByField("content"),
|
...getByField("content"),
|
||||||
...getByField("tags"),
|
...getByField("tags"),
|
||||||
])
|
])
|
||||||
const finalResults = [...allIds].map((id) =>
|
const finalResults = [...allIds].map((id) => formatForDisplay(currentSearchTerm, id))
|
||||||
formatForDisplay(currentSearchTerm, id),
|
|
||||||
)
|
|
||||||
await displayResults(finalResults)
|
await displayResults(finalResults)
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener("keydown", shortcutHandler)
|
document.addEventListener("keydown", shortcutHandler)
|
||||||
window.addCleanup(() =>
|
window.addCleanup(() => document.removeEventListener("keydown", shortcutHandler))
|
||||||
document.removeEventListener("keydown", shortcutHandler),
|
searchButton?.addEventListener("click", () => showSearch("basic"))
|
||||||
)
|
window.addCleanup(() => searchButton?.removeEventListener("click", () => showSearch("basic")))
|
||||||
searchIcon?.addEventListener("click", () => showSearch("basic"))
|
|
||||||
window.addCleanup(() =>
|
|
||||||
searchIcon?.removeEventListener("click", () => showSearch("basic")),
|
|
||||||
)
|
|
||||||
searchBar?.addEventListener("input", onType)
|
searchBar?.addEventListener("input", onType)
|
||||||
window.addCleanup(() => searchBar?.removeEventListener("input", onType))
|
window.addCleanup(() => searchBar?.removeEventListener("input", onType))
|
||||||
|
|
||||||
@ -518,7 +474,7 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
|
|||||||
* @param index index to fill
|
* @param index index to fill
|
||||||
* @param data data to fill index with
|
* @param data data to fill index with
|
||||||
*/
|
*/
|
||||||
async function fillDocument(data: {[key: FullSlug]: ContentDetails}) {
|
async function fillDocument(data: { [key: FullSlug]: ContentDetails }) {
|
||||||
let id = 0
|
let id = 0
|
||||||
const promises: Array<Promise<unknown>> = []
|
const promises: Array<Promise<unknown>> = []
|
||||||
for (const [slug, fileData] of Object.entries<ContentDetails>(data)) {
|
for (const [slug, fileData] of Object.entries<ContentDetails>(data)) {
|
||||||
|
|||||||
@ -1,10 +1,5 @@
|
|||||||
import micromorph from "micromorph"
|
import micromorph from "micromorph"
|
||||||
import {
|
import { FullSlug, RelativeURL, getFullSlug, normalizeRelativeURLs } from "../../util/path"
|
||||||
FullSlug,
|
|
||||||
RelativeURL,
|
|
||||||
getFullSlug,
|
|
||||||
normalizeRelativeURLs,
|
|
||||||
} from "../../util/path"
|
|
||||||
|
|
||||||
// adapted from `micromorph`
|
// adapted from `micromorph`
|
||||||
// https://github.com/natemoo-re/micromorph
|
// https://github.com/natemoo-re/micromorph
|
||||||
@ -28,24 +23,19 @@ const isSamePage = (url: URL): boolean => {
|
|||||||
return sameOrigin && samePath
|
return sameOrigin && samePath
|
||||||
}
|
}
|
||||||
|
|
||||||
const getOpts = ({target}: Event): {url: URL; scroll?: boolean} | undefined => {
|
const getOpts = ({ target }: Event): { url: URL; scroll?: boolean } | undefined => {
|
||||||
if (!isElement(target)) return
|
if (!isElement(target)) return
|
||||||
if (target.attributes.getNamedItem("target")?.value === "_blank") return
|
if (target.attributes.getNamedItem("target")?.value === "_blank") return
|
||||||
const a = target.closest("a")
|
const a = target.closest("a")
|
||||||
if (!a) return
|
if (!a) return
|
||||||
if ("routerIgnore" in a.dataset) return
|
if ("routerIgnore" in a.dataset) return
|
||||||
const {href} = a
|
const { href } = a
|
||||||
if (!isLocalUrl(href)) return
|
if (!isLocalUrl(href)) return
|
||||||
return {
|
return { url: new URL(href), scroll: "routerNoscroll" in a.dataset ? false : undefined }
|
||||||
url: new URL(href),
|
|
||||||
scroll: "routerNoscroll" in a.dataset ? false : undefined,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function notifyNav(url: FullSlug) {
|
function notifyNav(url: FullSlug) {
|
||||||
const event: CustomEventMap["nav"] = new CustomEvent("nav", {
|
const event: CustomEventMap["nav"] = new CustomEvent("nav", { detail: { url } })
|
||||||
detail: {url},
|
|
||||||
})
|
|
||||||
document.dispatchEvent(event)
|
document.dispatchEvent(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,19 +86,15 @@ async function navigate(url: URL, isBack: boolean = false) {
|
|||||||
// scroll into place and add history
|
// scroll into place and add history
|
||||||
if (!isBack) {
|
if (!isBack) {
|
||||||
if (url.hash) {
|
if (url.hash) {
|
||||||
const el = document.getElementById(
|
const el = document.getElementById(decodeURIComponent(url.hash.substring(1)))
|
||||||
decodeURIComponent(url.hash.substring(1)),
|
|
||||||
)
|
|
||||||
el?.scrollIntoView()
|
el?.scrollIntoView()
|
||||||
} else {
|
} else {
|
||||||
window.scrollTo({top: 0})
|
window.scrollTo({ top: 0 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// now, patch head
|
// now, patch head
|
||||||
const elementsToRemove = document.head.querySelectorAll(
|
const elementsToRemove = document.head.querySelectorAll(":not([spa-preserve])")
|
||||||
":not([spa-preserve])",
|
|
||||||
)
|
|
||||||
elementsToRemove.forEach((el) => el.remove())
|
elementsToRemove.forEach((el) => el.remove())
|
||||||
const elementsToAdd = html.head.querySelectorAll(":not([spa-preserve])")
|
const elementsToAdd = html.head.querySelectorAll(":not([spa-preserve])")
|
||||||
elementsToAdd.forEach((el) => document.head.appendChild(el))
|
elementsToAdd.forEach((el) => document.head.appendChild(el))
|
||||||
@ -127,15 +113,13 @@ window.spaNavigate = navigate
|
|||||||
function createRouter() {
|
function createRouter() {
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window !== "undefined") {
|
||||||
window.addEventListener("click", async (event) => {
|
window.addEventListener("click", async (event) => {
|
||||||
const {url} = getOpts(event) ?? {}
|
const { url } = getOpts(event) ?? {}
|
||||||
// dont hijack behaviour, just let browser act normally
|
// dont hijack behaviour, just let browser act normally
|
||||||
if (!url || event.ctrlKey || event.metaKey) return
|
if (!url || event.ctrlKey || event.metaKey) return
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
|
||||||
if (isSamePage(url) && url.hash) {
|
if (isSamePage(url) && url.hash) {
|
||||||
const el = document.getElementById(
|
const el = document.getElementById(decodeURIComponent(url.hash.substring(1)))
|
||||||
decodeURIComponent(url.hash.substring(1)),
|
|
||||||
)
|
|
||||||
el?.scrollIntoView()
|
el?.scrollIntoView()
|
||||||
history.pushState({}, "", url)
|
history.pushState({}, "", url)
|
||||||
return
|
return
|
||||||
@ -149,9 +133,8 @@ function createRouter() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
window.addEventListener("popstate", (event) => {
|
window.addEventListener("popstate", (event) => {
|
||||||
const {url} = getOpts(event) ?? {}
|
const { url } = getOpts(event) ?? {}
|
||||||
if (window.location.hash && window.location.pathname === url?.pathname)
|
if (window.location.hash && window.location.pathname === url?.pathname) return
|
||||||
return
|
|
||||||
try {
|
try {
|
||||||
navigate(new URL(window.location.toString()), true)
|
navigate(new URL(window.location.toString()), true)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -184,7 +167,7 @@ if (!customElements.get("route-announcer")) {
|
|||||||
const attrs = {
|
const attrs = {
|
||||||
"aria-live": "assertive",
|
"aria-live": "assertive",
|
||||||
"aria-atomic": "true",
|
"aria-atomic": "true",
|
||||||
"style":
|
style:
|
||||||
"position: absolute; left: 0; top: 0; clip: rect(0 0 0 0); clip-path: inset(50%); overflow: hidden; white-space: nowrap; width: 1px; height: 1px",
|
"position: absolute; left: 0; top: 0; clip: rect(0 0 0 0); clip-path: inset(50%); overflow: hidden; white-space: nowrap; width: 1px; height: 1px",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -23,8 +23,7 @@ function toggleToc(this: HTMLElement) {
|
|||||||
const content = this.nextElementSibling as HTMLElement | undefined
|
const content = this.nextElementSibling as HTMLElement | undefined
|
||||||
if (!content) return
|
if (!content) return
|
||||||
content.classList.toggle("collapsed")
|
content.classList.toggle("collapsed")
|
||||||
content.style.maxHeight =
|
content.style.maxHeight = content.style.maxHeight === "0px" ? content.scrollHeight + "px" : "0px"
|
||||||
content.style.maxHeight === "0px" ? content.scrollHeight + "px" : "0px"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupToc() {
|
function setupToc() {
|
||||||
@ -45,8 +44,6 @@ document.addEventListener("nav", () => {
|
|||||||
|
|
||||||
// update toc entry highlighting
|
// update toc entry highlighting
|
||||||
observer.disconnect()
|
observer.disconnect()
|
||||||
const headers = document.querySelectorAll(
|
const headers = document.querySelectorAll("h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]")
|
||||||
"h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]",
|
|
||||||
)
|
|
||||||
headers.forEach((header) => observer.observe(header))
|
headers.forEach((header) => observer.observe(header))
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,7 +1,4 @@
|
|||||||
export function registerEscapeHandler(
|
export function registerEscapeHandler(outsideContainer: HTMLElement | null, cb: () => void) {
|
||||||
outsideContainer: HTMLElement | null,
|
|
||||||
cb: () => void,
|
|
||||||
) {
|
|
||||||
if (!outsideContainer) return
|
if (!outsideContainer) return
|
||||||
function click(this: HTMLElement, e: HTMLElementEventMap["click"]) {
|
function click(this: HTMLElement, e: HTMLElementEventMap["click"]) {
|
||||||
if (e.target !== this) return
|
if (e.target !== this) return
|
||||||
|
|||||||
@ -62,9 +62,13 @@
|
|||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
|
|
||||||
|
@media all and (max-width: $fullPageWidth) {
|
||||||
|
width: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
& > * {
|
& > * {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-radius: 5px;
|
border-radius: 7px;
|
||||||
background: var(--light);
|
background: var(--light);
|
||||||
box-shadow:
|
box-shadow:
|
||||||
0 14px 50px rgba(27, 33, 48, 0.12),
|
0 14px 50px rgba(27, 33, 48, 0.12),
|
||||||
@ -86,71 +90,81 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
& > #search-layout {
|
& > #search-layout {
|
||||||
display: flex;
|
display: none;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
opacity: 0;
|
|
||||||
border: 1px solid var(--lightgray);
|
border: 1px solid var(--lightgray);
|
||||||
|
flex: 0 0 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
& > div {
|
&.display-results {
|
||||||
// vh - #search-space.margin-top
|
display: flex;
|
||||||
height: calc(75vh - 12vh);
|
}
|
||||||
background: none;
|
|
||||||
|
|
||||||
&:first-child {
|
&[data-preview] > #results-container {
|
||||||
border-top-left-radius: 5px;
|
flex: 0 0 min(30%, 450px);
|
||||||
border-bottom-left-radius: 5px;
|
}
|
||||||
border-right: 1px solid var(--lightgray);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:last-child {
|
@media all and (min-width: $tabletBreakpoint) {
|
||||||
border-top-right-radius: 5px;
|
&[data-preview] {
|
||||||
border-bottom-right-radius: 5px;
|
& .result-card > p.preview {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
&:first-child {
|
||||||
|
border-right: 1px solid var(--lightgray);
|
||||||
|
border-top-right-radius: unset;
|
||||||
|
border-bottom-right-radius: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-top-left-radius: unset;
|
||||||
|
border-bottom-left-radius: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
height: calc(75vh - 12vh);
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
@media all and (max-width: $tabletBreakpoint) {
|
@media all and (max-width: $tabletBreakpoint) {
|
||||||
display: block;
|
& > #preview-container {
|
||||||
& > *:not(#results-container) {
|
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
& > #results-container {
|
&[data-preview] > #results-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
|
flex: 0 0 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
& .highlight {
|
& .highlight {
|
||||||
background: color-mix(in srgb, var(--tertiary) 60%, transparent);
|
background: color-mix(in srgb, var(--tertiary) 60%, rgba(255, 255, 255, 0));
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
|
scroll-margin-top: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
& > #preview-container {
|
& > #preview-container {
|
||||||
display: block;
|
display: block;
|
||||||
box-sizing: border-box;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
font-family: inherit;
|
||||||
|
color: var(--dark);
|
||||||
|
line-height: 1.5em;
|
||||||
|
font-weight: $normalWeight;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 0 2rem;
|
||||||
|
|
||||||
& .preview-inner {
|
& .preview-inner {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 1em;
|
width: min($pageWidth, 100%);
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
overflow-y: auto;
|
|
||||||
font-family: inherit;
|
|
||||||
color: var(--dark);
|
|
||||||
line-height: 1.5em;
|
|
||||||
font-weight: 400;
|
|
||||||
background: var(--light);
|
|
||||||
border-top-right-radius: 5px;
|
|
||||||
border-bottom-right-radius: 5px;
|
|
||||||
box-shadow:
|
|
||||||
0 14px 50px rgba(27, 33, 48, 0.12),
|
|
||||||
0 10px 30px rgba(27, 33, 48, 0.16);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
a.internal {
|
a[role="anchor"] {
|
||||||
background-color: none;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,6 +172,7 @@
|
|||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
|
||||||
& .result-card {
|
& .result-card {
|
||||||
|
overflow: hidden;
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background 0.2s ease;
|
transition: background 0.2s ease;
|
||||||
@ -173,7 +188,6 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
background: var(--light);
|
|
||||||
outline: none;
|
outline: none;
|
||||||
font-weight: inherit;
|
font-weight: inherit;
|
||||||
|
|
||||||
@ -187,41 +201,23 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
& > ul > li {
|
& > ul.tags {
|
||||||
margin: 0;
|
|
||||||
display: inline-block;
|
|
||||||
white-space: nowrap;
|
|
||||||
margin: 0;
|
|
||||||
overflow-wrap: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
& > ul {
|
|
||||||
list-style: none;
|
|
||||||
display: flex;
|
|
||||||
padding-left: 0;
|
|
||||||
gap: 0.4rem;
|
|
||||||
margin: 0;
|
|
||||||
margin-top: 0.45rem;
|
margin-top: 0.45rem;
|
||||||
box-sizing: border-box;
|
margin-bottom: 0;
|
||||||
overflow: hidden;
|
|
||||||
background-clip: border-box;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
& > ul > li > p {
|
& > ul > li > p {
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background-color: var(--highlight);
|
background-color: var(--highlight);
|
||||||
overflow: hidden;
|
padding: 0.2rem 0.4rem;
|
||||||
background-clip: border-box;
|
margin: 0 0.1rem;
|
||||||
padding: 0.03rem 0.4rem;
|
line-height: 1.4rem;
|
||||||
margin: 0;
|
font-weight: $boldWeight;
|
||||||
color: var(--secondary);
|
color: var(--secondary);
|
||||||
opacity: 0.85;
|
|
||||||
}
|
|
||||||
|
|
||||||
& > ul > li > .match-tag {
|
&.match-tag {
|
||||||
color: var(--tertiary);
|
color: var(--tertiary);
|
||||||
font-weight: bold;
|
}
|
||||||
opacity: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
& > p {
|
& > p {
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import {ComponentType, JSX} from "preact"
|
import { ComponentType, JSX } from "preact"
|
||||||
import {StaticResources} from "../util/resources"
|
import { StaticResources } from "../util/resources"
|
||||||
import {QuartzPluginData} from "../plugins/vfile"
|
import { QuartzPluginData } from "../plugins/vfile"
|
||||||
import {GlobalConfiguration} from "../cfg"
|
import { GlobalConfiguration } from "../cfg"
|
||||||
import {Node} from "hast"
|
import { Node } from "hast"
|
||||||
import {BuildCtx} from "../util/ctx"
|
import { BuildCtx } from "../util/ctx"
|
||||||
|
|
||||||
export type QuartzComponentProps = {
|
export type QuartzComponentProps = {
|
||||||
ctx: BuildCtx
|
ctx: BuildCtx
|
||||||
@ -24,6 +24,6 @@ export type QuartzComponent = ComponentType<QuartzComponentProps> & {
|
|||||||
afterDOMLoaded?: string
|
afterDOMLoaded?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type QuartzComponentConstructor<
|
export type QuartzComponentConstructor<Options extends object | undefined = undefined> = (
|
||||||
Options extends object | undefined = undefined,
|
opts: Options,
|
||||||
> = (opts: Options) => QuartzComponent
|
) => QuartzComponent
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import {Translation, CalloutTranslation} from "./locales/definition"
|
import { Translation, CalloutTranslation } from "./locales/definition"
|
||||||
import en from "./locales/en-US"
|
import enUs from "./locales/en-US"
|
||||||
|
import enGb from "./locales/en-GB"
|
||||||
import fr from "./locales/fr-FR"
|
import fr from "./locales/fr-FR"
|
||||||
import it from "./locales/it-IT"
|
import it from "./locales/it-IT"
|
||||||
import ja from "./locales/ja-JP"
|
import ja from "./locales/ja-JP"
|
||||||
@ -64,7 +65,6 @@ export const TRANSLATIONS = {
|
|||||||
} as const
|
} as const
|
||||||
|
|
||||||
export const defaultTranslation = "en-US"
|
export const defaultTranslation = "en-US"
|
||||||
export const i18n = (locale: ValidLocale): Translation =>
|
export const i18n = (locale: ValidLocale): Translation => TRANSLATIONS[locale ?? defaultTranslation]
|
||||||
TRANSLATIONS[locale ?? defaultTranslation]
|
|
||||||
export type ValidLocale = keyof typeof TRANSLATIONS
|
export type ValidLocale = keyof typeof TRANSLATIONS
|
||||||
export type ValidCallout = keyof CalloutTranslation
|
export type ValidCallout = keyof CalloutTranslation
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {Translation} from "./definition"
|
import { Translation } from "./definition"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
propertyDefaults: {
|
propertyDefaults: {
|
||||||
@ -40,10 +40,10 @@ export default {
|
|||||||
},
|
},
|
||||||
recentNotes: {
|
recentNotes: {
|
||||||
title: "آخر الملاحظات",
|
title: "آخر الملاحظات",
|
||||||
seeRemainingMore: ({remaining}) => `تصفح ${remaining} أكثر →`,
|
seeRemainingMore: ({ remaining }) => `تصفح ${remaining} أكثر →`,
|
||||||
},
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({targetSlug}) => `مقتبس من ${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `مقتبس من ${targetSlug}`,
|
||||||
linkToOriginal: "وصلة للملاحظة الرئيسة",
|
linkToOriginal: "وصلة للملاحظة الرئيسة",
|
||||||
},
|
},
|
||||||
search: {
|
search: {
|
||||||
@ -54,7 +54,7 @@ export default {
|
|||||||
title: "فهرس المحتويات",
|
title: "فهرس المحتويات",
|
||||||
},
|
},
|
||||||
contentMeta: {
|
contentMeta: {
|
||||||
readingTime: ({minutes}) =>
|
readingTime: ({ minutes }) =>
|
||||||
minutes == 1
|
minutes == 1
|
||||||
? `دقيقة أو أقل للقراءة`
|
? `دقيقة أو أقل للقراءة`
|
||||||
: minutes == 2
|
: minutes == 2
|
||||||
@ -65,7 +65,7 @@ export default {
|
|||||||
pages: {
|
pages: {
|
||||||
rss: {
|
rss: {
|
||||||
recentNotes: "آخر الملاحظات",
|
recentNotes: "آخر الملاحظات",
|
||||||
lastFewNotes: ({count}) => `آخر ${count} ملاحظة`,
|
lastFewNotes: ({ count }) => `آخر ${count} ملاحظة`,
|
||||||
},
|
},
|
||||||
error: {
|
error: {
|
||||||
title: "غير موجود",
|
title: "غير موجود",
|
||||||
@ -74,20 +74,16 @@ export default {
|
|||||||
},
|
},
|
||||||
folderContent: {
|
folderContent: {
|
||||||
folder: "مجلد",
|
folder: "مجلد",
|
||||||
itemsUnderFolder: ({count}) =>
|
itemsUnderFolder: ({ count }) =>
|
||||||
count === 1
|
count === 1 ? "يوجد عنصر واحد فقط تحت هذا المجلد" : `يوجد ${count} عناصر تحت هذا المجلد.`,
|
||||||
? "يوجد عنصر واحد فقط تحت هذا المجلد"
|
|
||||||
: `يوجد ${count} عناصر تحت هذا المجلد.`,
|
|
||||||
},
|
},
|
||||||
tagContent: {
|
tagContent: {
|
||||||
tag: "الوسم",
|
tag: "الوسم",
|
||||||
tagIndex: "مؤشر الوسم",
|
tagIndex: "مؤشر الوسم",
|
||||||
itemsUnderTag: ({count}) =>
|
itemsUnderTag: ({ count }) =>
|
||||||
count === 1
|
count === 1 ? "يوجد عنصر واحد فقط تحت هذا الوسم" : `يوجد ${count} عناصر تحت هذا الوسم.`,
|
||||||
? "يوجد عنصر واحد فقط تحت هذا الوسم"
|
showingFirst: ({ count }) => `إظهار أول ${count} أوسمة.`,
|
||||||
: `يوجد ${count} عناصر تحت هذا الوسم.`,
|
totalTags: ({ count }) => `يوجد ${count} أوسمة.`,
|
||||||
showingFirst: ({count}) => `إظهار أول ${count} أوسمة.`,
|
|
||||||
totalTags: ({count}) => `يوجد ${count} أوسمة.`,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const satisfies Translation
|
} as const satisfies Translation
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {Translation} from "./definition"
|
import { Translation } from "./definition"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
propertyDefaults: {
|
propertyDefaults: {
|
||||||
@ -40,10 +40,10 @@ export default {
|
|||||||
},
|
},
|
||||||
recentNotes: {
|
recentNotes: {
|
||||||
title: "Zuletzt bearbeitete Seiten",
|
title: "Zuletzt bearbeitete Seiten",
|
||||||
seeRemainingMore: ({remaining}) => `${remaining} weitere ansehen →`,
|
seeRemainingMore: ({ remaining }) => `${remaining} weitere ansehen →`,
|
||||||
},
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({targetSlug}) => `Transklusion von ${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `Transklusion von ${targetSlug}`,
|
||||||
linkToOriginal: "Link zum Original",
|
linkToOriginal: "Link zum Original",
|
||||||
},
|
},
|
||||||
search: {
|
search: {
|
||||||
@ -54,36 +54,31 @@ export default {
|
|||||||
title: "Inhaltsverzeichnis",
|
title: "Inhaltsverzeichnis",
|
||||||
},
|
},
|
||||||
contentMeta: {
|
contentMeta: {
|
||||||
readingTime: ({minutes}) => `${minutes} min read`,
|
readingTime: ({ minutes }) => `${minutes} min read`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
pages: {
|
pages: {
|
||||||
rss: {
|
rss: {
|
||||||
recentNotes: "Zuletzt bearbeitete Seiten",
|
recentNotes: "Zuletzt bearbeitete Seiten",
|
||||||
lastFewNotes: ({count}) => `Letzte ${count} Seiten`,
|
lastFewNotes: ({ count }) => `Letzte ${count} Seiten`,
|
||||||
},
|
},
|
||||||
error: {
|
error: {
|
||||||
title: "Nicht gefunden",
|
title: "Nicht gefunden",
|
||||||
notFound:
|
notFound: "Diese Seite ist entweder nicht öffentlich oder existiert nicht.",
|
||||||
"Diese Seite ist entweder nicht öffentlich oder existiert nicht.",
|
|
||||||
home: "Return to Homepage",
|
home: "Return to Homepage",
|
||||||
},
|
},
|
||||||
folderContent: {
|
folderContent: {
|
||||||
folder: "Ordner",
|
folder: "Ordner",
|
||||||
itemsUnderFolder: ({count}) =>
|
itemsUnderFolder: ({ count }) =>
|
||||||
count === 1
|
count === 1 ? "1 Datei in diesem Ordner." : `${count} Dateien in diesem Ordner.`,
|
||||||
? "1 Datei in diesem Ordner."
|
|
||||||
: `${count} Dateien in diesem Ordner.`,
|
|
||||||
},
|
},
|
||||||
tagContent: {
|
tagContent: {
|
||||||
tag: "Tag",
|
tag: "Tag",
|
||||||
tagIndex: "Tag-Übersicht",
|
tagIndex: "Tag-Übersicht",
|
||||||
itemsUnderTag: ({count}) =>
|
itemsUnderTag: ({ count }) =>
|
||||||
count === 1
|
count === 1 ? "1 Datei mit diesem Tag." : `${count} Dateien mit diesem Tag.`,
|
||||||
? "1 Datei mit diesem Tag."
|
showingFirst: ({ count }) => `Die ersten ${count} Tags werden angezeigt.`,
|
||||||
: `${count} Dateien mit diesem Tag.`,
|
totalTags: ({ count }) => `${count} Tags insgesamt.`,
|
||||||
showingFirst: ({count}) => `Die ersten ${count} Tags werden angezeigt.`,
|
|
||||||
totalTags: ({count}) => `${count} Tags insgesamt.`,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const satisfies Translation
|
} as const satisfies Translation
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {FullSlug} from "../../util/path"
|
import { FullSlug } from "../../util/path"
|
||||||
|
|
||||||
export interface CalloutTranslation {
|
export interface CalloutTranslation {
|
||||||
note: string
|
note: string
|
||||||
@ -42,10 +42,10 @@ export interface Translation {
|
|||||||
}
|
}
|
||||||
recentNotes: {
|
recentNotes: {
|
||||||
title: string
|
title: string
|
||||||
seeRemainingMore: (variables: {remaining: number}) => string
|
seeRemainingMore: (variables: { remaining: number }) => string
|
||||||
}
|
}
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: (variables: {targetSlug: FullSlug}) => string
|
transcludeOf: (variables: { targetSlug: FullSlug }) => string
|
||||||
linkToOriginal: string
|
linkToOriginal: string
|
||||||
}
|
}
|
||||||
search: {
|
search: {
|
||||||
@ -56,13 +56,13 @@ export interface Translation {
|
|||||||
title: string
|
title: string
|
||||||
}
|
}
|
||||||
contentMeta: {
|
contentMeta: {
|
||||||
readingTime: (variables: {minutes: number}) => string
|
readingTime: (variables: { minutes: number }) => string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pages: {
|
pages: {
|
||||||
rss: {
|
rss: {
|
||||||
recentNotes: string
|
recentNotes: string
|
||||||
lastFewNotes: (variables: {count: number}) => string
|
lastFewNotes: (variables: { count: number }) => string
|
||||||
}
|
}
|
||||||
error: {
|
error: {
|
||||||
title: string
|
title: string
|
||||||
@ -71,14 +71,14 @@ export interface Translation {
|
|||||||
}
|
}
|
||||||
folderContent: {
|
folderContent: {
|
||||||
folder: string
|
folder: string
|
||||||
itemsUnderFolder: (variables: {count: number}) => string
|
itemsUnderFolder: (variables: { count: number }) => string
|
||||||
}
|
}
|
||||||
tagContent: {
|
tagContent: {
|
||||||
tag: string
|
tag: string
|
||||||
tagIndex: string
|
tagIndex: string
|
||||||
itemsUnderTag: (variables: {count: number}) => string
|
itemsUnderTag: (variables: { count: number }) => string
|
||||||
showingFirst: (variables: {count: number}) => string
|
showingFirst: (variables: { count: number }) => string
|
||||||
totalTags: (variables: {count: number}) => string
|
totalTags: (variables: { count: number }) => string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {Translation} from "./definition"
|
import { Translation } from "./definition"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
propertyDefaults: {
|
propertyDefaults: {
|
||||||
@ -40,10 +40,10 @@ export default {
|
|||||||
},
|
},
|
||||||
recentNotes: {
|
recentNotes: {
|
||||||
title: "Recent Notes",
|
title: "Recent Notes",
|
||||||
seeRemainingMore: ({remaining}) => `See ${remaining} more →`,
|
seeRemainingMore: ({ remaining }) => `See ${remaining} more →`,
|
||||||
},
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({targetSlug}) => `Transclude of ${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `Transclude of ${targetSlug}`,
|
||||||
linkToOriginal: "Link to original",
|
linkToOriginal: "Link to original",
|
||||||
},
|
},
|
||||||
search: {
|
search: {
|
||||||
@ -54,13 +54,13 @@ export default {
|
|||||||
title: "Table of Contents",
|
title: "Table of Contents",
|
||||||
},
|
},
|
||||||
contentMeta: {
|
contentMeta: {
|
||||||
readingTime: ({minutes}) => `${minutes} min read`,
|
readingTime: ({ minutes }) => `${minutes} min read`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
pages: {
|
pages: {
|
||||||
rss: {
|
rss: {
|
||||||
recentNotes: "Recent notes",
|
recentNotes: "Recent notes",
|
||||||
lastFewNotes: ({count}) => `Last ${count} notes`,
|
lastFewNotes: ({ count }) => `Last ${count} notes`,
|
||||||
},
|
},
|
||||||
error: {
|
error: {
|
||||||
title: "Not Found",
|
title: "Not Found",
|
||||||
@ -69,18 +69,16 @@ export default {
|
|||||||
},
|
},
|
||||||
folderContent: {
|
folderContent: {
|
||||||
folder: "Folder",
|
folder: "Folder",
|
||||||
itemsUnderFolder: ({count}) =>
|
itemsUnderFolder: ({ count }) =>
|
||||||
count === 1
|
count === 1 ? "1 item under this folder." : `${count} items under this folder.`,
|
||||||
? "1 item under this folder."
|
|
||||||
: `${count} items under this folder.`,
|
|
||||||
},
|
},
|
||||||
tagContent: {
|
tagContent: {
|
||||||
tag: "Tag",
|
tag: "Tag",
|
||||||
tagIndex: "Tag Index",
|
tagIndex: "Tag Index",
|
||||||
itemsUnderTag: ({count}) =>
|
itemsUnderTag: ({ count }) =>
|
||||||
count === 1 ? "1 item with this tag." : `${count} items with this tag.`,
|
count === 1 ? "1 item with this tag." : `${count} items with this tag.`,
|
||||||
showingFirst: ({count}) => `Showing first ${count} tags.`,
|
showingFirst: ({ count }) => `Showing first ${count} tags.`,
|
||||||
totalTags: ({count}) => `Found ${count} total tags.`,
|
totalTags: ({ count }) => `Found ${count} total tags.`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const satisfies Translation
|
} as const satisfies Translation
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {Translation} from "./definition"
|
import { Translation } from "./definition"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
propertyDefaults: {
|
propertyDefaults: {
|
||||||
@ -40,10 +40,10 @@ export default {
|
|||||||
},
|
},
|
||||||
recentNotes: {
|
recentNotes: {
|
||||||
title: "Notas Recientes",
|
title: "Notas Recientes",
|
||||||
seeRemainingMore: ({remaining}) => `Vea ${remaining} más →`,
|
seeRemainingMore: ({ remaining }) => `Vea ${remaining} más →`,
|
||||||
},
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({targetSlug}) => `Transcluido de ${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `Transcluido de ${targetSlug}`,
|
||||||
linkToOriginal: "Enlace al original",
|
linkToOriginal: "Enlace al original",
|
||||||
},
|
},
|
||||||
search: {
|
search: {
|
||||||
@ -54,13 +54,13 @@ export default {
|
|||||||
title: "Tabla de Contenidos",
|
title: "Tabla de Contenidos",
|
||||||
},
|
},
|
||||||
contentMeta: {
|
contentMeta: {
|
||||||
readingTime: ({minutes}) => `${minutes} min read`,
|
readingTime: ({ minutes }) => `Se lee en ${minutes} min`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
pages: {
|
pages: {
|
||||||
rss: {
|
rss: {
|
||||||
recentNotes: "Notas recientes",
|
recentNotes: "Notas recientes",
|
||||||
lastFewNotes: ({count}) => `Últimás ${count} notas`,
|
lastFewNotes: ({ count }) => `Últimas ${count} notas`,
|
||||||
},
|
},
|
||||||
error: {
|
error: {
|
||||||
title: "No se ha encontrado.",
|
title: "No se ha encontrado.",
|
||||||
@ -69,20 +69,16 @@ export default {
|
|||||||
},
|
},
|
||||||
folderContent: {
|
folderContent: {
|
||||||
folder: "Carpeta",
|
folder: "Carpeta",
|
||||||
itemsUnderFolder: ({count}) =>
|
itemsUnderFolder: ({ count }) =>
|
||||||
count === 1
|
count === 1 ? "1 artículo en esta carpeta." : `${count} artículos en esta carpeta.`,
|
||||||
? "1 artículo en esta carpeta."
|
|
||||||
: `${count} artículos en esta carpeta.`,
|
|
||||||
},
|
},
|
||||||
tagContent: {
|
tagContent: {
|
||||||
tag: "Etiqueta",
|
tag: "Etiqueta",
|
||||||
tagIndex: "Índice de Etiquetas",
|
tagIndex: "Índice de Etiquetas",
|
||||||
itemsUnderTag: ({count}) =>
|
itemsUnderTag: ({ count }) =>
|
||||||
count === 1
|
count === 1 ? "1 artículo con esta etiqueta." : `${count} artículos con esta etiqueta.`,
|
||||||
? "1 artículo con esta etiqueta."
|
showingFirst: ({ count }) => `Mostrando las primeras ${count} etiquetas.`,
|
||||||
: `${count} artículos con esta etiqueta.`,
|
totalTags: ({ count }) => `Se han encontrado ${count} etiquetas en total.`,
|
||||||
showingFirst: ({count}) => `Mostrando las primeras ${count} etiquetas.`,
|
|
||||||
totalTags: ({count}) => `Se encontraron ${count} etiquetas en total.`,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const satisfies Translation
|
} as const satisfies Translation
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {Translation} from "./definition"
|
import { Translation } from "./definition"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
propertyDefaults: {
|
propertyDefaults: {
|
||||||
@ -40,10 +40,10 @@ export default {
|
|||||||
},
|
},
|
||||||
recentNotes: {
|
recentNotes: {
|
||||||
title: "یادداشتهای اخیر",
|
title: "یادداشتهای اخیر",
|
||||||
seeRemainingMore: ({remaining}) => `${remaining} یادداشت دیگر →`,
|
seeRemainingMore: ({ remaining }) => `${remaining} یادداشت دیگر →`,
|
||||||
},
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({targetSlug}) => `از ${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `از ${targetSlug}`,
|
||||||
linkToOriginal: "پیوند به اصلی",
|
linkToOriginal: "پیوند به اصلی",
|
||||||
},
|
},
|
||||||
search: {
|
search: {
|
||||||
@ -54,13 +54,13 @@ export default {
|
|||||||
title: "فهرست",
|
title: "فهرست",
|
||||||
},
|
},
|
||||||
contentMeta: {
|
contentMeta: {
|
||||||
readingTime: ({minutes}) => `زمان تقریبی مطالعه: ${minutes} دقیقه`,
|
readingTime: ({ minutes }) => `زمان تقریبی مطالعه: ${minutes} دقیقه`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
pages: {
|
pages: {
|
||||||
rss: {
|
rss: {
|
||||||
recentNotes: "یادداشتهای اخیر",
|
recentNotes: "یادداشتهای اخیر",
|
||||||
lastFewNotes: ({count}) => `${count} یادداشت اخیر`,
|
lastFewNotes: ({ count }) => `${count} یادداشت اخیر`,
|
||||||
},
|
},
|
||||||
error: {
|
error: {
|
||||||
title: "یافت نشد",
|
title: "یافت نشد",
|
||||||
@ -69,18 +69,16 @@ export default {
|
|||||||
},
|
},
|
||||||
folderContent: {
|
folderContent: {
|
||||||
folder: "پوشه",
|
folder: "پوشه",
|
||||||
itemsUnderFolder: ({count}) =>
|
itemsUnderFolder: ({ count }) =>
|
||||||
count === 1
|
count === 1 ? ".یک مطلب در این پوشه است" : `${count} مطلب در این پوشه است.`,
|
||||||
? ".یک مطلب در این پوشه است"
|
|
||||||
: `${count} مطلب در این پوشه است.`,
|
|
||||||
},
|
},
|
||||||
tagContent: {
|
tagContent: {
|
||||||
tag: "برچسب",
|
tag: "برچسب",
|
||||||
tagIndex: "فهرست برچسبها",
|
tagIndex: "فهرست برچسبها",
|
||||||
itemsUnderTag: ({count}) =>
|
itemsUnderTag: ({ count }) =>
|
||||||
count === 1 ? "یک مطلب با این برچسب" : `${count} مطلب با این برچسب.`,
|
count === 1 ? "یک مطلب با این برچسب" : `${count} مطلب با این برچسب.`,
|
||||||
showingFirst: ({count}) => `در حال نمایش ${count} برچسب.`,
|
showingFirst: ({ count }) => `در حال نمایش ${count} برچسب.`,
|
||||||
totalTags: ({count}) => `${count} برچسب یافت شد.`,
|
totalTags: ({ count }) => `${count} برچسب یافت شد.`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const satisfies Translation
|
} as const satisfies Translation
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {Translation} from "./definition"
|
import { Translation } from "./definition"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
propertyDefaults: {
|
propertyDefaults: {
|
||||||
@ -40,10 +40,10 @@ export default {
|
|||||||
},
|
},
|
||||||
recentNotes: {
|
recentNotes: {
|
||||||
title: "Notes Récentes",
|
title: "Notes Récentes",
|
||||||
seeRemainingMore: ({remaining}) => `Voir ${remaining} de plus →`,
|
seeRemainingMore: ({ remaining }) => `Voir ${remaining} de plus →`,
|
||||||
},
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({targetSlug}) => `Transclusion de ${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `Transclusion de ${targetSlug}`,
|
||||||
linkToOriginal: "Lien vers l'original",
|
linkToOriginal: "Lien vers l'original",
|
||||||
},
|
},
|
||||||
search: {
|
search: {
|
||||||
@ -54,35 +54,31 @@ export default {
|
|||||||
title: "Table des Matières",
|
title: "Table des Matières",
|
||||||
},
|
},
|
||||||
contentMeta: {
|
contentMeta: {
|
||||||
readingTime: ({minutes}) => `${minutes} min read`,
|
readingTime: ({ minutes }) => `${minutes} min de lecture`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
pages: {
|
pages: {
|
||||||
rss: {
|
rss: {
|
||||||
recentNotes: "Notes récentes",
|
recentNotes: "Notes récentes",
|
||||||
lastFewNotes: ({count}) => `Les dernières ${count} notes`,
|
lastFewNotes: ({ count }) => `Les dernières ${count} notes`,
|
||||||
},
|
},
|
||||||
error: {
|
error: {
|
||||||
title: "Pas trouvé",
|
title: "Introuvable",
|
||||||
notFound: "Cette page est soit privée, soit elle n'existe pas.",
|
notFound: "Cette page est soit privée, soit elle n'existe pas.",
|
||||||
home: "Retour à la page d'accueil",
|
home: "Retour à la page d'accueil",
|
||||||
},
|
},
|
||||||
folderContent: {
|
folderContent: {
|
||||||
folder: "Dossier",
|
folder: "Dossier",
|
||||||
itemsUnderFolder: ({count}) =>
|
itemsUnderFolder: ({ count }) =>
|
||||||
count === 1
|
count === 1 ? "1 élément sous ce dossier." : `${count} éléments sous ce dossier.`,
|
||||||
? "1 élément sous ce dossier."
|
|
||||||
: `${count} éléments sous ce dossier.`,
|
|
||||||
},
|
},
|
||||||
tagContent: {
|
tagContent: {
|
||||||
tag: "Étiquette",
|
tag: "Étiquette",
|
||||||
tagIndex: "Index des étiquettes",
|
tagIndex: "Index des étiquettes",
|
||||||
itemsUnderTag: ({count}) =>
|
itemsUnderTag: ({ count }) =>
|
||||||
count === 1
|
count === 1 ? "1 élément avec cette étiquette." : `${count} éléments avec cette étiquette.`,
|
||||||
? "1 élément avec cette étiquette."
|
showingFirst: ({ count }) => `Affichage des premières ${count} étiquettes.`,
|
||||||
: `${count} éléments avec cette étiquette.`,
|
totalTags: ({ count }) => `Trouvé ${count} étiquettes au total.`,
|
||||||
showingFirst: ({count}) => `Affichage des premières ${count} étiquettes.`,
|
|
||||||
totalTags: ({count}) => `Trouvé ${count} étiquettes au total.`,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const satisfies Translation
|
} as const satisfies Translation
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {Translation} from "./definition"
|
import { Translation } from "./definition"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
propertyDefaults: {
|
propertyDefaults: {
|
||||||
@ -40,10 +40,10 @@ export default {
|
|||||||
},
|
},
|
||||||
recentNotes: {
|
recentNotes: {
|
||||||
title: "Legutóbbi jegyzetek",
|
title: "Legutóbbi jegyzetek",
|
||||||
seeRemainingMore: ({remaining}) => `${remaining} további megtekintése →`,
|
seeRemainingMore: ({ remaining }) => `${remaining} további megtekintése →`,
|
||||||
},
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({targetSlug}) => `${targetSlug} áthivatkozása`,
|
transcludeOf: ({ targetSlug }) => `${targetSlug} áthivatkozása`,
|
||||||
linkToOriginal: "Hivatkozás az eredetire",
|
linkToOriginal: "Hivatkozás az eredetire",
|
||||||
},
|
},
|
||||||
search: {
|
search: {
|
||||||
@ -54,13 +54,13 @@ export default {
|
|||||||
title: "Tartalomjegyzék",
|
title: "Tartalomjegyzék",
|
||||||
},
|
},
|
||||||
contentMeta: {
|
contentMeta: {
|
||||||
readingTime: ({minutes}) => `${minutes} perces olvasás`,
|
readingTime: ({ minutes }) => `${minutes} perces olvasás`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
pages: {
|
pages: {
|
||||||
rss: {
|
rss: {
|
||||||
recentNotes: "Legutóbbi jegyzetek",
|
recentNotes: "Legutóbbi jegyzetek",
|
||||||
lastFewNotes: ({count}) => `Legutóbbi ${count} jegyzet`,
|
lastFewNotes: ({ count }) => `Legutóbbi ${count} jegyzet`,
|
||||||
},
|
},
|
||||||
error: {
|
error: {
|
||||||
title: "Nem található",
|
title: "Nem található",
|
||||||
@ -69,15 +69,14 @@ export default {
|
|||||||
},
|
},
|
||||||
folderContent: {
|
folderContent: {
|
||||||
folder: "Mappa",
|
folder: "Mappa",
|
||||||
itemsUnderFolder: ({count}) =>
|
itemsUnderFolder: ({ count }) => `Ebben a mappában ${count} elem található.`,
|
||||||
`Ebben a mappában ${count} elem található.`,
|
|
||||||
},
|
},
|
||||||
tagContent: {
|
tagContent: {
|
||||||
tag: "Címke",
|
tag: "Címke",
|
||||||
tagIndex: "Címke index",
|
tagIndex: "Címke index",
|
||||||
itemsUnderTag: ({count}) => `${count} elem található ezzel a címkével.`,
|
itemsUnderTag: ({ count }) => `${count} elem található ezzel a címkével.`,
|
||||||
showingFirst: ({count}) => `Első ${count} címke megjelenítve.`,
|
showingFirst: ({ count }) => `Első ${count} címke megjelenítve.`,
|
||||||
totalTags: ({count}) => `Összesen ${count} címke található.`,
|
totalTags: ({ count }) => `Összesen ${count} címke található.`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const satisfies Translation
|
} as const satisfies Translation
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {Translation} from "./definition"
|
import { Translation } from "./definition"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
propertyDefaults: {
|
propertyDefaults: {
|
||||||
@ -40,10 +40,10 @@ export default {
|
|||||||
},
|
},
|
||||||
recentNotes: {
|
recentNotes: {
|
||||||
title: "Note recenti",
|
title: "Note recenti",
|
||||||
seeRemainingMore: ({remaining}) => `Vedi ${remaining} altro →`,
|
seeRemainingMore: ({ remaining }) => `Vedi ${remaining} altro →`,
|
||||||
},
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({targetSlug}) => `Transclusione di ${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `Transclusione di ${targetSlug}`,
|
||||||
linkToOriginal: "Link all'originale",
|
linkToOriginal: "Link all'originale",
|
||||||
},
|
},
|
||||||
search: {
|
search: {
|
||||||
@ -54,13 +54,13 @@ export default {
|
|||||||
title: "Tabella dei contenuti",
|
title: "Tabella dei contenuti",
|
||||||
},
|
},
|
||||||
contentMeta: {
|
contentMeta: {
|
||||||
readingTime: ({minutes}) => `${minutes} minuti`,
|
readingTime: ({ minutes }) => `${minutes} minuti`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
pages: {
|
pages: {
|
||||||
rss: {
|
rss: {
|
||||||
recentNotes: "Note recenti",
|
recentNotes: "Note recenti",
|
||||||
lastFewNotes: ({count}) => `Ultime ${count} note`,
|
lastFewNotes: ({ count }) => `Ultime ${count} note`,
|
||||||
},
|
},
|
||||||
error: {
|
error: {
|
||||||
title: "Non trovato",
|
title: "Non trovato",
|
||||||
@ -69,20 +69,16 @@ export default {
|
|||||||
},
|
},
|
||||||
folderContent: {
|
folderContent: {
|
||||||
folder: "Cartella",
|
folder: "Cartella",
|
||||||
itemsUnderFolder: ({count}) =>
|
itemsUnderFolder: ({ count }) =>
|
||||||
count === 1
|
count === 1 ? "1 oggetto in questa cartella." : `${count} oggetti in questa cartella.`,
|
||||||
? "1 oggetto in questa cartella."
|
|
||||||
: `${count} oggetti in questa cartella.`,
|
|
||||||
},
|
},
|
||||||
tagContent: {
|
tagContent: {
|
||||||
tag: "Etichetta",
|
tag: "Etichetta",
|
||||||
tagIndex: "Indice etichette",
|
tagIndex: "Indice etichette",
|
||||||
itemsUnderTag: ({count}) =>
|
itemsUnderTag: ({ count }) =>
|
||||||
count === 1
|
count === 1 ? "1 oggetto con questa etichetta." : `${count} oggetti con questa etichetta.`,
|
||||||
? "1 oggetto con questa etichetta."
|
showingFirst: ({ count }) => `Prime ${count} etichette.`,
|
||||||
: `${count} oggetti con questa etichetta.`,
|
totalTags: ({ count }) => `Trovate ${count} etichette totali.`,
|
||||||
showingFirst: ({count}) => `Prime ${count} etichette.`,
|
|
||||||
totalTags: ({count}) => `Trovate ${count} etichette totali.`,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const satisfies Translation
|
} as const satisfies Translation
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {Translation} from "./definition"
|
import { Translation } from "./definition"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
propertyDefaults: {
|
propertyDefaults: {
|
||||||
@ -40,10 +40,10 @@ export default {
|
|||||||
},
|
},
|
||||||
recentNotes: {
|
recentNotes: {
|
||||||
title: "最近の記事",
|
title: "最近の記事",
|
||||||
seeRemainingMore: ({remaining}) => `さらに${remaining}件 →`,
|
seeRemainingMore: ({ remaining }) => `さらに${remaining}件 →`,
|
||||||
},
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({targetSlug}) => `${targetSlug}のまとめ`,
|
transcludeOf: ({ targetSlug }) => `${targetSlug}のまとめ`,
|
||||||
linkToOriginal: "元記事へのリンク",
|
linkToOriginal: "元記事へのリンク",
|
||||||
},
|
},
|
||||||
search: {
|
search: {
|
||||||
@ -54,13 +54,13 @@ export default {
|
|||||||
title: "目次",
|
title: "目次",
|
||||||
},
|
},
|
||||||
contentMeta: {
|
contentMeta: {
|
||||||
readingTime: ({minutes}) => `${minutes} min read`,
|
readingTime: ({ minutes }) => `${minutes} min read`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
pages: {
|
pages: {
|
||||||
rss: {
|
rss: {
|
||||||
recentNotes: "最近の記事",
|
recentNotes: "最近の記事",
|
||||||
lastFewNotes: ({count}) => `最新の${count}件`,
|
lastFewNotes: ({ count }) => `最新の${count}件`,
|
||||||
},
|
},
|
||||||
error: {
|
error: {
|
||||||
title: "Not Found",
|
title: "Not Found",
|
||||||
@ -69,14 +69,14 @@ export default {
|
|||||||
},
|
},
|
||||||
folderContent: {
|
folderContent: {
|
||||||
folder: "フォルダ",
|
folder: "フォルダ",
|
||||||
itemsUnderFolder: ({count}) => `${count}件のページ`,
|
itemsUnderFolder: ({ count }) => `${count}件のページ`,
|
||||||
},
|
},
|
||||||
tagContent: {
|
tagContent: {
|
||||||
tag: "タグ",
|
tag: "タグ",
|
||||||
tagIndex: "タグ一覧",
|
tagIndex: "タグ一覧",
|
||||||
itemsUnderTag: ({count}) => `${count}件のページ`,
|
itemsUnderTag: ({ count }) => `${count}件のページ`,
|
||||||
showingFirst: ({count}) => `のうち最初の${count}件を表示しています`,
|
showingFirst: ({ count }) => `のうち最初の${count}件を表示しています`,
|
||||||
totalTags: ({count}) => `全${count}個のタグを表示中`,
|
totalTags: ({ count }) => `全${count}個のタグを表示中`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const satisfies Translation
|
} as const satisfies Translation
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {Translation} from "./definition"
|
import { Translation } from "./definition"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
propertyDefaults: {
|
propertyDefaults: {
|
||||||
@ -40,10 +40,10 @@ export default {
|
|||||||
},
|
},
|
||||||
recentNotes: {
|
recentNotes: {
|
||||||
title: "최근 게시글",
|
title: "최근 게시글",
|
||||||
seeRemainingMore: ({remaining}) => `${remaining}건 더보기 →`,
|
seeRemainingMore: ({ remaining }) => `${remaining}건 더보기 →`,
|
||||||
},
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({targetSlug}) => `${targetSlug}의 포함`,
|
transcludeOf: ({ targetSlug }) => `${targetSlug}의 포함`,
|
||||||
linkToOriginal: "원본 링크",
|
linkToOriginal: "원본 링크",
|
||||||
},
|
},
|
||||||
search: {
|
search: {
|
||||||
@ -54,13 +54,13 @@ export default {
|
|||||||
title: "목차",
|
title: "목차",
|
||||||
},
|
},
|
||||||
contentMeta: {
|
contentMeta: {
|
||||||
readingTime: ({minutes}) => `${minutes} min read`,
|
readingTime: ({ minutes }) => `${minutes} min read`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
pages: {
|
pages: {
|
||||||
rss: {
|
rss: {
|
||||||
recentNotes: "최근 게시글",
|
recentNotes: "최근 게시글",
|
||||||
lastFewNotes: ({count}) => `최근 ${count} 건`,
|
lastFewNotes: ({ count }) => `최근 ${count} 건`,
|
||||||
},
|
},
|
||||||
error: {
|
error: {
|
||||||
title: "Not Found",
|
title: "Not Found",
|
||||||
@ -69,14 +69,14 @@ export default {
|
|||||||
},
|
},
|
||||||
folderContent: {
|
folderContent: {
|
||||||
folder: "폴더",
|
folder: "폴더",
|
||||||
itemsUnderFolder: ({count}) => `${count}건의 항목`,
|
itemsUnderFolder: ({ count }) => `${count}건의 항목`,
|
||||||
},
|
},
|
||||||
tagContent: {
|
tagContent: {
|
||||||
tag: "태그",
|
tag: "태그",
|
||||||
tagIndex: "태그 목록",
|
tagIndex: "태그 목록",
|
||||||
itemsUnderTag: ({count}) => `${count}건의 항목`,
|
itemsUnderTag: ({ count }) => `${count}건의 항목`,
|
||||||
showingFirst: ({count}) => `처음 ${count}개의 태그`,
|
showingFirst: ({ count }) => `처음 ${count}개의 태그`,
|
||||||
totalTags: ({count}) => `총 ${count}개의 태그를 찾았습니다.`,
|
totalTags: ({ count }) => `총 ${count}개의 태그를 찾았습니다.`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const satisfies Translation
|
} as const satisfies Translation
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {Translation} from "./definition"
|
import { Translation } from "./definition"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
propertyDefaults: {
|
propertyDefaults: {
|
||||||
@ -40,10 +40,10 @@ export default {
|
|||||||
},
|
},
|
||||||
recentNotes: {
|
recentNotes: {
|
||||||
title: "Recente notities",
|
title: "Recente notities",
|
||||||
seeRemainingMore: ({remaining}) => `Zie ${remaining} meer →`,
|
seeRemainingMore: ({ remaining }) => `Zie ${remaining} meer →`,
|
||||||
},
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({targetSlug}) => `Invoeging van ${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `Invoeging van ${targetSlug}`,
|
||||||
linkToOriginal: "Link naar origineel",
|
linkToOriginal: "Link naar origineel",
|
||||||
},
|
},
|
||||||
search: {
|
search: {
|
||||||
@ -54,14 +54,14 @@ export default {
|
|||||||
title: "Inhoudsopgave",
|
title: "Inhoudsopgave",
|
||||||
},
|
},
|
||||||
contentMeta: {
|
contentMeta: {
|
||||||
readingTime: ({minutes}) =>
|
readingTime: ({ minutes }) =>
|
||||||
minutes === 1 ? "1 minuut leestijd" : `${minutes} minuten leestijd`,
|
minutes === 1 ? "1 minuut leestijd" : `${minutes} minuten leestijd`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
pages: {
|
pages: {
|
||||||
rss: {
|
rss: {
|
||||||
recentNotes: "Recente notities",
|
recentNotes: "Recente notities",
|
||||||
lastFewNotes: ({count}) => `Laatste ${count} notities`,
|
lastFewNotes: ({ count }) => `Laatste ${count} notities`,
|
||||||
},
|
},
|
||||||
error: {
|
error: {
|
||||||
title: "Niet gevonden",
|
title: "Niet gevonden",
|
||||||
@ -70,17 +70,17 @@ export default {
|
|||||||
},
|
},
|
||||||
folderContent: {
|
folderContent: {
|
||||||
folder: "Map",
|
folder: "Map",
|
||||||
itemsUnderFolder: ({count}) =>
|
itemsUnderFolder: ({ count }) =>
|
||||||
count === 1 ? "1 item in deze map." : `${count} items in deze map.`,
|
count === 1 ? "1 item in deze map." : `${count} items in deze map.`,
|
||||||
},
|
},
|
||||||
tagContent: {
|
tagContent: {
|
||||||
tag: "Label",
|
tag: "Label",
|
||||||
tagIndex: "Label-index",
|
tagIndex: "Label-index",
|
||||||
itemsUnderTag: ({count}) =>
|
itemsUnderTag: ({ count }) =>
|
||||||
count === 1 ? "1 item met dit label." : `${count} items met dit label.`,
|
count === 1 ? "1 item met dit label." : `${count} items met dit label.`,
|
||||||
showingFirst: ({count}) =>
|
showingFirst: ({ count }) =>
|
||||||
count === 1 ? "Eerste label tonen." : `Eerste ${count} labels tonen.`,
|
count === 1 ? "Eerste label tonen." : `Eerste ${count} labels tonen.`,
|
||||||
totalTags: ({count}) => `${count} labels gevonden.`,
|
totalTags: ({ count }) => `${count} labels gevonden.`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const satisfies Translation
|
} as const satisfies Translation
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {Translation} from "./definition"
|
import { Translation } from "./definition"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
propertyDefaults: {
|
propertyDefaults: {
|
||||||
@ -40,10 +40,10 @@ export default {
|
|||||||
},
|
},
|
||||||
recentNotes: {
|
recentNotes: {
|
||||||
title: "Najnowsze notatki",
|
title: "Najnowsze notatki",
|
||||||
seeRemainingMore: ({remaining}) => `Zobacz ${remaining} nastepnych →`,
|
seeRemainingMore: ({ remaining }) => `Zobacz ${remaining} nastepnych →`,
|
||||||
},
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({targetSlug}) => `Osadzone ${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `Osadzone ${targetSlug}`,
|
||||||
linkToOriginal: "Łącze do oryginału",
|
linkToOriginal: "Łącze do oryginału",
|
||||||
},
|
},
|
||||||
search: {
|
search: {
|
||||||
@ -54,13 +54,13 @@ export default {
|
|||||||
title: "Spis treści",
|
title: "Spis treści",
|
||||||
},
|
},
|
||||||
contentMeta: {
|
contentMeta: {
|
||||||
readingTime: ({minutes}) => `${minutes} min. czytania `,
|
readingTime: ({ minutes }) => `${minutes} min. czytania `,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
pages: {
|
pages: {
|
||||||
rss: {
|
rss: {
|
||||||
recentNotes: "Najnowsze notatki",
|
recentNotes: "Najnowsze notatki",
|
||||||
lastFewNotes: ({count}) => `Ostatnie ${count} notatek`,
|
lastFewNotes: ({ count }) => `Ostatnie ${count} notatek`,
|
||||||
},
|
},
|
||||||
error: {
|
error: {
|
||||||
title: "Nie znaleziono",
|
title: "Nie znaleziono",
|
||||||
@ -69,20 +69,16 @@ export default {
|
|||||||
},
|
},
|
||||||
folderContent: {
|
folderContent: {
|
||||||
folder: "Folder",
|
folder: "Folder",
|
||||||
itemsUnderFolder: ({count}) =>
|
itemsUnderFolder: ({ count }) =>
|
||||||
count === 1
|
count === 1 ? "W tym folderze jest 1 element." : `Elementów w folderze: ${count}.`,
|
||||||
? "W tym folderze jest 1 element."
|
|
||||||
: `Elementów w folderze: ${count}.`,
|
|
||||||
},
|
},
|
||||||
tagContent: {
|
tagContent: {
|
||||||
tag: "Znacznik",
|
tag: "Znacznik",
|
||||||
tagIndex: "Spis znaczników",
|
tagIndex: "Spis znaczników",
|
||||||
itemsUnderTag: ({count}) =>
|
itemsUnderTag: ({ count }) =>
|
||||||
count === 1
|
count === 1 ? "Oznaczony 1 element." : `Elementów z tym znacznikiem: ${count}.`,
|
||||||
? "Oznaczony 1 element."
|
showingFirst: ({ count }) => `Pokazuje ${count} pierwszych znaczników.`,
|
||||||
: `Elementów z tym znacznikiem: ${count}.`,
|
totalTags: ({ count }) => `Znalezionych wszystkich znaczników: ${count}.`,
|
||||||
showingFirst: ({count}) => `Pokazuje ${count} pierwszych znaczników.`,
|
|
||||||
totalTags: ({count}) => `Znalezionych wszystkich znaczników: ${count}.`,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const satisfies Translation
|
} as const satisfies Translation
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {Translation} from "./definition"
|
import { Translation } from "./definition"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
propertyDefaults: {
|
propertyDefaults: {
|
||||||
@ -40,10 +40,10 @@ export default {
|
|||||||
},
|
},
|
||||||
recentNotes: {
|
recentNotes: {
|
||||||
title: "Notas recentes",
|
title: "Notas recentes",
|
||||||
seeRemainingMore: ({remaining}) => `Veja mais ${remaining} →`,
|
seeRemainingMore: ({ remaining }) => `Veja mais ${remaining} →`,
|
||||||
},
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({targetSlug}) => `Transcrever de ${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `Transcrever de ${targetSlug}`,
|
||||||
linkToOriginal: "Link ao original",
|
linkToOriginal: "Link ao original",
|
||||||
},
|
},
|
||||||
search: {
|
search: {
|
||||||
@ -54,13 +54,13 @@ export default {
|
|||||||
title: "Sumário",
|
title: "Sumário",
|
||||||
},
|
},
|
||||||
contentMeta: {
|
contentMeta: {
|
||||||
readingTime: ({minutes}) => `Leitura de ${minutes} min`,
|
readingTime: ({ minutes }) => `Leitura de ${minutes} min`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
pages: {
|
pages: {
|
||||||
rss: {
|
rss: {
|
||||||
recentNotes: "Notas recentes",
|
recentNotes: "Notas recentes",
|
||||||
lastFewNotes: ({count}) => `Últimas ${count} notas`,
|
lastFewNotes: ({ count }) => `Últimas ${count} notas`,
|
||||||
},
|
},
|
||||||
error: {
|
error: {
|
||||||
title: "Não encontrado",
|
title: "Não encontrado",
|
||||||
@ -69,18 +69,16 @@ export default {
|
|||||||
},
|
},
|
||||||
folderContent: {
|
folderContent: {
|
||||||
folder: "Arquivo",
|
folder: "Arquivo",
|
||||||
itemsUnderFolder: ({count}) =>
|
itemsUnderFolder: ({ count }) =>
|
||||||
count === 1
|
count === 1 ? "1 item neste arquivo." : `${count} items neste arquivo.`,
|
||||||
? "1 item mneste arquivo."
|
|
||||||
: `${count} items neste arquivo.`,
|
|
||||||
},
|
},
|
||||||
tagContent: {
|
tagContent: {
|
||||||
tag: "Tag",
|
tag: "Tag",
|
||||||
tagIndex: "Sumário de Tags",
|
tagIndex: "Sumário de Tags",
|
||||||
itemsUnderTag: ({count}) =>
|
itemsUnderTag: ({ count }) =>
|
||||||
count === 1 ? "1 item com esta tag." : `${count} items com esta tag.`,
|
count === 1 ? "1 item com esta tag." : `${count} items com esta tag.`,
|
||||||
showingFirst: ({count}) => `Mostrando as ${count} primeiras tags.`,
|
showingFirst: ({ count }) => `Mostrando as ${count} primeiras tags.`,
|
||||||
totalTags: ({count}) => `Encontradas ${count} tags.`,
|
totalTags: ({ count }) => `Encontradas ${count} tags.`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const satisfies Translation
|
} as const satisfies Translation
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {Translation} from "./definition"
|
import { Translation } from "./definition"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
propertyDefaults: {
|
propertyDefaults: {
|
||||||
@ -40,10 +40,10 @@ export default {
|
|||||||
},
|
},
|
||||||
recentNotes: {
|
recentNotes: {
|
||||||
title: "Notițe recente",
|
title: "Notițe recente",
|
||||||
seeRemainingMore: ({remaining}) => `Vezi încă ${remaining} →`,
|
seeRemainingMore: ({ remaining }) => `Vezi încă ${remaining} →`,
|
||||||
},
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({targetSlug}) => `Extras din ${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `Extras din ${targetSlug}`,
|
||||||
linkToOriginal: "Legătură către original",
|
linkToOriginal: "Legătură către original",
|
||||||
},
|
},
|
||||||
search: {
|
search: {
|
||||||
@ -54,14 +54,14 @@ export default {
|
|||||||
title: "Cuprins",
|
title: "Cuprins",
|
||||||
},
|
},
|
||||||
contentMeta: {
|
contentMeta: {
|
||||||
readingTime: ({minutes}) =>
|
readingTime: ({ minutes }) =>
|
||||||
minutes == 1 ? `lectură de 1 minut` : `lectură de ${minutes} minute`,
|
minutes == 1 ? `lectură de 1 minut` : `lectură de ${minutes} minute`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
pages: {
|
pages: {
|
||||||
rss: {
|
rss: {
|
||||||
recentNotes: "Notițe recente",
|
recentNotes: "Notițe recente",
|
||||||
lastFewNotes: ({count}) => `Ultimele ${count} notițe`,
|
lastFewNotes: ({ count }) => `Ultimele ${count} notițe`,
|
||||||
},
|
},
|
||||||
error: {
|
error: {
|
||||||
title: "Pagina nu a fost găsită",
|
title: "Pagina nu a fost găsită",
|
||||||
@ -70,20 +70,16 @@ export default {
|
|||||||
},
|
},
|
||||||
folderContent: {
|
folderContent: {
|
||||||
folder: "Dosar",
|
folder: "Dosar",
|
||||||
itemsUnderFolder: ({count}) =>
|
itemsUnderFolder: ({ count }) =>
|
||||||
count === 1
|
count === 1 ? "1 articol în acest dosar." : `${count} elemente în acest dosar.`,
|
||||||
? "1 articol în acest dosar."
|
|
||||||
: `${count} elemente în acest dosar.`,
|
|
||||||
},
|
},
|
||||||
tagContent: {
|
tagContent: {
|
||||||
tag: "Etichetă",
|
tag: "Etichetă",
|
||||||
tagIndex: "Indexul etichetelor",
|
tagIndex: "Indexul etichetelor",
|
||||||
itemsUnderTag: ({count}) =>
|
itemsUnderTag: ({ count }) =>
|
||||||
count === 1
|
count === 1 ? "1 articol cu această etichetă." : `${count} articole cu această etichetă.`,
|
||||||
? "1 articol cu această etichetă."
|
showingFirst: ({ count }) => `Se afișează primele ${count} etichete.`,
|
||||||
: `${count} articole cu această etichetă.`,
|
totalTags: ({ count }) => `Au fost găsite ${count} etichete în total.`,
|
||||||
showingFirst: ({count}) => `Se afișează primele ${count} etichete.`,
|
|
||||||
totalTags: ({count}) => `Au fost găsite ${count} etichete în total.`,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const satisfies Translation
|
} as const satisfies Translation
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {Translation} from "./definition"
|
import { Translation } from "./definition"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
propertyDefaults: {
|
propertyDefaults: {
|
||||||
@ -40,11 +40,11 @@ export default {
|
|||||||
},
|
},
|
||||||
recentNotes: {
|
recentNotes: {
|
||||||
title: "Недавние заметки",
|
title: "Недавние заметки",
|
||||||
seeRemainingMore: ({remaining}) =>
|
seeRemainingMore: ({ remaining }) =>
|
||||||
`Посмотреть оставш${getForm(remaining, "уюся", "иеся", "иеся")} ${remaining} →`,
|
`Посмотреть оставш${getForm(remaining, "уюся", "иеся", "иеся")} ${remaining} →`,
|
||||||
},
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({targetSlug}) => `Переход из ${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `Переход из ${targetSlug}`,
|
||||||
linkToOriginal: "Ссылка на оригинал",
|
linkToOriginal: "Ссылка на оригинал",
|
||||||
},
|
},
|
||||||
search: {
|
search: {
|
||||||
@ -55,13 +55,13 @@ export default {
|
|||||||
title: "Оглавление",
|
title: "Оглавление",
|
||||||
},
|
},
|
||||||
contentMeta: {
|
contentMeta: {
|
||||||
readingTime: ({minutes}) => `время чтения ~${minutes} мин.`,
|
readingTime: ({ minutes }) => `время чтения ~${minutes} мин.`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
pages: {
|
pages: {
|
||||||
rss: {
|
rss: {
|
||||||
recentNotes: "Недавние заметки",
|
recentNotes: "Недавние заметки",
|
||||||
lastFewNotes: ({count}) =>
|
lastFewNotes: ({ count }) =>
|
||||||
`Последн${getForm(count, "яя", "ие", "ие")} ${count} замет${getForm(count, "ка", "ки", "ок")}`,
|
`Последн${getForm(count, "яя", "ие", "ие")} ${count} замет${getForm(count, "ка", "ки", "ок")}`,
|
||||||
},
|
},
|
||||||
error: {
|
error: {
|
||||||
@ -71,28 +71,21 @@ export default {
|
|||||||
},
|
},
|
||||||
folderContent: {
|
folderContent: {
|
||||||
folder: "Папка",
|
folder: "Папка",
|
||||||
itemsUnderFolder: ({count}) =>
|
itemsUnderFolder: ({ count }) =>
|
||||||
`в этой папке ${count} элемент${getForm(count, "", "а", "ов")}`,
|
`в этой папке ${count} элемент${getForm(count, "", "а", "ов")}`,
|
||||||
},
|
},
|
||||||
tagContent: {
|
tagContent: {
|
||||||
tag: "Тег",
|
tag: "Тег",
|
||||||
tagIndex: "Индекс тегов",
|
tagIndex: "Индекс тегов",
|
||||||
itemsUnderTag: ({count}) =>
|
itemsUnderTag: ({ count }) => `с этим тегом ${count} элемент${getForm(count, "", "а", "ов")}`,
|
||||||
`с этим тегом ${count} элемент${getForm(count, "", "а", "ов")}`,
|
showingFirst: ({ count }) =>
|
||||||
showingFirst: ({count}) =>
|
|
||||||
`Показыва${getForm(count, "ется", "ются", "ются")} ${count} тег${getForm(count, "", "а", "ов")}`,
|
`Показыва${getForm(count, "ется", "ются", "ются")} ${count} тег${getForm(count, "", "а", "ов")}`,
|
||||||
totalTags: ({count}) =>
|
totalTags: ({ count }) => `Всего ${count} тег${getForm(count, "", "а", "ов")}`,
|
||||||
`Всего ${count} тег${getForm(count, "", "а", "ов")}`,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const satisfies Translation
|
} as const satisfies Translation
|
||||||
|
|
||||||
function getForm(
|
function getForm(number: number, form1: string, form2: string, form5: string): string {
|
||||||
number: number,
|
|
||||||
form1: string,
|
|
||||||
form2: string,
|
|
||||||
form5: string,
|
|
||||||
): string {
|
|
||||||
const remainder100 = number % 100
|
const remainder100 = number % 100
|
||||||
const remainder10 = remainder100 % 10
|
const remainder10 = remainder100 % 10
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {Translation} from "./definition"
|
import { Translation } from "./definition"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
propertyDefaults: {
|
propertyDefaults: {
|
||||||
@ -40,10 +40,10 @@ export default {
|
|||||||
},
|
},
|
||||||
recentNotes: {
|
recentNotes: {
|
||||||
title: "Останні нотатки",
|
title: "Останні нотатки",
|
||||||
seeRemainingMore: ({remaining}) => `Переглянути ще ${remaining} →`,
|
seeRemainingMore: ({ remaining }) => `Переглянути ще ${remaining} →`,
|
||||||
},
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({targetSlug}) => `Видобуто з ${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `Видобуто з ${targetSlug}`,
|
||||||
linkToOriginal: "Посилання на оригінал",
|
linkToOriginal: "Посилання на оригінал",
|
||||||
},
|
},
|
||||||
search: {
|
search: {
|
||||||
@ -54,13 +54,13 @@ export default {
|
|||||||
title: "Зміст",
|
title: "Зміст",
|
||||||
},
|
},
|
||||||
contentMeta: {
|
contentMeta: {
|
||||||
readingTime: ({minutes}) => `${minutes} min read`,
|
readingTime: ({ minutes }) => `${minutes} хв читання`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
pages: {
|
pages: {
|
||||||
rss: {
|
rss: {
|
||||||
recentNotes: "Останні нотатки",
|
recentNotes: "Останні нотатки",
|
||||||
lastFewNotes: ({count}) => `Останні нотатки: ${count}`,
|
lastFewNotes: ({ count }) => `Останні нотатки: ${count}`,
|
||||||
},
|
},
|
||||||
error: {
|
error: {
|
||||||
title: "Не знайдено",
|
title: "Не знайдено",
|
||||||
@ -68,21 +68,17 @@ export default {
|
|||||||
home: "Повернутися на головну сторінку",
|
home: "Повернутися на головну сторінку",
|
||||||
},
|
},
|
||||||
folderContent: {
|
folderContent: {
|
||||||
folder: "Папка",
|
folder: "Тека",
|
||||||
itemsUnderFolder: ({count}) =>
|
itemsUnderFolder: ({ count }) =>
|
||||||
count === 1
|
count === 1 ? "У цій теці 1 елемент." : `Елементів у цій теці: ${count}.`,
|
||||||
? "У цій папці 1 елемент."
|
|
||||||
: `Елементів у цій папці: ${count}.`,
|
|
||||||
},
|
},
|
||||||
tagContent: {
|
tagContent: {
|
||||||
tag: "Тег",
|
tag: "Мітка",
|
||||||
tagIndex: "Індекс тегу",
|
tagIndex: "Індекс мітки",
|
||||||
itemsUnderTag: ({count}) =>
|
itemsUnderTag: ({ count }) =>
|
||||||
count === 1
|
count === 1 ? "1 елемент з цією міткою." : `Елементів з цією міткою: ${count}.`,
|
||||||
? "1 елемент з цим тегом."
|
showingFirst: ({ count }) => `Показ перших ${count} міток.`,
|
||||||
: `Елементів з цим тегом: ${count}.`,
|
totalTags: ({ count }) => `Всього знайдено міток: ${count}.`,
|
||||||
showingFirst: ({count}) => `Показ перших ${count} тегів.`,
|
|
||||||
totalTags: ({count}) => `Всього знайдено тегів: ${count}.`,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const satisfies Translation
|
} as const satisfies Translation
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {Translation} from "./definition"
|
import { Translation } from "./definition"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
propertyDefaults: {
|
propertyDefaults: {
|
||||||
@ -40,10 +40,10 @@ export default {
|
|||||||
},
|
},
|
||||||
recentNotes: {
|
recentNotes: {
|
||||||
title: "Bài viết gần đây",
|
title: "Bài viết gần đây",
|
||||||
seeRemainingMore: ({remaining}) => `Xem ${remaining} thêm →`,
|
seeRemainingMore: ({ remaining }) => `Xem ${remaining} thêm →`,
|
||||||
},
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({targetSlug}) => `Bao gồm ${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `Bao gồm ${targetSlug}`,
|
||||||
linkToOriginal: "Liên Kết Gốc",
|
linkToOriginal: "Liên Kết Gốc",
|
||||||
},
|
},
|
||||||
search: {
|
search: {
|
||||||
@ -54,13 +54,13 @@ export default {
|
|||||||
title: "Bảng Nội Dung",
|
title: "Bảng Nội Dung",
|
||||||
},
|
},
|
||||||
contentMeta: {
|
contentMeta: {
|
||||||
readingTime: ({minutes}) => `đọc ${minutes} phút`,
|
readingTime: ({ minutes }) => `đọc ${minutes} phút`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
pages: {
|
pages: {
|
||||||
rss: {
|
rss: {
|
||||||
recentNotes: "Những bài gần đây",
|
recentNotes: "Những bài gần đây",
|
||||||
lastFewNotes: ({count}) => `${count} Bài gần đây`,
|
lastFewNotes: ({ count }) => `${count} Bài gần đây`,
|
||||||
},
|
},
|
||||||
error: {
|
error: {
|
||||||
title: "Không Tìm Thấy",
|
title: "Không Tìm Thấy",
|
||||||
@ -69,18 +69,16 @@ export default {
|
|||||||
},
|
},
|
||||||
folderContent: {
|
folderContent: {
|
||||||
folder: "Thư Mục",
|
folder: "Thư Mục",
|
||||||
itemsUnderFolder: ({count}) =>
|
itemsUnderFolder: ({ count }) =>
|
||||||
count === 1
|
count === 1 ? "1 mục trong thư mục này." : `${count} mục trong thư mục này.`,
|
||||||
? "1 mục trong thư mục này."
|
|
||||||
: `${count} mục trong thư mục này.`,
|
|
||||||
},
|
},
|
||||||
tagContent: {
|
tagContent: {
|
||||||
tag: "Thẻ",
|
tag: "Thẻ",
|
||||||
tagIndex: "Thẻ Mục Lục",
|
tagIndex: "Thẻ Mục Lục",
|
||||||
itemsUnderTag: ({count}) =>
|
itemsUnderTag: ({ count }) =>
|
||||||
count === 1 ? "1 mục gắn thẻ này." : `${count} mục gắn thẻ này.`,
|
count === 1 ? "1 mục gắn thẻ này." : `${count} mục gắn thẻ này.`,
|
||||||
showingFirst: ({count}) => `Hiển thị trước ${count} thẻ.`,
|
showingFirst: ({ count }) => `Hiển thị trước ${count} thẻ.`,
|
||||||
totalTags: ({count}) => `Tìm thấy ${count} thẻ tổng cộng.`,
|
totalTags: ({ count }) => `Tìm thấy ${count} thẻ tổng cộng.`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const satisfies Translation
|
} as const satisfies Translation
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {Translation} from "./definition"
|
import { Translation } from "./definition"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
propertyDefaults: {
|
propertyDefaults: {
|
||||||
@ -40,10 +40,10 @@ export default {
|
|||||||
},
|
},
|
||||||
recentNotes: {
|
recentNotes: {
|
||||||
title: "最近的笔记",
|
title: "最近的笔记",
|
||||||
seeRemainingMore: ({remaining}) => `查看更多${remaining}篇笔记 →`,
|
seeRemainingMore: ({ remaining }) => `查看更多${remaining}篇笔记 →`,
|
||||||
},
|
},
|
||||||
transcludes: {
|
transcludes: {
|
||||||
transcludeOf: ({targetSlug}) => `包含${targetSlug}`,
|
transcludeOf: ({ targetSlug }) => `包含${targetSlug}`,
|
||||||
linkToOriginal: "指向原始笔记的链接",
|
linkToOriginal: "指向原始笔记的链接",
|
||||||
},
|
},
|
||||||
search: {
|
search: {
|
||||||
@ -54,13 +54,13 @@ export default {
|
|||||||
title: "目录",
|
title: "目录",
|
||||||
},
|
},
|
||||||
contentMeta: {
|
contentMeta: {
|
||||||
readingTime: ({minutes}) => `${minutes}分钟阅读`,
|
readingTime: ({ minutes }) => `${minutes}分钟阅读`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
pages: {
|
pages: {
|
||||||
rss: {
|
rss: {
|
||||||
recentNotes: "最近的笔记",
|
recentNotes: "最近的笔记",
|
||||||
lastFewNotes: ({count}) => `最近的${count}条笔记`,
|
lastFewNotes: ({ count }) => `最近的${count}条笔记`,
|
||||||
},
|
},
|
||||||
error: {
|
error: {
|
||||||
title: "无法找到",
|
title: "无法找到",
|
||||||
@ -69,14 +69,14 @@ export default {
|
|||||||
},
|
},
|
||||||
folderContent: {
|
folderContent: {
|
||||||
folder: "文件夹",
|
folder: "文件夹",
|
||||||
itemsUnderFolder: ({count}) => `此文件夹下有${count}条笔记。`,
|
itemsUnderFolder: ({ count }) => `此文件夹下有${count}条笔记。`,
|
||||||
},
|
},
|
||||||
tagContent: {
|
tagContent: {
|
||||||
tag: "标签",
|
tag: "标签",
|
||||||
tagIndex: "标签索引",
|
tagIndex: "标签索引",
|
||||||
itemsUnderTag: ({count}) => `此标签下有${count}条笔记。`,
|
itemsUnderTag: ({ count }) => `此标签下有${count}条笔记。`,
|
||||||
showingFirst: ({count}) => `显示前${count}个标签。`,
|
showingFirst: ({ count }) => `显示前${count}个标签。`,
|
||||||
totalTags: ({count}) => `总共有${count}个标签。`,
|
totalTags: ({ count }) => `总共有${count}个标签。`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const satisfies Translation
|
} as const satisfies Translation
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
import {QuartzEmitterPlugin} from "../types"
|
import { QuartzEmitterPlugin } from "../types"
|
||||||
import {QuartzComponentProps} from "../../components/types"
|
import { QuartzComponentProps } from "../../components/types"
|
||||||
import BodyConstructor from "../../components/Body"
|
import BodyConstructor from "../../components/Body"
|
||||||
import {pageResources, renderPage} from "../../components/renderPage"
|
import { pageResources, renderPage } from "../../components/renderPage"
|
||||||
import {FullPageLayout} from "../../cfg"
|
import { FullPageLayout } from "../../cfg"
|
||||||
import {FilePath, FullSlug} from "../../util/path"
|
import { FilePath, FullSlug } from "../../util/path"
|
||||||
import {sharedPageComponents} from "../../../quartz.layout"
|
import { sharedPageComponents } from "../../../quartz.layout"
|
||||||
import {NotFound} from "../../components"
|
import { NotFound } from "../../components"
|
||||||
import {defaultProcessedContent} from "../vfile"
|
import { defaultProcessedContent } from "../vfile"
|
||||||
import {write} from "./helpers"
|
import { write } from "./helpers"
|
||||||
import {i18n} from "../../i18n"
|
import { i18n } from "../../i18n"
|
||||||
import DepGraph from "../../depgraph"
|
import DepGraph from "../../depgraph"
|
||||||
|
|
||||||
export const NotFoundPage: QuartzEmitterPlugin = () => {
|
export const NotFoundPage: QuartzEmitterPlugin = () => {
|
||||||
@ -20,7 +20,7 @@ export const NotFoundPage: QuartzEmitterPlugin = () => {
|
|||||||
right: [],
|
right: [],
|
||||||
}
|
}
|
||||||
|
|
||||||
const {head: Head, pageBody, footer: Footer} = opts
|
const { head: Head, pageBody, footer: Footer } = opts
|
||||||
const Body = BodyConstructor()
|
const Body = BodyConstructor()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -43,7 +43,7 @@ export const NotFoundPage: QuartzEmitterPlugin = () => {
|
|||||||
slug,
|
slug,
|
||||||
text: notFound,
|
text: notFound,
|
||||||
description: notFound,
|
description: notFound,
|
||||||
frontmatter: {title: notFound, tags: []},
|
frontmatter: { title: notFound, tags: [] },
|
||||||
})
|
})
|
||||||
const componentData: QuartzComponentProps = {
|
const componentData: QuartzComponentProps = {
|
||||||
ctx,
|
ctx,
|
||||||
@ -58,13 +58,7 @@ export const NotFoundPage: QuartzEmitterPlugin = () => {
|
|||||||
return [
|
return [
|
||||||
await write({
|
await write({
|
||||||
ctx,
|
ctx,
|
||||||
content: renderPage(
|
content: renderPage(cfg, slug, componentData, opts, externalResources),
|
||||||
cfg,
|
|
||||||
slug,
|
|
||||||
componentData,
|
|
||||||
opts,
|
|
||||||
externalResources,
|
|
||||||
),
|
|
||||||
slug,
|
slug,
|
||||||
ext: ".html",
|
ext: ".html",
|
||||||
}),
|
}),
|
||||||
|
|||||||
@ -1,13 +1,7 @@
|
|||||||
import {
|
import { FilePath, FullSlug, joinSegments, resolveRelative, simplifySlug } from "../../util/path"
|
||||||
FilePath,
|
import { QuartzEmitterPlugin } from "../types"
|
||||||
FullSlug,
|
|
||||||
joinSegments,
|
|
||||||
resolveRelative,
|
|
||||||
simplifySlug,
|
|
||||||
} from "../../util/path"
|
|
||||||
import {QuartzEmitterPlugin} from "../types"
|
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import {write} from "./helpers"
|
import { write } from "./helpers"
|
||||||
import DepGraph from "../../depgraph"
|
import DepGraph from "../../depgraph"
|
||||||
|
|
||||||
export const AliasRedirects: QuartzEmitterPlugin = () => ({
|
export const AliasRedirects: QuartzEmitterPlugin = () => ({
|
||||||
@ -18,16 +12,11 @@ export const AliasRedirects: QuartzEmitterPlugin = () => ({
|
|||||||
async getDependencyGraph(ctx, content, _resources) {
|
async getDependencyGraph(ctx, content, _resources) {
|
||||||
const graph = new DepGraph<FilePath>()
|
const graph = new DepGraph<FilePath>()
|
||||||
|
|
||||||
const {argv} = ctx
|
const { argv } = ctx
|
||||||
for (const [_tree, file] of content) {
|
for (const [_tree, file] of content) {
|
||||||
const dir = path.posix.relative(
|
const dir = path.posix.relative(argv.directory, path.dirname(file.data.filePath!))
|
||||||
argv.directory,
|
|
||||||
path.dirname(file.data.filePath!),
|
|
||||||
)
|
|
||||||
const aliases = file.data.frontmatter?.aliases ?? []
|
const aliases = file.data.frontmatter?.aliases ?? []
|
||||||
const slugs = aliases.map(
|
const slugs = aliases.map((alias) => path.posix.join(dir, alias) as FullSlug)
|
||||||
(alias) => path.posix.join(dir, alias) as FullSlug,
|
|
||||||
)
|
|
||||||
const permalink = file.data.frontmatter?.permalink
|
const permalink = file.data.frontmatter?.permalink
|
||||||
if (typeof permalink === "string") {
|
if (typeof permalink === "string") {
|
||||||
slugs.push(permalink as FullSlug)
|
slugs.push(permalink as FullSlug)
|
||||||
@ -39,29 +28,21 @@ export const AliasRedirects: QuartzEmitterPlugin = () => ({
|
|||||||
slug = joinSegments(slug, "index") as FullSlug
|
slug = joinSegments(slug, "index") as FullSlug
|
||||||
}
|
}
|
||||||
|
|
||||||
graph.addEdge(
|
graph.addEdge(file.data.filePath!, joinSegments(argv.output, slug + ".html") as FilePath)
|
||||||
file.data.filePath!,
|
|
||||||
joinSegments(argv.output, slug + ".html") as FilePath,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return graph
|
return graph
|
||||||
},
|
},
|
||||||
async emit(ctx, content, _resources): Promise<FilePath[]> {
|
async emit(ctx, content, _resources): Promise<FilePath[]> {
|
||||||
const {argv} = ctx
|
const { argv } = ctx
|
||||||
const fps: FilePath[] = []
|
const fps: FilePath[] = []
|
||||||
|
|
||||||
for (const [_tree, file] of content) {
|
for (const [_tree, file] of content) {
|
||||||
const ogSlug = simplifySlug(file.data.slug!)
|
const ogSlug = simplifySlug(file.data.slug!)
|
||||||
const dir = path.posix.relative(
|
const dir = path.posix.relative(argv.directory, path.dirname(file.data.filePath!))
|
||||||
argv.directory,
|
|
||||||
path.dirname(file.data.filePath!),
|
|
||||||
)
|
|
||||||
const aliases = file.data.frontmatter?.aliases ?? []
|
const aliases = file.data.frontmatter?.aliases ?? []
|
||||||
const slugs: FullSlug[] = aliases.map(
|
const slugs: FullSlug[] = aliases.map((alias) => path.posix.join(dir, alias) as FullSlug)
|
||||||
(alias) => path.posix.join(dir, alias) as FullSlug,
|
|
||||||
)
|
|
||||||
const permalink = file.data.frontmatter?.permalink
|
const permalink = file.data.frontmatter?.permalink
|
||||||
if (typeof permalink === "string") {
|
if (typeof permalink === "string") {
|
||||||
slugs.push(permalink as FullSlug)
|
slugs.push(permalink as FullSlug)
|
||||||
@ -78,7 +59,7 @@ export const AliasRedirects: QuartzEmitterPlugin = () => ({
|
|||||||
ctx,
|
ctx,
|
||||||
content: `
|
content: `
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en-us">
|
||||||
<head>
|
<head>
|
||||||
<title>${ogSlug}</title>
|
<title>${ogSlug}</title>
|
||||||
<link rel="canonical" href="${redirUrl}">
|
<link rel="canonical" href="${redirUrl}">
|
||||||
|
|||||||
@ -1,18 +1,15 @@
|
|||||||
import {FilePath, joinSegments, slugifyFilePath} from "../../util/path"
|
import { FilePath, joinSegments, slugifyFilePath } from "../../util/path"
|
||||||
import {QuartzEmitterPlugin} from "../types"
|
import { QuartzEmitterPlugin } from "../types"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import fs from "fs"
|
import fs from "fs"
|
||||||
import {glob} from "../../util/glob"
|
import { glob } from "../../util/glob"
|
||||||
import DepGraph from "../../depgraph"
|
import DepGraph from "../../depgraph"
|
||||||
import {Argv} from "../../util/ctx"
|
import { Argv } from "../../util/ctx"
|
||||||
import {QuartzConfig} from "../../cfg"
|
import { QuartzConfig } from "../../cfg"
|
||||||
|
|
||||||
const filesToCopy = async (argv: Argv, cfg: QuartzConfig) => {
|
const filesToCopy = async (argv: Argv, cfg: QuartzConfig) => {
|
||||||
// glob all non MD files in content folder and copy it over
|
// glob all non MD files in content folder and copy it over
|
||||||
return await glob("**", argv.directory, [
|
return await glob("**", argv.directory, ["**/*.md", ...cfg.configuration.ignorePatterns])
|
||||||
"**/*.md",
|
|
||||||
...cfg.configuration.ignorePatterns,
|
|
||||||
])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Assets: QuartzEmitterPlugin = () => {
|
export const Assets: QuartzEmitterPlugin = () => {
|
||||||
@ -22,7 +19,7 @@ export const Assets: QuartzEmitterPlugin = () => {
|
|||||||
return []
|
return []
|
||||||
},
|
},
|
||||||
async getDependencyGraph(ctx, _content, _resources) {
|
async getDependencyGraph(ctx, _content, _resources) {
|
||||||
const {argv, cfg} = ctx
|
const { argv, cfg } = ctx
|
||||||
const graph = new DepGraph<FilePath>()
|
const graph = new DepGraph<FilePath>()
|
||||||
|
|
||||||
const fps = await filesToCopy(argv, cfg)
|
const fps = await filesToCopy(argv, cfg)
|
||||||
@ -39,7 +36,7 @@ export const Assets: QuartzEmitterPlugin = () => {
|
|||||||
|
|
||||||
return graph
|
return graph
|
||||||
},
|
},
|
||||||
async emit({argv, cfg}, _content, _resources): Promise<FilePath[]> {
|
async emit({ argv, cfg }, _content, _resources): Promise<FilePath[]> {
|
||||||
const assetsPath = argv.output
|
const assetsPath = argv.output
|
||||||
const fps = await filesToCopy(argv, cfg)
|
const fps = await filesToCopy(argv, cfg)
|
||||||
const res: FilePath[] = []
|
const res: FilePath[] = []
|
||||||
@ -50,7 +47,7 @@ export const Assets: QuartzEmitterPlugin = () => {
|
|||||||
|
|
||||||
const dest = joinSegments(assetsPath, name) as FilePath
|
const dest = joinSegments(assetsPath, name) as FilePath
|
||||||
const dir = path.dirname(dest) as FilePath
|
const dir = path.dirname(dest) as FilePath
|
||||||
await fs.promises.mkdir(dir, {recursive: true}) // ensure dir exists
|
await fs.promises.mkdir(dir, { recursive: true }) // ensure dir exists
|
||||||
await fs.promises.copyFile(src, dest)
|
await fs.promises.copyFile(src, dest)
|
||||||
res.push(dest)
|
res.push(dest)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import {FilePath, joinSegments} from "../../util/path"
|
import { FilePath, joinSegments } from "../../util/path"
|
||||||
import {QuartzEmitterPlugin} from "../types"
|
import { QuartzEmitterPlugin } from "../types"
|
||||||
import fs from "fs"
|
import fs from "fs"
|
||||||
import chalk from "chalk"
|
import chalk from "chalk"
|
||||||
import DepGraph from "../../depgraph"
|
import DepGraph from "../../depgraph"
|
||||||
@ -17,13 +17,9 @@ export const CNAME: QuartzEmitterPlugin = () => ({
|
|||||||
async getDependencyGraph(_ctx, _content, _resources) {
|
async getDependencyGraph(_ctx, _content, _resources) {
|
||||||
return new DepGraph<FilePath>()
|
return new DepGraph<FilePath>()
|
||||||
},
|
},
|
||||||
async emit({argv, cfg}, _content, _resources): Promise<FilePath[]> {
|
async emit({ argv, cfg }, _content, _resources): Promise<FilePath[]> {
|
||||||
if (!cfg.configuration.baseUrl) {
|
if (!cfg.configuration.baseUrl) {
|
||||||
console.warn(
|
console.warn(chalk.yellow("CNAME emitter requires `baseUrl` to be set in your configuration"))
|
||||||
chalk.yellow(
|
|
||||||
"CNAME emitter requires `baseUrl` to be set in your configuration",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
const path = joinSegments(argv.output, "CNAME")
|
const path = joinSegments(argv.output, "CNAME")
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import {FilePath, FullSlug, joinSegments} from "../../util/path"
|
import { FilePath, FullSlug, joinSegments } from "../../util/path"
|
||||||
import {QuartzEmitterPlugin} from "../types"
|
import { QuartzEmitterPlugin } from "../types"
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import spaRouterScript from "../../components/scripts/spa.inline"
|
import spaRouterScript from "../../components/scripts/spa.inline"
|
||||||
@ -7,12 +7,12 @@ import spaRouterScript from "../../components/scripts/spa.inline"
|
|||||||
import popoverScript from "../../components/scripts/popover.inline"
|
import popoverScript from "../../components/scripts/popover.inline"
|
||||||
import styles from "../../styles/custom.scss"
|
import styles from "../../styles/custom.scss"
|
||||||
import popoverStyle from "../../components/styles/popover.scss"
|
import popoverStyle from "../../components/styles/popover.scss"
|
||||||
import {BuildCtx} from "../../util/ctx"
|
import { BuildCtx } from "../../util/ctx"
|
||||||
import {QuartzComponent} from "../../components/types"
|
import { QuartzComponent } from "../../components/types"
|
||||||
import {googleFontHref, joinStyles} from "../../util/theme"
|
import { googleFontHref, joinStyles } from "../../util/theme"
|
||||||
import {Features, transform} from "lightningcss"
|
import { Features, transform } from "lightningcss"
|
||||||
import {transform as transpile} from "esbuild"
|
import { transform as transpile } from "esbuild"
|
||||||
import {write} from "./helpers"
|
import { write } from "./helpers"
|
||||||
import DepGraph from "../../depgraph"
|
import DepGraph from "../../depgraph"
|
||||||
|
|
||||||
type ComponentResources = {
|
type ComponentResources = {
|
||||||
@ -37,7 +37,7 @@ function getComponentResources(ctx: BuildCtx): ComponentResources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const component of allComponents) {
|
for (const component of allComponents) {
|
||||||
const {css, beforeDOMLoaded, afterDOMLoaded} = component
|
const { css, beforeDOMLoaded, afterDOMLoaded } = component
|
||||||
if (css) {
|
if (css) {
|
||||||
componentResources.css.add(css)
|
componentResources.css.add(css)
|
||||||
}
|
}
|
||||||
@ -58,9 +58,7 @@ function getComponentResources(ctx: BuildCtx): ComponentResources {
|
|||||||
|
|
||||||
async function joinScripts(scripts: string[]): Promise<string> {
|
async function joinScripts(scripts: string[]): Promise<string> {
|
||||||
// wrap with iife to prevent scope collision
|
// wrap with iife to prevent scope collision
|
||||||
const script = scripts
|
const script = scripts.map((script) => `(function () {${script}})();`).join("\n")
|
||||||
.map((script) => `(function () {${script}})();`)
|
|
||||||
.join("\n")
|
|
||||||
|
|
||||||
// minify with esbuild
|
// minify with esbuild
|
||||||
const res = await transpile(script, {
|
const res = await transpile(script, {
|
||||||
@ -70,10 +68,7 @@ async function joinScripts(scripts: string[]): Promise<string> {
|
|||||||
return res.code
|
return res.code
|
||||||
}
|
}
|
||||||
|
|
||||||
function addGlobalPageResources(
|
function addGlobalPageResources(ctx: BuildCtx, componentResources: ComponentResources) {
|
||||||
ctx: BuildCtx,
|
|
||||||
componentResources: ComponentResources,
|
|
||||||
) {
|
|
||||||
const cfg = ctx.cfg.configuration
|
const cfg = ctx.cfg.configuration
|
||||||
|
|
||||||
// popovers
|
// popovers
|
||||||
@ -190,15 +185,11 @@ export const ComponentResources: QuartzEmitterPlugin = () => {
|
|||||||
let googleFontsStyleSheet = ""
|
let googleFontsStyleSheet = ""
|
||||||
if (cfg.theme.fontOrigin === "local") {
|
if (cfg.theme.fontOrigin === "local") {
|
||||||
// let the user do it themselves in css
|
// let the user do it themselves in css
|
||||||
} else if (
|
} else if (cfg.theme.fontOrigin === "googleFonts" && !cfg.theme.cdnCaching) {
|
||||||
cfg.theme.fontOrigin === "googleFonts" &&
|
|
||||||
!cfg.theme.cdnCaching
|
|
||||||
) {
|
|
||||||
// when cdnCaching is true, we link to google fonts in Head.tsx
|
// when cdnCaching is true, we link to google fonts in Head.tsx
|
||||||
let match
|
let match
|
||||||
|
|
||||||
const fontSourceRegex =
|
const fontSourceRegex = /url\((https:\/\/fonts.gstatic.com\/s\/[^)]+\.(woff2|ttf))\)/g
|
||||||
/url\((https:\/\/fonts.gstatic.com\/s\/[^)]+\.(woff2|ttf))\)/g
|
|
||||||
|
|
||||||
googleFontsStyleSheet = await (
|
googleFontsStyleSheet = await (
|
||||||
await fetch(googleFontHref(ctx.cfg.configuration.theme))
|
await fetch(googleFontHref(ctx.cfg.configuration.theme))
|
||||||
|
|||||||
@ -1,18 +1,12 @@
|
|||||||
import {Root} from "hast"
|
import { Root } from "hast"
|
||||||
import {GlobalConfiguration} from "../../cfg"
|
import { GlobalConfiguration } from "../../cfg"
|
||||||
import {getDate} from "../../components/Date"
|
import { getDate } from "../../components/Date"
|
||||||
import {escapeHTML} from "../../util/escape"
|
import { escapeHTML } from "../../util/escape"
|
||||||
import {
|
import { FilePath, FullSlug, SimpleSlug, joinSegments, simplifySlug } from "../../util/path"
|
||||||
FilePath,
|
import { QuartzEmitterPlugin } from "../types"
|
||||||
FullSlug,
|
import { toHtml } from "hast-util-to-html"
|
||||||
SimpleSlug,
|
import { write } from "./helpers"
|
||||||
joinSegments,
|
import { i18n } from "../../i18n"
|
||||||
simplifySlug,
|
|
||||||
} from "../../util/path"
|
|
||||||
import {QuartzEmitterPlugin} from "../types"
|
|
||||||
import {toHtml} from "hast-util-to-html"
|
|
||||||
import {write} from "./helpers"
|
|
||||||
import {i18n} from "../../i18n"
|
|
||||||
import DepGraph from "../../depgraph"
|
import DepGraph from "../../depgraph"
|
||||||
|
|
||||||
export type ContentIndex = Map<FullSlug, ContentDetails>
|
export type ContentIndex = Map<FullSlug, ContentDetails>
|
||||||
@ -44,10 +38,7 @@ const defaultOptions: Options = {
|
|||||||
|
|
||||||
function generateSiteMap(cfg: GlobalConfiguration, idx: ContentIndex): string {
|
function generateSiteMap(cfg: GlobalConfiguration, idx: ContentIndex): string {
|
||||||
const base = cfg.baseUrl ?? ""
|
const base = cfg.baseUrl ?? ""
|
||||||
const createURLEntry = (
|
const createURLEntry = (slug: SimpleSlug, content: ContentDetails): string => `<url>
|
||||||
slug: SimpleSlug,
|
|
||||||
content: ContentDetails,
|
|
||||||
): string => `<url>
|
|
||||||
<loc>https://${joinSegments(base, encodeURI(slug))}</loc>
|
<loc>https://${joinSegments(base, encodeURI(slug))}</loc>
|
||||||
${content.date && `<lastmod>${content.date.toISOString()}</lastmod>`}
|
${content.date && `<lastmod>${content.date.toISOString()}</lastmod>`}
|
||||||
</url>`
|
</url>`
|
||||||
@ -57,17 +48,10 @@ function generateSiteMap(cfg: GlobalConfiguration, idx: ContentIndex): string {
|
|||||||
return `<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">${urls}</urlset>`
|
return `<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">${urls}</urlset>`
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateRSSFeed(
|
function generateRSSFeed(cfg: GlobalConfiguration, idx: ContentIndex, limit?: number): string {
|
||||||
cfg: GlobalConfiguration,
|
|
||||||
idx: ContentIndex,
|
|
||||||
limit?: number,
|
|
||||||
): string {
|
|
||||||
const base = cfg.baseUrl ?? ""
|
const base = cfg.baseUrl ?? ""
|
||||||
|
|
||||||
const createURLEntry = (
|
const createURLEntry = (slug: SimpleSlug, content: ContentDetails): string => `<item>
|
||||||
slug: SimpleSlug,
|
|
||||||
content: ContentDetails,
|
|
||||||
): string => `<item>
|
|
||||||
<title>${escapeHTML(content.title)}</title>
|
<title>${escapeHTML(content.title)}</title>
|
||||||
<link>https://${joinSegments(base, encodeURI(slug))}</link>
|
<link>https://${joinSegments(base, encodeURI(slug))}</link>
|
||||||
<guid>https://${joinSegments(base, encodeURI(slug))}</guid>
|
<guid>https://${joinSegments(base, encodeURI(slug))}</guid>
|
||||||
@ -96,7 +80,7 @@ function generateRSSFeed(
|
|||||||
<channel>
|
<channel>
|
||||||
<title>${escapeHTML(cfg.pageTitle)}</title>
|
<title>${escapeHTML(cfg.pageTitle)}</title>
|
||||||
<link>https://${base}</link>
|
<link>https://${base}</link>
|
||||||
<description>${!!limit ? i18n(cfg.locale).pages.rss.lastFewNotes({count: limit}) : i18n(cfg.locale).pages.rss.recentNotes} on ${escapeHTML(
|
<description>${!!limit ? i18n(cfg.locale).pages.rss.lastFewNotes({ count: limit }) : i18n(cfg.locale).pages.rss.recentNotes} on ${escapeHTML(
|
||||||
cfg.pageTitle,
|
cfg.pageTitle,
|
||||||
)}</description>
|
)}</description>
|
||||||
<generator>Quartz -- quartz.jzhao.xyz</generator>
|
<generator>Quartz -- quartz.jzhao.xyz</generator>
|
||||||
@ -106,7 +90,7 @@ function generateRSSFeed(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const ContentIndex: QuartzEmitterPlugin<Partial<Options>> = (opts) => {
|
export const ContentIndex: QuartzEmitterPlugin<Partial<Options>> = (opts) => {
|
||||||
opts = {...defaultOptions, ...opts}
|
opts = { ...defaultOptions, ...opts }
|
||||||
return {
|
return {
|
||||||
name: "ContentIndex",
|
name: "ContentIndex",
|
||||||
async getDependencyGraph(ctx, content, _resources) {
|
async getDependencyGraph(ctx, content, _resources) {
|
||||||
@ -120,16 +104,10 @@ export const ContentIndex: QuartzEmitterPlugin<Partial<Options>> = (opts) => {
|
|||||||
joinSegments(ctx.argv.output, "static/contentIndex.json") as FilePath,
|
joinSegments(ctx.argv.output, "static/contentIndex.json") as FilePath,
|
||||||
)
|
)
|
||||||
if (opts?.enableSiteMap) {
|
if (opts?.enableSiteMap) {
|
||||||
graph.addEdge(
|
graph.addEdge(sourcePath, joinSegments(ctx.argv.output, "sitemap.xml") as FilePath)
|
||||||
sourcePath,
|
|
||||||
joinSegments(ctx.argv.output, "sitemap.xml") as FilePath,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
if (opts?.enableRSS) {
|
if (opts?.enableRSS) {
|
||||||
graph.addEdge(
|
graph.addEdge(sourcePath, joinSegments(ctx.argv.output, "index.xml") as FilePath)
|
||||||
sourcePath,
|
|
||||||
joinSegments(ctx.argv.output, "index.xml") as FilePath,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,21 +120,14 @@ export const ContentIndex: QuartzEmitterPlugin<Partial<Options>> = (opts) => {
|
|||||||
for (const [tree, file] of content) {
|
for (const [tree, file] of content) {
|
||||||
const slug = file.data.slug!
|
const slug = file.data.slug!
|
||||||
const date = getDate(ctx.cfg.configuration, file.data) ?? new Date()
|
const date = getDate(ctx.cfg.configuration, file.data) ?? new Date()
|
||||||
if (
|
if (opts?.includeEmptyFiles || (file.data.text && file.data.text !== "")) {
|
||||||
opts?.includeEmptyFiles ||
|
|
||||||
(file.data.text && file.data.text !== "")
|
|
||||||
) {
|
|
||||||
linkIndex.set(slug, {
|
linkIndex.set(slug, {
|
||||||
title: file.data.frontmatter?.title!,
|
title: file.data.frontmatter?.title!,
|
||||||
links: file.data.links ?? [],
|
links: file.data.links ?? [],
|
||||||
tags: file.data.frontmatter?.tags ?? [],
|
tags: file.data.frontmatter?.tags ?? [],
|
||||||
content: file.data.text ?? "",
|
content: file.data.text ?? "",
|
||||||
richContent: opts?.rssFullHtml
|
richContent: opts?.rssFullHtml
|
||||||
? escapeHTML(
|
? escapeHTML(toHtml(tree as Root, { allowDangerousHtml: true }))
|
||||||
toHtml(tree as Root, {
|
|
||||||
allowDangerousHtml: true,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
: undefined,
|
: undefined,
|
||||||
date: date,
|
date: date,
|
||||||
description: file.data.description ?? "",
|
description: file.data.description ?? "",
|
||||||
|
|||||||
@ -1,27 +1,19 @@
|
|||||||
import path from "path"
|
import path from "path"
|
||||||
import {visit} from "unist-util-visit"
|
import { visit } from "unist-util-visit"
|
||||||
import {Root} from "hast"
|
import { Root } from "hast"
|
||||||
import {VFile} from "vfile"
|
import { VFile } from "vfile"
|
||||||
import {QuartzEmitterPlugin} from "../types"
|
import { QuartzEmitterPlugin } from "../types"
|
||||||
import {QuartzComponentProps} from "../../components/types"
|
import { QuartzComponentProps } from "../../components/types"
|
||||||
import HeaderConstructor from "../../components/Header"
|
import HeaderConstructor from "../../components/Header"
|
||||||
import BodyConstructor from "../../components/Body"
|
import BodyConstructor from "../../components/Body"
|
||||||
import {pageResources, renderPage} from "../../components/renderPage"
|
import { pageResources, renderPage } from "../../components/renderPage"
|
||||||
import {FullPageLayout} from "../../cfg"
|
import { FullPageLayout } from "../../cfg"
|
||||||
import {Argv} from "../../util/ctx"
|
import { Argv } from "../../util/ctx"
|
||||||
import {
|
import { FilePath, isRelativeURL, joinSegments, pathToRoot } from "../../util/path"
|
||||||
FilePath,
|
import { defaultContentPageLayout, sharedPageComponents } from "../../../quartz.layout"
|
||||||
isRelativeURL,
|
import { Content } from "../../components"
|
||||||
joinSegments,
|
|
||||||
pathToRoot,
|
|
||||||
} from "../../util/path"
|
|
||||||
import {
|
|
||||||
defaultContentPageLayout,
|
|
||||||
sharedPageComponents,
|
|
||||||
} from "../../../quartz.layout"
|
|
||||||
import {Content} from "../../components"
|
|
||||||
import chalk from "chalk"
|
import chalk from "chalk"
|
||||||
import {write} from "./helpers"
|
import { write } from "./helpers"
|
||||||
import DepGraph from "../../depgraph"
|
import DepGraph from "../../depgraph"
|
||||||
|
|
||||||
// get all the dependencies for the markdown file
|
// get all the dependencies for the markdown file
|
||||||
@ -33,9 +25,7 @@ const parseDependencies = (argv: Argv, hast: Root, file: VFile): string[] => {
|
|||||||
let ref: string | null = null
|
let ref: string | null = null
|
||||||
|
|
||||||
if (
|
if (
|
||||||
["script", "img", "audio", "video", "source", "iframe"].includes(
|
["script", "img", "audio", "video", "source", "iframe"].includes(elem.tagName) &&
|
||||||
elem.tagName,
|
|
||||||
) &&
|
|
||||||
elem?.properties?.src
|
elem?.properties?.src
|
||||||
) {
|
) {
|
||||||
ref = elem.properties.src.toString()
|
ref = elem.properties.src.toString()
|
||||||
@ -50,9 +40,7 @@ const parseDependencies = (argv: Argv, hast: Root, file: VFile): string[] => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let fp = path
|
let fp = path.join(file.data.filePath!, path.relative(argv.directory, ref)).replace(/\\/g, "/")
|
||||||
.join(file.data.filePath!, path.relative(argv.directory, ref))
|
|
||||||
.replace(/\\/g, "/")
|
|
||||||
// markdown files have the .md extension stripped in hrefs, add it back here
|
// markdown files have the .md extension stripped in hrefs, add it back here
|
||||||
if (!fp.split("/").pop()?.includes(".")) {
|
if (!fp.split("/").pop()?.includes(".")) {
|
||||||
fp += ".md"
|
fp += ".md"
|
||||||
@ -63,9 +51,7 @@ const parseDependencies = (argv: Argv, hast: Root, file: VFile): string[] => {
|
|||||||
return dependencies
|
return dependencies
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ContentPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (
|
export const ContentPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (userOpts) => {
|
||||||
userOpts,
|
|
||||||
) => {
|
|
||||||
const opts: FullPageLayout = {
|
const opts: FullPageLayout = {
|
||||||
...sharedPageComponents,
|
...sharedPageComponents,
|
||||||
...defaultContentPageLayout,
|
...defaultContentPageLayout,
|
||||||
@ -73,15 +59,7 @@ export const ContentPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (
|
|||||||
...userOpts,
|
...userOpts,
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const { head: Head, header, beforeBody, pageBody, afterBody, left, right, footer: Footer } = opts
|
||||||
head: Head,
|
|
||||||
header,
|
|
||||||
beforeBody,
|
|
||||||
pageBody,
|
|
||||||
left,
|
|
||||||
right,
|
|
||||||
footer: Footer,
|
|
||||||
} = opts
|
|
||||||
const Header = HeaderConstructor()
|
const Header = HeaderConstructor()
|
||||||
const Body = BodyConstructor()
|
const Body = BodyConstructor()
|
||||||
|
|
||||||
@ -95,6 +73,7 @@ export const ContentPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (
|
|||||||
...header,
|
...header,
|
||||||
...beforeBody,
|
...beforeBody,
|
||||||
pageBody,
|
pageBody,
|
||||||
|
...afterBody,
|
||||||
...left,
|
...left,
|
||||||
...right,
|
...right,
|
||||||
Footer,
|
Footer,
|
||||||
@ -106,10 +85,7 @@ export const ContentPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (
|
|||||||
for (const [tree, file] of content) {
|
for (const [tree, file] of content) {
|
||||||
const sourcePath = file.data.filePath!
|
const sourcePath = file.data.filePath!
|
||||||
const slug = file.data.slug!
|
const slug = file.data.slug!
|
||||||
graph.addEdge(
|
graph.addEdge(sourcePath, joinSegments(ctx.argv.output, slug + ".html") as FilePath)
|
||||||
sourcePath,
|
|
||||||
joinSegments(ctx.argv.output, slug + ".html") as FilePath,
|
|
||||||
)
|
|
||||||
|
|
||||||
parseDependencies(ctx.argv, tree as Root, file).forEach((dep) => {
|
parseDependencies(ctx.argv, tree as Root, file).forEach((dep) => {
|
||||||
graph.addEdge(dep as FilePath, sourcePath)
|
graph.addEdge(dep as FilePath, sourcePath)
|
||||||
@ -141,13 +117,7 @@ export const ContentPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (
|
|||||||
allFiles,
|
allFiles,
|
||||||
}
|
}
|
||||||
|
|
||||||
const content = renderPage(
|
const content = renderPage(cfg, slug, componentData, opts, externalResources)
|
||||||
cfg,
|
|
||||||
slug,
|
|
||||||
componentData,
|
|
||||||
opts,
|
|
||||||
externalResources,
|
|
||||||
)
|
|
||||||
const fp = await write({
|
const fp = await write({
|
||||||
ctx,
|
ctx,
|
||||||
content,
|
content,
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import {QuartzEmitterPlugin} from "../types"
|
import { QuartzEmitterPlugin } from "../types"
|
||||||
import {QuartzComponentProps} from "../../components/types"
|
import { QuartzComponentProps } from "../../components/types"
|
||||||
import HeaderConstructor from "../../components/Header"
|
import HeaderConstructor from "../../components/Header"
|
||||||
import BodyConstructor from "../../components/Body"
|
import BodyConstructor from "../../components/Body"
|
||||||
import {pageResources, renderPage} from "../../components/renderPage"
|
import { pageResources, renderPage } from "../../components/renderPage"
|
||||||
import {ProcessedContent, defaultProcessedContent} from "../vfile"
|
import { ProcessedContent, QuartzPluginData, defaultProcessedContent } from "../vfile"
|
||||||
import {FullPageLayout} from "../../cfg"
|
import { FullPageLayout } from "../../cfg"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import {
|
import {
|
||||||
FilePath,
|
FilePath,
|
||||||
@ -15,18 +15,17 @@ import {
|
|||||||
pathToRoot,
|
pathToRoot,
|
||||||
simplifySlug,
|
simplifySlug,
|
||||||
} from "../../util/path"
|
} from "../../util/path"
|
||||||
import {
|
import { defaultListPageLayout, sharedPageComponents } from "../../../quartz.layout"
|
||||||
defaultListPageLayout,
|
import { FolderContent } from "../../components"
|
||||||
sharedPageComponents,
|
import { write } from "./helpers"
|
||||||
} from "../../../quartz.layout"
|
import { i18n } from "../../i18n"
|
||||||
import {FolderContent} from "../../components"
|
|
||||||
import {write} from "./helpers"
|
|
||||||
import {i18n} from "../../i18n"
|
|
||||||
import DepGraph from "../../depgraph"
|
import DepGraph from "../../depgraph"
|
||||||
|
|
||||||
export const FolderPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (
|
interface FolderPageOptions extends FullPageLayout {
|
||||||
userOpts,
|
sort?: (f1: QuartzPluginData, f2: QuartzPluginData) => number
|
||||||
) => {
|
}
|
||||||
|
|
||||||
|
export const FolderPage: QuartzEmitterPlugin<Partial<FolderPageOptions>> = (userOpts) => {
|
||||||
const opts: FullPageLayout = {
|
const opts: FullPageLayout = {
|
||||||
...sharedPageComponents,
|
...sharedPageComponents,
|
||||||
...defaultListPageLayout,
|
...defaultListPageLayout,
|
||||||
@ -34,15 +33,7 @@ export const FolderPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (
|
|||||||
...userOpts,
|
...userOpts,
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const { head: Head, header, beforeBody, pageBody, afterBody, left, right, footer: Footer } = opts
|
||||||
head: Head,
|
|
||||||
header,
|
|
||||||
beforeBody,
|
|
||||||
pageBody,
|
|
||||||
left,
|
|
||||||
right,
|
|
||||||
footer: Footer,
|
|
||||||
} = opts
|
|
||||||
const Header = HeaderConstructor()
|
const Header = HeaderConstructor()
|
||||||
const Body = BodyConstructor()
|
const Body = BodyConstructor()
|
||||||
|
|
||||||
@ -56,6 +47,7 @@ export const FolderPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (
|
|||||||
...header,
|
...header,
|
||||||
...beforeBody,
|
...beforeBody,
|
||||||
pageBody,
|
pageBody,
|
||||||
|
...afterBody,
|
||||||
...left,
|
...left,
|
||||||
...right,
|
...right,
|
||||||
Footer,
|
Footer,
|
||||||
@ -71,10 +63,7 @@ export const FolderPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (
|
|||||||
const slug = vfile.data.slug
|
const slug = vfile.data.slug
|
||||||
const folderName = path.dirname(slug ?? "") as SimpleSlug
|
const folderName = path.dirname(slug ?? "") as SimpleSlug
|
||||||
if (slug && folderName !== "." && folderName !== "tags") {
|
if (slug && folderName !== "." && folderName !== "tags") {
|
||||||
graph.addEdge(
|
graph.addEdge(vfile.data.filePath!, joinSegments(folderName, "index.html") as FilePath)
|
||||||
vfile.data.filePath!,
|
|
||||||
joinSegments(folderName, "index.html") as FilePath,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -96,19 +85,18 @@ export const FolderPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
const folderDescriptions: Record<string, ProcessedContent> =
|
const folderDescriptions: Record<string, ProcessedContent> = Object.fromEntries(
|
||||||
Object.fromEntries(
|
[...folders].map((folder) => [
|
||||||
[...folders].map((folder) => [
|
folder,
|
||||||
folder,
|
defaultProcessedContent({
|
||||||
defaultProcessedContent({
|
slug: joinSegments(folder, "index") as FullSlug,
|
||||||
slug: joinSegments(folder, "index") as FullSlug,
|
frontmatter: {
|
||||||
frontmatter: {
|
title: `${i18n(cfg.locale).pages.folderContent.folder}: ${folder}`,
|
||||||
title: `${i18n(cfg.locale).pages.folderContent.folder}: ${folder}`,
|
tags: [],
|
||||||
tags: [],
|
},
|
||||||
},
|
}),
|
||||||
}),
|
]),
|
||||||
]),
|
)
|
||||||
)
|
|
||||||
|
|
||||||
for (const [tree, file] of content) {
|
for (const [tree, file] of content) {
|
||||||
const slug = stripSlashes(simplifySlug(file.data.slug!)) as SimpleSlug
|
const slug = stripSlashes(simplifySlug(file.data.slug!)) as SimpleSlug
|
||||||
@ -131,13 +119,7 @@ export const FolderPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (
|
|||||||
allFiles,
|
allFiles,
|
||||||
}
|
}
|
||||||
|
|
||||||
const content = renderPage(
|
const content = renderPage(cfg, slug, componentData, opts, externalResources)
|
||||||
cfg,
|
|
||||||
slug,
|
|
||||||
componentData,
|
|
||||||
opts,
|
|
||||||
externalResources,
|
|
||||||
)
|
|
||||||
const fp = await write({
|
const fp = await write({
|
||||||
ctx,
|
ctx,
|
||||||
content,
|
content,
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import path from "path"
|
import path from "path"
|
||||||
import fs from "fs"
|
import fs from "fs"
|
||||||
import {BuildCtx} from "../../util/ctx"
|
import { BuildCtx } from "../../util/ctx"
|
||||||
import {FilePath, FullSlug, joinSegments} from "../../util/path"
|
import { FilePath, FullSlug, joinSegments } from "../../util/path"
|
||||||
|
|
||||||
type WriteOptions = {
|
type WriteOptions = {
|
||||||
ctx: BuildCtx
|
ctx: BuildCtx
|
||||||
@ -10,15 +10,10 @@ type WriteOptions = {
|
|||||||
content: string | Buffer
|
content: string | Buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
export const write = async ({
|
export const write = async ({ ctx, slug, ext, content }: WriteOptions): Promise<FilePath> => {
|
||||||
ctx,
|
|
||||||
slug,
|
|
||||||
ext,
|
|
||||||
content,
|
|
||||||
}: WriteOptions): Promise<FilePath> => {
|
|
||||||
const pathToPage = joinSegments(ctx.argv.output, slug + ext) as FilePath
|
const pathToPage = joinSegments(ctx.argv.output, slug + ext) as FilePath
|
||||||
const dir = path.dirname(pathToPage)
|
const dir = path.dirname(pathToPage)
|
||||||
await fs.promises.mkdir(dir, {recursive: true})
|
await fs.promises.mkdir(dir, { recursive: true })
|
||||||
await fs.promises.writeFile(pathToPage, content)
|
await fs.promises.writeFile(pathToPage, content)
|
||||||
return pathToPage
|
return pathToPage
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
export {ContentPage} from "./contentPage"
|
export { ContentPage } from "./contentPage"
|
||||||
export {TagPage} from "./tagPage"
|
export { TagPage } from "./tagPage"
|
||||||
export {FolderPage} from "./folderPage"
|
export { FolderPage } from "./folderPage"
|
||||||
export {ContentIndex} from "./contentIndex"
|
export { ContentIndex } from "./contentIndex"
|
||||||
export {AliasRedirects} from "./aliases"
|
export { AliasRedirects } from "./aliases"
|
||||||
export {Assets} from "./assets"
|
export { Assets } from "./assets"
|
||||||
export {Static} from "./static"
|
export { Static } from "./static"
|
||||||
export {ComponentResources} from "./componentResources"
|
export { ComponentResources } from "./componentResources"
|
||||||
export {NotFoundPage} from "./404"
|
export { NotFoundPage } from "./404"
|
||||||
export {CNAME} from "./cname"
|
export { CNAME } from "./cname"
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import {FilePath, QUARTZ, joinSegments} from "../../util/path"
|
import { FilePath, QUARTZ, joinSegments } from "../../util/path"
|
||||||
import {QuartzEmitterPlugin} from "../types"
|
import { QuartzEmitterPlugin } from "../types"
|
||||||
import fs from "fs"
|
import fs from "fs"
|
||||||
import {glob} from "../../util/glob"
|
import { glob } from "../../util/glob"
|
||||||
import DepGraph from "../../depgraph"
|
import DepGraph from "../../depgraph"
|
||||||
|
|
||||||
export const Static: QuartzEmitterPlugin = () => ({
|
export const Static: QuartzEmitterPlugin = () => ({
|
||||||
@ -9,7 +9,7 @@ export const Static: QuartzEmitterPlugin = () => ({
|
|||||||
getQuartzComponents() {
|
getQuartzComponents() {
|
||||||
return []
|
return []
|
||||||
},
|
},
|
||||||
async getDependencyGraph({argv, cfg}, _content, _resources) {
|
async getDependencyGraph({ argv, cfg }, _content, _resources) {
|
||||||
const graph = new DepGraph<FilePath>()
|
const graph = new DepGraph<FilePath>()
|
||||||
|
|
||||||
const staticPath = joinSegments(QUARTZ, "static")
|
const staticPath = joinSegments(QUARTZ, "static")
|
||||||
@ -23,15 +23,13 @@ export const Static: QuartzEmitterPlugin = () => ({
|
|||||||
|
|
||||||
return graph
|
return graph
|
||||||
},
|
},
|
||||||
async emit({argv, cfg}, _content, _resources): Promise<FilePath[]> {
|
async emit({ argv, cfg }, _content, _resources): Promise<FilePath[]> {
|
||||||
const staticPath = joinSegments(QUARTZ, "static")
|
const staticPath = joinSegments(QUARTZ, "static")
|
||||||
const fps = await glob("**", staticPath, cfg.configuration.ignorePatterns)
|
const fps = await glob("**", staticPath, cfg.configuration.ignorePatterns)
|
||||||
await fs.promises.cp(staticPath, joinSegments(argv.output, "static"), {
|
await fs.promises.cp(staticPath, joinSegments(argv.output, "static"), {
|
||||||
recursive: true,
|
recursive: true,
|
||||||
dereference: true,
|
dereference: true,
|
||||||
})
|
})
|
||||||
return fps.map((fp) =>
|
return fps.map((fp) => joinSegments(argv.output, "static", fp)) as FilePath[]
|
||||||
joinSegments(argv.output, "static", fp),
|
|
||||||
) as FilePath[]
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import {QuartzEmitterPlugin} from "../types"
|
import { QuartzEmitterPlugin } from "../types"
|
||||||
import {QuartzComponentProps} from "../../components/types"
|
import { QuartzComponentProps } from "../../components/types"
|
||||||
import HeaderConstructor from "../../components/Header"
|
import HeaderConstructor from "../../components/Header"
|
||||||
import BodyConstructor from "../../components/Body"
|
import BodyConstructor from "../../components/Body"
|
||||||
import {pageResources, renderPage} from "../../components/renderPage"
|
import { pageResources, renderPage } from "../../components/renderPage"
|
||||||
import {ProcessedContent, defaultProcessedContent} from "../vfile"
|
import { ProcessedContent, QuartzPluginData, defaultProcessedContent } from "../vfile"
|
||||||
import {FullPageLayout} from "../../cfg"
|
import { FullPageLayout } from "../../cfg"
|
||||||
import {
|
import {
|
||||||
FilePath,
|
FilePath,
|
||||||
FullSlug,
|
FullSlug,
|
||||||
@ -12,18 +12,17 @@ import {
|
|||||||
joinSegments,
|
joinSegments,
|
||||||
pathToRoot,
|
pathToRoot,
|
||||||
} from "../../util/path"
|
} from "../../util/path"
|
||||||
import {
|
import { defaultListPageLayout, sharedPageComponents } from "../../../quartz.layout"
|
||||||
defaultListPageLayout,
|
import { TagContent } from "../../components"
|
||||||
sharedPageComponents,
|
import { write } from "./helpers"
|
||||||
} from "../../../quartz.layout"
|
import { i18n } from "../../i18n"
|
||||||
import {TagContent} from "../../components"
|
|
||||||
import {write} from "./helpers"
|
|
||||||
import {i18n} from "../../i18n"
|
|
||||||
import DepGraph from "../../depgraph"
|
import DepGraph from "../../depgraph"
|
||||||
|
|
||||||
export const TagPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (
|
interface TagPageOptions extends FullPageLayout {
|
||||||
userOpts,
|
sort?: (f1: QuartzPluginData, f2: QuartzPluginData) => number
|
||||||
) => {
|
}
|
||||||
|
|
||||||
|
export const TagPage: QuartzEmitterPlugin<Partial<TagPageOptions>> = (userOpts) => {
|
||||||
const opts: FullPageLayout = {
|
const opts: FullPageLayout = {
|
||||||
...sharedPageComponents,
|
...sharedPageComponents,
|
||||||
...defaultListPageLayout,
|
...defaultListPageLayout,
|
||||||
@ -31,15 +30,7 @@ export const TagPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (
|
|||||||
...userOpts,
|
...userOpts,
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const { head: Head, header, beforeBody, pageBody, afterBody, left, right, footer: Footer } = opts
|
||||||
head: Head,
|
|
||||||
header,
|
|
||||||
beforeBody,
|
|
||||||
pageBody,
|
|
||||||
left,
|
|
||||||
right,
|
|
||||||
footer: Footer,
|
|
||||||
} = opts
|
|
||||||
const Header = HeaderConstructor()
|
const Header = HeaderConstructor()
|
||||||
const Body = BodyConstructor()
|
const Body = BodyConstructor()
|
||||||
|
|
||||||
@ -53,6 +44,7 @@ export const TagPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (
|
|||||||
...header,
|
...header,
|
||||||
...beforeBody,
|
...beforeBody,
|
||||||
pageBody,
|
pageBody,
|
||||||
|
...afterBody,
|
||||||
...left,
|
...left,
|
||||||
...right,
|
...right,
|
||||||
Footer,
|
Footer,
|
||||||
@ -63,9 +55,7 @@ export const TagPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (
|
|||||||
|
|
||||||
for (const [_tree, file] of content) {
|
for (const [_tree, file] of content) {
|
||||||
const sourcePath = file.data.filePath!
|
const sourcePath = file.data.filePath!
|
||||||
const tags = (file.data.frontmatter?.tags ?? []).flatMap(
|
const tags = (file.data.frontmatter?.tags ?? []).flatMap(getAllSegmentPrefixes)
|
||||||
getAllSegmentPrefixes,
|
|
||||||
)
|
|
||||||
// if the file has at least one tag, it is used in the tag index page
|
// if the file has at least one tag, it is used in the tag index page
|
||||||
if (tags.length > 0) {
|
if (tags.length > 0) {
|
||||||
tags.push("index")
|
tags.push("index")
|
||||||
@ -87,30 +77,27 @@ export const TagPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (
|
|||||||
const cfg = ctx.cfg.configuration
|
const cfg = ctx.cfg.configuration
|
||||||
|
|
||||||
const tags: Set<string> = new Set(
|
const tags: Set<string> = new Set(
|
||||||
allFiles
|
allFiles.flatMap((data) => data.frontmatter?.tags ?? []).flatMap(getAllSegmentPrefixes),
|
||||||
.flatMap((data) => data.frontmatter?.tags ?? [])
|
|
||||||
.flatMap(getAllSegmentPrefixes),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// add base tag
|
// add base tag
|
||||||
tags.add("index")
|
tags.add("index")
|
||||||
|
|
||||||
const tagDescriptions: Record<string, ProcessedContent> =
|
const tagDescriptions: Record<string, ProcessedContent> = Object.fromEntries(
|
||||||
Object.fromEntries(
|
[...tags].map((tag) => {
|
||||||
[...tags].map((tag) => {
|
const title =
|
||||||
const title =
|
tag === "index"
|
||||||
tag === "index"
|
? i18n(cfg.locale).pages.tagContent.tagIndex
|
||||||
? i18n(cfg.locale).pages.tagContent.tagIndex
|
: `${i18n(cfg.locale).pages.tagContent.tag}: ${tag}`
|
||||||
: `${i18n(cfg.locale).pages.tagContent.tag}: ${tag}`
|
return [
|
||||||
return [
|
tag,
|
||||||
tag,
|
defaultProcessedContent({
|
||||||
defaultProcessedContent({
|
slug: joinSegments("tags", tag) as FullSlug,
|
||||||
slug: joinSegments("tags", tag) as FullSlug,
|
frontmatter: { title, tags: [] },
|
||||||
frontmatter: {title, tags: []},
|
}),
|
||||||
}),
|
]
|
||||||
]
|
}),
|
||||||
}),
|
)
|
||||||
)
|
|
||||||
|
|
||||||
for (const [tree, file] of content) {
|
for (const [tree, file] of content) {
|
||||||
const slug = file.data.slug!
|
const slug = file.data.slug!
|
||||||
@ -136,13 +123,7 @@ export const TagPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (
|
|||||||
allFiles,
|
allFiles,
|
||||||
}
|
}
|
||||||
|
|
||||||
const content = renderPage(
|
const content = renderPage(cfg, slug, componentData, opts, externalResources)
|
||||||
cfg,
|
|
||||||
slug,
|
|
||||||
componentData,
|
|
||||||
opts,
|
|
||||||
externalResources,
|
|
||||||
)
|
|
||||||
const fp = await write({
|
const fp = await write({
|
||||||
ctx,
|
ctx,
|
||||||
content,
|
content,
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {QuartzFilterPlugin} from "../types"
|
import { QuartzFilterPlugin } from "../types"
|
||||||
|
|
||||||
export const RemoveDrafts: QuartzFilterPlugin<{}> = () => ({
|
export const RemoveDrafts: QuartzFilterPlugin<{}> = () => ({
|
||||||
name: "RemoveDrafts",
|
name: "RemoveDrafts",
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {QuartzFilterPlugin} from "../types"
|
import { QuartzFilterPlugin } from "../types"
|
||||||
|
|
||||||
export const ExplicitPublish: QuartzFilterPlugin = () => ({
|
export const ExplicitPublish: QuartzFilterPlugin = () => ({
|
||||||
name: "ExplicitPublish",
|
name: "ExplicitPublish",
|
||||||
|
|||||||
@ -1,2 +1,2 @@
|
|||||||
export {RemoveDrafts} from "./draft"
|
export { RemoveDrafts } from "./draft"
|
||||||
export {ExplicitPublish} from "./explicit"
|
export { ExplicitPublish } from "./explicit"
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import {StaticResources} from "../util/resources"
|
import { StaticResources } from "../util/resources"
|
||||||
import {FilePath, FullSlug} from "../util/path"
|
import { FilePath, FullSlug } from "../util/path"
|
||||||
import {BuildCtx} from "../util/ctx"
|
import { BuildCtx } from "../util/ctx"
|
||||||
|
|
||||||
export function getStaticResourcesFromPlugins(ctx: BuildCtx) {
|
export function getStaticResourcesFromPlugins(ctx: BuildCtx) {
|
||||||
const staticResources: StaticResources = {
|
const staticResources: StaticResources = {
|
||||||
@ -9,9 +9,7 @@ export function getStaticResourcesFromPlugins(ctx: BuildCtx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const transformer of ctx.cfg.plugins.transformers) {
|
for (const transformer of ctx.cfg.plugins.transformers) {
|
||||||
const res = transformer.externalResources
|
const res = transformer.externalResources ? transformer.externalResources(ctx) : {}
|
||||||
? transformer.externalResources(ctx)
|
|
||||||
: {}
|
|
||||||
if (res?.js) {
|
if (res?.js) {
|
||||||
staticResources.js.push(...res.js)
|
staticResources.js.push(...res.js)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import rehypeCitation from "rehype-citation"
|
import rehypeCitation from "rehype-citation"
|
||||||
import {PluggableList} from "unified"
|
import { PluggableList } from "unified"
|
||||||
import {visit} from "unist-util-visit"
|
import { visit } from "unist-util-visit"
|
||||||
import {QuartzTransformerPlugin} from "../types"
|
import { QuartzTransformerPlugin } from "../types"
|
||||||
|
|
||||||
export interface Options {
|
export interface Options {
|
||||||
bibliographyFile: string
|
bibliographyFile: string
|
||||||
@ -17,10 +17,8 @@ const defaultOptions: Options = {
|
|||||||
csl: "apa",
|
csl: "apa",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Citations: QuartzTransformerPlugin<
|
export const Citations: QuartzTransformerPlugin<Partial<Options>> = (userOpts) => {
|
||||||
Partial<Options> | undefined
|
const opts = { ...defaultOptions, ...userOpts }
|
||||||
> = (userOpts) => {
|
|
||||||
const opts = {...defaultOptions, ...userOpts}
|
|
||||||
return {
|
return {
|
||||||
name: "Citations",
|
name: "Citations",
|
||||||
htmlPlugins() {
|
htmlPlugins() {
|
||||||
@ -40,11 +38,8 @@ export const Citations: QuartzTransformerPlugin<
|
|||||||
// using https://github.com/syntax-tree/unist-util-visit as they're just anochor links
|
// using https://github.com/syntax-tree/unist-util-visit as they're just anochor links
|
||||||
plugins.push(() => {
|
plugins.push(() => {
|
||||||
return (tree, _file) => {
|
return (tree, _file) => {
|
||||||
visit(tree, "element", (node, index, parent) => {
|
visit(tree, "element", (node, _index, _parent) => {
|
||||||
if (
|
if (node.tagName === "a" && node.properties?.href?.startsWith("#bib")) {
|
||||||
node.tagName === "a" &&
|
|
||||||
node.properties?.href?.startsWith("#bib")
|
|
||||||
) {
|
|
||||||
node.properties["data-no-popover"] = true
|
node.properties["data-no-popover"] = true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import {Root as HTMLRoot} from "hast"
|
import { Root as HTMLRoot } from "hast"
|
||||||
import {toString} from "hast-util-to-string"
|
import { toString } from "hast-util-to-string"
|
||||||
import {QuartzTransformerPlugin} from "../types"
|
import { QuartzTransformerPlugin } from "../types"
|
||||||
import {escapeHTML} from "../../util/escape"
|
import { escapeHTML } from "../../util/escape"
|
||||||
|
|
||||||
export interface Options {
|
export interface Options {
|
||||||
descriptionLength: number
|
descriptionLength: number
|
||||||
@ -18,10 +18,8 @@ const urlRegex = new RegExp(
|
|||||||
"g",
|
"g",
|
||||||
)
|
)
|
||||||
|
|
||||||
export const Description: QuartzTransformerPlugin<
|
export const Description: QuartzTransformerPlugin<Partial<Options>> = (userOpts) => {
|
||||||
Partial<Options> | undefined
|
const opts = { ...defaultOptions, ...userOpts }
|
||||||
> = (userOpts) => {
|
|
||||||
const opts = {...defaultOptions, ...userOpts}
|
|
||||||
return {
|
return {
|
||||||
name: "Description",
|
name: "Description",
|
||||||
htmlPlugins() {
|
htmlPlugins() {
|
||||||
@ -60,9 +58,7 @@ export const Description: QuartzTransformerPlugin<
|
|||||||
while (currentDescriptionLength < len) {
|
while (currentDescriptionLength < len) {
|
||||||
const sentence = sentences[sentenceIdx]
|
const sentence = sentences[sentenceIdx]
|
||||||
if (!sentence) break
|
if (!sentence) break
|
||||||
const currentSentence = sentence.endsWith(".")
|
const currentSentence = sentence.endsWith(".") ? sentence : sentence + "."
|
||||||
? sentence
|
|
||||||
: sentence + "."
|
|
||||||
finalDesc.push(currentSentence)
|
finalDesc.push(currentSentence)
|
||||||
currentDescriptionLength += currentSentence.length
|
currentDescriptionLength += currentSentence.length
|
||||||
sentenceIdx++
|
sentenceIdx++
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import matter from "gray-matter"
|
import matter from "gray-matter"
|
||||||
import remarkFrontmatter from "remark-frontmatter"
|
import remarkFrontmatter from "remark-frontmatter"
|
||||||
import {QuartzTransformerPlugin} from "../types"
|
import { QuartzTransformerPlugin } from "../types"
|
||||||
import yaml from "js-yaml"
|
import yaml from "js-yaml"
|
||||||
import toml from "toml"
|
import toml from "toml"
|
||||||
import {slugTag} from "../../util/path"
|
import { slugTag } from "../../util/path"
|
||||||
import {QuartzPluginData} from "../vfile"
|
import { QuartzPluginData } from "../vfile"
|
||||||
import {i18n} from "../../i18n"
|
import { i18n } from "../../i18n"
|
||||||
|
|
||||||
export interface Options {
|
export interface Options {
|
||||||
delimiters: string | [string, string]
|
delimiters: string | [string, string]
|
||||||
@ -17,7 +17,7 @@ const defaultOptions: Options = {
|
|||||||
language: "yaml",
|
language: "yaml",
|
||||||
}
|
}
|
||||||
|
|
||||||
function coalesceAliases(data: {[key: string]: any}, aliases: string[]) {
|
function coalesceAliases(data: { [key: string]: any }, aliases: string[]) {
|
||||||
for (const alias of aliases) {
|
for (const alias of aliases) {
|
||||||
if (data[alias] !== undefined && data[alias] !== null) return data[alias]
|
if (data[alias] !== undefined && data[alias] !== null) return data[alias]
|
||||||
}
|
}
|
||||||
@ -36,30 +36,23 @@ function coerceToArray(input: string | string[]): string[] | undefined {
|
|||||||
|
|
||||||
// remove all non-strings
|
// remove all non-strings
|
||||||
return input
|
return input
|
||||||
.filter(
|
.filter((tag: unknown) => typeof tag === "string" || typeof tag === "number")
|
||||||
(tag: unknown) => typeof tag === "string" || typeof tag === "number",
|
|
||||||
)
|
|
||||||
.map((tag: string | number) => tag.toString())
|
.map((tag: string | number) => tag.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FrontMatter: QuartzTransformerPlugin<
|
export const FrontMatter: QuartzTransformerPlugin<Partial<Options>> = (userOpts) => {
|
||||||
Partial<Options> | undefined
|
const opts = { ...defaultOptions, ...userOpts }
|
||||||
> = (userOpts) => {
|
|
||||||
const opts = {...defaultOptions, ...userOpts}
|
|
||||||
return {
|
return {
|
||||||
name: "FrontMatter",
|
name: "FrontMatter",
|
||||||
markdownPlugins({cfg}) {
|
markdownPlugins({ cfg }) {
|
||||||
return [
|
return [
|
||||||
[remarkFrontmatter, ["yaml", "toml"]],
|
[remarkFrontmatter, ["yaml", "toml"]],
|
||||||
() => {
|
() => {
|
||||||
return (_, file) => {
|
return (_, file) => {
|
||||||
const {data} = matter(Buffer.from(file.value), {
|
const { data } = matter(Buffer.from(file.value), {
|
||||||
...opts,
|
...opts,
|
||||||
engines: {
|
engines: {
|
||||||
yaml: (s) =>
|
yaml: (s) => yaml.load(s, { schema: yaml.JSON_SCHEMA }) as object,
|
||||||
yaml.load(s, {
|
|
||||||
schema: yaml.JSON_SCHEMA,
|
|
||||||
}) as object,
|
|
||||||
toml: (s) => toml.parse(s) as object,
|
toml: (s) => toml.parse(s) as object,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -67,22 +60,15 @@ export const FrontMatter: QuartzTransformerPlugin<
|
|||||||
if (data.title != null && data.title.toString() !== "") {
|
if (data.title != null && data.title.toString() !== "") {
|
||||||
data.title = data.title.toString()
|
data.title = data.title.toString()
|
||||||
} else {
|
} else {
|
||||||
data.title =
|
data.title = file.stem ?? i18n(cfg.configuration.locale).propertyDefaults.title
|
||||||
file.stem ??
|
|
||||||
i18n(cfg.configuration.locale).propertyDefaults.title
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const tags = coerceToArray(coalesceAliases(data, ["tags", "tag"]))
|
const tags = coerceToArray(coalesceAliases(data, ["tags", "tag"]))
|
||||||
if (tags)
|
if (tags) data.tags = [...new Set(tags.map((tag: string) => slugTag(tag)))]
|
||||||
data.tags = [...new Set(tags.map((tag: string) => slugTag(tag)))]
|
|
||||||
|
|
||||||
const aliases = coerceToArray(
|
const aliases = coerceToArray(coalesceAliases(data, ["aliases", "alias"]))
|
||||||
coalesceAliases(data, ["aliases", "alias"]),
|
|
||||||
)
|
|
||||||
if (aliases) data.aliases = aliases
|
if (aliases) data.aliases = aliases
|
||||||
const cssclasses = coerceToArray(
|
const cssclasses = coerceToArray(coalesceAliases(data, ["cssclasses", "cssclass"]))
|
||||||
coalesceAliases(data, ["cssclasses", "cssclass"]),
|
|
||||||
)
|
|
||||||
if (cssclasses) data.cssclasses = cssclasses
|
if (cssclasses) data.cssclasses = cssclasses
|
||||||
|
|
||||||
// fill in frontmatter
|
// fill in frontmatter
|
||||||
@ -96,7 +82,7 @@ export const FrontMatter: QuartzTransformerPlugin<
|
|||||||
|
|
||||||
declare module "vfile" {
|
declare module "vfile" {
|
||||||
interface DataMap {
|
interface DataMap {
|
||||||
frontmatter: {[key: string]: unknown} & {
|
frontmatter: { [key: string]: unknown } & {
|
||||||
title: string
|
title: string
|
||||||
} & Partial<{
|
} & Partial<{
|
||||||
tags: string[]
|
tags: string[]
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import remarkGfm from "remark-gfm"
|
import remarkGfm from "remark-gfm"
|
||||||
import smartypants from "remark-smartypants"
|
import smartypants from "remark-smartypants"
|
||||||
import {QuartzTransformerPlugin} from "../types"
|
import { QuartzTransformerPlugin } from "../types"
|
||||||
import rehypeSlug from "rehype-slug"
|
import rehypeSlug from "rehype-slug"
|
||||||
import rehypeAutolinkHeadings from "rehype-autolink-headings"
|
import rehypeAutolinkHeadings from "rehype-autolink-headings"
|
||||||
|
|
||||||
@ -14,10 +14,8 @@ const defaultOptions: Options = {
|
|||||||
linkHeadings: true,
|
linkHeadings: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const GitHubFlavoredMarkdown: QuartzTransformerPlugin<
|
export const GitHubFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options>> = (userOpts) => {
|
||||||
Partial<Options> | undefined
|
const opts = { ...defaultOptions, ...userOpts }
|
||||||
> = (userOpts) => {
|
|
||||||
const opts = {...defaultOptions, ...userOpts}
|
|
||||||
return {
|
return {
|
||||||
name: "GitHubFlavoredMarkdown",
|
name: "GitHubFlavoredMarkdown",
|
||||||
markdownPlugins() {
|
markdownPlugins() {
|
||||||
@ -32,20 +30,20 @@ export const GitHubFlavoredMarkdown: QuartzTransformerPlugin<
|
|||||||
{
|
{
|
||||||
behavior: "append",
|
behavior: "append",
|
||||||
properties: {
|
properties: {
|
||||||
"role": "anchor",
|
role: "anchor",
|
||||||
"ariaHidden": true,
|
ariaHidden: true,
|
||||||
"tabIndex": -1,
|
tabIndex: -1,
|
||||||
"data-no-popover": true,
|
"data-no-popover": true,
|
||||||
},
|
},
|
||||||
content: {
|
content: {
|
||||||
type: "element",
|
type: "element",
|
||||||
tagName: "svg",
|
tagName: "svg",
|
||||||
properties: {
|
properties: {
|
||||||
"width": 18,
|
width: 18,
|
||||||
"height": 18,
|
height: 18,
|
||||||
"viewBox": "0 0 24 24",
|
viewBox: "0 0 24 24",
|
||||||
"fill": "none",
|
fill: "none",
|
||||||
"stroke": "currentColor",
|
stroke: "currentColor",
|
||||||
"stroke-width": "2",
|
"stroke-width": "2",
|
||||||
"stroke-linecap": "round",
|
"stroke-linecap": "round",
|
||||||
"stroke-linejoin": "round",
|
"stroke-linejoin": "round",
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
export {FrontMatter} from "./frontmatter"
|
export { FrontMatter } from "./frontmatter"
|
||||||
export {GitHubFlavoredMarkdown} from "./gfm"
|
export { GitHubFlavoredMarkdown } from "./gfm"
|
||||||
export {Citations} from "./citations"
|
export { Citations } from "./citations"
|
||||||
export {CreatedModifiedDate} from "./lastmod"
|
export { CreatedModifiedDate } from "./lastmod"
|
||||||
export {Latex} from "./latex"
|
export { Latex } from "./latex"
|
||||||
export {Description} from "./description"
|
export { Description } from "./description"
|
||||||
export {CrawlLinks} from "./links"
|
export { CrawlLinks } from "./links"
|
||||||
export {ObsidianFlavoredMarkdown} from "./ofm"
|
export { ObsidianFlavoredMarkdown } from "./ofm"
|
||||||
export {OxHugoFlavouredMarkdown} from "./oxhugofm"
|
export { OxHugoFlavouredMarkdown } from "./oxhugofm"
|
||||||
export {SyntaxHighlighting} from "./syntax"
|
export { SyntaxHighlighting } from "./syntax"
|
||||||
export {TableOfContents} from "./toc"
|
export { TableOfContents } from "./toc"
|
||||||
export {HardLineBreaks} from "./linebreaks"
|
export { HardLineBreaks } from "./linebreaks"
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import fs from "fs"
|
import fs from "fs"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import {Repository} from "@napi-rs/simple-git"
|
import { Repository } from "@napi-rs/simple-git"
|
||||||
import {QuartzTransformerPlugin} from "../types"
|
import { QuartzTransformerPlugin } from "../types"
|
||||||
import chalk from "chalk"
|
import chalk from "chalk"
|
||||||
|
|
||||||
export interface Options {
|
export interface Options {
|
||||||
@ -27,10 +27,8 @@ function coerceDate(fp: string, d: any): Date {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type MaybeDate = undefined | string | number
|
type MaybeDate = undefined | string | number
|
||||||
export const CreatedModifiedDate: QuartzTransformerPlugin<
|
export const CreatedModifiedDate: QuartzTransformerPlugin<Partial<Options>> = (userOpts) => {
|
||||||
Partial<Options> | undefined
|
const opts = { ...defaultOptions, ...userOpts }
|
||||||
> = (userOpts) => {
|
|
||||||
const opts = {...defaultOptions, ...userOpts}
|
|
||||||
return {
|
return {
|
||||||
name: "CreatedModifiedDate",
|
name: "CreatedModifiedDate",
|
||||||
markdownPlugins() {
|
markdownPlugins() {
|
||||||
@ -43,9 +41,7 @@ export const CreatedModifiedDate: QuartzTransformerPlugin<
|
|||||||
let published: MaybeDate = undefined
|
let published: MaybeDate = undefined
|
||||||
|
|
||||||
const fp = file.data.filePath!
|
const fp = file.data.filePath!
|
||||||
const fullFp = path.isAbsolute(fp)
|
const fullFp = path.isAbsolute(fp) ? fp : path.posix.join(file.cwd, fp)
|
||||||
? fp
|
|
||||||
: path.posix.join(file.cwd, fp)
|
|
||||||
for (const source of opts.priority) {
|
for (const source of opts.priority) {
|
||||||
if (source === "filesystem") {
|
if (source === "filesystem") {
|
||||||
const st = await fs.promises.stat(fullFp)
|
const st = await fs.promises.stat(fullFp)
|
||||||
@ -66,9 +62,7 @@ export const CreatedModifiedDate: QuartzTransformerPlugin<
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
modified ||= await repo.getFileLatestModifiedDateAsync(
|
modified ||= await repo.getFileLatestModifiedDateAsync(file.data.filePath!)
|
||||||
file.data.filePath!,
|
|
||||||
)
|
|
||||||
} catch {
|
} catch {
|
||||||
console.log(
|
console.log(
|
||||||
chalk.yellow(
|
chalk.yellow(
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import remarkMath from "remark-math"
|
import remarkMath from "remark-math"
|
||||||
import rehypeKatex from "rehype-katex"
|
import rehypeKatex from "rehype-katex"
|
||||||
import rehypeMathjax from "rehype-mathjax/svg"
|
import rehypeMathjax from "rehype-mathjax/svg"
|
||||||
import {QuartzTransformerPlugin} from "../types"
|
import { QuartzTransformerPlugin } from "../types"
|
||||||
|
|
||||||
interface Options {
|
interface Options {
|
||||||
renderEngine: "katex" | "mathjax"
|
renderEngine: "katex" | "mathjax"
|
||||||
@ -22,7 +22,7 @@ export const Latex: QuartzTransformerPlugin<Partial<Options>> = (opts) => {
|
|||||||
},
|
},
|
||||||
htmlPlugins() {
|
htmlPlugins() {
|
||||||
if (engine === "katex") {
|
if (engine === "katex") {
|
||||||
return [[rehypeKatex, {output: "html"}]]
|
return [[rehypeKatex, { output: "html", macros }]]
|
||||||
} else {
|
} else {
|
||||||
return [[rehypeMathjax, { macros }]]
|
return [[rehypeMathjax, { macros }]]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {QuartzTransformerPlugin} from "../types"
|
import { QuartzTransformerPlugin } from "../types"
|
||||||
import remarkBreaks from "remark-breaks"
|
import remarkBreaks from "remark-breaks"
|
||||||
|
|
||||||
export const HardLineBreaks: QuartzTransformerPlugin = () => {
|
export const HardLineBreaks: QuartzTransformerPlugin = () => {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {QuartzTransformerPlugin} from "../types"
|
import { QuartzTransformerPlugin } from "../types"
|
||||||
import {
|
import {
|
||||||
FullSlug,
|
FullSlug,
|
||||||
RelativeURL,
|
RelativeURL,
|
||||||
@ -10,9 +10,9 @@ import {
|
|||||||
transformLink,
|
transformLink,
|
||||||
} from "../../util/path"
|
} from "../../util/path"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import {visit} from "unist-util-visit"
|
import { visit } from "unist-util-visit"
|
||||||
import isAbsoluteUrl from "is-absolute-url"
|
import isAbsoluteUrl from "is-absolute-url"
|
||||||
import {Root} from "hast"
|
import { Root } from "hast"
|
||||||
|
|
||||||
interface Options {
|
interface Options {
|
||||||
/** How to resolve Markdown paths */
|
/** How to resolve Markdown paths */
|
||||||
@ -32,10 +32,8 @@ const defaultOptions: Options = {
|
|||||||
externalLinkIcon: true,
|
externalLinkIcon: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CrawlLinks: QuartzTransformerPlugin<
|
export const CrawlLinks: QuartzTransformerPlugin<Partial<Options>> = (userOpts) => {
|
||||||
Partial<Options> | undefined
|
const opts = { ...defaultOptions, ...userOpts }
|
||||||
> = (userOpts) => {
|
|
||||||
const opts = {...defaultOptions, ...userOpts}
|
|
||||||
return {
|
return {
|
||||||
name: "LinkProcessing",
|
name: "LinkProcessing",
|
||||||
htmlPlugins(ctx) {
|
htmlPlugins(ctx) {
|
||||||
@ -100,9 +98,7 @@ export const CrawlLinks: QuartzTransformerPlugin<
|
|||||||
}
|
}
|
||||||
|
|
||||||
// don't process external links or intra-document anchors
|
// don't process external links or intra-document anchors
|
||||||
const isInternal = !(
|
const isInternal = !(isAbsoluteUrl(dest) || dest.startsWith("#"))
|
||||||
isAbsoluteUrl(dest) || dest.startsWith("#")
|
|
||||||
)
|
|
||||||
if (isInternal) {
|
if (isInternal) {
|
||||||
dest = node.properties.href = transformLink(
|
dest = node.properties.href = transformLink(
|
||||||
file.data.slug!,
|
file.data.slug!,
|
||||||
@ -112,10 +108,7 @@ export const CrawlLinks: QuartzTransformerPlugin<
|
|||||||
|
|
||||||
// url.resolve is considered legacy
|
// url.resolve is considered legacy
|
||||||
// WHATWG equivalent https://nodejs.dev/en/api/v18/url/#urlresolvefrom-to
|
// WHATWG equivalent https://nodejs.dev/en/api/v18/url/#urlresolvefrom-to
|
||||||
const url = new URL(
|
const url = new URL(dest, "https://base.com/" + stripSlashes(curSlug, true))
|
||||||
dest,
|
|
||||||
"https://base.com/" + stripSlashes(curSlug, true),
|
|
||||||
)
|
|
||||||
const canonicalDest = url.pathname
|
const canonicalDest = url.pathname
|
||||||
let [destCanonical, _destAnchor] = splitAnchor(canonicalDest)
|
let [destCanonical, _destAnchor] = splitAnchor(canonicalDest)
|
||||||
if (destCanonical.endsWith("/")) {
|
if (destCanonical.endsWith("/")) {
|
||||||
@ -123,9 +116,7 @@ export const CrawlLinks: QuartzTransformerPlugin<
|
|||||||
}
|
}
|
||||||
|
|
||||||
// need to decodeURIComponent here as WHATWG URL percent-encodes everything
|
// need to decodeURIComponent here as WHATWG URL percent-encodes everything
|
||||||
const full = decodeURIComponent(
|
const full = decodeURIComponent(stripSlashes(destCanonical, true)) as FullSlug
|
||||||
stripSlashes(destCanonical, true),
|
|
||||||
) as FullSlug
|
|
||||||
const simple = simplifySlug(full)
|
const simple = simplifySlug(full)
|
||||||
outgoing.add(simple)
|
outgoing.add(simple)
|
||||||
node.properties["data-slug"] = full
|
node.properties["data-slug"] = full
|
||||||
|
|||||||
@ -1,33 +1,22 @@
|
|||||||
import {QuartzTransformerPlugin} from "../types"
|
import { QuartzTransformerPlugin } from "../types"
|
||||||
import {
|
import { Root, Html, BlockContent, DefinitionContent, Paragraph, Code } from "mdast"
|
||||||
Root,
|
import { Element, Literal, Root as HtmlRoot } from "hast"
|
||||||
Html,
|
import { ReplaceFunction, findAndReplace as mdastFindReplace } from "mdast-util-find-and-replace"
|
||||||
BlockContent,
|
|
||||||
DefinitionContent,
|
|
||||||
Paragraph,
|
|
||||||
Code,
|
|
||||||
} from "mdast"
|
|
||||||
import {Element, Literal, Root as HtmlRoot} from "hast"
|
|
||||||
import {
|
|
||||||
ReplaceFunction,
|
|
||||||
findAndReplace as mdastFindReplace,
|
|
||||||
} from "mdast-util-find-and-replace"
|
|
||||||
import {slug as slugAnchor} from "github-slugger"
|
|
||||||
import rehypeRaw from "rehype-raw"
|
import rehypeRaw from "rehype-raw"
|
||||||
import {SKIP, visit} from "unist-util-visit"
|
import { SKIP, visit } from "unist-util-visit"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import {splitAnchor} from "../../util/path"
|
import { splitAnchor } from "../../util/path"
|
||||||
import {JSResource} from "../../util/resources"
|
import { JSResource } from "../../util/resources"
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import calloutScript from "../../components/scripts/callout.inline.ts"
|
import calloutScript from "../../components/scripts/callout.inline.ts"
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import checkboxScript from "../../components/scripts/checkbox.inline.ts"
|
import checkboxScript from "../../components/scripts/checkbox.inline.ts"
|
||||||
import {FilePath, pathToRoot, slugTag, slugifyFilePath} from "../../util/path"
|
import { FilePath, pathToRoot, slugTag, slugifyFilePath } from "../../util/path"
|
||||||
import {toHast} from "mdast-util-to-hast"
|
import { toHast } from "mdast-util-to-hast"
|
||||||
import {toHtml} from "hast-util-to-html"
|
import { toHtml } from "hast-util-to-html"
|
||||||
import {PhrasingContent} from "mdast-util-find-and-replace/lib"
|
import { PhrasingContent } from "mdast-util-find-and-replace/lib"
|
||||||
import {capitalize} from "../../util/lang"
|
import { capitalize } from "../../util/lang"
|
||||||
import {PluggableList} from "unified"
|
import { PluggableList } from "unified"
|
||||||
|
|
||||||
export interface Options {
|
export interface Options {
|
||||||
comments: boolean
|
comments: boolean
|
||||||
@ -101,8 +90,7 @@ const arrowMapping: Record<string, string> = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function canonicalizeCallout(calloutName: string): keyof typeof calloutMapping {
|
function canonicalizeCallout(calloutName: string): keyof typeof calloutMapping {
|
||||||
const normalizedCallout =
|
const normalizedCallout = calloutName.toLowerCase() as keyof typeof calloutMapping
|
||||||
calloutName.toLowerCase() as keyof typeof calloutMapping
|
|
||||||
// if callout is not recognized, make it a custom one
|
// if callout is not recognized, make it a custom one
|
||||||
return calloutMapping[normalizedCallout] ?? calloutName
|
return calloutMapping[normalizedCallout] ?? calloutName
|
||||||
}
|
}
|
||||||
@ -140,25 +128,20 @@ const calloutLineRegex = new RegExp(/^> *\[\!\w+\|?.*?\][+-]?.*$/gm)
|
|||||||
const tagRegex = new RegExp(
|
const tagRegex = new RegExp(
|
||||||
/(?:^| )#((?:[-_\p{L}\p{Emoji}\p{M}\d])+(?:\/[-_\p{L}\p{Emoji}\p{M}\d]+)*)/gu,
|
/(?:^| )#((?:[-_\p{L}\p{Emoji}\p{M}\d])+(?:\/[-_\p{L}\p{Emoji}\p{M}\d]+)*)/gu,
|
||||||
)
|
)
|
||||||
const blockReferenceRegex = new RegExp(/\^([-_A-Za-z0-9]+)$/, "g")
|
const blockReferenceRegex = new RegExp(/\^([-_A-Za-z0-9]+)$/g)
|
||||||
const ytLinkRegex =
|
const ytLinkRegex = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/
|
||||||
/^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/
|
|
||||||
const ytPlaylistLinkRegex = /[?&]list=([^#?&]*)/
|
const ytPlaylistLinkRegex = /[?&]list=([^#?&]*)/
|
||||||
const videoExtensionRegex = new RegExp(
|
const videoExtensionRegex = new RegExp(/\.(mp4|webm|ogg|avi|mov|flv|wmv|mkv|mpg|mpeg|3gp|m4v)$/)
|
||||||
/\.(mp4|webm|ogg|avi|mov|flv|wmv|mkv|mpg|mpeg|3gp|m4v)$/,
|
|
||||||
)
|
|
||||||
const wikilinkImageEmbedRegex = new RegExp(
|
const wikilinkImageEmbedRegex = new RegExp(
|
||||||
/^(?<alt>(?!^\d*x?\d*$).*?)?(\|?\s*?(?<width>\d+)(x(?<height>\d+))?)?$/,
|
/^(?<alt>(?!^\d*x?\d*$).*?)?(\|?\s*?(?<width>\d+)(x(?<height>\d+))?)?$/,
|
||||||
)
|
)
|
||||||
|
|
||||||
export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<
|
export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options>> = (userOpts) => {
|
||||||
Partial<Options> | undefined
|
const opts = { ...defaultOptions, ...userOpts }
|
||||||
> = (userOpts) => {
|
|
||||||
const opts = {...defaultOptions, ...userOpts}
|
|
||||||
|
|
||||||
const mdastToHtml = (ast: PhrasingContent | Paragraph) => {
|
const mdastToHtml = (ast: PhrasingContent | Paragraph) => {
|
||||||
const hast = toHast(ast, {allowDangerousHtml: true})!
|
const hast = toHast(ast, { allowDangerousHtml: true })!
|
||||||
return toHtml(hast, {allowDangerousHtml: true})
|
return toHtml(hast, { allowDangerousHtml: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -210,10 +193,8 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<
|
|||||||
const [rawFp, rawHeader, rawAlias]: (string | undefined)[] = capture
|
const [rawFp, rawHeader, rawAlias]: (string | undefined)[] = capture
|
||||||
|
|
||||||
const [fp, anchor] = splitAnchor(`${rawFp ?? ""}${rawHeader ?? ""}`)
|
const [fp, anchor] = splitAnchor(`${rawFp ?? ""}${rawHeader ?? ""}`)
|
||||||
const blockRef = Boolean(anchor?.startsWith("^")) ? "^" : ""
|
const blockRef = Boolean(rawHeader?.match(/^#?\^/)) ? "^" : ""
|
||||||
const displayAnchor = anchor
|
const displayAnchor = anchor ? `#${blockRef}${anchor.trim().replace(/^#+/, "")}` : ""
|
||||||
? `#${blockRef}${anchor.trim().replace(/^#+/, "")}`
|
|
||||||
: ""
|
|
||||||
const displayAlias = rawAlias ?? rawHeader?.replace("#", "|") ?? ""
|
const displayAlias = rawAlias ?? rawHeader?.replace("#", "|") ?? ""
|
||||||
const embedDisplay = value.startsWith("!") ? "!" : ""
|
const embedDisplay = value.startsWith("!") ? "!" : ""
|
||||||
|
|
||||||
@ -249,17 +230,7 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<
|
|||||||
if (value.startsWith("!")) {
|
if (value.startsWith("!")) {
|
||||||
const ext: string = path.extname(fp).toLowerCase()
|
const ext: string = path.extname(fp).toLowerCase()
|
||||||
const url = slugifyFilePath(fp as FilePath)
|
const url = slugifyFilePath(fp as FilePath)
|
||||||
if (
|
if ([".png", ".jpg", ".jpeg", ".gif", ".bmp", ".svg", ".webp"].includes(ext)) {
|
||||||
[
|
|
||||||
".png",
|
|
||||||
".jpg",
|
|
||||||
".jpeg",
|
|
||||||
".gif",
|
|
||||||
".bmp",
|
|
||||||
".svg",
|
|
||||||
".webp",
|
|
||||||
].includes(ext)
|
|
||||||
) {
|
|
||||||
const match = wikilinkImageEmbedRegex.exec(alias ?? "")
|
const match = wikilinkImageEmbedRegex.exec(alias ?? "")
|
||||||
const alt = match?.groups?.alt ?? ""
|
const alt = match?.groups?.alt ?? ""
|
||||||
const width = match?.groups?.width ?? "auto"
|
const width = match?.groups?.width ?? "auto"
|
||||||
@ -275,23 +246,13 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
} else if (
|
} else if ([".mp4", ".webm", ".ogv", ".mov", ".mkv"].includes(ext)) {
|
||||||
[".mp4", ".webm", ".ogv", ".mov", ".mkv"].includes(ext)
|
|
||||||
) {
|
|
||||||
return {
|
return {
|
||||||
type: "html",
|
type: "html",
|
||||||
value: `<video src="${url}" controls></video>`,
|
value: `<video src="${url}" controls></video>`,
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (
|
||||||
[
|
[".mp3", ".webm", ".wav", ".m4a", ".ogg", ".3gp", ".flac"].includes(ext)
|
||||||
".mp3",
|
|
||||||
".webm",
|
|
||||||
".wav",
|
|
||||||
".m4a",
|
|
||||||
".ogg",
|
|
||||||
".3gp",
|
|
||||||
".flac",
|
|
||||||
].includes(ext)
|
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
type: "html",
|
type: "html",
|
||||||
@ -306,8 +267,8 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<
|
|||||||
const block = anchor
|
const block = anchor
|
||||||
return {
|
return {
|
||||||
type: "html",
|
type: "html",
|
||||||
data: {hProperties: {transclude: true}},
|
data: { hProperties: { transclude: true } },
|
||||||
value: `<blockquote class="transclude" data-url="${url}" data-block="${block}"><a href="${
|
value: `<blockquote class="transclude" data-url="${url}" data-block="${block}" data-embed-alias="${alias}"><a href="${
|
||||||
url + anchor
|
url + anchor
|
||||||
}" class="transclude-inner">Transclude of ${url}${block}</a></blockquote>`,
|
}" class="transclude-inner">Transclude of ${url}${block}</a></blockquote>`,
|
||||||
}
|
}
|
||||||
@ -399,24 +360,18 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<
|
|||||||
if (typeof replace === "string") {
|
if (typeof replace === "string") {
|
||||||
node.value = node.value.replace(regex, replace)
|
node.value = node.value.replace(regex, replace)
|
||||||
} else {
|
} else {
|
||||||
node.value = node.value.replace(
|
node.value = node.value.replace(regex, (substring: string, ...args) => {
|
||||||
regex,
|
const replaceValue = replace(substring, ...args)
|
||||||
(substring: string, ...args) => {
|
if (typeof replaceValue === "string") {
|
||||||
const replaceValue = replace(substring, ...args)
|
return replaceValue
|
||||||
if (typeof replaceValue === "string") {
|
} else if (Array.isArray(replaceValue)) {
|
||||||
return replaceValue
|
return replaceValue.map(mdastToHtml).join("")
|
||||||
} else if (Array.isArray(replaceValue)) {
|
} else if (typeof replaceValue === "object" && replaceValue !== null) {
|
||||||
return replaceValue.map(mdastToHtml).join("")
|
return mdastToHtml(replaceValue)
|
||||||
} else if (
|
} else {
|
||||||
typeof replaceValue === "object" &&
|
return substring
|
||||||
replaceValue !== null
|
}
|
||||||
) {
|
})
|
||||||
return mdastToHtml(replaceValue)
|
|
||||||
} else {
|
|
||||||
return substring
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -429,11 +384,7 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<
|
|||||||
plugins.push(() => {
|
plugins.push(() => {
|
||||||
return (tree: Root, _file) => {
|
return (tree: Root, _file) => {
|
||||||
visit(tree, "image", (node, index, parent) => {
|
visit(tree, "image", (node, index, parent) => {
|
||||||
if (
|
if (parent && index != undefined && videoExtensionRegex.test(node.url)) {
|
||||||
parent &&
|
|
||||||
index != undefined &&
|
|
||||||
videoExtensionRegex.test(node.url)
|
|
||||||
) {
|
|
||||||
const newNode: Html = {
|
const newNode: Html = {
|
||||||
type: "html",
|
type: "html",
|
||||||
value: `<video controls src="${node.url}"></video>`,
|
value: `<video controls src="${node.url}"></video>`,
|
||||||
@ -457,10 +408,7 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<
|
|||||||
|
|
||||||
// find first line and callout content
|
// find first line and callout content
|
||||||
const [firstChild, ...calloutContent] = node.children
|
const [firstChild, ...calloutContent] = node.children
|
||||||
if (
|
if (firstChild.type !== "paragraph" || firstChild.children[0]?.type !== "text") {
|
||||||
firstChild.type !== "paragraph" ||
|
|
||||||
firstChild.children[0]?.type !== "text"
|
|
||||||
) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -471,31 +419,18 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<
|
|||||||
|
|
||||||
const match = firstLine.match(calloutRegex)
|
const match = firstLine.match(calloutRegex)
|
||||||
if (match && match.input) {
|
if (match && match.input) {
|
||||||
const [
|
const [calloutDirective, typeString, calloutMetaData, collapseChar] = match
|
||||||
calloutDirective,
|
const calloutType = canonicalizeCallout(typeString.toLowerCase())
|
||||||
typeString,
|
|
||||||
calloutMetaData,
|
|
||||||
collapseChar,
|
|
||||||
] = match
|
|
||||||
const calloutType = canonicalizeCallout(
|
|
||||||
typeString.toLowerCase(),
|
|
||||||
)
|
|
||||||
const collapse = collapseChar === "+" || collapseChar === "-"
|
const collapse = collapseChar === "+" || collapseChar === "-"
|
||||||
const defaultState =
|
const defaultState = collapseChar === "-" ? "collapsed" : "expanded"
|
||||||
collapseChar === "-" ? "collapsed" : "expanded"
|
const titleContent = match.input.slice(calloutDirective.length).trim()
|
||||||
const titleContent = match.input
|
const useDefaultTitle = titleContent === "" && restOfTitle.length === 0
|
||||||
.slice(calloutDirective.length)
|
|
||||||
.trim()
|
|
||||||
const useDefaultTitle =
|
|
||||||
titleContent === "" && restOfTitle.length === 0
|
|
||||||
const titleNode: Paragraph = {
|
const titleNode: Paragraph = {
|
||||||
type: "paragraph",
|
type: "paragraph",
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "text",
|
type: "text",
|
||||||
value: useDefaultTitle
|
value: useDefaultTitle ? capitalize(typeString) : titleContent + " ",
|
||||||
? capitalize(typeString)
|
|
||||||
: titleContent + " ",
|
|
||||||
},
|
},
|
||||||
...restOfTitle,
|
...restOfTitle,
|
||||||
],
|
],
|
||||||
@ -515,8 +450,7 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<
|
|||||||
</div>`,
|
</div>`,
|
||||||
}
|
}
|
||||||
|
|
||||||
const blockquoteContent: (BlockContent | DefinitionContent)[] =
|
const blockquoteContent: (BlockContent | DefinitionContent)[] = [titleHtml]
|
||||||
[titleHtml]
|
|
||||||
if (remainingText.length > 0) {
|
if (remainingText.length > 0) {
|
||||||
blockquoteContent.push({
|
blockquoteContent.push({
|
||||||
type: "paragraph",
|
type: "paragraph",
|
||||||
@ -544,7 +478,7 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<
|
|||||||
node.data = {
|
node.data = {
|
||||||
hProperties: {
|
hProperties: {
|
||||||
...(node.data?.hProperties ?? {}),
|
...(node.data?.hProperties ?? {}),
|
||||||
"className": classNames.join(" "),
|
className: classNames.join(" "),
|
||||||
"data-callout": calloutType,
|
"data-callout": calloutType,
|
||||||
"data-callout-fold": collapse,
|
"data-callout-fold": collapse,
|
||||||
"data-callout-metadata": calloutMetaData,
|
"data-callout-metadata": calloutMetaData,
|
||||||
@ -672,14 +606,10 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<
|
|||||||
plugins.push(() => {
|
plugins.push(() => {
|
||||||
return (tree: HtmlRoot) => {
|
return (tree: HtmlRoot) => {
|
||||||
visit(tree, "element", (node) => {
|
visit(tree, "element", (node) => {
|
||||||
if (
|
if (node.tagName === "img" && typeof node.properties.src === "string") {
|
||||||
node.tagName === "img" &&
|
|
||||||
typeof node.properties.src === "string"
|
|
||||||
) {
|
|
||||||
const match = node.properties.src.match(ytLinkRegex)
|
const match = node.properties.src.match(ytLinkRegex)
|
||||||
const videoId = match && match[2].length == 11 ? match[2] : null
|
const videoId = match && match[2].length == 11 ? match[2] : null
|
||||||
const playlistId =
|
const playlistId = node.properties.src.match(ytPlaylistLinkRegex)?.[1]
|
||||||
node.properties.src.match(ytPlaylistLinkRegex)?.[1]
|
|
||||||
if (videoId) {
|
if (videoId) {
|
||||||
// YouTube video (with optional playlist)
|
// YouTube video (with optional playlist)
|
||||||
node.tagName = "iframe"
|
node.tagName = "iframe"
|
||||||
@ -713,10 +643,7 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<
|
|||||||
plugins.push(() => {
|
plugins.push(() => {
|
||||||
return (tree: HtmlRoot, _file) => {
|
return (tree: HtmlRoot, _file) => {
|
||||||
visit(tree, "element", (node) => {
|
visit(tree, "element", (node) => {
|
||||||
if (
|
if (node.tagName === "input" && node.properties.type === "checkbox") {
|
||||||
node.tagName === "input" &&
|
|
||||||
node.properties.type === "checkbox"
|
|
||||||
) {
|
|
||||||
const isChecked = node.properties?.checked ?? false
|
const isChecked = node.properties?.checked ?? false
|
||||||
node.properties = {
|
node.properties = {
|
||||||
type: "checkbox",
|
type: "checkbox",
|
||||||
@ -778,7 +705,7 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return {js}
|
return { js }
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {QuartzTransformerPlugin} from "../types"
|
import { QuartzTransformerPlugin } from "../types"
|
||||||
|
|
||||||
export interface Options {
|
export interface Options {
|
||||||
/** Replace {{ relref }} with quartz wikilinks []() */
|
/** Replace {{ relref }} with quartz wikilinks []() */
|
||||||
@ -22,10 +22,7 @@ const defaultOptions: Options = {
|
|||||||
replaceOrgLatex: true,
|
replaceOrgLatex: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
const relrefRegex = new RegExp(
|
const relrefRegex = new RegExp(/\[([^\]]+)\]\(\{\{< relref "([^"]+)" >\}\}\)/, "g")
|
||||||
/\[([^\]]+)\]\(\{\{< relref "([^"]+)" >\}\}\)/,
|
|
||||||
"g",
|
|
||||||
)
|
|
||||||
const predefinedHeadingIdRegex = new RegExp(/(.*) {#(?:.*)}/, "g")
|
const predefinedHeadingIdRegex = new RegExp(/(.*) {#(?:.*)}/, "g")
|
||||||
const hugoShortcodeRegex = new RegExp(/{{(.*)}}/, "g")
|
const hugoShortcodeRegex = new RegExp(/{{(.*)}}/, "g")
|
||||||
const figureTagRegex = new RegExp(/< ?figure src="(.*)" ?>/, "g")
|
const figureTagRegex = new RegExp(/< ?figure src="(.*)" ?>/, "g")
|
||||||
@ -50,10 +47,8 @@ const quartzLatexRegex = new RegExp(/\$\$[\s\S]*?\$\$|\$.*?\$/, "g")
|
|||||||
* markdown to make it compatible with quartz but the list of changes applied it
|
* markdown to make it compatible with quartz but the list of changes applied it
|
||||||
* is not exhaustive.
|
* is not exhaustive.
|
||||||
* */
|
* */
|
||||||
export const OxHugoFlavouredMarkdown: QuartzTransformerPlugin<
|
export const OxHugoFlavouredMarkdown: QuartzTransformerPlugin<Partial<Options>> = (userOpts) => {
|
||||||
Partial<Options> | undefined
|
const opts = { ...defaultOptions, ...userOpts }
|
||||||
> = (userOpts) => {
|
|
||||||
const opts = {...defaultOptions, ...userOpts}
|
|
||||||
return {
|
return {
|
||||||
name: "OxHugoFlavouredMarkdown",
|
name: "OxHugoFlavouredMarkdown",
|
||||||
textTransform(_ctx, src) {
|
textTransform(_ctx, src) {
|
||||||
|
|||||||
@ -1,8 +1,5 @@
|
|||||||
import {QuartzTransformerPlugin} from "../types"
|
import { QuartzTransformerPlugin } from "../types"
|
||||||
import rehypePrettyCode, {
|
import rehypePrettyCode, { Options as CodeOptions, Theme as CodeTheme } from "rehype-pretty-code"
|
||||||
Options as CodeOptions,
|
|
||||||
Theme as CodeTheme,
|
|
||||||
} from "rehype-pretty-code"
|
|
||||||
|
|
||||||
interface Theme extends Record<string, CodeTheme> {
|
interface Theme extends Record<string, CodeTheme> {
|
||||||
light: CodeTheme
|
light: CodeTheme
|
||||||
@ -22,10 +19,8 @@ const defaultOptions: Options = {
|
|||||||
keepBackground: false,
|
keepBackground: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SyntaxHighlighting: QuartzTransformerPlugin<Options> = (
|
export const SyntaxHighlighting: QuartzTransformerPlugin<Partial<Options>> = (userOpts) => {
|
||||||
userOpts?: Partial<Options>,
|
const opts: CodeOptions = { ...defaultOptions, ...userOpts }
|
||||||
) => {
|
|
||||||
const opts: Partial<CodeOptions> = {...defaultOptions, ...userOpts}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: "SyntaxHighlighting",
|
name: "SyntaxHighlighting",
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import {QuartzTransformerPlugin} from "../types"
|
import { QuartzTransformerPlugin } from "../types"
|
||||||
import {Root} from "mdast"
|
import { Root } from "mdast"
|
||||||
import {visit} from "unist-util-visit"
|
import { visit } from "unist-util-visit"
|
||||||
import {toString} from "mdast-util-to-string"
|
import { toString } from "mdast-util-to-string"
|
||||||
import Slugger from "github-slugger"
|
import Slugger from "github-slugger"
|
||||||
|
|
||||||
export interface Options {
|
export interface Options {
|
||||||
@ -12,7 +12,7 @@ export interface Options {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const defaultOptions: Options = {
|
const defaultOptions: Options = {
|
||||||
maxDepth: 2, // 3
|
maxDepth: 3,
|
||||||
minEntries: 1,
|
minEntries: 1,
|
||||||
showByDefault: true,
|
showByDefault: true,
|
||||||
collapseByDefault: false,
|
collapseByDefault: false,
|
||||||
@ -25,18 +25,15 @@ interface TocEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const slugAnchor = new Slugger()
|
const slugAnchor = new Slugger()
|
||||||
export const TableOfContents: QuartzTransformerPlugin<
|
export const TableOfContents: QuartzTransformerPlugin<Partial<Options>> = (userOpts) => {
|
||||||
Partial<Options> | undefined
|
const opts = { ...defaultOptions, ...userOpts }
|
||||||
> = (userOpts) => {
|
|
||||||
const opts = {...defaultOptions, ...userOpts}
|
|
||||||
return {
|
return {
|
||||||
name: "TableOfContents",
|
name: "TableOfContents",
|
||||||
markdownPlugins() {
|
markdownPlugins() {
|
||||||
return [
|
return [
|
||||||
() => {
|
() => {
|
||||||
return async (tree: Root, file) => {
|
return async (tree: Root, file) => {
|
||||||
const display =
|
const display = file.data.frontmatter?.enableToc ?? opts.showByDefault
|
||||||
file.data.frontmatter?.enableToc ?? opts.showByDefault
|
|
||||||
if (display) {
|
if (display) {
|
||||||
slugAnchor.reset()
|
slugAnchor.reset()
|
||||||
const toc: TocEntry[] = []
|
const toc: TocEntry[] = []
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import {PluggableList} from "unified"
|
import { PluggableList } from "unified"
|
||||||
import {StaticResources} from "../util/resources"
|
import { StaticResources } from "../util/resources"
|
||||||
import {ProcessedContent} from "./vfile"
|
import { ProcessedContent } from "./vfile"
|
||||||
import {QuartzComponent} from "../components/types"
|
import { QuartzComponent } from "../components/types"
|
||||||
import {FilePath} from "../util/path"
|
import { FilePath } from "../util/path"
|
||||||
import {BuildCtx} from "../util/ctx"
|
import { BuildCtx } from "../util/ctx"
|
||||||
import DepGraph from "../depgraph"
|
import DepGraph from "../depgraph"
|
||||||
|
|
||||||
export interface PluginTypes {
|
export interface PluginTypes {
|
||||||
@ -37,11 +37,7 @@ export type QuartzEmitterPlugin<Options extends OptionType = undefined> = (
|
|||||||
) => QuartzEmitterPluginInstance
|
) => QuartzEmitterPluginInstance
|
||||||
export type QuartzEmitterPluginInstance = {
|
export type QuartzEmitterPluginInstance = {
|
||||||
name: string
|
name: string
|
||||||
emit(
|
emit(ctx: BuildCtx, content: ProcessedContent[], resources: StaticResources): Promise<FilePath[]>
|
||||||
ctx: BuildCtx,
|
|
||||||
content: ProcessedContent[],
|
|
||||||
resources: StaticResources,
|
|
||||||
): Promise<FilePath[]>
|
|
||||||
getQuartzComponents(ctx: BuildCtx): QuartzComponent[]
|
getQuartzComponents(ctx: BuildCtx): QuartzComponent[]
|
||||||
getDependencyGraph?(
|
getDependencyGraph?(
|
||||||
ctx: BuildCtx,
|
ctx: BuildCtx,
|
||||||
|
|||||||
@ -1,13 +1,11 @@
|
|||||||
import {Node, Parent} from "hast"
|
import { Node, Parent } from "hast"
|
||||||
import {Data, VFile} from "vfile"
|
import { Data, VFile } from "vfile"
|
||||||
|
|
||||||
export type QuartzPluginData = Data
|
export type QuartzPluginData = Data
|
||||||
export type ProcessedContent = [Node, VFile]
|
export type ProcessedContent = [Node, VFile]
|
||||||
|
|
||||||
export function defaultProcessedContent(
|
export function defaultProcessedContent(vfileData: Partial<QuartzPluginData>): ProcessedContent {
|
||||||
vfileData: Partial<QuartzPluginData>,
|
const root: Parent = { type: "root", children: [] }
|
||||||
): ProcessedContent {
|
|
||||||
const root: Parent = {type: "root", children: []}
|
|
||||||
const vfile = new VFile("")
|
const vfile = new VFile("")
|
||||||
vfile.data = vfileData
|
vfile.data = vfileData
|
||||||
return [root, vfile]
|
return [root, vfile]
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import {QuartzConfig} from "../cfg"
|
import { QuartzConfig } from "../cfg"
|
||||||
import {FullSlug} from "./path"
|
import { FullSlug } from "./path"
|
||||||
|
|
||||||
export interface Argv {
|
export interface Argv {
|
||||||
directory: string
|
directory: string
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import path from "path"
|
import path from "path"
|
||||||
import {FilePath} from "./path"
|
import { FilePath } from "./path"
|
||||||
import {globby} from "globby"
|
import { globby } from "globby"
|
||||||
|
|
||||||
export function toPosixPath(fp: string): string {
|
export function toPosixPath(fp: string): string {
|
||||||
return fp.split(path.sep).join("/")
|
return fp.split(path.sep).join("/")
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import {Components, Jsx, toJsxRuntime} from "hast-util-to-jsx-runtime"
|
import { Components, Jsx, toJsxRuntime } from "hast-util-to-jsx-runtime"
|
||||||
import {Node, Root} from "hast"
|
import { Node, Root } from "hast"
|
||||||
import {Fragment, jsx, jsxs} from "preact/jsx-runtime"
|
import { Fragment, jsx, jsxs } from "preact/jsx-runtime"
|
||||||
import {trace} from "./trace"
|
import { trace } from "./trace"
|
||||||
import {type FilePath} from "./path"
|
import { type FilePath } from "./path"
|
||||||
|
|
||||||
const customComponents: Components = {
|
const customComponents: Components = {
|
||||||
table: (props) => (
|
table: (props) => (
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {Spinner} from "cli-spinner"
|
import { Spinner } from "cli-spinner"
|
||||||
|
|
||||||
export class QuartzLogger {
|
export class QuartzLogger {
|
||||||
verbose: boolean
|
verbose: boolean
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user