mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2025-07-04 06:22:57 +00:00
Added file type filtering to file selection dialogs (#442)
This commit is contained in:
parent
437ea2c243
commit
10dc03f33b
@ -1,3 +1,4 @@
|
|||||||
|
using AIStudio.Tools.Rust;
|
||||||
using AIStudio.Tools.Services;
|
using AIStudio.Tools.Services;
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
@ -17,12 +18,9 @@ public partial class ReadPDFContent : MSGComponentBase
|
|||||||
|
|
||||||
private async Task SelectFile()
|
private async Task SelectFile()
|
||||||
{
|
{
|
||||||
var pdfFile = await this.RustService.SelectFile(T("Select PDF file"));
|
var pdfFile = await this.RustService.SelectFile(T("Select PDF file"), FileTypeFilter.PDF);
|
||||||
if (pdfFile.UserCancelled)
|
if (pdfFile.UserCancelled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!pdfFile.SelectedFilePath.EndsWith(".pdf", StringComparison.OrdinalIgnoreCase))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if(!File.Exists(pdfFile.SelectedFilePath))
|
if(!File.Exists(pdfFile.SelectedFilePath))
|
||||||
return;
|
return;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using AIStudio.Settings;
|
using AIStudio.Settings;
|
||||||
|
using AIStudio.Tools.Rust;
|
||||||
using AIStudio.Tools.Services;
|
using AIStudio.Tools.Services;
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
@ -22,6 +23,9 @@ public partial class SelectFile : ComponentBase
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public string FileDialogTitle { get; set; } = "Select File";
|
public string FileDialogTitle { get; set; } = "Select File";
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public FileTypeFilter? Filter { get; set; }
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public Func<string, string?> Validation { get; set; } = _ => null;
|
public Func<string, string?> Validation { get; set; } = _ => null;
|
||||||
|
|
||||||
@ -55,7 +59,7 @@ public partial class SelectFile : ComponentBase
|
|||||||
|
|
||||||
private async Task OpenFileDialog()
|
private async Task OpenFileDialog()
|
||||||
{
|
{
|
||||||
var response = await this.RustService.SelectFile(this.FileDialogTitle, string.IsNullOrWhiteSpace(this.File) ? null : this.File);
|
var response = await this.RustService.SelectFile(this.FileDialogTitle, this.Filter, string.IsNullOrWhiteSpace(this.File) ? null : this.File);
|
||||||
this.Logger.LogInformation($"The user selected the file '{response.SelectedFilePath}'.");
|
this.Logger.LogInformation($"The user selected the file '{response.SelectedFilePath}'.");
|
||||||
|
|
||||||
if (!response.UserCancelled)
|
if (!response.UserCancelled)
|
||||||
|
18
app/MindWork AI Studio/Tools/Rust/FileTypeFilter.cs
Normal file
18
app/MindWork AI Studio/Tools/Rust/FileTypeFilter.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// ReSharper disable NotAccessedPositionalProperty.Global
|
||||||
|
namespace AIStudio.Tools.Rust;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a file type filter for file selection dialogs.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="FilterName">The name of the filter.</param>
|
||||||
|
/// <param name="FilterExtensions">The file extensions associated with the filter.</param>
|
||||||
|
public readonly record struct FileTypeFilter(string FilterName, string[] FilterExtensions)
|
||||||
|
{
|
||||||
|
public static FileTypeFilter PDF => new("PDF Files", ["pdf"]);
|
||||||
|
|
||||||
|
public static FileTypeFilter Text => new("Text Files", ["txt", "md"]);
|
||||||
|
|
||||||
|
public static FileTypeFilter AllOffice => new("All Office Files", ["docx", "xlsx", "pptx", "doc", "xls", "ppt", "pdf"]);
|
||||||
|
|
||||||
|
public static FileTypeFilter AllImages => new("All Image Files", ["jpg", "jpeg", "png", "gif", "bmp", "tiff"]);
|
||||||
|
}
|
10
app/MindWork AI Studio/Tools/Rust/SelectFileOptions.cs
Normal file
10
app/MindWork AI Studio/Tools/Rust/SelectFileOptions.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
namespace AIStudio.Tools.Rust;
|
||||||
|
|
||||||
|
public sealed class SelectFileOptions
|
||||||
|
{
|
||||||
|
public required string Title { get; init; }
|
||||||
|
|
||||||
|
public PreviousFile? PreviousFile { get; init; }
|
||||||
|
|
||||||
|
public FileTypeFilter? Filter { get; init; }
|
||||||
|
}
|
@ -17,10 +17,16 @@ public sealed partial class RustService
|
|||||||
return await result.Content.ReadFromJsonAsync<DirectorySelectionResponse>(this.jsonRustSerializerOptions);
|
return await result.Content.ReadFromJsonAsync<DirectorySelectionResponse>(this.jsonRustSerializerOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<FileSelectionResponse> SelectFile(string title, string? initialFile = null)
|
public async Task<FileSelectionResponse> SelectFile(string title, FileTypeFilter? filter = null, string? initialFile = null)
|
||||||
{
|
{
|
||||||
PreviousFile? previousFile = initialFile is null ? null : new (initialFile);
|
var payload = new SelectFileOptions
|
||||||
var result = await this.http.PostAsJsonAsync($"/select/file?title={title}", previousFile, this.jsonRustSerializerOptions);
|
{
|
||||||
|
Title = title,
|
||||||
|
PreviousFile = initialFile is null ? null : new (initialFile),
|
||||||
|
Filter = filter
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = await this.http.PostAsJsonAsync("/select/file", payload, this.jsonRustSerializerOptions);
|
||||||
if (!result.IsSuccessStatusCode)
|
if (!result.IsSuccessStatusCode)
|
||||||
{
|
{
|
||||||
this.logger!.LogError($"Failed to select a file: '{result.StatusCode}'");
|
this.logger!.LogError($"Failed to select a file: '{result.StatusCode}'");
|
||||||
|
@ -267,6 +267,19 @@ pub struct PreviousDirectory {
|
|||||||
path: String,
|
path: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Deserialize)]
|
||||||
|
pub struct FileTypeFilter {
|
||||||
|
filter_name: String,
|
||||||
|
filter_extensions: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Deserialize)]
|
||||||
|
pub struct SelectFileOptions {
|
||||||
|
title: String,
|
||||||
|
previous_file: Option<PreviousFile>,
|
||||||
|
filter: Option<FileTypeFilter>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
pub struct DirectorySelectionResponse {
|
pub struct DirectorySelectionResponse {
|
||||||
user_cancelled: bool,
|
user_cancelled: bool,
|
||||||
@ -274,24 +287,36 @@ pub struct DirectorySelectionResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Let the user select a file.
|
/// Let the user select a file.
|
||||||
#[post("/select/file?<title>", data = "<previous_file>")]
|
#[post("/select/file", data = "<payload>")]
|
||||||
pub fn select_file(_token: APIToken, title: &str, previous_file: Option<Json<PreviousFile>>) -> Json<FileSelectionResponse> {
|
pub fn select_file(_token: APIToken, payload: Json<SelectFileOptions>) -> Json<FileSelectionResponse> {
|
||||||
let file_path = match previous_file {
|
|
||||||
Some(previous) => {
|
// Create a new file dialog builder:
|
||||||
let previous_path = previous.file_path.as_str();
|
let file_dialog = FileDialogBuilder::new();
|
||||||
FileDialogBuilder::new()
|
|
||||||
.set_title(title)
|
// Set the title of the file dialog:
|
||||||
.set_directory(previous_path)
|
let file_dialog = file_dialog.set_title(&payload.title);
|
||||||
.pick_file()
|
|
||||||
|
// Set the file type filter if provided:
|
||||||
|
let file_dialog = match &payload.filter {
|
||||||
|
Some(filter) => {
|
||||||
|
file_dialog.add_filter(&filter.filter_name, &filter.filter_extensions.iter().map(|s| s.as_str()).collect::<Vec<&str>>())
|
||||||
},
|
},
|
||||||
|
|
||||||
None => {
|
None => file_dialog,
|
||||||
FileDialogBuilder::new()
|
|
||||||
.set_title(title)
|
|
||||||
.pick_file()
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Set the previous file path if provided:
|
||||||
|
let file_dialog = match &payload.previous_file {
|
||||||
|
Some(previous) => {
|
||||||
|
let previous_path = previous.file_path.as_str();
|
||||||
|
file_dialog.set_directory(previous_path)
|
||||||
|
},
|
||||||
|
|
||||||
|
None => file_dialog,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Show the file dialog and get the selected file path:
|
||||||
|
let file_path = file_dialog.pick_file();
|
||||||
match file_path {
|
match file_path {
|
||||||
Some(path) => {
|
Some(path) => {
|
||||||
info!("User selected file: {path:?}");
|
info!("User selected file: {path:?}");
|
||||||
|
Loading…
Reference in New Issue
Block a user