Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,16 @@ unexpected_cfgs = "allow"
dbg_macro = "deny"
let_underscore_future = "deny"
unchecked_duration_subtraction = "deny"
collapsible_if = "deny"
manual_is_multiple_of = "deny"
clone_on_copy = "deny"
redundant_closure = "deny"
ptr_arg = "deny"
len_zero = "deny"
let_unit_value = "deny"
unnecessary_lazy_evaluations = "deny"
needless_range_loop = "deny"
manual_clamp = "deny"

# Optimize for smaller binary size
[profile.release]
Expand Down
11 changes: 4 additions & 7 deletions apps/desktop/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,6 @@ use tauri_plugin_notification::{NotificationExt, PermissionState};
use tauri_plugin_opener::OpenerExt;
use tauri_plugin_shell::ShellExt;
use tauri_specta::Event;
#[cfg(target_os = "macos")]
use tokio::sync::Mutex;
use tokio::sync::{RwLock, oneshot};
use tracing::{error, instrument, trace, warn};
use upload::{create_or_get_video, upload_image, upload_video};
Expand Down Expand Up @@ -897,8 +895,7 @@ async fn get_video_metadata(path: PathBuf) -> Result<VideoRecordingMetadata, Str
let raw_duration = input.duration();
if raw_duration <= 0 {
return Err(format!(
"Unknown or invalid duration for video file: {:?}",
path
"Unknown or invalid duration for video file: {path:?}"
));
}

