Fix paste-to-app, settings highlight, and titleBarStyle casing

Paste fix:
- Switch from std::process::Command to tokio::process::Command to avoid
  blocking the async runtime (was stalling the paste action)
- Use explicit AppleScript that activates the frontmost app before sending
  Cmd+V keystroke, making paste more reliable
- Increase delay to 300ms for macOS app refocus
- Add tokio "process" feature

Settings highlight fix:
- SettingsPanel now uses local useState for immediate optimistic updates
  when clicking position/history buttons, instead of waiting for the
  parent prop round-trip
- Buttons highlight instantly on click, then sync with backend

Config fix:
- titleBarStyle must be PascalCase "Overlay" in Tauri 2 config (was
  lowercase "overlay" which caused a startup error)

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
ilia 2026-05-12 14:28:08 -04:00
parent 80a6c01cdb
commit 11e91a4f36
5 changed files with 50 additions and 9 deletions

11
src-tauri/Cargo.lock generated
View File

@ -3098,6 +3098,16 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "signal-hook-registry"
version = "1.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
dependencies = [
"errno",
"libc",
]
[[package]]
name = "simd-adler32"
version = "0.3.9"
@ -3742,6 +3752,7 @@ dependencies = [
"libc",
"mio",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"windows-sys 0.61.2",

View File

@ -26,7 +26,7 @@ base64 = "0.22"
log = "0.4"
png = "0.17"
dirs = "5"
tokio = { version = "1", features = ["time", "macros"] }
tokio = { version = "1", features = ["time", "macros", "process"] }
[target.'cfg(target_os = "macos")'.dependencies]
core-graphics = "0.24"

View File

@ -74,16 +74,33 @@ pub async fn paste_and_refocus(app: tauri::AppHandle) -> Result<(), String> {
}
// macOS needs time to refocus the previous application
tokio::time::sleep(std::time::Duration::from_millis(250)).await;
tokio::time::sleep(std::time::Duration::from_millis(300)).await;
#[cfg(target_os = "macos")]
{
// .output() blocks until osascript finishes, ensuring the keystroke lands
std::process::Command::new("osascript")
// Activate the frontmost app explicitly, then send Cmd+V.
// Using tokio::process::Command to avoid blocking the async runtime.
let script = r#"
tell application "System Events"
set frontApp to name of first application process whose frontmost is true
end tell
tell application frontApp to activate
delay 0.1
tell application "System Events"
keystroke "v" using command down
end tell
"#;
let output = tokio::process::Command::new("osascript")
.arg("-e")
.arg(r#"tell application "System Events" to keystroke "v" using command down"#)
.arg(script)
.output()
.map_err(|e| format!("osascript failed: {}", e))?;
.await
.map_err(|e| format!("osascript spawn failed: {}", e))?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
log::warn!("osascript paste failed: {}", stderr);
}
}
Ok(())

View File

@ -21,7 +21,7 @@
"minWidth": 320,
"minHeight": 300,
"decorations": true,
"titleBarStyle": "overlay",
"titleBarStyle": "Overlay",
"hiddenTitle": true,
"visible": false,
"alwaysOnTop": true,

View File

@ -1,4 +1,4 @@
import { useCallback } from "react";
import { useState, useCallback } from "react";
import { invoke } from "@tauri-apps/api/core";
import type { Settings } from "../types";
@ -19,11 +19,17 @@ const POSITION_OPTIONS: { value: string; label: string }[] = [
{ value: "bottom-left", label: "Bottom left" },
];
export default function SettingsPanel({ settings, onClose, onUpdate }: Props) {
export default function SettingsPanel({ settings: initialSettings, onClose, onUpdate }: Props) {
const [settings, setSettings] = useState<Settings>(initialSettings);
const updateSetting = useCallback(
async (key: string, value: string) => {
// Optimistically update local state for instant visual feedback
setSettings((prev) => ({ ...prev, [key]: parseSettingValue(key, value) }));
await invoke("set_setting", { key, value });
const updated: Settings = await invoke("get_settings");
setSettings(updated);
onUpdate(updated);
},
[onUpdate]
@ -122,6 +128,13 @@ export default function SettingsPanel({ settings, onClose, onUpdate }: Props) {
);
}
function parseSettingValue(key: string, value: string): unknown {
if (key === "launch_at_login" || key === "show_images") return value === "true";
if (key === "max_history" || key === "window_width" || key === "window_height")
return parseInt(value, 10);
return value;
}
function ToggleRow({
label,
checked,