2026-10-22 16:49:25 +08:00
# trade-pro 第 5 主题移植实施计划
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 把 cms Astro 主题移植为 SiteForge V8_10 第 5 套内置主题 trade-pro(英文外贸 B2B),平台适配 + 骨架跑通 + 完整模板 + 验收。
**Architecture:** 三步走——先做平台适配(主题发现单一源、宏覆盖机制、主题资产优先、6 处前端入口),再以骨架主题包在真实 hesupipe 站点(`483295c29ea0` ,193 条内容)跑通构建,最后按 cms 设计源(`D:\project\cms\runtimes\astro-site-runtime` )逐模板移植视觉。设计依据:[spec R3 ](../specs/2026-10-22-trade-pro-theme-design.md )。
**Tech Stack:** Python 3.12 / FastAPI / Jinja2( FileSystemLoader 多目录)/ pytest / 原生 JS。
**环境事实(执行者必读):**
- 工作目录:`C:\Users\30997\Downloads\SiteForge_Factory_V5_Web_Docker\SiteForge_Enterprise_V8_10` (下称 `$ROOT` , bash 里用正斜杠)
- Docker 实例已运行:`http://127.0.0.1:8787` (容器 siteforge-v8-10-siteforge-1/-worker-1),admin 密码在 `.env`
- 测试命令:`python -m pytest -q` (当前 210 项全绿,任何改动后必须保持);JS 检查 `node --check app/static/<file>.js`
2026-10-22 17:28:57 +08:00
- **宿主机快速循环**(V8_12 起):`PYTHONUTF8=1 .venv/Scripts/python -m pytest -q` ,基线 **227 passed + 2 known-fail** ( test_p0_backup.py::test_sqlite_backup_and_restore_with_media、test_p0_p4_completion_v89.py::test_restore_replaces_directory_atomically_and_preserves_previous——均为 Windows sqlite 文件锁环境问题,改动前即失败);任何新失败都是回归。权威全量验证用容器:`MSYS_NO_PATHCONV=1 docker run --rm -v "$(pwd -W):/project" -w /project python:3.12-slim sh -c "python -m pip install -q -r requirements-dev.lock && python -m pytest -q"` (容器基线 226 passed + 1 node 环境失败)
2026-10-22 16:49:25 +08:00
- cms 设计源:`D:/project/cms/runtimes/astro-site-runtime/src` ( tokens.css 250 行、base.css 445 行、components/*.astro、pages/[locale]/*.astro);像素基准 `dist/hesupipe/en/`
- 改动 Python 后需 `docker compose up -d --build` 重建容器再验证 HTTP 层(构建约 1 分钟,有缓存)
- 契约清单已固化于 spec §2(F1-F13),本计划直接引用
---
## Phase 1+2:平台适配与骨架跑通(Task 1-8)
2026-10-22 17:02:45 +08:00
### Task 1: Git 基线 —— ✅ 已完成(2026-10-22,幂等跳过)
2026-10-22 16:49:25 +08:00
2026-10-22 17:02:45 +08:00
已执行并有扩展:仓库初始化、基线提交、远程 `http://219.157.255.213:25311/xinyouhe/cms-SiteForge.git` 配置并推送(main + feat/trade-pro-theme),远程原有 V5 基线历史经 `-s ours` 无损合并,`docs/COMPLETION_PLAN.md` 带入当前树;`.env` /密码确认未入库。随后追加 **V8_12 原地升级** (26 文件覆盖 + bug 修复重放 + compose 项目名钉回 `siteforge-v8-10` 保护数据卷)。执行者无需重跑本任务。
2026-10-22 16:49:25 +08:00
### Task 2: 主题发现单一源(F1)
**Files:**
- Modify: `app/core.py:12`
- Modify: `app/theme_pro.py:16-18`
- Modify: `app/theme_store.py:14`
- Test: `tests/test_theme_registry.py` (新建)
- [ ] **Step 1: 写失败测试**
```python
"""Theme discovery must be a single source of truth."""
2026-10-22 17:02:45 +08:00
import json
from pathlib import Path
2026-10-22 16:49:25 +08:00
from app import core , theme_pro , theme_store
2026-10-22 17:02:45 +08:00
ROOT = Path ( core . __file__ ) . resolve () . parent . parent
2026-10-22 16:49:25 +08:00
def test_discover_returns_builtin_themes_with_manifests ():
ids = core . discover_themes ()
for tid in ( 'automotive' , 'industrial' , 'corporate' , 'minimal' ):
assert tid in ids
for tid in ids :
2026-10-22 17:02:45 +08:00
manifest = json . loads (( ROOT / 'themes' / tid / 'manifest.json' ) . read_text ( encoding = 'utf-8' ))
2026-10-22 16:49:25 +08:00
assert manifest . get ( 'id' ) == tid
def test_three_whitelists_agree ():
assert set ( core . THEMES ) == set ( theme_pro . THEMES ) == set ( theme_store . BUILTIN )
assert isinstance ( core . THEMES , tuple )
```
- [ ] **Step 2:** `python -m pytest tests/test_theme_registry.py -q` → 预期 FAIL( `discover_themes` 不存在)
- [ ] **Step 3: 实现** ——`app/core.py:12` 替换为:
```python
ROOT_PATH = __import__ ( 'pathlib' ) . Path ( __file__ ) . resolve () . parent . parent
def discover_themes ():
"""Single source of truth: scan themes/*/manifest.json (id must match folder)."""
import json
themes_dir = ROOT_PATH / 'themes'
found = []
for folder in sorted ( themes_dir . iterdir ()):
if not folder . is_dir () or folder . name . startswith ( '_' ):
continue
manifest = folder / 'manifest.json'
if not manifest . is_file ():
continue
try :
if json . loads ( manifest . read_text ( encoding = 'utf-8' )) . get ( 'id' ) == folder . name :
found . append ( folder . name )
except ( ValueError , UnicodeError ):
continue
return tuple ( found )
THEMES = discover_themes ()
```
( core.py 顶部已有 `import json` ,可复用;实现时去掉重复 import,风格与文件一致。)
`app/theme_pro.py:16-18` 改为:
```python
from .core import sanitize , route_path , THEMES
```
(删除原第 18 行的元组定义。)
`app/theme_store.py:14` 改为:
```python
from .core import THEMES as BUILTIN
```
- [ ] **Step 4:** `python -m pytest tests/test_theme_registry.py -q` → PASS; `python -m pytest -q` 全绿
- [ ] **Step 5:** `git add -A && git commit -m "feat: single-source theme discovery replaces three hardcoded whitelists"`
### Task 3: theme_pro 每主题环境(宏覆盖机制,F4)
**Files:**
- Modify: `app/theme_pro.py:18-20` ( LOADER 旁新增 env_for)
- Modify: `app/theme_pro.py:190` ( render 用 env_for)
- Test: `tests/test_theme_registry.py` (追加)
- [ ] **Step 1: 追加失败测试**
```python
def test_env_for_theme_prefers_theme_dir_then_shared_root ():
from app import theme_pro
env = theme_pro . env_for ( 'industrial' )
paths = [ str ( p ) for p in env . loader . searchpaths ]
assert paths [ 0 ] . replace ( ' \\ ' , '/' ) . endswith ( 'themes/industrial' )
assert paths [ 1 ] . replace ( ' \\ ' , '/' ) . endswith ( 'themes' )
# 共享宏库必须仍可解析(回落路径)
assert env . get_template ( '_professional_components.html' ) is not None
def test_env_for_is_cached_per_theme ():
from app import theme_pro
assert theme_pro . env_for ( 'industrial' ) is theme_pro . env_for ( 'industrial' )
```
- [ ] **Step 2:** 跑测试 → FAIL( env_for 不存在)
- [ ] **Step 3: 实现** ——`app/theme_pro.py` 在 LOADER 定义后追加:
```python
_ENVS = {}
def env_for ( theme ):
"""Per-theme Jinja env: theme dir first, shared themes/ root as fallback.
Lets a theme override shared macros (_professional_components.html) while
inheriting everything else. Cached: one env per theme across 5000-page builds.
"""
env = _ENVS . get ( theme )
if env is None :
theme_dir = ROOT / 'themes' / theme
search = [ str ( theme_dir ), str ( ROOT / 'themes' )] if theme_dir . is_dir () else [ str ( ROOT / 'themes' )]
env = Environment ( loader = FileSystemLoader ( search ), autoescape = select_autoescape ([ 'html' , 'xml' ]))
_ENVS [ theme ] = env
return env
```
`render()` 末行(:190)改为:
```python
return env_for ( theme ) . get_template ( 'pro.html' ) . render ( ** ctx )
```
- [ ] **Step 4:** `python -m pytest -q` 全绿(现有 test_theme_pro_v3.py 即回归)
- [ ] **Step 5:** commit `feat: per-theme Jinja env enables theme-level macro override`
### Task 4: builder 主题资产优先(F5)
**Files:**
- Modify: `app/builder.py:191`
- Test: `tests/test_theme_registry.py` (追加)
- [ ] **Step 1: 追加失败测试**
```python
def test_theme_blocks_css_overrides_platform ( tmp_path ):
from app import builder
from pathlib import Path
dist = tmp_path / 'assets' ; dist . mkdir ()
theme_dir = tmp_path / 'theme' ; theme_dir . mkdir ()
( theme_dir / 'blocks.css' ) . write_text ( '/* theme version */' , encoding = 'utf-8' )
builder . copy_blocks_css ( theme_dir , dist )
assert ( dist / 'blocks.css' ) . read_text ( encoding = 'utf-8' ) == '/* theme version */'
# 无主题版时回落平台版
dist2 = tmp_path / 'assets2' ; dist2 . mkdir ()
builder . copy_blocks_css ( tmp_path / 'nonexistent' , dist2 )
assert ( dist2 / 'blocks.css' ) . is_file ()
```
- [ ] **Step 2:** 跑 → FAIL
- [ ] **Step 3: 实现** ——`app/builder.py:191` 的 `shutil.copy2(ROOT/'app'/'static'/'blocks.css',dist/'assets'/'blocks.css')` 替换为调用新函数,并在文件合适位置(output_path 附近)定义:
```python
def copy_blocks_css ( theme_dir , dist_assets ):
"""Theme-provided blocks.css wins; platform default is the fallback."""
src = theme_dir / 'blocks.css' if ( theme_dir / 'blocks.css' ) . is_file () else ROOT / 'app' / 'static' / 'blocks.css'
shutil . copy2 ( src , dist_assets / 'blocks.css' )
```
- [ ] **Step 4:** `python -m pytest -q` 全绿;commit `feat: theme-level blocks.css takes priority over platform asset`
### Task 5: trade-pro 骨架主题包(Phase 2 核心)
**Files:**
- Create: `themes/trade-pro/manifest.json` 、`site.html` 、`pro.html` 、`_professional_components.html` 、`theme.css` 、`blocks.css` 、`pro.css` 、`page-presets.json` 、`pages/` ( 14 个 html)、`visual/.gitkeep` 、`versions/.gitkeep`
- Test: `tests/test_theme_registry.py` (追加)
- [ ] **Step 1: 追加失败测试**
```python
def test_trade_pro_registered_and_compatible ():
assert 'trade-pro' in core . THEMES
result = theme_store . compatibility ( 'trade-pro' )
assert result [ 'valid' ], result [ 'errors' ]
assert not result [ 'errors' ]
2026-10-22 17:02:45 +08:00
def test_trade_pro_builds_real_site ( tmp_path ):
# 复用 tests/test_theme_pro_v3.py 中"建站→切主题→构建→断言预览页"的既有写法
# (夹具工厂在 tests/test_factory_v2.py:15-30),theme 用 'trade-pro'。
2026-10-22 17:04:30 +08:00
# 产物读取走夹具的 HTTP 形式(与 v3 一致):
html = factory . get ( f '/preview/ { sid } / { bid } /' ) . text
2026-10-22 17:02:45 +08:00
assert 'lang="en"' in html
assert '>首页<' not in html
2026-10-22 16:49:25 +08:00
```
2026-10-22 17:02:45 +08:00
(执行时先读 test_theme_pro_v3.py 的相邻测试,逐行复制其夹具调用方式,仅替换 theme 参数与断言;禁止保留 `...` 空壳。)
2026-10-22 16:49:25 +08:00
- [ ] **Step 2:** 跑 → FAIL
- [ ] **Step 3: 创建文件**
`manifest.json` :
```json
{
"id" : "trade-pro" ,
"name" : "Trade Pro — International B2B" ,
"description" : "English-first international B2B theme ported from the hesupipe Astro design: SVG icon set, responsive trade catalogue, product specs and inquiry CTA." ,
"version" : "1.0.0" ,
"style" : "international-b2b" ,
"entry" : "pro.html" ,
"styles" : [ "theme.css" , "blocks.css" ],
2026-10-22 17:02:45 +08:00
"page_templates" : [ "home" , "about" , "contact" , "listing" , "category" , "product" , "article" , "brand" , "case" , "service" , "faq" , "standard" , "notfound" ],
2026-10-22 16:49:25 +08:00
"blocks" : [ "hero" , "richtext" , "features" , "stats" , "collection" , "image_text" , "gallery" , "faq" , "cta" , "attributes" , "spacer" , "layout" , "carousel" , "video" , "tabs" , "pricing" , "map" , "form_link" ],
"responsive" : true ,
"auto_binding" : true ,
"professional_layout" : true ,
"commercial_ui" : true ,
"accent" : "#0b3d91"
}
```
2026-10-22 17:02:45 +08:00
`site.html` :以 `themes/industrial/site.html` 为底(它是 4.4KB 的完整独立模板,自带 header/footer 结构,**不引用共享宏库**),改 `lang="zh-CN"` → `lang="en"` 。注意:pro 主题的正式渲染走 pro.html, site.html 主要服务非 pro 路径/兜底。
**已知新行为(评审发现,记录不阻断)** : Task 2 后 trade-pro 进入 BUILTIN,上传主题 ZIP 可声明 `layout_family:'trade-pro'` ( theme_store.install 会拷贝 trade-pro 的 site/pro.html),而 ZIP 包渲染走共享宏回落——与依赖自有宏库的 trade-pro 存在潜在错配。本轮接受(上传包本就以声明式为主),后续如出问题在 install() 校验 layout_family 白名单。
2026-10-22 16:49:25 +08:00
`_professional_components.html` :先做**透传骨架**(从共享库复制 card/cover 等宏,Task 10 起逐个换成 cms 设计 + SVG 图标)。
`pro.html` :以 industrial 的 pro.html 为底最小化:`lang="en"` ;页眉/页脚消费 site_design chrome 但按 spec 4.2.5 值比对输出英文兜底(中文默认值视为未配置);正文区渲染 page content 与 custom_blocks;按钮类名一律 `sf-tp-*` 。
`theme.css` :先放 tokens 骨架(Task 9 全量移植):
```css
: root { --sf-tp-primary : #0b3d91 ; --sf-tp-accent : #f5a623 ; --sf-tp-bg : #ffffff ; --sf-tp-text : #1a2332 ; --sf-tp-muted : #5a6a7e ; --sf-tp-radius : 8 px ; --sf-tp-font : 'Inter' , system-ui , - apple-system , sans-serif }
```
`blocks.css` :空文件 + 注释 `/* trade-pro component styles: filled in Phase 3 */` 。
`pro.css` :空文件(兼容性硬性要求,spec F8)。
`page-presets.json` : **复制 industrial 的同名文件原样**( Phase 4 精修;13 种 key 齐全即可)。
`pages/` : `home.html` 写最小可用骨架(hero + showcase 列表 + cta 引用共享宏);其余 13 个(about/contact/listing/product/brand/article/case/service/faq/category/search/standard/notfound)先各写薄变体(复用 home 的结构、引用同样宏),文件必须存在(spec F7 硬性)。
- [ ] **Step 4:** `python -m pytest -q` 全绿(含新测试)
- [ ] **Step 5:** `docker compose up -d --build` 后:`curl -s http://127.0.0.1:8787/api/health` 版本不变、`curl -s "http://127.0.0.1:8787/api/factory/themes/trade-pro/compatibility"` (带登录 cookie)返回 valid=true
- [ ] **Step 6:** commit `feat: trade-pro skeleton theme package (Phase 2)`
### Task 6: theme_color 映射 + 6 处前端入口(F3/F11)
**Files:**
- Modify: `app/theme_pro.py` ( theme_color 字典,make_context 内,约 :171)
- Modify: `app/templates/admin.html:13` ( siteTheme select)
- Modify: `app/templates/workspace.html:41` (建站模态框 theme select)
- Modify: `app/templates/theme-gallery.html:4` ( theme select)
- Modify: `app/static/workspace.js:5` ( themeName)
- Modify: `app/static/admin.js:5` ( cfg)
- Modify: `app/static/factory-v2.js:30` ( schemes)
- Test: `tests/test_theme_registry.py` (追加)
- [ ] **Step 1: 追加失败测试**
```python
def test_frontend_entries_include_trade_pro ():
import pathlib
base = pathlib . Path ( __file__ ) . resolve () . parent . parent
for rel , needle in [
( 'app/templates/admin.html' , 'value="trade-pro"' ),
( 'app/templates/workspace.html' , 'value="trade-pro"' ),
( 'app/templates/theme-gallery.html' , 'value="trade-pro"' ),
( 'app/static/workspace.js' , "'trade-pro'" ),
( 'app/static/admin.js' , "'trade-pro'" ),
( 'app/static/factory-v2.js' , "'trade-pro'" ),
]:
assert needle in ( base / rel ) . read_text ( encoding = 'utf-8' ), rel
2026-10-22 17:04:30 +08:00
def test_theme_color_map_covers_trade_pro ():
2026-10-22 17:02:45 +08:00
# 真实断言:trade-pro 构建出的首页含 meta theme-color #0b3d91
2026-10-22 17:04:30 +08:00
# (复用 Task 5 的构建夹具与 /preview/ HTTP 读取写法)
html = factory . get ( f '/preview/ { sid } / { bid } /' ) . text
2026-10-22 17:02:45 +08:00
assert '<meta name="theme-color" content="#0b3d91"' in html
2026-10-22 16:49:25 +08:00
```
- [ ] **Step 2:** 跑 → FAIL
- [ ] **Step 3: 实现** ——逐处追加(保持原文风格):
- theme_color 字典追加 `'trade-pro':'#0b3d91'`
- 三个 HTML select 各追加:`<option value="trade-pro">Trade Pro · 外贸 B2B(英文)</option>` ( theme-gallery 用 `Trade Pro · International B2B` )
- `workspace.js` themeName 追加 `'trade-pro':'Trade Pro'`
- `admin.js` cfg 追加 `trade-pro:{label:'Trade Pro',desc:'英文外贸 · B2B 目录与询盘',prev:'corp',title:'Go global.'}`
- `factory-v2.js` schemes 追加 `'trade-pro':['#0b3d91','#e8f1fb','GO GLOBAL']`
- [ ] **Step 4:** `python -m pytest -q` 全绿 + `for f in app/static/*.js; do node --check "$f"; done` 通过
- [ ] **Step 5:** commit `feat: register trade-pro in theme color map and all six frontend entry points`
### Task 7: E2E 验收——真实站点切主题构建(Phase 2 门槛)
**Files:** 无代码(验证 + 记录)
- [ ] **Step 1:** 登录拿 cookie/CSRF(参照已验证流程:POST /api/auth/login → GET /api/auth/me)
- [ ] **Step 2:** `PUT /api/factory/sites/483295c29ea0/theme` body `{"theme":"trade-pro"}` (确认返回成功)
- [ ] **Step 3:** `POST /api/sites/483295c29ea0/builds` ( Idempotency-Key + CSRF)→ 轮询 job 至 done
- [ ] **Step 4:** 断言:pages ≥203、errors 空;预览首页 HTTP 200、含 `lang="en"` 、不含"首页/联系我们"(平台中文默认,spec F9 验收);`/products/` 、`/product/alloy-resin-system-window/` 200
- [ ] **Step 5:** 断言 6 入口:新工作台/`/classic` 建站表单、theme-gallery、factory-studio 主题卡里均出现 trade-pro(浏览器人工点验,截图存 `docs/superpowers/` 或记录于验收文件)
- [ ] **Step 6:** 把结果写入 `docs/superpowers/specs/2026-10-22-trade-pro-theme-design.md` 附录"Phase 2 验收记录"; commit
---
## Phase 3:完整视觉移植(Task 8-12)
> 以下任务的代码无法在计划里全文预写(从 cms 源逐段移植),但每步给出**精确源文件、提取规则与验收断言**。共用纪律:每完成一个 Task 跑全量 pytest + commit。
### Task 8: 设计令牌与基础样式全量移植
- 源:`D:/project/cms/runtimes/astro-site-runtime/src/styles/tokens.css` ( 250 行)+ `base.css` ( 445 行)
- [ ] 变量重命名加 `--sf-tp-` 前缀(spec 4.2.6);按钮/交互元素全部 `sf-tp-` 类名(避开平台 `.btn/.primary/.request/.button/.nav-btn` )
- [ ] 验收断言:`themes/trade-pro/theme.css` 覆盖 tokens 全集(颜色/字体/圆角/间距);构建产物 `assets/theme.css` 含 `--sf-tp-` 变量;平台 custom.css 的 `.btn{...}` 与主题按钮类无交集(grep 断言)
- [ ] pytest(现有快照不受影响)+ commit
2026-10-22 17:02:45 +08:00
### Task 9: 图标宏库(新增设计——不是 cms 移植)
> 评审修正(2026-10-22):cms 源码与 dist 实测 **0 个 SVG**,其图标实为文本箭头 `→`( index 15 处、product 17 处)。用户需求"图标需要优化、现在很多都是箭头"意味着图标是**超出 cms 源的新增设计**,不与 dist 对照。
2026-10-22 16:49:25 +08:00
2026-10-22 17:02:45 +08:00
- [ ] `themes/trade-pro/_professional_components.html` 定义 `icon(name)` 宏:自设计内联 SVG 集合,线条风格与 `--sf-tp-*` 令牌一致,≥8 个:arrow-right / check / phone / mail / location / download / search / quote
- [ ] 尺寸/描边随 CSS 变量(`currentColor` 、`1.5px` stroke),跟随深浅文字色
- [ ] 验收:home/product 渲染中**交互元素的图标均为 SVG**,不再以裸 `→` 作为唯一图标(对照对象是本主题自身,非 dist);`aria-hidden="true"` 保证可访问性
2026-10-22 16:49:25 +08:00
- [ ] commit
### Task 10: 九种独立布局逐个移植(每布局一个 commit)
顺序与源对照(cms `src/pages/[locale]/` ↔ dist `en/` ↔ trade-pro `pages/` ):
1. `home` : index.astro ↔ dist 首页(Hero/FeatureGrid/CardGrid/CTA 结构)
2. `product` : products/[slug].astro ↔ dist/products/alloy-resin-system-window/(参数表 specs、多图 gallery、询盘 CTA)
3. `listing` : news/[...page].astro + products 列表 ↔ 按 kind 分支(Phase 0 已确认模板可拿到 page/kind 上下文;若不可分支由 category 承担产品列表,spec §7 兜底)
4. `category` : product-category ↔ 分类页
5. `article` : news/[slug] ↔ 文章详情
6. `about` / 7. `contact` ( LeadForm 样式 + form_endpoint) / 8. `case` ( projects) / 9. `standard` ( applications、support)
每个布局的统一验收:构建对应真实页面 200;关键结构选择器与 dist 对照(header/hero/卡片/参数表/页脚);英文文案断言(无中文默认);手机宽度可读(人工抽查一次,全量视觉验收在 Task 12)。
### Task 11: 五个变体布局 + search
- brand/service/faq/notfound/search 以 standard/listing 变体补齐为"结构合理"版本(后续迭代精修,spec 4.3)
### Task 12: 预设、编辑器与全量验收(Phase 4)
- [ ] `page-presets.json` 13 种按 cms 首页/产品页实际区块结构重写(编辑器初始布局与真实设计一致)
2026-10-22 17:58:05 +08:00
- [ ] 编辑器验证:hero/cta/features/collection/attributes 在 trade-pro 下可编辑、预览、保存草稿、发布(spec §6)。**已知观察(Task 4 质量审查)**:main.py 编辑器侧读平台版 blocks.css,而构建产物可能用主题版——编辑器预览与最终站点可能样式不一致;本项验收时确认影响,必要时编辑器预览也走主题版
2026-10-22 16:49:25 +08:00
- [ ] 全站设计草稿验证:页眉/页脚/导航配置生效(显式值渲染、中文默认被值比对替换为英文兜底)
- [ ] 全量回归:`python -m pytest -q` (≥210+新增全绿)、`node --check` 全部 JS
- [ ] 实战验收:站点 483295c29ea0 最终构建 ≥203 页零错误;视觉对照两层标准(spec §6)逐页记录
- [ ] 更新 spec 附录验收记录;commit
2026-10-22 17:02:45 +08:00
## 与 spec 的已记录偏差(评审裁定可接受)
1. **发现函数放 core 而非 spec 4.2.1 写的 theme_store** :等价实现。core 已被全部引用方(builder/main/theme_pro)导入,放 core 消除反向耦合;theme_store.BUILTIN 经 import 别名保持一致。
2. **builder.ENV 保持单目录** (spec 4.2.4 提到两处构造点):已验证 site.html 是不引用共享宏的独立模板,且带主题前缀的路径加载在单目录下安全;仅 theme_pro.LOADER 改为 per-theme env_for。
3. **spec §6 "渲染快照"以 E2E 页面断言实现** (构建后逐页 200 + 结构选择器 + 文案断言),不另建 Jinja 快照测试文件。
4. ** /api/themes( main.py:267)经 core.THEMES 动态化自动生效**,不设专项测试(catalog 由既有测试覆盖)。
5. **前端入口 option 文案** :三个 HTML 下拉用"Trade Pro · 外贸 B2B(英文)"中文风格与各文件现状一致(theme-gallery 用英文)。
2026-10-22 18:40:49 +08:00
6. **平台数据层中文残留(Task 5 质量审查发现)** : bootstrap 生成的 8 个标准页(/about/ /contact/ /products/ /news/ 等)的 `<title>` 、`<h1>` 、面包屑来自平台造的中文标题(关于我们/联系我们/产品中心…),主题层无法翻译用户数据。Phase 3 视觉验收时接受(真实站点内容为英文导入数据时自然英文),若 bootstrap 英文化成为需求属平台任务另行立项。
2026-10-22 17:02:45 +08:00
2026-10-23 11:00:13 +08:00
## 用户反馈修复队列(2026-10-23 首页预览反馈,10b 收口后立即执行)
0. ** [新增 10j] cms 页眉/页脚移植**(用户反馈"整体不是 cms 效果"):在平台 site_design 生成的 DOM 结构上,用 .sf-theme-trade-pro 作用域 CSS 完整还原 cms Header/Footer 视觉(Header.astro/Footer.astro 源码样式:白底细分割线、导航字距/悬停态、蓝色 GET A QUOTE 按钮、cms 页脚深色带与栏目布局)。保持平台 chrome 数据流(导航/公告/CTA 配置仍生效)。排在 10c-10i 之后、Task 11 之前。
1. `.sf-tp-feature` 补 cms FeatureGrid.astro 的卡片皮肤:白底(--sf-tp-color-surface-default) + hairline 边框(--sf-tp-color-border-subtle) + 圆角 + 居中 + padding, 900px 两列、620px 横排(源码 scoped CSS 全量对照)
2. `.sf-tp-card` 对照 cms dist 的 .card scoped CSS 补边框卡片皮肤(含 hover 抬升已有则保留)
3. `.sf-theme-trade-pro` 作用域内重设计 `.pro-art` 无图占位:brand 渐变底(surface-inverse → brand-primary 混合)+ 白色首字母居中 + 小写 kind 标签,替代共享宏灰块("10 PRODUCT"噪字)
4. Hero 双按钮改横排(flex gap, 620px 换行)——核对 pro.css hero 区现状
5. 验收方式:浏览器截图前后对照(Playwright),产品卡/特性卡/占位块三处逐一确认
2026-10-22 16:49:25 +08:00
## 风险与回退
- 每个 Task 独立 commit,出问题 `git revert` 单点回退
- 平台适配层(Task 2-4)有 4 主题回归保护:任何现有测试变红立即停止
- trade-pro 目录独立,删目录 + revert 白名单改动即完全移除