feat(core/frontend): P1 pixel restoration (settings/messages/wizard/product-create faithful; ai-tools draft)
- settings.tsx + settings-page.css: restore to settings.html (left-nav + sections), real user/team data - messages.tsx + messages-page.css: rich inbox restore (filters/detail/props grid) on real notifications - projects.tsx ProjectWizardPage + project-wizard-page.css: restore to projects-new.html - products.tsx ProductCreateUploadPage + product-create-page.css: restore to product-create-v2 baseline - ai-tools.tsx (AssetFactory/ImageWorkbench) + ai-tools-page.css: DRAFT unified studio shell; deviates from per-page baselines (image-optimize should be chat-stream; model-photo product+person picker) -> pending rework alongside P3 standalone image-gen decision - shot-p1.mjs: playwright visual-parity capture (react vs exact baseline; uses 127.0.0.1 not localhost) - verified: tsc --noEmit clean; screenshots confirm settings/wizard/product-create/messages faithful Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
25bf3293df
commit
78fd7ee13d
@@ -0,0 +1,62 @@
|
||||
// shot-p1.mjs · P1 像素还原验收截图(React 实页 vs public/exact 基线)
|
||||
// 用法: node shot-p1.mjs [outDir]
|
||||
// 前置: 后端 8010 + 前端 5173 在跑;playwright 已装;chromium 已缓存。
|
||||
import { chromium } from "playwright";
|
||||
import { mkdirSync } from "node:fs";
|
||||
|
||||
const BASE = "http://127.0.0.1:5180";
|
||||
const API = "http://127.0.0.1:8010";
|
||||
const OUT = process.argv[2] || "shots-p1";
|
||||
mkdirSync(OUT, { recursive: true });
|
||||
|
||||
// [name, React 路由, 基线 html]
|
||||
const PAGES = [
|
||||
["settings", "/settings", "/exact/settings.html"],
|
||||
["messages", "/messages", "/exact/messages.html"],
|
||||
["asset-factory", "/asset-factory", "/exact/asset-factory.html"],
|
||||
["image-optimize", "/image-optimize", "/exact/image-optimize.html"],
|
||||
["model-photo", "/model-photo", "/exact/model-photo.html"],
|
||||
["platform-cover", "/platform-cover", "/exact/platform-cover.html"],
|
||||
["product-create", "/products/new", "/exact/product-create-upload.html"],
|
||||
["project-wizard", "/projects/new", "/exact/projects-new.html"]
|
||||
];
|
||||
|
||||
const res = await fetch(`${API}/api/auth/login/`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ username: "airshelf", password: "Restraint2026" })
|
||||
});
|
||||
const data = await res.json();
|
||||
const token = data.token;
|
||||
if (!token) {
|
||||
console.error("login failed:", JSON.stringify(data));
|
||||
process.exit(1);
|
||||
}
|
||||
console.log("token ok");
|
||||
|
||||
const browser = await chromium.launch();
|
||||
const ctx = await browser.newContext({ viewport: { width: 1440, height: 900 }, deviceScaleFactor: 1 });
|
||||
await ctx.addInitScript((t) => localStorage.setItem("airshelf_token", t), token);
|
||||
const page = await ctx.newPage();
|
||||
page.on("pageerror", (e) => console.error(" pageerror:", e.message));
|
||||
|
||||
for (const [name, route, baseline] of PAGES) {
|
||||
try {
|
||||
await page.goto(BASE + route, { waitUntil: "networkidle", timeout: 30000 });
|
||||
await page.waitForTimeout(1400);
|
||||
await page.screenshot({ path: `${OUT}/${name}.react.png`, fullPage: true });
|
||||
console.log("react ", name);
|
||||
} catch (e) {
|
||||
console.error("FAIL react", name, e.message);
|
||||
}
|
||||
try {
|
||||
await page.goto(BASE + baseline, { waitUntil: "networkidle", timeout: 30000 });
|
||||
await page.waitForTimeout(900);
|
||||
await page.screenshot({ path: `${OUT}/${name}.baseline.png`, fullPage: true });
|
||||
console.log("baseline", name);
|
||||
} catch (e) {
|
||||
console.error("FAIL baseline", name, e.message);
|
||||
}
|
||||
}
|
||||
await browser.close();
|
||||
console.log("DONE ->", OUT);
|
||||
Reference in New Issue
Block a user