Coverage Report

Created: 2026-09-05 23:00

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
11
use super::{AppModel, DeviceState};
12
13
/// Builds the navigation bar with all Super STT pages inserted in order.
14
0
fn build_nav() -> nav_bar::Model {
15
0
    let mut nav = nav_bar::Model::default();
16
17
    // Models is the primary page (the active backend) — first in the rail and
18
    // active on launch. Library (manage/install backends) sits directly below.
19
0
    nav.insert()
20
0
        .text("Models")
21
0
        .data::<crate::state::Page>(crate::state::Page::Models)
22
0
        .icon(icons::phosphor(icons::BRAIN))
23
0
        .activate();
24
25
0
    nav.insert()
26
0
        .text("Library")
27
0
        .data::<crate::state::Page>(crate::state::Page::Library)
28
0
        .icon(icons::phosphor(icons::BOOKS));
29
30
0
    nav.insert()
31
0
        .text("Customization")
32
0
        .data::<crate::state::Page>(crate::state::Page::Customization)
33
0
        .icon(icons::phosphor(icons::GEAR));
34
35
0
    nav.insert()
36
0
        .text("Recording")
37
0
        .data::<crate::state::Page>(crate::state::Page::Recording)
38
0
        .icon(icons::phosphor(icons::MICROPHONE));
39
40
0
    nav.insert()
41
0
        .text("Input Simulation")
42
0
        .data::<crate::state::Page>(crate::state::Page::InputSimulation)
43
0
        .icon(icons::phosphor(icons::KEYBOARD));
44
45
0
    nav.insert()
46
0
        .text("Connection")
47
0
        .data::<crate::state::Page>(crate::state::Page::Connection)
48
0
        .icon(icons::phosphor(icons::PLUG));
49
50
0
    nav.insert()
51
0
        .text("Updates")
52
0
        .data::<crate::state::Page>(crate::state::Page::Updates)
53
0
        .icon(icons::phosphor(icons::ARROWS_CLOCKWISE));
54
55
0
    nav
56
0
}
57
58
/// Builds the initial batch of startup tasks (audio themes, daemon ping, data load).
59
0
fn initial_load_tasks(
60
0
    title_command: Task<cosmic::Action<Message>>,
61
0
) -> Task<cosmic::Action<Message>> {
62
    // Load audio themes on startup (always available)
63
0
    let load_themes = Task::perform(load_audio_themes(), |themes| {
64
0
        cosmic::Action::App(Message::Recording(RecordingMessage::AudioThemesLoaded(
65
0
            themes,
66
0
        )))
67
0
    });
68
69
    // Try to ping the daemon on startup
70
0
    let initial_ping = crate::core::app::handlers::tasks::ping_task();
71
72
    // Load initial data (models + device info) on startup
73
0
    let load_initial_data = Task::perform(
74
0
        async move {
75
            // Small delay to let daemon connection establish
76
0
            tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
77
0
        },
78
0
        |()| cosmic::Action::App(Message::Model(ModelMessage::LoadInitialData)),
79
    );
80
81
0
    Task::batch([title_command, load_themes, initial_ping, load_initial_data])
82
0
}
83
84
impl AppModel {
85
    /// Initializes the application with any given flags and startup commands.
86
0
    pub(super) fn init_model(
87
0
        core: cosmic::Core,
88
0
        _flags: (),
89
0
    ) -> (Self, Task<cosmic::Action<Message>>) {
90
0
        let nav = build_nav();
91
92
        // Construct the app model with the runtime's core.
93
0
        let mut app = AppModel {
94
0
            core,
95
0
            context_page: ContextPage::default(),
96
0
            nav,
97
0
            // Initialize Super STT state using proper socket path
98
0
            socket_path: super_stt_shared::validation::get_http_socket_path(),
99
0
            daemon_status: DaemonStatus::Disconnected,
100
0
            reconnect_retry: super_stt_shared::daemon::retry::RetryStrategy::for_initial_connection(
101
0
            ),
102
0
            recording_status: RecordingStatus::Idle,
103
0
            transcription_text: String::new(),
104
0
            preview_text: String::new(),
105
0
            audio_level: 0.0,
106
0
            is_speech_detected: false,
107
0
            audio_themes: Vec::new(),
108
0
            selected_audio_theme: AudioTheme::default(),
109
0
            last_non_silent_theme: AudioTheme::default(),
110
0
            udp_restart_counter: 0,
111
0
            last_udp_data: std::time::Instant::now(),
112
0
113
0
            // Initialize model state
114
0
            current_model: String::new(),
115
0
            current_source: String::new(),
116
0
            current_model_epoch: 0,
117
0
            model_operations: crate::state::model_operations::ModelOperations::opening(
118
0
                "Loading initial model state...".to_string(),
119
0
            ),
120
0
121
0
            // Initialize device state
122
0
            current_device: String::new(), // Empty until loaded from daemon
123
0
            gpu_info: Vec::new(),
124
0
            device_state: DeviceState::Ready,
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
            // Replaced by the daemon's own state as soon as the settings load
130
0
            // completes; the default is the daemon's default too (off).
131
0
            post_processor: crate::daemon::client::StageState::default(),
132
0
            staged_picks: crate::state::staged_picks::StagedPicks::default(),
133
0
            device_offers: crate::state::device_offers::DeviceOffers::default(),
134
0
            stage_catalog: crate::state::stage_catalog::StageCatalog::default(),
135
0
            recording_stop_mode:
136
0
                super_stt_shared::models::recording_stop_mode::RecordingStopMode::default(),
137
0
            write_method: super_stt_shared::models::write_method::WriteMethod::default(),
138
0
            write_method_test_text: String::new(),
139
0
            resolved_write_method: None,
140
0
            write_method_test_countdown: None,
141
0
            notification_method:
142
0
                super_stt_shared::models::notification_method::NotificationMethod::default(),
143
0
            volume: 100,
144
0
            last_committed_volume: 100,
145
0
146
0
            // Custom models directory
147
0
            custom_models_dir: None,
148
0
            custom_models_dir_input: String::new(),
149
0
150
0
            // Models page UI state
151
0
            models_page: crate::state::models_page::ModelsPageState::default(),
152
0
153
0
            // Transcription language state
154
0
            language: crate::state::language::LanguageState::default(),
155
0
156
0
            // Backend catalog + per-backend configuration state
157
0
            backends: Vec::new(),
158
0
            backend_secret_inputs: HashMap::new(),
159
0
            backend_secret_configured: HashMap::new(),
160
0
            backend_option_inputs: HashMap::new(),
161
0
162
0
            // Registry state
163
0
            registry: crate::state::registry::RegistryState::default(),
164
0
165
0
            // Self-update state
166
0
            update: crate::state::update::UpdateState::default(),
167
0
168
0
            // No pending scoped action error at startup.
169
0
            action_error: None,
170
0
        };
171
172
        // Create startup commands
173
0
        let title_command = app.update_title();
174
0
        (app, initial_load_tasks(title_command))
175
0
    }
176
}