Files
blog/docs/写作指南.md
T
wait4xandGLM-5.3 264148f0a6 feat(cms): 内容管理闭环——站点单例三件套+文章视频字段
- A 站点文案单例:content/site/{home,about,opensource}.yaml +
  src/lib/site-content.ts 访问层(字段级回落默认,默认值=原文案逐字
  一致,零回归);接线主页 tagline(en 首页新增该块)、关于页 intro、
  开源页叙述三段 + PINNED 参数化(projects.ts: loadProjects(pinned)
  /orderPinned,PINNED_DEFAULT 仅缺省兜底)
- loader 语义修正(返工如实入档):Astro 7 的 file() 把 YAML 顶层对象
  按 Object.entries 拆成多条目(id=key、data=value),单例字段必炸
  schema;改 glob 精确到单文件 = 真单例语义,data 即文件根对象,
  Sveltia 可编辑根字段零嵌套(比包 main: 层的方案后台体验更好)
- B 文章媒体:posts +video/+poster;Sveltia file 字段
  (media_folder=public/media、public_folder=/media、hint ≤50MB);
  正文后渲染 video controls preload=metadata playsinline,无则不渲染;
  图片链路核对本已一致(./assets 走 Astro 管线);链接 markdown 原生
- docs/写作指南.md 补「后台上传」节(三类媒体 + 三单例操作 + 体积纪律)

Sveltia 集合:posts / moments / photos / site_pages(三单例)

车道:op-gitea(实测 tagline 改 yaml 产物联动、置空回落默认、pinned
改名排序变、视频临时件渲染全对、构建绿 + lint 45 页 0/0/0);收编
复验:构建 44 页 + E2E 37/37 + 三 yaml 扁平键就位

验证: pnpm astro build 44 页 exit 0;E2E 37/37 PASS;content-lint
      0/0/0;播放器/单例/置顶 playwright 实测全过

Spec: none (内容管理闭环,车道级交付)

Co-authored-by: GLM-5.3
2026-11-02 12:42:10 +08:00

153 lines
6.2 KiB
Markdown
Raw 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.
# 写作指南:在文章里插入图片 / 视频 / 音频
> 适用:`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
![外滩夜景](./assets/my-post/bund.jpg)
*图:外滩的傍晚* ← 紧跟媒体的斜体独立段 = 图注(居中小字)
```
- 图注可选;写法固定为 `*…*` 单独成段,紧跟图片
- 压缩纪律:入仓前长边 ≤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 服务
## 发布提醒
- 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 同步段待首次出现时补)。
### 链接
markdown 原生语法,无需任何配置:
```markdown
[文字](https://example.com)
```
想让链接显示成 OG 预览卡(带标题/描述/图标),把它**单独放一行**即可——规则见上文「链接卡片(OG 预览卡)」。
### 页面文案(主页/关于/开源)
后台「页面文案」下三个入口:
| 入口 | 文件 | 可改内容 |
|---|---|---|
| 主页设置 | `content/site/home.yaml` | 身份语 tagline(zh/en) |
| 关于页 | `content/site/about.yaml` | 身份陈述段 intro(zh/en) |
| 开源页 | `content/site/opensource.yaml` | 叙述 blurb/blurb_after、仓链接、置顶项目 pinned |
- 中文/英文各一个字段,页面按语言取;**留空则该字段回落默认文案**(构建不会炸)
- `pinned` 填仓库短名(如 `blog`、`pi-board`),**顺序即展示顺序**;留空回落 `blog`