Files
NewBlog/astro.config.mjs
TimeCrackbot f4a49ef94c feat: 支持 Obsidian 风格 callout,并把脚注文案中文化
Callout
- 接入 satteri-callouts(HAST 插件),用 Obsidian 语法:
    > [!tip] 标题
    > 内容
    > [!warning]- 默认折叠
  类型名大小写不敏感,支持嵌套、可折叠、标题内联 Markdown。
- 样式没有用它自带的主题 CSS(Obsidian 原版是高饱和蓝/青/紫,还靠
  mix-blend-mode 叠底色,在暖纸底上会发脏),改为新建 src/styles/callouts.css,
  复用主题已有的 --admonitions-color-* token,与 .admonition(免责声明块)
  统一成「3px 左边框 + 彩色标题、无底色」。
- 类型映射:tip/hint→tip,note/info/todo/abstract/summary/tldr→note,
  important/example→important,warning/attention/question/help/faq→warning,
  caution→caution,success/check/done→(新增的)success,
  danger/error/failure/fail/missing/bug→destructive,quote/cite→muted-foreground。
- global.css 补一个 --admonitions-color-success(浅色 oklch(0.62 0.16 150) /
  深色 oklch(0.7 0.18 150)),与 tip(180)/note(250)/important(310) 同明度彩度。

脚注中文化
features.gfm.footnotes 设 label「脚注」、backLabel「返回第 {reference} 条」、
backContent「↑」,替换默认的 Footnotes / Back to reference。

正文
- 周末愉快.md:`::: tip 下周计划`(冒号后有空格,从来不是合法指令,
  一直渲染成普通段落)改成 `> [!tip] 下周计划`,任务列表一并加 `>` 前缀。
- 关于价值感和归属感的思考.md 的 `> [!tip] Tip` 本来就合法,一行未改,
  现在自动变成真 callout。

顺带修
autolink-headings 不再给脚注区那个 sr-only 的「脚注」标题加 # 锚点。

验证
- 两处 callout 均正确渲染;callout 内的图片仍带 data-lightbox-src
  (说明 imageLightbox 与 satteriCallouts 的执行顺序没问题)。
- 临时加了一篇带脚注和折叠 callout 的文章验证:脚注文案、返回链接、
  折叠态 <details>/<summary> 结构都正确,验证后已删除。
- 整页对比基线:37/41 一致,4 处差异中 2 处是本次有意的 callout 生效,
  另 2 处是已知的智能标点引号方向(Sätteri 的结果更正确)。
- pnpm build 41 页 + pagefind 通过。
2026-09-17 18:01:44 +08:00

132 lines
4.8 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { defineConfig } from "astro/config"
import react from "@astrojs/react"
import sitemap from "@astrojs/sitemap"
import mdx from "@astrojs/mdx"
import { pluginCollapsibleSections } from "@expressive-code/plugin-collapsible-sections"
import { pluginLineNumbers } from "@expressive-code/plugin-line-numbers"
import expressiveCode from "astro-expressive-code"
import { pluginLanguageBadge } from "./src/plugins/expressive-code/language-badge"
import { pluginCustomCopyButton } from "./src/plugins/expressive-code/custom-copy-button"
import { satteri, satteriHeadingIdsPlugin } from "@astrojs/markdown-satteri"
import { excerpt } from "./src/plugins/satteri/excerpt.mjs"
import { readingTime } from "./src/plugins/satteri/reading-time.mjs"
import { sectionize } from "./src/plugins/satteri/sectionize.mjs"
import { shiftHeadings } from "./src/plugins/satteri/shift-headings.mjs"
import { autolinkHeadings } from "./src/plugins/satteri/autolink-headings.mjs"
import { imageLightbox } from "./src/plugins/satteri/image-lightbox.mjs"
import { satteriKatex } from "satteri-katex"
import satteriCallouts from "satteri-callouts"
import { site } from "./src/data/site"
export default defineConfig({
srcDir: "./src",
output: "static",
site: site.url,
trailingSlash: "always",
alias: {
"@": "./src",
},
integrations: [
expressiveCode({
themes: ["github-dark", "github-light"],
themeCssSelector: (theme) => {
// 根据主题名称返回对应的 CSS 选择器
if (theme.name === "github-light") return ":root:not(.dark)"
return ".dark"
},
plugins: [
pluginCollapsibleSections(),
pluginLineNumbers(),
pluginLanguageBadge(),
pluginCustomCopyButton(),
],
defaultProps: {
wrap: true,
overridesByLang: {
shellsession: {
showLineNumbers: false,
},
},
},
styleOverrides: {
codeBackground: "var(--codeblock-bg)",
borderRadius: "0.75rem",
borderColor: "transparent",
codeFontSize: "0.875rem",
codeFontFamily: "var(--font-mono), ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
codeLineHeight: "1.625",
frames: {
editorBackground: "var(--codeblock-bg)",
terminalBackground: "var(--codeblock-bg)",
terminalTitlebarBackground: "var(--codeblock-topbar-bg)",
editorTabBarBackground: "var(--codeblock-topbar-bg)",
editorActiveTabBackground: "transparent",
editorActiveTabIndicatorBottomColor: "var(--primary)",
editorActiveTabIndicatorTopColor: "transparent",
editorTabBarBorderBottomColor: "var(--codeblock-topbar-bg)",
terminalTitlebarBorderBottomColor: "transparent",
},
},
frames: {
showCopyToClipboardButton: false,
},
}),
react(),
sitemap(),
mdx(),
],
compressHTML: true,
markdown: {
// Astro 7 的 Sätteri 管线。以下插件是 unified 时期 remark/rehype 插件的对应移植,
// 见 src/plugins/satteri/。
//
// 注意:math 只负责「解析」,渲染成 KaTeX 需要额外的 HAST 插件 —— 尚未迁移。
processor: satteri({
// dashes: false 是为了和迁移前的输出逐字一致:Astro 的 unified 管线
// 会把直引号排成弯引号,但不转换 --- (Sätteri 默认会转成破折号)。
features: {
math: true,
smartPunctuation: { dashes: false },
// 脚注文案中文化(默认是 "Footnotes" / "Back to reference 1")
gfm: {
footnotes: {
label: "脚注",
backLabel: "返回第 {reference} 条",
backContent: "↑",
},
},
},
mdastPlugins: [
readingTime,
excerpt,
shiftHeadings(),
sectionize(),
// KaTeX 必须排在 reading-time / excerpt 之后:它把 math 节点换成 KaTeX 的
// HTML,先换的话 ctx.textContent() 会把 KaTeX 标记也算进字数。
satteriKatex(),
],
hastPlugins: [
satteriHeadingIdsPlugin(),
autolinkHeadings,
imageLightbox,
// Obsidian 语法的 callout:> [!tip] 标题
satteriCallouts(),
],
}),
},
vite: {
server: {
watch: {
/*
* 项目根目录下的 .pnpm-store 是 pnpm 的内容寻址仓库:5.4 万个文件、672 MB,
* 而且已经被 .gitignore 忽略。Vite 默认会把项目根整个 watch,加上它以后
* inotify 直接到顶(max_user_watches = 65536),dev server 一启动就刷屏:
* ENOSPC: System limit for number of file watchers reached
* 排除掉即可。node_modules / .git 是 Vite 默认就排除的,不用管。
*/
ignored: ["**/.pnpm-store/**"],
},
},
},
})