fix: expose incomplete UI coverage honestly
This commit is contained in:
@@ -25,14 +25,18 @@ progression, formulas, and expression must be original.
|
||||
|
||||
## Review The Design
|
||||
|
||||
The single owner-facing review artifact is the interactive HTML prototype in
|
||||
[`prototype/site/`](prototype/site/). It shows one subject at a time and links
|
||||
the complete design package behind these review paths:
|
||||
The current owner-facing HTML in [`prototype/site/`](prototype/site/) is a
|
||||
**coverage audit**, not a completed UI prototype. Owner review on 2026-08-10
|
||||
proved that generic specification templates were not understandable as game
|
||||
screens. Its page browser now distinguishes one dedicated page, 40 shared
|
||||
family mockups, and 41 pages whose UI is still missing.
|
||||
|
||||
1. **Core flow:** walk through the 17-step title-to-restoration path.
|
||||
1. **Flow specification:** inspect the required 17-step title-to-restoration
|
||||
order; do not treat it as a validated clickable experience.
|
||||
2. **Eight chapters:** inspect route, battle, and restored-world direction for
|
||||
every chapter.
|
||||
3. **All screens:** inspect all 82 player-facing pages and their key states.
|
||||
3. **Page coverage:** inspect all 82 requirements and identify which screens
|
||||
still need a dedicated composition.
|
||||
4. **Content:** search all 30 characters, 36 professions, 100 enemies, 320
|
||||
items, 48 achievements, and 20 collections.
|
||||
5. **Production and readiness:** inspect the art/audio scope and the gates that
|
||||
|
||||
@@ -11,7 +11,8 @@ The prototype must let the owner decide whether:
|
||||
1. the landscape 2D hand-drawn direction fits the intended game;
|
||||
2. the player can recognize Guild, company, route, battle, diagnosis, reward,
|
||||
restoration, records, and settings without a card-wall interface;
|
||||
3. the 82-screen inventory and eight chapters appear complete;
|
||||
3. the 82-screen requirement inventory and eight chapters are present, while
|
||||
unfinished UI is labeled as unfinished rather than presented as complete;
|
||||
4. formation, telegraph, Directive, Cache, Anchor, Strain, profession, and item
|
||||
concepts are sufficiently visible to proceed to a dynamic vertical slice;
|
||||
5. any page family or chapter direction should be redesigned before runtime
|
||||
@@ -66,9 +67,11 @@ Every screen entry shows:
|
||||
- previous/next page actions plus direct selection from the left list;
|
||||
- a full-size viewing mode without cropping the game canvas.
|
||||
|
||||
The mockup renderer uses the canonical shell for the selected prefix and adds
|
||||
page-specific state. Core flow pages receive more detailed compositions. The
|
||||
prototype must not imply that all 82 pages have final bespoke raster art.
|
||||
Owner review on 2026-08-10 rejected the shared-template result as
|
||||
incomprehensible. The coverage audit found `1` dedicated page, `40` shared
|
||||
family mockups, and `41` pages without an individual UI composition. A shared
|
||||
template is not a completed page. Until this gap is closed, the HTML may expose
|
||||
requirements and coverage but must not call itself a complete UI prototype.
|
||||
|
||||
## 4. Compressed Click Path
|
||||
|
||||
@@ -90,8 +93,10 @@ Title SYS-004
|
||||
-> Guild transformed GLD-001
|
||||
```
|
||||
|
||||
This proves navigation and information hierarchy only. It does not simulate
|
||||
combat formulas, persistence, pacing, audio, touch feel, or fun.
|
||||
This currently proves only the required node order. It does not yet prove
|
||||
navigation comprehension or information hierarchy because several core nodes
|
||||
still lack their own page design. It also does not simulate combat formulas,
|
||||
persistence, pacing, audio, touch feel, or fun.
|
||||
|
||||
## 5. Visual Assets In This Prototype
|
||||
|
||||
@@ -121,6 +126,9 @@ legible and do not depend on generated text.
|
||||
## 7. Prototype Acceptance
|
||||
|
||||
- [x] All 82 screen IDs appear exactly once in the screen selector.
|
||||
- [x] Coverage is reported truthfully as 1 dedicated / 40 family / 41 missing.
|
||||
- [ ] All 82 requirements have page-specific, player-readable UI compositions.
|
||||
- [ ] All 17 core-flow nodes have dedicated UI and pass owner comprehension.
|
||||
- [x] All eight chapters and 24 chapter screen families are represented.
|
||||
- [x] Content indexes show 30/36/100/320/48/20 exact counts.
|
||||
- [x] Core click path reaches restoration without a dead control.
|
||||
@@ -129,5 +137,5 @@ legible and do not depend on generated text.
|
||||
- [x] Generated concepts remain labeled honestly and carry task provenance.
|
||||
- [x] Local links, image paths, JSON, browser verification, Gallery publication,
|
||||
and Gallery health/share checks pass.
|
||||
- [x] Owner visual, comprehension, fun, audio, device, and release gates remain
|
||||
explicitly open.
|
||||
- [x] Owner comprehension failed on the generic screen presentation; visual,
|
||||
fun, audio, device, and release gates remain explicitly open.
|
||||
|
||||
@@ -4,10 +4,15 @@ Date: 2026-08-10
|
||||
|
||||
## Decision
|
||||
|
||||
The complete static design package and owner-facing HTML prototype are ready
|
||||
for owner review. Formal game implementation remains frozen. This result does
|
||||
not claim that the game is playable, understandable, fun, visually accepted,
|
||||
heard, device-verified, packaged, or release-ready.
|
||||
`FAIL`: the static system/content specifications are indexed, but the
|
||||
owner-facing UI package is not complete and is not ready for approval. Owner
|
||||
review on 2026-08-10 could not understand `SYS-001`; inspection proved that the
|
||||
82 entries were rendered through six shared templates rather than 82 designed
|
||||
pages. Formal game implementation remains frozen.
|
||||
|
||||
Earliest failed boundary: page-specific visual communication. `SYS-001`
|
||||
incorrectly inherited a windmill background, save/cache HUD, return action and
|
||||
`none` button even though its contract is an automatic boot/loading state.
|
||||
|
||||
## Verified Design Authority
|
||||
|
||||
@@ -17,10 +22,10 @@ heard, device-verified, packaged, or release-ready.
|
||||
| Battle and systems | six-person formation, telegraphs, Directives, automation, failure and recovery | specified and cross-checked |
|
||||
| Economy and save | 1,700 authored minutes, five pacing traces, prices, caps, atomic save and corruption handling | static validator pass |
|
||||
| Content | 30 characters, 36 professions, 100 enemies, 320 items, 8 chapters, 48 achievements, 20 collections | complete indexed rows |
|
||||
| Pages and states | 82 pages, 24 chapter visual families, onboarding, accessibility, localization, errors and exits | complete indexed rows |
|
||||
| Pages and states | 82 requirements indexed | 1 dedicated page / 40 family mockups / 41 missing page compositions |
|
||||
| Art production | character, profession, enemy, item, environment, UI, animation and VFX identities | production catalog complete; final assets open |
|
||||
| Audio production | 30 music suites, 94 ambience configurations, at least 2,602 bilingual voice assets, 710 SFX identities | production catalog complete; generation/listening open |
|
||||
| HTML prototype | 11 views, 82-screen selector, 17-step flow, 8 chapters and searchable content catalogs | browser verified |
|
||||
| HTML shell | 11 views, selector, flow order, chapters and searchable content catalogs | technically browser verified; product comprehension failed |
|
||||
|
||||
## Reproducible Local Checks
|
||||
|
||||
@@ -53,12 +58,11 @@ ABG_PROTOTYPE_BROWSER_OK views=11 screens=82 flow_steps=17 chapters=8 responsive
|
||||
|
||||
## Owner Review Order
|
||||
|
||||
1. Complete the 17-step core flow and name any step whose purpose or result is
|
||||
unclear.
|
||||
1. Do not ask the owner to review the 82-page set again yet.
|
||||
2. Inspect all eight chapter directions and judge whether each promises a
|
||||
strategically and visually different problem.
|
||||
3. Inspect the 82-screen map for missing pages, crowded pages, unclear primary
|
||||
actions, and unsafe exits.
|
||||
3. Produce dedicated title, Guild, formation, map, route, battle, result,
|
||||
reward, Anchor and restoration pages before the next owner candidate.
|
||||
4. Search the complete content catalogs and challenge roles, counters,
|
||||
progression relationships, and production feasibility.
|
||||
5. Accept, revise, or reject the visual direction and product scope before a
|
||||
|
||||
Vendored
+99
-33
@@ -10,8 +10,8 @@ const state = {
|
||||
|
||||
const viewNames = {
|
||||
overview: "总览",
|
||||
screens: "全部页面",
|
||||
flow: "核心流程",
|
||||
screens: "页面规格与缺口",
|
||||
flow: "流程规格链",
|
||||
chapters: "八章画面",
|
||||
characters: "角色图鉴",
|
||||
professions: "职业系统",
|
||||
@@ -25,7 +25,7 @@ const viewNames = {
|
||||
const screenCategoryNames = {
|
||||
ALL: "全部",
|
||||
SYS: "系统",
|
||||
GLD: "Guild",
|
||||
GLD: "公会",
|
||||
WRK: "工坊",
|
||||
REC: "档案",
|
||||
WLD: "世界",
|
||||
@@ -35,6 +35,52 @@ const screenCategoryNames = {
|
||||
SET: "设置",
|
||||
};
|
||||
|
||||
const localizedScreenNames = {
|
||||
"SYS-001": "启动画面",
|
||||
"SYS-002": "首次无障碍设置",
|
||||
"SYS-003": "语言与安全区校准",
|
||||
"SYS-004": "标题首页",
|
||||
"SYS-005": "游戏存档",
|
||||
"SYS-006": "新游戏设置",
|
||||
"SYS-007": "回归摘要",
|
||||
"SYS-008": "存档恢复",
|
||||
"SYS-009": "完成度总览",
|
||||
"SYS-010": "制作人员、许可与数据说明",
|
||||
};
|
||||
|
||||
function screenName(screen) {
|
||||
return localizedScreenNames[screen.id] || screen.name;
|
||||
}
|
||||
|
||||
function layoutKind(screen) {
|
||||
const prefix = screen.id.slice(0, 3);
|
||||
if (screen.id === "SYS-001") return "dedicated";
|
||||
if (screen.id === "GLD-005") return "family-formation";
|
||||
if (["EXP-002", "EXP-003", "WLD-001", "WLD-002", "WLD-003"].includes(screen.id)) return "family-route";
|
||||
if (prefix === "BAT" || ["EXP-004", "RES-001", "RES-002", "RES-003", "RES-004"].includes(screen.id)) return "family-battle";
|
||||
if (prefix === "SET") return "family-settings";
|
||||
if (prefix === "REC") return "family-records";
|
||||
return "missing";
|
||||
}
|
||||
|
||||
function coverage(screen) {
|
||||
const kind = layoutKind(screen);
|
||||
if (kind === "dedicated") return { key: "dedicated", label: "逐页设计" };
|
||||
if (kind.startsWith("family-")) return { key: "family", label: "页面族示意" };
|
||||
return { key: "missing", label: "未设计" };
|
||||
}
|
||||
|
||||
function reviewCopy(screen) {
|
||||
if (screen.id === "SYS-001") {
|
||||
return {
|
||||
focal: "游戏标志、加载光线、当前载入状态",
|
||||
action: "无需操作;完成后自动进入首次设置或标题首页",
|
||||
variants: "首次启动、内容迁移、可恢复载入错误;错误时才显示重试与诊断入口",
|
||||
};
|
||||
}
|
||||
return screen;
|
||||
}
|
||||
|
||||
const chapterImages = [
|
||||
"assets/web/ch_ch01.webp",
|
||||
"assets/web/ch_ch02.webp",
|
||||
@@ -141,7 +187,7 @@ function renderOverview() {
|
||||
</div>
|
||||
</div>
|
||||
<div class="metric-row">
|
||||
${metric(counts.screens, "完整页面与状态定义")}
|
||||
${metric(counts.screens, "页面需求定义;UI 尚未逐页完成")}
|
||||
${metric("8 × 3", "章节路线 / 战场 / 修复画面族")}
|
||||
${metric(counts.characters, "有姓名、有成长弧的角色")}
|
||||
${metric(counts.professions, "12 基础 + 24 进阶职业")}
|
||||
@@ -269,46 +315,61 @@ function renderRecordsStage(screen, art) {
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function renderGenericStage(screen) {
|
||||
const prefix = screen.id.slice(0, 3);
|
||||
const light = prefix === "WRK" || prefix === "GLD";
|
||||
function renderBootStage() {
|
||||
return `
|
||||
${stageTop(screen)}
|
||||
<div class="stage-title"><small>${escapeHtml(screen.id)} · DESIGN SPEC</small><strong>${escapeHtml(screen.name)}</strong><p>${escapeHtml(screen.focal)}</p></div>
|
||||
<div class="stage-generic-panel"><h3>当前操作</h3><p>${escapeHtml(screen.action)}</p><div class="stage-list-row">状态与成本可见</div><div class="stage-list-row">取消不会消耗资源</div><div class="stage-list-row">所有拖动都有点选路径</div></div>
|
||||
<div class="stage-bottom"><button class="stage-secondary" type="button">返回</button><button class="stage-primary" type="button" data-flow-next>${escapeHtml(screen.action)}</button></div>
|
||||
${light ? "" : ""}`;
|
||||
<div class="boot-stage">
|
||||
<div class="boot-sigil" aria-hidden="true"><span></span><i></i></div>
|
||||
<div class="boot-title"><small>AETHERBOUND GUILD</small><strong>裂脉远征团</strong><p>让每一次修复,改变下一段远征。</p></div>
|
||||
<div class="boot-progress"><i></i></div>
|
||||
<p class="boot-status">正在校验远征档案 · 68%</p>
|
||||
<small class="boot-auto">完成后自动进入,无需点击</small>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function renderMissingStage(screen) {
|
||||
const copy = reviewCopy(screen);
|
||||
return `
|
||||
<div class="prototype-missing">
|
||||
<span>UI COVERAGE GAP · ${escapeHtml(screen.id)}</span>
|
||||
<strong>${escapeHtml(screenName(screen))}尚未完成页面设计</strong>
|
||||
<p>目前只有页面需求,不能把这块内容当作游戏画面审核。</p>
|
||||
<dl><div><dt>玩家首先需要看见</dt><dd>${escapeHtml(copy.focal)}</dd></div><div><dt>页面必须支持</dt><dd>${escapeHtml(copy.action || "无需操作")}</dd></div></dl>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function renderGameStage(screen) {
|
||||
const prefix = screen.id.slice(0, 3);
|
||||
const kind = layoutKind(screen);
|
||||
const art = artForScreen(screen);
|
||||
const light = prefix === "SET" || prefix === "REC";
|
||||
let content;
|
||||
if (screen.id === "GLD-005") content = renderFormationStage(screen);
|
||||
else if (["EXP-002", "EXP-003", "WLD-001", "WLD-002", "WLD-003"].includes(screen.id)) content = renderRouteStage(screen);
|
||||
else if (prefix === "BAT" || ["EXP-004", "RES-001", "RES-002", "RES-003", "RES-004"].includes(screen.id)) content = renderBattleStage(screen);
|
||||
else if (prefix === "SET") content = renderSettingsStage(screen);
|
||||
else if (prefix === "REC") content = renderRecordsStage(screen, art);
|
||||
else content = renderGenericStage(screen);
|
||||
if (kind === "dedicated") content = renderBootStage();
|
||||
else if (kind === "family-formation") content = renderFormationStage(screen);
|
||||
else if (kind === "family-route") content = renderRouteStage(screen);
|
||||
else if (kind === "family-battle") content = renderBattleStage(screen);
|
||||
else if (kind === "family-settings") content = renderSettingsStage(screen);
|
||||
else if (kind === "family-records") content = renderRecordsStage(screen, art);
|
||||
else content = renderMissingStage(screen);
|
||||
|
||||
const decorativeArt = art && prefix !== "REC" ? `<img class="stage-art" src="${art}" alt="" />` : "";
|
||||
return `<div class="game-stage ${light ? "stage-light" : ""}" data-stage-id="${escapeHtml(screen.id)}">${decorativeArt}${content}</div>`;
|
||||
const decorativeArt = art && prefix !== "REC" && kind !== "dedicated" && kind !== "missing" ? `<img class="stage-art" src="${art}" alt="" />` : "";
|
||||
return `<div class="game-stage ${light ? "stage-light" : ""} stage-${coverage(screen).key}" data-stage-id="${escapeHtml(screen.id)}">${decorativeArt}${content}</div>`;
|
||||
}
|
||||
|
||||
function screenMeta(screen, index, screens) {
|
||||
const copy = reviewCopy(screen);
|
||||
const status = coverage(screen);
|
||||
const previous = screens[(index - 1 + screens.length) % screens.length];
|
||||
const next = screens[(index + 1) % screens.length];
|
||||
return `
|
||||
<div class="screen-meta">
|
||||
<div class="screen-meta-head">
|
||||
<div><span class="screen-id">${escapeHtml(screen.id)}</span><h2>${escapeHtml(screen.name)}</h2>${chip("Design specification")} ${chip("Human review open")}</div>
|
||||
<div><span class="screen-id">${escapeHtml(screen.id)}</span><h2>${escapeHtml(screenName(screen))}</h2>${chip(status.label)} ${chip("人类审核未通过")}</div>
|
||||
<div class="pager"><button type="button" data-screen-id="${escapeHtml(previous.id)}">← 上一页</button><button type="button" data-screen-id="${escapeHtml(next.id)}">下一页 →</button></div>
|
||||
</div>
|
||||
<div class="spec-grid">
|
||||
<div class="spec-cell"><span>三秒焦点顺序</span><p>${escapeHtml(screen.focal)}</p></div>
|
||||
<div class="spec-cell"><span>唯一主操作</span><p>${escapeHtml(screen.action)}</p></div>
|
||||
<div class="spec-cell"><span>状态、异常与退出</span><p>${escapeHtml(screen.variants)}</p></div>
|
||||
<div class="spec-cell"><span>玩家首先需要看见</span><p>${escapeHtml(copy.focal)}</p></div>
|
||||
<div class="spec-cell"><span>玩家在这里做什么</span><p>${escapeHtml(copy.action || "无需操作")}</p></div>
|
||||
<div class="spec-cell"><span>异常与安全退出</span><p>${escapeHtml(copy.variants)}</p></div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
@@ -319,12 +380,16 @@ function renderScreens() {
|
||||
if (!filtered.some((screen) => screen.id === state.screenId)) state.screenId = filtered[0].id;
|
||||
const screen = getScreen(state.screenId);
|
||||
const index = filtered.findIndex((entry) => entry.id === screen.id);
|
||||
const coverageCounts = state.data.screens.reduce((result, entry) => {
|
||||
result[coverage(entry).key] += 1;
|
||||
return result;
|
||||
}, { dedicated: 0, family: 0, missing: 0 });
|
||||
return `
|
||||
<section class="view">
|
||||
${viewHeading("82 SCREEN DEFINITIONS", "每一个页面和关键状态", "页面按系统、Guild、工坊、档案、世界、远征、战斗、结果与设置分类。每一项都锁定玩家先看什么、主操作是什么,以及错误和返回怎么处理。")}
|
||||
${viewHeading("UI COVERAGE AUDIT", "82 个页面需求,不是 82 张完成的 UI", "当前只有启动页完成逐页设计;部分页面仅有共用页面族示意,其余仍是规格缺口。这里用于识别要继续设计什么,不能作为完整 UI 验收。", `${chip(`${coverageCounts.dedicated} 逐页设计`)} ${chip(`${coverageCounts.family} 页面族示意`)} ${chip(`${coverageCounts.missing} 未设计`)}`)}
|
||||
<div class="toolbar"><div class="segmented">${Object.entries(screenCategoryNames).map(([key, label]) => `<button class="${key === category ? "is-active" : ""}" type="button" data-screen-category="${key}">${escapeHtml(label)}</button>`).join("")}</div></div>
|
||||
<div class="screen-layout">
|
||||
<aside class="screen-index" aria-label="页面列表">${filtered.map((entry) => `<button class="${entry.id === screen.id ? "is-active" : ""}" type="button" data-screen-id="${escapeHtml(entry.id)}"><span>${escapeHtml(entry.id)}</span><b>${escapeHtml(entry.name)}</b></button>`).join("")}</aside>
|
||||
<aside class="screen-index" aria-label="页面列表">${filtered.map((entry) => { const status = coverage(entry); return `<button class="${entry.id === screen.id ? "is-active" : ""}" type="button" data-screen-id="${escapeHtml(entry.id)}" data-coverage="${status.key}"><span>${escapeHtml(entry.id)}</span><b>${escapeHtml(screenName(entry))}</b><i class="coverage-${status.key}">${escapeHtml(status.label)}</i></button>`; }).join("")}</aside>
|
||||
<div class="screen-detail"><div class="screen-stage-wrap">${renderGameStage(screen)}</div>${screenMeta(screen, index, filtered)}</div>
|
||||
</div>
|
||||
</section>`;
|
||||
@@ -335,10 +400,10 @@ function renderFlow() {
|
||||
const screen = flowScreens[state.flowIndex];
|
||||
return `
|
||||
<section class="view">
|
||||
${viewHeading("CLICKABLE EXPERIENCE", "从首页到章节修复的完整点击链", "这是压缩的设计原型路径,用来检查玩家是否始终知道当前位置、压力、下一步和退出方式。它不模拟战斗公式或存档。", `${chip(`${state.flowIndex + 1} / ${flowScreens.length}`)} ${chip("无死按钮")}`)}
|
||||
${viewHeading("NAVIGATION SPECIFICATION", "从首页到章节修复的流程规格链", "这条链只证明 17 个需求节点和顺序已经定义;其中多个节点仍没有独立 UI,因此它不是可以进行理解或乐趣验收的产品原型。", `${chip(`${state.flowIndex + 1} / ${flowScreens.length}`)} ${chip("UI 未完成")}`)}
|
||||
<div class="flow-shell">
|
||||
<div><div class="screen-stage-wrap">${renderGameStage(screen)}</div>${screenMeta(screen, state.flowIndex, flowScreens)}</div>
|
||||
<aside class="flow-steps" aria-label="核心流程步骤">${flowScreens.map((entry, index) => `<button class="${index === state.flowIndex ? "is-active" : ""}" type="button" data-flow-index="${index}"><span>${String(index + 1).padStart(2, "0")}</span><b>${escapeHtml(entry.id)}</b>${escapeHtml(entry.name)}</button>`).join("")}</aside>
|
||||
<aside class="flow-steps" aria-label="流程规格步骤">${flowScreens.map((entry, index) => `<button class="${index === state.flowIndex ? "is-active" : ""}" type="button" data-flow-index="${index}"><span>${String(index + 1).padStart(2, "0")}</span><b>${escapeHtml(entry.id)}</b>${escapeHtml(screenName(entry))}</button>`).join("")}</aside>
|
||||
</div>
|
||||
<div class="flow-controls"><button class="text-button" type="button" data-flow-prev>← 上一步</button><button class="flow-action" type="button" data-flow-next>${state.flowIndex === flowScreens.length - 1 ? "回到流程开头" : "执行主操作,进入下一步 →"}</button></div>
|
||||
</section>`;
|
||||
@@ -467,18 +532,19 @@ function renderReadiness() {
|
||||
["战斗 / 职业 / 装备", "完整", "Deterministic combat, 12+24 professions, 320 items", "需通过可玩动态切片验证"],
|
||||
["经济 / 难度 / 存档", "完整", "1700-min authored budget, five traces, atomic save/failure", "公式静态校验通过;运行时未实现"],
|
||||
["世界与内容", "完整", "30 characters, 100 enemies, 8 chapters, ending/postgame", "数量与链接校验通过;无人工完整试玩"],
|
||||
["页面与 UI", "完整规格", "82 screens, 24 chapter screen families, 14 layout families", "本 HTML 等 Owner 视觉/流程审核"],
|
||||
["页面与 UI", "未完成", "82 个页面需求已定义;1 页逐页设计、40 页页面族示意", "41 页没有独立 UI;其余页面族也必须逐页深化"],
|
||||
["美术与动画", "完整清单", "Identity/modular production and CH-01 slice", "仅 4 张概念图;无 final/integrated/runtime"],
|
||||
["音乐 / 环境 / 语音 / SFX", "完整清单", "30 music, 94 ambience, 2602 voice, 710 events", "未生成、未试听、未授权批量 SFX/music"],
|
||||
["HTML 产品原型", "当前候选", "All indexes + compressed clickable core path", "需 Owner 逐页审阅与修改"],
|
||||
["HTML 产品原型", "失败待修", "内容索引和响应式外壳可用", "Owner 已证明页面表达不可理解;当前只能用于缺口审计"],
|
||||
["引擎开发", "冻结", "No Godot project or runtime code", "完整设计 Owner 批准后另开 Goal"],
|
||||
["设备 / 打包 / 上架", "开放", "No build, signing, device or store action", "必须在运行时完成后分别授权"],
|
||||
];
|
||||
const blockedStatuses = new Set(["冻结", "开放", "未完成", "失败待修"]);
|
||||
return `
|
||||
<section class="view">
|
||||
${viewHeading("PRE-DEVELOPMENT GATE", "现在还没有进入正式开发", "策划与生产规格已经大幅补齐,但 Owner 仍需要先审核页面、八章画面和核心流程。开发不能把‘文档完整’误认为‘游戏好玩’。")}
|
||||
<table class="readiness-table"><thead><tr><th>领域</th><th>状态</th><th>已经具备</th><th>仍然开放</th></tr></thead><tbody>${rows.map(([area, status, ready, open]) => `<tr><td><strong>${escapeHtml(area)}</strong></td><td class="${status === "冻结" || status === "开放" ? "state-open" : "state-ready"}">${escapeHtml(status)}</td><td>${escapeHtml(ready)}</td><td>${escapeHtml(open)}</td></tr>`).join("")}</tbody></table>
|
||||
<section class="section-band"><h2>你的最短下一步</h2><p>先走一遍“核心流程”,再看八章画面和全部页面。把不理解、不喜欢或认为过于复杂的页面 ID 直接记下来。确认这些设计方向后,才适合制作 CH-01 可玩动态切片。</p><button class="flow-action" type="button" data-open-view="flow">开始 Owner 审核 →</button></section>
|
||||
${viewHeading("PRE-DEVELOPMENT GATE", "UI 设计没有完成,不能进入正式开发", "系统和内容文档已经建立,但 82 页 UI 只有 1 页逐页设计。Owner 已确认现有页面难以理解,因此完整产品原型验收失败。")}
|
||||
<table class="readiness-table"><thead><tr><th>领域</th><th>状态</th><th>已经具备</th><th>仍然开放</th></tr></thead><tbody>${rows.map(([area, status, ready, open]) => `<tr><td><strong>${escapeHtml(area)}</strong></td><td class="${blockedStatuses.has(status) ? "state-open" : "state-ready"}">${escapeHtml(status)}</td><td>${escapeHtml(ready)}</td><td>${escapeHtml(open)}</td></tr>`).join("")}</tbody></table>
|
||||
<section class="section-band"><h2>下一步不是让你继续猜</h2><p>先逐页完成标题、公会、编队、地图、路线、战斗、结算、奖励、锚点和修复等核心链 UI,再扩展其余页面。新的审核候选必须让页面目的、当前状态和唯一主操作直接可见。</p><button class="flow-action" type="button" data-open-view="screens">查看 UI 缺口 →</button></section>
|
||||
</section>`;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,8 +22,8 @@
|
||||
</div>
|
||||
<nav class="primary-nav" id="primary-nav">
|
||||
<button class="nav-item is-active" data-view="overview" type="button"><span>01</span> 总览</button>
|
||||
<button class="nav-item" data-view="screens" type="button"><span>02</span> 全部页面 <b>82</b></button>
|
||||
<button class="nav-item" data-view="flow" type="button"><span>03</span> 核心流程</button>
|
||||
<button class="nav-item" data-view="screens" type="button"><span>02</span> 页面规格与缺口 <b>82</b></button>
|
||||
<button class="nav-item" data-view="flow" type="button"><span>03</span> 流程规格链</button>
|
||||
<button class="nav-item" data-view="chapters" type="button"><span>04</span> 八章画面 <b>24</b></button>
|
||||
<button class="nav-item" data-view="characters" type="button"><span>05</span> 角色 <b>30</b></button>
|
||||
<button class="nav-item" data-view="professions" type="button"><span>06</span> 职业 <b>36</b></button>
|
||||
|
||||
@@ -96,9 +96,13 @@ p { color: var(--ink-650); line-height: 1.7; }
|
||||
.segmented button.is-active { color: #fff; background: var(--ink-800); }
|
||||
.screen-layout { display: grid; grid-template-columns: minmax(220px, 300px) minmax(0, 1fr); gap: clamp(18px, 2.5vw, 34px); align-items: start; }
|
||||
.screen-index { position: sticky; top: calc(var(--topbar) + 20px); max-height: calc(100vh - var(--topbar) - 40px); overflow: auto; background: var(--white); border: 1px solid var(--line); }
|
||||
.screen-index button { width: 100%; min-height: 52px; display: grid; grid-template-columns: 68px minmax(0,1fr); gap: 8px; align-items: center; padding: 9px 12px; text-align: left; color: var(--ink-650); background: transparent; border: 0; border-bottom: 1px solid var(--line); }
|
||||
.screen-index button { width: 100%; min-height: 58px; display: grid; grid-template-columns: 68px minmax(0,1fr) auto; gap: 8px; align-items: center; padding: 9px 12px; text-align: left; color: var(--ink-650); background: transparent; border: 0; border-bottom: 1px solid var(--line); }
|
||||
.screen-index button span { font-size: 11px; font-weight: 800; color: var(--copper); }
|
||||
.screen-index button b { overflow: hidden; font-size: 12px; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.screen-index button i { padding: 3px 5px; font-size: 9px; font-style: normal; font-weight: 800; white-space: nowrap; border: 1px solid var(--line); border-radius: 3px; }
|
||||
.coverage-dedicated { color: #fff; background: var(--secured); }
|
||||
.coverage-family { color: var(--ink-800); background: var(--paper-200); }
|
||||
.coverage-missing { color: #fff; background: #a54640; }
|
||||
.screen-index button.is-active { color: var(--ink-950); background: #f1ead8; box-shadow: inset 4px 0 var(--gold); }
|
||||
.screen-detail { min-width: 0; }
|
||||
.screen-stage-wrap { padding: clamp(8px, 1.2vw, 16px); background: #d9d4c7; border: 1px solid var(--line); box-shadow: var(--shadow); }
|
||||
@@ -107,6 +111,7 @@ p { color: var(--ink-650); line-height: 1.7; }
|
||||
.game-stage::after { content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none; background: linear-gradient(180deg, rgba(10,15,18,.48) 0, transparent 22%, transparent 70%, rgba(10,15,18,.68) 100%); }
|
||||
.game-stage.stage-light { color: var(--ink-950); background: #e6e0d2; }
|
||||
.game-stage.stage-light::after { background: linear-gradient(180deg, rgba(247,243,232,.2), rgba(247,243,232,.02) 55%, rgba(247,243,232,.55)); }
|
||||
.game-stage.stage-dedicated::after, .game-stage.stage-missing::after { display: none; }
|
||||
.game-stage button { min-height: 38px; color: inherit; border: 1px solid rgba(255,255,255,.34); border-radius: 4px; background: rgba(23,28,34,.84); }
|
||||
.hud-top { position: absolute; inset: 2.2% 2.4% auto; height: 8%; display: flex; justify-content: space-between; gap: 8px; align-items: center; }
|
||||
.hud-group { display: flex; gap: 7px; align-items: center; }
|
||||
@@ -163,6 +168,26 @@ p { color: var(--ink-650); line-height: 1.7; }
|
||||
.stage-generic-panel h3 { font-size: clamp(11px,1.3vw,20px); }
|
||||
.stage-generic-panel p { color: #dce4e8; font-size: clamp(8px,.78vw,12px); }
|
||||
.stage-list-row { padding: 5% 0; font-size: clamp(8px,.75vw,12px); border-bottom: 1px solid rgba(255,255,255,.15); }
|
||||
.boot-stage { position: absolute; inset: 0; display: grid; place-content: center; justify-items: center; padding: 8%; text-align: center; background: radial-gradient(circle at 50% 44%, #2f4650 0, #182127 43%, #0e1418 100%); }
|
||||
.boot-sigil { position: relative; width: clamp(72px, 9vw, 138px); aspect-ratio: 1; margin-bottom: 5%; border: 2px solid rgba(229,180,69,.5); transform: rotate(45deg); }
|
||||
.boot-sigil span, .boot-sigil i { position: absolute; display: block; background: var(--gold); box-shadow: 0 0 20px rgba(229,180,69,.45); }
|
||||
.boot-sigil span { left: 48%; top: 8%; width: 4%; height: 84%; }
|
||||
.boot-sigil i { left: 8%; top: 48%; width: 84%; height: 4%; }
|
||||
.boot-title small { color: #b8c5cc; font-size: clamp(8px,.8vw,12px); font-weight: 800; letter-spacing: .12em; }
|
||||
.boot-title strong { display: block; margin-top: 4px; font-size: clamp(23px,3vw,48px); }
|
||||
.boot-title p { margin: 7px 0 0; color: #d8e0e3; font-size: clamp(9px,.9vw,14px); }
|
||||
.boot-progress { width: min(48%, 420px); height: 5px; margin-top: 6%; overflow: hidden; background: rgba(255,255,255,.16); }
|
||||
.boot-progress i { display: block; width: 68%; height: 100%; background: var(--gold); }
|
||||
.boot-status { margin: 10px 0 0; color: #fff; font-size: clamp(9px,.8vw,13px); }
|
||||
.boot-auto { margin-top: 5px; color: #8f9ba2; font-size: clamp(8px,.7vw,11px); }
|
||||
.prototype-missing { position: absolute; inset: 0; display: grid; align-content: center; padding: 10% 12%; color: var(--ink-950); background: repeating-linear-gradient(-45deg, #f4efe3, #f4efe3 12px, #ede5d5 12px, #ede5d5 24px); }
|
||||
.prototype-missing > span { margin-bottom: 12px; color: #a54640; font-size: clamp(8px,.75vw,12px); font-weight: 900; }
|
||||
.prototype-missing > strong { max-width: 720px; font-size: clamp(20px,2.7vw,42px); line-height: 1.15; }
|
||||
.prototype-missing > p { max-width: 680px; margin: 10px 0 22px; font-size: clamp(10px,.9vw,14px); }
|
||||
.prototype-missing dl { width: min(100%, 760px); margin: 0; display: grid; grid-template-columns: repeat(2,minmax(0,1fr)); gap: 1px; background: var(--line); border: 1px solid var(--line); }
|
||||
.prototype-missing dl div { padding: 14px; background: rgba(255,254,249,.92); }
|
||||
.prototype-missing dt { margin-bottom: 6px; color: var(--copper); font-size: clamp(8px,.7vw,10px); font-weight: 900; }
|
||||
.prototype-missing dd { margin: 0; color: var(--ink-650); font-size: clamp(9px,.8vw,12px); line-height: 1.45; }
|
||||
.screen-meta { margin-top: 24px; padding: 0 2px; }
|
||||
.screen-meta-head { display: flex; justify-content: space-between; gap: 16px; align-items: start; }
|
||||
.screen-id { color: var(--copper); font-weight: 900; }
|
||||
@@ -265,13 +290,15 @@ p { color: var(--ink-650); line-height: 1.7; }
|
||||
.overview-hero img { aspect-ratio: 16/9; object-fit: contain; }
|
||||
.screen-layout { display: block; }
|
||||
.screen-index { position: static; display: flex; max-height: none; overflow-x: auto; margin-bottom: 14px; }
|
||||
.screen-index button { flex: 0 0 150px; grid-template-columns: 1fr; border-right: 1px solid var(--line); border-bottom: 0; }
|
||||
.screen-index button { flex: 0 0 170px; grid-template-columns: 1fr auto; border-right: 1px solid var(--line); border-bottom: 0; }
|
||||
.screen-index button > span { grid-column: 1 / -1; }
|
||||
.screen-index button.is-active { box-shadow: inset 0 -4px var(--gold); }
|
||||
.screen-index button b { white-space: normal; }
|
||||
.screen-stage-wrap { padding: 5px; }
|
||||
.screen-meta-head { display: block; }
|
||||
.pager { margin-top: 12px; }
|
||||
.spec-grid { grid-template-columns: 1fr; }
|
||||
.prototype-missing dl { grid-template-columns: 1fr; }
|
||||
.spec-cell { min-height: 0; }
|
||||
.chapter-tabs { display: flex; overflow-x: auto; }
|
||||
.chapter-tabs button { flex: 0 0 70px; }
|
||||
|
||||
@@ -63,6 +63,17 @@ async function main() {
|
||||
assert.equal(await page.locator(".screen-index button").count(), 82, "screen inventory count");
|
||||
const ids = await page.locator(".screen-index button span").allTextContents();
|
||||
assert.equal(new Set(ids).size, 82, "screen IDs must be unique");
|
||||
assert.equal(await page.locator('[data-coverage="dedicated"]').count(), 1, "dedicated page count");
|
||||
assert.equal(await page.locator('[data-coverage="family"]').count(), 40, "shared-family page count");
|
||||
assert.equal(await page.locator('[data-coverage="missing"]').count(), 41, "missing page count");
|
||||
await page.locator('[data-screen-id="SYS-001"]').first().click();
|
||||
assert.ok(await page.locator('[data-stage-id="SYS-001"] .boot-stage').isVisible(), "dedicated boot page");
|
||||
assert.equal(await page.locator('[data-stage-id="SYS-001"] button').count(), 0, "boot has no player action");
|
||||
assert.equal(await page.locator('[data-stage-id="SYS-001"]').getByText(/Cache|Saved|none/).count(), 0, "boot excludes unrelated HUD");
|
||||
await page.screenshot({ path: path.join(outputDir, "desktop-boot-screen.png"), fullPage: true });
|
||||
await page.locator('[data-screen-id="SYS-004"]').first().click();
|
||||
assert.ok(await page.locator('[data-stage-id="SYS-004"] .prototype-missing').isVisible(), "title gap is explicit");
|
||||
await page.screenshot({ path: path.join(outputDir, "desktop-title-gap.png"), fullPage: true });
|
||||
await page.locator('[data-screen-id="BAT-002"]').first().click();
|
||||
assert.ok(await page.locator('[data-stage-id="BAT-002"]').isVisible(), "battle screen stage");
|
||||
assert.ok(await page.locator(".battle-telegraph").isVisible(), "battle telegraph");
|
||||
|
||||
Reference in New Issue
Block a user