253 lines
21 KiB
PowerShell
253 lines
21 KiB
PowerShell
$ErrorActionPreference = 'Stop'
|
|
$root = Split-Path -Parent $PSScriptRoot
|
|
$requiredAssets = @(
|
|
'static/assets/foundation/transparent/brand-seal.png',
|
|
'static/assets/foundation/transparent/notice.png',
|
|
'static/assets/foundation/transparent/chevron-right.png',
|
|
'static/assets/foundation/transparent/meta-location.png',
|
|
'static/assets/foundation/transparent/meta-member.png',
|
|
'static/assets/foundation/transparent/meta-admin.png',
|
|
'static/assets/modules/genealogy/transparent/shortcut-tree.png',
|
|
'static/assets/modules/genealogy/transparent/shortcut-members.png',
|
|
'static/assets/modules/genealogy/transparent/shortcut-generation-poem.png',
|
|
'static/assets/modules/genealogy/transparent/shortcut-application.png',
|
|
'static/assets/modules/genealogy/transparent/current-seal-frame.png',
|
|
'static/assets/modules/genealogy/transparent/row-seal-frame.png',
|
|
'static/assets/modules/genealogy/transparent/add.png',
|
|
'static/assets/modules/genealogy/transparent/create-cloud.png',
|
|
'static/assets/foundation/transparent/tab-genealogy.png',
|
|
'static/assets/foundation/transparent/tab-genealogy-active.png',
|
|
'static/assets/foundation/transparent/tab-family.png',
|
|
'static/assets/foundation/transparent/tab-family-active.png',
|
|
'static/assets/foundation/transparent/tab-profile.png',
|
|
'static/assets/foundation/transparent/tab-profile-active.png',
|
|
'static/assets/modules/genealogy/transparent/current-slip-frame.png',
|
|
'static/assets/modules/genealogy/transparent/list-slip-frame.png',
|
|
'static/assets/modules/genealogy/transparent/g01-dialog-close.png',
|
|
'static/assets/modules/genealogy/transparent/g01-add-sheet-background-v3.png',
|
|
'static/assets/foundation/opaque/root-header-cinnabar.jpg',
|
|
'static/assets/modules/genealogy/opaque/genealogy-page-background-long.png',
|
|
'static/assets/modules/genealogy/transparent/section-divider.png'
|
|
)
|
|
|
|
foreach ($asset in $requiredAssets) {
|
|
if (-not (Test-Path -LiteralPath (Join-Path $root $asset))) {
|
|
throw "Missing G-01 visual asset: $asset"
|
|
}
|
|
}
|
|
|
|
$header = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'components/PageHeader.vue')
|
|
$tabbar = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'components/AppTabbar.vue')
|
|
|
|
if ($header -notmatch 'brand-seal\.png') { throw 'Header does not use the approved tight brand PNG.' }
|
|
if ($header -notmatch 'brand-seal\.png') { throw 'Header does not use the tight header Logo asset.' }
|
|
if ($header -notmatch 'notice\.png') { throw 'Header does not use the approved notice PNG.' }
|
|
if ($header -notmatch 'unreadCount\s*>\s*0') { throw 'Header badge is not conditional on unread count.' }
|
|
if ($header -notmatch 'class="header-hall"') { throw 'Header does not isolate the hall line-art as a low-contrast background layer.' }
|
|
if ($header -match 'background-image: url') { throw 'Header still applies hall line-art directly to the red header surface.' }
|
|
if ($header -notmatch 'opacity:\s*0?\.29') { throw 'Header hall line-art is not visible enough for the approved eave layer.' }
|
|
if ($header -notmatch 'root-header-cinnabar\.jpg') { throw 'Header does not use the approved cinnabar texture asset.' }
|
|
if ($header -notmatch 'height:\s*calc\(124rpx\s*\+\s*var\(--status-bar-height,\s*0px\)\)') { throw 'Header does not preserve the approved visual height above the system status bar.' }
|
|
if ($header -notmatch 'padding-top:\s*var\(--status-bar-height,\s*0px\)') { throw 'Header does not reserve the real system status-bar height.' }
|
|
if ($tabbar -match '/static/icons/tab-') { throw 'Tabbar still references rejected legacy tab assets.' }
|
|
if ($tabbar -notmatch 'safe-area-inset-bottom') { throw 'Tabbar has no Android safe-area padding.' }
|
|
if ($tabbar -notmatch 'font-size:\s*clamp\(17px, 30rpx, 22px\)') { throw 'Tabbar label size does not match the approved visual weight.' }
|
|
if ($tabbar -notmatch 'width:\s*64rpx') { throw 'Tabbar icon size does not match the approved visual weight.' }
|
|
|
|
$page = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'pages/genealogy/g01-my-genealogies.vue')
|
|
$profiles = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'styles/adaptive-frame-profiles.scss')
|
|
if ($page -match "from '@/utils/api\.js'") { throw 'G-01 visual phase must not call the remote API.' }
|
|
foreach ($state in @('isLoading', 'hasGenealogies', 'createdGenealogies', 'joinedGenealogies')) {
|
|
if ($page -notmatch $state) { throw "G-01 is missing presentation state: $state" }
|
|
}
|
|
foreach ($state in @('hasError', 'applicationRecords', 'addDialogVisible', 'switcherVisible')) {
|
|
if ($page -notmatch $state) { throw "G-01 is missing current acceptance state: $state" }
|
|
}
|
|
foreach ($required in @('class="add-dialog-layer"', 'class="genealogy-switcher-layer"', 'application-section')) {
|
|
if ($page -notmatch [regex]::Escape($required)) { throw "G-01 is missing current acceptance structure: $required" }
|
|
}
|
|
$closeCopy = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('5YWz6Zet'))
|
|
$cancelCopy = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('5Y+W5raI'))
|
|
$searchCopy = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('5pCc57Si5a626LCx'))
|
|
$inviteCopy = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('6YKA6K+356CB5Yqg5YWl'))
|
|
$continueCreateCopy = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('57un57ut5Yib5bu65a626LCx'))
|
|
$legacyCreateCopy = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('56Gu6K6k5rKh5pyJ546w5pyJ5a626LCx77yM57un57ut5Yib5bu6'))
|
|
$addMarkup = [regex]::Match($page, '(?s)<view\s+[^>]*v-if="addDialogVisible".*?<view\s+[^>]*v-if="switcherVisible"').Value
|
|
if (-not $addMarkup) { throw 'G-01 add sheet markup could not be isolated.' }
|
|
foreach ($token in @(
|
|
'class="add-dialog__body"',
|
|
'class="add-dialog__close-icon"',
|
|
'class="add-dialog__actions"',
|
|
'g01-dialog-close.png',
|
|
"aria-label=`"$closeCopy`""
|
|
)) {
|
|
if ($addMarkup -notmatch [regex]::Escape($token)) { throw "G-01 paper sheet is missing $token" }
|
|
}
|
|
if ($addMarkup -notmatch '(?s)<view class="add-dialog__body">\s*<view class="add-dialog__heading">.*?class="add-dialog__close".*?</view>\s*</view>\s*<view class="add-dialog__actions">') {
|
|
throw 'G-01 add sheet must keep the close control with the centered heading and actions body.'
|
|
}
|
|
if ($addMarkup -match 'add-dialog__paper|add-dialog__edge|page-paper\.jpg|a01-paper-transition-v1\.png') {
|
|
throw 'G-01 add sheet still assembles its background from separate paper and edge layers.'
|
|
}
|
|
if ($addMarkup -match 'a01-scroll-dialog-v3\.png') { throw 'G-01 add sheet still uses the rejected complete dialog frame.' }
|
|
foreach ($copy in @($closeCopy, $cancelCopy)) {
|
|
if ($addMarkup -match [regex]::Escape(">$copy</view>")) { throw 'G-01 add sheet still renders a text close control.' }
|
|
}
|
|
foreach ($copy in @($searchCopy, $inviteCopy, $continueCreateCopy)) {
|
|
$token = "label=`"$copy`""
|
|
if ($addMarkup -notmatch [regex]::Escape($token)) { throw "G-01 add sheet no longer reuses AppButton action $token" }
|
|
}
|
|
if ($addMarkup -match [regex]::Escape("label=`"$legacyCreateCopy`"")) {
|
|
throw 'G-01 add sheet still puts guidance copy inside the create button.'
|
|
}
|
|
if ($page -notmatch '(?s)\.add-dialog-layer\s*\{[^}]*align-items:\s*flex-end;[^}]*background:\s*rgba\(34,\s*20,\s*12,\s*0\.68\);') {
|
|
throw 'G-01 add sheet is not a bottom-aligned layer with the approved mask.'
|
|
}
|
|
if ($page -notmatch '(?s)\.add-dialog__close\s*\{[^}]*width:\s*80rpx;[^}]*height:\s*80rpx;') {
|
|
throw 'G-01 add sheet close control does not reserve the approved hit area.'
|
|
}
|
|
if ($page -notmatch '(?s)\.add-dialog\s*\{[^}]*min-height:\s*780rpx;[^}]*max-height:\s*calc\(100vh\s*-\s*80rpx\);') {
|
|
throw 'G-01 add sheet does not reach the approved arrow-aligned minimum height.'
|
|
}
|
|
if ($page -notmatch '(?s)\.add-dialog__content\s*\{[^}]*min-height:\s*780rpx;[^}]*max-height:\s*calc\(100vh\s*-\s*80rpx\);[^}]*padding:\s*96rpx\s+52rpx\s+calc\(96rpx\s*\+\s*env\(safe-area-inset-bottom\)\);') {
|
|
throw 'G-01 add sheet does not reserve the approved symmetric centering area.'
|
|
}
|
|
if ($page -notmatch '(?s)\.add-dialog__body\s*\{[^}]*margin:\s*auto\s+0;') {
|
|
throw 'G-01 add sheet body does not center safely with collapsible auto margins.'
|
|
}
|
|
if ($page -notmatch '(?s)\.add-dialog__heading\s*\{[^}]*display:\s*grid;[^}]*grid-template-columns:\s*minmax\(0,\s*1fr\)\s+96rpx;') {
|
|
throw 'G-01 add sheet heading does not reserve a document-flow close-control column.'
|
|
}
|
|
if ($page -match '(?s)\.add-dialog__heading\s*\{[^}]*top\s*:') {
|
|
throw 'G-01 add sheet heading must use real flow spacing instead of a visual offset.'
|
|
}
|
|
if ($page -notmatch '(?s)\.add-dialog__close\s*\{[^}]*grid-column:\s*2;[^}]*grid-row:\s*1\s*/\s*span\s+2;[^}]*margin-right:\s*-22rpx;') {
|
|
throw 'G-01 add sheet close control does not follow the heading grid.'
|
|
}
|
|
if ($page -notmatch '@include\s+adaptive-g01-add-sheet' -or $profiles -notmatch [regex]::Escape('/static/assets/modules/genealogy/transparent/g01-add-sheet-background-v3.png')) {
|
|
throw 'G-01 add sheet does not use the approved complete background asset.'
|
|
}
|
|
if ($profiles -notmatch '(?s)@mixin\s+adaptive-g01-add-sheet\s*\{[^}]*border-image-source:\s*url\("/static/assets/modules/genealogy/transparent/g01-add-sheet-background-v3\.png"\);[^}]*border-image-slice:\s*220\s+0\s+1\s+0\s+fill;[^}]*border-image-width:\s*118rpx\s+0\s+1rpx;') {
|
|
throw 'G-01 add sheet does not preserve the complete sheet top while stretching only the paper body.'
|
|
}
|
|
if ($page -notmatch '(?s)\.add-dialog__close-icon\s*\{[^}]*width:\s*80rpx;[^}]*height:\s*80rpx;') {
|
|
throw 'G-01 add sheet close icon canvas does not compensate for the asset transparent padding.'
|
|
}
|
|
if ($page -match '(?s)\.add-dialog\s+\.app-button\s*\{') { throw 'G-01 add sheet must not restyle the existing AppButton.' }
|
|
if ($page -notmatch '(?s)\.add-dialog__actions\s*\{[^}]*margin:\s*62rpx\s+-32rpx\s+0;') {
|
|
throw 'G-01 add sheet actions do not compensate for the existing button assets transparent side padding.'
|
|
}
|
|
if ($page -notmatch '(?s)\.add-dialog__actions\s*>\s*\.app-button\s*\{[^}]*width:\s*595rpx;[^}]*max-width:\s*100%;[^}]*min-height:\s*96rpx;') {
|
|
throw 'G-01 add sheet buttons do not keep the approved equal-width geometry.'
|
|
}
|
|
if ($page -notmatch '(?s)\.add-dialog__actions\s*\{[^}]*align-items:\s*center;') {
|
|
throw 'G-01 add sheet buttons are not centered after equal-width sizing.'
|
|
}
|
|
if ($page -notmatch '(?s)\.add-dialog__actions\s*>\s*\.app-button\s*\+\s*\.app-button\s*\{[^}]*margin-top:\s*24rpx;') {
|
|
throw 'G-01 add sheet buttons do not keep the approved 24rpx spacing.'
|
|
}
|
|
if ($page -notmatch '(?s)\.genealogy-switcher-layer\s*\{[^}]*align-items:\s*center;[^}]*padding:\s*40rpx;') {
|
|
throw 'G-01 switcher must remain a centered dialog.'
|
|
}
|
|
if ($page -match 'class="genealogy-switcher__skin"') {
|
|
throw 'G-01 switcher must not render the complete background as a fixed aspectFit image.'
|
|
}
|
|
if ($page -notmatch '(?s)\.genealogy-switcher\s*\{[^}]*@include\s+adaptive-g01-switcher;[^}]*min-height:\s*600rpx;[^}]*max-height:\s*calc\(100vh\s*-\s*120rpx\);' -or $profiles -notmatch '(?s)@mixin\s+adaptive-g01-switcher\s*\{[^}]*border-image-source:\s*url\("/static/assets/modules/auth/transparent/a01-scroll-dialog-v3\.png"\);[^}]*border-image-slice:\s*300\s+260\s+360\s+260\s+fill;[^}]*border-image-width:\s*110rpx\s+48rpx\s+132rpx\s+48rpx;') {
|
|
throw 'G-01 switcher does not use the approved stretchable complete background.'
|
|
}
|
|
if ($page -notmatch '(?s)\.genealogy-switcher__content\s*\{[^}]*min-height:\s*600rpx;[^}]*max-height:\s*calc\(100vh\s*-\s*120rpx\);[^}]*padding:\s*120rpx\s+58rpx\s+140rpx;') {
|
|
throw 'G-01 switcher content does not keep the approved decoration safe area.'
|
|
}
|
|
if ($page -notmatch 'class="genealogy-switcher__list"[^>]*scroll-y') {
|
|
throw 'G-01 switcher does not provide an independent scroll list.'
|
|
}
|
|
if ($page -notmatch '(?s)class="genealogy-switcher__close"[^>]*aria-label="\u5173\u95ed".*?g01-dialog-close\.png') {
|
|
throw 'G-01 switcher does not use the custom accessible close control.'
|
|
}
|
|
if ($page -match 'class="dialog-close"[^>]*@click="closeSwitcher"') {
|
|
throw 'G-01 switcher still exposes the obsolete bottom close copy.'
|
|
}
|
|
if ($page -notmatch 'import\s*\{[^}]*onBackPress[^}]*\}\s*from\s*"@dcloudio/uni-app"') {
|
|
throw 'G-01 add sheet does not import onBackPress.'
|
|
}
|
|
foreach ($nativeUi in @('uni.showToast', 'uni.showModal', 'uni.showLoading', 'uni.showActionSheet')) {
|
|
if ($page -match [regex]::Escape($nativeUi)) { throw "G-01 must not use native UniApp UI: $nativeUi" }
|
|
}
|
|
foreach ($asset in @('shortcut-tree.png', 'shortcut-members.png', 'shortcut-generation-poem.png', 'shortcut-application.png', 'add.png')) {
|
|
if ($page -notmatch [regex]::Escape($asset)) { throw "G-01 does not consume $asset" }
|
|
}
|
|
if ($page -notmatch '@include\s+adaptive-genealogy-current-slip' -or $profiles -notmatch 'current-slip-frame\.png') { throw 'G-01 does not consume the approved current genealogy frame profile.' }
|
|
foreach ($token in @('current-summary', 'current-meta-item', 'current-seal-frame', 'meta-location.png', 'meta-member.png', 'meta-admin.png', 'create-cloud.png')) {
|
|
if ($page -notmatch $token) { throw "G-01 current panel is missing $token" }
|
|
}
|
|
if ($page -notmatch '(?s)\.shortcut-icon\s*\{[^}]*width:\s*76rpx;[^}]*height:\s*76rpx;') { throw 'G-01 shortcut icons do not keep the compact readable size.' }
|
|
if ($page -notmatch '<GenealogyPageBackground\s*/>') { throw 'G-01 does not consume the shared genealogy background component.' }
|
|
if ($page -notmatch 'section-divider\.png') { throw 'G-01 does not consume the approved section divider asset.' }
|
|
$pageWithoutAddMarkup = $page.Replace($addMarkup, '')
|
|
foreach ($legacyBackground in @('page-paper.jpg', 'footer-mountain-bamboo.png', 'page-paper-texture', 'page-footer-landscape')) {
|
|
if ($pageWithoutAddMarkup -match [regex]::Escape($legacyBackground)) { throw "G-01 must replace the legacy layered page background: $legacyBackground" }
|
|
}
|
|
$backgroundComponent = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'components/GenealogyPageBackground.vue')
|
|
if ($backgroundComponent -notmatch 'mode="widthFix"') { throw 'G shared background must preserve the complete C artwork without horizontal cropping.' }
|
|
if ($backgroundComponent -match 'aspectFill|scaleToFill') { throw 'G shared background must not crop or stretch the selected C artwork.' }
|
|
if ($backgroundComponent -notmatch '(?s)\.genealogy-page-background\s*\{.*?position:\s*fixed;.*?inset:\s*0;.*?background:\s*#e7ded1;') { throw 'G shared background must extend the selected C artwork with the approved paper color.' }
|
|
if ($page -match 'background:\s*rgba\(255,\s*249,\s*238,\s*\.88\)') { throw 'G-01 current genealogy card retains a rectangular pale backing behind its transparent corners.' }
|
|
|
|
$card = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'components/GenealogyCard.vue')
|
|
if ($card -notmatch 'list-slip-frame\.png') { throw 'G-01 list rows do not use the approved slip frame asset.' }
|
|
if ($card -match 'background:\s*rgba\(255,\s*249,\s*238,\s*\.(9|96)\)') { throw 'G-01 list cards retain a rectangular pale backing behind their transparent corners.' }
|
|
foreach ($token in @('row-seal-frame.png', 'card-main', 'card-title-row', 'card-detail-row', 'card-updated')) {
|
|
if ($card -notmatch $token) { throw "G-01 list card is missing $token" }
|
|
}
|
|
if ($card -match 'background:\s*#b93b2e') { throw 'G-01 list seal is still a plain CSS red block.' }
|
|
foreach ($asset in @('meta-location.png', 'meta-member.png', 'meta-admin.png', 'current-seal-frame.png', 'row-seal-frame.png', 'create-cloud.png')) {
|
|
if ($page -notmatch [regex]::Escape($asset) -and $card -notmatch [regex]::Escape($asset)) {
|
|
throw "G-01 does not consume $asset"
|
|
}
|
|
}
|
|
|
|
$mock = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'data/mock.js')
|
|
$fixtures = @(
|
|
[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('bmFtZTogJ+axpOawj+WutuiwsSc=')),
|
|
[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('bG9jYXRpb246ICfmsrPljZfCt+a0m+mYsyc=')),
|
|
'memberCount: 158',
|
|
[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('bmFtZTogJ+axpOawj+Wul+iwsSc=')),
|
|
[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('bG9jYXRpb246ICflsbHkuJzCt+a1juWugSc=')),
|
|
'memberCount: 286'
|
|
)
|
|
foreach ($fixture in $fixtures) {
|
|
if (-not $mock.Contains($fixture)) { throw "G-01 visual fixture is missing $fixture" }
|
|
}
|
|
if ($page -notmatch 'current-info-divider') { throw 'G-01 current panel is missing the reference title-to-meta divider.' }
|
|
if ($card -match 'calendar-v1\.png') { throw 'G-01 list shows a calendar icon that does not exist in the reference.' }
|
|
if ($card -match 'genealogy\.surname') { throw 'G-01 list seal still shows a surname instead of the fixed 家谱 seal.' }
|
|
if ($card -match 'card-copy') { throw 'G-01 list still retains the superseded split content column.' }
|
|
if ($card -notmatch '(?s)\.card-title-row\s*\{.*?justify-content:\s*space-between') { throw 'G-01 list title, role, and chevron do not share the required first row.' }
|
|
if ($page -notmatch '(?s)\.current-switch-copy\s*\{[^}]*border-radius:\s*999rpx;[^}]*font-size:\s*clamp\(15px, 25rpx, 18px\)') { throw 'G-01 switch copy is not a compact visual action.' }
|
|
if ($page -notmatch '(?s)\.current-meta\s*\{[^}]*display:\s*grid;[^}]*grid-template-columns:\s*minmax\(0, 1fr\)\s+auto;[^}]*font-size:\s*clamp\(15px, 25rpx, 18px\)') { throw 'G-01 current metadata is not split into readable rows.' }
|
|
if ($page -notmatch '(?s)\.current-meta-item--location\s*\{[^}]*grid-column:\s*1\s*/\s*-1;') { throw 'G-01 location does not have its own metadata row.' }
|
|
if ($page -notmatch '(?s)\.current-meta-icon\s*\{[^}]*width:\s*34rpx;[^}]*height:\s*34rpx;[^}]*opacity:\s*1;') { throw 'G-01 current genealogy metadata icons do not use the compact readable size.' }
|
|
if ($page -notmatch '(?s)@media screen and \(max-width:\s*340px\)[^{]*\{.*?\.current-meta-icon\s*\{[^}]*width:\s*32rpx;[^}]*height:\s*32rpx;') { throw 'G-01 compact viewport metadata icons do not preserve the approved 32rpx size.' }
|
|
if ($card -notmatch '(?s)\.card-meta-icon\s*\{[^}]*width:\s*38rpx;[^}]*height:\s*38rpx;[^}]*flex:\s*0 0 auto;[^}]*opacity:\s*1;[^}]*filter:\s*saturate\(1\.35\) brightness\(0\.82\) contrast\(1\.15\);') { throw 'G-01 list location and member icons do not use the balanced compact size and contrast.' }
|
|
if ($card -notmatch '(?s)\.genealogy-card\s*\{[^}]*grid-template-columns:\s*68rpx\s+minmax\(0, 1fr\);[^}]*min-height:\s*204rpx;') { throw 'G-01 list cards do not reserve the readable three-row layout.' }
|
|
if ($card -notmatch '(?s)\.card-detail-row\s*\{[^}]*display:\s*grid;[^}]*grid-template-columns:\s*minmax\(0, 1fr\)\s+auto;') { throw 'G-01 list details are not arranged as a two-column grid.' }
|
|
if ($card -notmatch '(?s)<view class="card-detail-row">\s*<view class="card-meta-item card-meta-item--location">.*?</view>\s*<view class="card-meta-item card-meta-item--members">.*?</view>\s*<view class="card-trailing">') { throw 'G-01 list card must show location, member count, then role in the readable order.' }
|
|
if ($card -notmatch '(?s)\.card-meta-item--location\s*\{[^}]*grid-column:\s*1\s*/\s*-1;') { throw 'G-01 list location does not have its own full row.' }
|
|
if ($card -notmatch '(?s)\.card-trailing\s*\{[^}]*grid-column:\s*2;[^}]*flex-direction:\s*column;') { throw 'G-01 role and update date are not anchored to the member-count row.' }
|
|
if ($card -notmatch 'v-if="genealogy\.updatedAt"') { throw 'G-01 must not show an empty update-date label.' }
|
|
if ($card -notmatch '(?s)@media screen and \(max-width:\s*340px\)[^{]*\{.*?\.genealogy-card\s*\{[^}]*min-height:\s*176rpx;') { throw 'G-01 compact list cards do not reserve the readable layout.' }
|
|
if ($card -notmatch '(?s)\.card-meta\s*\{[^}]*font-size:\s*clamp\(15px, 24rpx, 18px\)') { throw 'G-01 list metadata does not use the approved readable size.' }
|
|
if ($card -notmatch '(?s)\.card-updated\s*\{[^}]*font-size:\s*clamp\(14px, 22rpx, 17px\)') { throw 'G-01 list update date does not use the secondary readable size.' }
|
|
if ($card -notmatch '(?s)\.card-role\s*\{[^}]*border-radius:\s*999rpx;[^}]*font-size:\s*clamp\(14px, 23rpx, 17px\)') { throw 'G-01 list role is not presented as a compact status tag.' }
|
|
if ($page -match '(?s)\.create-action\s*>\s*text\s*\{[^}]*white-space:\s*nowrap') { throw 'G-01 create action text must not use a fixed single-line capacity.' }
|
|
if ($page -notmatch '(?s)\.application-record\s*\+\s*\.application-record\s*\{[^}]*margin-top:\s*12rpx') { throw 'G-01 adjacent application cards must keep the approved 12rpx separation.' }
|
|
if ($page -notmatch '(?s)\.application-record__copy\s*\{[^}]*color:\s*#62584c;[^}]*font-size:\s*clamp\(16px, 28rpx, 20px\);[^}]*font-weight:\s*500;[^}]*line-height:\s*1\.4;') { throw 'G-01 application descriptions do not use the approved readable style.' }
|
|
if ($page -notmatch '(?s)\.application-record__status\s*\{[^}]*color:\s*#7f4f16;[^}]*font-size:\s*clamp\(16px, 27rpx, 20px\);[^}]*font-weight:\s*600;') { throw 'G-01 application statuses do not use the approved readable base style.' }
|
|
if ($page -notmatch '(?s)\.application-record__status--rejected\s*\{[^}]*color:\s*#a7160c;') { throw 'G-01 rejected status must preserve the approved semantic red.' }
|
|
if ($page -notmatch '(?s)\.application-record__status--muted\s*\{[^}]*color:\s*#62584c;') { throw 'G-01 muted status does not use the approved readable color.' }
|
|
|
|
$manifest = Get-Content -Raw -Encoding UTF8 (Join-Path $root 'manifest.json')
|
|
if ($manifest -notmatch '"statusbar"\s*:\s*\{\s*"immersed"\s*:\s*"supportedDevice"\s*,\s*"style"\s*:\s*"light"\s*,\s*"background"\s*:\s*"#B52E22"') { throw 'Android status bar is not configured for the cinnabar root header.' }
|
|
|
|
Write-Output 'PASS G-01 visual contract'
|