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 = "")]
+pub fn select_directory(_token: APIToken, title: &str, previous_directory: Option>) -> Json {
+ 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,