From 14e1f2b7177167c72ef39ac0529e20b67285c7e3 Mon Sep 17 00:00:00 2001 From: Eliauk Date: Sat, 25 Apr 2026 19:02:30 +0800 Subject: [PATCH] =?UTF-8?q?fix(mock+docs):=20=E5=8F=8B=E9=93=BE=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=E5=AF=B9=E9=BD=90=20Gridea=20Pro=20=E7=9C=9F=E5=AE=9E?= =?UTF-8?q?=E8=BF=90=E8=A1=8C=E6=97=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 问题 skill 的 mock 数据和 Gridea Pro 真实运行时的友链对象结构不一致,导致: - 主题作者用 `link.name` / `link.url` 写模板 → mock 测试 PASS,**但客户端真实运行时空白**(`bitcron-pro` 的反例:用真实字段 `link.siteName` 反而 mock 测试空白) - `template-variables.md` 没有 Link 对象字段表,作者只能猜 - 现有主题三种写法、两种是错的、一种是对的但 mock 渲染失败 —— 用户在客户端实测才能暴露 ## Gridea Pro 真实运行时(来源代码确认) - 数据来源:`backend/internal/engine/data_builder.go` 第 232-247 行 把 `domain.Link` 注入到 `customConfig.links`,字段重命名: - `Name` → `siteName` - `Url` → `siteLink` - `Description` / `Avatar` 保持原名 - 渲染暴露:`backend/internal/render/jinja2_renderer.go` 第 231-256 行 把 `customConfig.links` **同时**暴露为顶层 `links` 变量和 `theme_config.links` → 主题既可以写 `{% for l in links %}` 也可以写 `{% for l in theme_config.links %}` ## 修复 ### 1. `assets/mock-data.json` 友链字段从 `name` / `url` 改为 `siteName` / `siteLink`,对齐真实运行时。 删除 `id` 字段(真实运行时不暴露给模板)。 ### 2. `scripts/render_test.py` `build_context()` 复刻 `jinja2_renderer.buildContext` 行为: 把 mock 友链同时注入到顶层 `links` 和 `theme_config.links`(之前只注入了顶层)。 删除 links.html 分支的特殊处理(base ctx 已包含)。 ### 3. `references/template-variables.md` - 新增「Link 对象」章节,列出 4 个真实字段 + 完整遍历示例 + 易错速查 - 「各页面可用变量表」补充说明 `links` 全局可用 - 文末「易错变量速查」总表加入 `link.name → siteName` 等 4 行 ## 验证 - ✅ JSON 合法 - ✅ bitcron-pro 主题 links.html:之前 mock 渲染显示空白,修复后正确输出 「Gridea Pro / 基于 Wails 的桌面静态博客写作客户端」 - ✅ 修改全部为增量性质,不删除任何旧逻辑,不影响其他模板 ## 配套关联 - gridea-pro-themes#9:liushen 主题友链页同样字段问题的双兼容修复(已提) Co-Authored-By: Claude Opus 4.7 --- assets/mock-data.json | 10 +++--- references/template-variables.md | 61 ++++++++++++++++++++++++++++++-- scripts/render_test.py | 15 ++++++-- 3 files changed, 75 insertions(+), 11 deletions(-) diff --git a/assets/mock-data.json b/assets/mock-data.json index 3d9e323..2802ce1 100644 --- a/assets/mock-data.json +++ b/assets/mock-data.json @@ -226,16 +226,14 @@ "post": null, "links": [ { - "id": "link-1", - "name": "Gridea Pro", - "url": "https://github.com/Gridea-Pro/gridea-pro", + "siteName": "Gridea Pro", + "siteLink": "https://github.com/Gridea-Pro/gridea-pro", "description": "基于 Wails 的桌面静态博客写作客户端", "avatar": "https://avatars.githubusercontent.com/u/0" }, { - "id": "link-2", - "name": "某友链示例", - "url": "https://example.com", + "siteName": "某友链示例", + "siteLink": "https://example.com", "description": "一个简短描述", "avatar": "" } diff --git a/references/template-variables.md b/references/template-variables.md index 375921c..9d1c21f 100644 --- a/references/template-variables.md +++ b/references/template-variables.md @@ -10,8 +10,9 @@ 4. [Menu 对象](#menu-对象) 5. [Pagination 对象](#pagination-对象) 6. [Memo 对象](#memo-对象) -7. [各页面可用变量表](#各页面可用变量表) -8. [三引擎语法对照表](#三引擎语法对照表) +7. [Link 对象](#link-对象) +8. [各页面可用变量表](#各页面可用变量表) +9. [三引擎语法对照表](#三引擎语法对照表) --- @@ -121,10 +122,60 @@ --- +## Link 对象 + +友链条目对象。Gridea Pro 把全部友链**同时**暴露为两条路径,两者**指向同一份数据**: + +- `links` —— 顶层变量,所有页面都可用 +- `theme_config.links` —— 经由 `customConfig.links` 合并到 `theme_config` 上,所有页面都可用 + +> ⚠️ 字段名是从 domain.Link 注入时**重命名**过的(参见 `backend/internal/engine/data_builder.go`)。模板中**不能**写 `link.name` / `link.url`,必须用下表的字段名。 + +| 字段 | 类型 | 说明 | +|------|------|------| +| `link.siteName` | string | 站点名称(来源于 `domain.Link.Name`) | +| `link.siteLink` | string | 站点 URL(来源于 `domain.Link.Url`) | +| `link.description` | string | 站点描述,可能为空字符串 | +| `link.avatar` | string | 站点头像 URL,可能为空字符串 | + +### 友链遍历示例 + +```jinja2 +{# 推荐写法 1:直接遍历顶层 links #} +{% if links and links|length > 0 %} + {% for link in links %} + + {% if link.avatar %} + {{ link.siteName }} + {% endif %} +

