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    Onboarding {
179        step: usize,
180    },
181}
182
183#[derive(Debug, Clone, Copy, PartialEq, Eq)]
184pub enum ConfirmationKind {
185    Exit,
186    Reset,
187    Delete,
188    Unload,
189    DeleteBackend,
190}
191
192/// Scroll state for text that exceeds display width.
193#[derive(Debug, Clone)]
194pub struct TextScrollState {
195    pub offset: usize,
196    pub last_tick: std::time::Instant,
197    pub direction: i8,
198    pub hold_count: u8,
199    pub max_offset: usize,
200    pub visible: bool,
201}
202
203/// Phase of model loading.
204#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
205pub enum LoadingPhase {
206    ServerStarting,
207    LoadingModel,
208    LoadingMeta,
209    LoadingTensors,
210    ServerListening,
211    Complete,
212}
213
214impl LoadingPhase {
215    pub fn label(&self) -> &'static str {
216        match self {
217            LoadingPhase::ServerStarting => "Server starting...",
218            LoadingPhase::LoadingModel => "Loading model weights...",
219            LoadingPhase::LoadingMeta => "Loading metadata...",
220            LoadingPhase::LoadingTensors => "Loading tensors...",
221            LoadingPhase::ServerListening => "Server listening...",
222            LoadingPhase::Complete => "Ready",
223        }
224    }
225}
226
227/// The main application state.
228pub struct App {
229    // Core state
230    pub running: bool,
231    pub config: Config,
232    pub models: Vec<DiscoveredModel>,
233    pub selected_model_idx: Option<usize>,
234    pub models_mode: ModelsMode,
235    pub settings: ModelSettings,
236    pub model_settings_cache: ModelSettings,
237    pub model_states: HashMap<String, ModelState>,
238    pub metrics: ServerMetrics,
239    pub max_threads: u32,
240    pub cancelled: Option<Arc<AtomicBool>>,
241    pub server_mode: crate::models::ServerMode,
242    pub router_max_models: u32,
243    pub ws_server_handle: Option<tokio::task::JoinHandle<()>>,
244    pub background_tasks: HashMap<String, tokio::task::JoinHandle<()>>,
245
246    // Sub-structs
247    pub settings_state: SettingsState,
248    pub picker: PickerState,
249    pub download: DownloadState,
250    pub server: ServerState,
251    pub bench_tune: BenchTuneState,
252    pub log: LogState,
253    pub loading: LoadingState,
254    pub pending: PendingOperations,
255    pub search: SearchState,
256    pub ui: UIState,
257    pub edit: EditState,
258
259    // ── Scheduler ────────────────────────────────────────────
260    pub pending_tx: tokio::sync::mpsc::Sender<super::pending_events::PendingEvent>,
261    pub pending_rx: tokio::sync::mpsc::Receiver<super::pending_events::PendingEvent>,
262    pub server_ready: bool,
263}