mirror of
https://github.com/ClovertaTheTrilobita/SanYeCao-blog.git
synced 2026-07-03 15:41:26 +00:00
refactor(comments): refactor comments from giscus to remark42
This commit is contained in:
parent
31d12a4a06
commit
a0eab19f31
16 changed files with 597 additions and 733 deletions
|
|
@ -2,4 +2,5 @@ GITHUB_TOKEN=
|
||||||
GISCUS_REPO_OWNER=
|
GISCUS_REPO_OWNER=
|
||||||
GISCUS_REPO_NAME=
|
GISCUS_REPO_NAME=
|
||||||
GISCUS_CATEGORY_ID=
|
GISCUS_CATEGORY_ID=
|
||||||
GISCUS_DATA_REPO_ID=
|
GISCUS_DATA_REPO_ID=
|
||||||
|
PUBLIC_COMMENT_SERVER_URL=
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// @ts-check
|
// @ts-check
|
||||||
import { defineConfig } from 'astro/config';
|
import { defineConfig } from 'astro/config';
|
||||||
import sitemap from "@astrojs/sitemap";
|
import sitemap from "@astrojs/sitemap";
|
||||||
|
import svelte from "@astrojs/svelte";
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
import { fileURLToPath } from 'node:url'
|
import { fileURLToPath } from 'node:url'
|
||||||
|
|
||||||
|
|
@ -16,5 +17,5 @@ export default defineConfig({
|
||||||
redirects: {
|
redirects: {
|
||||||
"/": "/zh",
|
"/": "/zh",
|
||||||
},
|
},
|
||||||
integrations: [sitemap()],
|
integrations: [sitemap(), svelte()],
|
||||||
})
|
})
|
||||||
|
|
|
||||||
273
package-lock.json
generated
273
package-lock.json
generated
|
|
@ -10,6 +10,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/rss": "^4.0.17",
|
"@astrojs/rss": "^4.0.17",
|
||||||
"@astrojs/sitemap": "^3.7.1",
|
"@astrojs/sitemap": "^3.7.1",
|
||||||
|
"@astrojs/svelte": "^8.0.4",
|
||||||
"astro": "^6.0.8",
|
"astro": "^6.0.8",
|
||||||
"url": "^0.11.4"
|
"url": "^0.11.4"
|
||||||
},
|
},
|
||||||
|
|
@ -98,6 +99,25 @@
|
||||||
"zod": "^4.3.6"
|
"zod": "^4.3.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@astrojs/svelte": {
|
||||||
|
"version": "8.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@astrojs/svelte/-/svelte-8.0.4.tgz",
|
||||||
|
"integrity": "sha512-c5m3chjtgxBE3BzsE/bZbCFBkLPhq041rm2WJFaTIKGwt/3xNm/5efYCj23reuAcBsl4iYS8n2UwkAHQJzhkZA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@sveltejs/vite-plugin-svelte": "^6.2.4",
|
||||||
|
"svelte2tsx": "^0.7.52",
|
||||||
|
"vite": "^7.3.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=22.12.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"astro": "^6.0.0",
|
||||||
|
"svelte": "^5.43.6",
|
||||||
|
"typescript": "^5.3.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@astrojs/telemetry": {
|
"node_modules/@astrojs/telemetry": {
|
||||||
"version": "3.3.0",
|
"version": "3.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.0.tgz",
|
||||||
|
|
@ -1155,12 +1175,55 @@
|
||||||
"url": "https://opencollective.com/libvips"
|
"url": "https://opencollective.com/libvips"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@jridgewell/gen-mapping": {
|
||||||
|
"version": "0.3.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
|
||||||
|
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@jridgewell/sourcemap-codec": "^1.5.0",
|
||||||
|
"@jridgewell/trace-mapping": "^0.3.24"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@jridgewell/remapping": {
|
||||||
|
"version": "2.3.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
|
||||||
|
"integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@jridgewell/gen-mapping": "^0.3.5",
|
||||||
|
"@jridgewell/trace-mapping": "^0.3.24"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@jridgewell/resolve-uri": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@jridgewell/sourcemap-codec": {
|
"node_modules/@jridgewell/sourcemap-codec": {
|
||||||
"version": "1.5.5",
|
"version": "1.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
||||||
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@jridgewell/trace-mapping": {
|
||||||
|
"version": "0.3.31",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
|
||||||
|
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@jridgewell/resolve-uri": "^3.1.0",
|
||||||
|
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@npmcli/fs": {
|
"node_modules/@npmcli/fs": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz",
|
||||||
|
|
@ -1682,6 +1745,53 @@
|
||||||
"integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==",
|
"integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@sveltejs/acorn-typescript": {
|
||||||
|
"version": "1.0.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.9.tgz",
|
||||||
|
"integrity": "sha512-lVJX6qEgs/4DOcRTpo56tmKzVPtoWAaVbL4hfO7t7NVwl9AAXzQR6cihesW1BmNMPl+bK6dreu2sOKBP2Q9CIA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"peerDependencies": {
|
||||||
|
"acorn": "^8.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@sveltejs/vite-plugin-svelte": {
|
||||||
|
"version": "6.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-6.2.4.tgz",
|
||||||
|
"integrity": "sha512-ou/d51QSdTyN26D7h6dSpusAKaZkAiGM55/AKYi+9AGZw7q85hElbjK3kEyzXHhLSnRISHOYzVge6x0jRZ7DXA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@sveltejs/vite-plugin-svelte-inspector": "^5.0.0",
|
||||||
|
"deepmerge": "^4.3.1",
|
||||||
|
"magic-string": "^0.30.21",
|
||||||
|
"obug": "^2.1.0",
|
||||||
|
"vitefu": "^1.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19 || ^22.12 || >=24"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"svelte": "^5.0.0",
|
||||||
|
"vite": "^6.3.0 || ^7.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@sveltejs/vite-plugin-svelte-inspector": {
|
||||||
|
"version": "5.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-5.0.2.tgz",
|
||||||
|
"integrity": "sha512-TZzRTcEtZffICSAoZGkPSl6Etsj2torOVrx6Uw0KpXxrec9Gg6jFWQ60Q3+LmNGfZSxHRCZL7vXVZIWmuV50Ig==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"obug": "^2.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19 || ^22.12 || >=24"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@sveltejs/vite-plugin-svelte": "^6.0.0-next.0",
|
||||||
|
"svelte": "^5.0.0",
|
||||||
|
"vite": "^6.3.0 || ^7.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@tootallnate/once": {
|
"node_modules/@tootallnate/once": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
|
||||||
|
|
@ -1782,12 +1892,33 @@
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/trusted-types": {
|
||||||
|
"version": "2.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
|
||||||
|
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
"node_modules/@types/unist": {
|
"node_modules/@types/unist": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
|
||||||
"integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
|
"integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@typescript-eslint/types": {
|
||||||
|
"version": "8.58.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.0.tgz",
|
||||||
|
"integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/typescript-eslint"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@ungap/structured-clone": {
|
"node_modules/@ungap/structured-clone": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz",
|
||||||
|
|
@ -1811,6 +1942,19 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/acorn": {
|
||||||
|
"version": "8.16.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
|
||||||
|
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"bin": {
|
||||||
|
"acorn": "bin/acorn"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/agent-base": {
|
"node_modules/agent-base": {
|
||||||
"version": "6.0.2",
|
"version": "6.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
|
||||||
|
|
@ -2760,6 +2904,21 @@
|
||||||
"url": "https://github.com/sponsors/wooorm"
|
"url": "https://github.com/sponsors/wooorm"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dedent-js": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/dedent-js/-/dedent-js-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-OUepMozQULMLUmhxS95Vudo0jb0UchLimi3+pQ2plj61Fcy8axbP9hbiD4Sz6DPqn6XG3kfmziVfQ1rSys5AJQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/deepmerge": {
|
||||||
|
"version": "4.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
|
||||||
|
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/define-data-property": {
|
"node_modules/define-data-property": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
|
||||||
|
|
@ -3134,6 +3293,24 @@
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/esm-env": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz",
|
||||||
|
"integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
|
"node_modules/esrap": {
|
||||||
|
"version": "2.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/esrap/-/esrap-2.2.4.tgz",
|
||||||
|
"integrity": "sha512-suICpxAmZ9A8bzJjEl/+rLJiDKC0X4gYWUxT6URAWBLvlXmtbZd5ySMu/N2ZGEtMCAmflUDPSehrP9BQcsGcSg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@jridgewell/sourcemap-codec": "^1.4.15",
|
||||||
|
"@typescript-eslint/types": "^8.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/estree-walker": {
|
"node_modules/estree-walker": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||||
|
|
@ -4117,6 +4294,16 @@
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/is-reference": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/estree": "^1.0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/is-relative": {
|
"node_modules/is-relative": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz",
|
||||||
|
|
@ -4336,6 +4523,13 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/locate-character": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
"node_modules/locate-path": {
|
"node_modules/locate-path": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
||||||
|
|
@ -6662,6 +6856,12 @@
|
||||||
"node": ">=11.0.0"
|
"node": ">=11.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/scule": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/scule/-/scule-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/semver": {
|
"node_modules/semver": {
|
||||||
"version": "7.7.4",
|
"version": "7.7.4",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
|
||||||
|
|
@ -7117,6 +7317,58 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/svelte": {
|
||||||
|
"version": "5.55.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.55.1.tgz",
|
||||||
|
"integrity": "sha512-QjvU7EFemf6mRzdMGlAFttMWtAAVXrax61SZYHdkD6yoVGQ89VeyKfZD4H1JrV1WLmJBxWhFch9H6ig/87VGjw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@jridgewell/remapping": "^2.3.4",
|
||||||
|
"@jridgewell/sourcemap-codec": "^1.5.0",
|
||||||
|
"@sveltejs/acorn-typescript": "^1.0.5",
|
||||||
|
"@types/estree": "^1.0.5",
|
||||||
|
"@types/trusted-types": "^2.0.7",
|
||||||
|
"acorn": "^8.12.1",
|
||||||
|
"aria-query": "5.3.1",
|
||||||
|
"axobject-query": "^4.1.0",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
|
"devalue": "^5.6.4",
|
||||||
|
"esm-env": "^1.2.1",
|
||||||
|
"esrap": "^2.2.4",
|
||||||
|
"is-reference": "^3.0.3",
|
||||||
|
"locate-character": "^3.0.0",
|
||||||
|
"magic-string": "^0.30.11",
|
||||||
|
"zimmerframe": "^1.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/svelte/node_modules/aria-query": {
|
||||||
|
"version": "5.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.1.tgz",
|
||||||
|
"integrity": "sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/svelte2tsx": {
|
||||||
|
"version": "0.7.53",
|
||||||
|
"resolved": "https://registry.npmjs.org/svelte2tsx/-/svelte2tsx-0.7.53.tgz",
|
||||||
|
"integrity": "sha512-ljVSwmnYRDHRm8+7ICP6QoAN7U7vgOFfPBLN6T745YWNYqRRSzHxlrzUVqMjYls2Un8MzJissfziy/38e6Deeg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"dedent-js": "^1.0.1",
|
||||||
|
"scule": "^1.3.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"svelte": "^3.55 || ^4.0.0-next.0 || ^4.0 || ^5.0.0-next.0",
|
||||||
|
"typescript": "^4.9.4 || ^5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/svgo": {
|
"node_modules/svgo": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.1.tgz",
|
||||||
|
|
@ -7400,6 +7652,20 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/typescript": {
|
||||||
|
"version": "5.9.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||||
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
|
"bin": {
|
||||||
|
"tsc": "bin/tsc",
|
||||||
|
"tsserver": "bin/tsserver"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.17"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ufo": {
|
"node_modules/ufo": {
|
||||||
"version": "1.6.3",
|
"version": "1.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz",
|
||||||
|
|
@ -8186,6 +8452,13 @@
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/zimmerframe": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
"node_modules/zod": {
|
"node_modules/zod": {
|
||||||
"version": "4.3.6",
|
"version": "4.3.6",
|
||||||
"resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/rss": "^4.0.17",
|
"@astrojs/rss": "^4.0.17",
|
||||||
"@astrojs/sitemap": "^3.7.1",
|
"@astrojs/sitemap": "^3.7.1",
|
||||||
|
"@astrojs/svelte": "^8.0.4",
|
||||||
"astro": "^6.0.8",
|
"astro": "^6.0.8",
|
||||||
"url": "^0.11.4"
|
"url": "^0.11.4"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,341 +0,0 @@
|
||||||
---
|
|
||||||
import { getLangFromUrl } from "@/i18n";
|
|
||||||
|
|
||||||
const limit = Number(Astro.props.limit ?? 5);
|
|
||||||
const lang = getLangFromUrl(Astro.url);
|
|
||||||
|
|
||||||
function formatDate(iso: string) {
|
|
||||||
return new Date(iso).toISOString().slice(0, 10);
|
|
||||||
}
|
|
||||||
---
|
|
||||||
|
|
||||||
<section
|
|
||||||
id="latest-comments"
|
|
||||||
class="latest-comments"
|
|
||||||
data-lang={lang}
|
|
||||||
data-limit={limit}
|
|
||||||
>
|
|
||||||
<h2>{lang === "zh" ? "最新评论" : "Latest Comments"}</h2>
|
|
||||||
|
|
||||||
<div class="comments-loading" id="comments-loading">
|
|
||||||
<div class="loading-card"></div>
|
|
||||||
<div class="loading-card"></div>
|
|
||||||
<div class="loading-card"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ul class="comment-list" id="comment-list" style="display: none;"></ul>
|
|
||||||
|
|
||||||
<p class="empty" id="comments-empty" style="display: none;">
|
|
||||||
{lang === "zh" ? "还没有评论。" : "No comments yet."}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class="empty" id="comments-error" style="display: none;">
|
|
||||||
{lang === "zh" ? "评论加载失败。" : "Failed to load comments."}
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
type CommentAuthor = {
|
|
||||||
login?: string;
|
|
||||||
avatarUrl?: string;
|
|
||||||
url?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type LatestCommentItem = {
|
|
||||||
id: string;
|
|
||||||
body?: string;
|
|
||||||
updatedAt: string;
|
|
||||||
author?: CommentAuthor | null;
|
|
||||||
postId: string;
|
|
||||||
postTitle: string;
|
|
||||||
localUrl: string;
|
|
||||||
isReply: boolean;
|
|
||||||
replyTo?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type CommentsResponse = {
|
|
||||||
comments?: LatestCommentItem[];
|
|
||||||
error?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const root = document.getElementById(
|
|
||||||
"latest-comments",
|
|
||||||
) as HTMLElement | null;
|
|
||||||
if (root) {
|
|
||||||
const lang = root.dataset.lang || "zh";
|
|
||||||
const limit = root.dataset.limit || "5";
|
|
||||||
|
|
||||||
const loadingEl = root.querySelector("#comments-loading");
|
|
||||||
const listEl = root.querySelector("#comment-list");
|
|
||||||
const emptyEl = root.querySelector("#comments-empty");
|
|
||||||
const errorEl = root.querySelector("#comments-error");
|
|
||||||
|
|
||||||
if (
|
|
||||||
!(loadingEl instanceof HTMLElement) ||
|
|
||||||
!(listEl instanceof HTMLElement) ||
|
|
||||||
!(emptyEl instanceof HTMLElement) ||
|
|
||||||
!(errorEl instanceof HTMLElement)
|
|
||||||
) {
|
|
||||||
throw new Error("Comments elements not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
const formatDate = (iso: string) =>
|
|
||||||
new Date(iso).toISOString().slice(0, 10);
|
|
||||||
|
|
||||||
const renderComment = (comment: LatestCommentItem) => {
|
|
||||||
console.log("Start render");
|
|
||||||
const li = document.createElement("li");
|
|
||||||
li.className = `comment-card ${comment.isReply ? "is-reply" : ""}`;
|
|
||||||
|
|
||||||
li.innerHTML = `
|
|
||||||
<a href="${comment.localUrl}" class="comment-link" data-astro-reload>
|
|
||||||
<div class="comment-top">
|
|
||||||
${
|
|
||||||
comment.author?.avatarUrl
|
|
||||||
? `<img
|
|
||||||
src="${comment.author.avatarUrl}"
|
|
||||||
alt="${comment.author?.login ?? "author"}"
|
|
||||||
class="comment-avatar"
|
|
||||||
/>`
|
|
||||||
: ""
|
|
||||||
}
|
|
||||||
|
|
||||||
<div class="comment-meta">
|
|
||||||
<span class="comment-author">${comment.author?.login ?? "Unknown"}</span>
|
|
||||||
<span class="comment-date">${formatDate(comment.updatedAt)}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<span class="comment-post-title" title="${comment.postTitle}">
|
|
||||||
${comment.postTitle}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
${
|
|
||||||
comment.isReply
|
|
||||||
? `<p class="comment-kind">${
|
|
||||||
lang === "zh"
|
|
||||||
? `回复 @${comment.replyTo ?? "Unknown"}`
|
|
||||||
: `Reply to @${comment.replyTo ?? "Unknown"}`
|
|
||||||
}</p>`
|
|
||||||
: ""
|
|
||||||
}
|
|
||||||
|
|
||||||
<p class="comment-body">${comment.body ?? ""}</p>
|
|
||||||
</a>
|
|
||||||
`;
|
|
||||||
|
|
||||||
return li;
|
|
||||||
};
|
|
||||||
|
|
||||||
fetch(`/api/comments.json?lang=${lang}&limit=${limit}`)
|
|
||||||
.then((res) => {
|
|
||||||
if (!res.ok) throw new Error("Request failed");
|
|
||||||
return res.json() as Promise<CommentsResponse>;
|
|
||||||
})
|
|
||||||
.then((data) => {
|
|
||||||
loadingEl.style.display = "none";
|
|
||||||
|
|
||||||
const comments = data.comments ?? [];
|
|
||||||
|
|
||||||
if (!comments.length) {
|
|
||||||
emptyEl.style.display = "block";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
listEl.style.display = "grid";
|
|
||||||
|
|
||||||
comments.forEach((comment) => {
|
|
||||||
listEl.appendChild(renderComment(comment));
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.error("Comments fetch failed:", err);
|
|
||||||
loadingEl.style.display = "none";
|
|
||||||
errorEl.style.display = "block";
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style is:global>
|
|
||||||
.latest-comments {
|
|
||||||
margin-top: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.latest-comments h2 {
|
|
||||||
margin-bottom: 0.8rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-list {
|
|
||||||
font-family:
|
|
||||||
system-ui,
|
|
||||||
-apple-system,
|
|
||||||
BlinkMacSystemFont,
|
|
||||||
"Segoe UI",
|
|
||||||
"PingFang SC",
|
|
||||||
"Hiragino Sans GB",
|
|
||||||
"Microsoft YaHei",
|
|
||||||
"Noto Sans CJK SC",
|
|
||||||
"Source Han Sans SC",
|
|
||||||
sans-serif;
|
|
||||||
list-style: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
display: grid;
|
|
||||||
gap: 0.9rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-card {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-link {
|
|
||||||
display: block;
|
|
||||||
padding: 0.9rem 1rem;
|
|
||||||
text-decoration: none;
|
|
||||||
color: inherit;
|
|
||||||
border: 1px dashed #aeb8c2;
|
|
||||||
background: rgba(160, 175, 190, 0.06);
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-card.is-reply .comment-link {
|
|
||||||
border-style: dashed;
|
|
||||||
background: rgba(160, 175, 190, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-top {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.75rem;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-meta {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-post-title {
|
|
||||||
margin-left: auto;
|
|
||||||
text-align: right;
|
|
||||||
font-size: 0.92rem;
|
|
||||||
font-weight: 700;
|
|
||||||
color: #6f8090;
|
|
||||||
max-width: 45%;
|
|
||||||
line-height: 1.35;
|
|
||||||
overflow-wrap: anywhere;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-avatar {
|
|
||||||
width: 42px;
|
|
||||||
height: 42px;
|
|
||||||
object-fit: cover;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-author {
|
|
||||||
font-weight: 700;
|
|
||||||
line-height: 1.3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-date {
|
|
||||||
font-size: 0.9rem;
|
|
||||||
color: #6f8090;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-kind {
|
|
||||||
margin: 0 0 0.35rem 0;
|
|
||||||
font-size: 0.82rem;
|
|
||||||
color: #8c8c8c;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-body {
|
|
||||||
margin: 0;
|
|
||||||
color: #555;
|
|
||||||
line-height: 1.6;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty {
|
|
||||||
color: #6f8090;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comments-loading {
|
|
||||||
display: grid;
|
|
||||||
gap: 0.9rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-card {
|
|
||||||
height: 92px;
|
|
||||||
border: 1px dashed #aeb8c2;
|
|
||||||
background: linear-gradient(
|
|
||||||
90deg,
|
|
||||||
rgba(160, 175, 190, 0.06) 25%,
|
|
||||||
rgba(160, 175, 190, 0.16) 50%,
|
|
||||||
rgba(160, 175, 190, 0.06) 75%
|
|
||||||
);
|
|
||||||
background-size: 200% 100%;
|
|
||||||
animation: shimmer 1.2s infinite linear;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes shimmer {
|
|
||||||
from {
|
|
||||||
background-position: 200% 0;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
background-position: -200% 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.dark) .comment-link {
|
|
||||||
border-color: #7f8c97;
|
|
||||||
background: rgba(180, 190, 200, 0.06);
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.dark) .comment-card.is-reply .comment-link {
|
|
||||||
background: rgba(180, 190, 200, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.dark) .comment-post-title {
|
|
||||||
color: #c8d2dc;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.dark) .comment-date,
|
|
||||||
:global(.dark) .empty {
|
|
||||||
color: #aab7c4;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.dark) .comment-kind {
|
|
||||||
color: #b8b8b8;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.dark) .comment-body {
|
|
||||||
color: #d3d7db;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.dark) .loading-card {
|
|
||||||
border-color: #7f8c97;
|
|
||||||
background: linear-gradient(
|
|
||||||
90deg,
|
|
||||||
rgba(180, 190, 200, 0.06) 25%,
|
|
||||||
rgba(180, 190, 200, 0.16) 50%,
|
|
||||||
rgba(180, 190, 200, 0.06) 75%
|
|
||||||
);
|
|
||||||
background-size: 200% 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 640px) {
|
|
||||||
.comment-top {
|
|
||||||
align-items: flex-start;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-post-title {
|
|
||||||
margin-left: 0;
|
|
||||||
max-width: 100%;
|
|
||||||
width: 100%;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -1,67 +0,0 @@
|
||||||
---
|
|
||||||
const { term } = Astro.props;
|
|
||||||
|
|
||||||
const repoOwner = import.meta.env.GISCUS_REPO_OWNER;
|
|
||||||
const repoName = import.meta.env.GISCUS_REPO_NAME;
|
|
||||||
const categoryId = import.meta.env.GISCUS_CATEGORY_ID;
|
|
||||||
const dataRepoId = import.meta.env.GISCUS_DATA_REPO_ID;
|
|
||||||
const repo = `${repoOwner}/${repoName}`;
|
|
||||||
---
|
|
||||||
|
|
||||||
<section class="comments">
|
|
||||||
<h2>Comments</h2>
|
|
||||||
|
|
||||||
<script
|
|
||||||
src="https://giscus.app/client.js"
|
|
||||||
data-repo={repo}
|
|
||||||
data-repo-id={dataRepoId}
|
|
||||||
data-category="Comments"
|
|
||||||
data-category-id={categoryId}
|
|
||||||
data-mapping="specific"
|
|
||||||
data-term={term}
|
|
||||||
data-strict="1"
|
|
||||||
data-reactions-enabled="1"
|
|
||||||
data-emit-metadata="0"
|
|
||||||
data-input-position="bottom"
|
|
||||||
data-theme="preferred_color_scheme"
|
|
||||||
data-lang="en"
|
|
||||||
data-loading="lazy"
|
|
||||||
crossorigin="anonymous"
|
|
||||||
async></script>
|
|
||||||
|
|
||||||
<script is:inline>
|
|
||||||
function setGiscusTheme(theme) {
|
|
||||||
const iframe = document.querySelector("iframe.giscus-frame");
|
|
||||||
if (!iframe) return;
|
|
||||||
|
|
||||||
iframe.contentWindow?.postMessage(
|
|
||||||
{
|
|
||||||
giscus: {
|
|
||||||
setConfig: {
|
|
||||||
theme,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"https://giscus.app",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function syncGiscusTheme() {
|
|
||||||
const isDark = document.documentElement.classList.contains("dark");
|
|
||||||
setGiscusTheme(isDark ? "dark" : "light");
|
|
||||||
}
|
|
||||||
|
|
||||||
const observer = new MutationObserver(() => {
|
|
||||||
syncGiscusTheme();
|
|
||||||
});
|
|
||||||
|
|
||||||
observer.observe(document.documentElement, {
|
|
||||||
attributes: true,
|
|
||||||
attributeFilter: ["class"],
|
|
||||||
});
|
|
||||||
|
|
||||||
window.addEventListener("load", () => {
|
|
||||||
syncGiscusTheme();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</section>
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
import "@/styles/global.css";
|
import "@/styles/global.css";
|
||||||
|
import Remark42Count from "@/components/remark42-counter.svelte";
|
||||||
const data = Astro.props;
|
const data = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -13,8 +14,9 @@ const data = Astro.props;
|
||||||
<span class="post-date">{data.date}</span>
|
<span class="post-date">{data.date}</span>
|
||||||
|
|
||||||
<span class="post-stats">
|
<span class="post-stats">
|
||||||
<span class="post-stat">💬 {data.commentCount ?? 0}</span>
|
<span class="post-stat"
|
||||||
<span class="post-stat">❤ {data.reactionCount ?? 0}</span>
|
>💬 <Remark42Count url=`${data.postPath}` client:idle /></span
|
||||||
|
>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -3,77 +3,9 @@ import { getCollection } from "astro:content";
|
||||||
import PostItem from "./PostItem.astro";
|
import PostItem from "./PostItem.astro";
|
||||||
import { getLangFromUrl } from "@/i18n";
|
import { getLangFromUrl } from "@/i18n";
|
||||||
|
|
||||||
const token = import.meta.env.GITHUB_TOKEN;
|
|
||||||
const owner = import.meta.env.GISCUS_REPO_OWNER;
|
|
||||||
const name = import.meta.env.GISCUS_REPO_NAME;
|
|
||||||
const categoryId = import.meta.env.GISCUS_CATEGORY_ID;
|
|
||||||
const lang = getLangFromUrl(Astro.url);
|
const lang = getLangFromUrl(Astro.url);
|
||||||
const allPosts = await getCollection("blog");
|
const allPosts = await getCollection("blog");
|
||||||
|
|
||||||
type ReactionGroup = {
|
|
||||||
users?: {
|
|
||||||
totalCount?: number;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
type DiscussionNode = {
|
|
||||||
title: string;
|
|
||||||
comments?: {
|
|
||||||
totalCount?: number;
|
|
||||||
};
|
|
||||||
reactionGroups?: ReactionGroup[];
|
|
||||||
};
|
|
||||||
|
|
||||||
function normalizePath(path: string) {
|
|
||||||
return path.replace(/^\/+|\/+$/g, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchDiscussionStats(): Promise<DiscussionNode[]> {
|
|
||||||
// const token = import.meta.env.GITHUB_TOKEN;
|
|
||||||
if (!token) return [];
|
|
||||||
|
|
||||||
const query = `
|
|
||||||
query($owner: String!, $name: String!, $categoryId: ID!) {
|
|
||||||
repository(owner: $owner, name: $name) {
|
|
||||||
discussions(first: 100, categoryId: $categoryId) {
|
|
||||||
nodes {
|
|
||||||
title
|
|
||||||
comments(first: 0) {
|
|
||||||
totalCount
|
|
||||||
}
|
|
||||||
reactionGroups {
|
|
||||||
users {
|
|
||||||
totalCount
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const res = await fetch("https://api.github.com/graphql", {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
query,
|
|
||||||
variables: {
|
|
||||||
owner: owner,
|
|
||||||
name: name,
|
|
||||||
categoryId: categoryId,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
const json = await res.json();
|
|
||||||
return (json?.data?.repository?.discussions?.nodes ?? []) as DiscussionNode[];
|
|
||||||
}
|
|
||||||
|
|
||||||
const discussions = await fetchDiscussionStats();
|
|
||||||
|
|
||||||
const sortedPosts = [...allPosts].sort(
|
const sortedPosts = [...allPosts].sort(
|
||||||
(a, b) =>
|
(a, b) =>
|
||||||
new Date(b.data.pubDate).getTime() - new Date(a.data.pubDate).getTime(),
|
new Date(b.data.pubDate).getTime() - new Date(a.data.pubDate).getTime(),
|
||||||
|
|
@ -97,30 +29,14 @@ const latestPosts = filteredPosts.slice(0, 5);
|
||||||
const [postLang, ...slugParts] = post.id.split("/");
|
const [postLang, ...slugParts] = post.id.split("/");
|
||||||
const slug = slugParts.join("/");
|
const slug = slugParts.join("/");
|
||||||
|
|
||||||
const pathname = `/${slug}/`;
|
|
||||||
|
|
||||||
const matchedDiscussion = discussions.find((d: DiscussionNode) => {
|
|
||||||
return normalizePath(d.title) === normalizePath(pathname);
|
|
||||||
});
|
|
||||||
|
|
||||||
const reactionCount =
|
|
||||||
matchedDiscussion?.reactionGroups?.reduce(
|
|
||||||
(sum: number, group: ReactionGroup) =>
|
|
||||||
sum + (group.users?.totalCount ?? 0),
|
|
||||||
0,
|
|
||||||
) ?? 0;
|
|
||||||
|
|
||||||
const commentCount = matchedDiscussion?.comments?.totalCount ?? 0;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PostItem
|
<PostItem
|
||||||
url={`/${postLang}/posts/${slug}/`}
|
url={`/${postLang}/posts/${slug}/`}
|
||||||
|
postPath={`/posts/${slug}/`}
|
||||||
title={post.data.title}
|
title={post.data.title}
|
||||||
description={post.data.description}
|
description={post.data.description}
|
||||||
date={formattedDate}
|
date={formattedDate}
|
||||||
img={post.data.image.url}
|
img={post.data.image.url}
|
||||||
commentCount={commentCount}
|
|
||||||
reactionCount={reactionCount}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
|
|
||||||
30
src/components/remark42-counter.svelte
Normal file
30
src/components/remark42-counter.svelte
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
<script>
|
||||||
|
export let url;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<script async lang="javascript">
|
||||||
|
var remark_config = {
|
||||||
|
host: "https://comments.cloverta.top",
|
||||||
|
site_id: "cloverta-blog",
|
||||||
|
components: ["counter"],
|
||||||
|
show_rss_subscription: false,
|
||||||
|
theme: localStorage.getItem("color-theme") ?? "light",
|
||||||
|
};
|
||||||
|
!(function (e, n) {
|
||||||
|
for (var o = 0; o < e.length; o++) {
|
||||||
|
var r = n.createElement("script"),
|
||||||
|
c = ".js",
|
||||||
|
d = n.head || n.body;
|
||||||
|
"noModule" in r
|
||||||
|
? ((r.type = "module"), (c = ".mjs"))
|
||||||
|
: (r.async = !0),
|
||||||
|
(r.defer = !0),
|
||||||
|
(r.src = remark_config.host + "/web/" + e[o] + c),
|
||||||
|
d.appendChild(r);
|
||||||
|
}
|
||||||
|
})(remark_config.components || ["embed"], document);
|
||||||
|
</script>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<span class="remark42__counter" data-url={url}></span>
|
||||||
41
src/components/remark42-embed.svelte
Normal file
41
src/components/remark42-embed.svelte
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
<script>
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
|
||||||
|
export let slug;
|
||||||
|
|
||||||
|
let pagePath = `/posts/${slug}/`;
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
const scriptId = "remark42-script";
|
||||||
|
|
||||||
|
window.remark_config = {
|
||||||
|
host: "https://comments.cloverta.top",
|
||||||
|
site_id: "cloverta-blog",
|
||||||
|
components: ["embed"],
|
||||||
|
show_rss_subscription: false,
|
||||||
|
theme: localStorage.getItem("color-theme") ?? "light",
|
||||||
|
url: pagePath,
|
||||||
|
page_id: pagePath,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (document.getElementById(scriptId)) return;
|
||||||
|
|
||||||
|
const script = document.createElement("script");
|
||||||
|
script.id = scriptId;
|
||||||
|
script.async = true;
|
||||||
|
script.innerHTML = `
|
||||||
|
!function(e,n){
|
||||||
|
for(var o=0;o<e.length;o++){
|
||||||
|
var r=n.createElement("script"),c=".js",d=n.head||n.body;
|
||||||
|
"noModule"in r?(r.type="module",c=".mjs"):r.async=!0;
|
||||||
|
r.defer=!0;
|
||||||
|
r.src=window.remark_config.host+"/web/"+e[o]+c;
|
||||||
|
d.appendChild(r);
|
||||||
|
}
|
||||||
|
}(window.remark_config.components||["embed"],document);
|
||||||
|
`;
|
||||||
|
document.body.appendChild(script);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div id="remark42"></div>
|
||||||
231
src/components/remark42-latest-comments.svelte
Normal file
231
src/components/remark42-latest-comments.svelte
Normal file
|
|
@ -0,0 +1,231 @@
|
||||||
|
<script>
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
|
||||||
|
export let lang = "zh";
|
||||||
|
let loaded = false;
|
||||||
|
|
||||||
|
function rewriteLinks() {
|
||||||
|
const links = document.querySelectorAll(
|
||||||
|
".latest-comments .comment__title-link",
|
||||||
|
);
|
||||||
|
|
||||||
|
links.forEach((link) => {
|
||||||
|
const href = link.getAttribute("href");
|
||||||
|
if (!href) return;
|
||||||
|
|
||||||
|
// 只改 /posts/... 这种相对路径
|
||||||
|
if (href.startsWith("/posts/")) {
|
||||||
|
link.setAttribute("href", `/${lang}${href}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
window.remark_config = {
|
||||||
|
host: "https://comments.cloverta.top",
|
||||||
|
site_id: "cloverta-blog",
|
||||||
|
components: ["last-comments"],
|
||||||
|
theme: localStorage.getItem("color-theme") ?? "light",
|
||||||
|
max_last_comments: 10,
|
||||||
|
no_footer: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const loaderId = "remark42-last-comments-loader";
|
||||||
|
if (!document.getElementById(loaderId)) {
|
||||||
|
const init = document.createElement("script");
|
||||||
|
init.id = loaderId;
|
||||||
|
init.innerHTML = `
|
||||||
|
!function(e,n){
|
||||||
|
for(var o=0;o<e.length;o++){
|
||||||
|
var r=n.createElement("script"),c=".js",d=n.head||n.body;
|
||||||
|
"noModule"in r?(r.type="module",c=".mjs"):r.async=!0;
|
||||||
|
r.defer=!0;
|
||||||
|
r.src=window.remark_config.host+"/web/"+e[o]+c;
|
||||||
|
d.appendChild(r);
|
||||||
|
}
|
||||||
|
}(window.remark_config.components||["last-comments"],document);
|
||||||
|
`;
|
||||||
|
document.body.appendChild(init);
|
||||||
|
}
|
||||||
|
|
||||||
|
const timer = setInterval(() => {
|
||||||
|
const root = document.querySelector(".remark42__last-comments");
|
||||||
|
if (root && root.children.length > 0) {
|
||||||
|
rewriteLinks();
|
||||||
|
loaded = true;
|
||||||
|
clearInterval(timer);
|
||||||
|
}
|
||||||
|
}, 200);
|
||||||
|
|
||||||
|
return () => clearInterval(timer);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<section class="latest-comments">
|
||||||
|
<h2>最新评论</h2>
|
||||||
|
|
||||||
|
{#if !loaded}
|
||||||
|
<div class="comments-loading" aria-hidden="true">
|
||||||
|
<div class="loading-card"></div>
|
||||||
|
<div class="loading-card"></div>
|
||||||
|
<div class="loading-card"></div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<div class="remark42__last-comments" data-max="10"></div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.loading-card {
|
||||||
|
height: 92px;
|
||||||
|
margin: 0 0 14px 0;
|
||||||
|
padding: 0.9rem 1rem;
|
||||||
|
border: 1px dashed #aeb8c2;
|
||||||
|
background: linear-gradient(
|
||||||
|
90deg,
|
||||||
|
rgba(160, 175, 190, 0.06) 25%,
|
||||||
|
rgba(160, 175, 190, 0.16) 50%,
|
||||||
|
rgba(160, 175, 190, 0.06) 75%
|
||||||
|
);
|
||||||
|
background-size: 200% 100%;
|
||||||
|
animation: shimmer 1.2s infinite linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes shimmer {
|
||||||
|
from {
|
||||||
|
background-position: 200% 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
background-position: -200% 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.latest-comments {
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.latest-comments h2 {
|
||||||
|
margin-bottom: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.latest-comments :global(.remark42__last-comments) {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.latest-comments :global(article.comment.list-comments__item) {
|
||||||
|
display: block;
|
||||||
|
margin: 0 0 14px 0;
|
||||||
|
padding: 0.9rem 1rem;
|
||||||
|
border: 1px dashed #aeb8c2;
|
||||||
|
background: rgba(160, 175, 190, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.latest-comments :global(article.comment.list-comments__item:last-child) {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.dark)
|
||||||
|
.latest-comments
|
||||||
|
:global(article.comment.list-comments__item) {
|
||||||
|
background: #2a3138;
|
||||||
|
border-color: #7f8c97;
|
||||||
|
}
|
||||||
|
|
||||||
|
.latest-comments :global(.comment__body) {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr) 320px;
|
||||||
|
grid-template-areas:
|
||||||
|
"info title"
|
||||||
|
"text title";
|
||||||
|
gap: 0.35rem 1rem;
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.latest-comments :global(.comment__title) {
|
||||||
|
grid-area: title;
|
||||||
|
text-align: right;
|
||||||
|
min-width: 0;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.latest-comments :global(.comment__title-link) {
|
||||||
|
color: #6f8090;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 0.87rem;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1.45;
|
||||||
|
white-space: normal;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.latest-comments :global(.comment__title-link:hover) {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.latest-comments :global(.comment__info) {
|
||||||
|
grid-area: info;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1.3;
|
||||||
|
color: inherit;
|
||||||
|
min-width: 0;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
word-break: break-word;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.latest-comments :global(.comment__info)::before {
|
||||||
|
content: "@";
|
||||||
|
margin-right: 0.08em;
|
||||||
|
opacity: 0.85;
|
||||||
|
}
|
||||||
|
|
||||||
|
.latest-comments :global(.comment__text) {
|
||||||
|
grid-area: text;
|
||||||
|
margin: 0;
|
||||||
|
color: #555;
|
||||||
|
line-height: 1.7;
|
||||||
|
font-size: 1.03rem;
|
||||||
|
min-width: 0;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
word-break: break-word;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.dark)
|
||||||
|
.latest-comments
|
||||||
|
:global(article.comment.list-comments__item) {
|
||||||
|
border-color: #7f8c97;
|
||||||
|
background: rgba(180, 190, 200, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.dark) .latest-comments :global(.comment__title-link) {
|
||||||
|
color: #c8d2dc;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.dark) .latest-comments :global(.comment__text) {
|
||||||
|
color: #d3d7db;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.latest-comments :global(.comment__body) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
grid-template-areas:
|
||||||
|
"info"
|
||||||
|
"title"
|
||||||
|
"text";
|
||||||
|
}
|
||||||
|
|
||||||
|
.latest-comments :global(.comment__title) {
|
||||||
|
text-align: left;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.latest-comments :global(.comment__title-link) {
|
||||||
|
display: block;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
---
|
---
|
||||||
import BaseLayout from "./BaseLayout.astro";
|
import BaseLayout from "./BaseLayout.astro";
|
||||||
import Giscus from "@/components/Giscus.astro";
|
import Remark42Embed from "@/components/remark42-embed.svelte";
|
||||||
import { getLangFromUrl, getTranslations } from "@/i18n";
|
import { getLangFromUrl, getTranslations } from "@/i18n";
|
||||||
import "@/styles/global.css";
|
import "@/styles/global.css";
|
||||||
|
|
||||||
const { frontmatter, lang, postId } = Astro.props;
|
const { frontmatter, lang, postId } = Astro.props;
|
||||||
|
const comments = lang === "zh" ? "评论区" : "comments";
|
||||||
const t = getTranslations(lang);
|
const t = getTranslations(lang);
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -47,7 +48,8 @@ const t = getTranslations(lang);
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Giscus term={postId} />
|
<h2>{comments}</h2>
|
||||||
|
<Remark42Embed slug={postId} client:load />
|
||||||
</article>
|
</article>
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
import BaseLayout from "@/layouts/BaseLayout.astro";
|
import BaseLayout from "@/layouts/BaseLayout.astro";
|
||||||
import PostList from "@/components/Posts/PostList.astro";
|
import PostList from "@/components/Posts/PostList.astro";
|
||||||
import Comments from "@/components/Comments.astro";
|
import Remark42LatestComments from "@/components/remark42-latest-comments.svelte";
|
||||||
import { getLangFromUrl, getTranslations } from "@/i18n";
|
import { getLangFromUrl, getTranslations } from "@/i18n";
|
||||||
import "@/styles/global.css";
|
import "@/styles/global.css";
|
||||||
|
|
||||||
|
|
@ -50,7 +50,7 @@ const pageTitle = t.home.title;
|
||||||
|
|
||||||
<div class="section-divider"></div>
|
<div class="section-divider"></div>
|
||||||
|
|
||||||
<Comments limit={7} />
|
<Remark42LatestComments lang={lang} client:load />
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
|
||||||
|
|
@ -1,231 +0,0 @@
|
||||||
import type { APIRoute } from "astro";
|
|
||||||
import { getCollection } from "astro:content";
|
|
||||||
|
|
||||||
const token = import.meta.env.GITHUB_TOKEN;
|
|
||||||
const owner = import.meta.env.GISCUS_REPO_OWNER ?? "ClovertaTheTrilobita";
|
|
||||||
const name = import.meta.env.GISCUS_REPO_NAME ?? "SanYeCao-blog";
|
|
||||||
const categoryId = import.meta.env.GISCUS_CATEGORY_ID ?? "DIC_kwDORvuVpM4C5MDE";
|
|
||||||
|
|
||||||
export function getStaticPaths() {
|
|
||||||
return [{ params: { lang: "zh" } }, { params: { lang: "en" } }];
|
|
||||||
}
|
|
||||||
|
|
||||||
type CommentAuthor = {
|
|
||||||
login?: string;
|
|
||||||
avatarUrl?: string;
|
|
||||||
url?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type RawReply = {
|
|
||||||
id: string;
|
|
||||||
body?: string;
|
|
||||||
updatedAt: string;
|
|
||||||
author?: CommentAuthor | null;
|
|
||||||
};
|
|
||||||
|
|
||||||
type RawComment = {
|
|
||||||
id: string;
|
|
||||||
body?: string;
|
|
||||||
updatedAt: string;
|
|
||||||
author?: CommentAuthor | null;
|
|
||||||
replies?: {
|
|
||||||
nodes?: RawReply[];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
type DiscussionNode = {
|
|
||||||
title: string;
|
|
||||||
comments?: {
|
|
||||||
nodes?: RawComment[];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
type LatestCommentItem = {
|
|
||||||
id: string;
|
|
||||||
body?: string;
|
|
||||||
updatedAt: string;
|
|
||||||
author?: CommentAuthor | null;
|
|
||||||
postId: string;
|
|
||||||
postTitle: string;
|
|
||||||
localUrl: string;
|
|
||||||
isReply: boolean;
|
|
||||||
replyTo?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
function normalizePath(path: string) {
|
|
||||||
return path.replace(/^\/+|\/+$/g, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
function excerpt(text = "", max = 110) {
|
|
||||||
const plain = text.replace(/\s+/g, " ").trim();
|
|
||||||
if (plain.length <= max) return plain;
|
|
||||||
return plain.slice(0, max) + "…";
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchLatestComments(lang: string, limit: number): Promise<LatestCommentItem[]> {
|
|
||||||
if (!token) return [];
|
|
||||||
|
|
||||||
const allPosts = await getCollection("blog");
|
|
||||||
|
|
||||||
const query = `
|
|
||||||
query($owner: String!, $name: String!, $categoryId: ID!) {
|
|
||||||
repository(owner: $owner, name: $name) {
|
|
||||||
discussions(
|
|
||||||
first: 20
|
|
||||||
categoryId: $categoryId
|
|
||||||
orderBy: { field: UPDATED_AT, direction: DESC }
|
|
||||||
) {
|
|
||||||
nodes {
|
|
||||||
title
|
|
||||||
comments(last: 10) {
|
|
||||||
nodes {
|
|
||||||
id
|
|
||||||
body
|
|
||||||
updatedAt
|
|
||||||
author {
|
|
||||||
... on User {
|
|
||||||
login
|
|
||||||
avatarUrl
|
|
||||||
url
|
|
||||||
}
|
|
||||||
... on Organization {
|
|
||||||
login
|
|
||||||
avatarUrl
|
|
||||||
url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
replies(first: 10) {
|
|
||||||
nodes {
|
|
||||||
id
|
|
||||||
body
|
|
||||||
updatedAt
|
|
||||||
author {
|
|
||||||
... on User {
|
|
||||||
login
|
|
||||||
avatarUrl
|
|
||||||
url
|
|
||||||
}
|
|
||||||
... on Organization {
|
|
||||||
login
|
|
||||||
avatarUrl
|
|
||||||
url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const res = await fetch("https://api.github.com/graphql", {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
query,
|
|
||||||
variables: { owner, name, categoryId },
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
const json = await res.json();
|
|
||||||
const discussions = (json?.data?.repository?.discussions?.nodes ?? []) as DiscussionNode[];
|
|
||||||
|
|
||||||
const postMap = new Map(
|
|
||||||
allPosts.map((post: any) => [
|
|
||||||
normalizePath(post.id),
|
|
||||||
{
|
|
||||||
title: post.data.title,
|
|
||||||
url: `/${lang}/posts/${post.id}/#comments`,
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
|
|
||||||
const comments: LatestCommentItem[] = [];
|
|
||||||
|
|
||||||
for (const discussion of discussions) {
|
|
||||||
const discussionKey = normalizePath(discussion.title);
|
|
||||||
|
|
||||||
let matchedPostId: string | undefined;
|
|
||||||
|
|
||||||
if (postMap.has(discussionKey)) {
|
|
||||||
matchedPostId = discussionKey;
|
|
||||||
} else {
|
|
||||||
matchedPostId = [...postMap.keys()].find(
|
|
||||||
(postId) =>
|
|
||||||
discussionKey === postId ||
|
|
||||||
discussionKey.includes(postId) ||
|
|
||||||
postId.includes(discussionKey),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!matchedPostId) continue;
|
|
||||||
|
|
||||||
const postInfo = postMap.get(matchedPostId);
|
|
||||||
if (!postInfo) continue;
|
|
||||||
|
|
||||||
for (const comment of discussion.comments?.nodes ?? []) {
|
|
||||||
comments.push({
|
|
||||||
id: comment.id,
|
|
||||||
body: excerpt(comment.body),
|
|
||||||
updatedAt: comment.updatedAt,
|
|
||||||
author: comment.author,
|
|
||||||
postId: matchedPostId,
|
|
||||||
postTitle: postInfo.title,
|
|
||||||
localUrl: postInfo.url,
|
|
||||||
isReply: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const reply of comment.replies?.nodes ?? []) {
|
|
||||||
comments.push({
|
|
||||||
id: reply.id,
|
|
||||||
body: excerpt(reply.body),
|
|
||||||
updatedAt: reply.updatedAt,
|
|
||||||
author: reply.author,
|
|
||||||
postId: matchedPostId,
|
|
||||||
postTitle: postInfo.title,
|
|
||||||
localUrl: postInfo.url,
|
|
||||||
isReply: true,
|
|
||||||
replyTo: comment.author?.login ?? "Unknown",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return comments
|
|
||||||
.sort(
|
|
||||||
(a, b) =>
|
|
||||||
new Date(b.updatedAt).getTime() -
|
|
||||||
new Date(a.updatedAt).getTime(),
|
|
||||||
)
|
|
||||||
.slice(0, limit);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const GET: APIRoute = async ({ params, url }) => {
|
|
||||||
try {
|
|
||||||
const lang = params.lang === "en" ? "en" : "zh";
|
|
||||||
const limit = Number(url.searchParams.get("limit") ?? 5);
|
|
||||||
|
|
||||||
const comments = await fetchLatestComments(lang, limit);
|
|
||||||
|
|
||||||
return new Response(JSON.stringify({ comments }), {
|
|
||||||
status: 200,
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json; charset=utf-8",
|
|
||||||
"Cache-Control": "public, max-age=300",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
return new Response(JSON.stringify({ comments: [], error: "Failed to load comments" }), {
|
|
||||||
status: 500,
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json; charset=utf-8",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -85,7 +85,7 @@ html.dark body::after {
|
||||||
background-image: url("https://files.seeusercontent.com/2026/03/30/vd8W/touhou___alice_margatroid_x_kiri.webp");
|
background-image: url("https://files.seeusercontent.com/2026/03/30/vd8W/touhou___alice_margatroid_x_kiri.webp");
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 900px) {
|
||||||
body::after {
|
body::after {
|
||||||
opacity: 0.06;
|
opacity: 0.06;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
5
svelte.config.js
Normal file
5
svelte.config.js
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
import { vitePreprocess } from "@astrojs/svelte";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
preprocess: vitePreprocess(),
|
||||||
|
};
|
||||||
Loading…
Reference in a new issue