Skip to content

Commit 159b787

Browse files
Improved several small things (#602)
1 parent c0c439d commit 159b787

File tree

19 files changed

+226
-151
lines changed

19 files changed

+226
-151
lines changed

app/MindWork AI Studio/Assistants/I18N/allTexts.lua

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1513,6 +1513,12 @@ UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T4188329028"] = "No, kee
15131513
-- Export Chat to Microsoft Word
15141514
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T861873672"] = "Export Chat to Microsoft Word"
15151515

1516+
-- The local image file is too large (>10 MB). Skipping the image.
1517+
UI_TEXT_CONTENT["AISTUDIO::CHAT::IIMAGESOURCEEXTENSIONS::T3219823625"] = "The local image file is too large (>10 MB). Skipping the image."
1518+
1519+
-- The image at the URL is too large (>10 MB). Skipping the image.
1520+
UI_TEXT_CONTENT["AISTUDIO::CHAT::IIMAGESOURCEEXTENSIONS::T349928509"] = "The image at the URL is too large (>10 MB). Skipping the image."
1521+
15161522
-- Open Settings
15171523
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTBLOCK::T1172211894"] = "Open Settings"
15181524

@@ -6013,6 +6019,12 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::DATASOURCEVALIDATION::T878007824"]
60136019
-- Please enter the secret necessary for authentication.
60146020
UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::DATASOURCEVALIDATION::T968385876"] = "Please enter the secret necessary for authentication."
60156021

6022+
-- Unsupported image format
6023+
UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::FILEEXTENSIONVALIDATION::T1398282880"] = "Unsupported image format"
6024+
6025+
-- File has no extension
6026+
UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::FILEEXTENSIONVALIDATION::T1555980031"] = "File has no extension"
6027+
60166028
-- Audio files are not supported yet
60176029
UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::FILEEXTENSIONVALIDATION::T2919730669"] = "Audio files are not supported yet"
60186030

app/MindWork AI Studio/Chat/ContentImage.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System.Text.Json.Serialization;
22

33
using AIStudio.Provider;
4+
using AIStudio.Tools.Validation;
45

56
namespace AIStudio.Chat;
67

@@ -50,6 +51,23 @@ public Task<ChatThread> CreateFromProviderAsync(IProvider provider, Model chatMo
5051

5152
#endregion
5253

54+
/// <summary>
55+
/// Creates a ContentImage from a local file path.
56+
/// </summary>
57+
/// <param name="filePath">The path to the image file.</param>
58+
/// <returns>A new ContentImage instance if the file is valid, null otherwise.</returns>
59+
public static async Task<ContentImage?> CreateFromFileAsync(string filePath)
60+
{
61+
if (!await FileExtensionValidation.IsImageExtensionValidWithNotifyAsync(filePath))
62+
return null;
63+
64+
return new ContentImage
65+
{
66+
SourceType = ContentImageSource.LOCAL_PATH,
67+
Source = filePath,
68+
};
69+
}
70+
5371
/// <summary>
5472
/// The type of the image source.
5573
/// </summary>

app/MindWork AI Studio/Chat/IImageSourceExtensions.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1+
using AIStudio.Tools.PluginSystem;
2+
13
namespace AIStudio.Chat;
24

35
public static class IImageSourceExtensions
46
{
7+
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(IImageSourceExtensions).Namespace, nameof(IImageSourceExtensions));
8+
59
/// <summary>
610
/// Read the image content as a base64 string.
711
/// </summary>
@@ -33,8 +37,11 @@ public static async Task<string> AsBase64(this IImageSource image, CancellationT
3337
// Read the length of the content:
3438
var lengthBytes = response.Content.Headers.ContentLength;
3539
if(lengthBytes > 10_000_000)
40+
{
41+
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.ImageNotSupported, TB("The image at the URL is too large (>10 MB). Skipping the image.")));
3642
return string.Empty;
37-
43+
}
44+
3845
var bytes = await response.Content.ReadAsByteArrayAsync(token);
3946
return Convert.ToBase64String(bytes);
4047
}
@@ -48,8 +55,11 @@ public static async Task<string> AsBase64(this IImageSource image, CancellationT
4855
// Read the content length:
4956
var length = new FileInfo(image.Source).Length;
5057
if(length > 10_000_000)
58+
{
59+
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.ImageNotSupported, TB("The local image file is too large (>10 MB). Skipping the image.")));
5160
return string.Empty;
52-
61+
}
62+
5363
var bytes = await File.ReadAllBytesAsync(image.Source, token);
5464
return Convert.ToBase64String(bytes);
5565
}

