Skip to content

Commit d709cb6

Browse files
committed
release: bump version to 0.7.10
- fix overlay positioning on external monitors (work_area validation fallback) - hide Gemini API from model selector when no API key configured - exclude Gemini from local model availability checks in onboarding - add skip button on accessibility permissions onboarding - skip accessibility checks for dev flavor (com.melvynx.parler.dev) - add i18n skip translations for all 17 languages - code formatting (prettier + cargo fmt)
1 parent 02e684e commit d709cb6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+199
-71
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "parler-app",
33
"private": true,
4-
"version": "0.7.9",
4+
"version": "0.7.10",
55
"type": "module",
66
"scripts": {
77
"dev": "vite",

src-tauri/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src-tauri/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "parler"
3-
version = "0.7.9"
3+
version = "0.7.10"
44
description = "Parler"
55
authors = ["cjpais"]
66
edition = "2021"

src-tauri/src/actions.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,10 @@ impl ShortcutAction for TranscribeAction {
434434
long_model_id
435435
);
436436
if let Err(e) = tm.load_model(long_model_id) {
437-
warn!("Failed to load long audio model '{}': {}, using current model", long_model_id, e);
437+
warn!(
438+
"Failed to load long audio model '{}': {}, using current model",
439+
long_model_id, e
440+
);
438441
} else {
439442
switched_model = true;
440443
}

src-tauri/src/commands/history.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,7 @@ pub async fn reprocess_history_entry(
9292
return Err("Audio file not found".to_string());
9393
}
9494

95-
let samples =
96-
crate::audio_toolkit::load_wav_file(&audio_path).map_err(|e| e.to_string())?;
95+
let samples = crate::audio_toolkit::load_wav_file(&audio_path).map_err(|e| e.to_string())?;
9796

9897
let previous_model = transcription_manager.get_current_model();
9998

src-tauri/src/commands/models.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::managers::model::{ModelInfo, ModelManager};
1+
use crate::managers::model::{EngineType, ModelInfo, ModelManager};
22
use crate::managers::transcription::TranscriptionManager;
33
use crate::settings::{get_settings, write_settings};
44
use std::sync::Arc;
@@ -119,7 +119,9 @@ pub async fn has_any_models_available(
119119
model_manager: State<'_, Arc<ModelManager>>,
120120
) -> Result<bool, String> {
121121
let models = model_manager.get_available_models();
122-
Ok(models.iter().any(|m| m.is_downloaded))
122+
Ok(models
123+
.iter()
124+
.any(|m| m.is_downloaded && !matches!(m.engine_type, EngineType::GeminiApi)))
123125
}
124126

125127
#[tauri::command]
@@ -128,8 +130,9 @@ pub async fn has_any_models_or_downloads(
128130
model_manager: State<'_, Arc<ModelManager>>,
129131
) -> Result<bool, String> {
130132
let models = model_manager.get_available_models();
131-
// Return true if any models are downloaded OR if any downloads are in progress
132-
Ok(models.iter().any(|m| m.is_downloaded))
133+
Ok(models.iter().any(|m| {
134+
!matches!(m.engine_type, EngineType::GeminiApi) && (m.is_downloaded || m.is_downloading)
135+
}))
133136
}
134137

135138
#[tauri::command]

src-tauri/src/gemini_client.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,7 @@ pub async fn transcribe_audio(api_key: &str, model: &str, audio_samples: &[f32])
113113
headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
114114
headers.insert(
115115
"x-goog-api-key",
116-
HeaderValue::from_str(api_key)
117-
.map_err(|e| anyhow::anyhow!("Invalid API key: {}", e))?,
116+
HeaderValue::from_str(api_key).map_err(|e| anyhow::anyhow!("Invalid API key: {}", e))?,
118117
);
119118

120119
let client = reqwest::Client::new();

src-tauri/src/managers/model.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,9 @@ impl ModelManager {
403403
ModelInfo {
404404
id: "gemini-api".to_string(),
405405
name: "Gemini API".to_string(),
406-
description: "Cloud-based transcription via Google Gemini. Requires API key and internet.".to_string(),
406+
description:
407+
"Cloud-based transcription via Google Gemini. Requires API key and internet."
408+
.to_string(),
407409
filename: "".to_string(),
408410
url: None,
409411
size_mb: 0,
@@ -558,11 +560,13 @@ impl ModelManager {
558560
}
559561
}
560562

561-
// If no model is selected, pick the first downloaded one
563+
// If no model is selected, pick the first downloaded local model.
564+
// Gemini is cloud-only and should not be auto-selected.
562565
if settings.selected_model.is_empty() {
563-
// Find the first available (downloaded) model
564566
let models = self.available_models.lock().unwrap();
565-
if let Some(available_model) = models.values().find(|model| model.is_downloaded) {
567+
if let Some(available_model) = models.values().find(|model| {
568+
model.is_downloaded && !matches!(model.engine_type, EngineType::GeminiApi)
569+
}) {
566570
info!(
567571
"Auto-selecting model: {} ({})",
568572
available_model.id, available_model.name
@@ -1133,7 +1137,10 @@ impl ModelManager {
11331137
.ok_or_else(|| anyhow::anyhow!("Model not found: {}", model_id))?;
11341138

11351139
if matches!(model_info.engine_type, EngineType::GeminiApi) {
1136-
return Err(anyhow::anyhow!("Cloud model has no local path: {}", model_id));
1140+
return Err(anyhow::anyhow!(
1141+
"Cloud model has no local path: {}",
1142+
model_id
1143+
));
11371144
}
11381145

11391146
if !model_info.is_downloaded {

src-tauri/src/managers/transcription.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -485,11 +485,7 @@ impl TranscriptionManager {
485485
// so block_in_place tells tokio to move its work off this thread first.
486486
let result = tokio::task::block_in_place(|| {
487487
tokio::runtime::Handle::current().block_on(
488-
crate::gemini_client::transcribe_audio(
489-
&api_key,
490-
&gemini_model,
491-
&audio,
492-
),
488+
crate::gemini_client::transcribe_audio(&api_key, &gemini_model, &audio),
493489
)
494490
})?;
495491

src-tauri/src/overlay.rs

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -232,28 +232,56 @@ fn get_monitor_with_cursor(app_handle: &AppHandle) -> Option<tauri::Monitor> {
232232

233233
fn calculate_overlay_position(app_handle: &AppHandle) -> Option<(f64, f64)> {
234234
if let Some(monitor) = get_monitor_with_cursor(app_handle) {
235-
let work_area = monitor.work_area();
236235
let scale = monitor.scale_factor();
237-
let work_area_width = work_area.size.width as f64 / scale;
238-
let work_area_height = work_area.size.height as f64 / scale;
239-
let work_area_x = work_area.position.x as f64 / scale;
240-
let work_area_y = work_area.position.y as f64 / scale;
236+
let monitor_x = monitor.position().x as f64 / scale;
237+
let monitor_y = monitor.position().y as f64 / scale;
238+
let monitor_width = monitor.size().width as f64 / scale;
239+
let monitor_height = monitor.size().height as f64 / scale;
240+
241+
let work_area = monitor.work_area();
242+
let wa_x = work_area.position.x as f64 / scale;
243+
let wa_y = work_area.position.y as f64 / scale;
244+
let wa_w = work_area.size.width as f64 / scale;
245+
let wa_h = work_area.size.height as f64 / scale;
246+
247+
// Validate work_area: on macOS, work_area() can return bogus values
248+
// for external monitors. Detect this by checking if work_area extends
249+
// beyond the monitor bounds.
250+
let wa_bottom = wa_y + wa_h;
251+
let monitor_bottom = monitor_y + monitor_height;
252+
let work_area_valid =
253+
wa_y >= monitor_y && wa_bottom <= monitor_bottom + 1.0 && wa_w <= monitor_width + 1.0;
254+
255+
let (area_x, area_y, area_w, area_h) = if work_area_valid {
256+
(wa_x, wa_y, wa_w, wa_h)
257+
} else {
258+
log::debug!(
259+
"work_area invalid for monitor (wa_bottom={:.0} > monitor_bottom={:.0}), using monitor bounds with safe offset",
260+
wa_bottom, monitor_bottom
261+
);
262+
// Use monitor bounds with a safe top offset for the macOS menu bar (~25px)
263+
let menu_bar_offset = 25.0;
264+
(
265+
monitor_x,
266+
monitor_y + menu_bar_offset,
267+
monitor_width,
268+
monitor_height - menu_bar_offset,
269+
)
270+
};
241271

242272
let settings = settings::get_settings(app_handle);
243273

244-
let x = work_area_x + (work_area_width - OVERLAY_WIDTH) / 2.0;
274+
let x = area_x + (area_w - OVERLAY_WIDTH) / 2.0;
245275
let y = match settings.overlay_position {
246-
OverlayPosition::Top => work_area_y + OVERLAY_TOP_OFFSET,
276+
OverlayPosition::Top => area_y + OVERLAY_TOP_OFFSET,
247277
OverlayPosition::Bottom | OverlayPosition::None => {
248-
work_area_y + work_area_height - OVERLAY_HEIGHT - OVERLAY_BOTTOM_OFFSET
278+
area_y + area_h - OVERLAY_HEIGHT - OVERLAY_BOTTOM_OFFSET
249279
}
250280
};
251281

252282
log::debug!(
253-
"Overlay position: ({:.0}, {:.0}) on monitor (scale={}, work_area=({:.0},{:.0},{:.0},{:.0}))",
254-
x, y,
255-
scale,
256-
work_area_x, work_area_y, work_area_width, work_area_height
283+
"Overlay position: ({:.0}, {:.0}) on monitor (scale={}, area=({:.0},{:.0},{:.0},{:.0}), wa_valid={})",
284+
x, y, scale, area_x, area_y, area_w, area_h, work_area_valid
257285
);
258286

259287
return Some((x, y));
@@ -356,7 +384,6 @@ pub fn create_recording_overlay(app_handle: &AppHandle) {
356384
}
357385

358386
fn show_overlay_state(app_handle: &AppHandle, state: &str) {
359-
// Check if overlay should be shown based on position setting
360387
let settings = settings::get_settings(app_handle);
361388
if settings.overlay_position == OverlayPosition::None {
362389
return;
@@ -367,7 +394,6 @@ fn show_overlay_state(app_handle: &AppHandle, state: &str) {
367394
if let Some(overlay_window) = app_handle.get_webview_window("recording_overlay") {
368395
let _ = overlay_window.show();
369396

370-
// On Windows, aggressively re-assert "topmost" in the native Z-order after showing
371397
#[cfg(target_os = "windows")]
372398
force_overlay_topmost(&overlay_window);
373399

0 commit comments

Comments
 (0)