mirror of
https://github.com/ClovertaTheTrilobita/SanYeCao-blog.git
synced 2026-07-03 15:41:26 +00:00
Compare commits
6 commits
d7fa430317
...
4b1726ac00
| Author | SHA1 | Date | |
|---|---|---|---|
| 4b1726ac00 | |||
| 9b1e1f6b93 | |||
| f44d976330 | |||
| 69e0e4e666 | |||
| 07a42ea123 | |||
| fc005b4944 |
23 changed files with 137 additions and 3175 deletions
|
|
@ -1,95 +0,0 @@
|
|||
import fs from "fs";
|
||||
import path from "path";
|
||||
import Fontmin from "fontmin";
|
||||
|
||||
function getFiles(dir) {
|
||||
const results = [];
|
||||
const list = fs.readdirSync(dir);
|
||||
|
||||
for (const file of list) {
|
||||
const filePath = path.join(dir, file);
|
||||
const stat = fs.statSync(filePath);
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
results.push(...getFiles(filePath));
|
||||
} else {
|
||||
results.push(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
function scanDirectory(dir) {
|
||||
let set = new Set();
|
||||
const files = getFiles(dir);
|
||||
|
||||
for (const file of files) {
|
||||
const ignoredExtensions = [
|
||||
".ttf",
|
||||
".otf",
|
||||
".woff",
|
||||
".woff2",
|
||||
".eot",
|
||||
".png",
|
||||
".jpg",
|
||||
".jpeg",
|
||||
".webp",
|
||||
".gif",
|
||||
".ico",
|
||||
".pdf",
|
||||
];
|
||||
|
||||
if (ignoredExtensions.some((ext) => file.endsWith(ext))) continue;
|
||||
|
||||
try {
|
||||
const content = fs.readFileSync(file, "utf8");
|
||||
const currentSet = new Set(content);
|
||||
set = new Set([...set, ...currentSet]);
|
||||
} catch {
|
||||
// 跳过二进制等不可读文件
|
||||
}
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
function subsetFont(src, text) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const fontmin = new Fontmin()
|
||||
.src(src)
|
||||
.use(
|
||||
Fontmin.glyph({
|
||||
text,
|
||||
hinting: false,
|
||||
})
|
||||
)
|
||||
.use(Fontmin.ttf2woff2())
|
||||
.dest("public/fonts/subset");
|
||||
|
||||
fontmin.run((err) => {
|
||||
if (err) return reject(err);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const baseChars =
|
||||
"首页文章标签关于作者评论发布于切换主题,。!?:“”‘’()《》【】、—…·-_/\\'\"()[]{}<>:;.!? ";
|
||||
const scanned = Array.from(scanDirectory("src")).join("");
|
||||
const chars = Array.from(new Set((scanned + baseChars).split(""))).join("");
|
||||
|
||||
await Promise.all([
|
||||
subsetFont("public/fonts/MapleMono-CN-Regular.ttf", chars),
|
||||
subsetFont("public/fonts/MapleMono-CN-Bold.ttf", chars),
|
||||
subsetFont("public/fonts/MapleMono-CN-Italic.ttf", chars),
|
||||
]);
|
||||
|
||||
console.log(`中文子集字体生成完成,共收集 ${chars.length} 个字符`);
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
3020
package-lock.json
generated
3020
package-lock.json
generated
File diff suppressed because it is too large
Load diff
11
package.json
11
package.json
|
|
@ -9,8 +9,7 @@
|
|||
"dev": "astro dev",
|
||||
"preview": "astro preview",
|
||||
"astro": "astro",
|
||||
"subset-font": "node build/fontmin.js",
|
||||
"build": "npm run subset-font && astro build"
|
||||
"build": "astro build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/rss": "^4.0.17",
|
||||
|
|
@ -18,11 +17,9 @@
|
|||
"@astrojs/svelte": "^8.0.4",
|
||||
"astro": "^6.0.8",
|
||||
"playwright": "^1.59.1",
|
||||
"rehype-mermaid": "^3.0.0",
|
||||
"url": "^0.11.4"
|
||||
"rehype-mermaid": "^3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^25.5.0",
|
||||
"fontmin": "^1.1.1"
|
||||
"@types/node": "^25.5.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -12,14 +12,14 @@ const tags = data.tags;
|
|||
<div class="post-link">
|
||||
<div class="post-text">
|
||||
<a href={data.url} class="post-title-link">
|
||||
<h2 class="post-title" transition:name={`post-title-${data.slug}`}>
|
||||
<h2 class="post-title" transition:name={`post-title-${data.postId}`}>
|
||||
{data.title}
|
||||
</h2>
|
||||
</a>
|
||||
<a href={data.url} class="post-description">
|
||||
{data.description}
|
||||
</a>
|
||||
<div class="tags">
|
||||
<div class="tags" transition:name={`post-tags-${data.postId}`}>
|
||||
{
|
||||
tags.map((tag: string) => (
|
||||
<p class="tag">
|
||||
|
|
@ -40,7 +40,13 @@ const tags = data.tags;
|
|||
</div>
|
||||
|
||||
<a href={data.url} class="post-image-link">
|
||||
<img src={data.img} alt={data.title} class="post-image" loading="lazy" />
|
||||
<img
|
||||
src={data.img}
|
||||
alt={data.title}
|
||||
class="post-image"
|
||||
loading="lazy"
|
||||
transition:name={`post-image-${data.postId}`}
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
|
|
@ -167,7 +173,7 @@ const tags = data.tags;
|
|||
text-decoration: none;
|
||||
font-weight: 400;
|
||||
font-size: 0.92rem;
|
||||
font-style: italic;
|
||||
/* font-style: italic; */
|
||||
}
|
||||
|
||||
.post-meta-row {
|
||||
|
|
@ -206,7 +212,7 @@ const tags = data.tags;
|
|||
aspect-ratio: 16 / 10;
|
||||
object-fit: cover;
|
||||
object-position: center;
|
||||
border: 2px #94a0ab dashed;
|
||||
border: 1.5px #94a0ab dashed;
|
||||
flex-shrink: 0;
|
||||
display: block;
|
||||
}
|
||||
|
|
@ -256,7 +262,7 @@ const tags = data.tags;
|
|||
}
|
||||
|
||||
.post-image {
|
||||
border: none;
|
||||
/* border: none; */
|
||||
grid-area: image;
|
||||
width: 100%;
|
||||
height: calc(145px * 10 / 16);
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ const latestPosts = filteredPosts.slice(0, 7);
|
|||
img={post.data.image.url}
|
||||
tags={post.data.tags}
|
||||
slug={slug}
|
||||
postId={post.id}
|
||||
/>
|
||||
);
|
||||
})
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ const groupedPosts = filteredPosts.reduce((acc: any[], post: any) => {
|
|||
>
|
||||
<h2
|
||||
class="post-title"
|
||||
transition:name={`post-title-${slug}`}
|
||||
transition:name={`post-title-${post.id}`}
|
||||
>
|
||||
{post.data.title}
|
||||
</h2>
|
||||
|
|
@ -147,7 +147,8 @@ const groupedPosts = filteredPosts.reduce((acc: any[], post: any) => {
|
|||
margin: 0;
|
||||
font-size: 0.88rem;
|
||||
color: #555;
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
/* font-style: italic; */
|
||||
line-height: 1.5;
|
||||
overflow-wrap: anywhere;
|
||||
word-break: break-word;
|
||||
|
|
|
|||
|
|
@ -70,15 +70,15 @@ const switchHref = "/" + segments.join("/");
|
|||
}
|
||||
|
||||
.lang-switch {
|
||||
color: black; !important
|
||||
font-weight: 700; !important
|
||||
font-size: 0.95rem; !important
|
||||
text-decoration: none; !important
|
||||
line-height: 1; !important
|
||||
color: black;
|
||||
font-weight: 700;
|
||||
font-size: 0.95rem;
|
||||
text-decoration: none;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.lang-switch:hover {
|
||||
text-decoration: underline; !important
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.sun {
|
||||
|
|
@ -104,37 +104,37 @@ const switchHref = "/" + segments.join("/");
|
|||
|
||||
<script is:inline>
|
||||
function applyTheme() {
|
||||
const localStorageTheme = localStorage?.getItem("theme") ?? "";
|
||||
let theme = "light";
|
||||
const localStorageTheme = localStorage?.getItem("theme") ?? "";
|
||||
let theme = "light";
|
||||
|
||||
if (["dark", "light"].includes(localStorageTheme)) {
|
||||
theme = localStorageTheme;
|
||||
} else if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
|
||||
theme = "dark";
|
||||
if (["dark", "light"].includes(localStorageTheme)) {
|
||||
theme = localStorageTheme;
|
||||
} else if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
|
||||
theme = "dark";
|
||||
}
|
||||
|
||||
document.documentElement.classList.toggle("dark", theme === "dark");
|
||||
window.localStorage.setItem("theme", theme);
|
||||
}
|
||||
|
||||
document.documentElement.classList.toggle("dark", theme === "dark");
|
||||
window.localStorage.setItem("theme", theme);
|
||||
}
|
||||
function bindThemeToggle() {
|
||||
const button = document.getElementById("themeToggle");
|
||||
if (!button) return;
|
||||
|
||||
function bindThemeToggle() {
|
||||
const button = document.getElementById("themeToggle");
|
||||
if (!button) return;
|
||||
button.onclick = () => {
|
||||
const element = document.documentElement;
|
||||
element.classList.toggle("dark");
|
||||
|
||||
button.onclick = () => {
|
||||
const element = document.documentElement;
|
||||
element.classList.toggle("dark");
|
||||
const isDark = element.classList.contains("dark");
|
||||
localStorage.setItem("theme", isDark ? "dark" : "light");
|
||||
};
|
||||
}
|
||||
|
||||
const isDark = element.classList.contains("dark");
|
||||
localStorage.setItem("theme", isDark ? "dark" : "light");
|
||||
};
|
||||
}
|
||||
|
||||
applyTheme();
|
||||
bindThemeToggle();
|
||||
|
||||
document.addEventListener("astro:after-swap", () => {
|
||||
applyTheme();
|
||||
bindThemeToggle();
|
||||
});
|
||||
|
||||
document.addEventListener("astro:after-swap", () => {
|
||||
applyTheme();
|
||||
bindThemeToggle();
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -2,5 +2,5 @@
|
|||
name: 'CLovertaTheTrilobita'
|
||||
description: "This is Cloverta's blog"
|
||||
url: "https://blog.cloverta.top"
|
||||
avatar: '/images/marisa.png'
|
||||
avatar: '/images/marisa.png'
|
||||
---
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
---
|
||||
import BaseLayout from "./BaseLayout.astro";
|
||||
import Remark42Embed from "@/components/Remark42Embed.astro";
|
||||
import { fade } from "astro:transitions";
|
||||
import { getLangFromUrl, getTranslations } from "@/i18n";
|
||||
import "@/styles/global.css";
|
||||
|
||||
const { frontmatter, lang, postId } = Astro.props;
|
||||
const { frontmatter, lang, slug, postId } = Astro.props;
|
||||
const comments = lang === "zh" ? "评论区" : "comments";
|
||||
const t = getTranslations(lang);
|
||||
---
|
||||
|
|
@ -14,7 +15,7 @@ const t = getTranslations(lang);
|
|||
description={frontmatter.description}
|
||||
image={frontmatter.image?.url}
|
||||
>
|
||||
<div id="reading-progress" aria-hidden="true"></div>
|
||||
<div id="reading-progress" aria-hidden="true"></div>
|
||||
<a
|
||||
href="/"
|
||||
class="back-button"
|
||||
|
|
@ -39,14 +40,18 @@ const t = getTranslations(lang);
|
|||
<article class="post-article">
|
||||
<div class="post-header">
|
||||
<div class="post-meta">
|
||||
<h1 class="post-title" transition:name={`post-title-${postId}`} >{frontmatter.title}</h1>
|
||||
<p class="description"><em>{frontmatter.description}</em></p>
|
||||
<h1 class="post-title" transition:name={`post-title-${postId}`}>
|
||||
{frontmatter.title}
|
||||
</h1>
|
||||
<p class="description">
|
||||
<em>{frontmatter.description}</em>
|
||||
</p>
|
||||
<p class="meta-line">
|
||||
{t.post.publishedOn}: {frontmatter.pubDate.toLocaleDateString()}
|
||||
</p>
|
||||
<p class="meta-line">{t.post.writtenBy}: {frontmatter.author}</p>
|
||||
|
||||
<div class="tags">
|
||||
<div class="tags" transition:name={`post-tags-${postId}`}>
|
||||
{
|
||||
frontmatter.tags.map((tag: string) => (
|
||||
<p class="tag">
|
||||
|
|
@ -61,17 +66,18 @@ const t = getTranslations(lang);
|
|||
src={frontmatter.image.url}
|
||||
alt={frontmatter.image.alt}
|
||||
class="post-cover"
|
||||
transition:name={`post-image-${postId}`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="post-divider"></div>
|
||||
|
||||
<div class="post-content">
|
||||
<div class="post-content" transition:animate={fade({ duration: "0.2s" })}>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<h2>{comments}</h2>
|
||||
<Remark42Embed slug={postId} />
|
||||
<Remark42Embed slug={slug} />
|
||||
</article>
|
||||
</BaseLayout>
|
||||
|
||||
|
|
@ -131,7 +137,7 @@ const t = getTranslations(lang);
|
|||
backButton.dataset.bound = "true";
|
||||
}
|
||||
};
|
||||
const updateProgress = () => {
|
||||
const updateProgress = () => {
|
||||
const doc = document.documentElement;
|
||||
const scrollTop = window.scrollY || doc.scrollTop;
|
||||
const scrollHeight = doc.scrollHeight - window.innerHeight;
|
||||
|
|
@ -152,7 +158,7 @@ const t = getTranslations(lang);
|
|||
</script>
|
||||
|
||||
<style>
|
||||
#reading-progress {
|
||||
#reading-progress {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
|
@ -186,8 +192,8 @@ const t = getTranslations(lang);
|
|||
/* opacity: 0; */
|
||||
transition:
|
||||
opacity 0.2s ease,
|
||||
transform 0.27s ease,
|
||||
visibility 0.2s ease;
|
||||
transform 0.27s ease,
|
||||
visibility 0.2s ease,
|
||||
box-shadow 0.2s ease,
|
||||
background 0.2s ease,
|
||||
color 0.2s ease;
|
||||
|
|
@ -284,10 +290,11 @@ const t = getTranslations(lang);
|
|||
.post-cover {
|
||||
width: 300px;
|
||||
height: auto;
|
||||
aspect-ratio: 16 / 9;
|
||||
aspect-ratio: 16 / 10;
|
||||
object-fit: cover;
|
||||
object-position: center;
|
||||
border-radius: 0.4rem;
|
||||
/* border-radius: 0.4rem; */
|
||||
border: 1.5px #94a0ab dashed;
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
|
@ -346,6 +353,7 @@ const t = getTranslations(lang);
|
|||
.post-cover {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
aspect-ratio: 16 / 9;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ const pageTitle = t.home.title;
|
|||
width="33"
|
||||
height="33"
|
||||
aria-hidden="true"
|
||||
style="margin-left: -15px; margin-bottom: -5px; color:#3f50e5"
|
||||
style="margin-left: 0px; margin-bottom: -5px; color:#3f50e5"
|
||||
>
|
||||
<path
|
||||
d="M6.18 17.82a1.64 1.64 0 1 1 0 3.28 1.64 1.64 0 0 1 0-3.28ZM3 10.44v2.25c4.56 0 8.27 3.71 8.27 8.27h2.25C13.52 15.16 8.84 10.44 3 10.44Zm0-4.54v2.25c7.06 0 12.81 5.75 12.81 12.81h2.25C18.06 12.66 11.3 5.9 3 5.9Z"
|
||||
|
|
@ -66,4 +66,8 @@ const pageTitle = t.home.title;
|
|||
:global(.dark) .section-divider {
|
||||
background: #7f8b97;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-weight: 700;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -28,6 +28,11 @@ const [postLang, ...slugParts] = post.id.split("/");
|
|||
const slug = slugParts.join("/");
|
||||
---
|
||||
|
||||
<MarkdownPostLayout frontmatter={post.data} lang={lang} postId={slug}>
|
||||
<MarkdownPostLayout
|
||||
frontmatter={post.data}
|
||||
lang={lang}
|
||||
slug={slug}
|
||||
postId={post.id}
|
||||
>
|
||||
<Content />
|
||||
</MarkdownPostLayout>
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ const t = getTranslations(lang);
|
|||
<BaseLayout pageTitle={String(tag)}>
|
||||
<p class="tag-heading">
|
||||
{lang === "zh" ? "带有标签" : "Posts tagged with"}{" "}
|
||||
<span class="tag-chip">{tag}</span>
|
||||
<span class="tag-chip" transition:name={`post-tags-${tag}`}>{tag}</span>
|
||||
{lang === "zh" ? "的文章" : ""}
|
||||
</p>
|
||||
<ul>
|
||||
|
|
@ -54,6 +54,7 @@ const t = getTranslations(lang);
|
|||
date={formattedDate}
|
||||
img={post.data.image.url}
|
||||
tags={post.data.tags}
|
||||
postId={post.id}
|
||||
/>
|
||||
);
|
||||
})
|
||||
|
|
|
|||
|
|
@ -15,14 +15,14 @@ const tags = [...new Set(allPosts.map((post: any) => post.data.tags).flat())];
|
|||
const pageTitle = lang === "zh" ? "标签索引" : "Tag Index";
|
||||
---
|
||||
|
||||
<BaseLayout pageTitle=`${pageTitle} - ${t.banner.title}` `>
|
||||
<BaseLayout pageTitle=`${pageTitle} - ${t.banner.title}`>
|
||||
<h1>{t.tags.title}</h1>
|
||||
<p>{t.tags.description}</p>
|
||||
|
||||
<div class="tags">
|
||||
{
|
||||
tags.map((tag) => (
|
||||
<p class="tag">
|
||||
<p class="tag" transition:name={`post-tags-${tag}`}>
|
||||
<a href={`/${lang}/tags/${tag}`}>{tag}</a>
|
||||
</p>
|
||||
))
|
||||
|
|
|
|||
|
|
@ -2,30 +2,6 @@
|
|||
@import url("https://unpkg.com/@fontsource/maple-mono@5.2.6/400-italic.css");
|
||||
@import url("https://unpkg.com/@fontsource/maple-mono@5.2.6/700.css");
|
||||
|
||||
@font-face {
|
||||
font-family: "Maple Mono CN";
|
||||
src: url("/fonts/subset/MapleMono-CN-Regular.woff2") format("woff2");
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Maple Mono CN";
|
||||
src: url("/fonts/subset/MapleMono-CN-Italic.woff2") format("woff2");
|
||||
font-weight: 400;
|
||||
font-style: italic;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Maple Mono CN";
|
||||
src: url("/fonts/subset/MapleMono-CN-Bold.woff2") format("woff2");
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
pre {
|
||||
padding: 1rem;
|
||||
border-radius: 0.5rem;
|
||||
|
|
@ -57,7 +33,19 @@ article svg[id^="mermaid-"] {
|
|||
}
|
||||
|
||||
html {
|
||||
font-family: "Maple Mono", "Maple Mono CN", monospace;
|
||||
/* font-family: "Maple Mono", "Maple Mono CN", monospace; */
|
||||
font-family:
|
||||
system-ui,
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
"Segoe UI",
|
||||
Roboto,
|
||||
Oxygen,
|
||||
Ubuntu,
|
||||
Cantarell,
|
||||
"Open Sans",
|
||||
"Helvetica Neue",
|
||||
sans-serif;
|
||||
background-color: #ffffff;
|
||||
color: #1f2328;
|
||||
}
|
||||
|
|
@ -101,7 +89,7 @@ html.dark body::after {
|
|||
|
||||
@media (max-width: 900px) {
|
||||
body::after {
|
||||
opacity: 0.1;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -113,10 +101,6 @@ html.dark body::after {
|
|||
body {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
body::after {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue