diff --git a/astro.config.mjs b/astro.config.mjs index 9fb1778..9eeb6bb 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -4,6 +4,8 @@ import pagefind from 'astro-pagefind'; import sitemap from '@astrojs/sitemap'; import { execSync } from 'node:child_process'; import rehypeLinkCards, { linkCardsReport } from './src/lib/rehype-link-cards'; +import rehypeClassWhitelist from './src/lib/rehype-class-whitelist'; +import rehypeRaw from 'rehype-raw'; // 构建透明(T7):commit/时间经 vite.define 注入,页脚与 /about 引用。 // 四重回退:CI 环境变量优先(GITEA_ACTIONS/GITHUB_SHA)→ 本地 git → dev 占位。 @@ -21,7 +23,9 @@ export default defineConfig({ integrations: [pagefind(), sitemap(), linkCardsReport()], markdown: { // 独立成段的裸链接 → OG 预览卡(构建期抓取,失败回落普通链接;见 src/lib/rehype-link-cards.ts) - rehypePlugins: [rehypeLinkCards], + // 顺序:rehypeRaw 先把 markdown 里的 raw HTML 解析成 hast element(否则白名单看不到 span), + // 再白名单净化(剥 style/on*/非白名单类),最后链接卡片(节点由插件自建,不经净化) + rehypePlugins: [rehypeRaw, rehypeClassWhitelist, rehypeLinkCards], }, i18n: { defaultLocale: 'zh', diff --git a/content/posts/fix3-test.md b/content/posts/fix3-test.md new file mode 100644 index 0000000..fd83fff --- /dev/null +++ b/content/posts/fix3-test.md @@ -0,0 +1,34 @@ +--- +title: "排版能力样例" +pubDate: 2026-11-02 +category: tech +tags: [test, hidden] +# 排版/斜体/白名单测试样张(fix3 车道):hidden 不列出,构建与 E2E 验证用 +draft: false +--- + +本篇验证三件事:斜体行内渲染、受限排版类白名单、图注类。 + +## 斜体(应为行内,不换行不居中) + +这一句里有 *行内斜体强调* 夹在文字中间,它必须与正文同处一行。 + +*这一整段是斜体独占段,它不该变成块级居中。* + +## 受限排版类(白名单) + +这一段是 ink-muted 灰字。 +这一段是霞鹜文楷字族。 +这一段是等宽字族 mono。 + +

首字下沉段落:秋天的海面像一块被风抚平的铁皮,落日把它的边缘烧红了一圈。

+ +

所谓暮色,就是光慢慢地承认自己要走。

+ +## 白名单剔除(非法类与内联样式必须被剥离) + +这一段同时带了非法类、内联颜色与 onclick,构建后应只剩 ink-2 且 style/onclick 消失。 + +## 图注(类驱动) + +

这是显式图注样式(fig-cap),居中灰字。

diff --git a/content/posts/media-sample.md b/content/posts/media-sample.md index 3f16e0f..d540986 100644 --- a/content/posts/media-sample.md +++ b/content/posts/media-sample.md @@ -14,7 +14,7 @@ draft: false ![样例照片](./assets/media-sample/sample-photo.jpg) -*图注写法:斜体单独成段,photos 目录样片副本(1600×2400 竖幅)。* +

图注新写法:fig-cap 类,photos 目录样片副本(1600×2400 竖幅)。

## 视频(raw HTML,H.264 320×240,ffmpeg testsrc2 生成) diff --git a/docs/写作指南.md b/docs/写作指南.md index e3c9120..aaa00b4 100644 --- a/docs/写作指南.md +++ b/docs/写作指南.md @@ -22,10 +22,11 @@ content/posts/ ```markdown ![外滩夜景](./assets/my-post/bund.jpg) -*图:外滩的傍晚* ← 紧跟媒体的斜体独立段 = 图注(居中小字) +

图:外滩的傍晚

← 显式图注类(居中小字) ``` -- 图注可选;写法固定为 `*…*` 单独成段,紧跟图片 +- 图注可选;**必须用 `

…

`**(2026-11-02 起) +- ⚠️ 旧写法 `*斜体独立段*` 已不再自动当图注:此前它会把正文里所有独占成段的斜体都变成块级居中(用户实测的排版 bug),现在斜体恢复为正常行内强调 - 压缩纪律:入仓前长边 ≤2400px(可跑 `python3 scripts/photos_ingest.py`,对 posts 资产同样有效的思路,图片请自行用同规格压缩) ## 视频 / 音频 @@ -83,6 +84,33 @@ https://astro.build/ - **缓存**:结果存 `content/.link-cache.json`(入仓,保证构建确定性);条目 30 天后自动重抓。想强制刷新某条——删掉该 URL 的条目即可 - 缩略图取 `og:image`,无图不占位;图标取站点而非第三方 favicon 服务 +## 文章排版类(受限白名单) + +正文里可用 `` / `

