diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 8b2c0a4..a13efb5 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -36,6 +36,9 @@ jobs: run: | raco pkg install --auto --no-docs "https://github.com/turinglambdaai/keel.git#$env:KEEL_PIN" raco pkg install --auto --no-docs "https://github.com/turinglambdaai/rivet.git#$env:RIVET_PIN" + - name: Verify Windows installer artwork + shell: pwsh + run: python scripts/check-windows-installer-bitmaps.py - name: Run executable acceptance contract shell: pwsh run: | diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 341d442..7835a05 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,6 +25,8 @@ jobs: run: bash scripts/check-release-version.sh - name: Verify dependency pins stay aligned run: bash scripts/check-dependency-pins.sh + - name: Verify Windows installer bitmap and checkbox surface + run: python3 scripts/check-windows-installer-bitmaps.py core: name: Racket core (contract tests) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3fba709..d550bc7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -102,6 +102,9 @@ jobs: - name: Version preflight (VERSION == rivet.rktd == updater == tag) shell: bash run: bash scripts/check-release-version.sh "${{ github.ref_name }}" + - name: Verify Windows installer checkbox surface + shell: pwsh + run: python scripts/check-windows-installer-bitmaps.py - name: Release (MSI + portable zip) shell: pwsh run: raco rivet release --without-updates --development diff --git a/assets/wizard-dialog.bmp b/assets/wizard-dialog.bmp index 3bcab09..e775df5 100644 Binary files a/assets/wizard-dialog.bmp and b/assets/wizard-dialog.bmp differ diff --git a/assets/wizard-dialog.png b/assets/wizard-dialog.png index 82c5a47..9604ce0 100644 Binary files a/assets/wizard-dialog.png and b/assets/wizard-dialog.png differ diff --git a/assets/wizard-dialog.svg b/assets/wizard-dialog.svg index de30f9a..cda33c1 100644 --- a/assets/wizard-dialog.svg +++ b/assets/wizard-dialog.svg @@ -11,4 +11,10 @@ taskly + + + diff --git a/scripts/check-windows-installer-bitmaps.py b/scripts/check-windows-installer-bitmaps.py new file mode 100644 index 0000000..a5c325a --- /dev/null +++ b/scripts/check-windows-installer-bitmaps.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +"""Verify WiX 5 completion dialog bitmap and native checkbox background.""" +from __future__ import annotations + +import struct +import zlib +from pathlib import Path + +ASSETS = Path(__file__).resolve().parents[1] / "assets" +WIDTH, HEIGHT = 493, 312 + + +def bmp_pixels() -> bytes: + source = (ASSETS / "wizard-dialog.bmp").read_bytes() + assert source[:2] == b"BM", "invalid BMP" + offset = struct.unpack_from(" bytes: + source = (ASSETS / "wizard-dialog.png").read_bytes() + assert source[:8] == b"\x89PNG\r\n\x1a\n" + pos, chunks = 8, [] + while pos < len(source): + size = struct.unpack_from(">I", source, pos)[0] + tag = source[pos + 4 : pos + 8] + payload = source[pos + 8 : pos + 8 + size] + if tag == b"IHDR": + assert struct.unpack_from(">IIBBBBB", payload) == ( + WIDTH, HEIGHT, 8, 2, 0, 0, 0 + ) + if tag == b"IDAT": + chunks.append(payload) + pos += size + 12 + raw = zlib.decompress(b"".join(chunks)) + stride = WIDTH * 3 + pixels = bytearray() + for y in range(HEIGHT): + offset = y * (stride + 1) + assert raw[offset] == 0, "PNG preview must use filter None" + pixels.extend(raw[offset + 1 : offset + 1 + stride]) + return bytes(pixels) + + +def main() -> None: + pixels = bmp_pixels() + assert png_pixels() == pixels, "preview PNG differs from shipping BMP" + row_width = WIDTH * 3 + for y in range(248, HEIGHT): + row = pixels[y * row_width : (y + 1) * row_width] + assert row == bytes((240, 240, 240)) * WIDTH, ( + f"Windows checkbox backing is not flat #F0F0F0 at y={y}" + ) + divider = pixels[247 * row_width : 248 * row_width] + assert divider == bytes((214, 210, 206)) * WIDTH + print("Windows MSI artwork OK: 493x312, flat native checkbox footer") + + +if __name__ == "__main__": + main() diff --git a/shared/spec/DESIGN-TOKENS.md b/shared/spec/DESIGN-TOKENS.md index 853904b..e4051a0 100644 --- a/shared/spec/DESIGN-TOKENS.md +++ b/shared/spec/DESIGN-TOKENS.md @@ -109,3 +109,18 @@ accent-filled white-text rounded button. No chrome field borders anywhere. White rounded square (macOS-squircle radius ≈ 22.5%) with a quiet checklist mark: one system-blue rounded bar over two neutral (`#CECED3`) bars, staggered widths. Monochrome, no gradients — must read on light and dark docks alike. + +## Windows MSI installer artwork + +WiX 5's `WixUI_InstallDir` shares `assets/wizard-dialog.bmp` (493×312) +between welcome and completion pages. Its `ExitDialog` optional checkbox is +**opaque** at x=135…355 and y=190…230 installer units, mapping to approximately +x=180…473 and y=253…307 on the bitmap. Gradients under this native control +produce a conspicuous rectangular patch. + +The image's **entire bottom footer** (y=248…311) is flat classic Windows +button-face `#F0F0F0`, with a subtle divider at y=247. The interactive +checkbox remains native and accessible, while its backing now blends into the +same surface. Keep SVG, PNG preview, and shipping BMP synchronized. +`scripts/check-windows-installer-bitmaps.py` verifies pixel identity and +native checkbox surface in CI, packaged acceptance and Windows release.