app/MindWork AI Studio/Plugins/languages/de-de-43065dbc-78d0-45b7-92be-f14c2926e2dc/plugin.lua

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1515,6 +1515,12 @@ UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T4188329028"] = "Nein, b
15151515
-- Export Chat to Microsoft Word
15161516
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T861873672"] = "Chat in Microsoft Word exportieren"
15171517

1518+
-- The local image file is too large (>10 MB). Skipping the image.
1519+
UI_TEXT_CONTENT["AISTUDIO::CHAT::IIMAGESOURCEEXTENSIONS::T3219823625"] = "Die lokale Bilddatei ist zu groß (>10 MB). Das Bild wird übersprungen."
1520+
1521+
-- The image at the URL is too large (>10 MB). Skipping the image.
1522+
UI_TEXT_CONTENT["AISTUDIO::CHAT::IIMAGESOURCEEXTENSIONS::T349928509"] = "Das Bild unter der URL ist zu groß (>10 MB). Das Bild wird übersprungen."
1523+
15181524
-- Open Settings
15191525
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTBLOCK::T1172211894"] = "Einstellungen öffnen"
15201526

@@ -6015,6 +6021,12 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::DATASOURCEVALIDATION::T878007824"]
60156021
-- Please enter the secret necessary for authentication.
60166022
UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::DATASOURCEVALIDATION::T968385876"] = "Bitte geben Sie das für die Authentifizierung benötigte Geheimnis ein."
60176023

6024+
-- Unsupported image format
6025+
UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::FILEEXTENSIONVALIDATION::T1398282880"] = "Nicht unterstütztes Bildformat"
6026+
6027+
-- File has no extension
6028+
UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::FILEEXTENSIONVALIDATION::T1555980031"] = "Datei hat keine Erweiterung"
6029+
60186030
-- Audio files are not supported yet
60196031
UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::FILEEXTENSIONVALIDATION::T2919730669"] = "Audio-Dateien werden noch nicht unterstützt."
60206032

app/MindWork AI Studio/Plugins/languages/en-us-97dfb1ba-50c4-4440-8dfa-6575daf543c8/plugin.lua

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1515,6 +1515,12 @@ UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T4188329028"] = "No, kee
15151515
-- Export Chat to Microsoft Word
15161516
UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTBLOCKCOMPONENT::T861873672"] = "Export Chat to Microsoft Word"
15171517

1518+
-- The local image file is too large (>10 MB). Skipping the image.
1519+
UI_TEXT_CONTENT["AISTUDIO::CHAT::IIMAGESOURCEEXTENSIONS::T3219823625"] = "The local image file is too large (>10 MB). Skipping the image."
1520+
1521+
-- The image at the URL is too large (>10 MB). Skipping the image.
1522+
UI_TEXT_CONTENT["AISTUDIO::CHAT::IIMAGESOURCEEXTENSIONS::T349928509"] = "The image at the URL is too large (>10 MB). Skipping the image."
1523+
15181524
-- Open Settings
15191525
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTBLOCK::T1172211894"] = "Open Settings"
15201526

@@ -6015,6 +6021,12 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::DATASOURCEVALIDATION::T878007824"]
60156021
-- Please enter the secret necessary for authentication.
60166022
UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::DATASOURCEVALIDATION::T968385876"] = "Please enter the secret necessary for authentication."
60176023

6024+
-- Unsupported image format
6025+
UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::FILEEXTENSIONVALIDATION::T1398282880"] = "Unsupported image format"
6026+
6027+
-- File has no extension
6028+
UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::FILEEXTENSIONVALIDATION::T1555980031"] = "File has no extension"
6029+
60186030
-- Audio files are not supported yet
60196031
UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::FILEEXTENSIONVALIDATION::T2919730669"] = "Audio files are not supported yet"
60206032

