Fixed file & folder selection dialogs

This commit is contained in:
Thorsten Sommer 2026-04-15 18:10:28 +02:00
parent d56eb5b4ea
commit 73f055960f
No known key found for this signature in database
GPG Key ID: B0B7E2FC074BF1F5
2 changed files with 51 additions and 22 deletions

View File

@ -32,6 +32,7 @@
- Fixed an issue with chat templates that could stop working because the stored validation result for attached files was reused. AI Studio now checks attached files again when you use a chat template.
- Fixed an issue with voice recording where AI Studio could log errors and keep the feature available even though required parts failed to initialize. Voice recording is now disabled automatically for the current session in that case.
- Fixed an issue where the app could turn white or appear invisible in certain chats after HTML-like content was shown. Thanks, Inga, for reporting this issue and providing some context on how to reproduce it.
- Fixed an issue where file and folder selection dialogs could open more than once on Windows. Thanks to Bernhard for reporting this bug.
- Fixed an issue where exporting to Word could fail when the message contained certain formatting.
- Fixed security issues in the native app runtime by strengthening how AI Studio creates and protects the secret values used for its internal secure connection.
- Updated several security-sensitive Rust dependencies in the native runtime to address known vulnerabilities.

View File

@ -133,7 +133,7 @@ pub fn start_tauri() {
if !matches!(event, RunEvent::MainEventsCleared) {
debug!(Source = "Tauri"; "Tauri event received: location=app event handler , event={event:?}");
}
match event {
RunEvent::WindowEvent { event, label, .. } => {
match event {
@ -311,11 +311,11 @@ impl Event {
FileDropEvent::Dropped(files) => Event::new(TauriEventType::FileDropDropped,
files.iter().map(|f| f.to_string_lossy().to_string()).collect(),
),
),
FileDropEvent::Cancelled => Event::new(TauriEventType::FileDropCanceled,
Vec::new(),
),
),
_ => Event::new(TauriEventType::Unknown,
Vec::new(),
@ -327,7 +327,7 @@ impl Event {
Event::new(TauriEventType::WindowFocused,
Vec::new(),
)
} else {
} else {
Event::new(TauriEventType::WindowNotFocused,
Vec::new(),
)
@ -476,23 +476,23 @@ pub async fn install_update(_token: APIToken) {
/// Let the user select a directory.
#[post("/select/directory?<title>", data = "<previous_directory>")]
pub fn select_directory(_token: APIToken, title: &str, previous_directory: Option<Json<PreviousDirectory>>) -> Json<DirectorySelectionResponse> {
pub fn select_directory(
_token: APIToken,
title: &str,
previous_directory: Option<Json<PreviousDirectory>>,
) -> Json<DirectorySelectionResponse> {
let folder_path = match previous_directory {
Some(previous) => {
let previous_path = previous.path.as_str();
FileDialogBuilder::new()
create_file_dialog()
.set_title(title)
.set_directory(previous_path)
.pick_folder()
},
None => {
FileDialogBuilder::new()
.set_title(title)
.pick_folder()
},
None => create_file_dialog().set_title(title).pick_folder(),
};
match folder_path {
Some(path) => {
info!("User selected directory: {path:?}");
@ -545,10 +545,12 @@ pub struct DirectorySelectionResponse {
/// Let the user select a file.
#[post("/select/file", data = "<payload>")]
pub fn select_file(_token: APIToken, payload: Json<SelectFileOptions>) -> Json<FileSelectionResponse> {
pub fn select_file(
_token: APIToken,
payload: Json<SelectFileOptions>,
) -> Json<FileSelectionResponse> {
// Create a new file dialog builder:
let file_dialog = FileDialogBuilder::new();
let file_dialog = create_file_dialog();
// Set the title of the file dialog:
let file_dialog = file_dialog.set_title(&payload.title);
@ -589,10 +591,12 @@ pub fn select_file(_token: APIToken, payload: Json<SelectFileOptions>) -> Json<F
/// Let the user select some files.
#[post("/select/files", data = "<payload>")]
pub fn select_files(_token: APIToken, payload: Json<SelectFileOptions>) -> Json<FilesSelectionResponse> {
pub fn select_files(
_token: APIToken,
payload: Json<SelectFileOptions>,
) -> Json<FilesSelectionResponse> {
// Create a new file dialog builder:
let file_dialog = FileDialogBuilder::new();
let file_dialog = create_file_dialog();
// Set the title of the file dialog:
let file_dialog = file_dialog.set_title(&payload.title);
@ -617,7 +621,10 @@ pub fn select_files(_token: APIToken, payload: Json<SelectFileOptions>) -> Json<
info!("User selected {} files.", paths.len());
Json(FilesSelectionResponse {
user_cancelled: false,
selected_file_paths: paths.iter().map(|p| p.to_str().unwrap().to_string()).collect(),
selected_file_paths: paths
.iter()
.map(|p| p.to_str().unwrap().to_string())
.collect(),
})
}
@ -633,9 +640,8 @@ pub fn select_files(_token: APIToken, payload: Json<SelectFileOptions>) -> Json<
#[post("/save/file", data = "<payload>")]
pub fn save_file(_token: APIToken, payload: Json<SaveFileOptions>) -> Json<FileSaveResponse> {
// Create a new file dialog builder:
let file_dialog = FileDialogBuilder::new();
let file_dialog = create_file_dialog();
// Set the title of the file dialog:
let file_dialog = file_dialog.set_title(&payload.title);
@ -679,6 +685,28 @@ pub struct PreviousFile {
file_path: String,
}
/// Creates a file dialog builder and assigns the main window as parent where supported.
fn create_file_dialog() -> FileDialogBuilder {
let file_dialog = FileDialogBuilder::new();
#[cfg(any(windows, target_os = "macos"))]
{
let main_window_lock = MAIN_WINDOW.lock().unwrap();
match main_window_lock.as_ref() {
Some(window) => file_dialog.set_parent(window),
None => {
warn!(Source = "Tauri"; "Cannot assign parent window to file dialog: main window not available.");
file_dialog
}
}
}
#[cfg(not(any(windows, target_os = "macos")))]
{
file_dialog
}
}
/// Applies an optional file type filter to a FileDialogBuilder.
fn apply_filter(file_dialog: FileDialogBuilder, filter: &Option<FileTypeFilter>) -> FileDialogBuilder {
match filter {
@ -804,7 +832,7 @@ pub fn register_shortcut(_token: APIToken, payload: Json<RegisterShortcutRequest
error_message: "Cannot register NONE shortcut".to_string(),
});
}
info!(Source = "Tauri"; "Registering global shortcut '{}' with key '{new_shortcut}'.", id);
// Get the main window to access the global shortcut manager: