@@ -206,10 +206,23 @@ pub fn start_disk_watcher(app: AppHandle) -> Result<(), String> {
206206 return ;
207207 }
208208
209+ // Also watch /dev for physical disk connect/disconnect events
210+ // This catches Linux-only disks that don't get mounted to /Volumes
211+ let dev_path = PathBuf :: from ( "/dev" ) ;
212+ if watcher. watch ( & dev_path, RecursiveMode :: NonRecursive ) . is_err ( ) {
213+ eprintln ! ( "Failed to watch /dev (continuing with /Volumes only)" ) ;
214+ // Don't return - /Volumes watching is still useful
215+ }
216+
209217 // Track pending event - we wait for events to settle before emitting
210218 let mut pending_event: Option < Instant > = None ;
211219 let settle_duration = Duration :: from_millis ( 1500 ) ; // Wait 1.5s after last event
212220
221+ // Track disk count for polling fallback (for Linux-only disks not in /Volumes)
222+ let mut last_disk_count = count_disks ( ) ;
223+ let mut last_poll = Instant :: now ( ) ;
224+ let poll_interval = Duration :: from_secs ( 3 ) ; // Poll every 3 seconds
225+
213226 loop {
214227 // Check if we should stop
215228 if state_clone. disk_watcher_stop . load ( Ordering :: SeqCst ) {
@@ -220,8 +233,18 @@ pub fn start_disk_watcher(app: AppHandle) -> Result<(), String> {
220233 Ok ( Ok ( event) ) => {
221234 match event. kind {
222235 EventKind :: Create ( _) | EventKind :: Remove ( _) => {
223- // Mark that we have a pending event, reset settle timer
224- pending_event = Some ( Instant :: now ( ) ) ;
236+ // Filter /dev events to only disk-related changes
237+ let is_disk_event = event. paths . iter ( ) . any ( |p| {
238+ let path_str = p. to_string_lossy ( ) ;
239+ // Match /Volumes/* or /dev/disk*
240+ path_str. starts_with ( "/Volumes/" ) ||
241+ ( path_str. starts_with ( "/dev/disk" ) && !path_str. contains ( "s" ) )
242+ } ) ;
243+
244+ if is_disk_event {
245+ // Mark that we have a pending event, reset settle timer
246+ pending_event = Some ( Instant :: now ( ) ) ;
247+ }
225248 }
226249 _ => { }
227250 }
@@ -236,8 +259,20 @@ pub fn start_disk_watcher(app: AppHandle) -> Result<(), String> {
236259 // Events have settled, emit and clear
237260 let _ = app. emit ( "disks-changed" , ( ) ) ;
238261 pending_event = None ;
262+ last_disk_count = count_disks ( ) ; // Update count after emit
239263 }
240264 }
265+
266+ // Polling fallback: check disk count periodically
267+ // This catches Linux-only disks that don't trigger /Volumes events
268+ if last_poll. elapsed ( ) >= poll_interval {
269+ let current_count = count_disks ( ) ;
270+ if current_count != last_disk_count {
271+ pending_event = Some ( Instant :: now ( ) ) ;
272+ last_disk_count = current_count;
273+ }
274+ last_poll = Instant :: now ( ) ;
275+ }
241276 }
242277 }
243278 }
@@ -248,6 +283,24 @@ pub fn start_disk_watcher(app: AppHandle) -> Result<(), String> {
248283 Ok ( ( ) )
249284}
250285
286+ /// Count physical disks by checking /dev/disk* entries
287+ fn count_disks ( ) -> usize {
288+ std:: fs:: read_dir ( "/dev" )
289+ . map ( |entries| {
290+ entries
291+ . filter_map ( |e| e. ok ( ) )
292+ . filter ( |e| {
293+ let name = e. file_name ( ) ;
294+ let name_str = name. to_string_lossy ( ) ;
295+ // Match disk0, disk1, etc. but not disk0s1 (partitions)
296+ name_str. starts_with ( "disk" ) &&
297+ name_str[ 4 ..] . chars ( ) . all ( |c| c. is_ascii_digit ( ) )
298+ } )
299+ . count ( )
300+ } )
301+ . unwrap_or ( 0 )
302+ }
303+
251304#[ tauri:: command]
252305pub fn stop_watchers ( app : AppHandle ) -> Result < ( ) , String > {
253306 let state = app. state :: < Arc < WatcherState > > ( ) ;
0 commit comments