app/MindWork AI Studio/Tools/Services/RustService.APIKeys.cs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
1-
using AIStudio.Tools.PluginSystem;
21
using AIStudio.Tools.Rust;
32

43
namespace AIStudio.Tools.Services;
54

65
public sealed partial class RustService
76
{
8-
private static string TB_APIKeys(string fallbackEN) => I18N.I.T(fallbackEN, typeof(RustService).Namespace, $"{nameof(RustService)}.APIKeys");
9-
107
/// <summary>
118
/// Try to get the API key for the given secret ID.
129
/// </summary>
@@ -15,8 +12,6 @@ public sealed partial class RustService
1512
/// <returns>The requested secret.</returns>
1613
public async Task<RequestedSecret> GetAPIKey(ISecretId secretId, bool isTrying = false)
1714
{
18-
static string TB(string fallbackEN) => TB_APIKeys(fallbackEN);
19-
2015
var secretRequest = new SelectSecretRequest($"provider::{secretId.SecretId}::{secretId.SecretName}::api_key", Environment.UserName, isTrying);
2116
var result = await this.http.PostAsJsonAsync("/secrets/get", secretRequest, this.jsonRustSerializerOptions);
2217
if (!result.IsSuccessStatusCode)
@@ -41,8 +36,6 @@ public async Task<RequestedSecret> GetAPIKey(ISecretId secretId, bool isTrying =
4136
/// <returns>The store secret response.</returns>
4237
public async Task<StoreSecretResponse> SetAPIKey(ISecretId secretId, string key)
4338
{
44-
static string TB(string fallbackEN) => TB_APIKeys(fallbackEN);
45-
4639
var encryptedKey = await this.encryptor!.Encrypt(key);
4740
var request = new StoreSecretRequest($"provider::{secretId.SecretId}::{secretId.SecretName}::api_key", Environment.UserName, encryptedKey);
4841
var result = await this.http.PostAsJsonAsync("/secrets/store", request, this.jsonRustSerializerOptions);
@@ -66,8 +59,6 @@ public async Task<StoreSecretResponse> SetAPIKey(ISecretId secretId, string key)
6659
/// <returns>The delete secret response.</returns>
6760
public async Task<DeleteSecretResponse> DeleteAPIKey(ISecretId secretId)
6861
{
69-
static string TB(string fallbackEN) => TB_APIKeys(fallbackEN);
70-
7162
var request = new SelectSecretRequest($"provider::{secretId.SecretId}::{secretId.SecretName}::api_key", Environment.UserName, false);
7263
var result = await this.http.PostAsJsonAsync("/secrets/delete", request, this.jsonRustSerializerOptions);
7364
if (!result.IsSuccessStatusCode)

app/MindWork AI Studio/Tools/Services/RustService.Clipboard.cs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,16 @@
1-
using AIStudio.Tools.PluginSystem;
21
using AIStudio.Tools.Rust;
32

43
namespace AIStudio.Tools.Services;
54

65
public sealed partial class RustService
76
{
8-
private static string TB_Clipboard(string fallbackEN) => I18N.I.T(fallbackEN, typeof(RustService).Namespace, $"{nameof(RustService)}.Clipboard");
9-
107
/// <summary>
118
/// Tries to copy the given text to the clipboard.
129
/// </summary>
1310
/// <param name="snackbar">The snackbar to show the result.</param>
1411
/// <param name="text">The text to copy to the clipboard.</param>
1512
public async Task CopyText2Clipboard(ISnackbar snackbar, string text)
1613
{
17-
static string TB(string fallbackEN) => TB_Clipboard(fallbackEN);
18-
1914
var message = TB("Successfully copied the text to your clipboard");
2015
var iconColor = Color.Error;
2116
var severity = Severity.Error;

app/MindWork AI Studio/Tools/Services/RustService.Secrets.cs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
1-
using AIStudio.Tools.PluginSystem;
21
using AIStudio.Tools.Rust;
32

43
namespace AIStudio.Tools.Services;
54

65
public sealed partial class RustService
76
{
8-
private static string TB_Secrets(string fallbackEN) => I18N.I.T(fallbackEN, typeof(RustService).Namespace, $"{nameof(RustService)}.Secrets");
9-
107
/// <summary>
118
/// Try to get the secret data for the given secret ID.
129
/// </summary>
@@ -15,8 +12,6 @@ public sealed partial class RustService
1512
/// <returns>The requested secret.</returns>
1613
public async Task<RequestedSecret> GetSecret(ISecretId secretId, bool isTrying = false)
1714
{
18-
static string TB(string fallbackEN) => TB_Secrets(fallbackEN);
19-
2015
var secretRequest = new SelectSecretRequest($"secret::{secretId.SecretId}::{secretId.SecretName}", Environment.UserName, isTrying);
2116
var result = await this.http.PostAsJsonAsync("/secrets/get", secretRequest, this.jsonRustSerializerOptions);
2217
if (!result.IsSuccessStatusCode)
@@ -41,8 +36,6 @@ public async Task<RequestedSecret> GetSecret(ISecretId secretId, bool isTrying =
4136
/// <returns>The store secret response.</returns>
4237
public async Task<StoreSecretResponse> SetSecret(ISecretId secretId, string secretData)
4338
{
44-
static string TB(string fallbackEN) => TB_Secrets(fallbackEN);
45-
4639
var encryptedSecret = await this.encryptor!.Encrypt(secretData);
4740
var request = new StoreSecretRequest($"secret::{secretId.SecretId}::{secretId.SecretName}", Environment.UserName, encryptedSecret);
4841
var result = await this.http.PostAsJsonAsync("/secrets/store", request, this.jsonRustSerializerOptions);
@@ -66,8 +59,6 @@ public async Task<StoreSecretResponse> SetSecret(ISecretId secretId, string secr
6659
/// <returns>The delete secret response.</returns>
6760
public async Task<DeleteSecretResponse> DeleteSecret(ISecretId secretId)
6861
{
69-
static string TB(string fallbackEN) => TB_Secrets(fallbackEN);
70-
7162
var request = new SelectSecretRequest($"secret::{secretId.SecretId}::{secretId.SecretName}", Environment.UserName, false);
7263
var result = await this.http.PostAsJsonAsync("/secrets/delete", request, this.jsonRustSerializerOptions);
7364
if (!result.IsSuccessStatusCode)

app/MindWork AI Studio/Tools/Services/RustService.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Text.Json;
33

44
using AIStudio.Settings;
5+
using AIStudio.Tools.PluginSystem;
56

67
using Version = System.Version;
78

@@ -14,6 +15,8 @@ namespace AIStudio.Tools.Services;
1415
/// </summary>
1516
public sealed partial class RustService : BackgroundService
1617
{
18+
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(RustService).Namespace, nameof(RustService));
19+
1720
private readonly HttpClient http;
1821

1922
private readonly JsonSerializerOptions jsonRustSerializerOptions = new()

app/MindWork AI Studio/Tools/Validation/FileExtensionValidation.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,31 @@ await MessageBus.INSTANCE.SendWarning(new(
5252

5353
return true;
5454
}
55+
56+
/// <summary>
57+
/// Validates that the file is a supported image format and sends appropriate MessageBus notifications when invalid.
58+
/// </summary>
59+
/// <param name="filePath">The file path to validate.</param>
60+
/// <returns>True if valid image, false if invalid (error already sent via MessageBus).</returns>
61+
public static async Task<bool> IsImageExtensionValidWithNotifyAsync(string filePath)
62+
{
63+
var ext = Path.GetExtension(filePath).TrimStart('.');
64+
if (string.IsNullOrWhiteSpace(ext))
65+
{
66+
await MessageBus.INSTANCE.SendError(new(
67+
Icons.Material.Filled.ImageNotSupported,
68+
TB("File has no extension")));
69+
return false;
70+
}
71+
72+
if (!Array.Exists(FileTypeFilter.AllImages.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase)))
73+
{
74+
await MessageBus.INSTANCE.SendError(new(
75+
Icons.Material.Filled.ImageNotSupported,
76+
TB("Unsupported image format")));
77+
return false;
78+
}
79+
80+
return true;
81+
}
5582
}

0 commit comments

Comments
 (0)