diff --git a/app/MindWork AI Studio/Assistants/BatchProcessing/AssistantBatchProcessing.razor b/app/MindWork AI Studio/Assistants/BatchProcessing/AssistantBatchProcessing.razor
index 7e9429d9..c0099c70 100644
--- a/app/MindWork AI Studio/Assistants/BatchProcessing/AssistantBatchProcessing.razor
+++ b/app/MindWork AI Studio/Assistants/BatchProcessing/AssistantBatchProcessing.razor
@@ -7,7 +7,9 @@
@T("Input")
-
+@* This page carries a second drop zone for the instructions, so neither of them catches all
+ documents: with two targets, only the one under the mouse may take the drop. *@
+
@@ -44,7 +46,7 @@
@if (this.promptSource is BatchProcessingPromptSource.FREE_PROMPT)
{
-
+
@@ -52,7 +54,7 @@
}
else if (this.promptSource is BatchProcessingPromptSource.FILE_IMPORT)
{
-
+
@if (!string.IsNullOrWhiteSpace(this.promptFilePath))
{
diff --git a/app/MindWork AI Studio/Assistants/ERI/AssistantERI.razor b/app/MindWork AI Studio/Assistants/ERI/AssistantERI.razor
index 112b6820..15a39c90 100644
--- a/app/MindWork AI Studio/Assistants/ERI/AssistantERI.razor
+++ b/app/MindWork AI Studio/Assistants/ERI/AssistantERI.razor
@@ -345,4 +345,4 @@ else
-
+
diff --git a/app/MindWork AI Studio/Assistants/I18N/allTexts.lua b/app/MindWork AI Studio/Assistants/I18N/allTexts.lua
index d753b8ec..003ff2a0 100644
--- a/app/MindWork AI Studio/Assistants/I18N/allTexts.lua
+++ b/app/MindWork AI Studio/Assistants/I18N/allTexts.lua
@@ -4291,12 +4291,33 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SECRETINPUTFIELD::T1273315904"] = "Hide c
-- Show content
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SECRETINPUTFIELD::T2891011873"] = "Show content"
+-- The dropped folder could not be accessed. Please choose it with the folder chooser instead.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SELECTDIRECTORY::T1153417816"] = "The dropped folder could not be accessed. Please choose it with the folder chooser instead."
+
+-- Please drop a folder, not a file.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SELECTDIRECTORY::T3289690493"] = "Please drop a folder, not a file."
+
+-- You can also drag & drop the folder here.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SELECTDIRECTORY::T350096725"] = "You can also drag & drop the folder here."
+
-- Choose Directory
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SELECTDIRECTORY::T4256489763"] = "Choose Directory"
+-- Please drop a file, not a folder.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SELECTFILE::T1472251601"] = "Please drop a file, not a folder."
+
+-- You can also drag & drop the file here.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SELECTFILE::T1984243691"] = "You can also drag & drop the file here."
+
-- Choose File
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SELECTFILE::T4285779702"] = "Choose File"
+-- Please drop a file with a supported file type.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SELECTFILE::T930441004"] = "Please drop a file with a supported file type."
+
+-- The dropped file could not be accessed. Please choose it with the file chooser instead.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SELECTFILE::T984660028"] = "The dropped file could not be accessed. Please choose it with the file chooser instead."
+
-- External Assistants rated below this audit level are treated as insufficiently reviewed.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAGENTASSISTANTAUDIT::T1162151451"] = "External Assistants rated below this audit level are treated as insufficiently reviewed."
diff --git a/app/MindWork AI Studio/Components/SelectDirectory.razor b/app/MindWork AI Studio/Components/SelectDirectory.razor
index 096db371..b078d474 100644
--- a/app/MindWork AI Studio/Components/SelectDirectory.razor
+++ b/app/MindWork AI Studio/Components/SelectDirectory.razor
@@ -1,19 +1,37 @@
@inherits MSGComponentBase
-
-
-
-
- @T("Choose Directory")
-
-
\ No newline at end of file
+@if (this.EnableDragDrop)
+{
+
+ @this.Picker
+
+ @T("You can also drag & drop the folder here.")
+
+
+}
+else
+{
+ @this.Picker
+}
+
+@code {
+
+ private RenderFragment Picker =>
+ @
+
+
+
+ @T("Choose Directory")
+
+ ;
+}
diff --git a/app/MindWork AI Studio/Components/SelectDirectory.razor.cs b/app/MindWork AI Studio/Components/SelectDirectory.razor.cs
index 6f576435..883d5277 100644
--- a/app/MindWork AI Studio/Components/SelectDirectory.razor.cs
+++ b/app/MindWork AI Studio/Components/SelectDirectory.razor.cs
@@ -2,6 +2,9 @@ using AIStudio.Tools.Services;
using Microsoft.AspNetCore.Components;
+// This component has a parameter called Directory, which would shadow the file system's Directory class:
+using IODirectory = System.IO.Directory;
+
namespace AIStudio.Components;
public partial class SelectDirectory : MSGComponentBase
@@ -24,6 +27,24 @@ public partial class SelectDirectory : MSGComponentBase
[Parameter]
public Func Validation { get; set; } = _ => null;
+ ///
+ /// When true, the folder can also be chosen by dropping it onto this component.
+ ///
+ [Parameter]
+ public bool EnableDragDrop { get; set; }
+
+ ///
+ /// On which layer to register the drop area. Higher layers have priority over lower layers.
+ ///
+ [Parameter]
+ public int Layer { get; set; } = DropLayers.ROOT;
+
+ ///
+ /// Catch all documents that are hovered over the AI Studio window and not only over the drop zone.
+ ///
+ [Parameter]
+ public bool CatchAllDocuments { get; set; }
+
[Inject]
public RustService RustService { get; set; } = null!;
@@ -69,4 +90,39 @@ public partial class SelectDirectory : MSGComponentBase
this.isDirectoryDialogOpen = false;
}
}
+
+ ///
+ /// Takes the first dropped path which leads to a folder.
+ ///
+ ///
+ /// A dropped file is rejected instead of being taken as its parent folder. Everything a folder
+ /// contains is processed, so guessing the parent of a mistakenly dropped file could pull in far
+ /// more data than the user meant to hand over.
+ ///
+ /// The dropped paths.
+ private async Task PathsDropped(List paths)
+ {
+ foreach (var path in paths)
+ {
+ if (!IODirectory.Exists(path))
+ continue;
+
+ this.Logger.LogInformation("The user dropped the directory '{DroppedDirectory}'.", path);
+ this.InternalDirectoryChanged(path);
+ return;
+ }
+
+ this.Logger.LogWarning("None of the {Count} dropped path(s) could be used as a directory.", paths.Count);
+ await this.MessageBus.SendWarning(new(Icons.Material.Filled.Warning, this.GetDropWarning(paths)));
+ }
+
+ private string GetDropWarning(List paths)
+ {
+ // Naming the actual mistake beats a generic "that did not work". Dropping a file onto a
+ // folder picker is the likeliest of them:
+ if (paths.Any(File.Exists))
+ return T("Please drop a folder, not a file.");
+
+ return T("The dropped folder could not be accessed. Please choose it with the folder chooser instead.");
+ }
}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Components/SelectFile.razor b/app/MindWork AI Studio/Components/SelectFile.razor
index 726965fd..4df232b8 100644
--- a/app/MindWork AI Studio/Components/SelectFile.razor
+++ b/app/MindWork AI Studio/Components/SelectFile.razor
@@ -1,19 +1,37 @@
@inherits MSGComponentBase
-
-
-
-
- @T("Choose File")
-
-
\ No newline at end of file
+@if (this.EnableDragDrop)
+{
+
+ @this.Picker
+
+ @T("You can also drag & drop the file here.")
+
+
+}
+else
+{
+ @this.Picker
+}
+
+@code {
+
+ private RenderFragment Picker =>
+ @
+
+
+
+ @T("Choose File")
+
+ ;
+}
diff --git a/app/MindWork AI Studio/Components/SelectFile.razor.cs b/app/MindWork AI Studio/Components/SelectFile.razor.cs
index de1f89a3..124f104f 100644
--- a/app/MindWork AI Studio/Components/SelectFile.razor.cs
+++ b/app/MindWork AI Studio/Components/SelectFile.razor.cs
@@ -3,6 +3,9 @@ using AIStudio.Tools.Services;
using Microsoft.AspNetCore.Components;
+// This component has a parameter called File, which would shadow the file system's File class:
+using IOFile = System.IO.File;
+
namespace AIStudio.Components;
public partial class SelectFile : MSGComponentBase
@@ -28,6 +31,24 @@ public partial class SelectFile : MSGComponentBase
[Parameter]
public Func Validation { get; set; } = _ => null;
+ ///
+ /// When true, the file can also be chosen by dropping it onto this component.
+ ///
+ [Parameter]
+ public bool EnableDragDrop { get; set; }
+
+ ///
+ /// On which layer to register the drop area. Higher layers have priority over lower layers.
+ ///
+ [Parameter]
+ public int Layer { get; set; } = DropLayers.ROOT;
+
+ ///
+ /// Catch all documents that are hovered over the AI Studio window and not only over the drop zone.
+ ///
+ [Parameter]
+ public bool CatchAllDocuments { get; set; }
+
[Inject]
public RustService RustService { get; set; } = null!;
@@ -73,4 +94,46 @@ public partial class SelectFile : MSGComponentBase
this.isFileDialogOpen = false;
}
}
+
+ ///
+ /// Takes the first dropped path which leads to a usable file.
+ ///
+ ///
+ /// This component carries exactly one file, so a multi-selection cannot be honored as a whole.
+ /// A dropped folder is rejected instead of being read as "the first file inside it": the user
+ /// was asked for a file, and picking one for them would be a surprise.
+ ///
+ /// The dropped paths.
+ private async Task PathsDropped(List paths)
+ {
+ foreach (var path in paths)
+ {
+ if (!IOFile.Exists(path))
+ continue;
+
+ if (this.Filter is { Length: > 0 } && !FileTypes.IsAllowedPath(path, this.Filter))
+ continue;
+
+ this.Logger.LogInformation("The user dropped the file '{DroppedFilePath}'.", path);
+ this.InternalFileChanged(path);
+ return;
+ }
+
+ this.Logger.LogWarning("None of the {Count} dropped path(s) could be used as a file.", paths.Count);
+ await this.MessageBus.SendWarning(new(Icons.Material.Filled.Warning, this.GetDropWarning(paths)));
+ }
+
+ private string GetDropWarning(List paths)
+ {
+ // Naming the actual mistake beats a generic "that did not work". Dropping a folder onto a
+ // file picker is the likeliest of them:
+ if (paths.Any(Directory.Exists))
+ return T("Please drop a file, not a folder.");
+
+ // The file exists, so the file type filter is what turned it down:
+ if (paths.Any(IOFile.Exists))
+ return T("Please drop a file with a supported file type.");
+
+ return T("The dropped file could not be accessed. Please choose it with the file chooser instead.");
+ }
}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Dialogs/DataSourceLocalDirectoryDialog.razor b/app/MindWork AI Studio/Dialogs/DataSourceLocalDirectoryDialog.razor
index f80a98a9..9679ead3 100644
--- a/app/MindWork AI Studio/Dialogs/DataSourceLocalDirectoryDialog.razor
+++ b/app/MindWork AI Studio/Dialogs/DataSourceLocalDirectoryDialog.razor
@@ -51,7 +51,7 @@
}
@if (this.CanChangeSourceAndEmbedding)
{
-
+
}
else
{
diff --git a/app/MindWork AI Studio/Dialogs/DataSourceLocalFileDialog.razor b/app/MindWork AI Studio/Dialogs/DataSourceLocalFileDialog.razor
index 8f3cb2e3..965a51f6 100644
--- a/app/MindWork AI Studio/Dialogs/DataSourceLocalFileDialog.razor
+++ b/app/MindWork AI Studio/Dialogs/DataSourceLocalFileDialog.razor
@@ -51,7 +51,7 @@
}
@if (this.CanChangeSourceAndEmbedding)
{
-
+
}
else
{
diff --git a/app/MindWork AI Studio/Plugins/languages/de-de-43065dbc-78d0-45b7-92be-f14c2926e2dc/plugin.lua b/app/MindWork AI Studio/Plugins/languages/de-de-43065dbc-78d0-45b7-92be-f14c2926e2dc/plugin.lua
index 42057668..abf0e349 100644
--- a/app/MindWork AI Studio/Plugins/languages/de-de-43065dbc-78d0-45b7-92be-f14c2926e2dc/plugin.lua
+++ b/app/MindWork AI Studio/Plugins/languages/de-de-43065dbc-78d0-45b7-92be-f14c2926e2dc/plugin.lua
@@ -4293,12 +4293,33 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SECRETINPUTFIELD::T1273315904"] = "Inhalt
-- Show content
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SECRETINPUTFIELD::T2891011873"] = "Inhalt anzeigen"
+-- The dropped folder could not be accessed. Please choose it with the folder chooser instead.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SELECTDIRECTORY::T1153417816"] = "Auf den abgelegten Ordner konnte nicht zugegriffen werden. Bitte wählen Sie ihn stattdessen über die Ordnerauswahl aus."
+
+-- Please drop a folder, not a file.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SELECTDIRECTORY::T3289690493"] = "Bitte legen Sie einen Ordner ab, keine Datei."
+
+-- You can also drag & drop the folder here.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SELECTDIRECTORY::T350096725"] = "Sie können den Ordner auch hierher ziehen und ablegen."
+
-- Choose Directory
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SELECTDIRECTORY::T4256489763"] = "Verzeichnis auswählen"
+-- Please drop a file, not a folder.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SELECTFILE::T1472251601"] = "Bitte legen Sie eine Datei ab, keinen Ordner."
+
+-- You can also drag & drop the file here.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SELECTFILE::T1984243691"] = "Sie können die Datei auch hierher ziehen und ablegen."
+
-- Choose File
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SELECTFILE::T4285779702"] = "Datei auswählen"
+-- Please drop a file with a supported file type.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SELECTFILE::T930441004"] = "Bitte legen Sie eine Datei mit einem unterstützten Dateityp ab."
+
+-- The dropped file could not be accessed. Please choose it with the file chooser instead.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SELECTFILE::T984660028"] = "Auf die abgelegte Datei konnte nicht zugegriffen werden. Bitte wählen Sie sie stattdessen über die Dateiauswahl aus."
+
-- External Assistants rated below this audit level are treated as insufficiently reviewed.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAGENTASSISTANTAUDIT::T1162151451"] = "Externe Assistenten, die unter diesem Audit Level bewertet werden, gelten als nicht ausreichend sicher."
diff --git a/app/MindWork AI Studio/Plugins/languages/en-us-97dfb1ba-50c4-4440-8dfa-6575daf543c8/plugin.lua b/app/MindWork AI Studio/Plugins/languages/en-us-97dfb1ba-50c4-4440-8dfa-6575daf543c8/plugin.lua
index 7b26d329..00fd7658 100644
--- a/app/MindWork AI Studio/Plugins/languages/en-us-97dfb1ba-50c4-4440-8dfa-6575daf543c8/plugin.lua
+++ b/app/MindWork AI Studio/Plugins/languages/en-us-97dfb1ba-50c4-4440-8dfa-6575daf543c8/plugin.lua
@@ -4293,12 +4293,33 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SECRETINPUTFIELD::T1273315904"] = "Hide c
-- Show content
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SECRETINPUTFIELD::T2891011873"] = "Show content"
+-- The dropped folder could not be accessed. Please choose it with the folder chooser instead.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SELECTDIRECTORY::T1153417816"] = "The dropped folder could not be accessed. Please choose it with the folder chooser instead."
+
+-- Please drop a folder, not a file.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SELECTDIRECTORY::T3289690493"] = "Please drop a folder, not a file."
+
+-- You can also drag & drop the folder here.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SELECTDIRECTORY::T350096725"] = "You can also drag & drop the folder here."
+
-- Choose Directory
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SELECTDIRECTORY::T4256489763"] = "Choose Directory"
+-- Please drop a file, not a folder.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SELECTFILE::T1472251601"] = "Please drop a file, not a folder."
+
+-- You can also drag & drop the file here.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SELECTFILE::T1984243691"] = "You can also drag & drop the file here."
+
-- Choose File
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SELECTFILE::T4285779702"] = "Choose File"
+-- Please drop a file with a supported file type.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SELECTFILE::T930441004"] = "Please drop a file with a supported file type."
+
+-- The dropped file could not be accessed. Please choose it with the file chooser instead.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SELECTFILE::T984660028"] = "The dropped file could not be accessed. Please choose it with the file chooser instead."
+
-- External Assistants rated below this audit level are treated as insufficiently reviewed.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAGENTASSISTANTAUDIT::T1162151451"] = "External Assistants rated below this audit level are treated as insufficiently reviewed."