diff --git a/docs/RELEASE_TESTING.md b/docs/RELEASE_TESTING.md index 46ce343..102550a 100644 --- a/docs/RELEASE_TESTING.md +++ b/docs/RELEASE_TESTING.md @@ -142,6 +142,9 @@ Bevy window-management verification workflow: `docs/BEVY_WINDOW_VERIFICATION.md` - at large scale values (>= 1.8), full sprite remains visible without clipping 13. Verify packaged tauri frontend freshness: - confirm package run reflects latest `frontend/tauri-ui` changes (no stale embedded UI bundle) +14. Verify packaged tauri overlay edge and scale consistency: +- place overlay over dark background and confirm no white strip/background bleed on left edge +- for same slider value, confirm main-window size is consistent across `default`/`ferris`/`demogorgon` ### Packaged Mode (Required Once Tauri Packaging Exists) diff --git a/docs/TAURI_RUNTIME_TESTING.md b/docs/TAURI_RUNTIME_TESTING.md index 921cbd9..3e5e249 100644 --- a/docs/TAURI_RUNTIME_TESTING.md +++ b/docs/TAURI_RUNTIME_TESTING.md @@ -122,6 +122,9 @@ An issue touching Tauri runtime behaviors must satisfy all requirements before ` API mismatch) - verify window resize uses consistent coordinate units (no accumulated drift over 20 scale changes) - no runtime command/type error from position updates (e.g. `set_position` expects integer coords) +- at the same slider scale value, main window size is consistent across packs (`default`, `ferris`, + `demogorgon`) within 1px rounding tolerance +- no white strip/background bleed is visible along any overlay window edge on dark desktop background ## Settings Window Checklist diff --git a/frontend/tauri-ui/src/main.tsx b/frontend/tauri-ui/src/main.tsx index 46237d5..8c1e266 100644 --- a/frontend/tauri-ui/src/main.tsx +++ b/frontend/tauri-ui/src/main.tsx @@ -31,6 +31,8 @@ const SIZE_EPSILON = 0.5; const SCALE_EPSILON = 0.0001; const SCALE_MIN = 0.5; const SCALE_MAX = 3.0; +const LOGICAL_BASE_FRAME_WIDTH = 512; +const LOGICAL_BASE_FRAME_HEIGHT = 512; async function invokeSetSpritePack(packIdOrPath: string): Promise { return invoke("set_sprite_pack", { packIdOrPath }); @@ -49,29 +51,31 @@ async function invokeSetAlwaysOnTop(alwaysOnTop: boolean): Promise { } function fittedWindowSize( - frameWidth: number, - frameHeight: number, scale: number ): { width: number; height: number } { const safeScale = Number.isFinite(scale) && scale > 0 ? scale : 1; - const width = Math.round(Math.max(frameWidth * safeScale + WINDOW_PADDING, MIN_WINDOW_SIZE)); - const height = Math.round(Math.max(frameHeight * safeScale + WINDOW_PADDING, MIN_WINDOW_SIZE)); + const width = Math.round( + Math.max(LOGICAL_BASE_FRAME_WIDTH * safeScale + WINDOW_PADDING, MIN_WINDOW_SIZE) + ); + const height = Math.round( + Math.max(LOGICAL_BASE_FRAME_HEIGHT * safeScale + WINDOW_PADDING, MIN_WINDOW_SIZE) + ); return { width, height }; } -function effectiveScaleForWindowSize(pack: UiSpritePack, width: number, height: number): number { +function effectiveScaleForWindowSize(width: number, height: number): number { const availableWidth = Math.max(width - WINDOW_PADDING, MIN_WINDOW_SIZE); const availableHeight = Math.max(height - WINDOW_PADDING, MIN_WINDOW_SIZE); - const scaleByWidth = availableWidth / Math.max(pack.frame_width, 1); - const scaleByHeight = availableHeight / Math.max(pack.frame_height, 1); + const scaleByWidth = availableWidth / LOGICAL_BASE_FRAME_WIDTH; + const scaleByHeight = availableHeight / LOGICAL_BASE_FRAME_HEIGHT; const scale = Math.min(scaleByWidth, scaleByHeight); return Math.max(SCALE_MIN, Math.min(scale, SCALE_MAX)); } -async function fitWindowForScale(pack: UiSpritePack, scale: number): Promise { +async function fitWindowForScale(scale: number): Promise { const window = getCurrentWindow(); const [outerPosition, innerSize] = await Promise.all([window.outerPosition(), window.innerSize()]); - const target = fittedWindowSize(pack.frame_width, pack.frame_height, scale); + const target = fittedWindowSize(scale); const centerX = outerPosition.x + innerSize.width / 2; const centerY = outerPosition.y + innerSize.height / 2; let targetWidth = target.width; @@ -126,7 +130,7 @@ async function fitWindowForScale(pack: UiSpritePack, scale: number): Promise => { + const tryFitWindow = async (scale: number): Promise => { try { - return await fitWindowForScale(pack, scale); + return await fitWindowForScale(scale); } catch (err) { if (mountedRef.current) { setError(String(err)); @@ -212,13 +216,13 @@ function MainOverlayWindow(): JSX.Element { loadingPackRef.current = true; let reloaded = false; try { - const pack = await invoke("load_active_sprite_pack"); - reloaded = await recreateRenderer(pack, value); - if (reloaded) { - const effectiveScale = await tryFitWindow(pack, value.scale); - if (effectiveScale !== null) { - await syncEffectiveScale(value.scale, effectiveScale); - } + const pack = await invoke("load_active_sprite_pack"); + reloaded = await recreateRenderer(pack, value); + if (reloaded) { + const effectiveScale = await tryFitWindow(value.scale); + if (effectiveScale !== null) { + await syncEffectiveScale(value.scale, effectiveScale); + } if (mountedRef.current && effectiveScale !== null) { setError(null); } @@ -236,7 +240,7 @@ function MainOverlayWindow(): JSX.Element { if (activePackRef.current === null) { return; } - const effectiveScale = await tryFitWindow(activePackRef.current, value.scale); + const effectiveScale = await tryFitWindow(value.scale); if (effectiveScale !== null) { await syncEffectiveScale(value.scale, effectiveScale); } diff --git a/frontend/tauri-ui/src/styles.css b/frontend/tauri-ui/src/styles.css index c8ee658..7dd326e 100644 --- a/frontend/tauri-ui/src/styles.css +++ b/frontend/tauri-ui/src/styles.css @@ -2,13 +2,23 @@ font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; } +html, body { margin: 0; + padding: 0; + width: 100%; + height: 100%; background: transparent; color: #e2e8f0; overflow: hidden; } +#root { + width: 100%; + height: 100%; + background: transparent; +} + .app { width: 100vw; height: 100vh; diff --git a/issues/issue5.md b/issues/issue5.md new file mode 100644 index 0000000..2f2dda6 --- /dev/null +++ b/issues/issue5.md @@ -0,0 +1,92 @@ +## Title + +Tauri overlay shows left-edge white strip and same scale yields different window sizes by pack. + +## Severity + +P2 + +## Environment + +- OS: Windows +- App version/build: packaged release (`sprimo-tauri.exe`) +- Evidence screenshots: + - `issues/screenshots/issue5.png` + - `issues/screenshots/issue5-b.png` + - `issues/screenshots/issue5-c.png` + +## Summary + +Two regressions were observed in packaged Tauri runtime: + +1. A visible white strip appears on the overlay left edge on dark backgrounds. +2. At the same slider scale, main overlay window size differs by sprite pack. + +## Reproduction Steps + +1. Run packaged `sprimo-tauri.exe`. +2. Place overlay over a dark/black background. +3. Observe left window edge. +4. Open settings and set the same scale value on `default`, `ferris`, and `demogorgon`. +5. Compare main window footprint. + +## Expected Result + +- No white strip or white background bleed on transparent overlay edges. +- Same scale value produces the same main overlay window size regardless of pack. + +## Actual Result + +- Left edge can show a white strip. +- Window size differs across packs at same scale. + +## Root Cause Analysis + +1. Transparency chain was incomplete in CSS: +- `body` was transparent, but `html`/`#root` were not explicitly transparent/full-size, allowing + white background bleed at edges in transparent frameless window mode. +2. Scale-to-window mapping depended on per-pack frame size: +- window sizing used pack `frame_width/frame_height`, so identical scale values produced different + target sizes across packs. + +## Implementation Notes + +1. `frontend/tauri-ui/src/main.tsx` +- introduced canonical scale basis `512x512` for window sizing semantics. +- changed `fittedWindowSize` and `effectiveScaleForWindowSize` to use canonical dimensions. +- removed pack-dependent sizing from `fitWindowForScale`; pack remains used for rendering/splitting. +2. `frontend/tauri-ui/src/styles.css` +- made `html`, `body`, and `#root` explicit full-size transparent surfaces to avoid white bleed. +3. `docs/TAURI_RUNTIME_TESTING.md` +- added explicit checks for same-scale cross-pack window-size consistency and no edge white strip. +4. `docs/RELEASE_TESTING.md` +- added packaged verification steps for white-edge bleed and cross-pack same-scale size consistency. + +## Verification + +### Commands Run + +- [x] `npm --prefix frontend/tauri-ui run build` +- [x] `cargo check -p sprimo-tauri` +- [ ] `just build-release-tauri` +- [ ] `just package-win-tauri` +- [ ] `just smoke-win-tauri` + +### Visual Checklist + +- [x] Before screenshot(s): `issues/screenshots/issue5.png` +- [x] Before screenshot(s): `issues/screenshots/issue5-b.png` +- [x] Before screenshot(s): `issues/screenshots/issue5-c.png` +- [ ] After screenshot(s): `issues/screenshots/issue5-after-YYYYMMDD-HHMMSS.png` + +### Result + +- Status: `Fix Implemented` +- Notes: packaged runtime verification pending. + +## Status History + +- `2026-02-14 00:00` - reporter - `Reported` - left white strip + same-scale size inconsistency reported. +- `2026-02-14 00:00` - codex - `Triaged` - identified transparency chain and scale-basis coupling root causes. +- `2026-02-14 00:00` - codex - `Fix Implemented` - switched to canonical scale basis and full-surface transparency. +