mirror of
https://github.com/ClovertaTheTrilobita/SanYeCao-blog.git
synced 2026-04-02 01:54:50 +00:00
Compare commits
No commits in common. "master" and "v1.0.0" have entirely different histories.
44 changed files with 147 additions and 344 deletions
21
LICENSE
21
LICENSE
|
|
@ -1,21 +0,0 @@
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2026 ClovertaTheTrilobita
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
15
README-en.md
15
README-en.md
|
|
@ -14,13 +14,7 @@
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
<p align="center">
|
<img width="1670" height="889" alt="image" src="https://github.com/user-attachments/assets/e98092b4-c97c-48cc-b905-961b24874b2b" />
|
||||||
<img
|
|
||||||
src="./docs/preview-en.png"
|
|
||||||
alt="preview-en"
|
|
||||||
style="max-width: 700px; width: 100%; height: auto;"
|
|
||||||
/>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
|
|
@ -68,10 +62,3 @@ For details, see: <b>[GithubActions-en.md](docs/GithubActions-en.md)</b>.
|
||||||
|
|
||||||
Upload the generated `dist/` directory to your server, and configure `NGINX` to point to `index.html`.
|
Upload the generated `dist/` directory to your server, and configure `NGINX` to point to `index.html`.
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
## ⚖️ License
|
|
||||||
|
|
||||||
The source code in this repository is licensed under the `MIT License`.
|
|
||||||
|
|
||||||
Unless otherwise stated, all blog posts and other original non-code content in this repository are licensed under `CC BY-NC-ND 4.0`.
|
|
||||||
|
|
|
||||||
16
README.md
16
README.md
|
|
@ -15,13 +15,7 @@
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
<p align="center">
|
<img width="1670" height="889" alt="image" src="https://github.com/user-attachments/assets/ded8f5ae-7f48-4b62-a79d-6b482fc77764" />
|
||||||
<img
|
|
||||||
src="./docs/preview-zh.png"
|
|
||||||
alt="preview-zh"
|
|
||||||
style="max-width: 700px; width: 100%; height: auto;"
|
|
||||||
/>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
|
|
@ -68,11 +62,3 @@
|
||||||
#### 手动部署
|
#### 手动部署
|
||||||
|
|
||||||
将生成的`dist/`目录上传至你的服务器,使用`NGINX`指向index.html。
|
将生成的`dist/`目录上传至你的服务器,使用`NGINX`指向index.html。
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
## ⚖️ 许可
|
|
||||||
|
|
||||||
本仓库中的源代码部分采用 `MIT License` 开源。
|
|
||||||
|
|
||||||
除非另有说明,本仓库中的博客文章、其他原创的非代码内容采用 `CC BY-NC-ND 4.0` 协议。
|
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 587 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 565 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 257 KiB |
BIN
public/fonts/MapleMono-Bold.ttf.woff2
Normal file
BIN
public/fonts/MapleMono-Bold.ttf.woff2
Normal file
Binary file not shown.
BIN
public/fonts/MapleMono-Italic.ttf.woff2
Normal file
BIN
public/fonts/MapleMono-Italic.ttf.woff2
Normal file
Binary file not shown.
BIN
public/fonts/MapleMono-Regular.ttf.woff2
Normal file
BIN
public/fonts/MapleMono-Regular.ttf.woff2
Normal file
Binary file not shown.
BIN
public/fonts/RecursiveMonoCslSt-Bold.woff2
Normal file
BIN
public/fonts/RecursiveMonoCslSt-Bold.woff2
Normal file
Binary file not shown.
BIN
public/fonts/RecursiveMonoCslSt-Italic.woff2
Normal file
BIN
public/fonts/RecursiveMonoCslSt-Italic.woff2
Normal file
Binary file not shown.
BIN
public/fonts/RecursiveMonoCslSt-Regular.woff2
Normal file
BIN
public/fonts/RecursiveMonoCslSt-Regular.woff2
Normal file
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.
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 19 KiB |
|
|
@ -1,61 +0,0 @@
|
||||||
---
|
|
||||||
title: "[Study Notes] A Step-by-Step Analysis of Why Peterson's Algorithm Does Not Deadlock"
|
|
||||||
pubDate: 2025-06-10
|
|
||||||
description: "Study notes on Peterson's Algorithm"
|
|
||||||
author: "Cloverta"
|
|
||||||
image:
|
|
||||||
url: "https://files.seeusercontent.com/2026/03/25/sTq9/pasted-image-1774456630694.webp"
|
|
||||||
alt: "zako2"
|
|
||||||
tags: ["Peterson's algorithm", "Operating System"]
|
|
||||||
---
|
|
||||||
|
|
||||||
In my OS course, I came across an interesting algorithm — Peterson's Algorithm. So why does Peterson's Algorithm satisfy the three conditions: "mutual exclusion", "progress", and "bounded waiting"?
|
|
||||||
|
|
||||||
First, here is the pseudocode:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
bool flag[2]; // Array indicating intention to enter critical section, initially false
|
|
||||||
int turn = 0; // turn indicates which process is given priority to enter the critical section
|
|
||||||
|
|
||||||
// Process P0
|
|
||||||
flag[0] = true; // First set its own flag to true, declaring it needs the critical section
|
|
||||||
turn = 1; // Let P1 execute first if P1 needs the critical section
|
|
||||||
while (flag[1] && turn == 1); // Check if P1 needs the critical section
|
|
||||||
CRITICAL_SECTION;
|
|
||||||
flag[0] = false;
|
|
||||||
REMAINDER_SECTION;
|
|
||||||
|
|
||||||
// Process P1
|
|
||||||
flag[1] = true;
|
|
||||||
turn = 0;
|
|
||||||
while (flag[0] && turn == 0);
|
|
||||||
CRITICAL_SECTION;
|
|
||||||
flag[1] = false;
|
|
||||||
REMAINDER_SECTION;
|
|
||||||
```
|
|
||||||
|
|
||||||
Let's analyze it case by case.
|
|
||||||
|
|
||||||
Assume that P0 and P1 are executing concurrently, and coincidentally they both complete the first step together — both of their flags are false.
|
|
||||||
|
|
||||||
At this point:
|
|
||||||
|
|
||||||
> **[Case 1]**
|
|
||||||
> If P0 gets on the CPU first, it sets turn = 1;
|
|
||||||
> P1 gets on the CPU, sets turn = 0;
|
|
||||||
> P0 gets on the CPU, checks flag and turn — finds turn has been changed to 0, the waiting condition is not met, so P0 enters the critical section;
|
|
||||||
> P1 gets on the CPU, checks flag and turn — finds flag[0] is true and turn is unchanged, so P1 waits.
|
|
||||||
> P0 gets on the CPU and finishes using the critical section, sets flag[1] = false;
|
|
||||||
> P1 gets on the CPU, finds flag[0] is false, stops waiting and enters the critical section;
|
|
||||||
>
|
|
||||||
> **[Case 2]**
|
|
||||||
> If P0 gets on the CPU first, sets turn = 1;
|
|
||||||
> P0 continues using the CPU, finds flag[0] is true and turn is still 1, the waiting condition is met, so P0 waits;
|
|
||||||
> P1 gets on the CPU, sets turn = 0;
|
|
||||||
> P1 continues using the CPU, finds flag[1] is true and turn is still 0, the waiting condition is met, so P1 waits;
|
|
||||||
> P0 gets on the CPU, finds turn has become 0, the waiting condition is not met, so P0 exits the wait and enters the critical section;
|
|
||||||
> P1 gets on the CPU, finds the waiting condition still holds, so P1 continues waiting;
|
|
||||||
> P0 gets on the CPU and finishes using the CPU, sets flag[0] = false;
|
|
||||||
> P1 gets on the CPU, finds flag[0] == false, the waiting condition is not met, stops waiting and enters the critical section;
|
|
||||||
|
|
||||||
_YES, IT WORKS ON MY MACHINE._
|
|
||||||
|
|
@ -13,7 +13,7 @@ tags: ["peterson算法", "操作系统"]
|
||||||
|
|
||||||
先上伪代码:
|
先上伪代码:
|
||||||
|
|
||||||
```cpp
|
```
|
||||||
bool flag[2]; // 表示进入临界区意愿的数组,初始值都为false
|
bool flag[2]; // 表示进入临界区意愿的数组,初始值都为false
|
||||||
int turn = 0; // turn表示优先让哪个进程进入临界区
|
int turn = 0; // turn表示优先让哪个进程进入临界区
|
||||||
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
---
|
|
||||||
title: '测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试'
|
|
||||||
pubDate: 2022-07-01
|
|
||||||
description: 'This is the first post of my new Astro blog.'
|
|
||||||
author: 'Astro Learner'
|
|
||||||
image:
|
|
||||||
url: 'https://s2.loli.net/2022/05/01/UNzy8c6pTHBSuMO.jpg'
|
|
||||||
alt: 'The Astro logo on a dark background with a pink glow.'
|
|
||||||
tags: ["astro", "blogging", "learning in public", "Hello"]
|
|
||||||
---
|
|
||||||
|
|
||||||
Welcome to my _new blog_ about learning Astro! Here, I will share my learning journey as I build a new website.
|
|
||||||
|
|
||||||
## What I've accomplished
|
|
||||||
|
|
||||||
1. **Installing Astro**: First, I created a new Astro project and set up my online accounts.
|
|
||||||
|
|
||||||
2. **Making Pages**: I then learned how to make pages by creating new `.astro` files and placing them in the `src/pages/` folder.
|
|
||||||
|
|
||||||
3. **Making Blog Posts**: This is my first blog post! I now have Astro pages and Markdown posts!
|
|
||||||
|
|
||||||
## What's next
|
|
||||||
|
|
||||||
I will finish the Astro tutorial, and then keep adding more posts. Watch this space for more to come.
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
---
|
|
||||||
title: 'My Second Blog Post'
|
|
||||||
pubDate: 2022-07-01
|
|
||||||
description: 'This is the first post of my new Astro blog.'
|
|
||||||
author: 'Astro Learner'
|
|
||||||
image:
|
|
||||||
url: 'https://files.seeusercontent.com/2026/03/25/0rSi/rikka-manga.jpeg'
|
|
||||||
alt: 'The Astro logo on a dark background with a pink glow.'
|
|
||||||
tags: ["astro", "blogging", "learning in public"]
|
|
||||||
---
|
|
||||||
|
|
||||||
Welcome to my _new blog_ about learning Astro! Here, I will share my learning journey as I build a new website.
|
|
||||||
|
|
||||||
## What I've accomplished
|
|
||||||
|
|
||||||
1. **Installing Astro**: First, I created a new Astro project and set up my online accounts.
|
|
||||||
|
|
||||||
2. **Making Pages**: I then learned how to make pages by creating new `.astro` files and placing them in the `src/pages/` folder.
|
|
||||||
|
|
||||||
3. **Making Blog Posts**: This is my first blog post! I now have Astro pages and Markdown posts!
|
|
||||||
|
|
||||||
## What's next
|
|
||||||
|
|
||||||
I will finish the Astro tutorial, and then keep adding more posts. Watch this space for more to come.
|
|
||||||
|
|
@ -13,39 +13,15 @@ const t = getTranslations(lang);
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav class="header-nav">
|
<nav class="header-nav">
|
||||||
<div class="banner">
|
<h1>
|
||||||
<a
|
<a href="/" data-astro-reload>{t.banner.title}</a>
|
||||||
href={`/${lang}/`}
|
</h1>
|
||||||
data-astro-reload
|
|
||||||
class="banner-link"
|
|
||||||
aria-label="banner"
|
|
||||||
>
|
|
||||||
<span class="banner-logo"></span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<Navigation />
|
<Navigation />
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.banner-link {
|
.header-nav h1 a {
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.banner-logo {
|
|
||||||
display: block;
|
|
||||||
height: 55px; /* 按你的 banner 比例改 */
|
|
||||||
background-color: #1f2328;
|
|
||||||
|
|
||||||
-webkit-mask: url("/images/banner-black.svg") no-repeat left / contain;
|
|
||||||
mask: url("/images/banner-black.svg") no-repeat left / contain;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(html.dark .banner-logo) {
|
|
||||||
background-color: #e6e6e6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-nav .banner a {
|
|
||||||
color: inherit;
|
color: inherit;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
display: block;
|
display: block;
|
||||||
|
|
@ -69,13 +45,13 @@ const t = getTranslations(lang);
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-nav .banner {
|
.header-nav h1 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
line-height: 1.1;
|
line-height: 1.1;
|
||||||
|
|
||||||
/* 给右上角 ThemeIcon 留空间 */
|
/* 给右上角 ThemeIcon 留空间 */
|
||||||
max-width: calc(100% - 7rem);
|
max-width: calc(100% - 4rem);
|
||||||
|
|
||||||
/* 桌面正常,手机自动缩小 */
|
/* 桌面正常,手机自动缩小 */
|
||||||
font-size: clamp(1.6rem, 6vw, 2.5rem);
|
font-size: clamp(1.6rem, 6vw, 2.5rem);
|
||||||
|
|
|
||||||
|
|
@ -7,21 +7,21 @@ const t = getTranslations(lang);
|
||||||
|
|
||||||
<nav class="site-nav" aria-label="Site navigation">
|
<nav class="site-nav" aria-label="Site navigation">
|
||||||
<div class="site-nav-desktop">
|
<div class="site-nav-desktop">
|
||||||
<a href={`/${lang}/`} data-astro-reload>{t.nav.home}</a>
|
<a href={`/${lang}`} data-astro-reload>{t.nav.home}</a>
|
||||||
<a href={`/${lang}/about/`}>{t.nav.about}</a>
|
<a href={`/${lang}/about`}>{t.nav.about}</a>
|
||||||
<a href={`/${lang}/tags/`}>{t.nav.tags}</a>
|
<a href={`/${lang}/tags`}>{t.nav.tags}</a>
|
||||||
<a href={`/${lang}/timeline/`}>{t.nav.timeline}</a>
|
<a href={`/${lang}/timeline`}>{t.nav.timeline}</a>
|
||||||
<a href={`/${lang}/friends/`}>{t.nav.friends}</a>
|
<a href={`/${lang}/friends`}>{t.nav.friends}</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<details class="site-nav-mobile">
|
<details class="site-nav-mobile">
|
||||||
<summary>☰ {lang === "zh" ? "导航栏" : "Menu"}</summary>
|
<summary>☰ {lang === "zh" ? "导航栏" : "Menu"}</summary>
|
||||||
<div class="dropdown-menu">
|
<div class="dropdown-menu">
|
||||||
<a href={`/${lang}/`} data-astro-reload>{t.nav.home}</a>
|
<a href={`/${lang}`} data-astro-reload>{t.nav.home}</a>
|
||||||
<a href={`/${lang}/about/`}>{t.nav.about}</a>
|
<a href={`/${lang}/about`}>{t.nav.about}</a>
|
||||||
<a href={`/${lang}/tags/`}>{t.nav.tags}</a>
|
<a href={`/${lang}/tags`}>{t.nav.tags}</a>
|
||||||
<a href={`/${lang}/timeline/`}>{t.nav.timeline}</a>
|
<a href={`/${lang}/timeline`}>{t.nav.timeline}</a>
|
||||||
<a href={`/${lang}/friends/`}>{t.nav.friends}</a>
|
<a href={`/${lang}/friends`}>{t.nav.friends}</a>
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ const data = Astro.props;
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<img src={data.img} alt={data.title} class="post-image" loading="lazy" />
|
<img src={data.img} alt={data.title} class="post-image" />
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
|
@ -52,7 +52,7 @@ const data = Astro.props;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-title {
|
.post-title {
|
||||||
font-family: "Maple Mono", "Maple Mono CN";
|
font-family: "Recursive Mono", "Maple Mono CN";
|
||||||
color: inherit;
|
color: inherit;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-size: 1.34rem;
|
font-size: 1.34rem;
|
||||||
|
|
|
||||||
|
|
@ -79,12 +79,7 @@ const sortedPosts = [...allPosts].sort(
|
||||||
new Date(b.data.pubDate).getTime() - new Date(a.data.pubDate).getTime(),
|
new Date(b.data.pubDate).getTime() - new Date(a.data.pubDate).getTime(),
|
||||||
);
|
);
|
||||||
|
|
||||||
const filteredPosts = sortedPosts.filter((post: any) => {
|
const latestPosts = sortedPosts.slice(0, 5);
|
||||||
const postLang = post.id.split("/")[0];
|
|
||||||
return postLang === lang;
|
|
||||||
});
|
|
||||||
|
|
||||||
const latestPosts = filteredPosts.slice(0, 5);
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
|
|
@ -94,10 +89,7 @@ const latestPosts = filteredPosts.slice(0, 5);
|
||||||
.toISOString()
|
.toISOString()
|
||||||
.split("T")[0];
|
.split("T")[0];
|
||||||
|
|
||||||
const [postLang, ...slugParts] = post.id.split("/");
|
const pathname = `/${post.id}/`;
|
||||||
const slug = slugParts.join("/");
|
|
||||||
|
|
||||||
const pathname = `/${slug}/`;
|
|
||||||
|
|
||||||
const matchedDiscussion = discussions.find((d: DiscussionNode) => {
|
const matchedDiscussion = discussions.find((d: DiscussionNode) => {
|
||||||
return normalizePath(d.title) === normalizePath(pathname);
|
return normalizePath(d.title) === normalizePath(pathname);
|
||||||
|
|
@ -114,7 +106,7 @@ const latestPosts = filteredPosts.slice(0, 5);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PostItem
|
<PostItem
|
||||||
url={`/${postLang}/posts/${slug}/`}
|
url={`/${lang}/posts/${post.id}/`}
|
||||||
title={post.data.title}
|
title={post.data.title}
|
||||||
description={post.data.description}
|
description={post.data.description}
|
||||||
date={formattedDate}
|
date={formattedDate}
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,7 @@ const sortedPosts = [...allPosts].sort(
|
||||||
new Date(b.data.pubDate).getTime() - new Date(a.data.pubDate).getTime(),
|
new Date(b.data.pubDate).getTime() - new Date(a.data.pubDate).getTime(),
|
||||||
);
|
);
|
||||||
|
|
||||||
const filteredPosts = sortedPosts.filter((post: any) => {
|
const groupedPosts = sortedPosts.reduce((acc: any[], post: any) => {
|
||||||
const postLang = post.id.split("/")[0];
|
|
||||||
return postLang === lang;
|
|
||||||
});
|
|
||||||
|
|
||||||
const groupedPosts = filteredPosts.reduce((acc: any[], post: any) => {
|
|
||||||
const month = new Date(post.data.pubDate).toISOString().slice(0, 7);
|
const month = new Date(post.data.pubDate).toISOString().slice(0, 7);
|
||||||
const lastGroup = acc[acc.length - 1];
|
const lastGroup = acc[acc.length - 1];
|
||||||
|
|
||||||
|
|
@ -43,25 +38,18 @@ const groupedPosts = filteredPosts.reduce((acc: any[], post: any) => {
|
||||||
<p class="post-date">{group.month}</p>
|
<p class="post-date">{group.month}</p>
|
||||||
|
|
||||||
<div class="month-posts">
|
<div class="month-posts">
|
||||||
{group.posts.map((post: any) => {
|
{group.posts.map((post: any) => (
|
||||||
const [postLang, ...slugParts] = post.id.split("/");
|
<a
|
||||||
const slug = slugParts.join("/");
|
href={`/${lang}/posts/${post.id}/`}
|
||||||
|
class="timeline-card"
|
||||||
return (
|
data-astro-reload
|
||||||
<a
|
>
|
||||||
href={`/${lang}/posts/${slug}/`}
|
<h2 class="post-title">{post.data.title}</h2>
|
||||||
class="timeline-card"
|
<p class="post-description">
|
||||||
data-astro-reload
|
{post.data.description}
|
||||||
>
|
</p>
|
||||||
<h2 class="post-title">
|
</a>
|
||||||
{post.data.title}
|
))}
|
||||||
</h2>
|
|
||||||
<p class="post-description">
|
|
||||||
{post.data.description}
|
|
||||||
</p>
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ const blog = defineCollection({
|
||||||
url: z.string(),
|
url: z.string(),
|
||||||
alt: z.string()
|
alt: z.string()
|
||||||
}),
|
}),
|
||||||
tags: z.array(z.string()),
|
tags: z.array(z.string())
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
export default {
|
export default {
|
||||||
banner: {
|
banner: {
|
||||||
title: "Cloverta's Blog",
|
title: "Cloverta's Blog",
|
||||||
subtitle: "More to explore here (kinda) — welcome to Cloverta's blog 🥳"
|
subtitle: ""
|
||||||
},
|
},
|
||||||
nav: {
|
nav: {
|
||||||
home: "Home",
|
home: "Home",
|
||||||
|
|
@ -51,7 +51,7 @@ export default {
|
||||||
description: "All tags that have appeared across the blog are collected here. Click a tag to jump to the corresponding list of posts."
|
description: "All tags that have appeared across the blog are collected here. Click a tag to jump to the corresponding list of posts."
|
||||||
},
|
},
|
||||||
footer: {
|
footer: {
|
||||||
githubIntro: 'See more on <a href="https://repo.cloverta.top/cloverta">Forgejo</a>!',
|
githubIntro: 'See more on <a href="https://www.github.com/ClovertaTheTrilobita">GitHub</a>!',
|
||||||
repoIntro: 'This blog is fully open source at <a href="https://repo.cloverta.top/cloverta/SanYeCao-blog">ClovertaTheTrilobita/SanYeCao-blog</a>'
|
repoIntro: 'This blog is fully open source at <a href="https://www.github.com/ClovertaTheTrilobita/SanYeCao-blog">ClovertaTheTrilobita/SanYeCao-blog</a>'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
export default {
|
export default {
|
||||||
banner: {
|
banner: {
|
||||||
title: "Cloverta的博客",
|
title: "Cloverta的博客",
|
||||||
subtitle: "在这里,发现更多(雾)欢迎来到三叶的博客🥳"
|
subtitle: ""
|
||||||
},
|
},
|
||||||
nav: {
|
nav: {
|
||||||
home: "首页",
|
home: "首页",
|
||||||
|
|
@ -51,7 +51,7 @@ export default {
|
||||||
description: "在这里收集着整篇博客出现过的标签,点击标签跳转对应的文章列表。",
|
description: "在这里收集着整篇博客出现过的标签,点击标签跳转对应的文章列表。",
|
||||||
},
|
},
|
||||||
footer: {
|
footer: {
|
||||||
githubIntro: '在 <a href="https://repo.cloverta.top/cloverta">Forgejo</a> 查看更多!',
|
githubIntro: '在 <a href="https://www.github.com/ClovertaTheTrilobita">GitHub</a> 查看更多!',
|
||||||
repoIntro: '这个博客完全开源于 <a href="https://repo.cloverta.top/cloverta/SanYeCao-blog">ClovertaTheTrilobita/SanYeCao-blog</a>'
|
repoIntro: '这个博客完全开源于 <a href="https://www.github.com/ClovertaTheTrilobita/SanYeCao-blog">ClovertaTheTrilobita/SanYeCao-blog</a>'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -21,10 +21,11 @@ const {
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/favicon.ico" />
|
||||||
<meta name="generator" content={Astro.generator} />
|
<meta name="generator" content={Astro.generator} />
|
||||||
<title>{pageTitle}</title>
|
<title>{pageTitle}</title>
|
||||||
|
<ClientRouter />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<Header />
|
<Header />
|
||||||
<main class="page-content">
|
<main class="page-content" transition:name="page">
|
||||||
<slot />
|
<slot />
|
||||||
</main>
|
</main>
|
||||||
<Footer />
|
<Footer />
|
||||||
|
|
@ -38,4 +39,36 @@ const {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::view-transition-old(page),
|
||||||
|
::view-transition-new(page) {
|
||||||
|
animation-duration: 0.05s;
|
||||||
|
animation-timing-function: ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
::view-transition-old(page) {
|
||||||
|
animation-name: fade-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
::view-transition-new(page) {
|
||||||
|
animation-name: fade-in;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade-out {
|
||||||
|
from {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade-in {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ const t = getTranslations(lang);
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout
|
<BaseLayout
|
||||||
pageTitle=`${frontmatter.title} - ${t.banner.title}`
|
pageTitle={frontmatter.title}
|
||||||
description={frontmatter.description}
|
description={frontmatter.description}
|
||||||
image={frontmatter.image?.url}
|
image={frontmatter.image?.url}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
---
|
|
||||||
import BaseLayout from "@/layouts/BaseLayout.astro";
|
|
||||||
import "@/styles/global.css";
|
|
||||||
---
|
|
||||||
|
|
||||||
<BaseLayout
|
|
||||||
pageTitle="404 Not Found"
|
|
||||||
description="Sorry, the page doesn't exists"
|
|
||||||
>
|
|
||||||
<div class="not-found">
|
|
||||||
<img src="/images/marisa.png" alt="Marisa" class="marisa-404" />
|
|
||||||
<div class="hint">
|
|
||||||
<h3>对不起,您请求的页面不存在哦。</h3>
|
|
||||||
<h3>Sorry, the page you are requesting for doesn't exists.</h3>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</BaseLayout>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.not-found {
|
|
||||||
min-height: 70vh;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
text-align: center;
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.marisa-404 {
|
|
||||||
width: 30%;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hint {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -13,7 +13,7 @@ const pageTitle = t.about.title;
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout
|
<BaseLayout
|
||||||
pageTitle=`${pageTitle} - ${t.banner.title}`
|
pageTitle={pageTitle}
|
||||||
image={t.about.profilePicture}
|
image={t.about.profilePicture}
|
||||||
>
|
>
|
||||||
<main class="about">
|
<main class="about">
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,6 @@ const t = getTranslations(lang);
|
||||||
const headerTitle = lang === "zh" ? "友情链接" : "Friends";
|
const headerTitle = lang === "zh" ? "友情链接" : "Friends";
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout pageTitle=`${headerTitle} - ${t.banner.title}`>
|
<BaseLayout pageTitle={headerTitle}>
|
||||||
<FriendlyLinkList />
|
<FriendlyLinkList />
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
|
|
|
||||||
|
|
@ -15,11 +15,11 @@ const headerTitle = lang === "zh" ? "Cloverta的博客" : "Cloverta's blog";
|
||||||
const pageTitle = t.home.title;
|
const pageTitle = t.home.title;
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout pageTitle=`${headerTitle} - ${t.banner.subtitle}`>
|
<BaseLayout pageTitle={headerTitle}>
|
||||||
<h1 class="page-title">
|
<h1 class="page-title">
|
||||||
<span>{pageTitle}</span>
|
<span>{pageTitle}</span>
|
||||||
<a
|
<a
|
||||||
href={`/rss.xml`}
|
href={`${import.meta.env.SITE}/rss.xml`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
aria-label="RSS Feed"
|
aria-label="RSS Feed"
|
||||||
|
|
|
||||||
|
|
@ -4,30 +4,26 @@ import MarkdownPostLayout from "@/layouts/MarkdownPostLayout.astro";
|
||||||
|
|
||||||
export async function getStaticPaths() {
|
export async function getStaticPaths() {
|
||||||
const posts = await getCollection("blog");
|
const posts = await getCollection("blog");
|
||||||
|
const langs = ["zh", "en"];
|
||||||
|
|
||||||
return posts.map((post) => {
|
return langs.flatMap((lang) =>
|
||||||
const [lang, ...slugParts] = post.id.split("/");
|
posts.map((post) => ({
|
||||||
|
|
||||||
return {
|
|
||||||
params: {
|
params: {
|
||||||
lang,
|
lang,
|
||||||
slug: slugParts.join("/"),
|
slug: post.id,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
post,
|
post,
|
||||||
lang,
|
lang,
|
||||||
},
|
},
|
||||||
};
|
})),
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { post, lang } = Astro.props;
|
const { post, lang } = Astro.props;
|
||||||
const { Content } = await render(post);
|
const { Content } = await render(post);
|
||||||
|
|
||||||
const [postLang, ...slugParts] = post.id.split("/");
|
|
||||||
const slug = slugParts.join("/");
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<MarkdownPostLayout frontmatter={post.data} lang={lang} postId={slug}>
|
<MarkdownPostLayout frontmatter={post.data} lang={lang} postId={post.id}>
|
||||||
<Content />
|
<Content />
|
||||||
</MarkdownPostLayout>
|
</MarkdownPostLayout>
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ const tags = [...new Set(allPosts.map((post: any) => post.data.tags).flat())];
|
||||||
const pageTitle = lang === "zh" ? "标签索引" : "Tag Index";
|
const pageTitle = lang === "zh" ? "标签索引" : "Tag Index";
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout pageTitle=`${pageTitle} - ${t.banner.title}` `>
|
<BaseLayout pageTitle={pageTitle}>
|
||||||
<h1>{t.tags.title}</h1>
|
<h1>{t.tags.title}</h1>
|
||||||
<p>{t.tags.description}</p>
|
<p>{t.tags.description}</p>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ const t = getTranslations(lang);
|
||||||
const headerTitle = lang === "zh" ? "时间轴" : "Timeline";
|
const headerTitle = lang === "zh" ? "时间轴" : "Timeline";
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout pageTitle=`${headerTitle} - ${t.banner.title}` `>
|
<BaseLayout pageTitle={headerTitle}>
|
||||||
<h1>{t.nav.timeline}</h1>
|
<h1>{t.nav.timeline}</h1>
|
||||||
<PostTimeline />
|
<PostTimeline />
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
|
|
|
||||||
|
|
@ -4,23 +4,17 @@ import { getCollection } from 'astro:content';
|
||||||
|
|
||||||
export async function GET(context) {
|
export async function GET(context) {
|
||||||
const posts = await getCollection("blog");
|
const posts = await getCollection("blog");
|
||||||
|
|
||||||
return rss({
|
return rss({
|
||||||
title: 'Cloverta的博客',
|
title: 'Cloverta的博客',
|
||||||
description: '在这里,发现更多(雾)欢迎来到三叶的博客🥳',
|
description: '在这里,发现更多(雾)欢迎来到三叶的博客🥳',
|
||||||
site: context.site,
|
site: context.site,
|
||||||
items: await pagesGlobToRssItems(import.meta.glob('./**/*.md')),
|
items: await pagesGlobToRssItems(import.meta.glob('./**/*.md')),
|
||||||
items: posts.map((post) => {
|
items: posts.map((post) => ({
|
||||||
const [postLang, ...slugParts] = post.id.split("/");
|
title: post.data.title,
|
||||||
const slug = slugParts.join("/");
|
pubDate: post.data.pubDate,
|
||||||
|
description: post.data.description,
|
||||||
return ({
|
link: `/posts/${post.id}/`,
|
||||||
title: post.data.title,
|
})),
|
||||||
pubDate: post.data.pubDate,
|
|
||||||
description: post.data.description,
|
|
||||||
link: `/${postLang}/posts/${slug}/`,
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
customData: `<language>en-us</language>`,
|
customData: `<language>en-us</language>`,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -2,6 +2,30 @@
|
||||||
@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/400-italic.css");
|
||||||
@import url("https://unpkg.com/@fontsource/maple-mono@5.2.6/700.css");
|
@import url("https://unpkg.com/@fontsource/maple-mono@5.2.6/700.css");
|
||||||
|
|
||||||
|
/* @font-face {
|
||||||
|
font-family: "Maple Mono";
|
||||||
|
src: url("/fonts/MapleMono-Regular.ttf.woff2") format("woff2");
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Maple Mono";
|
||||||
|
src: url("/fonts/MapleMono-Italic.ttf.woff2") format("woff2");
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: italic;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Maple Mono";
|
||||||
|
src: url("/fonts/MapleMono-Bold.ttf.woff2") format("woff2");
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
} */
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "Maple Mono CN";
|
font-family: "Maple Mono CN";
|
||||||
src: url("/fonts/subset/MapleMono-CN-Regular.woff2") format("woff2");
|
src: url("/fonts/subset/MapleMono-CN-Regular.woff2") format("woff2");
|
||||||
|
|
@ -26,6 +50,30 @@
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Recursive Mono";
|
||||||
|
src: url("/fonts/RecursiveMonoCslSt-Bold.woff2") format("woff2");
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Recursive Mono";
|
||||||
|
src: url("/fonts/RecursiveMonoCslSt-Regular.woff2") format("woff2");
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Recursive Mono";
|
||||||
|
src: url("/fonts/RecursiveMonoCslSt-Italic.woff2") format("woff2");
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: italic;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
|
|
@ -63,34 +111,6 @@ body {
|
||||||
line-height: 1.7;
|
line-height: 1.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
body::after {
|
|
||||||
content: "";
|
|
||||||
position: fixed;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
width: 400px;
|
|
||||||
/* 按需调整大小 */
|
|
||||||
height: 400px;
|
|
||||||
/* 按需调整大小 */
|
|
||||||
background-image: url("https://files.seeusercontent.com/2026/03/30/4Xfr/bc7e804dc2c8ecaf407c9d665414ff72.webp");
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: right bottom;
|
|
||||||
background-size: contain;
|
|
||||||
opacity: 0.35;
|
|
||||||
pointer-events: none;
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
html.dark body::after {
|
|
||||||
background-image: url("https://files.seeusercontent.com/2026/03/30/vd8W/touhou___alice_margatroid_x_kiri.webp");
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
body::after {
|
|
||||||
opacity: 0.06;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
* {
|
* {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
@ -103,7 +123,7 @@ h1 {
|
||||||
|
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: #416bd6;
|
color: #7fb3ff;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue