Files
Lumotia/src-tauri/src/commands/windows.rs
Claude 89c63891fa chore: rebrand from Kon/Corbie to Magnotia
Replace all instances of the legacy product names "Kon" and "Corbie" with
"Magnotia" across user-facing copy, code identifiers, package names, bundle
ids, file paths, and documentation. Preserves the unrelated "konsole" (KDE
terminal) reference and the parent CORBEL company name.

- Renames 10 Rust crates (kon-* → magnotia-*) and the tauri binary
- Updates package.json, tauri.conf.json (productName + identifier)
- Renames CSS classes (kon-rh-* → magnotia-rh-*) and animations
- Renames brand and roadmap docs
- Regenerates Cargo.lock and package-lock.json

Verified: svelte-check passes; pure-rust crates compile under new names.
2026-04-30 13:06:55 +00:00

198 lines
7.4 KiB
Rust

// Multi-window support is desktop-only. Android Tauri apps run as a
// single Activity, so `WebviewWindowBuilder` is not available there.
// All four commands below have an Android stub that returns a clear
// error so frontend invokes don't panic — the frontend itself is
// expected to detect Android via `isAndroid()` and route the previously-
// secondary content (preview overlay, transcript viewer, task float)
// into routes inside the main window instead.
#[cfg(not(target_os = "android"))]
use tauri::{Emitter, Manager, WebviewUrl, WebviewWindowBuilder};
#[cfg(not(target_os = "android"))]
use crate::PreferencesScript;
#[cfg(target_os = "android")]
const ANDROID_MULTIWINDOW_ERR: &str =
"Multi-window is not supported on Android; this command is desktop-only";
#[cfg(target_os = "android")]
#[tauri::command]
pub async fn open_task_window(_app: tauri::AppHandle) -> Result<(), String> {
Err(ANDROID_MULTIWINDOW_ERR.into())
}
#[cfg(target_os = "android")]
#[tauri::command]
pub async fn open_preview_window(_app: tauri::AppHandle) -> Result<(), String> {
Err(ANDROID_MULTIWINDOW_ERR.into())
}
#[cfg(target_os = "android")]
#[tauri::command]
pub async fn close_preview_window(_app: tauri::AppHandle) -> Result<(), String> {
Err(ANDROID_MULTIWINDOW_ERR.into())
}
#[cfg(target_os = "android")]
#[tauri::command]
pub async fn open_viewer_window(_app: tauri::AppHandle) -> Result<(), String> {
Err(ANDROID_MULTIWINDOW_ERR.into())
}
/// Open a floating always-on-top task window.
#[cfg(not(target_os = "android"))]
#[tauri::command]
pub async fn open_task_window(app: tauri::AppHandle) -> Result<(), String> {
if let Some(window) = app.get_webview_window("tasks-float") {
window.show().map_err(|e| e.to_string())?;
window.set_focus().map_err(|e| e.to_string())?;
app.emit("task-window-focus", ())
.map_err(|e| e.to_string())?;
return Ok(());
}
// On Linux we use native KWin/Mutter decorations so resize and drag
// are handled by the compositor. Tauri's frameless path on Wayland
// doesn't honour diagonal resize reliably and Tauri's own drag
// region adds latency on webkit2gtk. macOS and Windows keep the
// custom frameless chrome drawn by the Titlebar component.
let use_native_decorations = cfg!(target_os = "linux");
let mut builder =
WebviewWindowBuilder::new(&app, "tasks-float", WebviewUrl::App("/float".into()))
.title("Magnotia Tasks")
.inner_size(480.0, 520.0)
.min_inner_size(360.0, 480.0)
.always_on_top(true)
.decorations(use_native_decorations)
.resizable(true);
// Inject preferences before Svelte mounts
if let Some(script) = app.try_state::<PreferencesScript>() {
if !script.0.is_empty() {
builder = builder.initialization_script(&script.0);
}
}
builder.build().map_err(|e| e.to_string())?;
Ok(())
}
/// Open the always-on-top transcription preview window. Idempotent — an
/// existing window is shown and focused; otherwise a new one is built.
/// The preview is passive: it subscribes to the `transcription-result`
/// event and the cross-window `preview-*` events that DictationPage fires
/// as it moves through the phases of a dictation run.
#[cfg(not(target_os = "android"))]
#[tauri::command]
pub async fn open_preview_window(app: tauri::AppHandle) -> Result<(), String> {
if let Some(window) = app.get_webview_window("transcription-preview") {
window.show().map_err(|e| e.to_string())?;
// Intentionally NOT set_focus — the preview window is meant to
// appear next to whatever the user is typing into; stealing focus
// defeats the whole point.
return Ok(());
}
let use_native_decorations = cfg!(target_os = "linux");
// Preview is a transient helper overlay, not a primary surface. It
// should (a) follow the user across virtual desktops — otherwise the
// overlay vanishes the moment they switch workspace mid-dictation —
// and (b) stay out of the Alt+Tab / taskbar lists. skip_taskbar covers
// both on KWin (KWin's default Alt+Tab list reads _NET_WM_STATE_SKIP_TASKBAR);
// visible_on_all_workspaces sets _NET_WM_STATE_STICKY via GTK on X11
// / XWayland so the overlay is pinned. Matches the ergonomic OpenWhispr
// PR #183 shipped for KDE Plasma Wayland.
//
// Built hidden so we can set the Linux WindowTypeHint before the
// window maps — GTK3 only honours the hint pre-realize.
let mut builder = WebviewWindowBuilder::new(
&app,
"transcription-preview",
WebviewUrl::App("/preview".into()),
)
.title("Magnotia — Preview")
.inner_size(420.0, 200.0)
.min_inner_size(360.0, 140.0)
.max_inner_size(520.0, 360.0)
.always_on_top(true)
.skip_taskbar(true)
.visible_on_all_workspaces(true)
.focused(false)
.visible(false)
.decorations(use_native_decorations)
.resizable(true);
if let Some(script) = app.try_state::<PreferencesScript>() {
if !script.0.is_empty() {
builder = builder.initialization_script(&script.0);
}
}
let window = builder.build().map_err(|e| e.to_string())?;
// Defence-in-depth for non-KDE compositors (Hyprland, Sway, GNOME
// Mutter) where SKIP_TASKBAR alone may not hide a window from the
// alt-tab switcher. Classifying as Utility signals to the compositor
// that this is an assistive auxiliary window — switchers and tilers
// treat it accordingly. Noop on KWin (already handled by skip_taskbar)
// but harmless. Must happen before show() per GTK3 docs.
#[cfg(target_os = "linux")]
{
use gdk::WindowTypeHint;
use gtk::prelude::GtkWindowExt;
if let Ok(gtk_window) = window.gtk_window() {
gtk_window.set_type_hint(WindowTypeHint::Utility);
}
}
window.show().map_err(|e| e.to_string())?;
Ok(())
}
/// Hide the transcription preview window without destroying it so the next
/// open is instant. Returns Ok even when no preview window exists.
#[cfg(not(target_os = "android"))]
#[tauri::command]
pub async fn close_preview_window(app: tauri::AppHandle) -> Result<(), String> {
if let Some(window) = app.get_webview_window("transcription-preview") {
window.hide().map_err(|e| e.to_string())?;
}
Ok(())
}
/// Open the transcript viewer window.
#[cfg(not(target_os = "android"))]
#[tauri::command]
pub async fn open_viewer_window(app: tauri::AppHandle) -> Result<(), String> {
if let Some(window) = app.get_webview_window("transcript-viewer") {
window.show().map_err(|e| e.to_string())?;
window.set_focus().map_err(|e| e.to_string())?;
return Ok(());
}
// See note in open_task_window for the Linux-vs-other platform split.
let use_native_decorations = cfg!(target_os = "linux");
let mut builder =
WebviewWindowBuilder::new(&app, "transcript-viewer", WebviewUrl::App("/viewer".into()))
.title("Magnotia - Transcription Editor")
.inner_size(600.0, 700.0)
.min_inner_size(560.0, 520.0)
.decorations(use_native_decorations)
.resizable(true);
// Inject preferences before Svelte mounts
if let Some(script) = app.try_state::<PreferencesScript>() {
if !script.0.is_empty() {
builder = builder.initialization_script(&script.0);
}
}
builder.build().map_err(|e| e.to_string())?;
Ok(())
}