more wip but roughly is there
This commit is contained in:
parent
d5f23cdfa5
commit
51105dde15
17 changed files with 1505 additions and 89 deletions
BIN
bun.lockb
BIN
bun.lockb
Binary file not shown.
1273
package-lock.json
generated
Normal file
1273
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -14,6 +14,7 @@
|
|||
"@tauri-apps/api": "^2.6.0",
|
||||
"@tauri-apps/plugin-autostart": "~2",
|
||||
"@tauri-apps/plugin-clipboard-manager": "~2",
|
||||
"@tauri-apps/plugin-fs": "~2",
|
||||
"@tauri-apps/plugin-global-shortcut": "~2",
|
||||
"@tauri-apps/plugin-opener": "^2",
|
||||
"@tauri-apps/plugin-os": "~2",
|
||||
|
|
|
|||
BIN
public/icon/recording.png
Normal file
BIN
public/icon/recording.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
BIN
public/icon/transcribing.png
Normal file
BIN
public/icon/transcribing.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
23
src-tauri/Cargo.lock
generated
23
src-tauri/Cargo.lock
generated
|
|
@ -2024,6 +2024,7 @@ dependencies = [
|
|||
"tauri-build",
|
||||
"tauri-plugin-autostart",
|
||||
"tauri-plugin-clipboard-manager",
|
||||
"tauri-plugin-fs",
|
||||
"tauri-plugin-global-shortcut",
|
||||
"tauri-plugin-macos-permissions",
|
||||
"tauri-plugin-opener",
|
||||
|
|
@ -5450,6 +5451,28 @@ dependencies = [
|
|||
"thiserror 2.0.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-fs"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c6ef84ee2f2094ce093e55106d90d763ba343fad57566992962e8f76d113f99"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"dunce",
|
||||
"glob",
|
||||
"percent-encoding",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"tauri-utils",
|
||||
"thiserror 2.0.11",
|
||||
"toml",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-global-shortcut"
|
||||
version = "2.2.0"
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ tauri-plugin-process = "2"
|
|||
rodio = { git = "https://github.com/cjpais/rodio.git" }
|
||||
reqwest = { version = "0.11", features = ["json", "stream"] }
|
||||
futures-util = "0.3"
|
||||
tauri-plugin-fs = "2"
|
||||
|
||||
[dependencies.ort-sys]
|
||||
version = "=2.0.0-rc.9"
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@
|
|||
"global-shortcut:allow-register",
|
||||
"global-shortcut:allow-unregister",
|
||||
"global-shortcut:allow-unregister-all",
|
||||
"macos-permissions:default"
|
||||
"macos-permissions:default",
|
||||
"fs:read-files",
|
||||
"fs:allow-resource-read-recursive"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
17
src-tauri/gen/apple/PrivacyInfo.xcprivacy
Normal file
17
src-tauri/gen/apple/PrivacyInfo.xcprivacy
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPITypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>C617.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -5,6 +5,8 @@ use crate::utils;
|
|||
use crate::utils::change_tray_icon;
|
||||
use crate::utils::play_recording_start_sound;
|
||||
use crate::utils::play_recording_stop_sound;
|
||||
use crate::utils::show_recording_overlay;
|
||||
use crate::utils::show_transcribing_overlay;
|
||||
use crate::utils::TrayIconState;
|
||||
use log::debug;
|
||||
use once_cell::sync::Lazy;
|
||||
|
|
@ -30,6 +32,7 @@ impl ShortcutAction for TranscribeAction {
|
|||
|
||||
let binding_id = binding_id.to_string();
|
||||
change_tray_icon(app, TrayIconState::Recording);
|
||||
show_recording_overlay(app);
|
||||
|
||||
let rm = app.state::<Arc<AudioRecordingManager>>();
|
||||
|
||||
|
|
@ -78,6 +81,7 @@ impl ShortcutAction for TranscribeAction {
|
|||
let tm = Arc::clone(&app.state::<Arc<TranscriptionManager>>());
|
||||
|
||||
change_tray_icon(app, TrayIconState::Transcribing);
|
||||
show_transcribing_overlay(app);
|
||||
|
||||
// Play audio feedback for recording stop
|
||||
play_recording_stop_sound(app);
|
||||
|
|
@ -119,23 +123,29 @@ impl ShortcutAction for TranscribeAction {
|
|||
),
|
||||
Err(e) => eprintln!("Failed to paste transcription: {}", e),
|
||||
}
|
||||
// Hide the overlay after transcription is complete
|
||||
utils::hide_recording_overlay(&ah_clone);
|
||||
change_tray_icon(&ah_clone, TrayIconState::Idle);
|
||||
})
|
||||
.unwrap_or_else(|e| {
|
||||
eprintln!("Failed to run paste on main thread: {:?}", e);
|
||||
utils::hide_recording_overlay(&ah);
|
||||
change_tray_icon(&ah, TrayIconState::Idle);
|
||||
});
|
||||
} else {
|
||||
utils::hide_recording_overlay(&ah);
|
||||
change_tray_icon(&ah, TrayIconState::Idle);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
debug!("Global Shortcut Transcription error: {}", err);
|
||||
utils::hide_recording_overlay(&ah);
|
||||
change_tray_icon(&ah, TrayIconState::Idle);
|
||||
},
|
||||
}
|
||||
}
|
||||
} else {
|
||||
debug!("No samples retrieved from recording stop");
|
||||
utils::hide_recording_overlay(&ah);
|
||||
change_tray_icon(&ah, TrayIconState::Idle);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ pub fn run() {
|
|||
env_logger::init();
|
||||
|
||||
tauri::Builder::default()
|
||||
.plugin(tauri_plugin_fs::init())
|
||||
.plugin(tauri_plugin_process::init())
|
||||
.plugin(tauri_plugin_updater::Builder::new().build())
|
||||
.plugin(tauri_plugin_os::init())
|
||||
|
|
@ -154,6 +155,9 @@ pub fn run() {
|
|||
app.manage(model_manager.clone());
|
||||
app.manage(transcription_manager.clone());
|
||||
|
||||
// Create the recording overlay window (hidden by default)
|
||||
utils::create_recording_overlay(&app.handle());
|
||||
|
||||
shortcut::init_shortcuts(app);
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use crate::settings::get_settings;
|
|||
use log::{debug, info};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Instant;
|
||||
use tauri::{App, LogicalSize, Manager, WebviewWindowBuilder};
|
||||
use tauri::{App, Manager};
|
||||
|
||||
const WHISPER_SAMPLE_RATE: usize = 16000;
|
||||
|
||||
|
|
@ -195,7 +195,6 @@ impl AudioRecordingManager {
|
|||
binding_id: binding_id.to_string(),
|
||||
};
|
||||
debug!("Recording started for binding {binding_id}");
|
||||
self.show_overlay();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -240,9 +239,6 @@ impl AudioRecordingManager {
|
|||
|
||||
*self.is_recording.lock().unwrap() = false;
|
||||
|
||||
// Hide the overlay
|
||||
self.hide_overlay();
|
||||
|
||||
// In on-demand mode turn the mic off again
|
||||
if matches!(*self.mode.lock().unwrap(), MicrophoneMode::OnDemand) {
|
||||
self.stop_microphone_stream();
|
||||
|
|
@ -262,58 +258,4 @@ impl AudioRecordingManager {
|
|||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- overlay window -------------------------------------------- */
|
||||
|
||||
fn show_overlay(&self) {
|
||||
if let Some(overlay_window) = self.app_handle.get_webview_window("recording_overlay") {
|
||||
let _ = overlay_window.show();
|
||||
} else {
|
||||
// Get screen dimensions for positioning
|
||||
if let Ok(monitors) = self.app_handle.primary_monitor() {
|
||||
if let Some(monitor) = monitors {
|
||||
let size = monitor.size();
|
||||
let scale = monitor.scale_factor();
|
||||
let screen_width = (size.width as f64 / scale) as i32;
|
||||
let screen_height = (size.height as f64 / scale) as i32;
|
||||
|
||||
// Position at bottom center, 30px from bottom
|
||||
let x = (screen_width - 150) / 2;
|
||||
let y = screen_height - 30 - 30;
|
||||
|
||||
match WebviewWindowBuilder::new(
|
||||
&self.app_handle,
|
||||
"recording_overlay",
|
||||
tauri::WebviewUrl::App("src/overlay/index.html".into()),
|
||||
)
|
||||
.title("Recording")
|
||||
.position(x as f64, y as f64)
|
||||
.resizable(false)
|
||||
.maximizable(false)
|
||||
.minimizable(false)
|
||||
.closable(false)
|
||||
.decorations(false)
|
||||
.always_on_top(true)
|
||||
.skip_taskbar(true)
|
||||
.transparent(true)
|
||||
.focused(false)
|
||||
.build()
|
||||
{
|
||||
Ok(_window) => {
|
||||
debug!("Recording overlay window created successfully");
|
||||
}
|
||||
Err(e) => {
|
||||
debug!("Failed to create recording overlay window: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn hide_overlay(&self) {
|
||||
if let Some(overlay_window) = self.app_handle.get_webview_window("recording_overlay") {
|
||||
let _ = overlay_window.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,14 +6,14 @@ use enigo::Keyboard;
|
|||
use enigo::Settings;
|
||||
|
||||
use cpal::traits::{DeviceTrait, HostTrait};
|
||||
use log::debug;
|
||||
use rodio::OutputStreamBuilder;
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use std::thread;
|
||||
use tauri::image::Image;
|
||||
use tauri::tray::TrayIcon;
|
||||
use tauri::AppHandle;
|
||||
use tauri::Manager;
|
||||
use tauri::{AppHandle, Emitter, Manager, WebviewWindowBuilder};
|
||||
use tauri_plugin_clipboard_manager::ClipboardExt;
|
||||
|
||||
fn send_paste() -> Result<(), String> {
|
||||
|
|
@ -194,3 +194,83 @@ fn play_audio_file(
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/* ──────────────────────────────────────────────────────────────── */
|
||||
/* OVERLAY MANAGEMENT */
|
||||
/* ──────────────────────────────────────────────────────────────── */
|
||||
|
||||
/// Creates the recording overlay window and keeps it hidden by default
|
||||
pub fn create_recording_overlay(app_handle: &AppHandle) {
|
||||
// Get screen dimensions for positioning
|
||||
if let Ok(monitors) = app_handle.primary_monitor() {
|
||||
if let Some(monitor) = monitors {
|
||||
let size = monitor.size();
|
||||
let scale = monitor.scale_factor();
|
||||
let screen_width = (size.width as f64 / scale) as i32;
|
||||
let screen_height = (size.height as f64 / scale) as i32;
|
||||
|
||||
// Position at bottom center, 30px from bottom
|
||||
let x = (screen_width - 150) / 2;
|
||||
let y = screen_height - 30 - 30;
|
||||
|
||||
match WebviewWindowBuilder::new(
|
||||
app_handle,
|
||||
"recording_overlay",
|
||||
tauri::WebviewUrl::App("src/overlay/index.html".into()),
|
||||
)
|
||||
.title("Recording")
|
||||
.position(x as f64, y as f64)
|
||||
.resizable(false)
|
||||
.maximizable(false)
|
||||
.minimizable(false)
|
||||
.closable(false)
|
||||
.decorations(false)
|
||||
.always_on_top(true)
|
||||
.skip_taskbar(true)
|
||||
.transparent(true)
|
||||
.focused(false)
|
||||
.visible(false) // Start hidden
|
||||
.build()
|
||||
{
|
||||
Ok(_window) => {
|
||||
debug!("Recording overlay window created successfully (hidden)");
|
||||
}
|
||||
Err(e) => {
|
||||
debug!("Failed to create recording overlay window: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Shows the recording overlay window with fade-in animation
|
||||
pub fn show_recording_overlay(app_handle: &AppHandle) {
|
||||
if let Some(overlay_window) = app_handle.get_webview_window("recording_overlay") {
|
||||
let _ = overlay_window.show();
|
||||
// Emit event to trigger fade-in animation with recording state
|
||||
let _ = overlay_window.emit("show-overlay", "recording");
|
||||
}
|
||||
}
|
||||
|
||||
/// Shows the transcribing overlay window
|
||||
pub fn show_transcribing_overlay(app_handle: &AppHandle) {
|
||||
if let Some(overlay_window) = app_handle.get_webview_window("recording_overlay") {
|
||||
let _ = overlay_window.show();
|
||||
// Emit event to switch to transcribing state
|
||||
let _ = overlay_window.emit("show-overlay", "transcribing");
|
||||
}
|
||||
}
|
||||
|
||||
/// Hides the recording overlay window with fade-out animation
|
||||
pub fn hide_recording_overlay(app_handle: &AppHandle) {
|
||||
if let Some(overlay_window) = app_handle.get_webview_window("recording_overlay") {
|
||||
// Emit event to trigger fade-out animation
|
||||
let _ = overlay_window.emit("hide-overlay", ());
|
||||
// Hide the window after a short delay to allow animation to complete
|
||||
let window_clone = overlay_window.clone();
|
||||
std::thread::spawn(move || {
|
||||
std::thread::sleep(std::time::Duration::from_millis(300));
|
||||
let _ = window_clone.hide();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
|
||||
|
Before Width: | Height: | Size: 4 KiB |
|
|
@ -1,13 +1,15 @@
|
|||
.recording-overlay {
|
||||
width: 128px;
|
||||
height: 32px;
|
||||
height: 40px;
|
||||
width: min-content;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
border-radius: 8px;
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
padding-left: 4px;
|
||||
padding-right: 24px;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
border-radius: 24px;
|
||||
opacity: 0;
|
||||
transition: opacity 300ms ease-out;
|
||||
}
|
||||
|
||||
.bars-container {
|
||||
|
|
@ -37,3 +39,26 @@
|
|||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.recording-overlay.fade-in {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.transcribing-text {
|
||||
color: #faa2ca;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
font-family:
|
||||
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
animation: transcribing-pulse 1.5s infinite ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes transcribing-pulse {
|
||||
0%,
|
||||
100% {
|
||||
opacity: 0.6;
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,28 +1,67 @@
|
|||
import React, { useEffect } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import "./RecordingOverlay.css";
|
||||
import { resolveResource } from "@tauri-apps/api/path";
|
||||
import { listen } from "@tauri-apps/api/event";
|
||||
|
||||
type OverlayState = "recording" | "transcribing";
|
||||
|
||||
const RecordingOverlay: React.FC = () => {
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const [state, setState] = useState<OverlayState>("recording");
|
||||
|
||||
useEffect(() => {
|
||||
resolveResource("tray_idle.png").then((r) => {
|
||||
console.log("res", r);
|
||||
});
|
||||
const setupEventListeners = async () => {
|
||||
// Listen for show-overlay event from Rust
|
||||
const unlistenShow = await listen("show-overlay", (event) => {
|
||||
const overlayState = event.payload as OverlayState;
|
||||
setState(overlayState);
|
||||
setIsVisible(true);
|
||||
});
|
||||
|
||||
// Listen for hide-overlay event from Rust
|
||||
const unlistenHide = await listen("hide-overlay", () => {
|
||||
setIsVisible(false);
|
||||
});
|
||||
|
||||
// Cleanup function
|
||||
return () => {
|
||||
unlistenShow();
|
||||
unlistenHide();
|
||||
};
|
||||
};
|
||||
|
||||
setupEventListeners();
|
||||
}, []);
|
||||
|
||||
const getIconPath = () => {
|
||||
return state === "recording"
|
||||
? "/icon/recording.png"
|
||||
: "/icon/transcribing.png";
|
||||
};
|
||||
|
||||
const getIconAlt = () => {
|
||||
return state === "recording" ? "Recording Icon" : "Transcribing Icon";
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="recording-overlay">
|
||||
<div className="bars-container">
|
||||
{Array.from({ length: 8 }, (_, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="bar"
|
||||
style={{
|
||||
animationDelay: `${i * 100}ms`,
|
||||
animationDuration: `${800 + Math.random() * 400}ms`,
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div className={`recording-overlay ${isVisible ? "fade-in" : ""}`}>
|
||||
<img width="32" height="32" src={getIconPath()} alt={getIconAlt()} />
|
||||
{state === "recording" && (
|
||||
<div className="bars-container">
|
||||
{Array.from({ length: 8 }, (_, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="bar"
|
||||
style={{
|
||||
animationDelay: `${i * 100}ms`,
|
||||
animationDuration: `${800 + Math.random() * 400}ms`,
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{state === "transcribing" && (
|
||||
<div className="transcribing-text">Transcribing...</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue