页脚改版:删掉重复的导航栏,桌面补站点信息;我的介绍可点去关于页

页脚「导航」栏
- 它是顶部导航的逐项复制(9 条名字和目标完全一样),手机上白占 312px
- 已整栏删除;桌面端改用「最新文章」(最近 4 篇,按发布时间倒序,
  不把置顶提前),把重复变成发现
- 手机端「最新文章」隐藏(上面就是文章列表,再列一遍没意义)
- 不影响 SEO:顶部导航 hidden md:flex 和移动端菜单里的同一批链接
  在每页 HTML 里都还在,爬虫照样抓得到

站点信息(新增)
- 新增 src/lib/site-stats.ts:文章数/偶得数/分类数/标签数/总字数
  在构建期算;「已运行 N 天」「最近更新」由页脚内联脚本按真实时间刷新,
  避免站点久不重建时数字停在构建那天;服务端先渲染构建时兜底值,关 JS 不空
- 页脚加一条站点信息带:建站时间 / 已运行 / 文章 / 偶得 / 总字数 / 最近更新
  桌面 6 列一行,手机 3 列两行
- 实测数值:2026-07-17 建站、已运行 60 天、30 篇文章、8 条偶得、
  2.9 万字、最近更新 59 天前

「我的介绍」跳关于页
- 页脚 BRAND 栏整块包 <a href="/about/">,加 hover 反馈(头像描边转主色、
  站名转主色、右上箭头位移)和常显的「关于我 →」提示(手机没有 hover,
  不能只在 hover 才出现)
- 侧栏作者卡片同样处理。注意这里**只包到签名那一段** —— 下面的社交图标
  本身是 <a>,<a> 不能嵌套 <a>;页脚 BRAND 栏没有嵌套链接,所以可以整块包
- 两处都给 aria-label,避免读屏把「头像 alt + 站名 + 签名」念成链接名

工程改动
- footer.tsx -> footer.astro:React 组件不能在构建期 await 统计数据,
  换成 Astro 组件后可以直接取;12 处引用同步改成默认导入
- README 补「页脚」一节,并修正组件路径

验证(headless Chrome 131,禁用缓存)
- 桌面 1280:页脚 529px,三栏(我的介绍/最新文章/联系)+ 站点信息 + 底栏,
  页脚内 nav 链接数 0,无横向溢出
