diff --git a/.superpowers/sdd/progress.md b/.superpowers/sdd/progress.md new file mode 100644 index 0000000..d6e52a9 --- /dev/null +++ b/.superpowers/sdd/progress.md @@ -0,0 +1,9 @@ +# SDD Progress + +Plan: docs/superpowers/plans/2026-07-21-page-header-safe-area-g03.md +Branch: fix/page-header-safe-area +Base: 779a442 + +Task 1: complete (commits 779a442..2f22df6, review clean) +Task 2: complete (commits 2f22df6..a0d19f4, review clean after strict-regex fix) +Task 3: complete (verification only at a0d19f4, review clean; 8 contracts + 2 H5 smokes pass) diff --git a/.superpowers/sdd/review-task-1-779a442..2f22df6.diff b/.superpowers/sdd/review-task-1-779a442..2f22df6.diff new file mode 100644 index 0000000..086e293 --- /dev/null +++ b/.superpowers/sdd/review-task-1-779a442..2f22df6.diff @@ -0,0 +1,143 @@ +# Review package: 779a442..2f22df6 + +## Commits +2f22df6 fix: adapt shared page header to safe area + +## Files changed + components/PageHeader.vue | 12 ++++++++++-- + tests/shared-component-document-flow-contract.ps1 | 4 ++++ + tests/shared-interaction-accessibility-contract.ps1 | 10 ++++++++++ + 3 files changed, 24 insertions(+), 2 deletions(-) + +## Diff +diff --git a/components/PageHeader.vue b/components/PageHeader.vue +index 5ef28dd..17ab7ef 100644 +--- a/components/PageHeader.vue ++++ b/components/PageHeader.vue +@@ -67,52 +67,60 @@ + + + + + + ').Groups[1].Value + $positions = [regex]::Matches([regex]::Replace($style, '(?s)/\*.*?\*/', ''), '(?m)\bposition\s*:\s*([^;]+);') + if ($positions.Count -ne 1 -or $positions[0].Groups[1].Value.Trim() -ne 'fixed') { + throw "G03 must retain only its combined fixed dialog layer rule; found $($positions.Count) position declarations" + } + foreach ($required in @( +- 'background: url("/static/assets/foundation/opaque/root-header-cinnabar.jpg")', + 'background: url("/static/assets/modules/genealogy/transparent/g01-empty-panel-frame.png")', + 'background: url("/static/assets/modules/auth/transparent/a01-scroll-dialog-v3.png")', + 'background: url("/static/assets/foundation/transparent/a01-scroll-primary-v3.png")' + )) { if ($page -notmatch [regex]::Escape($required)) { throw "G03 is missing real asset background: $required" } } ++foreach ($forbidden in @('flow-header', 'root-header-cinnabar.jpg')) { ++ if ($page -match [regex]::Escape($forbidden)) { throw "G03 must not retain private header token: $forbidden" } ++} + Write-Output 'G03-DOCUMENT-FLOW-CONTRACT PASS' +diff --git a/tests/g03-visual-states-contract.ps1 b/tests/g03-visual-states-contract.ps1 +index 5342acb..cfb7911 100644 +--- a/tests/g03-visual-states-contract.ps1 ++++ b/tests/g03-visual-states-contract.ps1 +@@ -2,22 +2,22 @@ $ErrorActionPreference = 'Stop' + + $root = Split-Path -Parent $PSScriptRoot + $g03 = Get-Content -LiteralPath (Join-Path $root 'pages/genealogy/g03-create-genealogy.vue') -Raw -Encoding utf8 + + function Assert-Match { + param([string]$Content, [string]$Pattern, [string]$Message) + if ($Content -notmatch $Pattern) { throw $Message } + } + + if ($g03 -match '返回') { throw 'G03 header must use only the approved image back arrow' } +-Assert-Match -Content $g03 -Pattern '(?s)\.flow-header__back,\s*\.flow-header__side\s*\{[^}]*width:\s*72rpx;' -Message 'G03 header side slots must preserve centered title after removing back text' +-Assert-Match -Content $g03 -Pattern '(?s)\.flow-header__back-icon\s*\{[^}]*width:\s*34rpx;[^}]*height:\s*34rpx;' -Message 'G03 back arrow must use the approved visible size' ++Assert-Match -Content $g03 -Pattern '(?s)' -Message 'G03 must use PageHeader with its dynamic title and custom back handler' ++if ($g03 -match 'flow-header|flow-header__back|flow-header__title') { throw 'G03 must not retain a private header implementation' } + Assert-Match -Content $g03 -Pattern '(?s)\.flow-note,\s*\.flow-rule__note\s*\{[^}]*font-size:\s*25rpx;' -Message 'G03 guidance copy must use the approved readable size' + Assert-Match -Content $g03 -Pattern '(?s)\.flow-rule__option\s*\{[^}]*font-size:\s*23rpx;' -Message 'G03 visibility options must use the approved readable size' + Assert-Match -Content $g03 -Pattern '(?s)\.field-error\s*\{[^}]*font-size:\s*24rpx;[^}]*line-height:\s*34rpx;' -Message 'G03 field errors must use the approved readable size' + Assert-Match -Content $g03 -Pattern '(?s)\.flow-error\s*\{[^}]*font-size:\s*25rpx;[^}]*line-height:\s*36rpx;' -Message 'G03 submit errors must use the approved readable size' + + foreach ($asset in @('a01-scroll-dialog-v3.png', 'a01-scroll-primary-v3.png', 'a01-scroll-secondary-v3.png')) { + if ($g03 -notmatch [regex]::Escape($asset)) { throw "G03 must retain approved shared asset: $asset" } + } + + Write-Output 'G03-VISUAL-STATES-CONTRACT PASS' diff --git a/.superpowers/sdd/review-task-2-rereview-2f22df6..a0d19f4.diff b/.superpowers/sdd/review-task-2-rereview-2f22df6..a0d19f4.diff new file mode 100644 index 0000000..82c65d9 --- /dev/null +++ b/.superpowers/sdd/review-task-2-rereview-2f22df6..a0d19f4.diff @@ -0,0 +1,230 @@ +# Review package: 2f22df6..a0d19f4 + +## Commits +a0d19f4 test: tighten G03 shared header contract +9362c47 refactor: reuse shared header on G03 + +## Files changed + pages/genealogy/g03-create-genealogy.vue | 62 ++++---------------------------- + tests/g03-create-flow-contract.ps1 | 9 +++-- + tests/g03-document-flow-contract.ps1 | 4 ++- + tests/g03-visual-states-contract.ps1 | 4 +-- + 4 files changed, 17 insertions(+), 62 deletions(-) + +## Diff +diff --git a/pages/genealogy/g03-create-genealogy.vue b/pages/genealogy/g03-create-genealogy.vue +index 2bc757d..f37cadc 100644 +--- a/pages/genealogy/g03-create-genealogy.vue ++++ b/pages/genealogy/g03-create-genealogy.vue +@@ -1,30 +1,20 @@ + + + + + + ').Groups[1].Value + $positions = [regex]::Matches([regex]::Replace($style, '(?s)/\*.*?\*/', ''), '(?m)\bposition\s*:\s*([^;]+);') + if ($positions.Count -ne 1 -or $positions[0].Groups[1].Value.Trim() -ne 'fixed') { + throw "G03 must retain only its combined fixed dialog layer rule; found $($positions.Count) position declarations" + } + foreach ($required in @( +- 'background: url("/static/assets/foundation/opaque/root-header-cinnabar.jpg")', + 'background: url("/static/assets/modules/genealogy/transparent/g01-empty-panel-frame.png")', + 'background: url("/static/assets/modules/auth/transparent/a01-scroll-dialog-v3.png")', + 'background: url("/static/assets/foundation/transparent/a01-scroll-primary-v3.png")' + )) { if ($page -notmatch [regex]::Escape($required)) { throw "G03 is missing real asset background: $required" } } ++foreach ($forbidden in @('flow-header', 'root-header-cinnabar.jpg')) { ++ if ($page -match [regex]::Escape($forbidden)) { throw "G03 must not retain private header token: $forbidden" } ++} + Write-Output 'G03-DOCUMENT-FLOW-CONTRACT PASS' +diff --git a/tests/g03-visual-states-contract.ps1 b/tests/g03-visual-states-contract.ps1 +index 5342acb..d539bc5 100644 +--- a/tests/g03-visual-states-contract.ps1 ++++ b/tests/g03-visual-states-contract.ps1 +@@ -2,22 +2,22 @@ $ErrorActionPreference = 'Stop' + + $root = Split-Path -Parent $PSScriptRoot + $g03 = Get-Content -LiteralPath (Join-Path $root 'pages/genealogy/g03-create-genealogy.vue') -Raw -Encoding utf8 + + function Assert-Match { + param([string]$Content, [string]$Pattern, [string]$Message) + if ($Content -notmatch $Pattern) { throw $Message } + } + + if ($g03 -match '返回') { throw 'G03 header must use only the approved image back arrow' } +-Assert-Match -Content $g03 -Pattern '(?s)\.flow-header__back,\s*\.flow-header__side\s*\{[^}]*width:\s*72rpx;' -Message 'G03 header side slots must preserve centered title after removing back text' +-Assert-Match -Content $g03 -Pattern '(?s)\.flow-header__back-icon\s*\{[^}]*width:\s*34rpx;[^}]*height:\s*34rpx;' -Message 'G03 back arrow must use the approved visible size' ++Assert-Match -Content $g03 -Pattern '(?s)' -Message 'G03 must use PageHeader with its dynamic title and custom back handler' ++if ($g03 -match 'flow-header|flow-header__back|flow-header__title') { throw 'G03 must not retain a private header implementation' } + Assert-Match -Content $g03 -Pattern '(?s)\.flow-note,\s*\.flow-rule__note\s*\{[^}]*font-size:\s*25rpx;' -Message 'G03 guidance copy must use the approved readable size' + Assert-Match -Content $g03 -Pattern '(?s)\.flow-rule__option\s*\{[^}]*font-size:\s*23rpx;' -Message 'G03 visibility options must use the approved readable size' + Assert-Match -Content $g03 -Pattern '(?s)\.field-error\s*\{[^}]*font-size:\s*24rpx;[^}]*line-height:\s*34rpx;' -Message 'G03 field errors must use the approved readable size' + Assert-Match -Content $g03 -Pattern '(?s)\.flow-error\s*\{[^}]*font-size:\s*25rpx;[^}]*line-height:\s*36rpx;' -Message 'G03 submit errors must use the approved readable size' + + foreach ($asset in @('a01-scroll-dialog-v3.png', 'a01-scroll-primary-v3.png', 'a01-scroll-secondary-v3.png')) { + if ($g03 -notmatch [regex]::Escape($asset)) { throw "G03 must retain approved shared asset: $asset" } + } + + Write-Output 'G03-VISUAL-STATES-CONTRACT PASS' diff --git a/.superpowers/sdd/review-task-3-a0d19f4..a0d19f4.diff b/.superpowers/sdd/review-task-3-a0d19f4..a0d19f4.diff new file mode 100644 index 0000000..bbdf4e1 --- /dev/null +++ b/.superpowers/sdd/review-task-3-a0d19f4..a0d19f4.diff @@ -0,0 +1,7 @@ +# Review package: a0d19f4..a0d19f4 + +## Commits + +## Files changed + +## Diff diff --git a/.superpowers/sdd/task-1-brief.md b/.superpowers/sdd/task-1-brief.md new file mode 100644 index 0000000..1c5e4e4 --- /dev/null +++ b/.superpowers/sdd/task-1-brief.md @@ -0,0 +1,116 @@ +### Task 1: 为公共 PageHeader 建立安全区与返回契约 + +**Files:** +- Modify: `tests/shared-component-document-flow-contract.ps1` +- Modify: `tests/shared-interaction-accessibility-contract.ps1` +- Modify: `components/PageHeader.vue` + +**Interfaces:** +- Consumes: uni-app 提供的 `var(--status-bar-height, 0px)`、`getCurrentPages()`、`uni.navigateBack()`、`uni.reLaunch()`。 +- Produces: `customBack: Boolean = false` 属性和 `back` 事件;普通页头及占位槽均使用安全区总高度。 + +- [ ] **Step 1: 写入普通页头安全区的失败契约** + +在 `tests/shared-component-document-flow-contract.ps1` 的 PageHeader 断言中加入: + +```powershell +Assert-Match $header '(?s)\.page-header-slot\s*\{[^}]*height:\s*calc\(104rpx \+ var\(--status-bar-height, 0px\)\);' 'PageHeader secondary slot must reserve status bar plus 104rpx content height' +Assert-Match $header '(?s)\.page-header\s*\{[^}]*height:\s*calc\(104rpx \+ var\(--status-bar-height, 0px\)\);[^}]*padding-top:\s*var\(--status-bar-height, 0px\);' 'PageHeader secondary header must place its 104rpx content below the status bar' +Assert-Match $header '(?s)\.page-header-slot--root\s*\{[^}]*height:\s*calc\(124rpx \+ var\(--status-bar-height, 0px\)\);' 'PageHeader root slot height must remain unchanged' +Assert-Match $header '(?s)\.page-header--root\s*\{[^}]*height:\s*calc\(124rpx \+ var\(--status-bar-height, 0px\)\);' 'PageHeader root height must remain unchanged' +``` + +- [ ] **Step 2: 写入自定义返回的失败契约** + +在 `tests/shared-interaction-accessibility-contract.ps1` 的 PageHeader 检查中加入: + +```powershell +foreach ($token in @( + 'customBack: { type: Boolean, default: false }', + 'const emit = defineEmits(["brand", "notice", "action", "back"]);', + 'if (props.customBack) {', + 'emit("back");', + 'uni.navigateBack();', + 'uni.reLaunch({ url: props.fallbackUrl });' +)) { + if (-not $header.Contains($token)) { throw "PageHeader back contract missing: $token" } +} +``` + +- [ ] **Step 3: 运行聚焦契约并确认 RED** + +Run: + +```powershell +& powershell -ExecutionPolicy Bypass -File tests/shared-component-document-flow-contract.ps1 +& powershell -ExecutionPolicy Bypass -File tests/shared-interaction-accessibility-contract.ps1 +``` + +Expected: 两项均 FAIL,分别指出普通页头仍为固定 `104rpx`、`customBack` 合同不存在。 + +- [ ] **Step 4: 实现最小公共组件改动** + +在 `components/PageHeader.vue` 中补充属性和事件: + +```js +const props = defineProps({ + title: { type: String, required: true }, + action: { type: String, default: "" }, + root: { type: Boolean, default: false }, + unreadCount: { type: Number, default: 0 }, + fallbackUrl: { type: String, default: "/pages/genealogy/g01-my-genealogies" }, + customBack: { type: Boolean, default: false }, +}); + +const emit = defineEmits(["brand", "notice", "action", "back"]); + +const goBack = () => { + if (props.customBack) { + emit("back"); + return; + } + const stack = getCurrentPages(); + if (stack.length > 1) { + uni.navigateBack(); + return; + } + uni.reLaunch({ url: props.fallbackUrl }); +}; +``` + +把普通变体的尺寸改为: + +```scss +.page-header-slot { + height: calc(104rpx + var(--status-bar-height, 0px)); + flex: none; +} + +.page-header { + height: calc(104rpx + var(--status-bar-height, 0px)); + padding: 0 24rpx; + padding-top: var(--status-bar-height, 0px); +} +``` + +保留 `.page-header-slot--root` 与 `.page-header--root` 的现有 `124rpx` 规则不变。 + +- [ ] **Step 5: 运行聚焦契约并确认 GREEN** + +Run: + +```powershell +& powershell -ExecutionPolicy Bypass -File tests/shared-component-document-flow-contract.ps1 +& powershell -ExecutionPolicy Bypass -File tests/shared-interaction-accessibility-contract.ps1 +git diff --check +``` + +Expected: 两项输出 `PASS`,`git diff --check` 退出码为 0。 + +- [ ] **Step 6: 提交公共组件改动** + +```powershell +git add components/PageHeader.vue tests/shared-component-document-flow-contract.ps1 tests/shared-interaction-accessibility-contract.ps1 +git commit -m "fix: adapt shared page header to safe area" +``` + diff --git a/.superpowers/sdd/task-1-report.md b/.superpowers/sdd/task-1-report.md new file mode 100644 index 0000000..3af5a92 --- /dev/null +++ b/.superpowers/sdd/task-1-report.md @@ -0,0 +1,56 @@ +# Task 1 Report + +Status: DONE + +## Changed files + +- `components/PageHeader.vue` +- `tests/shared-component-document-flow-contract.ps1` +- `tests/shared-interaction-accessibility-contract.ps1` + +## RED + +Command: + +```powershell +& powershell -ExecutionPolicy Bypass -File tests/shared-component-document-flow-contract.ps1 +& powershell -ExecutionPolicy Bypass -File tests/shared-interaction-accessibility-contract.ps1 +``` + +Failure evidence: + +- `PageHeader secondary slot must reserve status bar plus 104rpx content height` +- `PageHeader back contract missing: customBack: { type: Boolean, default: false }` + +Both contract scripts exited with code `1`, for the expected missing behavior. + +## GREEN + +Command: + +```powershell +& powershell -ExecutionPolicy Bypass -File tests/shared-component-document-flow-contract.ps1 +& powershell -ExecutionPolicy Bypass -File tests/shared-interaction-accessibility-contract.ps1 +git diff --check +``` + +Result summary: + +- `SHARED-COMPONENT-DOCUMENT-FLOW-CONTRACT PASS` +- `SHARED-INTERACTION-ACCESSIBILITY-CONTRACT PASS` +- `git diff --check` exited `0` + +## Commit + +`2f22df6 fix: adapt shared page header to safe area` + +## Self-review + +- Normal PageHeader slot and header use `calc(104rpx + var(--status-bar-height, 0px))`; the header applies status-bar top padding. +- Root slot and header retain their `calc(124rpx + var(--status-bar-height, 0px))` rules. +- `customBack` emits `back` and returns before navigation; normal navigation retains stack-aware back and fallback relaunch behavior. +- The added contract tests cover the required exact public interface and layout declarations. + +## Concerns + +None. Git emitted existing line-ending conversion warnings during checks and commit, but all required checks passed. diff --git a/.superpowers/sdd/task-2-brief.md b/.superpowers/sdd/task-2-brief.md new file mode 100644 index 0000000..05fdf23 --- /dev/null +++ b/.superpowers/sdd/task-2-brief.md @@ -0,0 +1,98 @@ +### Task 2: 用公共 PageHeader 替换 G03 自建页头 + +**Files:** +- Modify: `tests/g03-visual-states-contract.ps1` +- Modify: `tests/g03-create-flow-contract.ps1` +- Modify: `tests/g03-document-flow-contract.ps1` +- Modify: `pages/genealogy/g03-create-genealogy.vue` + +**Interfaces:** +- Consumes: Task 1 的 ``。 +- Produces: G03 动态标题 `isAncestorStep ? "录入首代人物" : "创建家谱"`,以及现有 `goBack()` 对第一、第二步的业务分流。 + +- [ ] **Step 1: 将 G03 契约改成公共页头要求** + +在 `tests/g03-visual-states-contract.ps1` 删除 `.flow-header__back`、`.flow-header__side` 和 `.flow-header__back-icon` 尺寸断言,改为: + +```powershell +Assert-Match -Content $g03 -Pattern '(?s)' -Message 'G03 must use PageHeader with its dynamic title and custom back handler' +if ($g03 -match 'flow-header|flow-header__back|flow-header__title') { throw 'G03 must not retain a private header implementation' } +``` + +在 `tests/g03-create-flow-contract.ps1` 的 required 项中加入: + +```powershell +'import PageHeader from "@/components/PageHeader.vue";', +' +``` + +在 `