Skip to content

Commit 0ac219a

Browse files
committed
fix(tray): gate selection until onboarding completes; add tests for tray label and selection logic; ui: simplify Upload section copy
1 parent 734fddb commit 0ac219a

File tree

3 files changed

+78
-18
lines changed

3 files changed

+78
-18
lines changed

src-tauri/src/lib.rs

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,35 @@ use tauri::menu::{CheckMenuItem, MenuBuilder, MenuItem, PredefinedMenuItem, Subm
6767
use whisper::cache::TranscriberCache;
6868
use window_manager::WindowManager;
6969

70+
/// Determines if a model should appear as selected in the tray given onboarding status
71+
pub(crate) fn should_mark_model_selected(
72+
onboarding_done: bool,
73+
model_name: &str,
74+
current_model: &str,
75+
) -> bool {
76+
onboarding_done && model_name == current_model
77+
}
78+
79+
/// Formats the tray's model label given onboarding status and an optional resolved display name
80+
pub(crate) fn format_tray_model_label(
81+
onboarding_done: bool,
82+
current_model: &str,
83+
resolved_display_name: Option<String>,
84+
) -> String {
85+
if !onboarding_done || current_model.is_empty() {
86+
"Model: None".to_string()
87+
} else {
88+
let name = resolved_display_name.unwrap_or_else(|| current_model.to_string());
89+
format!("Model: {}", name)
90+
}
91+
}
92+
7093
// Function to build the tray menu
7194
async fn build_tray_menu<R: tauri::Runtime>(
7295
app: &tauri::AppHandle<R>,
7396
) -> Result<tauri::menu::Menu<R>, Box<dyn std::error::Error>> {
7497
// Get current settings for menu state
75-
let (current_model, selected_microphone) = {
98+
let (current_model, selected_microphone, onboarding_done) = {
7699
match app.store("settings") {
77100
Ok(store) => {
78101
let model = store
@@ -82,9 +105,13 @@ async fn build_tray_menu<R: tauri::Runtime>(
82105
let microphone = store
83106
.get("selected_microphone")
84107
.and_then(|v| v.as_str().map(|s| s.to_string()));
85-
(model, microphone)
108+
let onboarding_done = store
109+
.get("onboarding_completed")
110+
.and_then(|v| v.as_bool())
111+
.unwrap_or(false);
112+
(model, microphone, onboarding_done)
86113
}
87-
Err(_) => ("".to_string(), None),
114+
Err(_) => ("".to_string(), None, false),
88115
}
89116
};
90117

@@ -123,7 +150,8 @@ async fn build_tray_menu<R: tauri::Runtime>(
123150
let mut model_check_items = Vec::new();
124151

125152
for (model_name, display_name) in available_models {
126-
let is_selected = model_name == current_model;
153+
// Do not show any selection until onboarding is completed
154+
let is_selected = should_mark_model_selected(onboarding_done, &model_name, &current_model);
127155
let model_item = CheckMenuItem::with_id(
128156
app,
129157
&format!("model_{}", model_name),
@@ -140,12 +168,11 @@ async fn build_tray_menu<R: tauri::Runtime>(
140168
model_items.push(item);
141169
}
142170

143-
let current_model_display = if current_model.is_empty() {
144-
"Model: None".to_string()
145-
} else {
171+
// Resolve a display name only if onboarding is complete and a model is set
172+
let resolved_display_name = if onboarding_done && !current_model.is_empty() {
146173
// Try Whisper first
147-
let display_name = if let Some(info) = whisper_models_info.get(&current_model) {
148-
info.display_name.clone()
174+
if let Some(info) = whisper_models_info.get(&current_model) {
175+
Some(info.display_name.clone())
149176
} else {
150177
// Try Parakeet registry
151178
let parakeet_manager = app.state::<crate::parakeet::ParakeetManager>();
@@ -154,16 +181,19 @@ async fn build_tray_menu<R: tauri::Runtime>(
154181
.into_iter()
155182
.find(|m| m.name == current_model)
156183
{
157-
pm.display_name
184+
Some(pm.display_name)
158185
} else if current_model == "soniox" {
159-
"Soniox (Cloud)".to_string()
186+
Some("Soniox (Cloud)".to_string())
160187
} else {
161-
current_model.clone()
188+
Some(current_model.clone())
162189
}
163-
};
164-
format!("Model: {}", display_name)
190+
}
191+
} else {
192+
None
165193
};
166194

195+
let current_model_display = format_tray_model_label(onboarding_done, &current_model, resolved_display_name);
196+
167197
Some(Submenu::with_id_and_items(
168198
app,
169199
"models",
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#[cfg(test)]
2+
mod tray_onboarding_tests {
3+
#[test]
4+
fn test_tray_label_when_onboarding_not_completed_returns_none() {
5+
let label = crate::format_tray_model_label(
6+
false,
7+
"parakeet-tdt-0.6b-v3",
8+
Some("Parakeet Tiny V3".to_string()),
9+
);
10+
assert_eq!(label, "Model: None");
11+
}
12+
13+
#[test]
14+
fn test_should_mark_model_selected_respects_onboarding() {
15+
assert_eq!(
16+
crate::should_mark_model_selected(false, "x", "x"),
17+
false
18+
);
19+
assert_eq!(crate::should_mark_model_selected(true, "x", "x"), true);
20+
assert_eq!(crate::should_mark_model_selected(true, "x", "y"), false);
21+
}
22+
23+
#[test]
24+
fn test_tray_label_when_onboarding_complete_uses_display_name() {
25+
let label = crate::format_tray_model_label(
26+
true,
27+
"parakeet-tdt-0.6b-v3",
28+
Some("Parakeet Tiny V3".to_string()),
29+
);
30+
assert_eq!(label, "Model: Parakeet Tiny V3");
31+
}
32+
}

src/components/sections/AudioUploadSection.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -172,11 +172,9 @@ export function AudioUploadSection() {
172172
<div className="px-6 py-4 border-b border-border/40">
173173
<div className="flex items-center justify-between">
174174
<div>
175-
<h1 className="text-2xl font-semibold">Audio Upload</h1>
175+
<h1 className="text-2xl font-semibold">Upload files</h1>
176176
<p className="text-sm text-muted-foreground mt-1">
177-
{settings?.current_model_engine === 'soniox'
178-
? 'Transcribe audio files through your connected cloud model'
179-
: 'Transcribe audio files locally with your downloaded model'}
177+
Transcribe audio or video files locally
180178
</p>
181179
</div>
182180
</div>

0 commit comments

Comments
 (0)