Show recording overlay in macOS fullscreen apps, and when switching between spaces (#361)
* Gitignore `*.local.*` Allows for local files like `CLAUDE.local.md` and `settings.local.json` * Support macOS spaces and fullscreen apps
This commit is contained in:
parent
71d0eeed73
commit
5085485e67
5 changed files with 103 additions and 5 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -11,6 +11,7 @@ node_modules
|
|||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
*.local.*
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
|
|
|
|||
19
src-tauri/Cargo.lock
generated
19
src-tauri/Cargo.lock
generated
|
|
@ -2528,6 +2528,7 @@ dependencies = [
|
|||
"tar",
|
||||
"tauri",
|
||||
"tauri-build",
|
||||
"tauri-nspanel",
|
||||
"tauri-plugin-autostart",
|
||||
"tauri-plugin-clipboard-manager",
|
||||
"tauri-plugin-fs",
|
||||
|
|
@ -4305,6 +4306,12 @@ dependencies = [
|
|||
"windows-link 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pastey"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec"
|
||||
|
||||
[[package]]
|
||||
name = "pathdiff"
|
||||
version = "0.2.3"
|
||||
|
|
@ -6629,6 +6636,18 @@ dependencies = [
|
|||
"tauri-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-nspanel"
|
||||
version = "2.1.0"
|
||||
source = "git+https://github.com/ahkohd/tauri-nspanel?branch=v2.1#da9c9a8d4eb7f0524a2508988df1a7d9585b4904"
|
||||
dependencies = [
|
||||
"objc2 0.6.3",
|
||||
"objc2-app-kit",
|
||||
"objc2-foundation 0.3.2",
|
||||
"pastey",
|
||||
"tauri",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin"
|
||||
version = "2.5.1"
|
||||
|
|
|
|||
|
|
@ -85,6 +85,9 @@ windows = { version = "0.61.3", features = [
|
|||
"Win32_System_Variant",
|
||||
] }
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
tauri-nspanel = { git = "https://github.com/ahkohd/tauri-nspanel", branch = "v2.1" }
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
|
|
|
|||
|
|
@ -211,7 +211,7 @@ pub fn run() {
|
|||
// when the variable is unset
|
||||
let console_filter = build_console_filter();
|
||||
|
||||
tauri::Builder::default()
|
||||
let mut builder = tauri::Builder::default()
|
||||
.plugin(
|
||||
LogBuilder::new()
|
||||
.level(log::LevelFilter::Trace) // Set to most verbose level globally
|
||||
|
|
@ -234,7 +234,14 @@ pub fn run() {
|
|||
}),
|
||||
])
|
||||
.build(),
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
builder = builder.plugin(tauri_nspanel::init());
|
||||
}
|
||||
|
||||
builder
|
||||
.plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| {
|
||||
show_main_window(app);
|
||||
}))
|
||||
|
|
|
|||
|
|
@ -1,8 +1,29 @@
|
|||
use crate::settings;
|
||||
use crate::settings::OverlayPosition;
|
||||
use enigo::{Enigo, Mouse};
|
||||
use tauri::{AppHandle, Emitter, Manager, PhysicalPosition, PhysicalSize};
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
use log::debug;
|
||||
use tauri::{AppHandle, Emitter, Manager, PhysicalPosition, PhysicalSize, WebviewWindowBuilder};
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
use tauri::WebviewWindowBuilder;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
use tauri::WebviewUrl;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
use tauri_nspanel::{tauri_panel, CollectionBehavior, PanelBuilder, PanelLevel};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
tauri_panel! {
|
||||
panel!(RecordingOverlayPanel {
|
||||
config: {
|
||||
can_become_key_window: false,
|
||||
is_floating_panel: true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const OVERLAY_WIDTH: f64 = 172.0;
|
||||
const OVERLAY_HEIGHT: f64 = 36.0;
|
||||
|
|
@ -84,6 +105,7 @@ fn calculate_overlay_position(app_handle: &AppHandle) -> Option<(f64, f64)> {
|
|||
}
|
||||
|
||||
/// Creates the recording overlay window and keeps it hidden by default
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
pub fn create_recording_overlay(app_handle: &AppHandle) {
|
||||
if let Some((x, y)) = calculate_overlay_position(app_handle) {
|
||||
match WebviewWindowBuilder::new(
|
||||
|
|
@ -118,6 +140,49 @@ pub fn create_recording_overlay(app_handle: &AppHandle) {
|
|||
}
|
||||
}
|
||||
|
||||
/// Creates the recording overlay panel and keeps it hidden by default (macOS)
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn create_recording_overlay(app_handle: &AppHandle) {
|
||||
if let Some((x, y)) = calculate_overlay_position(app_handle) {
|
||||
// PanelBuilder creates a Tauri window then converts it to NSPanel.
|
||||
// The window remains registered, so get_webview_window() still works.
|
||||
match PanelBuilder::<_, RecordingOverlayPanel>::new(
|
||||
app_handle,
|
||||
"recording_overlay"
|
||||
)
|
||||
.url(WebviewUrl::App("src/overlay/index.html".into()))
|
||||
.title("Recording")
|
||||
.position(tauri::Position::Logical(tauri::LogicalPosition { x, y }))
|
||||
.level(PanelLevel::Status)
|
||||
.size(tauri::Size::Logical(tauri::LogicalSize {
|
||||
width: OVERLAY_WIDTH,
|
||||
height: OVERLAY_HEIGHT
|
||||
}))
|
||||
.has_shadow(false)
|
||||
.transparent(true)
|
||||
.no_activate(true)
|
||||
.corner_radius(0.0)
|
||||
.with_window(|w| {
|
||||
w.decorations(false)
|
||||
.transparent(true)
|
||||
})
|
||||
.collection_behavior(
|
||||
CollectionBehavior::new()
|
||||
.can_join_all_spaces()
|
||||
.full_screen_auxiliary()
|
||||
)
|
||||
.build()
|
||||
{
|
||||
Ok(panel) => {
|
||||
let _ = panel.hide();
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Failed to create recording overlay panel: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Shows the recording overlay window with fade-in animation
|
||||
pub fn show_recording_overlay(app_handle: &AppHandle) {
|
||||
// Check if overlay should be shown based on position setting
|
||||
|
|
@ -126,9 +191,12 @@ pub fn show_recording_overlay(app_handle: &AppHandle) {
|
|||
return;
|
||||
}
|
||||
|
||||
update_overlay_position(app_handle);
|
||||
|
||||
if let Some(overlay_window) = app_handle.get_webview_window("recording_overlay") {
|
||||
// Update position before showing to prevent flicker from position changes
|
||||
if let Some((x, y)) = calculate_overlay_position(app_handle) {
|
||||
let _ = overlay_window.set_position(tauri::Position::Logical(tauri::LogicalPosition { x, y }));
|
||||
}
|
||||
|
||||
let _ = overlay_window.show();
|
||||
// Emit event to trigger fade-in animation with recording state
|
||||
let _ = overlay_window.emit("show-overlay", "recording");
|
||||
|
|
|
|||
Loading…
Reference in a new issue