From e5e123cc846b8d6eb8bd7592b976bdefe0e398ed Mon Sep 17 00:00:00 2001 From: DaZuo0122 <1085701449@qq.com> Date: Fri, 13 Feb 2026 22:31:22 +0800 Subject: [PATCH] Add: config for controlling debug overlay visibility of tauri --- crates/sprimo-config/src/lib.rs | 4 ++ crates/sprimo-runtime-core/src/lib.rs | 32 +++++++++++ crates/sprimo-tauri/src/main.rs | 27 ++++++++- docs/CONFIG_REFERENCE.md | 2 + docs/IMPLEMENTATION_STATUS.md | 2 +- docs/TAURI_FRONTEND_DESIGN.md | 1 + docs/TAURI_RUNTIME_TESTING.md | 5 ++ frontend/tauri-ui/src/main.tsx | 81 +++++++++++++++++++-------- frontend/tauri-ui/src/styles.css | 20 +++++++ 9 files changed, 148 insertions(+), 26 deletions(-) diff --git a/crates/sprimo-config/src/lib.rs b/crates/sprimo-config/src/lib.rs index 9154ca6..14110ff 100644 --- a/crates/sprimo-config/src/lib.rs +++ b/crates/sprimo-config/src/lib.rs @@ -158,12 +158,14 @@ pub enum FrontendBackend { #[serde(default)] pub struct FrontendConfig { pub backend: FrontendBackend, + pub debug_overlay_visible: bool, } impl Default for FrontendConfig { fn default() -> Self { Self { backend: FrontendBackend::Bevy, + debug_overlay_visible: false, } } } @@ -222,10 +224,12 @@ mod tests { let mut config = AppConfig::default(); config.window.x = 42.0; config.frontend.backend = super::FrontendBackend::Tauri; + config.frontend.debug_overlay_visible = true; save(&path, &config).expect("save"); let (_, loaded) = load_or_create_at(&path).expect("reload"); assert!((loaded.window.x - 42.0).abs() < f32::EPSILON); assert_eq!(loaded.frontend.backend, super::FrontendBackend::Tauri); + assert!(loaded.frontend.debug_overlay_visible); } } diff --git a/crates/sprimo-runtime-core/src/lib.rs b/crates/sprimo-runtime-core/src/lib.rs index 9b5ed74..7ceda69 100644 --- a/crates/sprimo-runtime-core/src/lib.rs +++ b/crates/sprimo-runtime-core/src/lib.rs @@ -82,6 +82,28 @@ impl RuntimeCore { self.command_tx.clone() } + pub fn frontend_debug_overlay_visible(&self) -> Result { + let guard = self + .config + .read() + .map_err(|_| RuntimeCoreError::ConfigPoisoned)?; + Ok(guard.frontend.debug_overlay_visible) + } + + pub fn set_frontend_debug_overlay_visible( + &self, + visible: bool, + ) -> Result<(), RuntimeCoreError> { + { + let mut guard = self + .config + .write() + .map_err(|_| RuntimeCoreError::ConfigPoisoned)?; + guard.frontend.debug_overlay_visible = visible; + } + self.persist_config() + } + pub fn api_config(&self) -> ApiConfig { self.api_config.clone() } @@ -283,4 +305,14 @@ mod tests { let config = core.config().read().expect("config lock").clone(); assert!(!config.window.click_through); } + + #[test] + fn frontend_debug_overlay_visibility_roundtrips() { + let temp = TempDir::new().expect("tempdir"); + let path = temp.path().join("config.toml"); + let core = RuntimeCore::new_with_config(path, AppConfig::default(), CapabilityFlags::default()) + .expect("core init"); + core.set_frontend_debug_overlay_visible(true).expect("set"); + assert!(core.frontend_debug_overlay_visible().expect("get")); + } } diff --git a/crates/sprimo-tauri/src/main.rs b/crates/sprimo-tauri/src/main.rs index 839c60d..b1eabfd 100644 --- a/crates/sprimo-tauri/src/main.rs +++ b/crates/sprimo-tauri/src/main.rs @@ -121,6 +121,26 @@ fn load_active_sprite_pack(state: tauri::State<'_, AppState>) -> Result) -> Result { + state + .runtime_core + .frontend_debug_overlay_visible() + .map_err(|err| err.to_string()) +} + +#[tauri::command] +fn set_debug_overlay_visible( + state: tauri::State<'_, AppState>, + visible: bool, +) -> Result { + state + .runtime_core + .set_frontend_debug_overlay_visible(visible) + .map_err(|err| err.to_string())?; + Ok(visible) +} + fn main() -> Result<(), AppError> { tracing_subscriber::fmt() .with_env_filter("sprimo=info") @@ -141,7 +161,12 @@ fn main() -> Result<(), AppError> { tauri::Builder::default() .plugin(tauri_plugin_global_shortcut::Builder::new().build()) .manage(state) - .invoke_handler(tauri::generate_handler![current_state, load_active_sprite_pack]) + .invoke_handler(tauri::generate_handler![ + current_state, + load_active_sprite_pack, + debug_overlay_visible, + set_debug_overlay_visible + ]) .setup(|app| { let app_state: tauri::State<'_, AppState> = app.state(); let runtime_core = Arc::clone(&app_state.runtime_core); diff --git a/docs/CONFIG_REFERENCE.md b/docs/CONFIG_REFERENCE.md index 2e03d35..e9ef7ca 100644 --- a/docs/CONFIG_REFERENCE.md +++ b/docs/CONFIG_REFERENCE.md @@ -39,6 +39,7 @@ recovery_hotkey = "Ctrl+Alt+P" [frontend] backend = "bevy" +debug_overlay_visible = false ``` ## Notes @@ -48,3 +49,4 @@ backend = "bevy" - `window.click_through` is deprecated and ignored at runtime; it is always forced to `false`. - On Windows, `recovery_hotkey` now forces `visible = true` and `always_on_top = true` for recovery. - `frontend.backend` selects runtime frontend implementation (`bevy` or `tauri`). +- `frontend.debug_overlay_visible` controls whether tauri window debug diagnostics panel is shown. diff --git a/docs/IMPLEMENTATION_STATUS.md b/docs/IMPLEMENTATION_STATUS.md index df7801c..33dc388 100644 --- a/docs/IMPLEMENTATION_STATUS.md +++ b/docs/IMPLEMENTATION_STATUS.md @@ -19,7 +19,7 @@ Date: 2026-02-12 | Random backend API tester | Implemented | `scripts/random_backend_tester.py` with `just random-backend-test` and strict variant | | QA/documentation workflow | Implemented | `docs/QA_WORKFLOW.md`, issue/evidence templates, and `scripts/qa_validate.py` with `just qa-validate` | | Shared runtime core | Implemented | `sprimo-runtime-core` now backs both Tauri and Bevy startup, snapshot/config ownership, and API wiring | -| Tauri alternative frontend | In progress | `sprimo-tauri` now runs runtime-core/API + PixiJS sprite rendering shell; scale updates now auto-fit window to avoid top clipping | +| Tauri alternative frontend | In progress | `sprimo-tauri` now runs runtime-core/API + PixiJS sprite rendering shell; scale auto-fit and persisted debug-overlay toggle are implemented | | Tauri runtime testing workflow | Implemented | `docs/TAURI_RUNTIME_TESTING.md` defines strict workspace testing; packaged mode pending packaging support | ## Next Major Gaps diff --git a/docs/TAURI_FRONTEND_DESIGN.md b/docs/TAURI_FRONTEND_DESIGN.md index c45369a..8098196 100644 --- a/docs/TAURI_FRONTEND_DESIGN.md +++ b/docs/TAURI_FRONTEND_DESIGN.md @@ -47,6 +47,7 @@ Frontend: - Tauri backend exposes: - `current_state` command (structured snapshot DTO) - `load_active_sprite_pack` command (manifest + atlas as base64 data URL) + - `debug_overlay_visible` / `set_debug_overlay_visible` commands for persisted debug panel control - `runtime:snapshot` event after command application. - React/Vite frontend now renders sprite atlas frames with PixiJS and updates animation/scale from runtime snapshot events. diff --git a/docs/TAURI_RUNTIME_TESTING.md b/docs/TAURI_RUNTIME_TESTING.md index fb8b155..a964bca 100644 --- a/docs/TAURI_RUNTIME_TESTING.md +++ b/docs/TAURI_RUNTIME_TESTING.md @@ -74,6 +74,10 @@ An issue touching Tauri runtime behaviors must satisfy all requirements before ` - left-mouse drag moves the window - window remains non-resizable - moved position is reflected in runtime snapshot state (`x`, `y`) and persists after restart +9. Verify debug-overlay visibility control: +- default startup behavior follows `frontend.debug_overlay_visible` config +- `debug_overlay_visible`/`set_debug_overlay_visible` invoke commands toggle panel at runtime +- toggle state persists after restart ## API + Runtime Contract Checklist @@ -91,6 +95,7 @@ An issue touching Tauri runtime behaviors must satisfy all requirements before ` - `current_state` output parsed successfully. - `load_active_sprite_pack` returns expected fields. - `runtime:snapshot` event received on runtime command changes. +- `debug_overlay_visible` and `set_debug_overlay_visible` invoke commands work and persist config. ## Evidence Requirements diff --git a/frontend/tauri-ui/src/main.tsx b/frontend/tauri-ui/src/main.tsx index ff79fba..bde3638 100644 --- a/frontend/tauri-ui/src/main.tsx +++ b/frontend/tauri-ui/src/main.tsx @@ -48,6 +48,7 @@ async function fitWindowForScale(pack: UiSpritePack, scale: number): Promise(null); const [error, setError] = React.useState(null); + const [debugOverlayVisible, setDebugOverlayVisible] = React.useState(false); const hostRef = React.useRef(null); const rendererRef = React.useRef(null); const scaleFitRef = React.useRef(null); @@ -58,13 +59,15 @@ function App(): JSX.Element { let activePack: UiSpritePack | null = null; Promise.all([ invoke("load_active_sprite_pack"), - invoke("current_state") + invoke("current_state"), + invoke("debug_overlay_visible") ]) - .then(async ([pack, initialSnapshot]) => { + .then(async ([pack, initialSnapshot, showDebug]) => { if (!mounted) { return; } activePack = pack; + setDebugOverlayVisible(showDebug); setSnapshot(initialSnapshot); if (hostRef.current !== null) { rendererRef.current = await PixiPetRenderer.create( @@ -115,6 +118,32 @@ function App(): JSX.Element { }; }, []); + const toggleDebugOverlay = React.useCallback(async () => { + try { + const next = !debugOverlayVisible; + const persisted = await invoke("set_debug_overlay_visible", { + visible: next + }); + setDebugOverlayVisible(persisted); + } catch (err) { + setError(String(err)); + } + }, [debugOverlayVisible]); + + React.useEffect(() => { + const onKeyDown = (event: KeyboardEvent): void => { + if (!event.ctrlKey || !event.shiftKey || event.code !== "KeyD") { + return; + } + event.preventDefault(); + void toggleDebugOverlay(); + }; + window.addEventListener("keydown", onKeyDown); + return () => { + window.removeEventListener("keydown", onKeyDown); + }; + }, [toggleDebugOverlay]); + const onMouseDown = React.useCallback((event: React.MouseEvent) => { if (event.button !== 0) { return; @@ -127,28 +156,32 @@ function App(): JSX.Element { return (
-
-

sprimo-tauri

- {error !== null ?

{error}

: null} - {snapshot === null ? ( -

Loading snapshot...

- ) : ( -
-
state
-
{snapshot.state}
-
animation
-
{snapshot.current_animation}
-
pack
-
{snapshot.active_sprite_pack}
-
position
-
- {snapshot.x}, {snapshot.y} -
-
scale
-
{snapshot.scale}
-
- )} -
+ {error !== null && !debugOverlayVisible ?

{error}

: null} + {debugOverlayVisible ? ( +
+

sprimo-tauri

+

Toggle: Ctrl+Shift+D

+ {error !== null ?

{error}

: null} + {snapshot === null ? ( +

Loading snapshot...

+ ) : ( +
+
state
+
{snapshot.state}
+
animation
+
{snapshot.current_animation}
+
pack
+
{snapshot.active_sprite_pack}
+
position
+
+ {snapshot.x}, {snapshot.y} +
+
scale
+
{snapshot.scale}
+
+ )} +
+ ) : null}
); } diff --git a/frontend/tauri-ui/src/styles.css b/frontend/tauri-ui/src/styles.css index b2db418..d52aff5 100644 --- a/frontend/tauri-ui/src/styles.css +++ b/frontend/tauri-ui/src/styles.css @@ -53,3 +53,23 @@ dd { .error { color: #fecaca; } + +.hint { + margin: 0 0 8px; + font-size: 12px; + opacity: 0.8; +} + +.error-banner { + position: absolute; + top: 8px; + left: 8px; + max-width: 320px; + margin: 0; + padding: 8px 10px; + border: 1px solid rgba(255, 255, 255, 0.2); + background: rgba(127, 29, 29, 0.75); + border-radius: 8px; + color: #fee2e2; + font-size: 12px; +}