diff --git a/content/posts/content/posts/assets/海风中失落的血色馈赠.png b/content/posts/content/posts/assets/海风中失落的血色馈赠.png deleted file mode 100644 index 466bc86..0000000 Binary files a/content/posts/content/posts/assets/海风中失落的血色馈赠.png and /dev/null differ diff --git a/content/site/about.yaml b/content/site/about.yaml index cd4363f..c613cf8 100644 --- a/content/site/about.yaml +++ b/content/site/about.yaml @@ -1,5 +1,28 @@ -# 关于页可变段落(后台「页面文案 → 关于页」可改) -# intro = 身份陈述段;留空则回落默认文案,构建不炸 +# 关于页正文(后台「页面文案 → 关于页」可改,markdown 编辑) +# intro = 多段 markdown 正文,渲染在「关于」标题下方、名片之前;留空回落默认文案 +# 支持的语法:段落、列表、粗斜体、行内代码、链接(渲染走 Astro 的 markdown 管线) intro: - zh: 写作者 · 读者 · 快门按压者,拥抱开源。这里是我三条轨道的公开工作台:读过的书留下评注,写过的代码进项目墙,按下的快门收入图库。 - en: Writer · Reader · Shutter-presser, embracing open source. This is the public workbench of my three tracks — books that left notes, code that reaches the project wall, and shutters I pressed. + zh: | + 写作者 · 读者 · 快门按压者,拥抱开源。 + + 这里是我三条轨道的公开工作台:**读过的书**留下评注,**写过的代码**进项目墙,**按下的快门**收入图库。 + + 所有内容都有一个明确去处: + + - 长文沉淀在文章与书架 + - 短句与随手拍落在瞬间 + - 影像收进图库,配一点 EXIF 与一句策展 + + 本站本身即开源,构建与发布全链都在仓里。 + en: | + Writer · Reader · Shutter-presser, embracing open source. + + This is the public workbench of my three tracks: **books** that left notes, **code** that reaches the project wall, and **shutters** I pressed that land in the gallery. + + Everything has a clear destination: + + - Long-form writing goes to posts and the bookshelf + - Short lines and snapshots land in moments + - Images go to the gallery, with a bit of EXIF and one line of curation + + This site is itself open source — the full build and deploy pipeline lives in the repo. diff --git a/content/site/home.yaml b/content/site/home.yaml index 8906933..0473380 100644 --- a/content/site/home.yaml +++ b/content/site/home.yaml @@ -3,3 +3,24 @@ tagline: zh: 写作者 · 读者 · 快门按压者,拥抱开源。 en: Writer · Reader · Shutter-presser — embracing open source. + +# 头像(可选):上传后自动写入 ./assets/avatar-xxx.jpg(走 Astro 图片管线) +# 留空 = 回退内置渐变占位 +# avatar: ./assets/avatar.jpg + +# 站点图标(favicon,全局生效):public 下的路径;留空回退 /favicon.svg +# 上传走 public/icons/(后台媒体库),可填 /icons/my.ico、/icons/my.svg +favicon: /favicon.svg + +# 社交链接(顺序即展示顺序,后台可增删拖动排序) +# icon 取值:github / rss / email / bilibili / x / 留空(留空按 label 显示纯文字) +socials: + - label: GitHub + url: https://github.com/wait4xx + icon: github + - label: RSS + url: /rss.xml + icon: rss + - label: 邮件 + url: mailto:admin@wait4x.online + icon: email diff --git a/docs/写作指南.md b/docs/写作指南.md index 8a1ec28..e3c9120 100644 --- a/docs/写作指南.md +++ b/docs/写作指南.md @@ -128,6 +128,11 @@ cover: ./assets/books/mian-sha.jpg 体积纪律:mp4/webm ≤50MB 走 `public/media/`(构建原样拷贝,不转码); 更大的请按上文「体积纪律」放仓外 store(该场景 build.sh 同步段待首次出现时补)。 +### 后台列表日期 + +文章/瞬间列表显示「日期 · 标题」,可按日期排序;图库照片没有 frontmatter 日期 +(时间在 EXIF/文件侧),列表按「分组 · 标题」展示、按标题/分组排序。 + ### 链接 markdown 原生语法,无需任何配置: @@ -144,9 +149,28 @@ markdown 原生语法,无需任何配置: | 入口 | 文件 | 可改内容 | |---|---|---| -| 主页设置 | `content/site/home.yaml` | 身份语 tagline(zh/en) | -| 关于页 | `content/site/about.yaml` | 身份陈述段 intro(zh/en) | +| 主页设置 | `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` diff --git a/public/admin/config.yml b/public/admin/config.yml index 5e093d8..24ea94d 100644 --- a/public/admin/config.yml +++ b/public/admin/config.yml @@ -26,7 +26,7 @@ collections: create: true # 中文主稿;英文为 TMT 预翻译入仓的 .en.md(posts-en 集合不在后台,防误改译文) slug: '{{slug}}' - summary: '{{title}} · {{category}}' + summary: '{{pubDate}} · {{title}}' sortable_fields: [pubDate, title] # 注意:Sveltia 对 folder 集合的 media_folder 按"相对集合目录"解析 # (写成 content/posts/assets 会二次嵌套成 content/posts/content/posts/assets, @@ -66,6 +66,7 @@ collections: create: true slug: '{{year}}-{{month}}-{{day}}-{{slug}}' summary: '{{fields.pubDate}} · {{fields.text}}' + sortable_fields: [pubDate, text] fields: - { label: 时间, name: pubDate, widget: datetime, date_format: 'YYYY-MM-DD', time_format: false, format: 'YYYY-MM-DD' } - { label: 文字, name: text, widget: text, required: false, hint: '一句话(与图片至少填一项)' } @@ -84,6 +85,8 @@ collections: media_folder: '' public_folder: '' summary: '{{fields.group}} · {{fields.title}}' + # 照片无 frontmatter 日期(JSON sidecar,时间在 exif/文件侧)——列表按标题/分组排序 + sortable_fields: [title, group] fields: # title 必填:Astro schema z.string() 无 .optional(),omit 纪律下必需且有值才会落键(空 title 会让 main 不可构建) - { label: 标题, name: title, widget: string, required: true } @@ -110,6 +113,9 @@ collections: - name: home label: 主页设置 file: content/site/home.yaml + # 头像上传落 content/site/assets/,yaml 写 ./assets/xxx(Astro 图片管线优化) + media_folder: content/site/assets + public_folder: ./assets fields: - label: 身份语 name: tagline @@ -117,17 +123,41 @@ collections: fields: - { label: 中文, name: zh, widget: string, required: false } - { label: 英文, name: en, widget: string, required: false } + - label: 头像(可选) + name: avatar + widget: image + required: false + hint: '留空 = 内置渐变占位;上传后自动优化为 60px WebP' + - label: 站点图标 favicon + name: favicon + widget: string + required: false + hint: 'public 下的路径,如 /icons/my.ico 或 /icons/my.svg;留空回退 /favicon.svg。经「图标」媒体库上传' + - label: 社交链接(顺序即展示顺序) + name: socials + widget: list + required: false + summary: '{{fields.label}} → {{fields.url}}' + fields: + - { label: 名称(无障碍文字), name: label, widget: string } + - { label: 链接, name: url, widget: string } + - label: 图标 + name: icon + widget: select + required: false + options: [github, rss, email, bilibili, x] + hint: '不在列表里的留空 → 显示纯文字按钮' - name: about label: 关于页 file: content/site/about.yaml fields: - - label: 身份陈述段 + - label: 正文(markdown,多段) name: intro widget: object - hint: 渲染在「关于」标题下方、名片之上;留空用默认文案 + hint: 渲染在「关于」标题下方、名片之上;支持段落/列表/粗斜体/链接;留空用默认文案 fields: - - { label: 中文, name: zh, widget: text, required: false } - - { label: 英文, name: en, widget: text, required: false } + - { label: 中文, name: zh, widget: markdown, required: false } + - { label: 英文, name: en, widget: markdown, required: false } - name: opensource label: 开源页 file: content/site/opensource.yaml diff --git a/scripts/build-perf/content-lint.mjs b/scripts/build-perf/content-lint.mjs index 3374766..4d7aae2 100644 --- a/scripts/build-perf/content-lint.mjs +++ b/scripts/build-perf/content-lint.mjs @@ -25,10 +25,19 @@ function walk(dir, out = []) { return out; } +/** URL 路径 → 磁盘路径:先解百分号编码(中文/空格文件名在 HTML 里是 %XX 形式) */ +function decodePath(p) { + try { + return decodeURI(p); + } catch { + return p; + } +} + /** /x/ → dist/x/index.html;/a.svg → dist/a.svg;找不到即 null */ function targetExists(url) { if (SKIP_PREFIX.some((s) => url.startsWith(s))) return true; - const p = url.split('#')[0].split('?')[0]; + const p = decodePath(url.split('#')[0].split('?')[0]); if (!p || p === '/') return existsSync(join(DIST, 'index.html')); if (p.endsWith('/')) return existsSync(join(DIST, p, 'index.html')); return existsSync(join(DIST, p)) || existsSync(join(DIST, `${p}.html`)) || existsSync(join(DIST, p, 'index.html')); diff --git a/src/components/Avatar.astro b/src/components/Avatar.astro index 90b472b..e1f269c 100644 --- a/src/components/Avatar.astro +++ b/src/components/Avatar.astro @@ -1,42 +1,60 @@ --- -// 名片头像占位:内联 SVG 渐变 60px;真实头像接入后整体替换本组件即可 +// 名片头像:后台「主页设置 → 头像」上传后在 content/site/assets/ 落图, +// 经 Astro 图片管线出 60px WebP;未配置则回退内置渐变占位(历史视觉不变) +import { Image } from 'astro:assets'; +import { homeAvatar } from '../lib/site-content'; + +const src = await homeAvatar(); --- - - - - - - - - - - - +) : ( + - w4 - - + + + + + + + + + + + w4 + + +)} diff --git a/src/components/PostList.astro b/src/components/PostList.astro index 0bab26f..e44267e 100644 --- a/src/components/PostList.astro +++ b/src/components/PostList.astro @@ -18,10 +18,12 @@ interface Props { limit?: number; /** 截断后的「全部文章 →」出口链接;缺省不渲染 */ moreHref?: string; + /** 分轨模式:阅读轨与技朮轨各取 N 条(首页两段式,各段自带「更多」出口) */ + perTrack?: number; /** true = 归档折叠形态:按年 details(最新年开)+年内按月分组 */ fold?: boolean; } -const { posts, locale = 'zh', titleOverrides, limit, moreHref, fold = false } = Astro.props; +const { posts, locale = 'zh', titleOverrides, limit, moreHref, perTrack, fold = false } = Astro.props; const visible = typeof limit === 'number' ? posts.slice(0, limit) : posts; // 轨道标签走字典(en 首页复用本组件时的双语位);zh 保持原词表语义 const TRACK_LABEL: Record = { @@ -58,7 +60,49 @@ const byMonth = (list: CollectionEntry<'posts'>[]) => { }; --- -{fold ? ( +{perTrack ? ( +
+ {( + [ + { key: 'reading' as PostCategory, more: locale === 'en' ? '/en/books/' : '/books/' }, + { key: 'tech' as PostCategory, more: locale === 'en' ? '/en/archives/' : '/archives/' }, + ] as const + ).map(({ key, more }) => { + const rows = posts.filter((post) => post.data.category === key).slice(0, perTrack); + if (rows.length === 0) return null; + return ( +
+

+ {TRACK_LABEL[key]} + {t('home_more_posts', locale)} +

+