refactor(ui): upgrade vector icons, add accent color theme, enlarge settings, and prevent daemon errors

This commit is contained in:
katR 2026-09-05 17:35:29 +05:30
parent 829f3e74d8
commit aede621eae
4 changed files with 482 additions and 216 deletions

View file

@ -262,42 +262,76 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
Some(Arc::clone(&audio_levels)),
).ok();
let mut source: Box<dyn FrameSource> = if std::env::args().any(|a| a == "--mock") {
let mut source_opt: Option<Box<dyn FrameSource>> = None;
if std::env::args().any(|a| a == "--mock") {
println!("Initializing MOCK capture...");
Box::new(scythe::capture::mock::MockCapture::new())
source_opt = Some(Box::new(scythe::capture::mock::MockCapture::new()));
} else if cfg!(target_os = "windows") {
#[cfg(target_os = "windows")]
{
println!("Initializing Windows DXGI capture...");
Box::new(scythe::capture::windows::WindowsCapture::new()?)
}
#[cfg(not(target_os = "windows"))]
{
unreachable!()
if let Ok(c) = scythe::capture::windows::WindowsCapture::new() {
source_opt = Some(Box::new(c));
}
}
} else if session_type.to_lowercase() == "wayland" {
#[cfg(target_os = "linux")]
{
println!("Initializing Wayland capture (cursor: {})...", initial_config.show_cursor);
Box::new(scythe::capture::wayland::WaylandCapture::new_with_cursor(initial_config.show_cursor).await?)
}
#[cfg(not(target_os = "linux"))]
{
unreachable!()
for attempt in 1..=5 {
println!("Initializing Wayland capture (cursor: {}, attempt {}/5)...", initial_config.show_cursor, attempt);
// Ensure desktop portal is running
let _ = std::process::Command::new("systemctl")
.args(["--user", "start", "xdg-desktop-portal-hyprland"])
.status();
match scythe::capture::wayland::WaylandCapture::new_with_cursor(initial_config.show_cursor).await {
Ok(cap) => {
source_opt = Some(Box::new(cap));
break;
}
Err(e) => {
eprintln!("Wayland capture attempt {} failed: {}", attempt, e);
if attempt < 5 {
let _ = std::process::Command::new("systemctl")
.args(["--user", "restart", "xdg-desktop-portal"])
.status();
tokio::time::sleep(std::time::Duration::from_millis(600)).await;
}
}
}
}
}
} else {
#[cfg(target_os = "linux")]
{
println!("Initializing X11 capture...");
Box::new(scythe::capture::x11::X11Capture::new()?)
if let Ok(c) = scythe::capture::x11::X11Capture::new() {
source_opt = Some(Box::new(c));
}
}
#[cfg(not(target_os = "linux"))]
{
unreachable!()
}
let mut source: Box<dyn FrameSource> = match source_opt {
Some(s) => s,
None => {
eprintln!("Warning: Hardware capture unavailable; falling back to mock capture to preserve daemon IPC.");
Box::new(scythe::capture::mock::MockCapture::new())
}
};
let first_frame = source.next_frame()?;
let first_frame = match source.next_frame() {
Ok(f) => f,
Err(e) => {
eprintln!("Initial frame capture error: {}. Using default frame.", e);
Frame::Raw {
data: vec![0u8; 1920 * 1080 * 4],
width: 1920,
height: 1080,
stride: 1920 * 4,
}
}
};
let (width, height) = match &first_frame {
Frame::Raw { width, height, .. } => (*width, *height),
Frame::DmaBuf { width, height, .. } => (*width, *height),

View file

@ -131,6 +131,18 @@ fn get_ui_cmd() -> std::process::Command {
fn check_and_toggle_overlay() -> bool {
let pid_path = scythe::ipc::get_overlay_pid_path();
if pid_path.exists() {
if let Ok(metadata) = std::fs::metadata(&pid_path) {
if let Ok(modified) = metadata.modified() {
if let Ok(elapsed) = modified.elapsed() {
// Debounce: if the overlay was spawned less than 400ms ago, this is a
// duplicate event from concurrent triggers. Do not kill it; simply exit.
if elapsed < std::time::Duration::from_millis(400) {
return true;
}
}
}
}
if let Ok(content) = std::fs::read_to_string(&pid_path) {
if let Ok(pid) = content.trim().parse::<i32>() {
#[cfg(unix)]
@ -167,6 +179,11 @@ fn ensure_daemon_running_async() {
|| UnixStream::connect(scythe::ipc::get_legacy_socket_path()).is_ok() {
return;
}
if scythe::hyprland_binds::is_hyprland() {
let _ = std::process::Command::new("systemctl")
.args(["--user", "start", "xdg-desktop-portal-hyprland"])
.status();
}
}
let _ = get_daemon_cmd()
.stdin(std::process::Stdio::null())
@ -177,13 +194,21 @@ fn ensure_daemon_running_async() {
fn ensure_daemon_running() {
if query_status().is_err() {
#[cfg(target_os = "linux")]
{
if scythe::hyprland_binds::is_hyprland() {
let _ = std::process::Command::new("systemctl")
.args(["--user", "start", "xdg-desktop-portal-hyprland"])
.status();
}
}
let _ = get_daemon_cmd()
.stdin(std::process::Stdio::null())
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.spawn();
for _ in 0..15 {
std::thread::sleep(std::time::Duration::from_millis(80));
for _ in 0..25 {
std::thread::sleep(std::time::Duration::from_millis(100));
if query_status().is_ok() {
break;
}
@ -322,6 +347,7 @@ fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
return send_with_notification(Command::StopRecording, "Recording saved");
}
"--reload" => {
ensure_daemon_running();
return send_command(Command::ReloadConfig);
}
"--status" => {
@ -421,7 +447,10 @@ fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let hotkey_record = HotKey::new(Some(Modifiers::CONTROL | Modifiers::SHIFT), Code::F9);
let hotkey_cursor = HotKey::new(Some(Modifiers::CONTROL | Modifiers::SHIFT), Code::F10);
let _ = manager.register(hotkey_menu);
let is_on_hyprland = scythe::hyprland_binds::is_hyprland();
if !is_on_hyprland {
let _ = manager.register(hotkey_menu);
}
let _ = manager.register(hotkey_save);
let _ = manager.register(hotkey_record);
let _ = manager.register(hotkey_cursor);

View file

@ -18,9 +18,12 @@ fn default_record_hotkey() -> String { "Ctrl+Shift+F9".to_string() }
fn default_cursor_hotkey() -> String { "Ctrl+Shift+F10".to_string() }
fn default_mic_volume() -> f32 { 0.60 }
fn default_system_volume() -> f32 { 1.00 }
fn default_accent_color() -> String { "blue".to_string() }
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ScytheConfig {
#[serde(default = "default_accent_color")]
pub accent_color: String,
#[serde(default = "default_true")]
pub replay_enabled: bool,
#[serde(default = "default_replay_duration")]
@ -70,6 +73,7 @@ pub type VrecConfig = ScytheConfig;
impl Default for ScytheConfig {
fn default() -> Self {
Self {
accent_color: default_accent_color(),
replay_enabled: true,
replay_duration_sec: 60,
replay_bitrate_kbps: 18000,
@ -184,6 +188,7 @@ mod tests {
#[test]
fn test_config_defaults_and_json() {
let cfg = ScytheConfig::default();
assert_eq!(cfg.accent_color, "blue");
assert_eq!(cfg.replay_duration_sec, 60);
assert_eq!(cfg.replay_bitrate_kbps, 18000);
assert_eq!(cfg.fps, 60);
@ -192,6 +197,7 @@ mod tests {
assert!(cfg.show_cursor);
let json = serde_json::to_string(&cfg).unwrap();
let parsed: ScytheConfig = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.accent_color, "blue");
assert_eq!(parsed.replay_duration_sec, 60);
assert_eq!(parsed.audio_mode, "system");
assert!(parsed.show_cursor);

File diff suppressed because it is too large Load diff