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
|
||||||
dist-ssr
|
dist-ssr
|
||||||
*.local
|
*.local
|
||||||
|
*.local.*
|
||||||
|
|
||||||
# Editor directories and files
|
# Editor directories and files
|
||||||
.vscode/*
|
.vscode/*
|
||||||
|
|
|
||||||
19
src-tauri/Cargo.lock
generated
19
src-tauri/Cargo.lock
generated
|
|
@ -2528,6 +2528,7 @@ dependencies = [
|
||||||
"tar",
|
"tar",
|
||||||
"tauri",
|
"tauri",
|
||||||
"tauri-build",
|
"tauri-build",
|
||||||
|
"tauri-nspanel",
|
||||||
"tauri-plugin-autostart",
|
"tauri-plugin-autostart",
|
||||||
"tauri-plugin-clipboard-manager",
|
"tauri-plugin-clipboard-manager",
|
||||||
"tauri-plugin-fs",
|
"tauri-plugin-fs",
|
||||||
|
|
@ -4305,6 +4306,12 @@ dependencies = [
|
||||||
"windows-link 0.2.1",
|
"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]]
|
[[package]]
|
||||||
name = "pathdiff"
|
name = "pathdiff"
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
|
|
@ -6629,6 +6636,18 @@ dependencies = [
|
||||||
"tauri-utils",
|
"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]]
|
[[package]]
|
||||||
name = "tauri-plugin"
|
name = "tauri-plugin"
|
||||||
version = "2.5.1"
|
version = "2.5.1"
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,9 @@ windows = { version = "0.61.3", features = [
|
||||||
"Win32_System_Variant",
|
"Win32_System_Variant",
|
||||||
] }
|
] }
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "macos")'.dependencies]
|
||||||
|
tauri-nspanel = { git = "https://github.com/ahkohd/tauri-nspanel", branch = "v2.1" }
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
lto = true
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
|
|
|
||||||
|
|
@ -211,7 +211,7 @@ pub fn run() {
|
||||||
// when the variable is unset
|
// when the variable is unset
|
||||||
let console_filter = build_console_filter();
|
let console_filter = build_console_filter();
|
||||||
|
|
||||||
tauri::Builder::default()
|
let mut builder = tauri::Builder::default()
|
||||||
.plugin(
|
.plugin(
|
||||||
LogBuilder::new()
|
LogBuilder::new()
|
||||||
.level(log::LevelFilter::Trace) // Set to most verbose level globally
|
.level(log::LevelFilter::Trace) // Set to most verbose level globally
|
||||||
|
|
@ -234,7 +234,14 @@ pub fn run() {
|
||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
.build(),
|
.build(),
|
||||||
)
|
);
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
{
|
||||||
|
builder = builder.plugin(tauri_nspanel::init());
|
||||||
|
}
|
||||||
|
|
||||||
|
builder
|
||||||
.plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| {
|
.plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| {
|
||||||
show_main_window(app);
|
show_main_window(app);
|
||||||
}))
|
}))
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,29 @@
|
||||||
use crate::settings;
|
use crate::settings;
|
||||||
use crate::settings::OverlayPosition;
|
use crate::settings::OverlayPosition;
|
||||||
use enigo::{Enigo, Mouse};
|
use enigo::{Enigo, Mouse};
|
||||||
|
use tauri::{AppHandle, Emitter, Manager, PhysicalPosition, PhysicalSize};
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
use log::debug;
|
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_WIDTH: f64 = 172.0;
|
||||||
const OVERLAY_HEIGHT: f64 = 36.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
|
/// Creates the recording overlay window and keeps it hidden by default
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
pub fn create_recording_overlay(app_handle: &AppHandle) {
|
pub fn create_recording_overlay(app_handle: &AppHandle) {
|
||||||
if let Some((x, y)) = calculate_overlay_position(app_handle) {
|
if let Some((x, y)) = calculate_overlay_position(app_handle) {
|
||||||
match WebviewWindowBuilder::new(
|
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
|
/// Shows the recording overlay window with fade-in animation
|
||||||
pub fn show_recording_overlay(app_handle: &AppHandle) {
|
pub fn show_recording_overlay(app_handle: &AppHandle) {
|
||||||
// Check if overlay should be shown based on position setting
|
// Check if overlay should be shown based on position setting
|
||||||
|
|
@ -126,9 +191,12 @@ pub fn show_recording_overlay(app_handle: &AppHandle) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
update_overlay_position(app_handle);
|
|
||||||
|
|
||||||
if let Some(overlay_window) = app_handle.get_webview_window("recording_overlay") {
|
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();
|
let _ = overlay_window.show();
|
||||||
// Emit event to trigger fade-in animation with recording state
|
// Emit event to trigger fade-in animation with recording state
|
||||||
let _ = overlay_window.emit("show-overlay", "recording");
|
let _ = overlay_window.emit("show-overlay", "recording");
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue