par_term/app/window_state/mod.rs
1//! Per-window state for multi-window terminal emulator
2//!
3//! This module contains `WindowState`, which holds all state specific to a single window,
4//! including its renderer, tab manager, input handler, and UI components.
5//!
6//! # ARC-002: Remaining God-Object Decomposition (Requires Manual Intervention)
7//!
8//! `WindowState` is being decomposed from a God Object into cohesive sub-state structs.
9//! Twelve already live in their own `*_state.rs` module beside this one — `agent_state`,
10//! `cursor_anim_state`, `debug_state`, `egui_state`, `focus_state`, `overlay_state`,
11//! `overlay_ui_state`, `render_loop_state`, `shader_state`, `trigger_state`,
12//! `update_state`, `watcher_state` — joined by `NotificationClickState` in
13//! `notifications.rs`.
14//!
15//! What remains is a wide field list plus an `impl WindowState` surface spread across
16//! most of `src/app/`. No process maintains a count here, so re-measure before acting
17//! on this note instead of trusting a number written into it:
18//!
19//! ```text
20//! grep -rc '^impl WindowState' --include='*.rs' src/ | awk -F: '{s+=$2} END {print s}'
21//! grep -rl '^impl WindowState' --include='*.rs' src/ | wc -l
22//! ```
23//!
24//! The remaining work is deferred to a future session:
25//!
26//! **Suggested next extractions (in order of isolation):**
27//!
28//! 1. `TmuxSubsystem` — owns `tmux_state` and all methods in `src/app/tmux_handler/`.
29//! Safe to extract once `TmuxState` has no shared borrow with other sub-state.
30//!
31//! 2. `SelectionSubsystem` — owns `smart_selection_cache`, `copy_mode`, and the
32//! text-selection helpers in `text_selection.rs`. These three fields form a tight
33//! read-only cluster during rendering.
34//!
35//! 3. `WindowInfrastructure` — groups `window`, `renderer`, `runtime` as the GPU/OS
36//! surface layer; separates it from application-level state.
37//!
38//! **Blocker:** every `impl WindowState` block must be audited before moving any
39//! field to ensure no method holds simultaneous mutable borrows across sub-systems.
40//! Recommend using `cargo expand` on each field before
41//! moving it. The `#[path]` redirect blocker (ARC-003) has been resolved — field
42//! extraction from step 3+ can now proceed.
43//!
44//! **Tracking:** ARC-002 on the projects board. Lifecycle and ownership rules for the
45//! extracted sub-states are documented in `docs/architecture/STATE_LIFECYCLE.md`.
46//!
47//! # ARC-003: render_pipeline `#[path]` Redirect — RESOLVED
48//!
49//! The `#[path = "../render_pipeline/mod.rs"]` redirect has been removed.
50//! `render_pipeline` is now declared as a first-class module in `src/app/mod.rs`,
51//! matching the physical directory layout (`src/app/render_pipeline/`).
52//! All `super::` references inside `render_pipeline/*.rs` correctly resolve to
53//! the `render_pipeline` module itself (unchanged).
54
55mod action_handlers;
56mod agent_config;
57mod agent_message_helpers;
58mod agent_messages;
59mod agent_screenshot;
60pub(crate) mod agent_state;
61mod agent_tick_helpers;
62pub(crate) mod anti_idle;
63mod automation_target;
64mod clipboard_sync;
65pub(crate) mod config_updates;
66mod config_watchers;
67pub(crate) mod cursor_anim_state;
68pub(crate) mod debug_state;
69mod egui_state;
70mod focus_state;
71pub(crate) mod frame_state;
72mod impl_agent;
73mod impl_helpers;
74mod impl_init;
75pub(crate) mod keyboard_handlers;
76mod notifications;
77mod overlay_state;
78pub(crate) mod overlay_ui_state;
79mod render_loop_state;
80pub(crate) mod renderer_init;
81mod renderer_ops;
82pub(crate) mod scroll_ops;
83pub(crate) mod search_highlight;
84mod shader_ops;
85pub(crate) mod shader_state;
86pub(crate) mod text_selection;
87mod trigger_state;
88mod ui_query_helpers;
89mod update_state;
90pub(crate) mod url_hover;
91mod watcher_state;
92
93// Re-export the sub-state types
94pub(crate) use crate::app::tmux_handler::tmux_state::TmuxState;
95pub(crate) use automation_target::AutomationTarget;
96pub(crate) use egui_state::EguiState;
97pub(crate) use focus_state::FocusState;
98pub(crate) use notifications::NotificationClickState;
99pub(crate) use overlay_state::OverlayState;
100pub(crate) use render_loop_state::{ConfigSaveState, RenderLoopState};
101pub(crate) use trigger_state::{PendingTriggerAction, TriggerState};
102pub(crate) use update_state::UpdateState;
103pub(crate) use watcher_state::WatcherState;
104
105use crate::app::window_state::debug_state::DebugState;
106use crate::badge::BadgeState;
107use crate::config::Config;
108use crate::smart_selection::SmartSelectionCache;
109use crate::status_bar::StatusBarUI;
110use crate::tab::TabManager;
111use crate::tab_bar_ui::TabBarUI;
112use arc_swap::ArcSwap;
113use par_term_input::InputHandler;
114use par_term_keybindings::{KeyCombo, KeybindingRegistry};
115use par_term_render::renderer::Renderer;
116use std::sync::Arc;
117use tokio::runtime::Runtime;
118use winit::window::Window;
119
120#[derive(Clone)]
121pub(crate) struct PreservedClipboardImage {
122 pub(crate) width: usize,
123 pub(crate) height: usize,
124 pub(crate) bytes: Vec<u8>,
125}
126
127pub(crate) struct ClipboardImageClickGuard {
128 pub(crate) image: PreservedClipboardImage,
129 pub(crate) press_position: (f64, f64),
130 pub(crate) suppress_terminal_mouse_click: bool,
131}
132
133/// Transient context shared between chained workflow actions.
134///
135/// Created by `ShellCommand` actions with `capture_output: true` and
136/// consumed by `Condition` checks (`ExitCode`, `OutputContains`).
137/// Never serialized to disk.
138#[derive(Debug, Clone)]
139pub(crate) struct WorkflowContext {
140 /// Exit code from the last captured shell command.
141 pub(crate) last_exit_code: Option<i32>,
142 /// Captured stdout+stderr from the last shell command (capped at 64 KB).
143 pub(crate) last_output: Option<String>,
144}
145
146/// Per-window state that manages a single terminal window with multiple tabs.
147pub struct WindowState {
148 // =========================================================================
149 // Core infrastructure
150 // =========================================================================
151 /// Global configuration (QA-001: ArcSwap for zero-cost reads, atomic whole-config swaps)
152 pub(crate) config: ArcSwap<Config>,
153 /// The winit window handle
154 pub(crate) window: Option<Arc<Window>>,
155 /// GPU renderer
156 pub(crate) renderer: Option<Renderer>,
157 /// Keyboard and mouse input handler
158 pub(crate) input_handler: InputHandler,
159 /// Tokio runtime shared with async PTY tasks
160 pub(crate) runtime: Arc<Runtime>,
161
162 // =========================================================================
163 // Tab & built-in UI bars
164 // =========================================================================
165 /// Tab manager for handling multiple terminal tabs
166 pub(crate) tab_manager: TabManager,
167 /// Tab bar UI
168 pub(crate) tab_bar_ui: TabBarUI,
169 /// Custom status bar UI
170 pub(crate) status_bar_ui: StatusBarUI,
171
172 // =========================================================================
173 // Window flags
174 // =========================================================================
175 /// Whether window is currently in fullscreen mode
176 pub(crate) is_fullscreen: bool,
177 /// Whether terminal session recording is active
178 pub(crate) is_recording: bool,
179 /// Flag to indicate shutdown is in progress
180 pub(crate) is_shutting_down: bool,
181 /// Window index (1-based) for display in title bar
182 pub(crate) window_index: usize,
183
184 // =========================================================================
185 // egui overlay layer (ARC-002 extraction: EguiState)
186 // =========================================================================
187 /// egui context, input state, and lifecycle flags (see `EguiState`)
188 pub(crate) egui: EguiState,
189
190 // =========================================================================
191 // Sub-system state bundles
192 // =========================================================================
193 /// Shader hot-reload watcher, metadata caches, and reload-error state
194 pub(crate) shader_state: crate::app::window_state::shader_state::ShaderState,
195 /// Overlay / modal / side-panel UI state
196 pub(crate) overlay_ui: crate::app::window_state::overlay_ui_state::OverlayUiState,
197 /// ACP agent connection and runtime state
198 pub(crate) agent_state: agent_state::AgentState,
199 /// Cursor animation state (opacity, blink timers)
200 pub(crate) cursor_anim: crate::app::window_state::cursor_anim_state::CursorAnimState,
201 /// Debug / diagnostics state
202 pub(crate) debug: DebugState,
203 /// Per-frame render decisions and reusable scratch buffers
204 pub(crate) frame: crate::app::window_state::frame_state::FrameState,
205
206 // =========================================================================
207 // Decomposed state objects (ARC-002)
208 // =========================================================================
209 /// State for focus, redraw tracking, and render throttling
210 pub(crate) focus_state: FocusState,
211 /// State for the in-app self-update flow
212 pub(crate) update_state: UpdateState,
213 /// State for transient UI overlays and pending UI requests
214 pub(crate) overlay_state: OverlayState,
215 /// State for file and request watchers
216 pub(crate) watcher_state: WatcherState,
217 /// State for terminal triggers and their spawned processes
218 pub(crate) trigger_state: TriggerState,
219 /// Pending OSC 99 notification click-to-action registry (per-window; see
220 /// `notifications::NotificationClickState` docs for why)
221 pub(crate) notification_click_state: NotificationClickState,
222
223 // =========================================================================
224 // Render loop control & config management (ARC-002 extraction: RenderLoopState)
225 // =========================================================================
226 /// Pending-work flags for the render loop (agent config change, font rebuild, config save)
227 pub(crate) render_loop: RenderLoopState,
228
229 // =========================================================================
230 // Feature state
231 // =========================================================================
232 /// Whether keyboard input is broadcast to all panes in current tab
233 pub(crate) broadcast_input: bool,
234 /// State machine for promote/demote pane-tab operations
235 pub(crate) pane_transfer_state: crate::app::tab_ops::pane_transfer::PaneTransferState,
236 /// Badge state for session information display
237 pub(crate) badge_state: BadgeState,
238 /// Copy mode state machine
239 pub(crate) copy_mode: crate::copy_mode::CopyModeState,
240 /// File transfer UI state
241 pub(crate) file_transfer_state: crate::app::file_transfers::FileTransferState,
242 /// Snapshot of clipboard image for restore after tmux clicks
243 pub(crate) clipboard_image_click_guard: Option<ClipboardImageClickGuard>,
244 /// Last OSC 52 clipboard content applied to the system clipboard (dedup).
245 pub(crate) last_osc52_clipboard: Option<String>,
246 /// Shared transient context for chained workflow actions (Sequence / Condition / Repeat).
247 /// Written by background ShellCommand threads (capture_output=true); read by Condition checks.
248 pub(crate) last_workflow_context: std::sync::Arc<std::sync::Mutex<Option<WorkflowContext>>>,
249
250 // =========================================================================
251 // Keybinding & smart selection caches
252 // =========================================================================
253 pub(crate) keybinding_registry: KeybindingRegistry,
254 pub(crate) custom_action_prefix_combo: Option<KeyCombo>,
255 pub(crate) custom_action_prefix_state: crate::tmux::PrefixState,
256 pub(crate) smart_selection_cache: SmartSelectionCache,
257
258 // =========================================================================
259 // tmux integration
260 // =========================================================================
261 pub(crate) tmux_state: TmuxState,
262
263 // =========================================================================
264 // Window snap-to-grid
265 // =========================================================================
266 /// Tracks the last size we requested via `request_inner_size` for snap-to-grid.
267 /// Cleared once we receive a Resized event matching this size, preventing infinite re-snap.
268 pub(crate) pending_snap_size: Option<winit::dpi::PhysicalSize<u32>>,
269}