- 手机 390:页脚 822px(原 957),最新文章 display:none,三栏只剩两栏
- 点页脚「我的介绍」-> /about/;点侧栏作者卡片 -> /about/,均正常
- 页面内 `a a` 嵌套数量:页脚 0、侧栏 0
This commit is contained in:
dsh
2026-09-15 09:55:26 +08:00
parent 5532c35064
commit 0cfcbd060e
17 changed files with 336 additions and 166 deletions
+24 -1
View File
@@ -168,7 +168,7 @@ epigraph: 题记 # 可选
| 评论(twikoo) | `src/data/comments.ts` |
| 友链列表 | `src/data/friends.json` |
| 首页打字机文案 | `src/components/hero.tsx` 顶部的 `phrases` |
| 导航栏、页脚 | `src/components/site-header.astro`、`src/components/footer.tsx` |
| 导航栏、页脚 | `src/components/site-header.astro`、`src/components/footer.astro` |
| 配色与全局样式 | `src/styles/global.css` |
| 代码块主题 | `astro.config.mjs` 里 `expressiveCode` 的 `themes` |
| 免责声明模板 | `src/data/disclaimers.ts` |
@@ -233,6 +233,29 @@ prose-p:leading-[1.78] md:prose-p:leading-[1.85]
`--header-h: 69px` 是固定页眉的实测高度,移动端菜单和这条胶囊条都靠它定位。**改页眉内边距或头像尺寸时记得同步改它**,否则吸附位置会错。
### 页脚
`src/components/footer.astro`(原来是个 React 组件 `footer.tsx`,为了能在构建期 `await` 统计数据改成了 Astro 组件;引用方式随之从 `import { Footer } from "@/components/footer"` 变成 `import Footer from "@/components/footer.astro"`)。
结构:
| 区块 | 内容 | 显示范围 |
|---|---|---|
| 我的介绍 | 头像 + 站名 + 签名,**整块可点去 `/about/`** | 全部 |
| 最新文章 | 最近 4 篇(按发布时间倒序,不把置顶提前) | 桌面 |
| 联系 | 社交图标 + 订阅 RSS | 全部 |
| 站点信息 | 建站时间 / 已运行 / 文章 / 偶得 / 总字数 / 最近更新 | 全部(想只在桌面显示就给它加 `hidden md:grid`) |
| 底栏 | 版权 / 备案 / 主题署名 | 全部 |
**原来的「导航」栏已删除** —— 它是顶部导航的逐项复制(9 条名字和目标完全一样),手机上白占 312px。桌面端改用「最新文章」,把重复变成发现。不影响 SEO:顶部导航和移动端菜单里的链接在每页 HTML 里都还在。
两点需要注意:
- **「我的介绍」整块包 `<a>` 是合法的**,因为这一块里没有别的链接。但**侧栏的作者卡片不行** —— 它下面有社交图标(本身是 `<a>`),`<a>` 不能嵌套。所以那里只包到签名那一段。
- 整块链接要给 `aria-label`,否则读屏会把「头像 alt + 站名 + 签名」整段念成链接名。
统计数据在 `src/lib/site-stats.ts`:文章数/偶得数/分类数/标签数/总字数在构建时算,「已运行 N 天」和「最近更新」由页脚里的内联脚本按**真实时间**刷新(站点久不重建也不会停在构建那天),服务端会先渲染一份构建时兜底值,关 JS 也不空。加减统计项改 `footer.astro` 里的 `statCells` 数组即可。
### 磨砂玻璃
`global.css` 里有四个 token:
+1 -1
View File
@@ -1,6 +1,6 @@
---
import SiteHeader from "@/components/site-header.astro"
import { Footer } from "@/components/footer"
import Footer from "@/components/footer.astro"
import { cn } from "@/lib/utils"
import booksData from "../../public/data/books.json"
import comicsData from "../../public/data/comics.json"
+188
View File
@@ -0,0 +1,188 @@
---
import type { ComponentType, SVGProps } from "react"
import { ArrowRight, Github, Mail, Music, Rss } from "lucide-react"
import { QqIcon } from "@/components/icons/qq-icon"
import { profile, type ExternalProfileLink, type ExternalProfileLinkType } from "@/data/profile"
import { site } from "@/data/site"
import { formatWords, getSiteStats } from "@/lib/site-stats"
const iconMap: Record<ExternalProfileLinkType, ComponentType<SVGProps<SVGSVGElement>>> = {
qq: QqIcon,
music: Music,
github: Github,
email: Mail,
}
const stats = await getSiteStats()
const contactLinks = profile.links.filter(
(link): link is ExternalProfileLink => link.type !== "wechat",
)
const statCells = [
{ label: "建站时间", value: stats.startDate, live: undefined },
{ label: "已运行", value: String(stats.runningDays), unit: "天", live: "running-days" },
{ label: "文章", value: String(stats.postCount), unit: "篇", live: undefined },
{ label: "偶得", value: String(stats.thoughtCount), unit: "条", live: undefined },
{ label: "总字数", value: formatWords(stats.totalWords), unit: "字", live: undefined },
{ label: "最近更新", value: stats.lastUpdatedText, live: "last-update" },
]
---
<footer class="border-t border-border/50 bg-background">
<div class="max-w-6xl mx-auto px-6 py-16">
<div class="grid gap-12 md:grid-cols-3">
{/*
我的介绍:整块可点去关于页(这里没有嵌套链接,所以整块包 <a> 是合法的)。
aria-label 是为了不让读屏把头像 alt + 站名 + 签名全念成链接名。
*/}
<a href="/about/" class="group block" aria-label={`关于 ${profile.name}`}>
<div class="flex items-center gap-3 mb-4">
<div class="w-8 h-8 rounded-full overflow-hidden border border-primary/20 bg-secondary transition-colors duration-200 group-hover:border-primary/60">
<img
src={profile.avatar}
alt={profile.name}
loading="lazy"
decoding="async"
class="w-full h-full object-cover"
/>
</div>
<span class="text-foreground font-medium transition-colors duration-200 group-hover:text-primary">
{site.name}
</span>
</div>
<p class="text-muted-foreground text-sm leading-relaxed">{profile.bio}</p>
<span class="mt-3 inline-flex items-center gap-1 text-xs text-muted-foreground transition-colors duration-200 group-hover:text-primary">
关于我
<ArrowRight class="w-3 h-3 transition-transform duration-200 group-hover:translate-x-0.5" />
</span>
</a>
{/*
最新文章:手机端上面就是文章列表,再列一遍没意义,所以只在 md 以上显示。
页脚原来那栏「导航」是顶部导航的逐项复制(9 条完全一样),已删掉。
*/}
<div class="hidden md:block md:justify-self-center">
<h4 class="text-sm font-medium text-foreground mb-4 uppercase tracking-wide">最新文章</h4>
<ul class="flex flex-col gap-3">
{stats.latestPosts.map((post) => (
<li>
<a
href={post.href}
class="group flex items-baseline gap-3 text-sm text-muted-foreground hover:text-foreground transition-colors duration-200"
>
<span class="tabular-nums text-xs text-muted-foreground/70 shrink-0">{post.date.slice(5)}</span>
<span class="line-clamp-1 transition-colors duration-200 group-hover:text-primary">{post.title}</span>
</a>
</li>
))}
</ul>
</div>
<div class="md:justify-self-end">
<h4 class="text-sm font-medium text-foreground mb-4 uppercase tracking-wide">联系</h4>
<div class="flex items-center gap-4">
{contactLinks.map((link) => {
const Icon = iconMap[link.type]
return (
<a
href={link.url}
target="_blank"
rel="noreferrer"
class="w-10 h-10 rounded-full border border-border flex items-center justify-center text-muted-foreground hover:text-foreground hover:border-primary/50 transition-all duration-200"
aria-label={link.name}
>
<Icon className="w-4 h-4" />
</a>
)
})}
</div>
<p class="text-muted-foreground text-sm mt-6">通过 RSS 订阅,获取最新文章更新。</p>
<a
href="/rss.xml"
target="_blank"
rel="noreferrer"
class="mt-3 inline-flex items-center gap-2 px-4 py-2 bg-primary text-primary-foreground text-sm font-medium rounded-lg hover:opacity-90 transition-opacity duration-200"
>
<Rss className="w-4 h-4" />
订阅 RSS
</a>
</div>
</div>
{/* 站点信息:桌面 6 列一行,手机 3 列两行 */}
<div class="mt-14 pt-8 border-t border-border/50 grid grid-cols-3 gap-x-4 gap-y-6 md:grid-cols-6">
{statCells.map((cell) => (
<div>
<div class="text-xs text-muted-foreground mb-1.5">{cell.label}</div>
<div class="text-foreground font-medium tabular-nums">
<span data-stat={cell.live}>{cell.value}</span>
{cell.unit && <span class="text-xs text-muted-foreground ml-0.5">{cell.unit}</span>}
</div>
</div>
))}
</div>
{/* Bottom */}
<div class="mt-10 pt-8 border-t border-border/50 grid gap-4 md:grid-cols-3">
<p class="text-muted-foreground text-sm">
© {site.startDate.slice(0, 4)} {profile.name}. All rights reserved.
</p>
<div class="flex flex-wrap items-center gap-x-4 gap-y-2 md:justify-center">
<a
href="https://beian.miit.gov.cn/"
target="_blank"
rel="noreferrer"
class="text-muted-foreground text-xs hover:text-foreground transition-colors duration-200"
>
陇ICP备2026007748号
</a>
<a
href="https://beian.mps.gov.cn/#/query/webSearch?code=62070202000904"
target="_blank"
rel="noreferrer"
class="inline-flex items-center gap-1 text-muted-foreground text-xs hover:text-foreground transition-colors duration-200"
>
<img src="/images/beian.png" alt="公安备案" class="h-3.5 w-auto shrink-0" />
甘公网安备62070202000904号
</a>
</div>
<a
href="https://github.com/Lapis0x0/VermilionVoid"
target="_blank"
rel="noreferrer"
class="flex items-center gap-2 text-muted-foreground text-xs hover:text-foreground transition-colors duration-200 group md:justify-end md:pr-4"
>
<Github class="w-3.5 h-3.5" />
<span>博客主题:朱墨留白 | VermilionVoid</span>
</a>
</div>
</div>
</footer>
<script is:inline define:vars={{ siteStartDate: stats.startDate, lastUpdatedIso: stats.lastUpdatedIso }}>
// 运行天数和「最近更新」按真实时间算,避免站点久不重建时数字停在构建那天。
// 服务端已经渲染了一份兜底值,没 JS 也不会是空的。
;(() => {
const updateStats = () => {
const now = new Date()
const start = new Date(siteStartDate + "T00:00:00+08:00")
const days = Math.max(0, Math.floor((now.getTime() - start.getTime()) / 86400000))
document.querySelectorAll('[data-stat="running-days"]').forEach((el) => {
el.textContent = String(days)
})
if (!lastUpdatedIso) return
const last = new Date(lastUpdatedIso)
const diff = Math.max(0, Math.floor((now.getTime() - last.getTime()) / 86400000))
const text = diff === 0 ? "今天" : diff === 1 ? "昨天" : `${diff} 天前`
document.querySelectorAll('[data-stat="last-update"]').forEach((el) => {
el.textContent = text
})
}
updateStats()
// ClientRouter 切页后页脚是新节点,要再刷一次
document.addEventListener("astro:page-load", updateStats)
})()
</script>
-138
View File
@@ -1,138 +0,0 @@
import type { ComponentType, SVGProps } from "react"
import { Github, Mail, Music, Rss } from "lucide-react"
import { profile, type ExternalProfileLink, type ExternalProfileLinkType } from "@/data/profile"
import { site } from "@/data/site"
import { QqIcon } from "@/components/icons/qq-icon"
const iconMap: Record<ExternalProfileLinkType, ComponentType<SVGProps<SVGSVGElement>>> = {
qq: QqIcon,
music: Music,
github: Github,
email: Mail,
}
export function Footer() {
return (
<footer className="border-t border-border/50 bg-background">
<div className="max-w-6xl mx-auto px-6 py-16">
<div className="grid gap-12 md:grid-cols-3">
{/* Brand */}
<div>
<div className="flex items-center gap-3 mb-4">
<div className="w-8 h-8 rounded-full overflow-hidden border border-primary/20 bg-secondary">
<img
src={profile.avatar}
alt={profile.name}
loading="lazy"
decoding="async"
className="w-full h-full object-cover"
/>
</div>
<span className="text-foreground font-medium">{site.name}</span>
</div>
<p className="text-muted-foreground text-sm leading-relaxed">
{profile.bio}
</p>
</div>
{/* Navigation */}
<div className="md:flex md:justify-center">
<div>
<h4 className="text-sm font-medium text-foreground mb-4 uppercase tracking-wide">导航</h4>
<nav className="flex flex-col gap-3">
{[
{ name: "首页", href: "/" },
{ name: "时间线", href: "/timeline/" },
{ name: "偶得", href: "/thoughts/" },
{ name: "相册", href: "/gallery/" },
{ name: "书架", href: "/bookshelf/" },
{ name: "专题", href: "/studies/" },
{ name: "友链", href: "/friends/" },
{ name: "关于", href: "/about/" },
{ name: "开往", href: "https://www.travellings.cn/go.html", external: true },
].map((item) => (
<a
key={item.name}
href={item.href}
target={item.external ? "_blank" : undefined}
rel={item.external ? "noreferrer" : undefined}
className="text-muted-foreground text-sm hover:text-foreground transition-colors duration-200"
>
{item.name}
</a>
))}
</nav>
</div>
</div>
{/* Connect */}
<div className="md:flex md:justify-end">
<div>
<h4 className="text-sm font-medium text-foreground mb-4 uppercase tracking-wide">联系</h4>
<div className="flex items-center gap-4">
{profile.links.filter((link): link is ExternalProfileLink => link.type !== "wechat").map((link) => {
const Icon = iconMap[link.type]
return (
<a
key={link.name}
href={link.url}
target="_blank"
rel="noreferrer"
className="w-10 h-10 rounded-full border border-border flex items-center justify-center text-muted-foreground hover:text-foreground hover:border-primary/50 transition-all duration-200"
aria-label={link.name}
>
<Icon className="w-4 h-4" />
</a>
)
})}
</div>
<p className="text-muted-foreground text-sm mt-6">通过 RSS 订阅,获取最新文章更新。</p>
<a
href="/rss.xml"
target="_blank"
rel="noreferrer"
className="mt-3 inline-flex items-center gap-2 px-4 py-2 bg-primary text-primary-foreground text-sm font-medium rounded-lg hover:opacity-90 transition-opacity duration-200"
>
<Rss className="w-4 h-4" />
订阅 RSS
</a>
</div>
</div>
</div>
{/* Bottom */}
<div className="mt-16 pt-8 border-t border-border/50 grid gap-4 md:grid-cols-3">
<p className="text-muted-foreground text-sm">© {site.startDate.slice(0, 4)} {profile.name}. All rights reserved.</p>
<div className="flex flex-wrap items-center gap-x-4 gap-y-2 md:justify-center">
<a
href="https://beian.miit.gov.cn/"
target="_blank"
rel="noreferrer"
className="text-muted-foreground text-xs hover:text-foreground transition-colors duration-200"
>
陇ICP备2026007748号
</a>
<a
href="https://beian.mps.gov.cn/#/query/webSearch?code=62070202000904"
target="_blank"
rel="noreferrer"
className="inline-flex items-center gap-1 text-muted-foreground text-xs hover:text-foreground transition-colors duration-200"
>
<img src="/images/beian.png" alt="公安备案" className="h-3.5 w-auto shrink-0" />
甘公网安备62070202000904号
</a>
</div>
<a
href="https://github.com/Lapis0x0/VermilionVoid"
target="_blank"
rel="noreferrer"
className="flex items-center gap-2 text-muted-foreground text-xs hover:text-foreground transition-colors duration-200 group md:justify-end md:pr-4"
>
<Github className="w-3.5 h-3.5" />
<span>博客主题:朱墨留白 | VermilionVoid</span>
</a>
</div>
</div>
</footer>
)
}
+17 -7
View File
@@ -2,7 +2,7 @@
import { useState, type ReactNode } from "react"
import { cn } from "@/lib/utils"
import { Calendar } from "lucide-react"
import { ArrowRight, Calendar } from "lucide-react"
import { profile } from "@/data/profile"
import { ProfileContactLink } from "@/components/profile-contact-link"
@@ -39,23 +39,33 @@ export function Sidebar({
return (
<aside className="w-full lg:w-72 shrink-0 space-y-8">
{/* Author Card */}
{/* Author Card —— 头像/站名/签名整块可点,跳关于页。
注意只能包到这里:下面的社交图标本身是 <a>,<a> 不能嵌套 <a>。
aria-label 是为了不让读屏把头像 alt + 站名 + 签名整段念成链接名。 */}
<div className="bg-card border border-border/50 rounded-xl p-6 text-center">
<div className="w-24 h-24 mx-auto mb-4 rounded-full overflow-hidden border-2 border-primary/20">
<a href="/about/" className="group block" aria-label={`关于 ${profile.name}`}>
<div className="w-24 h-24 mx-auto mb-4 rounded-full overflow-hidden border-2 border-primary/20 transition-colors duration-200 group-hover:border-primary/60">
<img
src={profile.avatar}
alt={profile.name}
loading="lazy"
decoding="async"
className="w-full h-full object-cover"
className="w-full h-full object-cover transition-transform duration-300 group-hover:scale-105"
/>
</div>
<h3 className="font-serif text-xl font-bold text-foreground mb-1">{profile.name}</h3>
<h3 className="font-serif text-xl font-bold text-foreground mb-1 transition-colors duration-200 group-hover:text-primary">
{profile.name}
</h3>
<div className="w-8 h-0.5 bg-primary mx-auto mb-3" />
<p className="text-sm text-muted-foreground leading-relaxed mb-4">{profile.bio}</p>
<p className="text-sm text-muted-foreground leading-relaxed">{profile.bio}</p>
<span className="mt-2 inline-flex items-center gap-1 text-xs text-muted-foreground transition-colors duration-200 group-hover:text-primary">
关于我
<ArrowRight className="w-3 h-3 transition-transform duration-200 group-hover:translate-x-0.5" />
</span>
</a>
{/* Social Links */}
<div className="flex items-center justify-center gap-3 mb-4">
<div className="flex items-center justify-center gap-3 mt-4 mb-4">
{profile.links.map((link) => {
return (
<ProfileContactLink
+87
View File
@@ -0,0 +1,87 @@
import { getCollection } from "astro:content"
import { site } from "@/data/site"
import { buildSidebarMeta, getSortedPosts, toPostMeta } from "./posts"
export type LatestPostLink = {
title: string
href: string
date: string
}
export type SiteStats = {
/** 建站日期,YYYY-MM-DD */
startDate: string
/** 已运行天数(构建时算的兜底值,页面加载后由内联脚本按真实时间刷新) */
runningDays: number
postCount: number
thoughtCount: number
categoryCount: number
tagCount: number
totalWords: number
/** 最后一篇的发布时间(ISO),用于前端算「最近更新」 */
lastUpdatedIso: string | null
/** 「最近更新」的构建时文案,作为无 JS 时的兜底 */
lastUpdatedText: string
/** 最近文章,按发布时间倒序(不把置顶提前) */
latestPosts: LatestPostLink[]
}
/** 把字数说成人话:12345 -> 1.2 万 */
export const formatWords = (n: number) => {
if (n >= 10000) return `${(n / 10000).toFixed(1)} 万`
return String(n)
}
const startOfDay = (d: Date) => new Date(d.getFullYear(), d.getMonth(), d.getDate())
export async function getSiteStats(latestCount = 4): Promise<SiteStats> {
const posts = await getSortedPosts()
const metas = posts.map(toPostMeta)
const { categories, tags } = buildSidebarMeta(metas)
const thoughts = await getCollection("thoughts")
const totalWords = metas.reduce((sum, meta) => sum + (meta.wordCount || 0), 0)
// 分类里有一条 id 为 "all" 的合集项,统计时要去掉
const categoryCount = categories.filter((category) => category.id !== "all").length
const startDate = site.startDate
const runningDays = Math.max(
0,
Math.floor((Date.now() - startOfDay(new Date(`${startDate}T00:00:00+08:00`)).getTime()) / 86_400_000),
)
// 「最近更新」看的是最后动过的时间,有 updated 就用它
const latestDate = posts.reduce<Date | null>((acc, post) => {
const d = new Date(post.data.updated ?? post.data.published)
return !acc || d > acc ? d : acc
}, null)
const latestPosts = [...posts]
.sort((a, b) => new Date(b.data.published).getTime() - new Date(a.data.published).getTime())
.slice(0, latestCount)
.map((post) => ({
title: post.data.title,
href: `/posts/${post.slug}/`,
date: new Date(post.data.published).toISOString().slice(0, 10),
}))
const sinceUpdate = latestDate
? Math.max(0, Math.floor((Date.now() - latestDate.getTime()) / 86_400_000))
: null
const lastUpdatedText =
sinceUpdate === null ? "—" : sinceUpdate === 0 ? "今天" : sinceUpdate === 1 ? "昨天" : `${sinceUpdate} 天前`
return {
startDate,
runningDays,
postCount: posts.length,
thoughtCount: thoughts.length,
categoryCount,
tagCount: tags.length,
totalWords,
lastUpdatedIso: latestDate ? latestDate.toISOString() : null,
lastUpdatedText,
latestPosts,
}
}
+1 -1
View File
@@ -2,7 +2,7 @@
import type { GetStaticPaths } from "astro"
import BaseLayout from "@/layouts/BaseLayout.astro"
import { ArticleList } from "@/components/article-list"
import { Footer } from "@/components/footer"
import Footer from "@/components/footer.astro"
import SiteHeader from "@/components/site-header.astro"
import { PAGE_SIZE } from "@/lib/constants"
import { buildSidebarMeta, getSortedPosts, toPostMeta } from "@/lib/posts"
+1 -1
View File
@@ -2,7 +2,7 @@
import BaseLayout from "@/layouts/BaseLayout.astro"
import AboutPage from "@/components/pages/AboutPage"
import Comments from "@/components/Comments.astro"
import { Footer } from "@/components/footer"
import Footer from "@/components/footer.astro"
import SiteHeader from "@/components/site-header.astro"
import { siteTitleSuffix } from "@/data/site"
+1 -1
View File
@@ -2,7 +2,7 @@
import BaseLayout from "@/layouts/BaseLayout.astro"
import FriendsPage from "@/components/pages/FriendsPage.astro"
import Comments from "@/components/Comments.astro"
import { Footer } from "@/components/footer"
import Footer from "@/components/footer.astro"
import { siteTitleSuffix } from "@/data/site"
const title = `友链 | ${siteTitleSuffix}`
+1 -1
View File
@@ -1,7 +1,7 @@
---
import BaseLayout from "@/layouts/BaseLayout.astro"
import SiteHeader from "@/components/site-header.astro"
import { Footer } from "@/components/footer"
import Footer from "@/components/footer.astro"
import { siteTitleSuffix } from "@/data/site"
import { galleryConfig } from "@/data/gallery"
import { getAlbumCover, scanAlbumPhotos } from "@/lib/gallery"
+1 -1
View File
@@ -1,7 +1,7 @@
---
import BaseLayout from "@/layouts/BaseLayout.astro"
import SiteHeader from "@/components/site-header.astro"
import { Footer } from "@/components/footer"
import Footer from "@/components/footer.astro"
import { siteTitleSuffix } from "@/data/site"
import { getAllAlbumTags, getAlbums } from "@/lib/gallery"
+1 -1
View File
@@ -1,7 +1,7 @@
---
import BaseLayout from "@/layouts/BaseLayout.astro"
import { ArticleList } from "@/components/article-list"
import { Footer } from "@/components/footer"
import Footer from "@/components/footer.astro"
import SiteHeader from "@/components/site-header.astro"
import { Hero } from "@/components/hero"
import { PAGE_SIZE } from "@/lib/constants"
+1 -1
View File
@@ -2,7 +2,7 @@
import BaseLayout from "@/layouts/BaseLayout.astro"
import ArticleContent from "@/components/ArticleContent.astro"
import SiteHeader from "@/components/site-header.astro"
import { Footer } from "@/components/footer"
import Footer from "@/components/footer.astro"
import { getPostsWithNav, type PostEntry } from "@/lib/posts"
import Disclaimer from "@/components/Disclaimer.astro"
import PasswordProtection from "@/components/PasswordProtection.astro"
+1 -1
View File
@@ -1,7 +1,7 @@
---
import BaseLayout from "@/layouts/BaseLayout.astro"
import SiteHeader from "@/components/site-header.astro"
import { Footer } from "@/components/footer"
import Footer from "@/components/footer.astro"
import { StudyDetailView } from "@/components/studies-detail"
import { getAllStudies } from "@/lib/studies"
import { siteTitleSuffix } from "@/data/site"
+1 -1
View File
@@ -1,7 +1,7 @@
---
import BaseLayout from "@/layouts/BaseLayout.astro"
import SiteHeader from "@/components/site-header.astro"
import { Footer } from "@/components/footer"
import Footer from "@/components/footer.astro"
import { StudiesList } from "@/components/studies-list"
import { getAllStudies } from "@/lib/studies"
import { siteTitleSuffix } from "@/data/site"
+1 -1
View File
@@ -1,7 +1,7 @@
---
import BaseLayout from "@/layouts/BaseLayout.astro"
import { ThoughtsView } from "@/components/thoughts-view"
import { Footer } from "@/components/footer"
import Footer from "@/components/footer.astro"
import SiteHeader from "@/components/site-header.astro"
import { getSortedThoughts, toThoughtMeta } from "@/lib/thoughts"
import { siteTitleSuffix } from "@/data/site"
+1 -1
View File
@@ -2,7 +2,7 @@
import BaseLayout from "@/layouts/BaseLayout.astro"
import Timeline from "@/components/Timeline.astro"
import SiteHeader from "@/components/site-header.astro"
import { Footer } from "@/components/footer"
import Footer from "@/components/footer.astro"
import { getSortedPosts } from "@/lib/posts"
import { siteTitleSuffix } from "@/data/site"