2025-12-30 18:30:32 +01:00
using AIStudio.Settings ;
2025-12-15 13:26:55 +01:00
using AIStudio.Tools.PluginSystem ;
using AIStudio.Tools.Rust ;
namespace AIStudio.Tools.Validation ;
/// <summary>
/// Provides centralized validation for file extensions.
/// </summary>
public static class FileExtensionValidation
{
private static string TB ( string fallbackEN ) => I18N . I . T ( fallbackEN , typeof ( FileExtensionValidation ). Namespace , nameof ( FileExtensionValidation ));
2025-12-30 18:30:32 +01:00
/// <summary>
/// Defines the use cases for file extension validation.
/// </summary>
public enum UseCase
{
/// <summary>
/// No specific use case; general validation.
/// </summary>
NONE ,
/// <summary>
/// Validating for directly loading content into the UI. In this state, there might be no provider selected yet.
/// </summary>
DIRECTLY_LOADING_CONTENT ,
/// <summary>
/// Validating for attaching content to a message or prompt.
/// </summary>
ATTACHING_CONTENT ,
}
2026-01-01 16:47:15 +01:00
2025-12-15 13:26:55 +01:00
/// <summary>
/// Validates the file extension and sends appropriate MessageBus notifications when invalid.
/// </summary>
2025-12-30 18:30:32 +01:00
/// <param name="useCae">The validation use case.</param>
2025-12-15 13:26:55 +01:00
/// <param name="filePath">The file path to validate.</param>
2026-01-01 16:47:15 +01:00
/// <param name="validateMediaFileTypes">Whether to validate media file types against provider capabilities.</param>
2025-12-30 18:30:32 +01:00
/// <param name="provider">The selected provider.</param>
2025-12-15 13:26:55 +01:00
/// <returns>True if valid, false if invalid (error/warning already sent via MessageBus).</returns>
2026-01-01 16:47:15 +01:00
public static async Task < bool > IsExtensionValidWithNotifyAsync ( UseCase useCae , string filePath , bool validateMediaFileTypes = true , Settings . Provider ? provider = null )
2025-12-15 13:26:55 +01:00
{
2026-05-22 16:03:34 +02:00
if ( string . Equals ( Path . GetExtension ( filePath ), ".doc" , StringComparison . OrdinalIgnoreCase ))
{
await MessageBus . INSTANCE . SendWarning ( new (
Icons . Material . Filled . Description ,
TB ( "This file format is not supported. Please convert the .doc file to .docx (e.g. with Microsoft Word)." )));
return false ;
}
2026-04-09 11:53:30 +02:00
if ( FileTypes . IsAllowedPath ( filePath , FileTypes . EXECUTABLES ))
2025-12-15 13:26:55 +01:00
{
await MessageBus . INSTANCE . SendError ( new (
Icons . Material . Filled . AppBlocking ,
TB ( "Executables are not allowed" )));
return false ;
}
2026-04-09 11:53:30 +02:00
if ( FileTypes . IsAllowedPath ( filePath , FileTypes . IMAGE ))
2025-12-15 13:26:55 +01:00
{
2025-12-30 18:30:32 +01:00
switch ( useCae )
{
// In this use case, we cannot guarantee that a provider is selected yet:
case UseCase . DIRECTLY_LOADING_CONTENT :
await MessageBus . INSTANCE . SendWarning ( new (
Icons . Material . Filled . ImageNotSupported ,
TB ( "Images are not supported at this place" )));
return false ;
2026-10-22 10:57:04 +02:00
//
// Whether AI Studio can prepare an image for a model does not depend on the
// provider. This holds for templates as well, therefore: a HEIC image attached
// there could never be sent.
//
case UseCase . ATTACHING_CONTENT when ! FileTypes . IsAllowedPath ( filePath , FileTypes . MODEL_IMAGE ):
await MessageBus . INSTANCE . SendWarning ( new (
Icons . Material . Filled . ImageNotSupported ,
string . Format ( TB ( "AI Studio cannot give {0} images to an AI model. Please convert the image to JPG or PNG." ), Path . GetExtension ( filePath ). TrimStart ( '.' ). ToUpperInvariant ())));
return false ;
2026-01-01 16:47:15 +01:00
// In this use case, we don't validate the provider capabilities:
case UseCase . ATTACHING_CONTENT when ! validateMediaFileTypes :
return true ;
2025-12-30 18:30:32 +01:00
// In this use case, we can check the provider capabilities:
2026-08-02 21:41:12 +02:00
case UseCase . ATTACHING_CONTENT when provider ?. SupportsImageInput () is true :
2025-12-30 18:30:32 +01:00
return true ;
// We know that images are not supported:
case UseCase . ATTACHING_CONTENT :
await MessageBus . INSTANCE . SendWarning ( new (
Icons . Material . Filled . ImageNotSupported ,
TB ( "Images are not supported by the selected provider and model" )));
return false ;
default :
await MessageBus . INSTANCE . SendWarning ( new (
Icons . Material . Filled . ImageNotSupported ,
TB ( "Images are not supported yet" )));
return false ;
}
2025-12-15 13:26:55 +01:00
}
2026-04-09 11:53:30 +02:00
if ( FileTypes . IsAllowedPath ( filePath , FileTypes . VIDEO ))
2025-12-15 13:26:55 +01:00
{
await MessageBus . INSTANCE . SendWarning ( new (
Icons . Material . Filled . FeaturedVideo ,
TB ( "Videos are not supported yet" )));
return false ;
}
2026-04-09 11:53:30 +02:00
if ( FileTypes . IsAllowedPath ( filePath , FileTypes . AUDIO ))
2025-12-15 13:26:55 +01:00
{
await MessageBus . INSTANCE . SendWarning ( new (
Icons . Material . Filled . AudioFile ,
TB ( "Audio files are not supported yet" )));
return false ;
}
2026-04-09 16:04:03 +02:00
if ( FileTypes . IsAllowedPath ( filePath , FileTypes . DOCUMENT ))
return true ;
await MessageBus . INSTANCE . SendWarning ( new (
Icons . Material . Filled . InsertDriveFile ,
TB ( "Unsupported file type" )));
return false ;
2025-12-15 13:26:55 +01:00
}
2026-05-22 16:03:34 +02:00
}