diff --git a/app/MindWork AI Studio/Assistants/DocumentAnalysis/DocumentAnalysisAssistant.razor b/app/MindWork AI Studio/Assistants/DocumentAnalysis/DocumentAnalysisAssistant.razor
index ec801e84..64d549a9 100644
--- a/app/MindWork AI Studio/Assistants/DocumentAnalysis/DocumentAnalysisAssistant.razor
+++ b/app/MindWork AI Studio/Assistants/DocumentAnalysis/DocumentAnalysisAssistant.razor
@@ -103,7 +103,7 @@ else
@T("Documents for the analysis")
-
+
diff --git a/app/MindWork AI Studio/Assistants/I18N/allTexts.lua b/app/MindWork AI Studio/Assistants/I18N/allTexts.lua
index a535e468..a7fcb7ad 100644
--- a/app/MindWork AI Studio/Assistants/I18N/allTexts.lua
+++ b/app/MindWork AI Studio/Assistants/I18N/allTexts.lua
@@ -2443,6 +2443,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2147062613"] = "Profile
-- Add messages of an example conversation (user prompt followed by assistant prompt) to demonstrate the desired interaction pattern. These examples help the AI understand your expectations by showing it the correct format, style, and content of responses before it receives actual user inputs.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2292424657"] = "Add messages of an example conversation (user prompt followed by assistant prompt) to demonstrate the desired interaction pattern. These examples help the AI understand your expectations by showing it the correct format, style, and content of responses before it receives actual user inputs."
+-- File Attachments
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2294745309"] = "File Attachments"
+
-- Role
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2418769465"] = "Role"
@@ -2482,6 +2485,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3016903701"] = "The nam
-- Image content
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3094908719"] = "Image content"
+-- You can attach files that will be automatically included when using this chat template. These files will be added to the first message sent in any chat using this template.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3108503534"] = "You can attach files that will be automatically included when using this chat template. These files will be added to the first message sent in any chat using this template."
+
-- Are you unsure which system prompt to use? You might start with the default system prompt that AI Studio uses for all chats.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3127437308"] = "Are you unsure which system prompt to use? You might start with the default system prompt that AI Studio uses for all chats."
@@ -5194,6 +5200,9 @@ UI_TEXT_CONTENT["AISTUDIO::PROVIDER::LLMPROVIDERSEXTENSIONS::T3424652889"] = "Un
-- no model selected
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::MODEL::T2234274832"] = "no model selected"
+-- Use no chat template
+UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CHATTEMPLATE::T4258819635"] = "Use no chat template"
+
-- Navigation never expands, but there are tooltips
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T1095779033"] = "Navigation never expands, but there are tooltips"
diff --git a/app/MindWork AI Studio/Components/AttachDocuments.razor.cs b/app/MindWork AI Studio/Components/AttachDocuments.razor.cs
index 9bc48747..acfc0dd2 100644
--- a/app/MindWork AI Studio/Components/AttachDocuments.razor.cs
+++ b/app/MindWork AI Studio/Components/AttachDocuments.razor.cs
@@ -17,6 +17,18 @@ public partial class AttachDocuments : MSGComponentBase
[Parameter]
public string Name { get; set; } = string.Empty;
+
+ ///
+ /// On which layer to register the drop area. Higher layers have priority over lower layers.
+ ///
+ [Parameter]
+ public int Layer { get; set; }
+
+ ///
+ /// When true, pause catching dropped files. Default is false.
+ ///
+ [Parameter]
+ public bool PauseCatchingDrops { get; set; }
[Parameter]
public HashSet DocumentPaths { get; set; } = [];
@@ -36,6 +48,15 @@ public partial class AttachDocuments : MSGComponentBase
[Parameter]
public bool UseSmallForm { get; set; }
+ ///
+ /// When true, validate media file types before attaching. Default is true. That means that
+ /// the user cannot attach unsupported media file types when the provider or model does not
+ /// support them. Set it to false in order to disable this validation. This is useful for places
+ /// where the user might want to prepare a template.
+ ///
+ [Parameter]
+ public bool ValidateMediaFileTypes { get; set; } = true;
+
[Parameter]
public AIStudio.Settings.Provider? Provider { get; set; }
@@ -54,6 +75,7 @@ public partial class AttachDocuments : MSGComponentBase
private const Placement TOOLBAR_TOOLTIP_PLACEMENT = Placement.Top;
private static readonly string DROP_FILES_HERE_TEXT = TB("Drop files here to attach them.");
+ private uint numDropAreasAboveThis;
private bool isComponentHovered;
private bool isDraggingOver;
@@ -61,7 +83,10 @@ public partial class AttachDocuments : MSGComponentBase
protected override async Task OnInitializedAsync()
{
- this.ApplyFilters([], [ Event.TAURI_EVENT_RECEIVED ]);
+ this.ApplyFilters([], [ Event.TAURI_EVENT_RECEIVED, Event.REGISTER_FILE_DROP_AREA, Event.UNREGISTER_FILE_DROP_AREA ]);
+
+ // Register this drop area:
+ await this.MessageBus.SendMessage(this, Event.REGISTER_FILE_DROP_AREA, this.Layer);
await base.OnInitializedAsync();
}
@@ -69,7 +94,35 @@ public partial class AttachDocuments : MSGComponentBase
{
switch (triggeredEvent)
{
+ case Event.REGISTER_FILE_DROP_AREA when sendingComponent != this:
+ {
+ if(data is int layer && layer > this.Layer)
+ {
+ this.numDropAreasAboveThis++;
+ this.PauseCatchingDrops = true;
+ }
+
+ break;
+ }
+
+ case Event.UNREGISTER_FILE_DROP_AREA when sendingComponent != this:
+ {
+ if(data is int layer && layer > this.Layer)
+ {
+ if(this.numDropAreasAboveThis > 0)
+ this.numDropAreasAboveThis--;
+
+ if(this.numDropAreasAboveThis is 0)
+ this.PauseCatchingDrops = false;
+ }
+
+ break;
+ }
+
case Event.TAURI_EVENT_RECEIVED when data is TauriEvent { EventType: TauriEventType.FILE_DROP_HOVERED }:
+ if(this.PauseCatchingDrops)
+ return;
+
if(!this.isComponentHovered && !this.CatchAllDocuments)
{
this.Logger.LogDebug("Attach documents component '{Name}' is not hovered, ignoring file drop hovered event.", this.Name);
@@ -82,11 +135,17 @@ public partial class AttachDocuments : MSGComponentBase
break;
case Event.TAURI_EVENT_RECEIVED when data is TauriEvent { EventType: TauriEventType.FILE_DROP_CANCELED }:
+ if(this.PauseCatchingDrops)
+ return;
+
this.isDraggingOver = false;
this.StateHasChanged();
break;
case Event.TAURI_EVENT_RECEIVED when data is TauriEvent { EventType: TauriEventType.WINDOW_NOT_FOCUSED }:
+ if(this.PauseCatchingDrops)
+ return;
+
this.isDraggingOver = false;
this.isComponentHovered = false;
this.ClearDragClass();
@@ -94,6 +153,9 @@ public partial class AttachDocuments : MSGComponentBase
break;
case Event.TAURI_EVENT_RECEIVED when data is TauriEvent { EventType: TauriEventType.FILE_DROP_DROPPED, Payload: var paths }:
+ if(this.PauseCatchingDrops)
+ return;
+
if(!this.isComponentHovered && !this.CatchAllDocuments)
{
this.Logger.LogDebug("Attach documents component '{Name}' is not hovered, ignoring file drop dropped event.", this.Name);
@@ -117,7 +179,7 @@ public partial class AttachDocuments : MSGComponentBase
foreach (var path in paths)
{
- if(!await FileExtensionValidation.IsExtensionValidWithNotifyAsync(FileExtensionValidation.UseCase.ATTACHING_CONTENT, path, this.Provider))
+ if(!await FileExtensionValidation.IsExtensionValidWithNotifyAsync(FileExtensionValidation.UseCase.ATTACHING_CONTENT, path, this.ValidateMediaFileTypes, this.Provider))
continue;
this.DocumentPaths.Add(FileAttachment.FromPath(path));
@@ -161,7 +223,7 @@ public partial class AttachDocuments : MSGComponentBase
if (!File.Exists(selectedFilePath))
continue;
- if (!await FileExtensionValidation.IsExtensionValidWithNotifyAsync(FileExtensionValidation.UseCase.ATTACHING_CONTENT, selectedFilePath, this.Provider))
+ if (!await FileExtensionValidation.IsExtensionValidWithNotifyAsync(FileExtensionValidation.UseCase.ATTACHING_CONTENT, selectedFilePath, this.ValidateMediaFileTypes, this.Provider))
continue;
this.DocumentPaths.Add(FileAttachment.FromPath(selectedFilePath));
@@ -189,6 +251,9 @@ public partial class AttachDocuments : MSGComponentBase
private void OnMouseEnter(EventArgs _)
{
+ if(this.PauseCatchingDrops)
+ return;
+
this.Logger.LogDebug("Attach documents component '{Name}' is hovered.", this.Name);
this.isComponentHovered = true;
this.SetDragClass();
@@ -197,6 +262,9 @@ public partial class AttachDocuments : MSGComponentBase
private void OnMouseLeave(EventArgs _)
{
+ if(this.PauseCatchingDrops)
+ return;
+
this.Logger.LogDebug("Attach documents component '{Name}' is no longer hovered.", this.Name);
this.isComponentHovered = false;
this.ClearDragClass();
diff --git a/app/MindWork AI Studio/Components/ChatComponent.razor b/app/MindWork AI Studio/Components/ChatComponent.razor
index 6396ba9a..9d8f0072 100644
--- a/app/MindWork AI Studio/Components/ChatComponent.razor
+++ b/app/MindWork AI Studio/Components/ChatComponent.razor
@@ -82,7 +82,7 @@
}
-
+
@if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is WorkspaceStorageBehavior.STORE_CHATS_AUTOMATICALLY)
{
diff --git a/app/MindWork AI Studio/Components/ChatComponent.razor.cs b/app/MindWork AI Studio/Components/ChatComponent.razor.cs
index 3236d31e..f6110e41 100644
--- a/app/MindWork AI Studio/Components/ChatComponent.razor.cs
+++ b/app/MindWork AI Studio/Components/ChatComponent.razor.cs
@@ -79,7 +79,11 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
// Get the preselected chat template:
this.currentChatTemplate = this.SettingsManager.GetPreselectedChatTemplate(Tools.Components.CHAT);
this.userInput = this.currentChatTemplate.PredefinedUserPrompt;
-
+
+ // Apply template's file attachments, if any:
+ foreach (var attachment in this.currentChatTemplate.FileAttachments)
+ this.chatDocumentPaths.Add(attachment);
+
//
// Check for deferred messages of the kind 'SEND_TO_CHAT',
// aka the user sends an assistant result to the chat:
@@ -328,7 +332,12 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
this.currentChatTemplate = chatTemplate;
if(!string.IsNullOrWhiteSpace(this.currentChatTemplate.PredefinedUserPrompt))
this.userInput = this.currentChatTemplate.PredefinedUserPrompt;
-
+
+ // Apply template's file attachments (replaces existing):
+ this.chatDocumentPaths.Clear();
+ foreach (var attachment in this.currentChatTemplate.FileAttachments)
+ this.chatDocumentPaths.Add(attachment);
+
if(this.ChatThread is null)
return;
@@ -677,9 +686,14 @@ public partial class ChatComponent : MSGComponentBase, IAsyncDisposable
Blocks = this.currentChatTemplate == ChatTemplate.NO_CHAT_TEMPLATE ? [] : this.currentChatTemplate.ExampleConversation.Select(x => x.DeepClone()).ToList(),
};
}
-
+
this.userInput = this.currentChatTemplate.PredefinedUserPrompt;
-
+
+ // Apply template's file attachments:
+ this.chatDocumentPaths.Clear();
+ foreach (var attachment in this.currentChatTemplate.FileAttachments)
+ this.chatDocumentPaths.Add(attachment);
+
// Now, we have to reset the data source options as well:
this.ApplyStandardDataSourceOptions();
diff --git a/app/MindWork AI Studio/Dialogs/ChatTemplateDialog.razor b/app/MindWork AI Studio/Dialogs/ChatTemplateDialog.razor
index 0de3cd9d..0a72ef07 100644
--- a/app/MindWork AI Studio/Dialogs/ChatTemplateDialog.razor
+++ b/app/MindWork AI Studio/Dialogs/ChatTemplateDialog.razor
@@ -77,7 +77,22 @@
UserAttributes="@SPELLCHECK_ATTRIBUTES"
HelperText="@T("Tell the AI your predefined user input.")"
/>
-
+
+
+ @T("File Attachments")
+
+
+ @T("You can attach files that will be automatically included when using this chat template. These files will be added to the first message sent in any chat using this template.")
+
+
+
@T("Profile Usage")
diff --git a/app/MindWork AI Studio/Dialogs/ChatTemplateDialog.razor.cs b/app/MindWork AI Studio/Dialogs/ChatTemplateDialog.razor.cs
index 3f9378ef..0aa16ddf 100644
--- a/app/MindWork AI Studio/Dialogs/ChatTemplateDialog.razor.cs
+++ b/app/MindWork AI Studio/Dialogs/ChatTemplateDialog.razor.cs
@@ -50,7 +50,10 @@ public partial class ChatTemplateDialog : MSGComponentBase
[Parameter]
public IReadOnlyCollection ExampleConversation { get; init; } = [];
- [Parameter]
+ [Parameter]
+ public IReadOnlyCollection FileAttachments { get; init; } = [];
+
+ [Parameter]
public bool AllowProfileUsage { get; set; } = true;
[Parameter]
@@ -71,6 +74,7 @@ public partial class ChatTemplateDialog : MSGComponentBase
private bool dataIsValid;
private List dataExampleConversation = [];
+ private HashSet fileAttachments = [];
private string[] dataIssues = [];
private string dataEditingPreviousName = string.Empty;
private bool isInlineEditOnGoing;
@@ -95,6 +99,7 @@ public partial class ChatTemplateDialog : MSGComponentBase
{
this.dataEditingPreviousName = this.DataName.ToLowerInvariant();
this.dataExampleConversation = this.ExampleConversation.Select(n => n.DeepClone()).ToList();
+ this.fileAttachments = [..this.FileAttachments];
}
if (this.CreateFromExistingChatThread && this.ExistingChatThread is not null)
@@ -128,6 +133,7 @@ public partial class ChatTemplateDialog : MSGComponentBase
SystemPrompt = this.DataSystemPrompt,
PredefinedUserPrompt = this.PredefinedUserPrompt,
ExampleConversation = this.dataExampleConversation,
+ FileAttachments = [..this.fileAttachments],
AllowProfileUsage = this.AllowProfileUsage,
EnterpriseConfigurationPluginId = Guid.Empty,
diff --git a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogChatTemplate.razor b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogChatTemplate.razor
index bb1fb816..060dc0ee 100644
--- a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogChatTemplate.razor
+++ b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogChatTemplate.razor
@@ -41,10 +41,10 @@
{
-
+
-
+
}
diff --git a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogChatTemplate.razor.cs b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogChatTemplate.razor.cs
index 2708ab4f..579fff22 100644
--- a/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogChatTemplate.razor.cs
+++ b/app/MindWork AI Studio/Dialogs/Settings/SettingsDialogChatTemplate.razor.cs
@@ -65,6 +65,7 @@ public partial class SettingsDialogChatTemplate : SettingsDialogBase
{ x => x.PredefinedUserPrompt, chatTemplate.PredefinedUserPrompt },
{ x => x.IsEditing, true },
{ x => x.ExampleConversation, chatTemplate.ExampleConversation },
+ { x => x.FileAttachments, chatTemplate.FileAttachments },
{ x => x.AllowProfileUsage, chatTemplate.AllowProfileUsage },
};
diff --git a/app/MindWork AI Studio/Plugins/configuration/plugin.lua b/app/MindWork AI Studio/Plugins/configuration/plugin.lua
index c4e5f08c..abecda7c 100644
--- a/app/MindWork AI Studio/Plugins/configuration/plugin.lua
+++ b/app/MindWork AI Studio/Plugins/configuration/plugin.lua
@@ -125,6 +125,33 @@ CONFIG["CHAT_TEMPLATES"][#CONFIG["CHAT_TEMPLATES"]+1] = {
}
}
+-- An example chat template with file attachments:
+-- This template automatically attaches specified files when the user selects it.
+CONFIG["CHAT_TEMPLATES"][#CONFIG["CHAT_TEMPLATES"]+1] = {
+ ["Id"] = "00000000-0000-0000-0000-000000000001",
+ ["Name"] = "Document Analysis Template",
+ ["SystemPrompt"] = "You are an expert document analyst. Please analyze the attached documents and provide insights.",
+ ["PredefinedUserPrompt"] = "Please analyze the attached company guidelines and summarize the key points.",
+ ["AllowProfileUsage"] = true,
+ -- Optional: Pre-attach files that will be automatically included when using this template.
+ -- These files will be loaded when the user selects this chat template.
+ -- Note: File paths must be absolute paths and accessible to all users.
+ ["FileAttachments"] = {
+ "G:\\Company\\Documents\\Guidelines.pdf",
+ "G:\\Company\\Documents\\CompanyPolicies.docx"
+ },
+ ["ExampleConversation"] = {
+ {
+ ["Role"] = "USER",
+ ["Content"] = "I have attached the company documents for analysis."
+ },
+ {
+ ["Role"] = "AI",
+ ["Content"] = "Thank you. I'll analyze the documents and provide a comprehensive summary."
+ }
+ }
+}
+
-- Profiles for this configuration:
CONFIG["PROFILES"] = {}
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 95471d53..69e621cc 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
@@ -2445,6 +2445,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2147062613"] = "Profiln
-- Add messages of an example conversation (user prompt followed by assistant prompt) to demonstrate the desired interaction pattern. These examples help the AI understand your expectations by showing it the correct format, style, and content of responses before it receives actual user inputs.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2292424657"] = "Fügen Sie Nachrichten einer Beispiel-Konversation hinzu (Nutzereingabe, gefolgt von einer Antwort des Assistenten), um das gewünschte Interaktionsmuster zu demonstrieren. Diese Beispiele helfen der KI, ihre Erwartungen zu verstehen, indem Sie das korrekte Format, den Stil und den Inhalt von Antworten zeigen, bevor tatsächliche Nutzereingaben erfolgen."
+-- File Attachments
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2294745309"] = "Dateianhänge"
+
-- Role
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2418769465"] = "Rolle"
@@ -2484,6 +2487,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3016903701"] = "Der Nam
-- Image content
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3094908719"] = "Bildinhalt"
+-- You can attach files that will be automatically included when using this chat template. These files will be added to the first message sent in any chat using this template.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3108503534"] = "Sie können Dateien anhängen, die bei Verwendung dieser Chat-Vorlage automatisch einbezogen werden. Diese Dateien werden der ersten Nachricht hinzugefügt, die in einem Chat gesendet wird, der diese Vorlage verwendet."
+
-- Are you unsure which system prompt to use? You might start with the default system prompt that AI Studio uses for all chats.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3127437308"] = "Sind Sie unsicher, welchen System-Prompt Sie verwenden sollen? Sie können mit dem Standard-System-Prompt beginnen, den AI Studio für alle Chats verwendet."
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 85edaf99..6d395b91 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
@@ -2445,6 +2445,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2147062613"] = "Profile
-- Add messages of an example conversation (user prompt followed by assistant prompt) to demonstrate the desired interaction pattern. These examples help the AI understand your expectations by showing it the correct format, style, and content of responses before it receives actual user inputs.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2292424657"] = "Add messages of an example conversation (user prompt followed by assistant prompt) to demonstrate the desired interaction pattern. These examples help the AI understand your expectations by showing it the correct format, style, and content of responses before it receives actual user inputs."
+-- File Attachments
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2294745309"] = "File Attachments"
+
-- Role
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T2418769465"] = "Role"
@@ -2484,6 +2487,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3016903701"] = "The nam
-- Image content
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3094908719"] = "Image content"
+-- You can attach files that will be automatically included when using this chat template. These files will be added to the first message sent in any chat using this template.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3108503534"] = "You can attach files that will be automatically included when using this chat template. These files will be added to the first message sent in any chat using this template."
+
-- Are you unsure which system prompt to use? You might start with the default system prompt that AI Studio uses for all chats.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T3127437308"] = "Are you unsure which system prompt to use? You might start with the default system prompt that AI Studio uses for all chats."
diff --git a/app/MindWork AI Studio/Settings/ChatTemplate.cs b/app/MindWork AI Studio/Settings/ChatTemplate.cs
index 54d37ce3..5f3c597a 100644
--- a/app/MindWork AI Studio/Settings/ChatTemplate.cs
+++ b/app/MindWork AI Studio/Settings/ChatTemplate.cs
@@ -12,13 +12,12 @@ public record ChatTemplate(
string SystemPrompt,
string PredefinedUserPrompt,
List ExampleConversation,
+ List FileAttachments,
bool AllowProfileUsage,
bool IsEnterpriseConfiguration = false,
Guid EnterpriseConfigurationPluginId = default) : ConfigurationBaseObject
{
- private const string USE_NO_CHAT_TEMPLATE_TEXT = "Use no chat template";
-
- public ChatTemplate() : this(0, Guid.Empty.ToString(), string.Empty, string.Empty, string.Empty, [], false)
+ public ChatTemplate() : this(0, Guid.Empty.ToString(), string.Empty, string.Empty, string.Empty, [], [], false)
{
}
@@ -28,12 +27,13 @@ public record ChatTemplate(
public static readonly ChatTemplate NO_CHAT_TEMPLATE = new()
{
- Name = TB(USE_NO_CHAT_TEMPLATE_TEXT), // Cannot be localized due to being a static readonly field
+ Name = TB("Use no chat template"), // Cannot be localized due to being a static readonly field
SystemPrompt = string.Empty,
PredefinedUserPrompt = string.Empty,
Id = Guid.Empty.ToString(),
Num = uint.MaxValue,
ExampleConversation = [],
+ FileAttachments = [],
AllowProfileUsage = true,
EnterpriseConfigurationPluginId = Guid.Empty,
IsEnterpriseConfiguration = false,
@@ -61,7 +61,7 @@ public record ChatTemplate(
public string GetSafeName()
{
if(this == NO_CHAT_TEMPLATE)
- return TB(USE_NO_CHAT_TEMPLATE_TEXT);
+ return TB("Use no chat template");
return this.Name;
}
@@ -102,7 +102,9 @@ public record ChatTemplate(
var allowProfileUsage = false;
if (table.TryGetValue("AllowProfileUsage", out var allowProfileValue) && allowProfileValue.TryRead(out var allow))
allowProfileUsage = allow;
-
+
+ var fileAttachments = ParseFileAttachments(idx, table);
+
template = new ChatTemplate
{
Num = 0,
@@ -111,6 +113,7 @@ public record ChatTemplate(
SystemPrompt = systemPrompt,
PredefinedUserPrompt = predefinedUserPrompt,
ExampleConversation = ParseExampleConversation(idx, table),
+ FileAttachments = fileAttachments,
AllowProfileUsage = allowProfileUsage,
IsEnterpriseConfiguration = true,
EnterpriseConfigurationPluginId = configPluginId,
@@ -165,4 +168,26 @@ public record ChatTemplate(
return exampleConversation;
}
+
+ private static List ParseFileAttachments(int idx, LuaTable table)
+ {
+ var fileAttachments = new List();
+ if (!table.TryGetValue("FileAttachments", out var fileAttValue) || !fileAttValue.TryRead(out var fileAttTable))
+ return fileAttachments;
+
+ var numAttachments = fileAttTable.ArrayLength;
+ for (var attachmentNum = 1; attachmentNum <= numAttachments; attachmentNum++)
+ {
+ var attachmentValue = fileAttTable[attachmentNum];
+ if (!attachmentValue.TryRead(out var filePath))
+ {
+ LOGGER.LogWarning("The FileAttachments entry {AttachmentNum} in chat template {IdxChatTemplate} is not a valid string.", attachmentNum, idx);
+ continue;
+ }
+
+ fileAttachments.Add(FileAttachment.FromPath(filePath));
+ }
+
+ return fileAttachments;
+ }
}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Tools/DropLayers.cs b/app/MindWork AI Studio/Tools/DropLayers.cs
new file mode 100644
index 00000000..8f1a370b
--- /dev/null
+++ b/app/MindWork AI Studio/Tools/DropLayers.cs
@@ -0,0 +1,11 @@
+namespace AIStudio.Tools;
+
+public static class DropLayers
+{
+ public const int ROOT = 0;
+
+ public const int PAGES = 10;
+ public const int ASSISTANTS = 20;
+
+ public const int DIALOGS = 100;
+}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Tools/Event.cs b/app/MindWork AI Studio/Tools/Event.cs
index 9f612f04..0590c5c7 100644
--- a/app/MindWork AI Studio/Tools/Event.cs
+++ b/app/MindWork AI Studio/Tools/Event.cs
@@ -34,6 +34,10 @@ public enum Event
// RAG events:
RAG_AUTO_DATA_SOURCES_SELECTED,
+ // File attachment events:
+ REGISTER_FILE_DROP_AREA,
+ UNREGISTER_FILE_DROP_AREA,
+
// Send events:
SEND_TO_GRAMMAR_SPELLING_ASSISTANT,
SEND_TO_ICON_FINDER_ASSISTANT,
diff --git a/app/MindWork AI Studio/Tools/Validation/FileExtensionValidation.cs b/app/MindWork AI Studio/Tools/Validation/FileExtensionValidation.cs
index 991dcbdf..02a978d1 100644
--- a/app/MindWork AI Studio/Tools/Validation/FileExtensionValidation.cs
+++ b/app/MindWork AI Studio/Tools/Validation/FileExtensionValidation.cs
@@ -32,15 +32,16 @@ public static class FileExtensionValidation
///
ATTACHING_CONTENT,
}
-
+
///
/// Validates the file extension and sends appropriate MessageBus notifications when invalid.
///
/// The validation use case.
/// The file path to validate.
+ /// Whether to validate media file types against provider capabilities.
/// The selected provider.
/// True if valid, false if invalid (error/warning already sent via MessageBus).
- public static async Task IsExtensionValidWithNotifyAsync(UseCase useCae, string filePath, Settings.Provider? provider = null)
+ public static async Task IsExtensionValidWithNotifyAsync(UseCase useCae, string filePath, bool validateMediaFileTypes = true, Settings.Provider? provider = null)
{
var ext = Path.GetExtension(filePath).TrimStart('.').ToLowerInvariant();
if(FileTypeFilter.Executables.FilterExtensions.Contains(ext))
@@ -63,6 +64,10 @@ public static class FileExtensionValidation
TB("Images are not supported at this place")));
return false;
+ // In this use case, we don't validate the provider capabilities:
+ case UseCase.ATTACHING_CONTENT when !validateMediaFileTypes:
+ return true;
+
// In this use case, we can check the provider capabilities:
case UseCase.ATTACHING_CONTENT when capabilities.Contains(Capability.SINGLE_IMAGE_INPUT) ||
capabilities.Contains(Capability.MULTIPLE_IMAGE_INPUT):
diff --git a/app/MindWork AI Studio/wwwroot/changelog/v0.10.1.md b/app/MindWork AI Studio/wwwroot/changelog/v0.10.1.md
deleted file mode 100644
index 28d6d1bd..00000000
--- a/app/MindWork AI Studio/wwwroot/changelog/v0.10.1.md
+++ /dev/null
@@ -1 +0,0 @@
-# v0.10.1, build 231 (2026-01-xx xx:xx UTC)
diff --git a/app/MindWork AI Studio/wwwroot/changelog/v26.1.1.md b/app/MindWork AI Studio/wwwroot/changelog/v26.1.1.md
new file mode 100644
index 00000000..6c2f797c
--- /dev/null
+++ b/app/MindWork AI Studio/wwwroot/changelog/v26.1.1.md
@@ -0,0 +1,3 @@
+# v26.1.1, build 231 (2026-01-xx xx:xx UTC)
+- Added the option to attach files, including images, to chat templates. You can also define templates with file attachments through a configuration plugin. These file attachments aren’t copied—they’re re-read every time. That means the AI will pick up any updates you make to those files.
+- Improved the app versioning. Starting in 2026, each version number includes the year, followed by the month. The last digit shows the release number for that month. For example, version `26.1.1` is the first release in January 2026.
\ No newline at end of file