Skip to main content

llm_manager/tui/app/
types.rs

1pub mod sub;
2use crate::config::Config;
3use crate::config::Profile;
4use crate::models::Backend;
5use crate::models::{
6    BenchTuneConfig, DiscoveredModel, ModelSettings, ModelState, SearchResult, SearchSort,
7    ServerMetrics,
8};
9use ratatui::layout::Rect;
10use ratatui::text::Line;
11use serde::{Deserialize, Serialize};
12use std::collections::HashMap;
13use std::sync::atomic::AtomicBool;
14use std::sync::{Arc, Mutex};
15
16// Re-export sub-structs
17pub use sub::{
18    BenchTuneState, DownloadState, EditState, LoadingState, LogState, PendingOperations,
19    PickerState, SearchState, ServerState, SettingsState, UIState,
20};
21
22/// Static cell for caching the API port string in help text.
23pub static API_PORT_CACHE: Mutex<(u16, String)> = Mutex::new((0, String::new()));
24
25/// State for an in-progress panel resize drag.
26pub struct ResizeState {
27    /// Starting X position of the mouse when drag began.
28    pub start_x: u16,
29    /// Starting left_pct value when drag began.
30    pub start_pct: u16,
31    /// The area of the top panels container (for border detection).
32    pub container: Rect,
33}
34
35/// Cache for the settings panel render output.
36pub struct SettingsRenderCache {
37    pub hash: u64,
38    pub selected: usize,
39    pub lines: Vec<Line<'static>>,
40}
41
42/// Which panel has focus.
43#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
44pub enum ActivePanel {
45    #[default]
46    Models,
47    Log,
48    ServerSettings,
49    LlmSettings,
50    Profiles,
51    SystemPromptPresets,
52    SearchReadme,
53    ActiveModel,
54    ModelInfo,
55    Downloads,
56}
57
58/// Mode for the models panel.
59#[derive(Debug, Clone)]
60pub enum ModelsMode {
61    /// Normal mode: list of local models.
62    List,
63    /// Search mode: searching HuggingFace.
64    Search {
65        query: String,
66        results: Vec<SearchResult>,
67        sort_by: SearchSort,
68        show_readme: bool,
69        page: usize,
70        /// Whether results are currently being loaded.
71        loading: bool,
72        /// Whether more results are available.
73        has_more: bool,
74    },
75    /// Files mode: listing available GGUF files for a model.
76    Files {
77        model_id: String,
78        files: Vec<(String, u64, String)>, // (filename, size, url)
79        selected_idx: Option<usize>,
80        previous_query: String,
81        previous_results: Vec<SearchResult>,
82        selected_result: Option<SearchResult>,
83    },
84    /// Benchmark tuning mode: running bench_tune on a model.
85    BenchTune,
86}
87
88/// Global mode that overlays all panels.
89#[derive(Debug, Clone, PartialEq)]
90pub enum GlobalMode {
91    Normal,
92    CmdLine {
93        cmd_line: String,
94    },
95    HostPicker {
96        entries: Vec<(String, String)>, // (ip, interface_name)
97        selected: usize,
98    },
99    BackendPicker {
100        entries: Vec<(Backend, Option<String>)>,
101        selected: usize,
102    },
103    Confirmation {
104        selected: bool,
105        kind: ConfirmationKind,
106        display_name: String,
107        detail: Option<String>,
108    },
109    RpcManager,
110    About,
111    MaxConcurrentPicker {
112        value: String,
113    },
114    SpecTypePicker {
115        entries: Vec<String>,
116        selected: usize,
117    },
118    YarnRoPESettings {
119        scale: String,
120        freq_base: String,
121        freq_scale: String,
122        selected_field: i32, // -1=enabled, 0=scale, 1=freq_base, 2=freq_scale
123        editing: bool,
124        edit_buffer: String,
125        edit_cursor_pos: usize,
126    },
127    BenchTuneSetup {
128        config: BenchTuneConfig,
129        selected_idx: usize,
130        editing_param: bool,
131        editing_param_field: i32,
132        param_edit_buffer: String,
133        param_edit_cursor_pos: usize,
134        bench_mode_selection: usize,
135        editing_prompt: bool,
136        editing_kwargs: bool,
137    },
138    PromptPicker {
139        entries: Vec<(String, String)>, // (name, description)
140        selected: usize,
141        editing: bool,
142        edit_buffer: String,
143        edit_cursor_pos: usize,
144        confirm_delete: bool,
145    },
146    ProfilePicker {
147        entries: Vec<(String, String)>, // (name, description)
148        selected: usize,
149        profiles: Vec<Profile>,
150    },
151    DashboardPicker {
152        enabled: bool,
153        port: String,
154        auth_key: String,
155        tls_enabled: bool,
156        tls_cert: String,
157        tls_key: String,
158        selected_field: i32, // -1=enabled, 0=port, 1=auth_key, 2=tls_enabled, 3=tls_cert, 4=tls_key
159        editing: bool,
160        edit_buffer: String,
161        edit_cursor_pos: usize,
162    },
163    DashboardUrl {
164        host: String,
165        port: String,
166        auth_key: String,
167        ws_enabled: bool,
168        tls_enabled: bool,
169    },
170    SearchInput {
171        buffer: String,
172        cursor_pos: usize,
173    },
174    GgufNaming {
175        explanation: crate::tui::gguf_naming::GgufExplanation,
176        filename: String,
177    },
178}
179
180#[derive(Debug, Clone, Copy, PartialEq, Eq)]
181pub enum ConfirmationKind {
182    Exit,
183    Reset,
184    Delete,
185    Unload,
186    DeleteBackend,
187}
188
189/// Scroll state for text that exceeds display width.
190#[derive(Debug, Clone)]
191pub struct TextScrollState {
192    pub offset: usize,
193    pub last_tick: std::time::Instant,
194    pub direction: i8,
195    pub hold_count: u8,
196    pub max_offset: usize,
197    pub visible: bool,
198}
199
200/// Phase of model loading.
201#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
202pub enum LoadingPhase {
203    ServerStarting,
204    LoadingModel,
205    LoadingMeta,
206    LoadingTensors,
207    ServerListening,
208    Complete,
209}
210
211impl LoadingPhase {
212    pub fn label(&self) -> &'static str {
213        match self {
214            LoadingPhase::ServerStarting => "Server starting...",
215            LoadingPhase::LoadingModel => "Loading model weights...",
216            LoadingPhase::LoadingMeta => "Loading metadata...",
217            LoadingPhase::LoadingTensors => "Loading tensors...",
218            LoadingPhase::ServerListening => "Server listening...",
219            LoadingPhase::Complete => "Ready",
220        }
221    }
222}
223
224/// The main application state.
225pub struct App {
226    // Core state
227    pub running: bool,
228    pub config: Config,
229    pub models: Vec<DiscoveredModel>,
230    pub selected_model_idx: Option<usize>,
231    pub models_mode: ModelsMode,
232    pub settings: ModelSettings,
233    pub model_settings_cache: ModelSettings,
234    pub model_states: HashMap<String, ModelState>,
235    pub metrics: ServerMetrics,
236    pub max_threads: u32,
237    pub cancelled: Option<Arc<AtomicBool>>,
238    pub server_mode: crate::models::ServerMode,
239    pub router_max_models: u32,
240    pub ws_server_handle: Option<tokio::task::JoinHandle<()>>,
241    pub background_tasks: HashMap<String, tokio::task::JoinHandle<()>>,
242
243    // Sub-structs
244    pub settings_state: SettingsState,
245    pub picker: PickerState,
246    pub download: DownloadState,
247    pub server: ServerState,
248    pub bench_tune: BenchTuneState,
249    pub log: LogState,
250    pub loading: LoadingState,
251    pub pending: PendingOperations,
252    pub search: SearchState,
253    pub ui: UIState,
254    pub edit: EditState,
255
256    // ── Scheduler ────────────────────────────────────────────
257    pub pending_tx: tokio::sync::mpsc::Sender<super::scheduler::PendingEvent>,
258    pub pending_rx: tokio::sync::mpsc::Receiver<super::scheduler::PendingEvent>,
259    pub server_ready: bool,
260}