@@ -13,6 +13,12 @@ struct InstalledApp {
1313 developer : Option < String > ,
1414}
1515
16+ #[ derive( Serialize ) ]
17+ struct InstalledPackagesResponse {
18+ apps : Vec < InstalledApp > ,
19+ runtimes : Vec < String > ,
20+ }
21+
1622// Helper function to extract developer name from app_id
1723// Takes the second-to-last segment (penultimate)
1824// Example: io.github.N3kosempai.klia-store -> N3kosempai
@@ -344,25 +350,27 @@ fn check_file_exists(path: String) -> bool {
344350}
345351
346352#[ tauri:: command]
347- async fn get_installed_flatpaks ( app : tauri:: AppHandle ) -> Result < Vec < InstalledApp > , String > {
353+ async fn get_installed_flatpaks ( app : tauri:: AppHandle ) -> Result < InstalledPackagesResponse , String > {
348354 let shell = app. shell ( ) ;
349355
350356 // Detect if we're running inside a flatpak
351357 let is_flatpak = std:: env:: var ( "FLATPAK_ID" ) . is_ok ( ) ;
352358
359+ // Get everything (apps + runtimes) with ref column to distinguish
360+ // Note: flatpak list without --system or --user gets both
353361 let output = if is_flatpak {
354362 // Inside flatpak, use flatpak-spawn to execute on the host
355363 shell
356364 . command ( "flatpak-spawn" )
357- . args ( [ "--host" , "flatpak" , "list" , "--app" , "-- columns=application,name,version,description"] )
365+ . args ( [ "--host" , "flatpak" , "list" , "--columns=application,name,version,description,ref " ] )
358366 . output ( )
359367 . await
360368 . map_err ( |e| format ! ( "Failed to execute flatpak-spawn: {}" , e) ) ?
361369 } else {
362370 // Outside flatpak, use flatpak directly
363371 shell
364372 . command ( "flatpak" )
365- . args ( [ "list" , "--app" , "-- columns=application,name,version,description"] )
373+ . args ( [ "list" , "--columns=application,name,version,description,ref " ] )
366374 . output ( )
367375 . await
368376 . map_err ( |e| format ! ( "Failed to execute flatpak: {}" , e) ) ?
@@ -374,31 +382,98 @@ async fn get_installed_flatpaks(app: tauri::AppHandle) -> Result<Vec<InstalledAp
374382 }
375383
376384 let stdout = String :: from_utf8_lossy ( & output. stdout ) ;
377- let apps: Vec < InstalledApp > = stdout
378- . lines ( )
379- . filter ( |line| !line. trim ( ) . is_empty ( ) )
380- . filter_map ( |line| {
381- let parts: Vec < & str > = line. split ( '\t' ) . collect ( ) ;
382- if parts. len ( ) >= 3 {
383- let app_id = parts[ 0 ] . trim ( ) . to_string ( ) ;
384- Some ( InstalledApp {
385- app_id : app_id. clone ( ) ,
385+ let mut apps: Vec < InstalledApp > = Vec :: new ( ) ;
386+ let mut runtimes: Vec < String > = Vec :: new ( ) ;
387+
388+ for line in stdout. lines ( ) {
389+ if line. trim ( ) . is_empty ( ) {
390+ continue ;
391+ }
392+
393+ let parts: Vec < & str > = line. split ( '\t' ) . collect ( ) ;
394+ if parts. len ( ) >= 5 {
395+ let app_id = parts[ 0 ] . trim ( ) ;
396+ let ref_full = parts[ 4 ] . trim ( ) ;
397+
398+ // Distinguish apps from runtimes based on naming convention
399+ // Apps usually have reverse-DNS like: org.example.AppName
400+ // Runtimes usually end with .Platform, .Sdk, .BaseApp, etc.
401+ let is_runtime = app_id. ends_with ( ".Platform" )
402+ || app_id. ends_with ( ".Sdk" )
403+ || app_id. ends_with ( ".BaseApp" )
404+ || app_id. ends_with ( ".Compat" )
405+ || app_id. ends_with ( ".Locale" )
406+ || app_id. ends_with ( ".Debug" )
407+ || app_id. contains ( ".GL." )
408+ || app_id. contains ( ".VAAPI." )
409+ || app_id. contains ( ".ffmpeg" ) ;
410+
411+ if is_runtime {
412+ // It's a runtime - store the ref
413+ runtimes. push ( ref_full. to_string ( ) ) ;
414+ } else {
415+ // It's an application
416+ apps. push ( InstalledApp {
417+ app_id : app_id. to_string ( ) ,
386418 name : parts[ 1 ] . trim ( ) . to_string ( ) ,
387419 version : parts[ 2 ] . trim ( ) . to_string ( ) ,
388- summary : if parts . len ( ) >= 4 && !parts[ 3 ] . trim ( ) . is_empty ( ) {
420+ summary : if !parts[ 3 ] . trim ( ) . is_empty ( ) {
389421 Some ( parts[ 3 ] . trim ( ) . to_string ( ) )
390422 } else {
391423 None
392424 } ,
393- developer : extract_developer ( & app_id) ,
394- } )
395- } else {
396- None
425+ developer : extract_developer ( app_id) ,
426+ } ) ;
397427 }
398- } )
399- . collect ( ) ;
428+ }
429+ }
430+
431+ Ok ( InstalledPackagesResponse { apps, runtimes } )
432+ }
433+
434+ #[ tauri:: command]
435+ async fn get_app_runtime_info ( app : tauri:: AppHandle , app_id : String ) -> Result < String , String > {
436+ let shell = app. shell ( ) ;
437+
438+ // Detect if we're running inside a flatpak
439+ let is_flatpak = std:: env:: var ( "FLATPAK_ID" ) . is_ok ( ) ;
440+
441+ // Use --user to match installation scope and avoid interactive prompt
442+ let output = if is_flatpak {
443+ // Inside flatpak, use flatpak-spawn to execute on the host
444+ shell
445+ . command ( "flatpak-spawn" )
446+ . args ( [ "--host" , "flatpak" , "remote-info" , "--user" , "--show-metadata" , "flathub" , & app_id] )
447+ . output ( )
448+ . await
449+ . map_err ( |e| format ! ( "Failed to execute flatpak-spawn: {}" , e) ) ?
450+ } else {
451+ // Outside flatpak, use flatpak directly
452+ shell
453+ . command ( "flatpak" )
454+ . args ( [ "remote-info" , "--user" , "--show-metadata" , "flathub" , & app_id] )
455+ . output ( )
456+ . await
457+ . map_err ( |e| format ! ( "Failed to execute flatpak: {}" , e) ) ?
458+ } ;
459+
460+ if !output. status . success ( ) {
461+ let error = String :: from_utf8_lossy ( & output. stderr ) ;
462+ return Err ( format ! ( "Flatpak command failed: {}" , error) ) ;
463+ }
464+
465+ let stdout = String :: from_utf8_lossy ( & output. stdout ) ;
466+
467+ // Parse the metadata to extract runtime=
468+ for line in stdout. lines ( ) {
469+ if line. starts_with ( "runtime=" ) {
470+ if let Some ( runtime) = line. strip_prefix ( "runtime=" ) {
471+ return Ok ( runtime. trim ( ) . to_string ( ) ) ;
472+ }
473+ }
474+ }
400475
401- Ok ( apps )
476+ Err ( "Runtime information not found in metadata" . to_string ( ) )
402477}
403478
404479#[ tauri:: command]
@@ -659,6 +734,7 @@ pub fn run() {
659734 get_cached_image_path,
660735 check_file_exists,
661736 get_installed_flatpaks,
737+ get_app_runtime_info,
662738 get_available_updates,
663739 update_flatpak,
664740 update_system_flatpaks,
0 commit comments