pacsea/state/app_state/mod.rs
1//! Central `AppState` container, split out from the monolithic module.
2
3use lru::LruCache;
4use ratatui::widgets::ListState;
5use std::{collections::HashMap, collections::HashSet, path::PathBuf, time::Instant};
6
7use crate::state::modal::{CascadeMode, Modal, PreflightAction, ServiceImpact};
8use crate::state::types::{
9 AppMode, ArchStatusColor, Focus, InstalledPackagesMode, NewsFeedItem, NewsReadFilter,
10 NewsSortMode, PackageDetails, PackageItem, RightPaneFocus, SortMode,
11};
12use crate::theme::KeyMap;
13
14mod constants;
15mod default_impl;
16mod defaults;
17mod defaults_cache;
18mod methods;
19
20#[cfg(test)]
21mod tests;
22
23pub use constants::{FileSyncResult, RECENT_CAPACITY, recent_capacity};
24
25/// Global application state shared by the event, networking, and UI layers.
26///
27/// This structure is mutated frequently in response to input and background
28/// updates. Certain subsets are persisted to disk to preserve user context
29/// across runs (e.g., recent searches, details cache, install list).
30#[derive(Debug)]
31#[allow(clippy::struct_excessive_bools)]
32pub struct AppState {
33 /// Current top-level mode (package management vs news feed).
34 pub app_mode: AppMode,
35 /// Current search input text.
36 pub input: String,
37 /// Current search results, most relevant first.
38 pub results: Vec<PackageItem>,
39 /// Unfiltered results as last received from the search worker.
40 pub all_results: Vec<PackageItem>,
41 /// Backup of results when toggling to installed-only view.
42 pub results_backup_for_toggle: Option<Vec<PackageItem>>,
43 /// Index into `results` that is currently highlighted.
44 pub selected: usize,
45 /// Details for the currently highlighted result.
46 pub details: PackageDetails,
47 /// List selection state for the search results list.
48 pub list_state: ListState,
49 /// Active modal dialog, if any.
50 pub modal: Modal,
51 /// Previous modal state (used to restore when closing help/alert modals).
52 pub previous_modal: Option<Modal>,
53 /// If `true`, show install steps without executing side effects.
54 pub dry_run: bool,
55 // Recent searches
56 /// Previously executed queries stored as an LRU cache (keyed case-insensitively).
57 pub recent: LruCache<String, String>,
58 /// List selection state for the Recent pane.
59 pub history_state: ListState,
60 /// Which pane is currently focused.
61 pub focus: Focus,
62 /// Timestamp of the last input edit, used for debouncing or throttling.
63 pub last_input_change: Instant,
64 /// Last value persisted for the input field, to avoid redundant writes.
65 pub last_saved_value: Option<String>,
66 // Persisted recent searches
67 /// Path where recent searches are persisted as JSON.
68 pub recent_path: PathBuf,
69 /// Dirty flag indicating `recent` needs to be saved.
70 pub recent_dirty: bool,
71
72 // Search coordination
73 /// Identifier of the latest query whose results are being displayed.
74 pub latest_query_id: u64,
75 /// Next query identifier to allocate.
76 pub next_query_id: u64,
77 // Search result cache
78 /// Cached search query text (None if cache is empty or invalid).
79 pub search_cache_query: Option<String>,
80 /// Whether fuzzy mode was used for cached query.
81 pub search_cache_fuzzy: bool,
82 /// Cached search results (None if cache is empty or invalid).
83 pub search_cache_results: Option<Vec<PackageItem>>,
84 // Details cache
85 /// Cache of details keyed by package name.
86 pub details_cache: HashMap<String, PackageDetails>,
87 /// Path where the details cache is persisted as JSON.
88 pub cache_path: PathBuf,
89 /// Dirty flag indicating `details_cache` needs to be saved.
90 pub cache_dirty: bool,
91
92 // News read/unread tracking (persisted)
93 /// Set of Arch news item URLs the user has marked as read.
94 pub news_read_urls: std::collections::HashSet<String>,
95 /// Path where the read news URLs are persisted as JSON.
96 pub news_read_path: PathBuf,
97 /// Dirty flag indicating `news_read_urls` needs to be saved.
98 pub news_read_dirty: bool,
99 /// Set of news feed item IDs the user has marked as read.
100 pub news_read_ids: std::collections::HashSet<String>,
101 /// Path where the read news IDs are persisted as JSON.
102 pub news_read_ids_path: PathBuf,
103 /// Dirty flag indicating `news_read_ids` needs to be saved.
104 pub news_read_ids_dirty: bool,
105 /// News feed items currently loaded.
106 pub news_items: Vec<NewsFeedItem>,
107 /// Filtered/sorted news results shown in the UI.
108 pub news_results: Vec<NewsFeedItem>,
109 /// Whether the news feed is currently loading.
110 pub news_loading: bool,
111 /// Whether news are ready to be viewed (loading complete and news available).
112 pub news_ready: bool,
113 /// Selected index within news results.
114 pub news_selected: usize,
115 /// List state for news results pane.
116 pub news_list_state: ListState,
117 /// News search input text.
118 pub news_search_input: String,
119 /// Caret position within news search input.
120 pub news_search_caret: usize,
121 /// Selection anchor within news search input.
122 pub news_search_select_anchor: Option<usize>,
123 /// LRU cache of recent news searches (case-insensitive key).
124 pub news_recent: LruCache<String, String>,
125 /// Path where news recent searches are persisted.
126 pub news_recent_path: PathBuf,
127 /// Dirty flag indicating `news_recent` needs to be saved.
128 pub news_recent_dirty: bool,
129 /// Pending news search awaiting debounce before saving to history.
130 pub news_history_pending: Option<String>,
131 /// Timestamp when the pending news search was last updated.
132 pub news_history_pending_at: Option<std::time::Instant>,
133 /// Last news search saved to history (prevents duplicate saves).
134 pub news_history_last_saved: Option<String>,
135 /// Whether to show Arch news items.
136 pub news_filter_show_arch_news: bool,
137 /// Whether to show security advisories.
138 pub news_filter_show_advisories: bool,
139 /// Whether to show installed package update items.
140 pub news_filter_show_pkg_updates: bool,
141 /// Whether to show AUR package update items.
142 pub news_filter_show_aur_updates: bool,
143 /// Whether to show AUR comment items.
144 pub news_filter_show_aur_comments: bool,
145 /// Whether to restrict advisories to installed packages.
146 pub news_filter_installed_only: bool,
147 /// Read/unread filter for the News Feed list.
148 pub news_filter_read_status: NewsReadFilter,
149 /// Clickable rectangle for Arch news filter chip in news title.
150 pub news_filter_arch_rect: Option<(u16, u16, u16, u16)>,
151 /// Clickable rectangle for security advisory filter chip in news title.
152 pub news_filter_advisory_rect: Option<(u16, u16, u16, u16)>,
153 /// Clickable rectangle for installed-only advisory filter chip in news title.
154 pub news_filter_installed_rect: Option<(u16, u16, u16, u16)>,
155 /// Clickable rectangle for installed update filter chip in news title.
156 pub news_filter_updates_rect: Option<(u16, u16, u16, u16)>,
157 /// Clickable rectangle for AUR update filter chip in news title.
158 pub news_filter_aur_updates_rect: Option<(u16, u16, u16, u16)>,
159 /// Clickable rectangle for AUR comment filter chip in news title.
160 pub news_filter_aur_comments_rect: Option<(u16, u16, u16, u16)>,
161 /// Clickable rectangle for read/unread filter chip in news title.
162 pub news_filter_read_rect: Option<(u16, u16, u16, u16)>,
163 /// Maximum age of news items in days (None = unlimited).
164 pub news_max_age_days: Option<u32>,
165 /// Whether to show the news history pane in News mode.
166 pub show_news_history_pane: bool,
167 /// Whether to show the news bookmarks pane in News mode.
168 pub show_news_bookmarks_pane: bool,
169 /// Sort mode for news results.
170 pub news_sort_mode: NewsSortMode,
171 /// Saved news/bookmarked items with cached content.
172 pub news_bookmarks: Vec<crate::state::types::NewsBookmark>,
173 /// Path where news bookmarks are persisted.
174 pub news_bookmarks_path: PathBuf,
175 /// Dirty flag indicating `news_bookmarks` needs to be saved.
176 pub news_bookmarks_dirty: bool,
177 /// Cache of fetched news article content (URL -> content).
178 pub news_content_cache: std::collections::HashMap<String, String>,
179 /// Path where the news content cache is persisted.
180 pub news_content_cache_path: PathBuf,
181 /// Dirty flag indicating `news_content_cache` needs to be saved.
182 pub news_content_cache_dirty: bool,
183 /// Currently displayed news content (for the selected item).
184 pub news_content: Option<String>,
185 /// Whether news content is currently being fetched.
186 pub news_content_loading: bool,
187 /// When the current news content load started (for timeout/logging).
188 pub news_content_loading_since: Option<std::time::Instant>,
189 /// Debounce timer for news content requests - tracks when user selected current item.
190 /// Only requests content after 0.5 seconds of staying on the same item.
191 pub news_content_debounce_timer: Option<std::time::Instant>,
192 /// Scroll offset for news content details.
193 pub news_content_scroll: u16,
194 /// Path where the cached news feed is persisted.
195 pub news_feed_path: PathBuf,
196 /// Last-seen versions for installed packages (dedup for update feed items).
197 pub news_seen_pkg_versions: HashMap<String, String>,
198 /// Path where last-seen package versions are persisted.
199 pub news_seen_pkg_versions_path: PathBuf,
200 /// Dirty flag indicating `news_seen_pkg_versions` needs to be saved.
201 pub news_seen_pkg_versions_dirty: bool,
202 /// Last-seen AUR comment identifiers per installed package.
203 pub news_seen_aur_comments: HashMap<String, String>,
204 /// Path where last-seen AUR comments are persisted.
205 pub news_seen_aur_comments_path: PathBuf,
206 /// Dirty flag indicating `news_seen_aur_comments` needs to be saved.
207 pub news_seen_aur_comments_dirty: bool,
208
209 // Announcement read tracking (persisted)
210 /// Set of announcement IDs the user has marked as read.
211 /// Tracks both version strings (e.g., "v0.6.0") and remote announcement IDs.
212 pub announcements_read_ids: std::collections::HashSet<String>,
213 /// Path where the read announcement IDs are persisted as JSON.
214 pub announcement_read_path: PathBuf,
215 /// Dirty flag indicating `announcements_read_ids` needs to be saved.
216 pub announcement_dirty: bool,
217
218 // Last startup tracking (for incremental updates)
219 /// Timestamp of the previous TUI startup (format: `YYYYMMDD:HHMMSS`).
220 /// Used to determine what news/updates need fresh fetching vs cached data.
221 pub last_startup_timestamp: Option<String>,
222 /// Path where the last startup timestamp is persisted.
223 pub last_startup_path: PathBuf,
224
225 // Install list pane
226 /// Packages selected for installation.
227 pub install_list: Vec<PackageItem>,
228 /// List selection state for the Install pane.
229 pub install_state: ListState,
230 /// Separate list of packages selected for removal (active in installed-only mode).
231 pub remove_list: Vec<PackageItem>,
232 /// List selection state for the Remove pane.
233 pub remove_state: ListState,
234 /// Separate list of packages selected for downgrade (shown in installed-only mode).
235 pub downgrade_list: Vec<PackageItem>,
236 /// List selection state for the Downgrade pane.
237 pub downgrade_state: ListState,
238 // Persisted install list
239 /// Path where the install list is persisted as JSON.
240 pub install_path: PathBuf,
241 /// Dirty flag indicating `install_list` needs to be saved.
242 pub install_dirty: bool,
243 /// Timestamp of the most recent change to the install list for throttling disk writes.
244 pub last_install_change: Option<Instant>,
245 /// `HashSet` of package names in install list for O(1) membership checking.
246 pub install_list_names: HashSet<String>,
247 /// `HashSet` of package names in remove list for O(1) membership checking.
248 pub remove_list_names: HashSet<String>,
249 /// `HashSet` of package names in downgrade list for O(1) membership checking.
250 pub downgrade_list_names: HashSet<String>,
251
252 // Visibility toggles for middle row panes
253 /// Whether the Recent pane is visible in the middle row.
254 pub show_recent_pane: bool,
255 /// Whether the Install/Remove pane is visible in the middle row.
256 pub show_install_pane: bool,
257 /// Whether to show the keybindings footer in the details pane.
258 pub show_keybinds_footer: bool,
259
260 // In-pane search (for Recent/Install panes)
261 /// Optional, transient find pattern used by pane-local search ("/").
262 pub pane_find: Option<String>,
263
264 /// Whether Search pane is in Normal mode (Vim-like navigation) instead of Insert mode.
265 pub search_normal_mode: bool,
266
267 /// Whether fuzzy search is enabled (fzf-style matching) instead of normal substring search.
268 pub fuzzy_search_enabled: bool,
269
270 /// Caret position (in characters) within the `Search` input.
271 /// Always clamped to the range 0..=`input.chars().count()`.
272 pub search_caret: usize,
273 /// Selection anchor (in characters) for the Search input when selecting text.
274 /// When `None`, no selection is active. When `Some(i)`, the selected range is
275 /// between `min(i, search_caret)` and `max(i, search_caret)` (exclusive upper bound).
276 pub search_select_anchor: Option<usize>,
277
278 // Official package index persistence
279 /// Path to the persisted official package index used for fast offline lookups.
280 pub official_index_path: PathBuf,
281
282 // Loading indicator for official index generation
283 /// Whether the application is currently generating the official index.
284 pub loading_index: bool,
285
286 // Track which package's details the UI is focused on
287 /// Name of the package whose details are being emphasized in the UI, if any.
288 pub details_focus: Option<String>,
289
290 // Ring prefetch debounce state
291 /// Smooth scrolling accumulator for prefetch heuristics.
292 pub scroll_moves: u32,
293 /// Timestamp at which to resume ring prefetching, if paused.
294 pub ring_resume_at: Option<Instant>,
295 /// Whether a ring prefetch is needed soon.
296 pub need_ring_prefetch: bool,
297
298 // Clickable URL button rectangle (x, y, w, h) in terminal cells
299 /// Rectangle of the clickable URL button in terminal cell coordinates.
300 pub url_button_rect: Option<(u16, u16, u16, u16)>,
301
302 // VirusTotal API setup modal clickable URL rectangle
303 /// Rectangle of the clickable `VirusTotal` API URL in the setup modal (x, y, w, h).
304 pub vt_url_rect: Option<(u16, u16, u16, u16)>,
305
306 // Install pane bottom action (Import)
307 /// Clickable rectangle for the Install pane bottom "Import" button (x, y, w, h).
308 pub install_import_rect: Option<(u16, u16, u16, u16)>,
309 /// Clickable rectangle for the Install pane bottom "Export" button (x, y, w, h).
310 pub install_export_rect: Option<(u16, u16, u16, u16)>,
311
312 // Arch status label (middle row footer)
313 /// Latest fetched status message from `status.archlinux.org`.
314 pub arch_status_text: String,
315 /// Clickable rectangle for the status label (x, y, w, h).
316 pub arch_status_rect: Option<(u16, u16, u16, u16)>,
317 /// Optional status color indicator (e.g., operational vs. current incident).
318 pub arch_status_color: ArchStatusColor,
319
320 // Package updates available
321 /// Number of available package updates, if checked.
322 pub updates_count: Option<usize>,
323 /// Sorted list of package names with available updates.
324 pub updates_list: Vec<String>,
325 /// Clickable rectangle for the updates button (x, y, w, h).
326 pub updates_button_rect: Option<(u16, u16, u16, u16)>,
327 /// Clickable rectangle for the news button in News mode (x, y, w, h).
328 pub news_button_rect: Option<(u16, u16, u16, u16)>,
329 /// Whether updates check is currently in progress.
330 pub updates_loading: bool,
331 /// Flag to trigger refresh of updates list after package installation/update.
332 pub refresh_updates: bool,
333 /// Flag to indicate that Updates modal should open after refresh completes.
334 pub pending_updates_modal: bool,
335
336 // Faillock lockout status
337 /// Whether the user account is currently locked out.
338 pub faillock_locked: bool,
339 /// Timestamp when the lockout will expire (if locked).
340 pub faillock_lockout_until: Option<std::time::SystemTime>,
341 /// Remaining lockout time in minutes (if locked).
342 pub faillock_remaining_minutes: Option<u32>,
343
344 // Clickable PKGBUILD button rectangle and viewer state
345 /// Rectangle of the clickable "Show PKGBUILD" in terminal cell coordinates.
346 pub pkgb_button_rect: Option<(u16, u16, u16, u16)>,
347 /// Rectangle of the clickable "Copy PKGBUILD" button in PKGBUILD title.
348 pub pkgb_check_button_rect: Option<(u16, u16, u16, u16)>,
349 /// Rectangle of the clickable "Reload PKGBUILD" button in PKGBUILD title.
350 pub pkgb_reload_button_rect: Option<(u16, u16, u16, u16)>,
351 /// Whether the PKGBUILD viewer is visible (details pane split in half).
352 pub pkgb_visible: bool,
353 /// The fetched PKGBUILD text when available.
354 pub pkgb_text: Option<String>,
355 /// Name of the package that the PKGBUILD is currently for.
356 pub pkgb_package_name: Option<String>,
357 /// Timestamp when PKGBUILD reload was last requested (for debouncing).
358 pub pkgb_reload_requested_at: Option<Instant>,
359 /// Name of the package for which PKGBUILD reload was requested (for debouncing).
360 pub pkgb_reload_requested_for: Option<String>,
361 /// Scroll offset (lines) for the PKGBUILD viewer.
362 pub pkgb_scroll: u16,
363 /// Content rectangle of the PKGBUILD viewer (x, y, w, h) when visible.
364 pub pkgb_rect: Option<(u16, u16, u16, u16)>,
365
366 // AUR comments viewer state
367 /// Rectangle of the clickable "Show comments" / "Hide comments" button in terminal cell coordinates.
368 pub comments_button_rect: Option<(u16, u16, u16, u16)>,
369 /// Whether the comments viewer is visible (details pane split).
370 pub comments_visible: bool,
371 /// The fetched comments data when available.
372 pub comments: Vec<crate::state::types::AurComment>,
373 /// Name of the package that the comments are currently for.
374 pub comments_package_name: Option<String>,
375 /// Timestamp when comments were last fetched (for cache invalidation).
376 pub comments_fetched_at: Option<Instant>,
377 /// Scroll offset (lines) for the comments viewer.
378 pub comments_scroll: u16,
379 /// Content rectangle of the comments viewer (x, y, w, h) when visible.
380 pub comments_rect: Option<(u16, u16, u16, u16)>,
381 /// Whether comments are currently being fetched.
382 pub comments_loading: bool,
383 /// Error message if comments fetch failed.
384 pub comments_error: Option<String>,
385 /// URLs in comments with their screen positions for click detection.
386 /// Vector of (`x`, `y`, `width`, `url_string`) tuples.
387 pub comments_urls: Vec<(u16, u16, u16, String)>,
388 /// Author names in comments with their screen positions for click detection.
389 /// Vector of (`x`, `y`, `width`, `username`) tuples.
390 pub comments_authors: Vec<(u16, u16, u16, String)>,
391 /// Dates in comments with their screen positions and URLs for click detection.
392 /// Vector of (`x`, `y`, `width`, `url_string`) tuples.
393 pub comments_dates: Vec<(u16, u16, u16, String)>,
394
395 // Transient toast message (bottom-right)
396 /// Optional short-lived info message rendered at the bottom-right corner.
397 pub toast_message: Option<String>,
398 /// Deadline (Instant) after which the toast is automatically hidden.
399 pub toast_expires_at: Option<Instant>,
400
401 // User settings loaded at startup
402 /// Left pane width percentage.
403 pub layout_left_pct: u16,
404 /// Center pane width percentage.
405 pub layout_center_pct: u16,
406 /// Right pane width percentage.
407 pub layout_right_pct: u16,
408 /// Resolved key bindings from user settings
409 pub keymap: KeyMap,
410 // Internationalization (i18n)
411 /// Resolved locale code (e.g., "de-DE", "en-US")
412 pub locale: String,
413 /// Translation map for the current locale
414 pub translations: crate::i18n::translations::TranslationMap,
415 /// Fallback translation map (English) for missing keys
416 pub translations_fallback: crate::i18n::translations::TranslationMap,
417
418 // Mouse hit-test rectangles for panes
419 /// Inner content rectangle of the Results list (x, y, w, h).
420 pub results_rect: Option<(u16, u16, u16, u16)>,
421 /// Inner content rectangle of the Package Info details pane (x, y, w, h).
422 pub details_rect: Option<(u16, u16, u16, u16)>,
423 /// Scroll offset (lines) for the Package Info details pane.
424 pub details_scroll: u16,
425 /// Inner content rectangle of the Recent pane list (x, y, w, h).
426 pub recent_rect: Option<(u16, u16, u16, u16)>,
427 /// Inner content rectangle of the Install pane list (x, y, w, h).
428 pub install_rect: Option<(u16, u16, u16, u16)>,
429 /// Inner content rectangle of the Downgrade subpane when visible.
430 pub downgrade_rect: Option<(u16, u16, u16, u16)>,
431 /// Whether mouse capture is temporarily disabled to allow text selection in details.
432 pub mouse_disabled_in_details: bool,
433 /// Last observed mouse position (column, row) in terminal cells.
434 pub last_mouse_pos: Option<(u16, u16)>,
435 /// Whether global terminal mouse capture is currently enabled.
436 pub mouse_capture_enabled: bool,
437
438 // News modal mouse hit-testing
439 /// Outer rectangle of the News modal (including borders) when visible.
440 pub news_rect: Option<(u16, u16, u16, u16)>,
441 /// Inner list rectangle for clickable news rows.
442 pub news_list_rect: Option<(u16, u16, u16, u16)>,
443
444 // Announcement modal mouse hit-testing
445 /// Outer rectangle of the Announcement modal (including borders) when visible.
446 pub announcement_rect: Option<(u16, u16, u16, u16)>,
447 /// URLs in announcement content with their screen positions for click detection.
448 /// Vector of (`x`, `y`, `width`, `url_string`) tuples.
449 pub announcement_urls: Vec<(u16, u16, u16, String)>,
450 /// Pending remote announcements to show after current announcement is dismissed.
451 pub pending_announcements: Vec<crate::announcements::RemoteAnnouncement>,
452 /// Pending news to show after all announcements are dismissed.
453 pub pending_news: Option<Vec<crate::state::NewsItem>>,
454 /// Flag to trigger startup news fetch after `NewsSetup` is completed.
455 pub trigger_startup_news_fetch: bool,
456
457 // Updates modal mouse hit-testing
458 /// Outer rectangle of the Updates modal (including borders) when visible.
459 pub updates_modal_rect: Option<(u16, u16, u16, u16)>,
460 /// Inner content rectangle for scrollable updates list.
461 pub updates_modal_content_rect: Option<(u16, u16, u16, u16)>,
462
463 // Help modal scroll and hit-testing
464 /// Scroll offset (lines) for the Help modal content.
465 pub help_scroll: u16,
466 /// Inner content rectangle of the Help modal (x, y, w, h) for hit-testing.
467 pub help_rect: Option<(u16, u16, u16, u16)>,
468
469 // Preflight modal mouse hit-testing
470 /// Clickable rectangles for preflight tabs (x, y, w, h) - Summary, Deps, Files, Services, Sandbox.
471 pub preflight_tab_rects: [Option<(u16, u16, u16, u16)>; 5],
472 /// Inner content rectangle of the preflight modal (x, y, w, h) for hit-testing package groups.
473 pub preflight_content_rect: Option<(u16, u16, u16, u16)>,
474
475 // Results sorting UI
476 /// Current sort mode for results.
477 pub sort_mode: SortMode,
478 /// Filter mode for installed packages (leaf only vs all explicit).
479 pub installed_packages_mode: InstalledPackagesMode,
480 /// Whether the sort dropdown is currently visible.
481 pub sort_menu_open: bool,
482 /// Clickable rectangle for the sort button in the Results title (x, y, w, h).
483 pub sort_button_rect: Option<(u16, u16, u16, u16)>,
484 /// Clickable rectangle for the news age toggle button (x, y, w, h).
485 pub news_age_button_rect: Option<(u16, u16, u16, u16)>,
486 /// Inner content rectangle of the sort dropdown menu when visible (x, y, w, h).
487 pub sort_menu_rect: Option<(u16, u16, u16, u16)>,
488 /// Deadline after which the sort dropdown auto-closes.
489 pub sort_menu_auto_close_at: Option<Instant>,
490 // Sort result caching for O(1) sort mode switching
491 /// Cached sort order for `RepoThenName` mode (indices into `results`).
492 pub sort_cache_repo_name: Option<Vec<usize>>,
493 /// Cached sort order for `AurPopularityThenOfficial` mode (indices into `results`).
494 pub sort_cache_aur_popularity: Option<Vec<usize>>,
495 /// Signature of results used to validate caches (order-insensitive hash of names).
496 pub sort_cache_signature: Option<u64>,
497
498 // Results options UI (top-right dropdown)
499 /// Whether the options dropdown is currently visible.
500 pub options_menu_open: bool,
501 /// Clickable rectangle for the options button in the Results title (x, y, w, h).
502 pub options_button_rect: Option<(u16, u16, u16, u16)>,
503 /// Inner content rectangle of the options dropdown menu when visible (x, y, w, h).
504 pub options_menu_rect: Option<(u16, u16, u16, u16)>,
505
506 // Panels dropdown UI (left of Options)
507 /// Whether the panels dropdown is currently visible.
508 pub panels_menu_open: bool,
509 /// Clickable rectangle for the panels button in the Results title (x, y, w, h).
510 pub panels_button_rect: Option<(u16, u16, u16, u16)>,
511 /// Inner content rectangle of the panels dropdown menu when visible (x, y, w, h).
512 pub panels_menu_rect: Option<(u16, u16, u16, u16)>,
513
514 // Config/Lists dropdown UI (left of Panels)
515 /// Whether the Config/Lists dropdown is currently visible.
516 pub config_menu_open: bool,
517 /// Clickable rectangle for the Config/Lists button in the Results title (x, y, w, h).
518 pub config_button_rect: Option<(u16, u16, u16, u16)>,
519 /// Inner content rectangle of the Config/Lists dropdown menu when visible (x, y, w, h).
520 pub config_menu_rect: Option<(u16, u16, u16, u16)>,
521
522 // Artix filter dropdown UI (when specific repo filters are hidden)
523 /// Whether the Artix filter dropdown is currently visible.
524 pub artix_filter_menu_open: bool,
525 /// Inner content rectangle of the Artix filter dropdown menu when visible (x, y, w, h).
526 pub artix_filter_menu_rect: Option<(u16, u16, u16, u16)>,
527
528 // Collapsed menu dropdown UI (when window is too narrow for all three buttons)
529 /// Whether the collapsed menu dropdown is currently visible.
530 pub collapsed_menu_open: bool,
531 /// Clickable rectangle for the collapsed menu button in the Results title (x, y, w, h).
532 pub collapsed_menu_button_rect: Option<(u16, u16, u16, u16)>,
533 /// Inner content rectangle of the collapsed menu dropdown when visible (x, y, w, h).
534 pub collapsed_menu_rect: Option<(u16, u16, u16, u16)>,
535
536 /// Whether Results is currently showing only explicitly installed packages.
537 pub installed_only_mode: bool,
538 /// Which right subpane is focused when installed-only mode splits the pane.
539 pub right_pane_focus: RightPaneFocus,
540 /// Visual marker style for packages added to lists (user preference cached at startup).
541 pub package_marker: crate::theme::PackageMarker,
542
543 // Results filters UI
544 /// Whether to include AUR packages in the Results view.
545 pub results_filter_show_aur: bool,
546 /// Whether to include packages from the `core` repo in the Results view.
547 pub results_filter_show_core: bool,
548 /// Whether to include packages from the `extra` repo in the Results view.
549 pub results_filter_show_extra: bool,
550 /// Whether to include packages from the `multilib` repo in the Results view.
551 pub results_filter_show_multilib: bool,
552 /// Whether to include packages from the `eos` repo in the Results view.
553 pub results_filter_show_eos: bool,
554 /// Whether to include packages from `cachyos*` repos in the Results view.
555 pub results_filter_show_cachyos: bool,
556 /// Whether to include packages from Artix Linux repos in the Results view.
557 pub results_filter_show_artix: bool,
558 /// Whether to include packages from Artix omniverse repo in the Results view.
559 pub results_filter_show_artix_omniverse: bool,
560 /// Whether to include packages from Artix universe repo in the Results view.
561 pub results_filter_show_artix_universe: bool,
562 /// Whether to include packages from Artix lib32 repo in the Results view.
563 pub results_filter_show_artix_lib32: bool,
564 /// Whether to include packages from Artix galaxy repo in the Results view.
565 pub results_filter_show_artix_galaxy: bool,
566 /// Whether to include packages from Artix world repo in the Results view.
567 pub results_filter_show_artix_world: bool,
568 /// Whether to include packages from Artix system repo in the Results view.
569 pub results_filter_show_artix_system: bool,
570 /// Whether to include packages labeled as `manjaro` in the Results view.
571 pub results_filter_show_manjaro: bool,
572 /// Clickable rectangle for the AUR filter toggle in the Results title (x, y, w, h).
573 pub results_filter_aur_rect: Option<(u16, u16, u16, u16)>,
574 /// Clickable rectangle for the core filter toggle in the Results title (x, y, w, h).
575 pub results_filter_core_rect: Option<(u16, u16, u16, u16)>,
576 /// Clickable rectangle for the extra filter toggle in the Results title (x, y, w, h).
577 pub results_filter_extra_rect: Option<(u16, u16, u16, u16)>,
578 /// Clickable rectangle for the multilib filter toggle in the Results title (x, y, w, h).
579 pub results_filter_multilib_rect: Option<(u16, u16, u16, u16)>,
580 /// Clickable rectangle for the EOS filter toggle in the Results title (x, y, w, h).
581 pub results_filter_eos_rect: Option<(u16, u16, u16, u16)>,
582 /// Clickable rectangle for the `CachyOS` filter toggle in the Results title (x, y, w, h).
583 pub results_filter_cachyos_rect: Option<(u16, u16, u16, u16)>,
584 /// Clickable rectangle for the Artix filter toggle in the Results title (x, y, w, h).
585 pub results_filter_artix_rect: Option<(u16, u16, u16, u16)>,
586 /// Clickable rectangle for the Artix omniverse filter toggle in the Results title (x, y, w, h).
587 pub results_filter_artix_omniverse_rect: Option<(u16, u16, u16, u16)>,
588 /// Clickable rectangle for the Artix universe filter toggle in the Results title (x, y, w, h).
589 pub results_filter_artix_universe_rect: Option<(u16, u16, u16, u16)>,
590 /// Clickable rectangle for the Artix lib32 filter toggle in the Results title (x, y, w, h).
591 pub results_filter_artix_lib32_rect: Option<(u16, u16, u16, u16)>,
592 /// Clickable rectangle for the Artix galaxy filter toggle in the Results title (x, y, w, h).
593 pub results_filter_artix_galaxy_rect: Option<(u16, u16, u16, u16)>,
594 /// Clickable rectangle for the Artix world filter toggle in the Results title (x, y, w, h).
595 pub results_filter_artix_world_rect: Option<(u16, u16, u16, u16)>,
596 /// Clickable rectangle for the Artix system filter toggle in the Results title (x, y, w, h).
597 pub results_filter_artix_system_rect: Option<(u16, u16, u16, u16)>,
598 /// Clickable rectangle for the Manjaro filter toggle in the Results title (x, y, w, h).
599 pub results_filter_manjaro_rect: Option<(u16, u16, u16, u16)>,
600 /// Clickable rectangle for the fuzzy search mode indicator in the Search title (x, y, w, h).
601 pub fuzzy_indicator_rect: Option<(u16, u16, u16, u16)>,
602
603 // Background refresh of installed/explicit caches after package mutations
604 /// If `Some`, keep polling pacman/yay to refresh installed/explicit caches until this time.
605 pub refresh_installed_until: Option<Instant>,
606 /// Next scheduled time to poll caches while `refresh_installed_until` is active.
607 pub next_installed_refresh_at: Option<Instant>,
608
609 // Pending installs to detect completion and clear Install list
610 /// Names of packages we just triggered to install; when all appear installed, clear Install list.
611 pub pending_install_names: Option<Vec<String>>,
612
613 // Pending removals to detect completion and log
614 /// Names of packages we just triggered to remove; when all disappear, append to removed log.
615 pub pending_remove_names: Option<Vec<String>>,
616
617 // Dependency resolution cache for install list
618 /// Cached resolved dependencies for the current install list (updated in background).
619 pub install_list_deps: Vec<crate::state::modal::DependencyInfo>,
620 /// Reverse dependency summary for the current remove preflight modal (populated on demand).
621 pub remove_preflight_summary: Vec<crate::state::modal::ReverseRootSummary>,
622 /// Selected cascade removal mode for upcoming removals.
623 pub remove_cascade_mode: CascadeMode,
624 /// Whether dependency resolution is currently in progress.
625 pub deps_resolving: bool,
626 /// Path where the dependency cache is persisted as JSON.
627 pub deps_cache_path: PathBuf,
628 /// Dirty flag indicating `install_list_deps` needs to be saved.
629 pub deps_cache_dirty: bool,
630
631 // File resolution cache for install list
632 /// Cached resolved file changes for the current install list (updated in background).
633 pub install_list_files: Vec<crate::state::modal::PackageFileInfo>,
634 /// Whether file resolution is currently in progress.
635 pub files_resolving: bool,
636 /// Path where the file cache is persisted as JSON.
637 pub files_cache_path: PathBuf,
638 /// Dirty flag indicating `install_list_files` needs to be saved.
639 pub files_cache_dirty: bool,
640
641 // Service impact cache for install list
642 /// Cached resolved service impacts for the current install list (updated in background).
643 pub install_list_services: Vec<crate::state::modal::ServiceImpact>,
644 /// Whether service impact resolution is currently in progress.
645 pub services_resolving: bool,
646 /// Path where the service cache is persisted as JSON.
647 pub services_cache_path: PathBuf,
648 /// Dirty flag indicating `install_list_services` needs to be saved.
649 pub services_cache_dirty: bool,
650 /// Flag requesting that the runtime schedule service impact resolution for the active Preflight modal.
651 pub service_resolve_now: bool,
652 /// Identifier of the active service impact resolution request, if any.
653 pub active_service_request: Option<u64>,
654 /// Monotonic counter used to tag service impact resolution requests.
655 pub next_service_request_id: u64,
656 /// Signature of the package set currently queued for service impact resolution.
657 pub services_pending_signature: Option<(PreflightAction, Vec<String>)>,
658 /// Service restart decisions captured during the Preflight Services tab.
659 pub pending_service_plan: Vec<ServiceImpact>,
660
661 // Sandbox analysis cache for install list
662 /// Cached resolved sandbox information for the current install list (updated in background).
663 pub install_list_sandbox: Vec<crate::logic::sandbox::SandboxInfo>,
664 /// Whether sandbox resolution is currently in progress.
665 pub sandbox_resolving: bool,
666 /// Path where the sandbox cache is persisted as JSON.
667 pub sandbox_cache_path: PathBuf,
668 /// Dirty flag indicating `install_list_sandbox` needs to be saved.
669 pub sandbox_cache_dirty: bool,
670
671 // Preflight modal background resolution requests
672 /// Packages to resolve for preflight summary computation.
673 pub preflight_summary_items: Option<(Vec<PackageItem>, crate::state::modal::PreflightAction)>,
674 /// Packages to resolve for preflight dependency analysis (with action for forward/reverse).
675 pub preflight_deps_items: Option<(Vec<PackageItem>, crate::state::modal::PreflightAction)>,
676 /// Packages to resolve for preflight file analysis.
677 pub preflight_files_items: Option<Vec<PackageItem>>,
678 /// Packages to resolve for preflight service analysis.
679 pub preflight_services_items: Option<Vec<PackageItem>>,
680 /// AUR packages to resolve for preflight sandbox analysis (subset only).
681 pub preflight_sandbox_items: Option<Vec<PackageItem>>,
682 /// Whether preflight summary computation is in progress.
683 pub preflight_summary_resolving: bool,
684 /// Whether preflight dependency resolution is in progress.
685 pub preflight_deps_resolving: bool,
686 /// Whether preflight file resolution is in progress.
687 pub preflight_files_resolving: bool,
688 /// Whether preflight service resolution is in progress.
689 pub preflight_services_resolving: bool,
690 /// Whether preflight sandbox resolution is in progress.
691 pub preflight_sandbox_resolving: bool,
692 /// Last preflight dependency log state to suppress duplicate tick logs.
693 pub last_logged_preflight_deps_state: Option<(usize, bool, bool)>,
694 /// Cancellation flag for preflight operations (set to true when modal closes).
695 pub preflight_cancelled: std::sync::Arc<std::sync::atomic::AtomicBool>,
696
697 // Executor integration
698 /// Pending executor request to be sent when `PreflightExec` modal is ready.
699 pub pending_executor_request: Option<crate::install::ExecutorRequest>,
700 /// Pending post-summary computation request (items and success flag to compute summary for).
701 pub pending_post_summary_items: Option<(Vec<PackageItem>, Option<bool>)>,
702 /// Header chips to use when transitioning to `PreflightExec` modal.
703 pub pending_exec_header_chips: Option<crate::state::modal::PreflightHeaderChips>,
704 /// Custom command to execute after password prompt (for special packages like paru/yay/semgrep-bin).
705 pub pending_custom_command: Option<String>,
706 /// Update commands to execute after password prompt (for system update).
707 pub pending_update_commands: Option<Vec<String>>,
708 /// AUR update command to execute conditionally if pacman fails (for system update).
709 pub pending_aur_update_command: Option<String>,
710 /// Password obtained from password prompt, stored temporarily for reinstall confirmation flow.
711 pub pending_executor_password: Option<String>,
712 /// File database sync result from background thread (checked in tick handler).
713 pub pending_file_sync_result: Option<FileSyncResult>,
714}