Coverage Report

Created: 2026-07-21 00:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
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
}