Expand Down Expand Up @@ -1195,7 +1192,7 @@ async fn upload_exported_video(
.map_err(|e| error!("Failed to save recording meta: {e}"))
.ok();

Err(e.to_string().into())
Err(e.to_string())
}
}
}
Expand Down Expand Up @@ -2254,7 +2251,7 @@ pub async fn run(recording_logging_handle: LoggingHandle, logs_dir: PathBuf) {

// This ensures settings reflects the correct value if it's set at startup
if should_update {
GeneralSettingsStore::update(&app, |mut s| {
GeneralSettingsStore::update(&app, |s| {
s.server_url = server_url.clone();
})
.map_err(|err| warn!("Error updating server URL into settings store: {err}"))
Expand Down Expand Up @@ -2583,7 +2580,7 @@ async fn resume_uploads(app: AppHandle) -> Result<(), String> {
}

let entries = std::fs::read_dir(&recordings_dir)
.map_err(|e| format!("Failed to read recordings directory: {}", e))?;
.map_err(|e| format!("Failed to read recordings directory: {e}"))?;
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() && path.extension().and_then(|s| s.to_str()) == Some("cap") {
Expand Down
15 changes: 7 additions & 8 deletions apps/desktop/src-tauri/src/logging.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,17 @@ pub async fn upload_log_file(app: &AppHandle) -> Result<(), String> {
let log_file = get_latest_log_file(app).await.ok_or("No log file found")?;

let metadata =
fs::metadata(&log_file).map_err(|e| format!("Failed to read log file metadata: {}", e))?;
fs::metadata(&log_file).map_err(|e| format!("Failed to read log file metadata: {e}"))?;
let file_size = metadata.len();

const MAX_SIZE: u64 = 1 * 1024 * 1024;
const MAX_SIZE: u64 = 1024 * 1024;

let log_content = if file_size > MAX_SIZE {
let content =
fs::read_to_string(&log_file).map_err(|e| format!("Failed to read log file: {}", e))?;
fs::read_to_string(&log_file).map_err(|e| format!("Failed to read log file: {e}"))?;

let header = format!(
"⚠️ Log file truncated (original size: {} bytes, showing last ~1MB)\n\n",
file_size
"⚠️ Log file truncated (original size: {file_size} bytes, showing last ~1MB)\n\n"
);
let max_content_size = (MAX_SIZE as usize) - header.len();

Expand All @@ -54,13 +53,13 @@ pub async fn upload_log_file(app: &AppHandle) -> Result<(), String> {
if let Some(newline_pos) = truncated.find('\n') {
format!("{}{}", header, &truncated[newline_pos + 1..])
} else {
format!("{}{}", header, truncated)
format!("{header}{truncated}")
}
} else {
content
}
} else {
fs::read_to_string(&log_file).map_err(|e| format!("Failed to read log file: {}", e))?
fs::read_to_string(&log_file).map_err(|e| format!("Failed to read log file: {e}"))?
};

let form = reqwest::multipart::Form::new()
Expand All @@ -73,7 +72,7 @@ pub async fn upload_log_file(app: &AppHandle) -> Result<(), String> {
client.post(url).multipart(form)
})
.await
.map_err(|e| format!("Failed to upload logs: {}", e))?;
.map_err(|e| format!("Failed to upload logs: {e}"))?;

if !response.status().is_success() {
return Err(format!("Upload failed with status: {}", response.status()));
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ fn main() {

// Ensure logs directory exists
std::fs::create_dir_all(&logs_dir).unwrap_or_else(|e| {
eprintln!("Failed to create logs directory: {}", e);
eprintln!("Failed to create logs directory: {e}");
});

let file_appender = tracing_appender::rolling::daily(&logs_dir, "cap-desktop.log");
Expand Down
44 changes: 3 additions & 41 deletions apps/desktop/src-tauri/src/platform/macos/sc_shareable_content.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
use cidre::{arc, ns, sc};
use core_graphics::{display::CGDirectDisplayID, window::CGWindowID};
use std::sync::Arc;
use std::{
collections::HashMap,
sync::{OnceLock, RwLock},
time::Instant,
};
use std::sync::{Arc, OnceLock, RwLock};
use tokio::sync::{Mutex, Notify};
use tracing::{debug, info, trace};
use tracing::trace;

#[derive(Default)]
struct CacheState {
Expand Down Expand Up @@ -62,8 +56,6 @@ pub async fn prewarm_shareable_content() -> Result<(), arc::R<ns::Error>> {

pub async fn get_shareable_content()
-> Result<Option<arc::R<sc::ShareableContent>>, arc::R<ns::Error>> {
let lookup_start = Instant::now();

if let Some(content) = state()
.cache
.read()
Expand All @@ -82,14 +74,10 @@ pub async fn get_shareable_content()

async fn run_warmup(task: WarmupTask) {
let result = async {
let warm_start = Instant::now();

let content = sc::ShareableContent::current().await?;
let cache = ShareableContentCache::new(content);
let elapsed_ms = warm_start.elapsed().as_micros() as f64 / 1000.0;

let mut guard = state().cache.write().unwrap();
let replaced = guard.is_some();
*guard = Some(cache);

Ok::<(), arc::R<ns::Error>>(())
Expand All @@ -115,40 +103,14 @@ async fn run_warmup(task: WarmupTask) {
struct ShareableContentCache {
#[allow(dead_code)]
content: arc::R<sc::ShareableContent>,
displays: HashMap<CGDirectDisplayID, arc::R<sc::Display>>,
windows: HashMap<CGWindowID, arc::R<sc::Window>>,
}

unsafe impl Send for ShareableContentCache {}
unsafe impl Sync for ShareableContentCache {}

impl ShareableContentCache {
fn new(content: arc::R<sc::ShareableContent>) -> Self {
let displays = content
.displays()
.iter()
.map(|display| (display.display_id().0, display.retained()))
.collect();

let windows = content
.windows()
.iter()
.map(|window| (window.id(), window.retained()))
.collect();

Self {
content,
displays,
windows,
}
}

fn display(&self, id: CGDirectDisplayID) -> Option<arc::R<sc::Display>> {
self.displays.get(&id).cloned()
}

fn window(&self, id: CGWindowID) -> Option<arc::R<sc::Window>> {
self.windows.get(&id).cloned()
Self { content }
}
}

Expand Down
14 changes: 5 additions & 9 deletions apps/desktop/src-tauri/src/recording.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@ use cap_project::{
TimelineConfiguration, TimelineSegment, UploadMeta, ZoomMode, ZoomSegment,
cursor::CursorEvents,
};
use cap_recording::PipelineDoneError;
use cap_recording::feeds::camera::CameraFeedLock;
use cap_recording::feeds::microphone::MicrophoneFeedLock;
use cap_recording::{
RecordingError, RecordingMode,
RecordingMode,
feeds::{camera, microphone},
instant_recording,
sources::{
Expand Down Expand Up @@ -471,7 +469,7 @@ pub async fn start_recording(
let shareable_content = crate::platform::get_shareable_content()
.await
.map_err(|e| format!("GetShareableContent: {e}"))?
.ok_or_else(|| format!("GetShareableContent/NotAvailable"))?;
.ok_or_else(|| "GetShareableContent/NotAvailable".to_string())?;

let common = InProgressRecordingCommon {
target_name,
Expand Down Expand Up @@ -830,7 +828,7 @@ async fn handle_recording_end(
}
}
RecordingMetaInner::Instant(meta) => {
*meta = InstantRecordingMeta::Failed { error: error };
*meta = InstantRecordingMeta::Failed { error };
}
}
project_meta
Expand Down Expand Up @@ -980,12 +978,10 @@ async fn handle_recording_finish(
return;
}

if GeneralSettingsStore::get(&app).ok().flatten().unwrap_or_default().delete_instant_recordings_after_upload {
if let Err(err) = tokio::fs::remove_dir_all(&recording_dir).await {
if GeneralSettingsStore::get(&app).ok().flatten().unwrap_or_default().delete_instant_recordings_after_upload && let Err(err) = tokio::fs::remove_dir_all(&recording_dir).await {
error!("Failed to remove recording files after upload: {err:?}");
return;
}
}

}
} else if let Ok(meta) = build_video_meta(&output_path)
.map_err(|err| error!("Error getting video metadata: {}", err))
Expand Down
39 changes: 19 additions & 20 deletions apps/desktop/src-tauri/src/recording_settings.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use cap_recording::{
RecordingMode, feeds::camera::DeviceOrModelID, sources::screen_capture::ScreenCaptureTarget,
};
use serde_json::json;
use tauri::{AppHandle, Wry};
use tauri_plugin_store::StoreExt;

Expand Down Expand Up @@ -40,23 +39,23 @@ impl RecordingSettingsStore {
}

// i don't trust anyone to not overwrite the whole store lols
pub fn update(app: &AppHandle, update: impl FnOnce(&mut Self)) -> Result<(), String> {
let Ok(store) = app.store("store") else {
return Err("Store not found".to_string());
};

let mut settings = Self::get(app)?.unwrap_or_default();
update(&mut settings);
store.set(Self::KEY, json!(settings));
store.save().map_err(|e| e.to_string())
}

fn save(&self, app: &AppHandle) -> Result<(), String> {
let Ok(store) = app.store("store") else {
return Err("Store not found".to_string());
};

store.set(Self::KEY, json!(self));
store.save().map_err(|e| e.to_string())
}
// pub fn update(app: &AppHandle, update: impl FnOnce(&mut Self)) -> Result<(), String> {
// let Ok(store) = app.store("store") else {
// return Err("Store not found".to_string());
// };

// let mut settings = Self::get(app)?.unwrap_or_default();
// update(&mut settings);
// store.set(Self::KEY, json!(settings));
// store.save().map_err(|e| e.to_string())
// }

// fn save(&self, app: &AppHandle) -> Result<(), String> {
// let Ok(store) = app.store("store") else {
// return Err("Store not found".to_string());
// };

// store.set(Self::KEY, json!(self));
// store.save().map_err(|e| e.to_string())
// }
Comment on lines 41 to +60
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Remove commented-out code entirely.

Commented-out code should be deleted rather than left in the codebase. Version control preserves the history if these methods need to be recovered later. The informal comment on line 41 doesn't clearly explain why these methods were removed or whether this is temporary.

Apply this diff to remove the commented-out code:

-    // i don't trust anyone to not overwrite the whole store lols
-    // pub fn update(app: &AppHandle, update: impl FnOnce(&mut Self)) -> Result<(), String> {
-    //     let Ok(store) = app.store("store") else {
-    //         return Err("Store not found".to_string());
-    //     };
-
-    //     let mut settings = Self::get(app)?.unwrap_or_default();
-    //     update(&mut settings);
-    //     store.set(Self::KEY, json!(settings));
-    //     store.save().map_err(|e| e.to_string())
-    // }
-
-    // fn save(&self, app: &AppHandle) -> Result<(), String> {
-    //     let Ok(store) = app.store("store") else {
-    //         return Err("Store not found".to_string());
-    //     };
-
-    //     store.set(Self::KEY, json!(self));
-    //     store.save().map_err(|e| e.to_string())
-    // }

Since update was a public method, verify that it's not called elsewhere in the codebase:


🏁 Script executed:

#!/bin/bash
# Description: Search for calls to RecordingSettingsStore::update

# Search for usage of the update method
rg -n 'RecordingSettingsStore::update' --type rust

# Also check for potential method calls on instances
rg -n '\.update\s*\(' apps/desktop/src-tauri/src/recording_settings.rs -A 2 -B 2 --type rust

Length of output: 0




🏁 Script executed:

#!/bin/bash
# Check the current state of the recording_settings.rs file around lines 41-60
cat apps/desktop/src-tauri/src/recording_settings.rs | head -70 | tail -35

Length of output: 1313


🏁 Script executed:

#!/bin/bash
# Broader search for 'update' calls in the codebase (all types of files)
# Focus on RecordingSettings context
rg -n 'update' apps/desktop/src-tauri/src/recording_settings.rs -B 2 -A 2 --type rust

Length of output: 507


🏁 Script executed:

#!/bin/bash
# Check if 'update' is called in JavaScript/TypeScript frontend files
# (Tauri apps often have JS frontend calling Rust backend)
rg -n "RecordingSettings.*update\|recording.*update" apps/desktop/src --type ts --type js -C 2

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Look for invocations to this struct/methods
rg -n 'recording_settings\|RecordingSettings' apps/desktop/src-tauri/src/ --type rust -C 1

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Check if the method might be used via module::method pattern
rg -n '\.update\(' apps/desktop/src-tauri/src/ --type rust --glob '!recording_settings.rs'

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Look for any trait implementations that might reference this
rg -n 'impl.*RecordingSettings' apps/desktop/src-tauri/src/ --type rust

Length of output: 81


🏁 Script executed:

#!/bin/bash
# Verify if the file compiles by checking it directly for syntax issues
cat apps/desktop/src-tauri/src/recording_settings.rs | sed -n '35,65p'

Length of output: 870


🏁 Script executed:

#!/bin/bash
# Try to find all methods defined in this file and check what's actually there
ast-grep --pattern $'impl RecordingSettingsStore {
  $$$
}'

Length of output: 3319


🏁 Script executed:

#!/bin/bash
# Check for pub fn update pattern more broadly
rg -n 'pub fn update' apps/desktop/src-tauri/src/recording_settings.rs --type rust

Length of output: 96


🏁 Script executed:

#!/bin/bash
# Search for commented fn update pattern
rg -n '// pub fn update\|// fn update' apps/desktop/src-tauri/src/recording_settings.rs --type rust

Length of output: 0


🏁 Script executed:

#!/bin/bash
# List all function definitions in recording_settings to understand the scope
rg -n 'fn ' apps/desktop/src-tauri/src/recording_settings.rs --type rust

Length of output: 236


🏁 Script executed:

#!/bin/bash
# Check Tauri invoke commands if any reference this
rg -n 'invoke.*update\|update.*settings' apps/desktop/src --type ts --type js --type json -C 1

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Simple: just show the exact lines requested in the review
sed -n '41,60p' apps/desktop/src-tauri/src/recording_settings.rs

Length of output: 792


Remove commented-out code entirely at lines 41-60.

Verification confirms the update method is not called anywhere in the codebase, making its removal safe. Commented-out code should be deleted rather than preserved; git history preserves it if needed later.

Apply this diff:

-    // i don't trust anyone to not overwrite the whole store lols
-    // pub fn update(app: &AppHandle, update: impl FnOnce(&mut Self)) -> Result<(), String> {
-    //     let Ok(store) = app.store("store") else {
-    //         return Err("Store not found".to_string());
-    //     };
-
-    //     let mut settings = Self::get(app)?.unwrap_or_default();
-    //     update(&mut settings);
-    //     store.set(Self::KEY, json!(settings));
-    //     store.save().map_err(|e| e.to_string())
-    // }
-
-    // fn save(&self, app: &AppHandle) -> Result<(), String> {
-    //     let Ok(store) = app.store("store") else {
-    //         return Err("Store not found".to_string());
-    //     };
-
-    //     store.set(Self::KEY, json!(self));
-    //     store.save().map_err(|e| e.to_string())
-    // }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// i don't trust anyone to not overwrite the whole store lols
pub fn update(app: &AppHandle, update: impl FnOnce(&mut Self)) -> Result<(), String> {
let Ok(store) = app.store("store") else {
return Err("Store not found".to_string());
};
let mut settings = Self::get(app)?.unwrap_or_default();
update(&mut settings);
store.set(Self::KEY, json!(settings));
store.save().map_err(|e| e.to_string())
}
fn save(&self, app: &AppHandle) -> Result<(), String> {
let Ok(store) = app.store("store") else {
return Err("Store not found".to_string());
};
store.set(Self::KEY, json!(self));
store.save().map_err(|e| e.to_string())
}
// pub fn update(app: &AppHandle, update: impl FnOnce(&mut Self)) -> Result<(), String> {
// let Ok(store) = app.store("store") else {
// return Err("Store not found".to_string());
// };
// let mut settings = Self::get(app)?.unwrap_or_default();
// update(&mut settings);
// store.set(Self::KEY, json!(settings));
// store.save().map_err(|e| e.to_string())
// }
// fn save(&self, app: &AppHandle) -> Result<(), String> {
// let Ok(store) = app.store("store") else {
// return Err("Store not found".to_string());
// };
// store.set(Self::KEY, json!(self));
// store.save().map_err(|e| e.to_string())
// }
🤖 Prompt for AI Agents
In apps/desktop/src-tauri/src/recording_settings.rs around lines 41 to 60,
remove the entire commented-out block containing the unused update and save
methods (all commented lines shown) so the file no longer contains that
dead/commented code; commit the deletion (rely on git history to recover if
needed) and run a quick build or formatting check to ensure no unrelated
formatting issues were introduced.

}
8 changes: 4 additions & 4 deletions apps/desktop/src-tauri/src/target_select_overlay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,11 @@ pub async fn open_target_select_overlays(
let display = focused_target
.as_ref()
.map(|v| v.display())
.unwrap_or_else(|| scap_targets::Display::get_containing_cursor());
.unwrap_or_else(scap_targets::Display::get_containing_cursor);
let window = focused_target
.as_ref()
.map(|v| v.window().and_then(|id| scap_targets::Window::from_id(&id)))
.unwrap_or_else(|| scap_targets::Window::get_topmost_at_cursor());
.unwrap_or_else(scap_targets::Window::get_topmost_at_cursor);

let _ = TargetUnderCursor {
display_id: display.map(|d| d.id()),
Expand Down Expand Up @@ -129,7 +129,7 @@ pub async fn close_target_select_overlays(app: AppHandle) -> Result<(), String>
pub async fn get_window_icon(window_id: &str) -> Result<Option<String>, String> {
let window_id = window_id
.parse::<WindowId>()
.map_err(|err| format!("Invalid window ID: {}", err))?;
.map_err(|err| format!("Invalid window ID: {err}"))?;

Ok(Window::from_id(&window_id)
.ok_or("Window not found")?
Expand All @@ -143,7 +143,7 @@ pub async fn get_window_icon(window_id: &str) -> Result<Option<String>, String>
pub async fn display_information(display_id: &str) -> Result<DisplayInformation, String> {
let display_id = display_id
.parse::<DisplayId>()
.map_err(|err| format!("Invalid display ID: {}", err))?;
.map_err(|err| format!("Invalid display ID: {err}"))?;
let display = Display::from_id(&display_id).ok_or("Display not found")?;

Ok(DisplayInformation {
Expand Down
Loading
Loading