diff --git a/app/MindWork AI Studio/Tools/Rust/DirectorySelectionResponse.cs b/app/MindWork AI Studio/Tools/Rust/DirectorySelectionResponse.cs new file mode 100644 index 00000000..7d874fdc --- /dev/null +++ b/app/MindWork AI Studio/Tools/Rust/DirectorySelectionResponse.cs @@ -0,0 +1,8 @@ +namespace AIStudio.Tools.Rust; + +/// +/// Data structure for selecting a directory. +/// +/// Was the directory selection canceled? +/// The selected directory, if any. +public readonly record struct DirectorySelectionResponse(bool UserCancelled, string SelectedDirectory); \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/Rust/PreviousDirectory.cs b/app/MindWork AI Studio/Tools/Rust/PreviousDirectory.cs new file mode 100644 index 00000000..b042293a --- /dev/null +++ b/app/MindWork AI Studio/Tools/Rust/PreviousDirectory.cs @@ -0,0 +1,7 @@ +namespace AIStudio.Tools.Rust; + +/// +/// Data structure for selecting a directory when a previous directory was selected. +/// +/// The path of the previous directory. +public readonly record struct PreviousDirectory(string Path); \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/RustService.cs b/app/MindWork AI Studio/Tools/RustService.cs index a38cf5fb..99476e52 100644 --- a/app/MindWork AI Studio/Tools/RustService.cs +++ b/app/MindWork AI Studio/Tools/RustService.cs @@ -322,6 +322,19 @@ public sealed class RustService : IDisposable return state; } + + public async Task SelectDirectory(string title, string? initialDirectory = null) + { + PreviousDirectory? previousDirectory = initialDirectory is null ? null : new (initialDirectory); + var result = await this.http.PostAsJsonAsync($"/select/directory?title={title}", previousDirectory, this.jsonRustSerializerOptions); + if (!result.IsSuccessStatusCode) + { + this.logger!.LogError($"Failed to select a directory: '{result.StatusCode}'"); + return new DirectorySelectionResponse(true, string.Empty); + } + + return await result.Content.ReadFromJsonAsync(this.jsonRustSerializerOptions); + } #region IDisposable diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 24c89d5e..af4953b8 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -9,7 +9,7 @@ authors = ["Thorsten Sommer"] tauri-build = { version = "1.5", features = [] } [dependencies] -tauri = { version = "1.8", features = [ "http-all", "updater", "shell-sidecar", "shell-open"] } +tauri = { version = "1.8", features = [ "http-all", "updater", "shell-sidecar", "shell-open", "dialog"] } tauri-plugin-window-state = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/runtime/src/app_window.rs b/runtime/src/app_window.rs index e5a5bc92..e83b2b57 100644 --- a/runtime/src/app_window.rs +++ b/runtime/src/app_window.rs @@ -2,11 +2,13 @@ use std::sync::Mutex; use std::time::Duration; use log::{error, info, warn}; use once_cell::sync::Lazy; -use rocket::get; +use rocket::{get, post}; use rocket::serde::json::Json; use rocket::serde::Serialize; +use serde::Deserialize; use tauri::updater::UpdateResponse; use tauri::{Manager, Window}; +use tauri::api::dialog::blocking::FileDialogBuilder; use tokio::time; use crate::api_token::APIToken; use crate::dotnet::stop_dotnet_server; @@ -219,4 +221,53 @@ pub async fn install_update(_token: APIToken) { error!(Source = "Updater"; "No update available to install. Did you check for updates first?"); }, } +} + +/// Let the user select a directory. +#[post("/select/directory?", data = "<previous_directory>")] +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() + .set_title(title) + .set_directory(previous_path) + .pick_folder() + }, + + None => { + FileDialogBuilder::new() + .set_title(title) + .pick_folder() + }, + }; + + match folder_path { + Some(path) => { + info!("User selected directory: {path:?}"); + Json(DirectorySelectionResponse { + user_cancelled: false, + selected_directory: path.to_str().unwrap().to_string(), + }) + }, + + None => { + info!("User cancelled directory selection."); + Json(DirectorySelectionResponse { + user_cancelled: true, + selected_directory: String::from(""), + }) + }, + } +} + +#[derive(Clone, Deserialize)] +pub struct PreviousDirectory { + path: String, +} + +#[derive(Serialize)] +pub struct DirectorySelectionResponse { + user_cancelled: bool, + selected_directory: String, } \ No newline at end of file diff --git a/runtime/src/runtime_api.rs b/runtime/src/runtime_api.rs index 4c12871d..963900d7 100644 --- a/runtime/src/runtime_api.rs +++ b/runtime/src/runtime_api.rs @@ -84,6 +84,7 @@ pub fn start_runtime_api() { crate::clipboard::set_clipboard, crate::app_window::check_for_update, crate::app_window::install_update, + crate::app_window::select_directory, crate::secret::get_secret, crate::secret::store_secret, crate::secret::delete_secret,