retypeset-odyssey 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +168 -0
- package/astro.config.ts +18 -0
- package/default-config.yaml +136 -0
- package/discover-collections.ts +160 -0
- package/integration.ts +394 -0
- package/package.json +105 -0
- package/patches/@qwik.dev__partytown@0.11.2.patch +98 -0
- package/public/_redirects +819 -0
- package/public/feeds/atom-style.xsl +105 -0
- package/public/feeds/rss-style.xsl +105 -0
- package/public/fonts/EarlySummer-VF-Split/00785494587e3487ac63a0e7e4fa30f0.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/08e5d941a4c76fad7b68e7a937ebb21f.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/1268e5072156188d601f1eeb4473655d.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/12a385475353c815d7a5add53ee51e37.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/12b11ca08223c65a21fc731d59dcfc11.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/16d6676d3cb645c520ee6df8a1f89afd.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/2912a75ffef95e7a5ae9e2b2311ad61d.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/298d96ea561e419a4104bc9fc18499ce.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/2a2c71acc17ec39f6780835899e53096.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/2a7e2d0e59d3f638074c50fab39fdef1.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/36931fc4370e1670ed76af5d3feccba2.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/3a68fdc792e4a9e0399a04e32d0cc2e3.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/4054d6a4d6b37719b51e0f71da6e7cd9.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/429cb25f825c3cbde6bfac5b36ae9675.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/42a9efc11298368ecdc1b85ab46f0b4f.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/432018d2bdc9df92a7662056eb2b1261.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/44a6fb782f2a01560faa0f95248b60ef.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/45367b060e8ba0aa2507e6b91b86620b.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/571db7564bda7c1a93542881b8976f4b.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/58d55eeef4cf455e86a1142b1f3110d3.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/59ea41e77309160a0f63cdc76a010202.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/5d19d9174e568db4755981aa2e4ab380.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/5e811eb3b4175ee93d7ec000bf4631c2.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/6268e0cd5d66d6fe05b331f259e7b9e4.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/6549844aa3d833ca06a68a8e839db465.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/714b459658a7321ceeb1e1386ce165c2.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/7511d97a469915013683eae06cb21cd9.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/7784b4ebe543d13f62f6f6e05beb0b2e.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/77c9bea70b3c6ab24e1497d5468c825b.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/789ebea9e81df623e930b86de98fbfab.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/885bb7ab0717e8a47fc17f953adcdbf1.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/896c58aff69a9a857764cee0663bc56d.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/8fb6fc01c59d1e3ad1910b58dec7f5e7.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/95be5462b91b9a0458797cdc89d94cb5.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/9a5b2724f983ca0fc0d5ff8d10c41396.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/9ffe17f9c0e4cc4356cb3f08ffdb9c6d.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/EarlySummer-VF-Subset.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/EarlySummerSerif License.txt +91 -0
- package/public/fonts/EarlySummer-VF-Split/a097ef49be62cd2565aca45600e1e3ac.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/a17a1ae6063088e5b3a48c06b816929a.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/a83fdcfc5ecf2f6996704b0c02758689.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/a8cf15ff9b71e59407d8406866ff6f99.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/af530ed51dd519e4456f8a5e259e908b.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/b195a8924915deec4aa9c3ec777cc93f.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/b4b6bb5df9239dd67b52ca858fd2a506.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/b7592e1e027923f19e0e55dfdac69668.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/b965859f69d8ccceaf0e2d6292afbcfb.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/bbe9333f1ff242bd96ecb23ff9e723b1.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/be758580e295339ea98f0240b9869f24.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/c07099e1d025617f6d40966986e1941b.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/c1b593dda62fdeb7dde3af02016da282.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/c89f0335910a68a0958f2846108370e8.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/ca49aa409fdedd3f2f894cd20a16640a.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/ccd4a28d2f63797e0183c87792e20b75.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/d2718da923fce8e7ea229d65e306e92c.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/d893e9b307d96041e9cfcbd03761b9f4.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/dafaedaee41b75e21479d4ff324b6a34.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/db392af65f1867e5fd580eed2195df99.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/dc7c73a9e5577143ccd11e05ab55cb39.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/de396881189f747eba67685298363242.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/df625b213228bba22a7733d4eff8f148.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/e6e60b384f220b893ef31a926ece829a.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/e6e8ce2c5972ab665630bb705383d0fb.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/e963c7ed7104c2d6d68fcb5f952fe2f5.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/e966b23b4cd7783f43e31032d41784f4.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/edaac57c3856ec13128f4c6c3e00975c.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/ee54e0d86edf068c6c9cbddb76a856fe.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/f612c78a5544ff2dd3e8296ac3e58344.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/f9e539bd9b7bf999c3da82f5403ec3b6.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/fa5863b923ac15993c52a619f699ee63.woff2 +0 -0
- package/public/fonts/EarlySummer-VF-Split/fc759e56ec6f6e6d3d4cb163d62fb557.woff2 +0 -0
- package/public/fonts/Font Subset List/CJK Common Characters.txt +7534 -0
- package/public/fonts/Font Subset List/EarlySummer Subset.txt +3 -0
- package/public/fonts/Font Subset List/Japanese Kana + Korean Letters.txt +6123 -0
- package/public/fonts/Font Subset List/Latin + Cyrillic + Greek + Arabic Glyphs.txt +121 -0
- package/public/fonts/Font Subset List/unicode_range.py +49 -0
- package/public/fonts/NotoSansSC-Bold.otf +0 -0
- package/public/fonts/NotoSansSC-Regular.otf +0 -0
- package/public/fonts/STIX-Italic-VF.woff2 +0 -0
- package/public/fonts/STIX-VF.woff2 +0 -0
- package/public/fonts/Snell-Black-SF.woff2 +0 -0
- package/public/fonts/Snell-Bold-SF.woff2 +0 -0
- package/public/giscus/theme-dark.css +208 -0
- package/public/giscus/theme-light.css +208 -0
- package/public/icons/favicon.svg +4 -0
- package/public/icons/og-logo.png +0 -0
- package/public/robots.txt +4 -0
- package/public/sounds/tap_01.wav +0 -0
- package/public/sounds/tap_02.wav +0 -0
- package/public/sounds/tap_03.wav +0 -0
- package/public/sounds/tap_04.wav +0 -0
- package/public/sounds/tap_05.wav +0 -0
- package/public/sounds/type_01.wav +0 -0
- package/public/sounds/type_02.wav +0 -0
- package/public/sounds/type_03.wav +0 -0
- package/public/sounds/type_04.wav +0 -0
- package/public/sounds/type_05.wav +0 -0
- package/scripts/apply-lqip.ts +276 -0
- package/scripts/format-posts.ts +105 -0
- package/scripts/migration/README.md +52 -0
- package/scripts/migration/migrate-hexo.ts +185 -0
- package/scripts/migration/validate-abbrlinks.ts +161 -0
- package/scripts/new-post.ts +52 -0
- package/scripts/seo/generate-legacy-redirects.ts +407 -0
- package/scripts/update-theme.ts +46 -0
- package/src/assets/icons/copy-check.svg +3 -0
- package/src/assets/icons/copy-icon.svg +4 -0
- package/src/assets/icons/go-back.svg +3 -0
- package/src/assets/icons/heading-anchor.svg +4 -0
- package/src/assets/icons/lang-en.svg +3 -0
- package/src/assets/icons/lang-ja.svg +5 -0
- package/src/assets/icons/lang-zh.svg +5 -0
- package/src/assets/icons/language-switcher.svg +3 -0
- package/src/assets/icons/pin-icon.svg +3 -0
- package/src/assets/icons/search-icon.svg +3 -0
- package/src/assets/icons/theme-toggle.svg +3 -0
- package/src/assets/icons/toc-icon.svg +3 -0
- package/src/assets/icons/top-icon.svg +3 -0
- package/src/assets/lqip-map.json +10 -0
- package/src/components/Button.astro +152 -0
- package/src/components/CategoryList.astro +66 -0
- package/src/components/Comment/Giscus.astro +119 -0
- package/src/components/Comment/Index.astro +30 -0
- package/src/components/Comment/Twikoo.astro +114 -0
- package/src/components/Comment/Waline.astro +149 -0
- package/src/components/FloatingButtons.astro +101 -0
- package/src/components/Footer.astro +74 -0
- package/src/components/Header.astro +62 -0
- package/src/components/JournalList.astro +56 -0
- package/src/components/Navbar.astro +69 -0
- package/src/components/NoteList.astro +56 -0
- package/src/components/Pagination.astro +267 -0
- package/src/components/PostDate.astro +80 -0
- package/src/components/PostList.astro +87 -0
- package/src/components/SearchModal.astro +340 -0
- package/src/components/TagList.astro +135 -0
- package/src/components/Widgets/BackButton.astro +43 -0
- package/src/components/Widgets/CodeCopyButton.astro +47 -0
- package/src/components/Widgets/GithubCard.astro +110 -0
- package/src/components/Widgets/ImageZoom.astro +135 -0
- package/src/components/Widgets/MediaEmbed.astro +127 -0
- package/src/components/Widgets/SoundEffect.astro +179 -0
- package/src/components/Widgets/TOC.astro +198 -0
- package/src/config-schema.ts +164 -0
- package/src/config.ts +127 -0
- package/src/config.ts.example +205 -0
- package/src/content/about/_example-about-en.md +6 -0
- package/src/content/about/about-en.md +21 -0
- package/src/content/about/about-ja.md +21 -0
- package/src/content/about/about-zh.md +24 -0
- package/src/content.config.ts +247 -0
- package/src/env.d.ts +25 -0
- package/src/i18n/config.ts +65 -0
- package/src/i18n/lang.ts +70 -0
- package/src/i18n/path.ts +160 -0
- package/src/i18n/ui.ts +214 -0
- package/src/layouts/Head.astro +203 -0
- package/src/layouts/Layout.astro +69 -0
- package/src/pages/404.astro +20 -0
- package/src/pages/[...lang]/[...page].astro +48 -0
- package/src/pages/[...lang]/about.astro +28 -0
- package/src/pages/[...lang]/atom.xml.ts +14 -0
- package/src/pages/[...lang]/categories/index.astro +35 -0
- package/src/pages/[...lang]/journals/[slug].astro +89 -0
- package/src/pages/[...lang]/journals/index.astro +55 -0
- package/src/pages/[...lang]/journals/page/[page].astro +66 -0
- package/src/pages/[...lang]/notes/[slug].astro +88 -0
- package/src/pages/[...lang]/notes/index.astro +55 -0
- package/src/pages/[...lang]/notes/page/[page].astro +66 -0
- package/src/pages/[...lang]/posts/[slug].astro +101 -0
- package/src/pages/[...lang]/rss.xml.ts +14 -0
- package/src/pages/[...lang]/search.astro +65 -0
- package/src/pages/[...lang]/tags/[tag].astro +53 -0
- package/src/pages/[...lang]/tags/index.astro +36 -0
- package/src/pages/_dynamic/list.astro +101 -0
- package/src/pages/_dynamic/slug.astro +100 -0
- package/src/pages/og/[...image].ts +114 -0
- package/src/pages/robots.txt.ts +20 -0
- package/src/plugins/rehype-code-copy-button.mjs +82 -0
- package/src/plugins/rehype-external-links.mjs +18 -0
- package/src/plugins/rehype-heading-anchor.mjs +55 -0
- package/src/plugins/rehype-image-processor.mjs +77 -0
- package/src/plugins/remark-container-directives.mjs +135 -0
- package/src/plugins/remark-leaf-directives.mjs +184 -0
- package/src/plugins/remark-reading-time.mjs +11 -0
- package/src/styles/comment.css +205 -0
- package/src/styles/extension.css +180 -0
- package/src/styles/font.css +111 -0
- package/src/styles/global.css +91 -0
- package/src/styles/lqip.css +71 -0
- package/src/styles/markdown.css +276 -0
- package/src/styles/transition.css +173 -0
- package/src/types/global.d.ts +22 -0
- package/src/types/index.d.ts +111 -0
- package/src/utils/cache.ts +32 -0
- package/src/utils/content.ts +819 -0
- package/src/utils/description.ts +147 -0
- package/src/utils/dynamic-collections.ts +155 -0
- package/src/utils/feed.ts +238 -0
- package/src/utils/page.ts +107 -0
- package/tsconfig.json +13 -0
- package/uno.config.ts +75 -0
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
2
|
+
<g transform="translate(-0.07,20.92) scale(0.0238,-0.0238)">
|
|
3
|
+
<path d="M425 765C387 765 361 766 334 770V702C359 706 379 707 423 707C434 604 447 543 475 464C510 365 550 290 605 216C530 131 417 53 276 -11C297 -29 307 -42 321 -65C387 -33 443 1 515 53C561 86 581 105 643 168C669 135 705 99 745 63C802 13 851 -21 927 -62C940 -33 950 -18 969 3C846 60 769 118 684 215C724 267 773 350 802 418C841 506 877 627 894 726C900 757 900 757 903 769C877 766 855 765 824 765ZM828 707C797 567 771 491 721 391C695 338 673 301 645 264C594 335 555 408 527 487C503 555 491 617 482 707ZM45 523C120 482 171 445 232 386L271 445C201 502 160 531 84 575ZM92 769C159 728 211 686 270 626L311 679C258 730 204 771 134 814ZM235 297C179 171 107 54 54 7L111 -48C112 -45 127 -24 153 15C194 75 245 167 285 255Z"/>
|
|
4
|
+
</g>
|
|
5
|
+
</svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
2
|
+
<path d="M23.7,8.1l-2.2-2.2-3.3-3.3L15.9.3C9,6.4,6.5,7,.4,7.5l6.4,6.4,1,1-6.5,6.9.9.9,6.9-6.5,1,1,6.4,6.4c.5-6.1,1.1-8.6,7.2-15.5ZM7.9,12.7l-4.4-4.4c4.1-.5,5.9-1,12-6l1.4,1.4,3.3,3.3,1.4,1.4c-5,6.1-5.5,7.9-6,12l-4.4-4.4-3.3-3.3Z"/>
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
2
|
+
<path d="m23.37 23.12-5.13-5.57c3.28-3.96 2.96-9.92-.9-13.78C13.25-.33 6.8-.45 2.87 3.48s-3.81 10.38.28 14.47c3.86 3.86 9.82 4.19 13.78.9l5.58 5.13zM3.61 17.49C.26 14.14.54 8.48 4.21 4.82c3.67-3.67 9.32-3.95 12.67-.6s3.07 9.01-.6 12.67c-3.67 3.67-9.32 3.95-12.67.6"/>
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"/_astro/1-dark.BivCfSmu_Z1BFPSa.webp": "55644522",
|
|
3
|
+
"/_astro/1-light.ClVuZxd5_Z1g9Oj3.webp": "bcddfed5",
|
|
4
|
+
"/_astro/2-dark.prgZMrfY_Z1exqGN.webp": "55644523",
|
|
5
|
+
"/_astro/2-light.DLXS38ks_ZXNxHS.webp": "bbddded6",
|
|
6
|
+
"/_astro/3-dark.CGQcMuXC_Z28xNQs.webp": "55644522",
|
|
7
|
+
"/_astro/3-light.8aAAK8Ad_2oyxts.webp": "bbdfdad5",
|
|
8
|
+
"/_astro/4-dark.CKqTknpt_2qCYkU.webp": "55644523",
|
|
9
|
+
"/_astro/4-light.DIUeDw9p_ZrQwsm.webp": "bcdffede"
|
|
10
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { Language } from '@/i18n/config'
|
|
3
|
+
import LangEnIcon from '@/assets/icons/lang-en.svg'
|
|
4
|
+
import LangJaIcon from '@/assets/icons/lang-ja.svg'
|
|
5
|
+
import LangZhIcon from '@/assets/icons/lang-zh.svg'
|
|
6
|
+
import SearchIcon from '@/assets/icons/search-icon.svg'
|
|
7
|
+
import ThemeToggleIcon from '@/assets/icons/theme-toggle.svg'
|
|
8
|
+
import { allLocales, moreLocales, themeConfig } from '@/config'
|
|
9
|
+
import { getNextGlobalLangPath, getNextSupportedLangPath } from '@/i18n/path'
|
|
10
|
+
import { getPageInfo } from '@/utils/page'
|
|
11
|
+
|
|
12
|
+
interface Props {
|
|
13
|
+
supportedLangs: Language[]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const {
|
|
17
|
+
light: { background: lightMode },
|
|
18
|
+
dark: { background: darkMode },
|
|
19
|
+
} = themeConfig.color
|
|
20
|
+
|
|
21
|
+
const { supportedLangs } = Astro.props
|
|
22
|
+
const currentPath = Astro.url.pathname
|
|
23
|
+
const { currentLang } = getPageInfo(currentPath)
|
|
24
|
+
|
|
25
|
+
const effectiveLangs = supportedLangs.length > 0 ? supportedLangs : allLocales
|
|
26
|
+
const showLanguageSwitcher = moreLocales.length > 0 && effectiveLangs.length > 1
|
|
27
|
+
|
|
28
|
+
// Choose a language switch list according to supported languages (when available)
|
|
29
|
+
const nextUrl = supportedLangs.length > 1
|
|
30
|
+
? getNextSupportedLangPath(currentPath, supportedLangs)
|
|
31
|
+
: getNextGlobalLangPath(currentPath)
|
|
32
|
+
|
|
33
|
+
// Determine which icon to show based on current language
|
|
34
|
+
// Show the icon of the NEXT language user will switch to
|
|
35
|
+
const langIconMap: Record<string, typeof LangEnIcon> = {
|
|
36
|
+
'zh': LangEnIcon, // zh → en, show "A"
|
|
37
|
+
'en': LangJaIcon, // en → ja, show "あ"
|
|
38
|
+
'ja': LangZhIcon, // ja → zh, show "汉"
|
|
39
|
+
}
|
|
40
|
+
const CurrentLangIcon = langIconMap[currentLang] || LangEnIcon
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
<div
|
|
44
|
+
class:list={[
|
|
45
|
+
'absolute right-7.25vw top-14.6 flex gap-6 min-[823px]:max-lg:right-[calc(50vw-22rem)]',
|
|
46
|
+
'lg:(fixed bottom-[min(10.27rem+1.92vw,12rem)] right-[max(5rem,calc(50vw-35rem))] top-auto w-14rem)',
|
|
47
|
+
]}
|
|
48
|
+
>
|
|
49
|
+
<!-- Search Button -->
|
|
50
|
+
<button
|
|
51
|
+
id="search-button"
|
|
52
|
+
class="aspect-square w-4 c-secondary active:scale-90 hover:c-primary"
|
|
53
|
+
aria-label="Search"
|
|
54
|
+
>
|
|
55
|
+
<SearchIcon aria-hidden="true" fill="currentColor" />
|
|
56
|
+
</button>
|
|
57
|
+
|
|
58
|
+
<!-- Language Switcher -->
|
|
59
|
+
{showLanguageSwitcher && (
|
|
60
|
+
<a
|
|
61
|
+
id="language-switcher"
|
|
62
|
+
href={nextUrl}
|
|
63
|
+
class="aspect-square w-4 c-secondary active:scale-90 hover:c-primary"
|
|
64
|
+
aria-label="Switch website language"
|
|
65
|
+
>
|
|
66
|
+
<CurrentLangIcon
|
|
67
|
+
aria-hidden="true"
|
|
68
|
+
fill="currentColor"
|
|
69
|
+
/>
|
|
70
|
+
</a>
|
|
71
|
+
)}
|
|
72
|
+
|
|
73
|
+
<!-- Theme Toggle -->
|
|
74
|
+
<button
|
|
75
|
+
id="theme-toggle-button"
|
|
76
|
+
class="aspect-square w-4 c-secondary active:scale-90 hover:c-primary"
|
|
77
|
+
data-light-mode={lightMode}
|
|
78
|
+
data-dark-mode={darkMode}
|
|
79
|
+
aria-label="Switch light/dark theme"
|
|
80
|
+
>
|
|
81
|
+
<ThemeToggleIcon
|
|
82
|
+
aria-hidden="true"
|
|
83
|
+
fill="currentColor"
|
|
84
|
+
/>
|
|
85
|
+
</button>
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
<!-- Theme Toggle Script >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -->
|
|
89
|
+
<script>
|
|
90
|
+
// Apply theme changes
|
|
91
|
+
function applyTheme(isDark: boolean) {
|
|
92
|
+
document.documentElement.classList.toggle('dark', isDark)
|
|
93
|
+
|
|
94
|
+
// Update meta theme-color tag
|
|
95
|
+
const metaThemeColor = document.head.querySelector('meta[name="theme-color"]')
|
|
96
|
+
const themeButton = document.getElementById('theme-toggle-button')
|
|
97
|
+
const lightMode = themeButton?.dataset.lightMode
|
|
98
|
+
const darkMode = themeButton?.dataset.darkMode
|
|
99
|
+
|
|
100
|
+
if (metaThemeColor && lightMode && darkMode) {
|
|
101
|
+
metaThemeColor.setAttribute('content', isDark ? darkMode : lightMode)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
localStorage.setItem('theme', isDark ? 'dark' : 'light')
|
|
105
|
+
document.dispatchEvent(new Event('theme-changed'))
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Toggle theme mode
|
|
109
|
+
function toggleTheme() {
|
|
110
|
+
const isDark = !document.documentElement.classList.contains('dark')
|
|
111
|
+
applyTheme(isDark)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Handle theme toggle click events
|
|
115
|
+
function handleThemeToggle(e: MouseEvent) {
|
|
116
|
+
if (!(e.target instanceof Element)) {
|
|
117
|
+
return
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (!e.target.closest('#theme-toggle-button')) {
|
|
121
|
+
return
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// If reduceMotion is enabled, update theme directly
|
|
125
|
+
if (document.documentElement.classList.contains('reduce-motion')) {
|
|
126
|
+
toggleTheme()
|
|
127
|
+
return
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Add temporary markers before view transition
|
|
131
|
+
document.documentElement.style.setProperty('view-transition-name', 'theme-toggle-transition')
|
|
132
|
+
document.documentElement.setAttribute('data-theme-changing', '')
|
|
133
|
+
|
|
134
|
+
// Use View Transitions API for theme toggle
|
|
135
|
+
const transition = document.startViewTransition(toggleTheme)
|
|
136
|
+
|
|
137
|
+
// Remove temporary markers after view transition
|
|
138
|
+
transition.finished.then(() => {
|
|
139
|
+
document.documentElement.style.removeProperty('view-transition-name')
|
|
140
|
+
document.documentElement.removeAttribute('data-theme-changing')
|
|
141
|
+
})
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Listen to system theme changes in real time
|
|
145
|
+
function handleSystemChange(event: MediaQueryListEvent) {
|
|
146
|
+
const isDark = event.matches
|
|
147
|
+
applyTheme(isDark)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
document.addEventListener('click', handleThemeToggle)
|
|
151
|
+
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', handleSystemChange)
|
|
152
|
+
</script>
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { Language } from '@/i18n/config'
|
|
3
|
+
import type { UserCategoryWithCount } from '@/utils/content'
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
categories: UserCategoryWithCount[]
|
|
7
|
+
currentCategory?: string
|
|
8
|
+
lang: Language
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const { categories, currentCategory = '', lang } = Astro.props
|
|
12
|
+
|
|
13
|
+
// Generate category path (user-defined categories)
|
|
14
|
+
function getCategoryPagePath(category: string): string {
|
|
15
|
+
const langPrefix = lang === 'zh' ? '' : `/${lang}`
|
|
16
|
+
return `${langPrefix}/categories/${encodeURIComponent(category)}`
|
|
17
|
+
}
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
<ul class="category-list no-heti">
|
|
21
|
+
{categories.map(cat => {
|
|
22
|
+
const isActive = currentCategory === cat.name
|
|
23
|
+
return (
|
|
24
|
+
<li class="category-item">
|
|
25
|
+
<a
|
|
26
|
+
href={getCategoryPagePath(cat.name)}
|
|
27
|
+
class:list={[
|
|
28
|
+
'category-link',
|
|
29
|
+
isActive && 'active',
|
|
30
|
+
]}
|
|
31
|
+
>
|
|
32
|
+
<span class="category-name">{cat.name}</span>
|
|
33
|
+
<span class="category-count">({cat.count})</span>
|
|
34
|
+
</a>
|
|
35
|
+
</li>
|
|
36
|
+
)
|
|
37
|
+
})}
|
|
38
|
+
</ul>
|
|
39
|
+
|
|
40
|
+
<style>
|
|
41
|
+
.category-list {
|
|
42
|
+
--at-apply: 'list-none p-0 m-0';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.category-item {
|
|
46
|
+
--at-apply: 'py-1.5';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.category-link {
|
|
50
|
+
--at-apply: 'inline-flex items-center gap-2';
|
|
51
|
+
--at-apply: 'c-secondary/80 transition-colors';
|
|
52
|
+
--at-apply: 'hover:c-primary';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.category-link.active {
|
|
56
|
+
--at-apply: 'c-primary font-semibold';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.category-name {
|
|
60
|
+
--at-apply: 'text-4';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.category-count {
|
|
64
|
+
--at-apply: 'text-3.5 c-secondary/60';
|
|
65
|
+
}
|
|
66
|
+
</style>
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { base, defaultLocale, themeConfig } from '@/config'
|
|
3
|
+
import { giscusLocaleMap } from '@/i18n/config'
|
|
4
|
+
|
|
5
|
+
const {
|
|
6
|
+
repo = '',
|
|
7
|
+
repoId = '',
|
|
8
|
+
category = '',
|
|
9
|
+
categoryId = '',
|
|
10
|
+
mapping = 'pathname',
|
|
11
|
+
strict = '0',
|
|
12
|
+
reactionsEnabled = '1',
|
|
13
|
+
emitMetadata = '0',
|
|
14
|
+
inputPosition = 'bottom',
|
|
15
|
+
} = themeConfig.comment.giscus ?? {}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
const currentPath = Astro.url.pathname
|
|
19
|
+
|
|
20
|
+
// Determine language
|
|
21
|
+
const pathWithoutBase = base && currentPath.startsWith(base)
|
|
22
|
+
? currentPath.slice(base.length)
|
|
23
|
+
: currentPath
|
|
24
|
+
const matchedLang = Object.keys(giscusLocaleMap).find(code => pathWithoutBase.startsWith(`/${code}/`))
|
|
25
|
+
const lang = (matchedLang ?? defaultLocale) as keyof typeof giscusLocaleMap
|
|
26
|
+
|
|
27
|
+
const giscusConfig = {
|
|
28
|
+
repo,
|
|
29
|
+
repoId,
|
|
30
|
+
category,
|
|
31
|
+
categoryId,
|
|
32
|
+
mapping,
|
|
33
|
+
strict,
|
|
34
|
+
reactionsEnabled,
|
|
35
|
+
emitMetadata,
|
|
36
|
+
inputPosition,
|
|
37
|
+
lang: giscusLocaleMap[lang],
|
|
38
|
+
}
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
<div
|
|
42
|
+
id="giscus"
|
|
43
|
+
class="giscus mt-16"
|
|
44
|
+
data-config={JSON.stringify(giscusConfig)}
|
|
45
|
+
/>
|
|
46
|
+
|
|
47
|
+
<!-- Giscus Script >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -->
|
|
48
|
+
<script>
|
|
49
|
+
let giscusContainer: HTMLElement | null = null
|
|
50
|
+
|
|
51
|
+
function getThemeUrl() {
|
|
52
|
+
const isDark = document.documentElement.classList.contains('dark')
|
|
53
|
+
return isDark
|
|
54
|
+
? `https://cdn.jsdelivr.net/gh/radishzzz/astro-theme-retypeset@master/public/giscus/theme-dark.css`
|
|
55
|
+
: `https://cdn.jsdelivr.net/gh/radishzzz/astro-theme-retypeset@master/public/giscus/theme-light.css`
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function updateGiscusTheme() {
|
|
59
|
+
if (!giscusContainer || !giscusContainer.dataset.config) {
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const giscusWindow = giscusContainer.querySelector<HTMLIFrameElement>('iframe.giscus-frame')?.contentWindow
|
|
64
|
+
if (!giscusWindow) {
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
giscusWindow.postMessage(
|
|
70
|
+
{ giscus: { setConfig: { theme: getThemeUrl() } } },
|
|
71
|
+
'https://giscus.app',
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
console.error('[Giscus] Failed to update theme:', error)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function setupGiscus() {
|
|
80
|
+
giscusContainer = document.getElementById('giscus')
|
|
81
|
+
if (!giscusContainer || !giscusContainer.dataset.config) {
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
giscusContainer.innerHTML = ''
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
const config = JSON.parse(giscusContainer.dataset.config)
|
|
89
|
+
const { category, ...restConfig } = config
|
|
90
|
+
|
|
91
|
+
const dataAttributes: Record<string, string> = {
|
|
92
|
+
...restConfig,
|
|
93
|
+
...(category && { category }),
|
|
94
|
+
theme: getThemeUrl(),
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const script = document.createElement('script')
|
|
98
|
+
script.crossOrigin = 'anonymous'
|
|
99
|
+
script.async = true
|
|
100
|
+
Object.assign(script.dataset, dataAttributes)
|
|
101
|
+
script.src = 'https://giscus.app/client.js'
|
|
102
|
+
giscusContainer.appendChild(script)
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
console.error('[Giscus] Failed to initialize:', error)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function cleanupGiscus() {
|
|
110
|
+
if (giscusContainer) {
|
|
111
|
+
giscusContainer.innerHTML = ''
|
|
112
|
+
giscusContainer = null
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
document.addEventListener('astro:page-load', setupGiscus)
|
|
117
|
+
document.addEventListener('astro:before-swap', cleanupGiscus)
|
|
118
|
+
document.addEventListener('theme-changed', updateGiscusTheme)
|
|
119
|
+
</script>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
// import Disqus from '@/components/Comment/Disqus.astro'
|
|
3
|
+
import Giscus from '@/components/Comment/Giscus.astro'
|
|
4
|
+
import Twikoo from '@/components/Comment/Twikoo.astro'
|
|
5
|
+
import Waline from '@/components/Comment/Waline.astro'
|
|
6
|
+
import { themeConfig } from '@/config'
|
|
7
|
+
|
|
8
|
+
const { enabled: enableComment } = themeConfig.comment
|
|
9
|
+
|
|
10
|
+
// Disqus
|
|
11
|
+
// const disqusShortname = themeConfig.comment.disqus?.shortname ?? ''
|
|
12
|
+
// const showDisqus = enableComment && Boolean(disqusShortname.trim())
|
|
13
|
+
|
|
14
|
+
// Giscus
|
|
15
|
+
const giscusRepo = themeConfig.comment.giscus?.repo ?? ''
|
|
16
|
+
const showGiscus = enableComment && Boolean(giscusRepo.trim())
|
|
17
|
+
|
|
18
|
+
// Twikoo
|
|
19
|
+
const twikooEnvId = themeConfig.comment.twikoo?.envId ?? ''
|
|
20
|
+
const showTwikoo = enableComment && Boolean(twikooEnvId.trim())
|
|
21
|
+
|
|
22
|
+
// Waline
|
|
23
|
+
const walineURL = themeConfig.comment.waline?.serverURL ?? ''
|
|
24
|
+
const showWaline = enableComment && Boolean(walineURL.trim())
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
<!-- {showDisqus && <Disqus />} -->
|
|
28
|
+
{showGiscus && <Giscus />}
|
|
29
|
+
{showTwikoo && <Twikoo />}
|
|
30
|
+
{showWaline && <Waline />}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
---
|
|
2
|
+
import twikooCSS from 'twikoo/dist/twikoo.css?url'
|
|
3
|
+
import twikooJS from 'twikoo/dist/twikoo.nocss.js?url' // Using twikoo.min.js causes CSS loading failure after page switching
|
|
4
|
+
import { allLocales, base, defaultLocale, themeConfig } from '@/config'
|
|
5
|
+
import { twikooLocaleMap } from '@/i18n/config'
|
|
6
|
+
|
|
7
|
+
const { envId = '' } = themeConfig.comment.twikoo ?? {}
|
|
8
|
+
|
|
9
|
+
// Remove locale prefix from path
|
|
10
|
+
const currentPath = Astro.url.pathname
|
|
11
|
+
const localePattern = new RegExp(`\\/(${allLocales.join('|')})\\/`)
|
|
12
|
+
const twikooPath = currentPath.replace(localePattern, '/')
|
|
13
|
+
|
|
14
|
+
// Determine language
|
|
15
|
+
const pathWithoutBase = base && currentPath.startsWith(base)
|
|
16
|
+
? currentPath.slice(base.length)
|
|
17
|
+
: currentPath
|
|
18
|
+
const matchedLang = Object.keys(twikooLocaleMap).find(code => pathWithoutBase.startsWith(`/${code}/`))
|
|
19
|
+
const lang = (matchedLang ?? defaultLocale) as keyof typeof twikooLocaleMap
|
|
20
|
+
|
|
21
|
+
const twikooConfig = {
|
|
22
|
+
envId,
|
|
23
|
+
path: twikooPath,
|
|
24
|
+
lang: twikooLocaleMap[lang],
|
|
25
|
+
twikooCSS,
|
|
26
|
+
}
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
<!-- Class not working on twikoo div -->
|
|
30
|
+
<div
|
|
31
|
+
id="twikoo-wrapper"
|
|
32
|
+
class="no-heti mt-16"
|
|
33
|
+
data-config={JSON.stringify(twikooConfig)}
|
|
34
|
+
>
|
|
35
|
+
<div id="twikoo" />
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
<!-- Twikoo Script >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -->
|
|
39
|
+
<script
|
|
40
|
+
is:inline
|
|
41
|
+
src={twikooJS}
|
|
42
|
+
></script>
|
|
43
|
+
|
|
44
|
+
<script>
|
|
45
|
+
declare const twikoo: any
|
|
46
|
+
|
|
47
|
+
let twikooObserver: IntersectionObserver | null = null
|
|
48
|
+
let twikooWrapper: HTMLElement | null = null
|
|
49
|
+
|
|
50
|
+
function setupTwikoo() {
|
|
51
|
+
const twikooWrapper = document.getElementById('twikoo-wrapper')
|
|
52
|
+
if (!twikooWrapper || !twikooWrapper.dataset.config) {
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
const config = JSON.parse(twikooWrapper.dataset.config)
|
|
58
|
+
const { envId, path, lang } = config
|
|
59
|
+
|
|
60
|
+
twikoo.init({
|
|
61
|
+
envId,
|
|
62
|
+
el: '#twikoo',
|
|
63
|
+
// region: 'ap-shanghai', // Specify for Tencent Cloud, omit for Vercel
|
|
64
|
+
path,
|
|
65
|
+
lang,
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
console.error('[Twikoo] Failed to initialize:', error)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Lazy load Twikoo when the wrapper enters viewport
|
|
74
|
+
function lazySetupTwikoo() {
|
|
75
|
+
twikooWrapper = document.getElementById('twikoo-wrapper')
|
|
76
|
+
if (!twikooWrapper || !twikooWrapper.dataset.config) {
|
|
77
|
+
return
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const config = JSON.parse(twikooWrapper.dataset.config)
|
|
81
|
+
const { twikooCSS } = config
|
|
82
|
+
|
|
83
|
+
if (!document.getElementById('twikoo-css')) {
|
|
84
|
+
const link = document.createElement('link')
|
|
85
|
+
link.id = 'twikoo-css'
|
|
86
|
+
link.rel = 'stylesheet'
|
|
87
|
+
link.href = twikooCSS
|
|
88
|
+
document.head.appendChild(link)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
twikooObserver = new IntersectionObserver((entries) => {
|
|
92
|
+
if (entries.some(entry => entry.isIntersecting)) {
|
|
93
|
+
setupTwikoo()
|
|
94
|
+
twikooObserver?.disconnect()
|
|
95
|
+
twikooObserver = null
|
|
96
|
+
}
|
|
97
|
+
}, { rootMargin: '500px' })
|
|
98
|
+
|
|
99
|
+
twikooObserver.observe(twikooWrapper)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function cleanupTwikoo() {
|
|
103
|
+
twikooObserver?.disconnect()
|
|
104
|
+
twikooObserver = null
|
|
105
|
+
|
|
106
|
+
if (twikooWrapper) {
|
|
107
|
+
twikooWrapper.innerHTML = ''
|
|
108
|
+
twikooWrapper = null
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
document.addEventListener('astro:page-load', lazySetupTwikoo)
|
|
113
|
+
document.addEventListener('astro:before-swap', cleanupTwikoo)
|
|
114
|
+
</script>
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
---
|
|
2
|
+
import walineJS from '@waline/client/full?url'
|
|
3
|
+
import walineCSS from '@waline/client/style?url'
|
|
4
|
+
import { allLocales, base, defaultLocale, themeConfig } from '@/config'
|
|
5
|
+
import { walineLocaleMap } from '@/i18n/config'
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
serverURL = '',
|
|
9
|
+
emoji = [],
|
|
10
|
+
search = false,
|
|
11
|
+
imageUploader = false,
|
|
12
|
+
} = themeConfig.comment.waline ?? {}
|
|
13
|
+
|
|
14
|
+
// Remove locale prefix from path
|
|
15
|
+
const currentPath = Astro.url.pathname
|
|
16
|
+
const localePattern = new RegExp(`\\/(${allLocales.join('|')})\\/`)
|
|
17
|
+
const walinePath = currentPath.replace(localePattern, '/')
|
|
18
|
+
|
|
19
|
+
// Determine language
|
|
20
|
+
const pathWithoutBase = base && currentPath.startsWith(base)
|
|
21
|
+
? currentPath.slice(base.length)
|
|
22
|
+
: currentPath
|
|
23
|
+
const matchedLang = Object.keys(walineLocaleMap).find(code => pathWithoutBase.startsWith(`/${code}/`))
|
|
24
|
+
const lang = (matchedLang ?? defaultLocale) as keyof typeof walineLocaleMap
|
|
25
|
+
|
|
26
|
+
const walineConfig = {
|
|
27
|
+
serverURL,
|
|
28
|
+
path: walinePath,
|
|
29
|
+
lang: walineLocaleMap[lang],
|
|
30
|
+
emoji,
|
|
31
|
+
search,
|
|
32
|
+
imageUploader,
|
|
33
|
+
walineJS,
|
|
34
|
+
walineCSS,
|
|
35
|
+
}
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
<div
|
|
39
|
+
id="waline"
|
|
40
|
+
class="no-heti mt-16"
|
|
41
|
+
data-config={JSON.stringify(walineConfig)}
|
|
42
|
+
/>
|
|
43
|
+
|
|
44
|
+
<!-- Waline Script >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -->
|
|
45
|
+
<script>
|
|
46
|
+
import type { WalineInstance } from '@waline/client'
|
|
47
|
+
|
|
48
|
+
let walineObserver: IntersectionObserver | null = null
|
|
49
|
+
let walineInstance: WalineInstance | null = null
|
|
50
|
+
|
|
51
|
+
async function setupWaline() {
|
|
52
|
+
const walineContainer = document.getElementById('waline')
|
|
53
|
+
if (!walineContainer || !walineContainer.dataset.config) {
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const config = JSON.parse(walineContainer.dataset.config)
|
|
59
|
+
const { walineJS, walineCSS, ...restConfig } = config
|
|
60
|
+
const { init } = await import(/* @vite-ignore */ walineJS)
|
|
61
|
+
|
|
62
|
+
walineInstance = init({
|
|
63
|
+
el: '#waline',
|
|
64
|
+
dark: 'html.dark',
|
|
65
|
+
requiredMeta: ['nick', 'mail'],
|
|
66
|
+
highlighter: false,
|
|
67
|
+
texRenderer: false,
|
|
68
|
+
noCopyright: true,
|
|
69
|
+
reaction: [],
|
|
70
|
+
...restConfig,
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
console.error('[Waline] Failed to initialize:', error)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Lazy load Waline when the container enters viewport
|
|
79
|
+
function lazySetupWaline() {
|
|
80
|
+
const walineContainer = document.getElementById('waline')
|
|
81
|
+
if (!walineContainer || !walineContainer.dataset.config) {
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const config = JSON.parse(walineContainer.dataset.config)
|
|
86
|
+
const { walineCSS } = config
|
|
87
|
+
|
|
88
|
+
if (!document.getElementById('waline-css')) {
|
|
89
|
+
const link = document.createElement('link')
|
|
90
|
+
link.id = 'waline-css'
|
|
91
|
+
link.rel = 'stylesheet'
|
|
92
|
+
link.href = walineCSS
|
|
93
|
+
document.head.appendChild(link)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
walineObserver = new IntersectionObserver((entries) => {
|
|
97
|
+
if (entries.some(entry => entry.isIntersecting)) {
|
|
98
|
+
setupWaline()
|
|
99
|
+
walineObserver?.disconnect()
|
|
100
|
+
walineObserver = null
|
|
101
|
+
}
|
|
102
|
+
}, { rootMargin: '500px' })
|
|
103
|
+
|
|
104
|
+
walineObserver.observe(walineContainer)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function cleanupWaline() {
|
|
108
|
+
walineObserver?.disconnect()
|
|
109
|
+
walineObserver = null
|
|
110
|
+
walineInstance?.destroy?.()
|
|
111
|
+
walineInstance = null
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
document.addEventListener('astro:page-load', lazySetupWaline)
|
|
115
|
+
document.addEventListener('astro:before-swap', cleanupWaline)
|
|
116
|
+
</script>
|
|
117
|
+
|
|
118
|
+
<!-- Official CSS Variables >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -->
|
|
119
|
+
<style>
|
|
120
|
+
#waline {
|
|
121
|
+
/* Regular Colors */
|
|
122
|
+
--waline-white: oklch(var(--un-preset-theme-colors-background));
|
|
123
|
+
--waline-light-grey: oklch(var(--un-preset-theme-colors-primary) / 0.25);
|
|
124
|
+
--waline-dark-grey: oklch(var(--un-preset-theme-colors-secondary));
|
|
125
|
+
|
|
126
|
+
/* Theme Colors */
|
|
127
|
+
--waline-theme-color: oklch(var(--un-preset-theme-colors-primary));
|
|
128
|
+
--waline-active-color: oklch(var(--un-preset-theme-colors-primary));
|
|
129
|
+
|
|
130
|
+
/* Layout Colors */
|
|
131
|
+
--waline-color: oklch(var(--un-preset-theme-colors-secondary));
|
|
132
|
+
--waline-bg-color: oklch(var(--un-preset-theme-colors-background));
|
|
133
|
+
--waline-bg-color-light: oklch(var(--un-preset-theme-colors-background));
|
|
134
|
+
--waline-bg-color-hover: oklch(var(--un-preset-theme-colors-background));
|
|
135
|
+
--waline-border-color: oklch(var(--un-preset-theme-colors-primary) / 0.25);
|
|
136
|
+
--waline-disable-bg-color: oklch(var(--un-preset-theme-colors-secondary) / 0.05);
|
|
137
|
+
--waline-disable-color: oklch(var(--un-preset-theme-colors-primary));
|
|
138
|
+
|
|
139
|
+
/* Special Colors */
|
|
140
|
+
--waline-bq-color: oklch(var(--un-preset-theme-colors-primary) / 0.25);
|
|
141
|
+
|
|
142
|
+
/* Information */
|
|
143
|
+
--waline-info-bg-color: oklch(var(--un-preset-theme-colors-background));
|
|
144
|
+
--waline-info-color: oklch(var(--un-preset-theme-colors-primary) / 0.25);
|
|
145
|
+
|
|
146
|
+
/* Rendering Options */
|
|
147
|
+
--waline-avatar-radius: 0.5rem;
|
|
148
|
+
}
|
|
149
|
+
</style>
|