Compare commits

...

11 commits

Author SHA1 Message Date
c95b9c978a Merge branch 'dev'
Some checks are pending
Sync and Deploy Astro Blog / sync-blog-content (push) Waiting to run
Sync and Deploy Astro Blog / build-and-deploy (push) Blocked by required conditions
Sync and Deploy Astro Blog / manual-deploy (push) Waiting to run
2026-04-16 03:04:51 +03:00
4b1726ac00 remove uneeded dep 2026-04-16 03:04:35 +03:00
983ef3bfb5 Merge branch 'dev' 2026-04-16 02:54:46 +03:00
9b1e1f6b93 remove uneeded packages 2026-04-16 02:53:33 +03:00
0a239f53c2 Merge branch 'dev' 2026-04-16 02:48:34 +03:00
f44d976330 merge test 2026-04-16 02:48:19 +03:00
ClovertaTheTrilobita
e3b38c20f0
Merge pull request #17 from ClovertaTheTrilobita/dev
Expanded transitions
2026-04-16 02:45:36 +03:00
ClovertaTheTrilobita
8168e1fed2
Merge pull request #16 from ClovertaTheTrilobita/dev
Deleted All Maple Mono CN for better performance
2026-04-16 02:44:33 +03:00
69e0e4e666 Expanded transitions 2026-04-16 02:43:42 +03:00
07a42ea123 deleted not needed fonts 2026-04-16 02:10:00 +03:00
fc005b4944 remove maplemono cn 2026-04-16 01:42:44 +03:00
23 changed files with 137 additions and 3175 deletions

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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.

View file

@ -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);

View file

@ -39,6 +39,7 @@ const latestPosts = filteredPosts.slice(0, 7);
img={post.data.image.url}
tags={post.data.tags}
slug={slug}
postId={post.id}
/>
);
})

View file

@ -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;

View file

@ -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>

View file

@ -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'
---

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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}
/>
);
})

View file

@ -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>
))

View file

@ -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;
}
}