diff --git a/app/MindWork AI Studio/Pages/Plugins.razor.cs b/app/MindWork AI Studio/Pages/Plugins.razor.cs index b563a9dc..bbed3dc6 100644 --- a/app/MindWork AI Studio/Pages/Plugins.razor.cs +++ b/app/MindWork AI Studio/Pages/Plugins.razor.cs @@ -559,7 +559,7 @@ public partial class Plugins : MSGComponentBase switch (archivePaths.Count) { case 0: - await this.MessageBus.SendWarning(new(Icons.Material.Filled.ReportProblem, string.Format(this.T("Please drop a plugin archive with the extension {0} or .zip."), PluginShareService.PLUGIN_FILE_EXTENSION))); + await this.MessageBus.SendWarning(new(Icons.Material.Filled.ReportProblem, string.Format(this.T("Please drop a plugin archive with the extension {0} or .zip."), PluginArchive.PLUGIN_FILE_EXTENSION))); return; case > 1: diff --git a/app/MindWork AI Studio/Tools/PluginSystem/PluginArchive.cs b/app/MindWork AI Studio/Tools/PluginSystem/PluginArchive.cs index 4f281f21..4db08c72 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/PluginArchive.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/PluginArchive.cs @@ -4,6 +4,16 @@ namespace AIStudio.Tools.PluginSystem; public static class PluginArchive { + /// + /// The file extension of plugin archives. + /// + /// + /// Keep in sync with SHARE_FILE_EXTENSION in runtime/src/share_sheet.rs: the runtime only hands + /// archives with this extension to the native share sheet. + /// + public const string PLUGIN_FILE_EXTENSION = ".mwplugin"; + + // Compatibility shim for Windows-created ZIPs with backslashes in entry names (dotnet/runtime#27620); // remove after dotnet/runtime#27620 and #41914 are fixed. // See documentation/compatibility-shims/2026-07-plugin-archive-zip-backslashes.md. diff --git a/app/MindWork AI Studio/Tools/Rust/FileTypes.cs b/app/MindWork AI Studio/Tools/Rust/FileTypes.cs index 9bc7ab59..038833f0 100644 --- a/app/MindWork AI Studio/Tools/Rust/FileTypes.cs +++ b/app/MindWork AI Studio/Tools/Rust/FileTypes.cs @@ -1,5 +1,4 @@ using AIStudio.Tools.PluginSystem; -using AIStudio.Tools.Services; // ReSharper disable MemberCanBePrivate.Global @@ -83,7 +82,7 @@ public static class FileTypes // Other standalone types public static readonly FileTypeFilter CERTIFICATE_BUNDLE = FileTypeFilter.Leaf(TB("Certificate bundle"), "pem", "crt", "cer"); public static readonly FileTypeFilter EXECUTABLES = FileTypeFilter.Leaf(TB("Executable"), "exe", "app", "bin", "appimage"); - public static readonly FileTypeFilter PLUGIN_ARCHIVE = FileTypeFilter.Leaf(TB("Plugin archive"), PluginShareService.PLUGIN_FILE_EXTENSION.TrimStart('.'), "zip"); + public static readonly FileTypeFilter PLUGIN_ARCHIVE = FileTypeFilter.Leaf(TB("Plugin archive"), PluginArchive.PLUGIN_FILE_EXTENSION.TrimStart('.'), "zip"); public static FileTypeFilter? AsOneFileType(params FileTypeFilter[]? types) { diff --git a/app/MindWork AI Studio/Tools/Services/PluginShareService.cs b/app/MindWork AI Studio/Tools/Services/PluginShareService.cs index a56c1886..9b7b2983 100644 --- a/app/MindWork AI Studio/Tools/Services/PluginShareService.cs +++ b/app/MindWork AI Studio/Tools/Services/PluginShareService.cs @@ -11,12 +11,6 @@ public sealed class PluginShareService(NativeShareService nativeShareService, Ru private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(PluginShareService).Namespace, nameof(PluginShareService)); - /// - /// Keep in sync with SHARE_FILE_EXTENSION in runtime/src/share_sheet.rs: the runtime only hands - /// archives with this extension to the native share sheet. - /// - public const string PLUGIN_FILE_EXTENSION = ".mwplugin"; - private const string PLUGIN_FILE_NAME = "plugin.lua"; /// @@ -69,7 +63,7 @@ public sealed class PluginShareService(NativeShareService nativeShareService, Ru /// The share result, including the chosen archive path when successful. private async Task ExportAsync(IAvailablePlugin plugin, string pluginRoot, CancellationToken token) { - var suggestedFileName = $"{CreateSafeFileNamePrefix(plugin.Name)}{PLUGIN_FILE_EXTENSION}"; + var suggestedFileName = $"{CreateSafeFileNamePrefix(plugin.Name)}{PluginArchive.PLUGIN_FILE_EXTENSION}"; var saveResponse = await rustService.SaveFile(TB("Export plugin archive"), [FileTypes.PLUGIN_ARCHIVE], suggestedFileName); if (saveResponse.UserCancelled) return new(false, plugin.Name, string.Empty, string.Empty, true); @@ -116,7 +110,7 @@ public sealed class PluginShareService(NativeShareService nativeShareService, Ru private async Task ShareViaNativeSheetAsync(IAvailablePlugin plugin, string pluginRoot, CancellationToken token) { var archiveDirectory = Path.Join(Path.GetTempPath(), TEMPORARY_ARCHIVE_DIRECTORY); - var archivePath = Path.Join(archiveDirectory, $"{CreateSafeFileNamePrefix(plugin.Name)}-{plugin.Id:N}-{Guid.NewGuid():N}{PLUGIN_FILE_EXTENSION}"); + var archivePath = Path.Join(archiveDirectory, $"{CreateSafeFileNamePrefix(plugin.Name)}-{plugin.Id:N}-{Guid.NewGuid():N}{PluginArchive.PLUGIN_FILE_EXTENSION}"); try { @@ -193,7 +187,7 @@ public sealed class PluginShareService(NativeShareService nativeShareService, Ru private void CleanUpExpiredArchives(string archiveDirectory) { var expiry = DateTime.UtcNow.AddHours(-TEMPORARY_ARCHIVE_RETENTION_HOURS); - foreach (var archivePath in Directory.EnumerateFiles(archiveDirectory, $"*{PLUGIN_FILE_EXTENSION}", SearchOption.TopDirectoryOnly)) + foreach (var archivePath in Directory.EnumerateFiles(archiveDirectory, $"*{PluginArchive.PLUGIN_FILE_EXTENSION}", SearchOption.TopDirectoryOnly)) { try {