【T1 PDF 内置阅读重做】iframe 对 arXiv PDF 实测 ERR_ABORTED(浏览器 PDF
插件策略差异)→ pdfjs-dist 6.4.299 客户端 canvas 渲染:只在 /papers/
动态 import(worker ?url 打包,不进全站 bundle);文档一次取回 + IO
懒渲染(rootMargin 400px)+ 页占位 aspect-ratio 防跳动 + 12s 首屏兜底 +
微信 UA 直接兜底;工具条页码/缩放/翻页/下载/arXiv/授权声明保留。红线:
PDF 由访客浏览器直取 arXiv(CORS * 实测),仓内零副本。真页实测:
13 页 canvas 全绘制、首屏墨迹像素非空、零 console 错误。
【T2 斜体排版 bug 真凶】global.css 的 `.prose-* p > em:only-child
{ display:block; text-align:center }` 图注样式按"斜体独占段"自动命中,
把所有正文独占段斜体变成块级居中。改:图注用显式 .fig-cap 驱动 + 补
em/i { display:inline; font-style:italic } 基线;media-sample 迁移新写法,
指南注明旧写法失效原因。实测独占段与行内斜体均 display:inline/italic。
【T3 评论"假挂载"真 bug】原 initArtalk 一进来就置 mounted=true——脚本
404/被拦截时把空容器误标已挂载,5s 兜底被绕过,正是"评论区一直空着"的
来源之一。改为库就绪才算成功;另加三触发路径(IO 主 + 锚点点击 + 滚到
页底 2s 兜底)、5s 失败提示+重试钮、软导航重挂、成功打
data-artalk-mounted=1 供 E2E。
【T4 文章排版类 + 安全白名单】11 个受限类(ink-1/2/3/muted/accent、
font-serif/kai/sans/mono、drop-cap、quote-line、fig-cap)全走 tokens 禁
任意 hex;新增 rehype-class-whitelist.ts:className 白名单 + 剥离 style
与 on*;实测注入用例只剩 class="ink-2",style/onclick 全无。新坑入档:
markdown raw HTML 在 hast 是 type:'raw' 节点、白名单插件看不见 span,必须
先 rehype-raw 再过滤 → 插件链 [rehypeRaw, rehypeClassWhitelist,
rehypeLinkCards]。新依赖:pdfjs-dist、rehype-raw。
车道:op-artalk(T1 6/6、T2 2/2、T3 3/3、T4 6/6);收编复验:build 46 页、
E2E 37/37、四专项 playwright 实测(PDF 13 canvas/斜体 inline/白名单净化/
评论 mounted 标记)
验证: node v26.13.0 + astro 7.4.4 构建 46 页 exit 0;E2E 37/37 PASS
Spec: none (阅读体验四件,车道级交付)
Co-authored-by: GLM-5.3
205 lines
9.0 KiB
Markdown
205 lines
9.0 KiB
Markdown
# 写作指南:在文章里插入图片 / 视频 / 音频
|
||
|
||
> 适用:`content/posts/*.md`(书评与技术轨通用)。资产目录约定 + 三种媒体的写法 + 体积纪律。
|
||
|
||
## 目录约定
|
||
|
||
```
|
||
content/posts/
|
||
├── my-post.md # 文章
|
||
└── assets/
|
||
└── my-post/ # 与文章 slug 同名:该篇私有资产
|
||
├── photo.jpg
|
||
├── clip.mp4
|
||
└── voice.mp3
|
||
```
|
||
|
||
相对引用一律 `./assets/{slug}/文件名`,构建时 Astro 图片服务自动出多尺寸 webp。
|
||
|
||
## 图片
|
||
|
||
标准 markdown 语法,alt 必填(无障碍 + 暗色降亮依据):
|
||
|
||
```markdown
|
||

|
||
<p class="fig-cap">图:外滩的傍晚</p> ← 显式图注类(居中小字)
|
||
```
|
||
|
||
- 图注可选;**必须用 `<p class="fig-cap">…</p>`**(2026-11-02 起)
|
||
- ⚠️ 旧写法 `*斜体独立段*` 已不再自动当图注:此前它会把正文里所有独占成段的斜体都变成块级居中(用户实测的排版 bug),现在斜体恢复为正常行内强调
|
||
- 压缩纪律:入仓前长边 ≤2400px(可跑 `python3 scripts/photos_ingest.py`,对 posts 资产同样有效的思路,图片请自行用同规格压缩)
|
||
|
||
## 视频 / 音频
|
||
|
||
markdown 原生不认视音频,写 HTML 标签;**raw HTML 的相对路径不走 Astro
|
||
资产管线**(只有 `![]()` 图片语法走),所以媒体文件放 **`public/media/{slug}/`**
|
||
(构建原样拷入 dist),引用用绝对路径 `/media/...`:
|
||
|
||
```
|
||
public/media/my-post/clip.mp4 ← 文件放这里
|
||
```
|
||
|
||
```html
|
||
<video controls preload="metadata" src="/media/my-post/clip.mp4"></video>
|
||
*片头三秒试拍*
|
||
|
||
<audio controls preload="metadata" src="/media/my-post/voice.mp3"></audio>
|
||
*朗读:面纱选段*
|
||
```
|
||
|
||
- `preload="metadata"` 必写(不预拖流量,只取时长/首帧)
|
||
- **图注警示**:斜体图注行内不要裸写 `<video>`/`<audio>` 字样——markdown 会把它渲染成真实元素(黑盒+吞文字),提及时用反引号 \`<video>\`
|
||
- 样式已全局处理(视频全宽圆角黑底、音频胶囊条)
|
||
- /media/ 命名空间同时覆盖"仓内小文件"与"仓外 store 大文件"(见体积纪律表)
|
||
|
||
## 体积纪律(git 仓红线)
|
||
|
||
| 媒体 | 入仓上限 | 超限怎么办 |
|
||
|---|---|---|
|
||
| 图片 | 单张 ≤1MB(2400px 压缩后通常 200-800KB) | 重压 |
|
||
| 音频 | ≤5MB(3 分钟 128kbps ≈ 2.8MB) | 降码率 |
|
||
| 视频 | ≤5MB | 放仓外 `~/docker_apps/blog/media/`(photos-originals 同模式),文章里用绝对路径 `/media/xxx.mp4`;build.sh 同步段待该场景首次出现时补 |
|
||
|
||
> 历史教训:21 张原图 196M 直接入仓曾撞死推送通道(git 历史 7a2880b 重写才甩掉)。
|
||
|
||
## 三媒齐用的样例
|
||
|
||
见 `content/posts/media-sample.md`(draft 文章,仅构建验证用,不上线)。
|
||
|
||
## 链接卡片(OG 预览卡)
|
||
|
||
**一行只放一个链接、且它独立成段**时,构建期会抓取该页的 OG 信息渲染成卡片(标题 / 描述 / 站点图标 / 可选缩略图):
|
||
|
||
```markdown
|
||
https://astro.build/
|
||
|
||
[也可以是 markdown 链接形态](https://astro.build/)
|
||
```
|
||
|
||
规则与边界:
|
||
|
||
- **内联链接不变**:夹在句子里的链接保持普通样式(`这是一句带 [链接](https://example.com) 的话` → 不会变卡),所以不会把整篇变成卡片墙
|
||
- **抓取失败一律回落普通链接**:404、无 OG、超时(4s)、站点拒爬都不影响构建
|
||
- **每篇最多 10 张卡**,超出的回落普通链接(防止文章变卡片流)
|
||
- **缓存**:结果存 `content/.link-cache.json`(入仓,保证构建确定性);条目 30 天后自动重抓。想强制刷新某条——删掉该 URL 的条目即可
|
||
- 缩略图取 `og:image`,无图不占位;图标取站点而非第三方 favicon 服务
|
||
|
||
## 文章排版类(受限白名单)
|
||
|
||
正文里可用 `<span class="…">` / `<p class="…">` 做**受限**排版。构建期有白名单过滤:**只放行下表类名,其它 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
|
||
<span class="ink-muted">次要说明</span>
|
||
<span class="font-kai">金句可以换文楷</span>
|
||
<p class="drop-cap">开篇段落,首字会下沉。</p>
|
||
<p class="quote-line">所谓暮色,就是光慢慢地承认自己要走。</p>
|
||
```
|
||
|
||
- **颜色只能走上面这些类**(内部用 tokens,亮暗双态都可读);写 `style="color:red"` 会被构建期剔除
|
||
- 斜体就用普通 markdown `*斜体*`——行内强调,不会换行也不会居中
|
||
|
||
## 发布提醒
|
||
|
||
- push main → webhook 自动构建发布(约 10 秒)
|
||
- 新文章要英文版:跑 `python3 scripts/translate/translate_posts.py`(幂等,代码块不会被翻译破坏的风险见其 README 注释;锁定润色用 frontmatter `locked: true`)
|
||
|
||
|
||
## 书封图(/books 书架)
|
||
|
||
阅读轨文章可选加书封:
|
||
|
||
1. 图片放 `content/posts/assets/books/<slug>.jpg`(建议宽 480px、高宽比约 2:3)
|
||
2. 对应文章 frontmatter 加一行:`cover: ./assets/books/<slug>.jpg`
|
||
3. 构建自动优化出 96/192 两档;**没有 cover 的文章优雅回退现有色脊卡设计**,无需改任何代码
|
||
|
||
示例 frontmatter:
|
||
```yaml
|
||
---
|
||
title: "面纱"
|
||
pubDate: 2024-06-01
|
||
category: reading
|
||
bookAuthor: "毛姆"
|
||
mood: "#8a6f4a"
|
||
cover: ./assets/books/mian-sha.jpg
|
||
---
|
||
```
|
||
|
||
## 后台上传(Sveltia 后台,推荐路径)
|
||
|
||
后台地址 `/admin/`(gitea 账号登录)。三类媒体都有后台入口,不必手写路径:
|
||
|
||
### 图片
|
||
|
||
文章编辑器里插入图片 → 自动上传到 `content/posts/assets/` 并写入 `./assets/...`
|
||
相对路径(走 Astro 图片管线,自动优化)。**不要手改成 `/assets/...` 绝对路径**。
|
||
|
||
### 视频(frontmatter 字段,正文后自动渲染)
|
||
|
||
1. 文章编辑页「附视频」字段上传(自动落 `public/media/`,路径写 `/media/<name>.mp4`)
|
||
2. 正文读完后自动出现 `<video controls preload="metadata" playsinline>`,全宽圆角黑底
|
||
3. 「视频封面」填 `/media/xxx.jpg` 可自定首帧;留空浏览器自取
|
||
4. 无视频字段 → 该区块不渲染(零占位)
|
||
|
||
体积纪律:mp4/webm ≤50MB 走 `public/media/`(构建原样拷贝,不转码);
|
||
更大的请按上文「体积纪律」放仓外 store(该场景 build.sh 同步段待首次出现时补)。
|
||
|
||
### 后台列表日期
|
||
|
||
文章/瞬间列表显示「日期 · 标题」,可按日期排序;图库照片没有 frontmatter 日期
|
||
(时间在 EXIF/文件侧),列表按「分组 · 标题」展示、按标题/分组排序。
|
||
|
||
### 链接
|
||
|
||
markdown 原生语法,无需任何配置:
|
||
|
||
```markdown
|
||
[文字](https://example.com)
|
||
```
|
||
|
||
想让链接显示成 OG 预览卡(带标题/描述/图标),把它**单独放一行**即可——规则见上文「链接卡片(OG 预览卡)」。
|
||
|
||
### 页面文案(主页/关于/开源)
|
||
|
||
后台「页面文案」下三个入口:
|
||
|
||
| 入口 | 文件 | 可改内容 |
|
||
|---|---|---|
|
||
| 主页设置 | `content/site/home.yaml` | 身份语 tagline、**头像 avatar**、**站点图标 favicon**、**社交链接 socials** |
|
||
| 关于页 | `content/site/about.yaml` | **markdown 正文 intro**(多段/列表/粗斜体/链接) |
|
||
| 开源页 | `content/site/opensource.yaml` | 叙述 blurb/blurb_after、仓链接、置顶项目 pinned |
|
||
|
||
#### 头像与站点图标
|
||
|
||
- **头像**:主页设置里「头像」上传 → 落 `content/site/assets/`,构建期优化成 60px WebP;
|
||
留空则显示内置渐变占位(w4 徽标)
|
||
- **站点图标 favicon**:先经「图标」媒体库上传(`public/icons/`),再把路径粘到
|
||
「站点图标」字段(如 `/icons/my.ico`、`/icons/my.svg`);留空回退 `/favicon.svg`。
|
||
支持 `.svg` / `.ico` / `.png`(直出,不经图片优化)
|
||
|
||
#### 社交链接
|
||
|
||
主页设置里「社交链接」是列表:可增删、拖动排序。每条 = 名称(无障碍文字)+ 链接 + 图标。
|
||
图标可选 `github / rss / email / bilibili / x`,**留空则显示纯文字按钮**。
|
||
首页名片区、以及任何引用该组件的页面都吃同一份数据(单一事实源,改一次全站生效)。
|
||
|
||
#### 首页文章列表
|
||
|
||
首页现在分两段:**阅读轨 6 条 + 技术轨 6 条**(各自带「全部文章」出口:阅读→书架、技术→归档)。
|
||
照片轨文章不进首页(在图库看)。完整列表见 `/archives/`。
|
||
|
||
- 中文/英文各一个字段,页面按语言取;**留空则该字段回落默认文案**(构建不会炸)
|
||
- `pinned` 填仓库短名(如 `blog`、`pi-board`),**顺序即展示顺序**;留空回落 `blog`
|