From 266fdb9b9646d012f9212ee1eb91ac839f8ed21f Mon Sep 17 00:00:00 2001 From: krut_ni Date: Tue, 8 Apr 2025 11:37:13 +0200 Subject: [PATCH 1/6] WIP: implemented a static function to determine if pandoc is installed or not --- app/MindWork AI Studio/Tools/Pandoc.cs | 64 ++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 app/MindWork AI Studio/Tools/Pandoc.cs diff --git a/app/MindWork AI Studio/Tools/Pandoc.cs b/app/MindWork AI Studio/Tools/Pandoc.cs new file mode 100644 index 00000000..42fce7dd --- /dev/null +++ b/app/MindWork AI Studio/Tools/Pandoc.cs @@ -0,0 +1,64 @@ +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; + +namespace AIStudio.Tools; + +public static partial class Pandoc +{ + // Minimale erforderliche Version von Pandoc + private static readonly Version MINIMUM_REQUIRED_VERSION = new Version(3, 6, 0); + + /// + /// Checks if pandoc is available on the system and can be started as a process + /// + /// True, if pandoc is available and the minimum required version is met, else False. + public static async Task IsPandocAvailableAsync() + { + try + { + var startInfo = new ProcessStartInfo + { + FileName = GetPandocExecutableName(), + Arguments = "--version", + RedirectStandardOutput = true, + UseShellExecute = false, + CreateNoWindow = true + }; + using var process = Process.Start(startInfo); + if (process == null) + return false; + + var output = await process.StandardOutput.ReadToEndAsync(); + await process.WaitForExitAsync(); + if (process.ExitCode != 0) + return false; + + var versionMatch = PandocRegex().Match(output); + if (!versionMatch.Success) return false; + var versions = versionMatch.Groups[1].Value.Split('.'); + var major = int.Parse(versions[0]); + var minor = int.Parse(versions[1]); + var patch = int.Parse(versions[2]); + var installedVersion = new Version(major, minor, patch); + + return installedVersion >= MINIMUM_REQUIRED_VERSION; + + } + catch (Exception) + { + return false; + } + } + + /// + /// Gibt den Namen der Pandoc-Executable basierend auf dem Betriebssystem zurück. + /// + private static string GetPandocExecutableName() + { + return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "pandoc.exe" : "pandoc"; + } + + [GeneratedRegex(@"pandoc(?:\.exe)?\s*([0-9]+\.[0-9]+\.[0-9]+)")] + private static partial Regex PandocRegex(); +} \ No newline at end of file From e578796dbfc8428abe6de6c44d8b3c845823d2ec Mon Sep 17 00:00:00 2001 From: krut_ni Date: Tue, 8 Apr 2025 14:11:19 +0200 Subject: [PATCH 2/6] Included expressive Error Messages if pandoc is not installed or the minimum version requirement is not met --- app/MindWork AI Studio/Tools/Pandoc.cs | 27 ++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/app/MindWork AI Studio/Tools/Pandoc.cs b/app/MindWork AI Studio/Tools/Pandoc.cs index 42fce7dd..b5902b39 100644 --- a/app/MindWork AI Studio/Tools/Pandoc.cs +++ b/app/MindWork AI Studio/Tools/Pandoc.cs @@ -6,7 +6,6 @@ namespace AIStudio.Tools; public static partial class Pandoc { - // Minimale erforderliche Version von Pandoc private static readonly Version MINIMUM_REQUIRED_VERSION = new Version(3, 6, 0); /// @@ -27,37 +26,49 @@ public static partial class Pandoc }; using var process = Process.Start(startInfo); if (process == null) + { + await MessageBus.INSTANCE.SendError(new (Icons.Material.Filled.AppsOutage, $"Pandoc is not installed.")); return false; + } var output = await process.StandardOutput.ReadToEndAsync(); await process.WaitForExitAsync(); if (process.ExitCode != 0) + { + await MessageBus.INSTANCE.SendError(new (Icons.Material.Filled.AppsOutage, $"Pandoc is not installed.")); return false; + } var versionMatch = PandocRegex().Match(output); - if (!versionMatch.Success) return false; + if (!versionMatch.Success) + { + await MessageBus.INSTANCE.SendError(new (Icons.Material.Filled.AppsOutage, $"Pandoc is not installed.")); + return false; + } var versions = versionMatch.Groups[1].Value.Split('.'); var major = int.Parse(versions[0]); var minor = int.Parse(versions[1]); var patch = int.Parse(versions[2]); var installedVersion = new Version(major, minor, patch); - return installedVersion >= MINIMUM_REQUIRED_VERSION; + if (installedVersion >= MINIMUM_REQUIRED_VERSION) + return true; + + await MessageBus.INSTANCE.SendError(new (Icons.Material.Filled.AppsOutage, $"Pandoc {installedVersion.ToString()} is installed, but it doesn't match the required version ({MINIMUM_REQUIRED_VERSION.ToString()}).\n")); + return false; } catch (Exception) { + await MessageBus.INSTANCE.SendError(new (@Icons.Material.Filled.AppsOutage, "An unknown error occured while checking for Pandoc.")); return false; } } /// - /// Gibt den Namen der Pandoc-Executable basierend auf dem Betriebssystem zurück. + /// Returns the name of the pandoc executable based on the running operating system /// - private static string GetPandocExecutableName() - { - return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "pandoc.exe" : "pandoc"; - } + private static string GetPandocExecutableName() => RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "pandoc.exe" : "pandoc"; [GeneratedRegex(@"pandoc(?:\.exe)?\s*([0-9]+\.[0-9]+\.[0-9]+)")] private static partial Regex PandocRegex(); From 62a4549f89142b4ff5d271e816a165f08fe8f818 Mon Sep 17 00:00:00 2001 From: krut_ni Date: Tue, 8 Apr 2025 15:04:28 +0200 Subject: [PATCH 3/6] Removed patch level from version requirements and included more expressive log messages --- app/MindWork AI Studio/Tools/Pandoc.cs | 27 +++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/app/MindWork AI Studio/Tools/Pandoc.cs b/app/MindWork AI Studio/Tools/Pandoc.cs index b5902b39..563ff8e0 100644 --- a/app/MindWork AI Studio/Tools/Pandoc.cs +++ b/app/MindWork AI Studio/Tools/Pandoc.cs @@ -1,13 +1,14 @@ using System.Diagnostics; using System.Runtime.InteropServices; using System.Text.RegularExpressions; +using AIStudio.Components; namespace AIStudio.Tools; public static partial class Pandoc { - private static readonly Version MINIMUM_REQUIRED_VERSION = new Version(3, 6, 0); - + private static readonly ILogger LOG = Program.LOGGER_FACTORY.CreateLogger("PluginFactory"); + private static readonly Version MINIMUM_REQUIRED_VERSION = new Version(3, 6); /// /// Checks if pandoc is available on the system and can be started as a process /// @@ -27,7 +28,8 @@ public static partial class Pandoc using var process = Process.Start(startInfo); if (process == null) { - await MessageBus.INSTANCE.SendError(new (Icons.Material.Filled.AppsOutage, $"Pandoc is not installed.")); + await MessageBus.INSTANCE.SendError(new (Icons.Material.Filled.Help, "The pandoc process could not be started.")); + LOG.LogInformation("The pandoc process was not started, it was null"); return false; } @@ -35,32 +37,35 @@ public static partial class Pandoc await process.WaitForExitAsync(); if (process.ExitCode != 0) { - await MessageBus.INSTANCE.SendError(new (Icons.Material.Filled.AppsOutage, $"Pandoc is not installed.")); + await MessageBus.INSTANCE.SendError(new (Icons.Material.Filled.Error, $"The pandoc process exited unexpectedly.")); + LOG.LogError("The pandoc process was exited with code {ProcessExitCode}", process.ExitCode); return false; } var versionMatch = PandocRegex().Match(output); if (!versionMatch.Success) { - await MessageBus.INSTANCE.SendError(new (Icons.Material.Filled.AppsOutage, $"Pandoc is not installed.")); + await MessageBus.INSTANCE.SendError(new (Icons.Material.Filled.Terminal, $"pandoc --version returned an invalid format.")); + LOG.LogError("pandoc --version returned an invalid format:\n {Output}", output); return false; } var versions = versionMatch.Groups[1].Value.Split('.'); var major = int.Parse(versions[0]); var minor = int.Parse(versions[1]); - var patch = int.Parse(versions[2]); - var installedVersion = new Version(major, minor, patch); + var installedVersion = new Version(major, minor); if (installedVersion >= MINIMUM_REQUIRED_VERSION) return true; - await MessageBus.INSTANCE.SendError(new (Icons.Material.Filled.AppsOutage, $"Pandoc {installedVersion.ToString()} is installed, but it doesn't match the required version ({MINIMUM_REQUIRED_VERSION.ToString()}).\n")); + await MessageBus.INSTANCE.SendError(new (Icons.Material.Filled.Build, $"Pandoc {installedVersion.ToString()} is installed, but it doesn't match the required version ({MINIMUM_REQUIRED_VERSION.ToString()}).")); + LOG.LogInformation("Pandoc {Installed} is installed, but it does not match the required version ({Requirement})", installedVersion.ToString(), MINIMUM_REQUIRED_VERSION.ToString()); return false; } - catch (Exception) + catch (Exception e) { - await MessageBus.INSTANCE.SendError(new (@Icons.Material.Filled.AppsOutage, "An unknown error occured while checking for Pandoc.")); + await MessageBus.INSTANCE.SendError(new (@Icons.Material.Filled.AppsOutage, "Pandoc is not installed.")); + LOG.LogError("Pandoc is not installed and threw an exception:\n {Message}", e.Message); return false; } } @@ -70,6 +75,6 @@ public static partial class Pandoc /// private static string GetPandocExecutableName() => RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "pandoc.exe" : "pandoc"; - [GeneratedRegex(@"pandoc(?:\.exe)?\s*([0-9]+\.[0-9]+\.[0-9]+)")] + [GeneratedRegex(@"pandoc(?:\.exe)?\s*([0-9]+\.[0-9]+)")] private static partial Regex PandocRegex(); } \ No newline at end of file From ca98c5548e9bda8c08409fcd04a03ba476a830cc Mon Sep 17 00:00:00 2001 From: krut_ni Date: Thu, 10 Apr 2025 20:49:23 +0200 Subject: [PATCH 4/6] Added a success event to the message bus for better ux --- app/MindWork AI Studio/Tools/Error.cs | 15 +++++++++++++++ app/MindWork AI Studio/Tools/Event.cs | 1 + app/MindWork AI Studio/Tools/MessageBus.cs | 2 ++ 3 files changed, 18 insertions(+) diff --git a/app/MindWork AI Studio/Tools/Error.cs b/app/MindWork AI Studio/Tools/Error.cs index a3ba6c61..77f8d7c2 100644 --- a/app/MindWork AI Studio/Tools/Error.cs +++ b/app/MindWork AI Studio/Tools/Error.cs @@ -13,4 +13,19 @@ public readonly record struct Error(string Icon, string Message) config.VisibleStateDuration = 14_000; }); } +} + +public readonly record struct Success(string Icon, string Message) +{ + public void Show(ISnackbar snackbar) + { + var icon = this.Icon; + snackbar.Add(this.Message, Severity.Success, config => + { + config.Icon = icon; + config.IconSize = Size.Large; + config.HideTransitionDuration = 600; + config.VisibleStateDuration = 10_000; + }); + } } \ 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 57758589..6e2e3a93 100644 --- a/app/MindWork AI Studio/Tools/Event.cs +++ b/app/MindWork AI Studio/Tools/Event.cs @@ -10,6 +10,7 @@ public enum Event COLOR_THEME_CHANGED, PLUGINS_RELOADED, SHOW_ERROR, + SHOW_SUCCESS, // Update events: USER_SEARCH_FOR_UPDATE, diff --git a/app/MindWork AI Studio/Tools/MessageBus.cs b/app/MindWork AI Studio/Tools/MessageBus.cs index 06a2dfd8..3d48a7bb 100644 --- a/app/MindWork AI Studio/Tools/MessageBus.cs +++ b/app/MindWork AI Studio/Tools/MessageBus.cs @@ -67,6 +67,8 @@ public sealed class MessageBus } public Task SendError(Error error) => this.SendMessage(null, Event.SHOW_ERROR, error); + + public Task SendSuccess(Success success) => this.SendMessage(null, Event.SHOW_SUCCESS, success); public void DeferMessage(ComponentBase? sendingComponent, Event triggeredEvent, T? data = default) { From f7771d95fc0d47426573d665a12d911ed873a6f3 Mon Sep 17 00:00:00 2001 From: krut_ni Date: Thu, 10 Apr 2025 20:53:13 +0200 Subject: [PATCH 5/6] added temporary buttons for debugging purposes TODO: Delete --- app/MindWork AI Studio/Pages/About.razor | 16 +++++++++++++ app/MindWork AI Studio/Pages/About.razor.cs | 26 +++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/app/MindWork AI Studio/Pages/About.razor b/app/MindWork AI Studio/Pages/About.razor index 32c8b5df..d4a37762 100644 --- a/app/MindWork AI Studio/Pages/About.razor +++ b/app/MindWork AI Studio/Pages/About.razor @@ -3,6 +3,22 @@
About MindWork AI Studio + + +

Pandoc Verfügbarkeit prüfen

+ + @(isChecking ? "Überprüfe..." : "Pandoc überprüfen") + +

@statusMessage

+
+ + +

Pandoc Installation

+ + Install Pandoc + +
+ diff --git a/app/MindWork AI Studio/Pages/About.razor.cs b/app/MindWork AI Studio/Pages/About.razor.cs index 51727506..94e5d612 100644 --- a/app/MindWork AI Studio/Pages/About.razor.cs +++ b/app/MindWork AI Studio/Pages/About.razor.cs @@ -174,4 +174,30 @@ public partial class About : ComponentBase { await this.MessageBus.SendMessage(this, Event.USER_SEARCH_FOR_UPDATE); } + + // TODO: DELETE FOR DEBUGGING ONLY + private bool isChecking; + private string statusMessage = string.Empty; + private async Task CheckPandoc() + { + this.isChecking = true; + this.statusMessage = "Überprüfe die Verfügbarkeit von Pandoc..."; + this.StateHasChanged(); // Aktualisiere die UI + var isPandocAvailable = await Pandoc.IsPandocAvailableAsync(); + if (isPandocAvailable) + { + this.statusMessage = "Pandoc ist verfügbar und erfüllt die Mindestversion."; + } + else + { + this.statusMessage = "Pandoc ist nicht verfügbar oder die installierte Version ist zu niedrig."; + } + this.isChecking = false; + this.StateHasChanged(); // Aktualisiere die UI + } + + private async Task InstallPandoc() + { + var installPandoc = Pandoc.InstallPandocAsync(this.RustService); + } } From 8a890d2ed9960b821b6718041e6d1f2e38135fb1 Mon Sep 17 00:00:00 2001 From: krut_ni Date: Thu, 10 Apr 2025 20:53:51 +0200 Subject: [PATCH 6/6] WIP: included function to download pandoc' latest zip into our data dir --- app/MindWork AI Studio/Tools/Pandoc.cs | 58 +++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/app/MindWork AI Studio/Tools/Pandoc.cs b/app/MindWork AI Studio/Tools/Pandoc.cs index 563ff8e0..73cdc6ad 100644 --- a/app/MindWork AI Studio/Tools/Pandoc.cs +++ b/app/MindWork AI Studio/Tools/Pandoc.cs @@ -1,14 +1,18 @@ using System.Diagnostics; +using System.IO.Compression; using System.Runtime.InteropServices; using System.Text.RegularExpressions; using AIStudio.Components; +using AIStudio.Tools.Services; namespace AIStudio.Tools; public static partial class Pandoc { private static readonly ILogger LOG = Program.LOGGER_FACTORY.CreateLogger("PluginFactory"); + private static readonly string DOWNLOAD_URL = "https://github.com/jgm/pandoc/releases/download/3.6.4/pandoc-3.6.4-windows-x86_64.zip"; private static readonly Version MINIMUM_REQUIRED_VERSION = new Version(3, 6); + /// /// Checks if pandoc is available on the system and can be started as a process /// @@ -53,9 +57,12 @@ public static partial class Pandoc var major = int.Parse(versions[0]); var minor = int.Parse(versions[1]); var installedVersion = new Version(major, minor); - + if (installedVersion >= MINIMUM_REQUIRED_VERSION) + { + await MessageBus.INSTANCE.SendSuccess(new(Icons.Material.Filled.CheckCircle, $"Pandoc {installedVersion.ToString()} is installed.")); return true; + } await MessageBus.INSTANCE.SendError(new (Icons.Material.Filled.Build, $"Pandoc {installedVersion.ToString()} is installed, but it doesn't match the required version ({MINIMUM_REQUIRED_VERSION.ToString()}).")); LOG.LogInformation("Pandoc {Installed} is installed, but it does not match the required version ({Requirement})", installedVersion.ToString(), MINIMUM_REQUIRED_VERSION.ToString()); @@ -70,6 +77,55 @@ public static partial class Pandoc } } + public static async Task InstallPandocAsync(RustService rustService) + { + var dataDir = await rustService.GetDataDirectory(); + await MessageBus.INSTANCE.SendError(new (Icons.Material.Filled.Help, $"{dataDir}")); + var installDir = Path.Join(dataDir, "pandoc"); + + try + { + if (!Directory.Exists(installDir)) + Directory.CreateDirectory(installDir); + + using var client = new HttpClient(); + var response = await client.GetAsync(DOWNLOAD_URL); + if (response.IsSuccessStatusCode) + { + var fileBytes = await response.Content.ReadAsByteArrayAsync(); + var tempZipPath = Path.Join(Path.GetTempPath(), "pandoc.zip"); + await File.WriteAllBytesAsync(tempZipPath, fileBytes); + ZipFile.ExtractToDirectory(tempZipPath, installDir); + File.Delete(tempZipPath); + + var currentPath = Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Machine); + var pandocDir = Path.Join(currentPath, "pandoc-3.6.4"); + if (currentPath != null && !currentPath.Contains(pandocDir)) + { + Environment.SetEnvironmentVariable( + "PATH", + $"{currentPath};{pandocDir}", + EnvironmentVariableTarget.Machine); + Console.WriteLine("Pandoc-Verzeichnis zum PATH hinzugefügt."); + } + else + { + Console.WriteLine("Pandoc-Verzeichnis ist bereits im PATH."); + } + + await MessageBus.INSTANCE.SendSuccess(new(Icons.Material.Filled.CheckCircle, $"Pandoc {MINIMUM_REQUIRED_VERSION.ToString()} was installed successfully.")); + } + else + { + Console.WriteLine("Fehler beim Herunterladen von Pandoc."); + } + } + catch (Exception ex) + { + Console.WriteLine($"Fehler: {ex.Message}"); + } + } + /// /// Returns the name of the pandoc executable based on the running operating system ///