diff --git a/app/MindWork AI Studio/wwwroot/changelog/v26.6.3.md b/app/MindWork AI Studio/wwwroot/changelog/v26.6.3.md index ed0cab20..81e7ccd5 100644 --- a/app/MindWork AI Studio/wwwroot/changelog/v26.6.3.md +++ b/app/MindWork AI Studio/wwwroot/changelog/v26.6.3.md @@ -5,6 +5,7 @@ - Added support for organization-approved assistant plugins, so trusted assistant plugins can be enabled without requiring each user to run a separate security audit. - Added support for assistant plugin tiles that can open a chat directly in a chosen workspace. - Improved the provider selection by showing small capability icons for supported audio, image, speech, and reasoning features of the selected model. +- Improved PDF file handling in Flatpak builds by supporting the standard Flatpak library location for PDFium and adding clearer diagnostics for troubleshooting PDF loading. - Improved source links in chat answers when file or document names contained spaces, umlauts, or other special characters. Source entries now open much more reliably for documents with names such as PDFs from shared portals or internal knowledge bases. - Improved all assistants, so running tasks can continue when you leave the assistant and return later. - Improved the assistant overview so it shows which assistants are still running or have a result ready. diff --git a/runtime/src/app_window.rs b/runtime/src/app_window.rs index bdcee20d..2413e0fe 100644 --- a/runtime/src/app_window.rs +++ b/runtime/src/app_window.rs @@ -24,7 +24,9 @@ use tokio::sync::broadcast; use tokio::time; use crate::api_token::APIToken; use crate::dotnet::{cleanup_dotnet_server, start_dotnet_server, stop_dotnet_server}; -use crate::environment::{is_prod, is_dev, CONFIG_DIRECTORY, DATA_DIRECTORY}; +use crate::environment::{ + is_prod, is_dev, is_flatpak, CONFIG_DIRECTORY, DATA_DIRECTORY, FLATPAK_LIBRARY_DIRECTORY, +}; use crate::log::switch_to_file_logging; use crate::pdfium::PDFIUM_LIB_PATH; use crate::qdrant_edge_database::{start_qdrant_edge_database, stop_qdrant_edge_database}; @@ -974,7 +976,7 @@ fn set_pdfium_path(path_resolver: &PathResolver) { } }; - match select_pdfium_library_directory(&resource_dir) { + match select_pdfium_library_directory(&resource_dir, is_flatpak()) { Some(path) => { *PDFIUM_LIB_PATH.lock().unwrap() = Some(path.to_string_lossy().to_string()); } @@ -984,11 +986,23 @@ fn set_pdfium_path(path_resolver: &PathResolver) { } } -fn select_pdfium_library_directory(resource_dir: &Path) -> Option { - let candidate_paths = [ - resource_dir.join("resources").join("libraries"), - resource_dir.join("libraries"), - ]; +fn select_pdfium_library_directory(resource_dir: &Path, include_flatpak_library_directory: bool) -> Option { + select_pdfium_library_directory_for(resource_dir, include_flatpak_library_directory, Path::new(FLATPAK_LIBRARY_DIRECTORY)) +} + +fn select_pdfium_library_directory_for( + resource_dir: &Path, + include_flatpak_library_directory: bool, + flatpak_library_directory: &Path, +) -> Option { + let mut candidate_paths = Vec::new(); + + if include_flatpak_library_directory { + candidate_paths.push(flatpak_library_directory.to_path_buf()); + } + + candidate_paths.push(resource_dir.join("resources").join("libraries")); + candidate_paths.push(resource_dir.join("libraries")); for path in candidate_paths { let pdfium_library_path = Pdfium::pdfium_platform_library_name_at_path(&path); @@ -1022,7 +1036,7 @@ mod tests { create_pdfium_library_in(&libraries); assert_eq!( - select_pdfium_library_directory(temp_dir.path()), + select_pdfium_library_directory(temp_dir.path(), false), Some(resources_libraries) ); } @@ -1036,7 +1050,7 @@ mod tests { create_pdfium_library_in(&libraries); assert_eq!( - select_pdfium_library_directory(temp_dir.path()), + select_pdfium_library_directory(temp_dir.path(), false), Some(libraries) ); } @@ -1047,7 +1061,33 @@ mod tests { fs::create_dir_all(temp_dir.path().join("resources").join("libraries")).unwrap(); fs::create_dir_all(temp_dir.path().join("libraries")).unwrap(); - assert_eq!(select_pdfium_library_directory(temp_dir.path()), None); + assert_eq!(select_pdfium_library_directory(temp_dir.path(), false), None); + } + + #[test] + fn pdfium_library_directory_prefers_flatpak_library_directory_when_flatpak() { + let temp_dir = tempfile::tempdir().unwrap(); + let flatpak_library_directory = temp_dir.path().join("app").join("lib"); + let resources_libraries = temp_dir.path().join("resources").join("libraries"); + create_pdfium_library_in(&flatpak_library_directory); + create_pdfium_library_in(&resources_libraries); + + assert_eq!( + select_pdfium_library_directory_for(temp_dir.path(), true, &flatpak_library_directory), + Some(flatpak_library_directory) + ); + } + + #[test] + fn pdfium_library_directory_skips_flatpak_library_directory_when_not_flatpak() { + let temp_dir = tempfile::tempdir().unwrap(); + let flatpak_library_directory = temp_dir.path().join("app").join("lib"); + create_pdfium_library_in(&flatpak_library_directory); + + assert_eq!( + select_pdfium_library_directory_for(temp_dir.path(), false, &flatpak_library_directory), + None + ); } fn create_pdfium_library_in(path: &Path) { diff --git a/runtime/src/environment.rs b/runtime/src/environment.rs index 8da33ced..6f10b1c9 100644 --- a/runtime/src/environment.rs +++ b/runtime/src/environment.rs @@ -31,6 +31,8 @@ pub const DOTNET_ENV_CUSTOM_ROOT_CERTIFICATE_ALLOWED_HOSTS: &str = "AI_STUDIO_EX #[cfg(any(target_os = "linux", test))] const FLATPAK_ENTERPRISE_POLICY_DIRECTORY: &str = "/app/etc/MindWorkAI"; +pub(crate) const FLATPAK_LIBRARY_DIRECTORY: &str = "/app/lib"; + const ENTERPRISE_ENV_CONFIG_ID_PREFIX: &str = "MINDWORK_AI_STUDIO_ENTERPRISE_CONFIG_ID"; const ENTERPRISE_ENV_CONFIG_SERVER_URL_PREFIX: &str = "MINDWORK_AI_STUDIO_ENTERPRISE_CONFIG_SERVER_URL"; const ENTERPRISE_ENV_CONFIGS: &str = "MINDWORK_AI_STUDIO_ENTERPRISE_CONFIGS"; diff --git a/runtime/src/pdfium.rs b/runtime/src/pdfium.rs index 98ba4046..7a128dcb 100644 --- a/runtime/src/pdfium.rs +++ b/runtime/src/pdfium.rs @@ -2,7 +2,7 @@ use std::error::Error; use std::sync::Mutex; use once_cell::sync::Lazy; use pdfium_render::prelude::Pdfium; -use log::{error, warn}; +use log::{error, info, warn}; pub static PDFIUM_LIB_PATH: Lazy>> = Lazy::new(|| Mutex::new(None)); static PDFIUM: Lazy>> = Lazy::new(|| Mutex::new(None)); @@ -32,11 +32,22 @@ impl PdfiumInit for Pdfium { fn load_pdfium() -> Result { let lib_path = PDFIUM_LIB_PATH.lock().unwrap().clone(); if let Some(path) = lib_path.as_ref() { - return match Pdfium::bind_to_library(Pdfium::pdfium_platform_library_name_at_path(path)) { - Ok(binding) => Ok(Pdfium::new(binding)), + let pdfium_library_path = Pdfium::pdfium_platform_library_name_at_path(path); + + return match Pdfium::bind_to_library(&pdfium_library_path) { + Ok(binding) => { + info!("Loaded PDFium from '{path}'.", path = pdfium_library_path.to_string_lossy()); + Ok(Pdfium::new(binding)) + }, Err(library_error) => { match Pdfium::bind_to_system_library() { - Ok(binding) => Ok(Pdfium::new(binding)), + Ok(binding) => { + info!( + "Loaded PDFium from the system library after failing to load '{path}'.", + path = pdfium_library_path.to_string_lossy(), + ); + Ok(Pdfium::new(binding)) + }, Err(system_error) => { let error_message = format!( "Failed to load PDFium from '{path}' and the system library. Developer action (from repo root): run the build script once to download the required PDFium version: `cd app/Build` and `dotnet run build`. Details: library error: '{library_error}'; system error: '{system_error}'." @@ -52,7 +63,10 @@ fn load_pdfium() -> Result { warn!("No custom PDFium library path set; trying to load PDFium from the system library."); match Pdfium::bind_to_system_library() { - Ok(binding) => Ok(Pdfium::new(binding)), + Ok(binding) => { + info!("Loaded PDFium from the system library."); + Ok(Pdfium::new(binding)) + }, Err(system_error) => { let error_message = format!( "Failed to load PDFium from the system library. Developer action (from repo root): run the build script once to download the required PDFium version: `cd app/Build` and `dotnet run build`. Details: '{system_error}'."