feat(侧边栏): 替换待办事项和时光卡片为灵感组件

添加新的灵感组件并移除旧的待办事项和时光卡片功能
更新图标库配置以支持Material Symbols Light
删除不再使用的待办事项相关类型和文件
This commit is contained in:
二叉树树
2025-12-09 14:33:39 +08:00
parent 98cfcac41b
commit 3bec260b71
10 changed files with 54 additions and 238 deletions

View File

@@ -72,6 +72,8 @@ export default defineConfig({
"fa6-regular": ["*"],
"fa6-solid": ["*"],
"simple-icons": ["*"],
"material-symbols-light": ["*"],
"material-symbols": ["*"],
},
}), svelte(), sitemap(),
expressiveCode({

View File

@@ -69,6 +69,7 @@
"devDependencies": {
"@astrojs/ts-plugin": "^1.10.4",
"@biomejs/biome": "1.9.4",
"@iconify-json/material-symbols-light": "^1.2.49",
"@rollup/plugin-yaml": "^4.1.2",
"@types/markdown-it": "^14.1.2",
"@types/mdast": "^4.0.4",

10
pnpm-lock.yaml generated
View File

@@ -170,6 +170,9 @@ importers:
'@biomejs/biome':
specifier: 1.9.4
version: 1.9.4
'@iconify-json/material-symbols-light':
specifier: ^1.2.49
version: 1.2.49
'@rollup/plugin-yaml':
specifier: ^4.1.2
version: 4.1.2(rollup@2.79.2)
@@ -1136,6 +1139,9 @@ packages:
'@iconify-json/fa6-solid@1.2.3':
resolution: {integrity: sha512-C5o8YJF+ekrS4wRb/6/0SE2KjRyJlCg++IOVC/fineiRinITivsmzFRNW1MQX2xfDZ1T7bxeKxLN6lcaTG3jGA==}
'@iconify-json/material-symbols-light@1.2.49':
resolution: {integrity: sha512-EpKeZ9NifWfU0mfxC7eULjuVtbRdbgg0cNDOlJZucKulC4bTvCcmlNtK5wqsyRICKi4xcfHlSTsmBMiFjF7GOQ==}
'@iconify-json/material-symbols@1.2.20':
resolution: {integrity: sha512-+KqOT+3fD+LC2FbWiV8gd4+JLMiVUtmqrjzpKN1ji7rfMQTwvYJ94RT0WQlmL+vfDNJ5MTRe3rBzzJyvIH/aSg==}
@@ -6587,6 +6593,10 @@ snapshots:
dependencies:
'@iconify/types': 2.0.0
'@iconify-json/material-symbols-light@1.2.49':
dependencies:
'@iconify/types': 2.0.0
'@iconify-json/material-symbols@1.2.20':
dependencies:
'@iconify/types': 2.0.0

View File

@@ -0,0 +1,33 @@
---
import WidgetLayout from "./WidgetLayout.astro";
import { Icon } from "astro-icon/components";
import inspirationContent from "@/data/inspiration.txt?raw";
const items = inspirationContent.split('\n').filter(line => line.trim() !== '');
interface Props {
class?: string;
style?: string;
}
const className = Astro.props.class;
const style = Astro.props.style;
---
{items.length > 0 && (
<WidgetLayout name="灵感一刻" id="inspiration-list" class={className} style={style}>
<div class="flex flex-col gap-2">
{items.map((item, index) => (
<div class="flex items-start gap-2 text-sm text-neutral-700 dark:text-neutral-300">
<span class="font-bold text-[var(--primary)] flex-shrink-0 select-none w-5 text-right">{index + 1}.</span>
<span class="leading-5">{item}</span>
</div>
))}
</div>
<div class="mt-4 flex justify-center">
<a href="https://github.com/afoim/fuwari/blob/main/src/data/inspiration.txt" target="_blank" rel="noopener noreferrer" class="btn-plain rounded-lg h-9 px-4 flex items-center gap-2 text-[var(--primary)] text-sm">
<Icon name="material-symbols:edit-square-outline" class="text-lg" />
记录新灵感
</a>
</div>
</WidgetLayout>
)}

View File

@@ -4,8 +4,7 @@ import type { MarkdownHeading } from "astro";
import Profile from "./Profile.astro";
import Tag from "./Tags.astro";
import DomainSwitcher from "./DomainSwitcher.astro";
import Todo from "./Todo.astro";
import TimeCard from "./TimeCard.astro";
import Inspiration from "./Inspiration.astro";
interface Props {
class?: string;
@@ -21,9 +20,8 @@ const className = Astro.props.class;
<div id="sidebar-sticky" class="transition-all duration-700 flex flex-col w-full gap-4 top-4 sticky top-4">
<DomainSwitcher class="onload-animation" style="animation-delay: 150ms"></DomainSwitcher>
<Tag class="onload-animation" style="animation-delay: 200ms"></Tag>
<Todo class="onload-animation" style="animation-delay: 250ms"></Todo>
<TimeCard class="onload-animation" style="animation-delay: 300ms"></TimeCard>
<!-- 赞助标 -->
<Inspiration class="onload-animation" style="animation-delay: 250ms"></Inspiration>
<!-- 赞助标 -->
<div class="overflow-hidden flex justify-center">
<a href="https://secbit.ai/" target="_blank" rel="noopener noreferrer">
<img src="/aff/secbit-banner-1.gif" alt="Secbit" class="w-full max-w-[280px] rounded-lg hover:opacity-90 transition-opacity" />

View File

@@ -1,169 +0,0 @@
---
import WidgetLayout from "./WidgetLayout.astro";
interface Props {
class?: string;
style?: string;
}
const className = Astro.props.class;
const style = Astro.props.style;
---
<WidgetLayout name="时光流逝" id="time-card" class={className} style={style}>
<div class="flex flex-col gap-4 p-1">
<div id="current-date-text" class="text-center text-xs font-bold text-neutral-700 dark:text-neutral-300 tabular-nums tracking-wide">
加载中...
</div>
<div class="grid grid-cols-3 gap-3">
<div class="relative aspect-square flex items-center justify-center group cursor-help" id="day-progress-container">
<svg class="w-full h-full -rotate-90 transform" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="45" stroke="currentColor" stroke-width="8" fill="transparent" class="text-black/5 dark:text-white/5" />
<circle id="day-progress-circle" cx="50" cy="50" r="45" stroke="currentColor" stroke-width="8" fill="transparent" stroke-linecap="round" class="text-[var(--primary)] transition-all duration-75 ease-linear" stroke-dasharray="282.743" stroke-dashoffset="282.743" />
</svg>
<div class="absolute inset-0 flex items-center justify-center flex-col">
<span id="day-progress-text" class="text-sm font-bold text-neutral-600 dark:text-neutral-400 tabular-nums">0时</span>
<span class="text-[0.6rem] text-neutral-400 dark:text-neutral-500 scale-90 origin-center font-medium">今日</span>
</div>
</div>
<div class="relative aspect-square flex items-center justify-center group cursor-help" id="week-progress-container">
<svg class="w-full h-full -rotate-90 transform" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="45" stroke="currentColor" stroke-width="8" fill="transparent" class="text-black/5 dark:text-white/5" />
<circle id="week-progress-circle" cx="50" cy="50" r="45" stroke="currentColor" stroke-width="8" fill="transparent" stroke-linecap="round" class="text-[var(--primary)] transition-all duration-75 ease-linear" stroke-dasharray="282.743" stroke-dashoffset="282.743" />
</svg>
<div class="absolute inset-0 flex items-center justify-center flex-col">
<span id="week-progress-text" class="text-sm font-bold text-neutral-600 dark:text-neutral-400 tabular-nums">星期</span>
<span class="text-[0.6rem] text-neutral-400 dark:text-neutral-500 scale-90 origin-center font-medium">本周</span>
</div>
</div>
<div class="relative aspect-square flex items-center justify-center group cursor-help" id="month-progress-container">
<svg class="w-full h-full -rotate-90 transform" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="45" stroke="currentColor" stroke-width="8" fill="transparent" class="text-black/5 dark:text-white/5" />
<circle id="month-progress-circle" cx="50" cy="50" r="45" stroke="currentColor" stroke-width="8" fill="transparent" stroke-linecap="round" class="text-[var(--primary)] transition-all duration-75 ease-linear" stroke-dasharray="282.743" stroke-dashoffset="282.743" />
</svg>
<div class="absolute inset-0 flex items-center justify-center flex-col">
<span id="month-progress-text" class="text-sm font-bold text-neutral-600 dark:text-neutral-400 tabular-nums">第0天</span>
<span class="text-[0.6rem] text-neutral-400 dark:text-neutral-500 scale-90 origin-center font-medium">本月</span>
</div>
</div>
</div>
<div class="grid grid-cols-2 gap-3">
<div class="relative aspect-square flex items-center justify-center group cursor-help" id="year-progress-container">
<svg class="w-full h-full -rotate-90 transform" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="45" stroke="currentColor" stroke-width="8" fill="transparent" class="text-black/5 dark:text-white/5" />
<circle id="year-progress-circle" cx="50" cy="50" r="45" stroke="currentColor" stroke-width="8" fill="transparent" stroke-linecap="round" class="text-[var(--primary)] transition-all duration-75 ease-linear" stroke-dasharray="282.743" stroke-dashoffset="282.743" />
</svg>
<div class="absolute inset-0 flex items-center justify-center flex-col">
<span id="year-progress-text" class="text-xl font-bold text-neutral-600 dark:text-neutral-400 tabular-nums">0月</span>
<span class="text-xs text-neutral-400 dark:text-neutral-500 scale-90 origin-center font-medium">今年</span>
</div>
</div>
<div class="relative aspect-square flex items-center justify-center group cursor-help" id="life-progress-container">
<svg class="w-full h-full -rotate-90 transform" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="45" stroke="currentColor" stroke-width="8" fill="transparent" class="text-black/5 dark:text-white/5" />
<circle id="life-progress-circle" cx="50" cy="50" r="45" stroke="currentColor" stroke-width="8" fill="transparent" stroke-linecap="round" class="text-[var(--primary)] transition-all duration-75 ease-linear" stroke-dasharray="282.743" stroke-dashoffset="282.743" />
</svg>
<div class="absolute inset-0 flex items-center justify-center flex-col">
<span id="life-progress-text" class="text-xl font-bold text-neutral-600 dark:text-neutral-400 tabular-nums">0岁</span>
<span class="text-xs text-neutral-400 dark:text-neutral-500 scale-90 origin-center font-medium">人生</span>
</div>
</div>
</div>
</div>
</WidgetLayout>
<script>
function updateTimeCard() {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
const day = String(now.getDate()).padStart(2, '0');
const weekDays = ['日', '一', '二', '三', '四', '五', '六'];
const weekDay = weekDays[now.getDay()];
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');
const seconds = String(now.getSeconds()).padStart(2, '0');
const milliseconds = String(now.getMilliseconds()).padStart(3, '0').slice(0, 1);
// Update Header
const dateText = document.getElementById('current-date-text');
if (dateText) {
dateText.textContent = `${year}年${month}月${day}日 星期${weekDay} ${hours}:${minutes}:${seconds}.${milliseconds}`;
}
// Calculations
const startOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate());
const endOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1);
const dayProgress = (now.getTime() - startOfDay.getTime()) / (endOfDay.getTime() - startOfDay.getTime()) * 100;
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
const nextMonth = new Date(now.getFullYear(), now.getMonth() + 1, 1);
const daysInMonth = (nextMonth.getTime() - startOfMonth.getTime()) / (1000 * 60 * 60 * 24);
const monthProgress = (now.getTime() - startOfMonth.getTime()) / (nextMonth.getTime() - startOfMonth.getTime()) * 100;
const startOfYear = new Date(now.getFullYear(), 0, 1);
const nextYear = new Date(now.getFullYear() + 1, 0, 1);
const yearProgress = (now.getTime() - startOfYear.getTime()) / (nextYear.getTime() - startOfYear.getTime()) * 100;
const birthDate = new Date('2007-09-20T00:00:00');
const deathDate = new Date('2107-09-20T00:00:00'); // 100 years? User code said 80 years but logic was 2107-2007 = 100.
// Re-checking previous code: 2007 to 2107 is 100 years. Previous comment said "80 years" but code used 2107. I'll stick to code.
const lifeProgress = Math.max(0, Math.min(100, (now.getTime() - birthDate.getTime()) / (deathDate.getTime() - birthDate.getTime()) * 100));
// Update Rings
const updateRing = (idPrefix: string, percent: number, textDisplay: string, tooltipText: string) => {
const circle = document.getElementById(`${idPrefix}-circle`);
const text = document.getElementById(`${idPrefix}-text`);
const container = document.getElementById(`${idPrefix}-container`);
if (circle) {
const r = 45;
const c = 2 * Math.PI * r;
const offset = c - (percent / 100) * c;
circle.style.strokeDashoffset = String(offset);
}
if (text) {
text.textContent = textDisplay;
}
if (container) {
container.title = tooltipText;
}
};
// Day: Hours passed
updateRing('day-progress', dayProgress, `${now.getHours()}时`, `今天已过去 ${dayProgress.toFixed(4)}%`);
// Week: Weekday
const startOfWeek = new Date(now);
const currentDay = now.getDay(); // 0 (Sun) - 6 (Sat)
const diffToMonday = (currentDay + 6) % 7; // Mon=0, Tue=1, ..., Sun=6
startOfWeek.setDate(now.getDate() - diffToMonday);
startOfWeek.setHours(0, 0, 0, 0);
const endOfWeek = new Date(startOfWeek);
endOfWeek.setDate(startOfWeek.getDate() + 7);
const weekProgress = (now.getTime() - startOfWeek.getTime()) / (endOfWeek.getTime() - startOfWeek.getTime()) * 100;
updateRing('week-progress', weekProgress, `星期${weekDay}`, `本周已过去 ${weekProgress.toFixed(4)}%`);
// Month: Days passed
updateRing('month-progress', monthProgress, `第${now.getDate()}天`, `本月已过去 ${monthProgress.toFixed(4)}%`);
// Year: Months passed
updateRing('year-progress', yearProgress, `${now.getMonth()}月`, `今年已过去 ${yearProgress.toFixed(4)}%`);
// Life: Years passed
const age = now.getFullYear() - 2007; // Approximate
updateRing('life-progress', lifeProgress, `${age}岁`, `人生已过去 ${lifeProgress.toFixed(5)}%`);
}
updateTimeCard();
setInterval(updateTimeCard, 50);
document.addEventListener('astro:page-load', () => {
updateTimeCard();
});
</script>

View File

@@ -1,35 +0,0 @@
---
import { todoConfig } from "@/data/todo";
import WidgetLayout from "./WidgetLayout.astro";
import { Icon } from "astro-icon/components";
interface Props {
class?: string;
style?: string;
}
const className = Astro.props.class;
const style = Astro.props.style;
---
{todoConfig.enable && (
<WidgetLayout name={todoConfig.title} id="todo-list" class={className} style={style}>
<div class="flex flex-col gap-2">
{todoConfig.items.map((item) => (
<div class="flex items-center gap-2 text-sm text-neutral-700 dark:text-neutral-300">
<Icon
name={item.completed ? "material-symbols:check-box-outline" : "material-symbols:check-box-outline-blank"}
class={`text-lg flex-shrink-0 ${item.completed ? "text-[var(--primary)]" : "text-neutral-400"}`}
/>
<span class={item.completed ? "line-through opacity-60" : ""}>{item.content}</span>
</div>
))}
</div>
<div class="mt-4 flex justify-center">
<a href="https://github.com/afoim/fuwari/tree/main/src/data/todo.ts" target="_blank" rel="noopener noreferrer" class="btn-plain rounded-lg h-9 px-4 flex items-center gap-2 text-[var(--primary)] text-sm">
<Icon name="material-symbols:add-task-rounded" class="text-lg" />
我要给你加工作!
</a>
</div>
</WidgetLayout>
)}

5
src/data/inspiration.txt Normal file
View File

@@ -0,0 +1,5 @@
教你用OBSAV1编码录制
教你配置Cloudflare Turnstile
博客开发日志
OneDrive Index搭建随时随地存取文件
Rybbit网站统计搭建教程

View File

@@ -1,20 +0,0 @@
import type { TodoConfig } from "@/types/config";
export const todoConfig: TodoConfig = {
enable: true,
title: "待办事项",
items: [
{ content: "为博客添加”待办事项“功能", completed: true },
{ content: "做视频利用STUN在世界各地连接上你的电脑", completed: true },
{ content: "做视频Ventoy+FirPE使用可能不会做", completed: false },
{ content: "做视频Cloudflare 利用Origin Rules 6转4 访问家里云", completed: true },
{ content: "做视频如何使用OBS优雅的录视频", completed: false },
{ content: "写文章Umami迁移记录", completed: true },
{ content: "自建Umami并从云迁移到自建", completed: true },
{ content: "更改文章底下的链接,用正则删去查询(?=xxx", completed: true },
{ content: "做视频anuneko.com米哈游AI上手体验", completed: true },
{ content: "完善Bot插件anuneko.com米哈游AI聊天机器人支持pick", completed: true },
{ content: "写文章当anuneko bot插件完善后编写开发文章", completed: true },
{ content: "更新文章Serverless添加RenderZeabur", completed: true },
],
};

View File

@@ -93,16 +93,7 @@ export type UmamiConfig = {
timezone: string;
};
export type TodoItem = {
content: string;
completed: boolean;
};
export type TodoConfig = {
enable: boolean;
title: string;
items: TodoItem[];
};
export type LIGHT_DARK_MODE =
| typeof LIGHT_MODE