diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c21f6d..7983b1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,24 @@ All notable changes to LLeMbas Desktop. The format follows ## [Unreleased] +## [0.7.1] — 2026-10-23 + +The microphone works, and LLeMbas CLI 1.5.1 inside. + +### Fixed + +- **Dictation said the microphone was blocked, with no way to allow it.** The server's page asks + for the microphone, and a webview grants it only if the app answers: WebKitGTK refuses whatever + it is not answered on, and shows no prompt of its own. Desktop now allows the microphone -- the + microphone only, and only for the server's page in the content view; a camera or a screen still + gets the platform's refusal. Media capture is also switched on explicitly, for the older + WebKitGTK the AppImage carries. + +### Changed + +- Carries LLeMbas CLI 1.5.1: an image counts by its size, so a screenshot no longer makes the + agent compact. + ## [0.7.0] — 2026-10-20 Carries LLeMbas CLI 1.5.0: harness spec 2.0.0 — background work as jobs the person can see, diff --git a/lembas-cli.version b/lembas-cli.version index bc80560..26ca594 100644 --- a/lembas-cli.version +++ b/lembas-cli.version @@ -1 +1 @@ -1.5.0 +1.5.1 diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 7205ae9..570c8d6 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -2064,7 +2064,7 @@ checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" [[package]] name = "llembas-desktop" -version = "0.7.0" +version = "0.7.1" dependencies = [ "gtk", "libc", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 6dcdf7b..d64cdc8 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "llembas-desktop" -version = "0.7.0" +version = "0.7.1" description = "LLeMbas Desktop: your LLeMbas server in a window, with the agent on this computer" authors = ["Jaroslav Beneš"] license = "MIT" @@ -17,7 +17,7 @@ crate-type = ["staticlib", "cdylib", "rlib"] tauri-build = { version = "2", features = [] } [dependencies] -tauri = { version = "2", features = ["tray-icon", "unstable"] } +tauri = { version = "2.12", features = ["tray-icon", "unstable"] } tauri-plugin-dialog = "2" tauri-plugin-opener = "2" tauri-plugin-single-instance = "2" diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index db1c998..2e0e4b3 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -30,7 +30,7 @@ use std::sync::Mutex; use serde::Serialize; use tauri::menu::{Menu, MenuItem, PredefinedMenuItem}; use tauri::tray::TrayIconBuilder; -use tauri::webview::WebviewBuilder; +use tauri::webview::{PermissionKind, PermissionResponse, WebviewBuilder}; use tauri::window::{Color, WindowBuilder}; use tauri::{ AppHandle, Emitter, LogicalPosition, LogicalSize, Manager, RunEvent, Url, Webview, WebviewUrl, @@ -425,22 +425,56 @@ fn build_main(handle: &AppHandle, target: Option) -> tauri::Result<()> { pin_titlebar(&bar); } let nav = handle.clone(); + let perm = handle.clone(); window.add_child( WebviewBuilder::new(CONTENT, url) .user_agent(&user_agent()) .background_color(theme.background()) .initialization_script(THEME_SCRIPT) - .on_navigation(move |url| allowed(&nav, url)), + .on_navigation(move |url| allowed(&nav, url)) + .on_permission_request(move |view, kind| permission(&perm, &view, kind)), LogicalPosition::new(FRAME, FRAME + TITLE_H), LogicalSize::new( size.width - 2.0 * FRAME, (size.height - TITLE_H - 2.0 * FRAME).max(0.0), ), )?; + #[cfg(target_os = "linux")] + if let Some(content) = handle.get_webview(CONTENT) { + enable_capture(&content); + } layout(&window); Ok(()) } +/// The server's dictation asks for the microphone, and a webview answers a permission request +/// only if its app does: WebKitGTK refuses anything it is not answered on, and has no prompt of +/// its own -- so the microphone simply said "Permission may be blocked" with no way to allow it. +/// Granted for the microphone, in the content view, once a server is chosen, and for nothing +/// else (a camera or a screen gets the platform's default, which on Linux is no). The content view +/// never shows another origin: `allowed` sends every other site to the system browser. +fn permission(handle: &AppHandle, view: &Webview, kind: PermissionKind) -> PermissionResponse { + if kind != PermissionKind::Microphone || view.label() != CONTENT { + return PermissionResponse::Default; + } + if handle.state::().settings().server.is_some() { + PermissionResponse::Allow + } else { + PermissionResponse::Deny + } +} + +/// Media capture on, explicitly: current WebKitGTK has it on by default, but the AppImage carries +/// the older WebKitGTK of the system it was built on. +#[cfg(target_os = "linux")] +fn enable_capture(view: &Webview) { + let _ = view.with_webview(|pv| { + use gtk::prelude::*; + let settings: gtk::glib::Object = pv.inner().property("settings"); + settings.set_property("enable-media-stream", true); + }); +} + /// On Linux, Tauri packs every webview of a window into the window's GTK box, each with an equal /// share of the height, and positions and sizes set on a webview are not applied there. So the /// title bar is told directly: a fixed height, no share of what is left (the content takes it).