{{ link.siteName }}

+ {% if link.description %}

{{ link.description }}

{% endif %} +
+ {% endfor %} +{% endif %} + +{# 写法 2:从 theme_config.links 取,效果完全等同 #} +{% for link in theme_config.links %}...{% endfor %} +``` + +### 易错变量速查(友链专用) + +| 错误写法 | 正确写法 | 原因 | +|----------|----------|------| +| `link.name` | `link.siteName` | 注入时被重命名 | +| `link.url` | `link.siteLink` | 注入时被重命名 | +| `link.title` | `link.siteName` | 没有 title 字段 | +| `link.desc` | `link.description` | 没有缩写 | +| `link.icon` | `link.avatar` | 没有 icon 字段 | + +--- + ## 各页面可用变量表 每个模板文件可访问的变量不同,在模板中使用变量前务必确认该页面是否有权访问: +> 注:`links` 变量(同时暴露为 `theme_config.links`)实际上**所有页面**都能访问 —— 这是 Gridea Pro 的设计,方便你在 sidebar / footer 等公共组件里展示友链。下表只在"主要消费方"那一行特别标出。 + | 页面模板 | 可用变量 | |----------|----------| | `index.html` | config, theme_config, menus, posts, tags, pagination, now | @@ -133,7 +184,7 @@ | `tag.html` | config, theme_config, menus, posts, tag, current_tag, tags, now | | `tags.html` | config, theme_config, menus, tags, now | | `about.html` | config, theme_config, menus, tags, now | -| `links.html` | config, theme_config, menus, tags, links, now | +| `links.html` | config, theme_config, menus, tags, **links**, now | | `memos.html` | config, theme_config, menus, memos, tags, now | | `blog.html` | config, theme_config, menus, posts, tags, pagination, now | | `404.html` | config, theme_config, menus, now | @@ -454,6 +505,10 @@ | `config.url` | `config.domain` | 域名字段为 `domain` | | `tag.slug` | `tag.link` | 标签链接字段为 `link` | | `tag.posts_count` | `tag.count` | 标签文章数字段为 `count` | +| `link.name` | `link.siteName` | 友链注入时被重命名(来源 `domain.Link.Name`) | +| `link.url` | `link.siteLink` | 友链注入时被重命名(来源 `domain.Link.Url`) | +| `link.desc` | `link.description` | 友链描述字段为 `description`,无缩写 | +| `link.icon` | `link.avatar` | 友链头像字段为 `avatar`,没有 `icon` | | `theme_config` 写成 `themeConfig` | `theme_config` | 模板中使用下划线命名 | | `pagination.previous` | `pagination.prev` | 上一页字段简写为 `prev` | | `{{ value\|default("x") }}` | `{{ value\|default:"x" }}` | Pongo2 过滤器参数用冒号 | diff --git a/scripts/render_test.py b/scripts/render_test.py index 8471aba..67dc248 100644 --- a/scripts/render_test.py +++ b/scripts/render_test.py @@ -172,13 +172,23 @@ def build_context(mock_data, template_name): Different templates need different context variables. """ # Base context available to all templates + # 复刻 Gridea Pro jinja2_renderer.buildContext 的友链注入: + # 真实运行时把 customConfig.links 同时暴露为顶层 `links` 变量和 `theme_config.links`, + # 主题既可以写 {% for l in links %} 也可以写 {% for l in theme_config.links %}。 + # 字段为 siteName / siteLink / description / avatar(见 backend/internal/engine/data_builder.go)。 + links_data = mock_data.get("links", mock_data.get("friends", [])) + theme_config = dict(mock_data.get("theme_config", {})) # 浅拷贝,避免污染 mock_data + if "links" not in theme_config: + theme_config["links"] = links_data + ctx = { "config": mock_data.get("config", {}), - "theme_config": mock_data.get("theme_config", {}), + "theme_config": theme_config, "menus": mock_data.get("menus", []), "tags": mock_data.get("tags", []), "posts": mock_data.get("posts", []), "memos": mock_data.get("memos", []), + "links": links_data, "pagination": mock_data.get("pagination", {"prev": "", "next": ""}), "now": mock_data.get("now", "2026-02-28T15:30:00+08:00"), } @@ -223,7 +233,8 @@ def build_context(mock_data, template_name): pass # config is enough elif basename == "links": - ctx["links"] = mock_data.get("links", mock_data.get("friends", [])) + # links / theme_config.links 已在 base ctx 中注入,无需特殊处理 + pass elif basename == "memos": pass # memos already in ctx