super-stt-app/src/core/app/init.rs
Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-3.0-only |
2 | | |
3 | | use crate::daemon::client::load_audio_themes; |
4 | | use crate::state::{AudioTheme, ContextPage, DaemonStatus, RecordingStatus}; |
5 | | use crate::ui::icons; |
6 | | use crate::ui::messages::{Message, ModelMessage, RecordingMessage}; |
7 | | use cosmic::prelude::*; |
8 | | use cosmic::widget::nav_bar; |
9 | | use std::collections::HashMap; |
10 | | use super_stt_shared::models::provider::Provider; |
11 | | |
12 | | use super::{AppModel, DeviceState, ModelOperationState}; |
13 | | |
14 | | /// Builds the navigation bar with all Super STT pages inserted in order. |
15 | 0 | fn build_nav() -> nav_bar::Model { |
16 | 0 | let mut nav = nav_bar::Model::default(); |
17 | | |
18 | | // Models is the primary page (the active backend) — first in the rail and |
19 | | // active on launch. Library (manage/install backends) sits directly below. |
20 | 0 | nav.insert() |
21 | 0 | .text("Models") |
22 | 0 | .data::<crate::state::Page>(crate::state::Page::Models) |
23 | 0 | .icon(icons::phosphor(icons::BRAIN)) |
24 | 0 | .activate(); |
25 | | |
26 | 0 | nav.insert() |
27 | 0 | .text("Library") |
28 | 0 | .data::<crate::state::Page>(crate::state::Page::Library) |
29 | 0 | .icon(icons::phosphor(icons::BOOKS)); |
30 | | |
31 | 0 | nav.insert() |
32 | 0 | .text("Customization") |
33 | 0 | .data::<crate::state::Page>(crate::state::Page::Customization) |
34 | 0 | .icon(icons::phosphor(icons::GEAR)); |
35 | | |
36 | 0 | nav.insert() |
37 | 0 | .text("Recording") |
38 | 0 | .data::<crate::state::Page>(crate::state::Page::Recording) |
39 | 0 | .icon(icons::phosphor(icons::MICROPHONE)); |
40 | | |
41 | 0 | nav.insert() |
42 | 0 | .text("Input Simulation") |
43 | 0 | .data::<crate::state::Page>(crate::state::Page::InputSimulation) |
44 | 0 | .icon(icons::phosphor(icons::KEYBOARD)); |
45 | | |
46 | 0 | nav.insert() |
47 | 0 | .text("Connection") |
48 | 0 | .data::<crate::state::Page>(crate::state::Page::Connection) |
49 | 0 | .icon(icons::phosphor(icons::PLUG)); |
50 | | |
51 | 0 | nav |
52 | 0 | } |
53 | | |
54 | | /// Builds the initial batch of startup tasks (audio themes, daemon ping, data load). |
55 | 0 | fn initial_load_tasks( |
56 | 0 | title_command: Task<cosmic::Action<Message>>, |
57 | 0 | ) -> Task<cosmic::Action<Message>> { |
58 | | // Load audio themes on startup (always available) |
59 | 0 | let load_themes = Task::perform(load_audio_themes(), |themes| { |
60 | 0 | cosmic::Action::App(Message::Recording(RecordingMessage::AudioThemesLoaded( |
61 | 0 | themes, |
62 | 0 | ))) |
63 | 0 | }); |
64 | | |
65 | | // Try to ping the daemon on startup |
66 | 0 | let initial_ping = crate::core::app::handlers::tasks::ping_task(); |
67 | | |
68 | | // Load initial data (models + device info) on startup |
69 | 0 | let load_initial_data = Task::perform( |
70 | 0 | async move { |
71 | | // Small delay to let daemon connection establish |
72 | 0 | tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; |
73 | 0 | }, |
74 | 0 | |()| cosmic::Action::App(Message::Model(ModelMessage::LoadInitialData)), |
75 | | ); |
76 | | |
77 | 0 | Task::batch([title_command, load_themes, initial_ping, load_initial_data]) |
78 | 0 | } |
79 | | |
80 | | impl AppModel { |
81 | | /// Initializes the application with any given flags and startup commands. |
82 | 0 | pub(super) fn init_model( |
83 | 0 | core: cosmic::Core, |
84 | 0 | _flags: (), |
85 | 0 | ) -> (Self, Task<cosmic::Action<Message>>) { |
86 | 0 | let nav = build_nav(); |
87 | | |
88 | | // Construct the app model with the runtime's core. |
89 | 0 | let mut app = AppModel { |
90 | 0 | core, |
91 | 0 | context_page: ContextPage::default(), |
92 | 0 | nav, |
93 | 0 | // Initialize Super STT state using proper socket path |
94 | 0 | socket_path: super_stt_shared::validation::get_http_socket_path(), |
95 | 0 | daemon_status: DaemonStatus::Disconnected, |
96 | 0 | reconnect_retry: super_stt_shared::daemon::retry::RetryStrategy::for_initial_connection( |
97 | 0 | ), |
98 | 0 | recording_status: RecordingStatus::Idle, |
99 | 0 | transcription_text: String::new(), |
100 | 0 | audio_level: 0.0, |
101 | 0 | is_speech_detected: false, |
102 | 0 | audio_themes: Vec::new(), |
103 | 0 | selected_audio_theme: AudioTheme::default(), |
104 | 0 | last_non_silent_theme: AudioTheme::default(), |
105 | 0 | udp_restart_counter: 0, |
106 | 0 | last_udp_data: std::time::Instant::now(), |
107 | 0 |
|
108 | 0 | // Initialize model state |
109 | 0 | available_models: Vec::new(), |
110 | 0 | current_model: String::new(), |
111 | 0 | current_provider: Provider::default(), |
112 | 0 | current_source: String::new(), |
113 | 0 | current_model_epoch: 0, |
114 | 0 | model_operation_state: ModelOperationState::Loading { |
115 | 0 | target_model: String::new(), |
116 | 0 | status_message: "Loading initial model state...".to_string(), |
117 | 0 | }, |
118 | 0 |
|
119 | 0 | // Initialize device state |
120 | 0 | current_device: String::new(), // Empty until loaded from daemon |
121 | 0 | available_devices: vec!["cpu".to_string()], // Default until loaded from daemon |
122 | 0 | gpu_info: Vec::new(), |
123 | 0 | device_state: DeviceState::Ready, |
124 | 0 | last_switch_progress_at: None, |
125 | 0 | last_event_timestamp: None, |
126 | 0 |
|
127 | 0 | // Initialize preview typing state (disabled by default as beta feature) |
128 | 0 | preview_typing_enabled: false, |
129 | 0 | recording_stop_mode: |
130 | 0 | super_stt_shared::models::recording_stop_mode::RecordingStopMode::default(), |
131 | 0 | write_method: super_stt_shared::models::write_method::WriteMethod::default(), |
132 | 0 | volume: 100, |
133 | 0 | last_committed_volume: 100, |
134 | 0 |
|
135 | 0 | // Custom models directory |
136 | 0 | custom_models_dir: None, |
137 | 0 | custom_models_dir_input: String::new(), |
138 | 0 |
|
139 | 0 | // Models page UI state |
140 | 0 | models_page: crate::state::models_page::ModelsPageState::default(), |
141 | 0 |
|
142 | 0 | // Transcription language state |
143 | 0 | language: crate::state::language::LanguageState::default(), |
144 | 0 |
|
145 | 0 | // Backend catalog + per-backend configuration state |
146 | 0 | backends: Vec::new(), |
147 | 0 | backend_secret_inputs: HashMap::new(), |
148 | 0 | backend_secret_configured: HashMap::new(), |
149 | 0 | backend_option_inputs: HashMap::new(), |
150 | 0 |
|
151 | 0 | // Registry state |
152 | 0 | registry: crate::state::registry::RegistryState::default(), |
153 | 0 |
|
154 | 0 | // No pending scoped action error at startup. |
155 | 0 | action_error: None, |
156 | 0 | }; |
157 | | |
158 | | // Create startup commands |
159 | 0 | let title_command = app.update_title(); |
160 | 0 | (app, initial_load_tasks(title_command)) |
161 | 0 | } |
162 | | } |