From cb4481f110730cfb110e52df74224f706300b9f2 Mon Sep 17 00:00:00 2001 From: Eliauk Date: Thu, 20 Aug 2026 12:40:46 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20customConfig=20type=20=E7=99=BD=E5=90=8D?= =?UTF-8?q?=E5=8D=95=E5=AF=B9=E9=BD=90=E7=9C=9F=E5=AE=9E=20GUI=EF=BC=8C?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20CI=20=E9=95=BF=E6=9C=9F=E7=BA=A2=E7=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit scaffold_theme.py 生成的 config.json 用了 type: "color",而 GUI 的取色器实际是 type: "input" + card: "color",validate_syntax.py 因此判其非法 —— 脚手架产物通不过 自己的校验器,CI 三个引擎自 2026-07-09 起全红。 - scaffold_theme.py / 三个 starter:primaryColor 改用 input + card: color - validate_syntax.py:白名单补齐 radio/switch/markdown/array/picture/image (逐条对照 CustomSetting.vue 的 v-if 分支核对) - validate_syntax.py:新增 card 合法值、array 缺 arrayItems、select/radio 缺 options 三项校验 - theme-config-schema.md:类型表重写并补充 arrayItems 示例 - CHANGELOG.md:记录本次修正 --- CHANGELOG.md | 7 ++++ assets/starters/ejs/config.json | 1 + assets/starters/go-templates/config.json | 1 + assets/starters/jinja2/config.json | 1 + references/theme-config-schema.md | 43 +++++++++++++++++++----- scripts/scaffold_theme.py | 5 +-- scripts/validate_syntax.py | 37 ++++++++++++++++---- 7 files changed, 78 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33daab0..b99144b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ ### Fixed / Changed +**2026-08-20 · customConfig type 白名单对齐真实 GUI(修复 CI 长期红灯)** + +- **`type: "color"` 不存在,取色器是 `type: "input"` + `"card": "color"`**:`scaffold_theme.py` 生成的 `config.json` 用了 `type: "color"`,被 `validate_syntax.py` 判为非法 —— 脚手架的产物通不过自己的校验器,CI 三个引擎自 2026-07-09 起全红。现改为 `input` + `card: "color"`,用户拿到的是真正的取色器而不是纯文本框;三个 starter 的 `primaryColor` 同步跟进。 +- **校验白名单漏了 6 种 GUI 实际支持的类型**:逐条对照 `CustomSetting.vue` 的 v-if 分支核对后,`radio`、`switch`、`markdown`、`array`、`picture`、`image` 均可正常渲染,此前被误判为非法并给出错误的替代建议。白名单与 `theme-config-schema.md` 的类型表一并重写。 +- **新增三条 config.json 校验**:`card` 只允许 `color` / `post`;`type: array` 缺 `arrayItems` 报错(否则面板渲染空卡片);`select` / `radio` 缺 `options` 报错(否则控件不渲染)。 +- **文档补充数组配置写法**:`theme-config-schema.md` 增加 `arrayItems` 的完整示例。 + **2026-07-08 · 基于 chatgpt 主题实战(真实引擎渲染)的修正与增强** - **修正 `post.date` 类型描述(推翻上一轮"修正")**:Jinja2 / EJS 渲染上下文经 `json.Marshal` 构建,`post.date` / `updatedAt` / `createdAt` 是 **RFC3339 字符串**;仅 Go Templates 直接传 struct 才是 `time.Time`。Jinja2 中对这些字段用 `|date:` 会报错并使整页降级(实战中曾导致首页 + 全部文章页降级)。`template-variables.md` / `jinja2-guide.md`(差异 8 重写)/ `SKILL.md` 规则 6 三处对齐,并给出正确用法:`dateFormat` / `|relative` / 直接输出 / `|slice`。 diff --git a/assets/starters/ejs/config.json b/assets/starters/ejs/config.json index 8bbae47..79879b7 100644 --- a/assets/starters/ejs/config.json +++ b/assets/starters/ejs/config.json @@ -11,6 +11,7 @@ "group": "外观设置", "value": "#2563eb", "type": "input", + "card": "color", "note": "主题主色调,用于链接和强调色" }, { diff --git a/assets/starters/go-templates/config.json b/assets/starters/go-templates/config.json index 8297abd..55b1a61 100644 --- a/assets/starters/go-templates/config.json +++ b/assets/starters/go-templates/config.json @@ -11,6 +11,7 @@ "group": "外观设置", "value": "#2563eb", "type": "input", + "card": "color", "note": "主题主色调,用于链接和强调色" }, { diff --git a/assets/starters/jinja2/config.json b/assets/starters/jinja2/config.json index 576ba22..70b7676 100644 --- a/assets/starters/jinja2/config.json +++ b/assets/starters/jinja2/config.json @@ -11,6 +11,7 @@ "group": "外观设置", "value": "#2563eb", "type": "input", + "card": "color", "note": "主题主色调,用于链接和强调色" }, { diff --git a/references/theme-config-schema.md b/references/theme-config-schema.md index f5ef8be..a407223 100644 --- a/references/theme-config-schema.md +++ b/references/theme-config-schema.md @@ -80,28 +80,53 @@ config.json 是主题与 Gridea Pro 之间的契约: ## 支持的配置类型 -> ⚠️ **GUI 设置面板只渲染以下 5 种类型**(经真实 GUI 验证)。写其他类型不会报错,但设置面板中只显示标题、**控件空白,用户无法配置**。 +> ⚠️ **只有下表中的 type 会被设置面板渲染**(逐条对照 `CustomSetting.vue` 的 v-if 分支核对)。写其他类型不会报错,但设置面板中只显示标题、**控件空白,用户无法配置**。 | type 值 | 渲染的 GUI 控件 | 值类型 | 额外字段 | 说明 | |---------|-----------------|--------|----------|------| -| `input` | 单行文本输入框 | string | — | 短文本:标题、链接、颜色 HEX 值等 | +| `input` | 单行文本输入框 | string | `card` 可选 | 短文本:标题、链接等 | +| `input` + `"card": "color"` | **取色器** | string | — | 颜色首选写法,值为 HEX(如 `#2563eb`) | +| `input` + `"card": "post"` | 文章选择器 | string | — | 值为文章标识 | | `textarea` | 多行文本域 | string | — | 长文本:自定义 CSS / JS、多行文案、注入代码 | -| `select` | 下拉选择框 | string | `options` 必需 | `options` 为 `[{"label":"显示名","value":"实际值"}]` 数组 | -| `toggle` | 开关(Switch) | boolean | — | 值为 `true` 或 `false` | -| `picture-upload` | 图片上传控件 | string | — | 值为图片路径字符串 | +| `select` | 下拉选择框 | string | `options` **必需** | `options` 为 `[{"label":"显示名","value":"实际值"}]` 数组 | +| `radio` | 单选按钮组 | string | `options` **必需** | 选项少(2–4 个)时比 `select` 直观 | +| `toggle` / `switch` | 开关 | boolean | — | 两者等价,值为 `true` 或 `false` | +| `markdown` | Markdown 编辑器 | string | — | 带编辑器的长文本 | +| `picture-upload` / `picture` / `image` | 图片上传控件 | string | — | 三者等价,值为图片路径字符串 | +| `array` | 可增删的卡片列表 | array | `arrayItems` **必需** | 见下方「数组配置」 | + +`card` 只在 `type: input` 上生效,合法值仅 `color` 和 `post`。 ### ❌ 无效类型与替代方案 -以下类型**GUI 不支持**(历史文档曾错误地列为可用),请按对照表替换: +以下类型**GUI 不支持**,请按对照表替换: | ❌ 无效 type | ✅ 替代方案 | |-------------|------------| | `boolean` | `toggle`(语义相同) | -| `image` | `picture-upload` | -| `color` | `input`(note 里注明 HEX 格式,如 `#10a37f`) | +| `color` | `input` 加 `"card": "color"`,渲染真正的取色器 | | `code` | `textarea` | | `number` | `select`(枚举常用值)或 `input`;**模板中比较前必须 `\|default:N\|to_int`**(GUI 保存的值是字符串) | -| `array` | 拆成多个 `input`(如 featured1Name / featured2Name…),或 `textarea` 每行一条 | + +### 数组配置 + +`type: "array"` 渲染成一组可增删的卡片,每张卡片的字段由 `arrayItems` 定义(字段本身支持 `input` / `select` / `switch` / `picture-upload` 等): + +```json +{ + "name": "featured", + "label": "推荐位", + "group": "首页", + "type": "array", + "value": [], + "arrayItems": [ + { "name": "title", "label": "标题", "type": "input" }, + { "name": "cover", "label": "封面", "type": "picture-upload" } + ] +} +``` + +漏写 `arrayItems` 会渲染出空白卡片,`validate_syntax.py` 会报错拦截。 ### 数字配置的完整模式 diff --git a/scripts/scaffold_theme.py b/scripts/scaffold_theme.py index 99dff1d..2747e13 100644 --- a/scripts/scaffold_theme.py +++ b/scripts/scaffold_theme.py @@ -1428,9 +1428,10 @@ def build_config(theme_name, engine, author): "name": "primaryColor", "label": "主色调", "group": "样式", - "type": "color", + "type": "input", + "card": "color", "value": "#2563eb", - "note": "主题的主要颜色" + "note": "主题的主要颜色(HEX,如 #2563eb)" }, { "name": "showFeatureImage", diff --git a/scripts/validate_syntax.py b/scripts/validate_syntax.py index a8b379a..d2e1dd5 100644 --- a/scripts/validate_syntax.py +++ b/scripts/validate_syntax.py @@ -493,17 +493,42 @@ def _validate_cross_engine(theme_dir, engine): f"config.json 中 engine 值无效: '{config['engine']}',应为 jinja2/go/ejs" )) - # --- customConfig type 白名单(GUI 只渲染这 5 种,其他类型控件空白无法配置) --- - VALID_CONFIG_TYPES = {"input", "textarea", "select", "toggle", "picture-upload"} + # --- customConfig type 白名单 --- + # 与设置面板 CustomSetting.vue 的 v-if 分支一一对应;不在表内的 type 只渲染标题、控件空白。 + # config.json 经 domain.Theme.CustomConfig([]interface{})原样透传到前端, + # 故 card / options / arrayItems 等附加字段不会在后端被裁掉。 + VALID_CONFIG_TYPES = { + "input", "textarea", "select", "radio", "switch", "toggle", + "markdown", "array", "picture-upload", "picture", "image", + } TYPE_SUGGESTIONS = { - "boolean": "toggle", "image": "picture-upload", "color": "input(note 注明 HEX)", - "code": "textarea", "number": "select 或 input(模板中 |default:N|to_int)", - "array": "多个 input 或 textarea 每行一条", "switch": "toggle", "radio": "select", + "boolean": "toggle", + "color": 'input 并加 "card": "color"(渲染取色器)', + "code": "textarea", + "number": "select 或 input(模板中 |default:N|to_int)", } + VALID_CARDS = {"color", "post"} for item in config.get("customConfig", []) or []: item_type = item.get("type", "") + card = item.get("card", "") + if card and card not in VALID_CARDS: + issues.append(Issue( + ERROR, "config.json", None, + f"customConfig '{item.get('name', '?')}' 的 card '{card}' GUI 不支持," + f"仅支持 {'/'.join(sorted(VALID_CARDS))}(且只对 type: input 生效)" + )) + if item_type == "array" and not item.get("arrayItems"): + issues.append(Issue( + ERROR, "config.json", None, + f"customConfig '{item.get('name', '?')}' 的 type 'array' 缺少 arrayItems,面板将渲染空卡片" + )) + if item_type in ("select", "radio") and not item.get("options"): + issues.append(Issue( + ERROR, "config.json", None, + f"customConfig '{item.get('name', '?')}' 的 type '{item_type}' 缺少 options,控件不会渲染" + )) if item_type and item_type not in VALID_CONFIG_TYPES: - hint = TYPE_SUGGESTIONS.get(item_type, "input/textarea/select/toggle/picture-upload 之一") + hint = TYPE_SUGGESTIONS.get(item_type, "/".join(sorted(VALID_CONFIG_TYPES)) + " 之一") issues.append(Issue( ERROR, "config.json", None, f"customConfig '{item.get('name', '?')}' 的 type '{item_type}' GUI 不支持(面板控件空白),请改用 {hint}"