` 做**受限**排版。构建期有白名单过滤:**只放行下表类名,其它 class 与 `style`、`on*` 属性一律被剥离**(防注入 + 保护灰阶骨架)。 + +| 类名 | 作用 | +|---|---| +| `ink-1` / `ink-2` / `ink-3` | 指定灰阶层(正文/次要/更淡) | +| `ink-muted` | 最淡灰(注释感) | +| `ink-accent` | 强调色(链接色,仅少量点缀) | +| `font-serif` | 思源宋(书评正文族) | +| `font-kai` | 霞鹜文楷(金句族) | +| `font-sans` | 思源黑(UI/技术族) | +| `font-mono` | 等宽族(标签/数值) | +| `drop-cap` | 段落首字下沉(书评开篇) | +| `quote-line` | 金句块(左侧竖线 + 文楷) | +| `fig-cap` | 图注(居中小字) | + +```markdown +次要说明 +金句可以换文楷 +

开篇段落,首字会下沉。

+

所谓暮色,就是光慢慢地承认自己要走。

+``` + +- **颜色只能走上面这些类**(内部用 tokens,亮暗双态都可读);写 `style="color:red"` 会被构建期剔除 +- 斜体就用普通 markdown `*斜体*`——行内强调,不会换行也不会居中 + ## 发布提醒 - push main → webhook 自动构建发布(约 10 秒) diff --git a/package.json b/package.json index f89701b..eab359e 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,8 @@ "astro-pagefind": "^2.0.1", "exifr": "^7.1.3", "lxgw-wenkai-screen-webfont": "^1.7.0", + "pdfjs-dist": "^6.4.299", + "rehype-raw": "^7.0.0", "sharp": "^0.35.5" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7926bd7..ff4141b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -38,6 +38,12 @@ importers: lxgw-wenkai-screen-webfont: specifier: ^1.7.0 version: 1.7.0 + pdfjs-dist: + specifier: ^6.4.299 + version: 6.4.299 + rehype-raw: + specifier: ^7.0.0 + version: 7.0.0 sharp: specifier: ^0.35.5 version: 0.35.5(@types/node@24.19.2) @@ -427,6 +433,81 @@ packages: '@mdx-js/mdx@3.1.1': resolution: {integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==} + '@napi-rs/canvas-android-arm64@1.1.0': + resolution: {integrity: sha512-mKrSmRyhWK4q699Q1xthbZIZi7JBvLtsRNnOZAeFaLPOFT1yo8y82aRC2nNZbXTGJXU7fVDONgGQPr0vo3kerw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@napi-rs/canvas-darwin-arm64@1.1.0': + resolution: {integrity: sha512-iqwST4g0oH+x8VCpBIYh+MYokv/mVvkM+v9oJGB0d9A3P2qBh42ogJlVyWq9SpPCrAjS7tplMb1D3OIfxl+utw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@napi-rs/canvas-darwin-x64@1.1.0': + resolution: {integrity: sha512-jkeA8eVTxDum03LTvc32YJ28F6KB1U0gYwnWMfOAUQZvWWEh/kvONby4iWGJx3+Tw45UqMGMXjtw2GKNVZDhVg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@napi-rs/canvas-linux-arm-gnueabihf@1.1.0': + resolution: {integrity: sha512-Y4YkKG1ijoqkgUdTEEBNG4k+2BK5bR5jGL6S56YxCXYPwGfUd5nED8/74xyc7z+bpixPasCjSYoP3xYPOIGNxw==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@napi-rs/canvas-linux-arm64-gnu@1.1.0': + resolution: {integrity: sha512-dE/XthqYkiWcBVtKFlZbUXwAYfDuQGqDWixznipJNmevLGpprNnTVQZyftm1RZJudkIOn2EkzYZDMMETM08Row==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@napi-rs/canvas-linux-arm64-musl@1.1.0': + resolution: {integrity: sha512-DqZ/CMcHlhU56pPjv9AP0h3U7MFQ5LzC1HzMyRMotL+PFsJmZJA/ziApPYVyyz78DaVLhI8t3So3ztqtijbYpg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@napi-rs/canvas-linux-riscv64-gnu@1.1.0': + resolution: {integrity: sha512-kJ7m5n2VffxWXM5QGa5lfq8ZN4LdOJEoz3aAJAmIqYvKiZw1gRdXqpucnRXsF6D9nLYiiIlevaAtAclR7c5ilg==} + engines: {node: '>= 10'} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@napi-rs/canvas-linux-x64-gnu@1.1.0': + resolution: {integrity: sha512-uQ9JuJlRhQdHyttcMWOiwLkfIqkqaB0YFUVe835LUe12THt2b51HXkTItq0fxWT1jdhUPX2ylWZ1walkJyD6GQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@napi-rs/canvas-linux-x64-musl@1.1.0': + resolution: {integrity: sha512-has4CPPAkkCac/LZ13VAw5p+MJLdaKVoyrhumN/A7uJd6960xYkJNDZgUebyxiha82AFzXx6RV+QJg5qGG079g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@napi-rs/canvas-win32-arm64-msvc@1.1.0': + resolution: {integrity: sha512-MJnktLKNijmXqdjTughHDZ9TDLZ9uUm2hvFXqaOqn0+w7lphXFnft6bpSgQAc9U1exc20UGIBlzGu9+kiCj9Ag==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@napi-rs/canvas-win32-x64-msvc@1.1.0': + resolution: {integrity: sha512-08575bFk7iiadxap4h+JxgGut9xDCHAGum6wh0eInN53zEMImkmEHi5MushX2FOmNO7qRNX7BPiDC5ZYBwHeEg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@napi-rs/canvas@1.1.0': + resolution: {integrity: sha512-k+ph/D8H9GS8ZZ48okvHrKeJynkqAA1kMUZRuzRWNlQUDHXngq7oRaWMZBdmSfd9ZhVy9bwNKei/7wMboI/uMg==} + engines: {node: '>= 10'} + '@napi-rs/wasm-runtime@1.2.6': resolution: {integrity: sha512-mRyDmEw9/fCDXCGfL1sbgIw/GUK0PVxRW4YwgPN61F3LQ+fSyoBFarJVzgGyhF6kRzEQZ0FejDqm4WLXSeqQmw==} engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0} @@ -1369,6 +1450,10 @@ packages: resolution: {integrity: sha512-enSlaiat05iasnzmgNxRj8reFdj3puY2QpNgP1aPIaVfT6nn9ICuPoFlKHk8EN22HcwewshO+mN2DGbkCEOtqQ==} engines: {node: '>=14.0.0'} + pdfjs-dist@6.4.299: + resolution: {integrity: sha512-AVl138zALtfaAPvADulE0PZThbYzCBS79nL4pOSL/6Sm/4AH5A21BD9VHt97OlCuzJuCpmeZtAtkinisF4Vb1g==} + engines: {node: '>=22.13.0 || >=24'} + piccolore@0.1.3: resolution: {integrity: sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw==} @@ -2166,6 +2251,54 @@ snapshots: transitivePeerDependencies: - supports-color + '@napi-rs/canvas-android-arm64@1.1.0': + optional: true + + '@napi-rs/canvas-darwin-arm64@1.1.0': + optional: true + + '@napi-rs/canvas-darwin-x64@1.1.0': + optional: true + + '@napi-rs/canvas-linux-arm-gnueabihf@1.1.0': + optional: true + + '@napi-rs/canvas-linux-arm64-gnu@1.1.0': + optional: true + + '@napi-rs/canvas-linux-arm64-musl@1.1.0': + optional: true + + '@napi-rs/canvas-linux-riscv64-gnu@1.1.0': + optional: true + + '@napi-rs/canvas-linux-x64-gnu@1.1.0': + optional: true + + '@napi-rs/canvas-linux-x64-musl@1.1.0': + optional: true + + '@napi-rs/canvas-win32-arm64-msvc@1.1.0': + optional: true + + '@napi-rs/canvas-win32-x64-msvc@1.1.0': + optional: true + + '@napi-rs/canvas@1.1.0': + optionalDependencies: + '@napi-rs/canvas-android-arm64': 1.1.0 + '@napi-rs/canvas-darwin-arm64': 1.1.0 + '@napi-rs/canvas-darwin-x64': 1.1.0 + '@napi-rs/canvas-linux-arm-gnueabihf': 1.1.0 + '@napi-rs/canvas-linux-arm64-gnu': 1.1.0 + '@napi-rs/canvas-linux-arm64-musl': 1.1.0 + '@napi-rs/canvas-linux-riscv64-gnu': 1.1.0 + '@napi-rs/canvas-linux-x64-gnu': 1.1.0 + '@napi-rs/canvas-linux-x64-musl': 1.1.0 + '@napi-rs/canvas-win32-arm64-msvc': 1.1.0 + '@napi-rs/canvas-win32-x64-msvc': 1.1.0 + optional: true + '@napi-rs/wasm-runtime@1.2.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': dependencies: '@emnapi/core': 1.11.1 @@ -3457,6 +3590,10 @@ snapshots: path-expression-matcher@1.6.2: {} + pdfjs-dist@6.4.299: + optionalDependencies: + '@napi-rs/canvas': 1.1.0 + piccolore@0.1.3: {} picocolors@1.1.1: {} diff --git a/src/components/ArtalkComments.astro b/src/components/ArtalkComments.astro index c9aa5de..af5362f 100644 --- a/src/components/ArtalkComments.astro +++ b/src/components/ArtalkComments.astro @@ -17,6 +17,10 @@ const locale = localeOf(Astro.url.pathname); {' '}{t('pv_viewed', locale)}

+ -
- + + - +
+ +
+ +