68 lines
5.2 KiB
PowerShell
68 lines
5.2 KiB
PowerShell
$ErrorActionPreference = 'Stop'
|
|
$root = Split-Path -Parent $PSScriptRoot
|
|
|
|
function Read-Utf8([string]$Path) {
|
|
[System.IO.File]::ReadAllText((Join-Path $root $Path), [System.Text.Encoding]::UTF8)
|
|
}
|
|
|
|
function Assert-Match([string]$Content, [string]$Pattern, [string]$Message) {
|
|
if ($Content -notmatch $Pattern) { throw $Message }
|
|
}
|
|
|
|
$button = Read-Utf8 'components/AppButton.vue'
|
|
$header = Read-Utf8 'components/PageHeader.vue'
|
|
$card = Read-Utf8 'components/GenealogyCard.vue'
|
|
$dialog = Read-Utf8 'components/AppDialog.vue'
|
|
$loading = Read-Utf8 'components/AppLoading.vue'
|
|
$toast = Read-Utf8 'components/AppToast.vue'
|
|
|
|
if ($button -match '(?im)(?<![-\w])position\s*:') {
|
|
throw 'AppButton must use grid overlay instead of positioning'
|
|
}
|
|
Assert-Match $button '(?s)\.app-button\s*\{[^}]*display:\s*inline-grid;[^}]*place-items:\s*center;' 'AppButton root must own the shared grid overlay'
|
|
Assert-Match $button '(?s)\.app-button--block\s*\{[^}]*display:\s*grid;' 'AppButton block variant must preserve the grid overlay'
|
|
Assert-Match $button '(?s)\.app-button__skin,\s*\.app-button__label\s*\{[^}]*grid-area:\s*1 / 1;' 'AppButton skin and label must share one grid cell'
|
|
|
|
Assert-Match $header '(?s)\.page-header--root \.header-side,\s*\.page-header--root \.header-title\s*\{[^}]*z-index:\s*2;' 'PageHeader root content must remain above decorations as flex items'
|
|
if ($header -match '(?s)\.page-header--root \.header-side,\s*\.page-header--root \.header-title\s*\{[^}]*position\s*:') {
|
|
throw 'PageHeader root content must not use positioning'
|
|
}
|
|
Assert-Match $header '(?s)\.header-icon-button\s*\{[^}]*display:\s*grid;[^}]*place-items:\s*center;' 'PageHeader icon button must use a grid overlay'
|
|
if ($header -match '(?s)\.header-icon-button\s*\{[^}]*position\s*:') {
|
|
throw 'PageHeader icon button must not be a positioning context'
|
|
}
|
|
Assert-Match $header '(?s)\.header-logo,\s*\.header-notice-icon,\s*\.notice-dot\s*\{[^}]*grid-area:\s*1 / 1;' 'PageHeader icon and notice dot must share one grid cell'
|
|
if ($header -match '(?s)\.notice-dot\s*\{[^}]*position\s*:') {
|
|
throw 'PageHeader notice dot must use grid alignment instead of positioning'
|
|
}
|
|
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'
|
|
|
|
if ($card -match '(?im)(?<![-\w])position\s*:') {
|
|
throw 'GenealogyCard must use grid instead of positioning'
|
|
}
|
|
Assert-Match $card '(?s)\.genealogy-card\s*\{[^}]*display:\s*grid;[^}]*grid-template-columns:\s*72rpx minmax\(0, 1fr\);[^}]*column-gap:\s*22rpx;' 'GenealogyCard must own the two-column document-flow grid'
|
|
Assert-Match $card '(?s)\.row-frame\s*\{[^}]*grid-area:\s*1 / 1 / 2 / -1;[^}]*z-index:\s*1;[^}]*width:\s*calc\(100% \+ var\(--card-padding-x\) \+ var\(--card-padding-x\)\);[^}]*height:\s*calc\(100% \+ var\(--card-padding-y\) \+ var\(--card-padding-y\)\);[^}]*margin:\s*calc\(-1 \* var\(--card-padding-y\)\) calc\(-1 \* var\(--card-padding-x\)\);' 'GenealogyCard row frame must span the padded card grid above the fixed page background'
|
|
Assert-Match $card '(?s)\.surname-seal\s*\{[^}]*display:\s*grid;[^}]*grid-row:\s*1;[^}]*z-index:\s*2;[^}]*place-items:\s*center;' 'GenealogyCard surname seal must share the frame row and stay above it'
|
|
Assert-Match $card '(?s)\.card-main\s*\{[^}]*grid-row:\s*1;[^}]*z-index:\s*2;' 'GenealogyCard content must share the frame row and stay above it'
|
|
Assert-Match $card '(?s)\.surname-seal-frame,\s*\.surname-seal-copy\s*\{[^}]*grid-area:\s*1 / 1;' 'GenealogyCard seal frame and copy must share one grid cell'
|
|
if ($card -match '(?s)\.card-name\s*\{[^}]*(?:overflow:\s*hidden|text-overflow:\s*ellipsis|white-space:\s*nowrap)') {
|
|
throw 'GenealogyCard name must wrap instead of clipping data-driven content'
|
|
}
|
|
foreach ($selector in @('card-meta', 'card-updated', 'card-role')) {
|
|
if ($card -match "(?s)\.$selector\s*\{[^}]*white-space:\s*nowrap") {
|
|
throw "GenealogyCard .$selector must wrap instead of fixing text capacity"
|
|
}
|
|
}
|
|
|
|
if ($dialog -match '(?s)\.app-dialog\s*\{[^}]*position\s*:|(?s)\.app-dialog__content\s*\{[^}]*position\s*:') {
|
|
throw 'AppDialog internal content must not use positioning'
|
|
}
|
|
Assert-Match $dialog '(?s)\.app-dialog\s*\{[^}]*background:\s*url\("/static/assets/modules/auth/transparent/a01-scroll-dialog-v3\.png"\)' 'AppDialog must render its approved dialog asset as the dialog background'
|
|
if ($loading -match '(?im)(?<![-\w])position\s*:') { throw 'AppLoading must stay in its parent document flow' }
|
|
if ($toast -match '(?s)\.app-toast__copy\s*\{[^}]*position\s*:') { throw 'AppToast copy must not create an unnecessary positioning context' }
|
|
|
|
Write-Output 'SHARED-COMPONENT-DOCUMENT-FLOW-CONTRACT PASS'
|