//! Tauri-managed application state. //! //! The non-Tauri composition root lives in `backend::BackendCore`. This type is //! the desktop driving-adapter envelope: it adds Tauri transport bridges and //! OS-window focus state, then dereferences to the shared core so existing //! commands keep their field/method access unchanged. use std::ops::Deref; use std::path::PathBuf; use std::sync::{Arc, Mutex}; use backend::BackendCore; use infrastructure::TicketToolProvider; use serde::{Deserialize, Serialize}; use crate::chat::ChatBridge; use crate::pty::PtyBridge; use crate::tickets::AppTicketToolProvider; pub use backend::ResumeContext; /// Focused project snapshot shared with detached panel windows. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct FocusedProjectDto { /// Project id as a string. pub id: String, /// Project display name. pub name: String, /// Absolute project root. pub root: String, } /// Desktop adapter state managed by Tauri. pub struct AppState { core: Arc, /// Generic PTY to Tauri Channel bridge registry. pub pty_bridge: Arc, /// Structured reply to Tauri Channel bridge registry. pub chat_bridge: Arc, /// Project currently focused by the main window; panel-only windows follow it. focused_project: Mutex>, } impl AppState { /// Builds the shared backend core and wraps it with desktop-only transport /// state. `app_data_dir` is resolved by the Tauri adapter before crossing /// into the backend core. #[must_use] pub fn build(app_data_dir: PathBuf) -> Self { let core = Arc::new(BackendCore::build(app_data_dir)); core.ticket_tool_binder .bind(Arc::new(AppTicketToolProvider { create: Arc::clone(&core.create_issue), read: Arc::clone(&core.read_issue), list: Arc::clone(&core.list_issues), update: Arc::clone(&core.update_issue), read_carnet: Arc::clone(&core.read_issue_carnet), update_carnet: Arc::clone(&core.update_issue_carnet), link: Arc::clone(&core.link_issues), unlink: Arc::clone(&core.unlink_issues), list_sprints: Arc::clone(&core.list_sprints), }) as Arc); Self { core, pty_bridge: Arc::new(PtyBridge::new()), chat_bridge: Arc::new(ChatBridge::new()), focused_project: Mutex::new(None), } } /// Updates the focused project state. `None` means no project is focused/open. pub fn set_focused_project(&self, project: Option) { *self .focused_project .lock() .expect("focused project mutex poisoned") = project; } /// Returns the current focused project, if any. #[must_use] pub fn get_focused_project(&self) -> Option { self.focused_project .lock() .expect("focused project mutex poisoned") .clone() } } impl Deref for AppState { type Target = BackendCore; fn deref(&self) -> &Self::Target { &self.core } }