From 8a606ce34869d93d220adefdcef13f61200b8eff Mon Sep 17 00:00:00 2001 From: karashiiro <49822414+karashiiro@users.noreply.github.com> Date: Mon, 5 Jan 2026 07:55:55 -0800 Subject: [PATCH] Fix warnings and update animation docs --- CONTRIBUTING.md | 2 +- README.md | 6 +-- docs/design/animation-system.md | 23 ++++++++++- docs/design/frontend-architecture.md | 2 +- tiny-workshop/game/src/backend.rs | 11 ++--- tiny-workshop/game/src/chat_ui.rs | 3 ++ tiny-workshop/game/src/credential_manager.rs | 12 +++--- tiny-workshop/game/src/ipc.rs | 3 ++ tiny-workshop/game/src/lib.rs | 42 +++++++++----------- 9 files changed, 64 insertions(+), 40 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ceb22a5..2cdc55f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -30,12 +30,12 @@ Tiny Workshop is a 3D interactive AI agent application featuring a Victorian stu - IPC communication between frontend and backend - Cross-platform support (Windows, macOS, Linux) - HTTP debug server for automated testing +- Animation state system (placeholder cube with color-coded states) **Planned:** - 3D Victorian study environment with AI character - Interactive room system with notes, images, and other interactables - Session persistence and logging -- Animation system for character interactions ## Architecture diff --git a/README.md b/README.md index 9486d1e..bd61640 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ tiny-workshop/ **Current Phase**: Active Development -Core infrastructure is implemented and functional. The project has working IPC communication, authentication, and chat UI. +Core infrastructure is implemented and functional. The project has working IPC communication, authentication, chat UI, and animation state system. ### What's Implemented @@ -89,6 +89,7 @@ Core infrastructure is implemented and functional. The project has working IPC c - Fyrox-UI chat interface with streaming message support - OS keychain integration (Windows Credential Manager, macOS Keychain, Linux Secret Service) - OAuth authentication flow +- Animation state system (placeholder cube with color-coded states for idle, working, speaking) - HTTP debug server for automated testing (feature-gated) **Backend (TypeScript)** @@ -99,9 +100,8 @@ Core infrastructure is implemented and functional. The project has working IPC c ### Not Yet Implemented - Room system (surfaces, MCP tools, image placement) -- Animation system (character state machine) - Persistence system (project files, session logs) -- 3D scene and character models +- 3D scene and character models (placeholder cube in place for animation testing) ### Design Documents diff --git a/docs/design/animation-system.md b/docs/design/animation-system.md index 282685b..aa5f64b 100644 --- a/docs/design/animation-system.md +++ b/docs/design/animation-system.md @@ -1,11 +1,32 @@ # Animation System Design **Component:** Animation System (Rust/Fyrox) -**Status:** Design +**Status:** MVP Implemented (placeholder cube), Full Design Pending **Related:** [Frontend Architecture](./frontend-architecture.md), [IPC Protocol](./ipc-protocol.md) --- +## Implementation Status + +**Current MVP** (`game/src/animation_state.rs`): +- ✅ `BodyState` enum with all states (Idle, Working, TurningToCamera, Talking, TurningFromCamera) +- ✅ `IdleVariant` enum (Sitting, Thinking, Stretching, LookingAround) +- ✅ `WorkAnimation` enum (Terminal, Reading, Writing, WebBrowsing, DeepThinking) +- ✅ `AnimationController` with state transitions +- ✅ Idle variation timer (10-30 second random changes) +- ✅ Tool categorization for 15+ tool types +- ✅ Backend message integration (ToolStart, ToolEnd, AssistantTextDelta, AssistantMessageComplete) +- ✅ Color-coded placeholder cube for visual feedback + +**Not Yet Implemented:** +- Fyrox animation machine with skeletal animations +- Blend shape system for facial expressions +- Camera focus controller +- Sound effects synchronization +- 3D character model + +--- + ## Overview The animation system brings the AI agent character to life through context-aware animations that correspond to tool usage, conversation state, and subagent spawning. It uses Fyrox's built-in animation state machine to blend between animations and synchronize with backend events. diff --git a/docs/design/frontend-architecture.md b/docs/design/frontend-architecture.md index 26efd82..9c48aad 100644 --- a/docs/design/frontend-architecture.md +++ b/docs/design/frontend-architecture.md @@ -1,7 +1,7 @@ # Frontend Architecture **Component:** Frontend (Rust/Fyrox) -**Status:** Design +**Status:** Partially Implemented (IPC, Chat UI, Auth, Animation MVP) **Related:** [IPC Protocol](./ipc-protocol.md), [Animation System](./animation-system.md), [Room System](./room-system.md), [Persistence System](./persistence-system.md) --- diff --git a/tiny-workshop/game/src/backend.rs b/tiny-workshop/game/src/backend.rs index 387e4ec..09dbf2b 100644 --- a/tiny-workshop/game/src/backend.rs +++ b/tiny-workshop/game/src/backend.rs @@ -42,6 +42,7 @@ impl BackendHandle { } /// Send cancel request + #[allow(dead_code)] pub fn send_cancel(&self) { let _ = self.send(FrontendMessage::Cancel); } @@ -86,11 +87,11 @@ pub fn spawn_backend(backend_path: PathBuf) -> (BackendHandle, mpsc::Receiver child, Err(e) => { - eprintln!("Failed to spawn backend: {}", e); + eprintln!("Failed to spawn backend: {e}"); let _ = from_backend_tx .send(BackendMessage::Error { code: "SPAWN_FAILED".to_string(), - message: format!("Failed to spawn backend: {}", e), + message: format!("Failed to spawn backend: {e}"), recoverable: false, }) .await; @@ -115,7 +116,7 @@ pub fn spawn_backend(backend_path: PathBuf) -> (BackendHandle, mpsc::Receiver { - eprintln!("Failed to parse backend message: {} - Line: {}", e, line); + eprintln!("Failed to parse backend message: {e} - Line: {line}"); } } } @@ -125,7 +126,7 @@ pub fn spawn_backend(backend_path: PathBuf) -> (BackendHandle, mpsc::Receiver { - let line = format!("{}\n", json); + let line = format!("{json}\n"); if stdin.write_all(line.as_bytes()).await.is_err() { eprintln!("Failed to write to backend stdin"); break; @@ -136,7 +137,7 @@ pub fn spawn_backend(backend_path: PathBuf) -> (BackendHandle, mpsc::Receiver { - eprintln!("Failed to serialize message: {}", e); + eprintln!("Failed to serialize message: {e}"); } } } diff --git a/tiny-workshop/game/src/chat_ui.rs b/tiny-workshop/game/src/chat_ui.rs index 8e7b56d..1f8b51e 100644 --- a/tiny-workshop/game/src/chat_ui.rs +++ b/tiny-workshop/game/src/chat_ui.rs @@ -26,6 +26,7 @@ use fyrox::{ /// Chat message for display #[derive(Debug, Clone)] +#[allow(dead_code)] pub struct ChatMessage { pub content: String, pub is_user: bool, @@ -36,8 +37,10 @@ pub struct ChatMessage { #[derive(Debug)] pub struct ChatUI { /// Root window widget + #[allow(dead_code)] pub window: Handle, /// Scrollable conversation area + #[allow(dead_code)] pub conversation_scroll: Handle, /// Stack panel containing messages pub conversation_stack: Handle, diff --git a/tiny-workshop/game/src/credential_manager.rs b/tiny-workshop/game/src/credential_manager.rs index 7f3bd32..3b745bc 100644 --- a/tiny-workshop/game/src/credential_manager.rs +++ b/tiny-workshop/game/src/credential_manager.rs @@ -56,8 +56,7 @@ impl CredentialManager { } Err(e) => { fyrox::core::log::Log::warn(format!( - "Keychain write verification FAILED - could not read back: {}", - e + "Keychain write verification FAILED - could not read back: {e}" )); } } @@ -68,14 +67,13 @@ impl CredentialManager { /// Get the stored refresh token from the OS keychain pub fn get_refresh_token() -> Option { fyrox::core::log::Log::info(format!( - "Looking for credential: service='{}', key='{}'", - SERVICE_NAME, REFRESH_TOKEN_KEY + "Looking for credential: service='{SERVICE_NAME}', key='{REFRESH_TOKEN_KEY}'" )); let entry = match Entry::new(SERVICE_NAME, REFRESH_TOKEN_KEY) { Ok(e) => e, Err(e) => { - fyrox::core::log::Log::warn(format!("Failed to access keychain entry: {}", e)); + fyrox::core::log::Log::warn(format!("Failed to access keychain entry: {e}")); return None; } }; @@ -91,13 +89,14 @@ impl CredentialManager { None } Err(e) => { - fyrox::core::log::Log::warn(format!("Keychain read failed: {}", e)); + fyrox::core::log::Log::warn(format!("Keychain read failed: {e}")); None } } } /// Clear the stored refresh token (logout) + #[allow(dead_code)] pub fn clear() -> Result<(), keyring::Error> { let entry = Entry::new(SERVICE_NAME, REFRESH_TOKEN_KEY)?; match entry.delete_credential() { @@ -114,6 +113,7 @@ impl CredentialManager { } /// Check if we have a stored refresh token + #[allow(dead_code)] pub fn has_refresh_token() -> bool { Self::get_refresh_token().is_some() } diff --git a/tiny-workshop/game/src/ipc.rs b/tiny-workshop/game/src/ipc.rs index 437ccc7..7d303dc 100644 --- a/tiny-workshop/game/src/ipc.rs +++ b/tiny-workshop/game/src/ipc.rs @@ -26,6 +26,7 @@ pub enum FrontendMessage { /// User sends a chat message UserMessage { content: String }, /// Cancel the current operation + #[allow(dead_code)] Cancel, /// Shut down the backend gracefully Shutdown, @@ -54,6 +55,7 @@ pub enum BackendMessage { id: String, tool: String, #[serde(default)] + #[allow(dead_code)] input: Option, }, /// Tool execution has completed @@ -61,6 +63,7 @@ pub enum BackendMessage { id: String, success: bool, #[serde(default)] + #[allow(dead_code)] output: Option, }, /// Session has ended diff --git a/tiny-workshop/game/src/lib.rs b/tiny-workshop/game/src/lib.rs index 3102a0d..db4b37e 100644 --- a/tiny-workshop/game/src/lib.rs +++ b/tiny-workshop/game/src/lib.rs @@ -425,7 +425,7 @@ impl Game { if let Some(mesh) = scene.graph[self.agent_cube].cast_mut::() { for surface in mesh.surfaces_mut() { let material = surface.material(); - let _ = material.data_ref().set_property( + material.data_ref().set_property( "diffuseColor", fyrox::core::color::Color::from_rgba(color.r, color.g, color.b, color.a), ); @@ -440,7 +440,7 @@ impl Game { match msg { BackendMessage::SessionStarted { session_id } => { - fyrox::core::log::Log::info(format!("Session started: {}", session_id)); + fyrox::core::log::Log::info(format!("Session started: {session_id}")); self.session_id = Some(session_id); } @@ -461,7 +461,7 @@ impl Game { } BackendMessage::AssistantMessageComplete { content } => { - fyrox::core::log::Log::info(format!("Assistant: {}", content)); + fyrox::core::log::Log::info(format!("Assistant: {content}")); // Finish streaming and show complete message if let Some(chat_ui) = &mut self.chat_ui { chat_ui.finish_streaming(&content, ui); @@ -478,30 +478,30 @@ impl Game { } BackendMessage::ToolStart { id, tool, .. } => { - fyrox::core::log::Log::info(format!("Tool started: {} ({})", tool, id)); + fyrox::core::log::Log::info(format!("Tool started: {tool} ({id})")); // Trigger tool animation self.animation_controller.on_tool_start(&tool); // Show tool indicator in chat if let Some(chat_ui) = &self.chat_ui { - chat_ui.add_user_message(&format!("🔧 Using tool: {}", tool), ui); + chat_ui.add_user_message(&format!("🔧 Using tool: {tool}"), ui); } // Track tool message for debug server #[cfg(feature = "debug_server")] - self.track_tool_message(&format!("Using tool: {}", tool)); + self.track_tool_message(&format!("Using tool: {tool}")); } BackendMessage::ToolEnd { id, success, .. } => { - fyrox::core::log::Log::info(format!("Tool ended: {} (success: {})", id, success)); + fyrox::core::log::Log::info(format!("Tool ended: {id} (success: {success})")); // Trigger end of tool animation self.animation_controller.on_tool_end(); } BackendMessage::SessionEnded { reason } => { - fyrox::core::log::Log::info(format!("Session ended: {}", reason)); + fyrox::core::log::Log::info(format!("Session ended: {reason}")); } BackendMessage::Error { @@ -510,29 +510,28 @@ impl Game { recoverable, } => { fyrox::core::log::Log::err(format!( - "Backend error [{}]: {} (recoverable: {})", - code, message, recoverable + "Backend error [{code}]: {message} (recoverable: {recoverable})" )); // Show error in chat UI if let Some(chat_ui) = &self.chat_ui { - chat_ui.add_user_message(&format!("❌ Error: {}", message), ui); + chat_ui.add_user_message(&format!("❌ Error: {message}"), ui); } } BackendMessage::AuthLoginUrl { url, instructions } => { - fyrox::core::log::Log::info(format!("OAuth URL: {}", url)); - fyrox::core::log::Log::info(format!("Instructions: {}", instructions)); + fyrox::core::log::Log::info(format!("OAuth URL: {url}")); + fyrox::core::log::Log::info(format!("Instructions: {instructions}")); // Set flag so next message is treated as auth code self.waiting_for_auth_code = true; // Auto-open the URL in the default browser if let Err(e) = open::that(&url) { - fyrox::core::log::Log::err(format!("Failed to open browser: {}", e)); + fyrox::core::log::Log::err(format!("Failed to open browser: {e}")); // Fall back to showing in chat if browser fails if let Some(chat_ui) = &self.chat_ui { chat_ui.add_user_message( - &format!("🔐 Please open this URL manually:\n{}", url), + &format!("🔐 Please open this URL manually:\n{url}"), ui, ); } @@ -559,7 +558,7 @@ impl Game { if let Some(token) = refresh_token { // Save to OS keychain for persistence across sessions if let Err(e) = CredentialManager::store_refresh_token(&token) { - fyrox::core::log::Log::warn(format!("Keychain save failed: {}", e)); + fyrox::core::log::Log::warn(format!("Keychain save failed: {e}")); // Notify user that login won't persist if let Some(chat_ui) = &self.chat_ui { chat_ui.add_user_message( @@ -572,12 +571,9 @@ impl Game { self.refresh_token = Some(token); } if let Some(ref err) = error { - fyrox::core::log::Log::err(format!("Auth error: {}", err)); + fyrox::core::log::Log::err(format!("Auth error: {err}")); } - fyrox::core::log::Log::info(format!( - "Auth status: authenticated={}", - authenticated - )); + fyrox::core::log::Log::info(format!("Auth status: authenticated={authenticated}")); // Show auth status in chat if let Some(chat_ui) = &self.chat_ui { if authenticated { @@ -590,11 +586,11 @@ impl Game { #[cfg(feature = "debug_server")] self.track_system_message("Authentication successful"); } else if let Some(err) = error { - chat_ui.add_user_message(&format!("❌ Auth failed: {}", err), ui); + chat_ui.add_user_message(&format!("❌ Auth failed: {err}"), ui); // Track auth failure for debug server #[cfg(feature = "debug_server")] - self.track_system_message(&format!("Auth failed: {}", err)); + self.track_system_message(&format!("Auth failed: {err}")); } } } -- 2.51.2