From 266fdb9b9646d012f9212ee1eb91ac839f8ed21f Mon Sep 17 00:00:00 2001 From: krut_ni Date: Tue, 8 Apr 2025 11:37:13 +0200 Subject: [PATCH 01/23] 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 000000000..42fce7dde --- /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 02/23] 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 42fce7dde..b5902b390 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 async Task IsPandocAvailableAsync() }; 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 03/23] 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 b5902b390..563ff8e08 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 async Task IsPandocAvailableAsync() 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 async Task IsPandocAvailableAsync() 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 async Task IsPandocAvailableAsync() /// 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 04/23] 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 a3ba6c613..77f8d7c20 100644 --- a/app/MindWork AI Studio/Tools/Error.cs +++ b/app/MindWork AI Studio/Tools/Error.cs @@ -13,4 +13,19 @@ public void Show(ISnackbar snackbar) 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 57758589b..6e2e3a939 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 06a2dfd8a..3d48a7bbc 100644 --- a/app/MindWork AI Studio/Tools/MessageBus.cs +++ b/app/MindWork AI Studio/Tools/MessageBus.cs @@ -67,6 +67,8 @@ public async Task SendMessage(ComponentBase? sendingComponent, Event triggere } 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 05/23] 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 32c8b5df1..d4a377627 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 51727506d..94e5d6129 100644 --- a/app/MindWork AI Studio/Pages/About.razor.cs +++ b/app/MindWork AI Studio/Pages/About.razor.cs @@ -174,4 +174,30 @@ private async Task CheckForUpdate() { 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 06/23] 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 563ff8e08..73cdc6ad3 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 async Task IsPandocAvailableAsync() 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 async Task IsPandocAvailableAsync() } } + 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 /// From 844e305bb9366a5b692a572f11d600c716ddd6a0 Mon Sep 17 00:00:00 2001 From: krut_ni Date: Mon, 14 Apr 2025 11:44:34 +0200 Subject: [PATCH 07/23] added warning messages to the message bus --- app/MindWork AI Studio/Layout/MainLayout.razor.cs | 14 +++++++++++++- app/MindWork AI Studio/Tools/Error.cs | 15 +++++++++++++++ app/MindWork AI Studio/Tools/Event.cs | 1 + app/MindWork AI Studio/Tools/MessageBus.cs | 2 ++ 4 files changed, 31 insertions(+), 1 deletion(-) diff --git a/app/MindWork AI Studio/Layout/MainLayout.razor.cs b/app/MindWork AI Studio/Layout/MainLayout.razor.cs index 08e963cb4..c95a69d48 100644 --- a/app/MindWork AI Studio/Layout/MainLayout.razor.cs +++ b/app/MindWork AI Studio/Layout/MainLayout.razor.cs @@ -102,7 +102,7 @@ protected override async Task OnInitializedAsync() // Register this component with the message bus: this.MessageBus.RegisterComponent(this); - this.MessageBus.ApplyFilters(this, [], [ Event.UPDATE_AVAILABLE, Event.CONFIGURATION_CHANGED, Event.COLOR_THEME_CHANGED, Event.SHOW_ERROR ]); + this.MessageBus.ApplyFilters(this, [], [ Event.UPDATE_AVAILABLE, Event.CONFIGURATION_CHANGED, Event.COLOR_THEME_CHANGED, Event.SHOW_ERROR, Event.SHOW_WARNING, Event.SHOW_SUCCESS, ]); // Set the snackbar for the update service: UpdateService.SetBlazorDependencies(this.Snackbar); @@ -174,11 +174,23 @@ public async Task ProcessMessage(ComponentBase? sendingComponent, Event trigg this.StateHasChanged(); break; + case Event.SHOW_SUCCESS: + if (data is Success success) + success.Show(this.Snackbar); + + break; + case Event.SHOW_ERROR: if (data is Error error) error.Show(this.Snackbar); break; + + case Event.SHOW_WARNING: + if (data is Warning warning) + warning.Show(this.Snackbar); + + break; } } diff --git a/app/MindWork AI Studio/Tools/Error.cs b/app/MindWork AI Studio/Tools/Error.cs index 77f8d7c20..a9e3af815 100644 --- a/app/MindWork AI Studio/Tools/Error.cs +++ b/app/MindWork AI Studio/Tools/Error.cs @@ -15,6 +15,21 @@ public void Show(ISnackbar snackbar) } } +public readonly record struct Warning(string Icon, string Message) +{ + public void Show(ISnackbar snackbar) + { + var icon = this.Icon; + snackbar.Add(this.Message, Severity.Warning, config => + { + config.Icon = icon; + config.IconSize = Size.Large; + config.HideTransitionDuration = 600; + config.VisibleStateDuration = 12_000; + }); + } +} + public readonly record struct Success(string Icon, string Message) { public void Show(ISnackbar snackbar) diff --git a/app/MindWork AI Studio/Tools/Event.cs b/app/MindWork AI Studio/Tools/Event.cs index 6e2e3a939..ab6fb3183 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_WARNING, SHOW_SUCCESS, // Update events: diff --git a/app/MindWork AI Studio/Tools/MessageBus.cs b/app/MindWork AI Studio/Tools/MessageBus.cs index 3d48a7bbc..73344f2e3 100644 --- a/app/MindWork AI Studio/Tools/MessageBus.cs +++ b/app/MindWork AI Studio/Tools/MessageBus.cs @@ -68,6 +68,8 @@ public async Task SendMessage(ComponentBase? sendingComponent, Event triggere public Task SendError(Error error) => this.SendMessage(null, Event.SHOW_ERROR, error); + public Task SendWarning(Warning warning) => this.SendMessage(null, Event.SHOW_WARNING, warning); + public Task SendSuccess(Success success) => this.SendMessage(null, Event.SHOW_SUCCESS, success); public void DeferMessage(ComponentBase? sendingComponent, Event triggeredEvent, T? data = default) From b60d23a91bd8b0184664804c1658c3b6bb5f7e60 Mon Sep 17 00:00:00 2001 From: krut_ni Date: Mon, 14 Apr 2025 14:05:01 +0200 Subject: [PATCH 08/23] Added functions to parse the current latest version from gh latest page and to generate an uri based on the latest version --- app/MindWork AI Studio/Pages/About.razor | 7 +++ app/MindWork AI Studio/Pages/About.razor.cs | 9 +++- app/MindWork AI Studio/Tools/Pandoc.cs | 57 +++++++++++++++++++-- 3 files changed, 66 insertions(+), 7 deletions(-) diff --git a/app/MindWork AI Studio/Pages/About.razor b/app/MindWork AI Studio/Pages/About.razor index d4a377627..5054de98d 100644 --- a/app/MindWork AI Studio/Pages/About.razor +++ b/app/MindWork AI Studio/Pages/About.razor @@ -19,6 +19,13 @@ + +

Latest Version

+ + Version + +
+ diff --git a/app/MindWork AI Studio/Pages/About.razor.cs b/app/MindWork AI Studio/Pages/About.razor.cs index 94e5d6129..d4c2c20ed 100644 --- a/app/MindWork AI Studio/Pages/About.razor.cs +++ b/app/MindWork AI Studio/Pages/About.razor.cs @@ -183,7 +183,7 @@ 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(); + var isPandocAvailable = await Pandoc.CheckAvailabilityAsync(); if (isPandocAvailable) { this.statusMessage = "Pandoc ist verfügbar und erfüllt die Mindestversion."; @@ -198,6 +198,11 @@ private async Task CheckPandoc() private async Task InstallPandoc() { - var installPandoc = Pandoc.InstallPandocAsync(this.RustService); + var installPandoc = Pandoc.InstallAsync(this.RustService); + } + + private async Task LatestVersion() + { + await Pandoc.FetchLatestVersionAsync(); } } diff --git a/app/MindWork AI Studio/Tools/Pandoc.cs b/app/MindWork AI Studio/Tools/Pandoc.cs index 73cdc6ad3..c4e28a37d 100644 --- a/app/MindWork AI Studio/Tools/Pandoc.cs +++ b/app/MindWork AI Studio/Tools/Pandoc.cs @@ -10,14 +10,17 @@ 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); + private static readonly string DOWNLOAD_URL = "https://github.com/jgm/pandoc/releases/download/"; + private static readonly string LATEST_URL = "https://github.com/jgm/pandoc/releases/latest"; + private static readonly Version MINIMUM_REQUIRED_VERSION = new (3, 6); + private static readonly Version FALLBACK_VERSION = new (3, 6, 4); + private static readonly string CPU_ARCHITECTURE = "win-x64"; /// /// 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() + public static async Task CheckAvailabilityAsync() { try { @@ -77,7 +80,7 @@ public static async Task IsPandocAvailableAsync() } } - public static async Task InstallPandocAsync(RustService rustService) + public static async Task InstallAsync(RustService rustService) { var dataDir = await rustService.GetDataDirectory(); await MessageBus.INSTANCE.SendError(new (Icons.Material.Filled.Help, $"{dataDir}")); @@ -89,7 +92,7 @@ public static async Task InstallPandocAsync(RustService rustService) Directory.CreateDirectory(installDir); using var client = new HttpClient(); - var response = await client.GetAsync(DOWNLOAD_URL); + var response = await client.GetAsync(await GenerateUriAsync()); if (response.IsSuccessStatusCode) { var fileBytes = await response.Content.ReadAsByteArrayAsync(); @@ -125,6 +128,47 @@ public static async Task InstallPandocAsync(RustService rustService) Console.WriteLine($"Fehler: {ex.Message}"); } } + + public static async Task FetchLatestVersionAsync() { + using var client = new HttpClient(); + var response = await client.GetAsync(LATEST_URL); + + if (!response.IsSuccessStatusCode) + { + LOG.LogError("Code {StatusCode}: Could not fetch pandocs latest page:\n {Response}", response.StatusCode, response.RequestMessage); + await MessageBus.INSTANCE.SendWarning(new (Icons.Material.Filled.Warning, $"The latest pandoc version was not found, installing version {FALLBACK_VERSION.ToString()} instead.")); + return FALLBACK_VERSION.ToString(); + } + + var htmlContent = await response.Content.ReadAsStringAsync(); + + var versionMatch = VersionRegex().Match(htmlContent); + if (!versionMatch.Success) + { + LOG.LogError("The latest version regex returned nothing:\n {Value}", versionMatch.Groups.ToString()); + await MessageBus.INSTANCE.SendWarning(new (Icons.Material.Filled.Warning, $"The latest pandoc version was not found, installing version {FALLBACK_VERSION.ToString()} instead.")); + return FALLBACK_VERSION.ToString(); + } + + var version = versionMatch.Groups[1].Value; + return version; + } + + // win arm not available + private static async Task GenerateUriAsync() + { + var version = await FetchLatestVersionAsync(); + var baseUri = $"{DOWNLOAD_URL}/{version}/pandoc-{version}-"; + return CPU_ARCHITECTURE switch + { + "win-x64" => $"{baseUri}windows-x86_64.zip", + "osx-x64" => $"{baseUri}x86_64-macOS.zip", + "osx-arm64" => $"{baseUri}arm64-macOS.zip", + "linux-x64" => $"{baseUri}linux-amd64.tar.gz", + "linux-arm" => $"{baseUri}linux-arm64.tar.gz", + _ => string.Empty, + }; + } /// /// Returns the name of the pandoc executable based on the running operating system @@ -133,4 +177,7 @@ public static async Task InstallPandocAsync(RustService rustService) [GeneratedRegex(@"pandoc(?:\.exe)?\s*([0-9]+\.[0-9]+)")] private static partial Regex PandocRegex(); + + [GeneratedRegex(@"pandoc(?:\.exe)?\s*([0-9]+\.[0-9]+\.[0-9]+)")] + private static partial Regex VersionRegex(); } \ No newline at end of file From 331c6caf4b7e2c20dce581b5116945948d39219f Mon Sep 17 00:00:00 2001 From: krut_ni Date: Mon, 14 Apr 2025 19:45:23 +0200 Subject: [PATCH 09/23] WIP: Change pandocs download function to account for different cpu architectures --- app/MindWork AI Studio/Tools/Pandoc.cs | 64 +++++++++++++++++--------- 1 file changed, 43 insertions(+), 21 deletions(-) diff --git a/app/MindWork AI Studio/Tools/Pandoc.cs b/app/MindWork AI Studio/Tools/Pandoc.cs index c4e28a37d..d89a430a6 100644 --- a/app/MindWork AI Studio/Tools/Pandoc.cs +++ b/app/MindWork AI Studio/Tools/Pandoc.cs @@ -92,42 +92,64 @@ public static async Task InstallAsync(RustService rustService) Directory.CreateDirectory(installDir); using var client = new HttpClient(); - var response = await client.GetAsync(await GenerateUriAsync()); - if (response.IsSuccessStatusCode) + var uri = await GenerateUriAsync(); + + var response = await client.GetAsync(uri); + if (!response.IsSuccessStatusCode) + { + await MessageBus.INSTANCE.SendError(new (Icons.Material.Filled.Error, $"Pandoc was not installed successfully, because the download archive was not found.")); + LOG.LogError("Pandoc was not installed, the release archive was not found (Status Code {StatusCode}):\n{Uri}\n{Message}", response.StatusCode, uri, response.RequestMessage); + return; + } + var fileBytes = await response.Content.ReadAsByteArrayAsync(); + + if (uri.Contains(".zip")) { - 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 if (uri.Contains(".tar.gz")) + { + Console.WriteLine("is zip"); + } + else + { + await MessageBus.INSTANCE.SendError(new (Icons.Material.Filled.Error, $"Pandoc was not installed successfully, because the download archive type is unknown.")); + LOG.LogError("Pandoc was not installed, the download archive is unknown:\n {Uri}", uri); + return; + } + + + 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("Fehler beim Herunterladen von Pandoc."); + 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.")); } catch (Exception ex) { Console.WriteLine($"Fehler: {ex.Message}"); } } + + private static async Task ExtractZipAsync(string zipPath, string targetDir) + { + + } public static async Task FetchLatestVersionAsync() { using var client = new HttpClient(); From 09c9834c04417850696f70ed99d698ed151f9055 Mon Sep 17 00:00:00 2001 From: krut_ni Date: Tue, 15 Apr 2025 15:16:12 +0200 Subject: [PATCH 10/23] WIP: Included a pandoc dialog to check for the availability and to include an automatic download process for the user --- .../Dialogs/PandocDialog.razor | 41 +++++++++++++++++++ .../Dialogs/PandocDialog.razor.cs | 37 +++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 app/MindWork AI Studio/Dialogs/PandocDialog.razor create mode 100644 app/MindWork AI Studio/Dialogs/PandocDialog.razor.cs diff --git a/app/MindWork AI Studio/Dialogs/PandocDialog.razor b/app/MindWork AI Studio/Dialogs/PandocDialog.razor new file mode 100644 index 000000000..5f2a6d248 --- /dev/null +++ b/app/MindWork AI Studio/Dialogs/PandocDialog.razor @@ -0,0 +1,41 @@ + + + Install Pandoc + + + @if (this.showInstallPage) + { + +

Installationpage

+
+ } + else + { + + @if (showSkeleton) + { + + + } + else if (isPandocAvailable) + { + + + Pandoc ist auf Ihrem System verfügbar + + } + else + { + + + Pandoc ist auf Ihrem System nicht verfügbar + + Proceed to installation + } + + } +
+ + + +
\ No newline at end of file diff --git a/app/MindWork AI Studio/Dialogs/PandocDialog.razor.cs b/app/MindWork AI Studio/Dialogs/PandocDialog.razor.cs new file mode 100644 index 000000000..43ad24005 --- /dev/null +++ b/app/MindWork AI Studio/Dialogs/PandocDialog.razor.cs @@ -0,0 +1,37 @@ +using Microsoft.AspNetCore.Components; + +namespace AIStudio.Dialogs; + +public partial class PandocDialog : ComponentBase +{ + [CascadingParameter] + private IMudDialogInstance MudDialog { get; set; } = null!; + + private bool isPandocAvailable; + private bool showSkeleton; + private bool showInstallPage; + + + #region Overrides of ComponentBase + + protected override async Task OnInitializedAsync() + { + await base.OnInitializedAsync(); + this.showSkeleton = true; + await this.CheckPandocAvailabilityAsync(); + } + + #endregion + + private void Cancel() => this.MudDialog.Cancel(); + + private async Task CheckPandocAvailabilityAsync() + { + await Task.Delay(2500); + this.isPandocAvailable = await Pandoc.CheckAvailabilityAsync(); + this.showSkeleton = false; + await this.InvokeAsync(this.StateHasChanged); + } + + private void ProceedToInstallation() => this.showInstallPage = true; +} \ No newline at end of file From 08c1a54e51fd772f3f5bc40b78a4742c7a752e67 Mon Sep 17 00:00:00 2001 From: krut_ni Date: Tue, 15 Apr 2025 21:39:24 +0200 Subject: [PATCH 11/23] WIP: Included expressive dialog for automatic and manual installation --- .../Dialogs/PandocDialog.razor | 107 +++++++++++++----- 1 file changed, 81 insertions(+), 26 deletions(-) diff --git a/app/MindWork AI Studio/Dialogs/PandocDialog.razor b/app/MindWork AI Studio/Dialogs/PandocDialog.razor index 5f2a6d248..a420667c8 100644 --- a/app/MindWork AI Studio/Dialogs/PandocDialog.razor +++ b/app/MindWork AI Studio/Dialogs/PandocDialog.razor @@ -1,38 +1,93 @@ - +@using ReverseMarkdown.Converters + Install Pandoc @if (this.showInstallPage) { - -

Installationpage

-
+
+ + AI Studio relies on the third-party app Pandoc to process and retrieve data from local + Office files (ex. Word) and later other text formats like LaTeX. + + + Unfortunately Pandocs GPL license is not compatible with AI Studios licences. + Therefore you have to accept Pandocs GPL license before we can download and install Pandoc automatically for you (recommended). + However you can download it yourself manually with the instructions below. + +
+ + + + Pandoc is distributed under the GNU General Public License v2 (GPL). + By clicking "Accept GPL and Install", you agree to the terms of the GPL license
and Pandoc will be installed automatically for you.
+
+ Accept GPL and Install +
+ + + If you prefer to install Pandoc yourself, please follow these steps: + + + + Accept the terms of the GPL license and download the latest archive with the button below. + + + Extract the archive to a folder of your choice. + (ex. C:\Users\%USERNAME%\pandoc) + + + Open the folder and copy the path to the pandoc.exe file into you clipboard. + (ex. C:\Users\%USERNAME%\pandoc\pandoc-3.6.4) + + + Add the copied path to your systems environment variables and check the installation by typing + pandoc --version into your command line interface. + + + + Pandoc is distributed under the GNU General Public License v2 (GPL). + By clicking "Accept GPL and Download", you agree to the terms of the GPL license. + + Accept GPL and Download + +
+
+ Reject GPL licence +
} else { - - @if (showSkeleton) - { - - - } - else if (isPandocAvailable) - { - - - Pandoc ist auf Ihrem System verfügbar - - } - else - { - - - Pandoc ist auf Ihrem System nicht verfügbar - - Proceed to installation - } - + + @if (showSkeleton) + { + + + } + else if (isPandocAvailable) + { + + + Pandoc ist auf Ihrem System verfügbar + + } + else + { + + + Pandoc ist auf Ihrem System nicht verfügbar + + Proceed + to installation + + } + }
From d9fb0dedafe8eed0690ca1786bfd22df7ad63bcd Mon Sep 17 00:00:00 2001 From: nilsk Date: Mon, 19 May 2025 10:12:42 +0200 Subject: [PATCH 12/23] Introduced a flexible code block that enables to show code examples in a tab menu, single blocks or inline code blocks --- .../Components/CodeBlock.razor | 15 +++++++ .../Components/CodeBlock.razor.cs | 42 +++++++++++++++++++ .../Components/CodeTabs.razor | 11 +++++ .../Components/CodeTabs.razor.cs | 28 +++++++++++++ 4 files changed, 96 insertions(+) create mode 100644 app/MindWork AI Studio/Components/CodeBlock.razor create mode 100644 app/MindWork AI Studio/Components/CodeBlock.razor.cs create mode 100644 app/MindWork AI Studio/Components/CodeTabs.razor create mode 100644 app/MindWork AI Studio/Components/CodeTabs.razor.cs diff --git a/app/MindWork AI Studio/Components/CodeBlock.razor b/app/MindWork AI Studio/Components/CodeBlock.razor new file mode 100644 index 000000000..f0a4167a1 --- /dev/null +++ b/app/MindWork AI Studio/Components/CodeBlock.razor @@ -0,0 +1,15 @@ + +@if (!this.IsInline) +{ + @if (this.ParentTabs is null) + { + +
@this.ChildContent
+
+ } +} +else +{ + @this.ChildContent +} + diff --git a/app/MindWork AI Studio/Components/CodeBlock.razor.cs b/app/MindWork AI Studio/Components/CodeBlock.razor.cs new file mode 100644 index 000000000..da6caa3b2 --- /dev/null +++ b/app/MindWork AI Studio/Components/CodeBlock.razor.cs @@ -0,0 +1,42 @@ +using Microsoft.AspNetCore.Components; +using MudBlazor.Utilities; + +namespace AIStudio.Components; + +public partial class CodeBlock : ComponentBase +{ + [Parameter] + public RenderFragment? ChildContent { get; set; } + + [Parameter] + public string? Title { get; set; } = string.Empty; + + [Parameter] + public bool IsInline { get; set; } = false; + + [CascadingParameter] + public CodeTabs? ParentTabs { get; set; } + + private static readonly string DARK_BACKGROUND_COLOR = "#2d2d2d"; + private static readonly string DARK_FOREGROUND_COLOR = "#f8f8f2"; + + protected override void OnInitialized() + { + if (this.ParentTabs is not null && this.Title is not null) + { + RenderFragment blockSelf = builder => + { + builder.OpenComponent(0); + builder.AddAttribute(1, "Title", this.Title); + builder.AddAttribute(2, "ChildContent", this.ChildContent); + builder.CloseComponent(); + }; + this.ParentTabs.RegisterBlock(this.Title, blockSelf); + } + } + + private string BlockPadding() + { + return this.ParentTabs is null ? "padding: 16px !important;" : "padding: 8px !important"; + } +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Components/CodeTabs.razor b/app/MindWork AI Studio/Components/CodeTabs.razor new file mode 100644 index 000000000..1a6dacc17 --- /dev/null +++ b/app/MindWork AI Studio/Components/CodeTabs.razor @@ -0,0 +1,11 @@ + + @foreach (var block in blocks) + { + + @block.Fragment + + } + + + @this.ChildContent + diff --git a/app/MindWork AI Studio/Components/CodeTabs.razor.cs b/app/MindWork AI Studio/Components/CodeTabs.razor.cs new file mode 100644 index 000000000..1640e51a3 --- /dev/null +++ b/app/MindWork AI Studio/Components/CodeTabs.razor.cs @@ -0,0 +1,28 @@ +using Microsoft.AspNetCore.Components; + +namespace AIStudio.Components; + +public partial class CodeTabs : ComponentBase +{ + [Parameter] + public RenderFragment? ChildContent { get; set; } + + private List blocks = new(); + private int selectedIndex = 0; + + internal void RegisterBlock(string title, RenderFragment fragment) + { + this.blocks.Add(new CodeTabItem + { + Title = title, + Fragment = fragment, + }); + this.StateHasChanged(); + } + + private class CodeTabItem + { + public string Title { get; init; } = string.Empty; + public RenderFragment Fragment { get; init; } + } +} \ No newline at end of file From 8031e1bcbe263d77b3ca5ca723719a889b95b2f3 Mon Sep 17 00:00:00 2001 From: nilsk Date: Mon, 19 May 2025 10:14:51 +0200 Subject: [PATCH 13/23] WIP: Extending dialog content to be very descriptive and beginner friendly --- .../Dialogs/PandocDialog.razor | 10 +++++-- .../Dialogs/PandocDialog.razor.cs | 1 - app/MindWork AI Studio/Pages/About.razor | 7 +++++ app/MindWork AI Studio/Pages/About.razor.cs | 10 ++++++- app/MindWork AI Studio/wwwroot/app.css | 29 +++++++++++++++++++ 5 files changed, 53 insertions(+), 4 deletions(-) diff --git a/app/MindWork AI Studio/Dialogs/PandocDialog.razor b/app/MindWork AI Studio/Dialogs/PandocDialog.razor index a420667c8..5341541c9 100644 --- a/app/MindWork AI Studio/Dialogs/PandocDialog.razor +++ b/app/MindWork AI Studio/Dialogs/PandocDialog.razor @@ -39,11 +39,17 @@ Open the folder and copy the path to the pandoc.exe file into you clipboard. - (ex. C:\Users\%USERNAME%\pandoc\pandoc-3.6.4) + (ex. ) + C:\Users\%USERNAME%\pandoc + Add the copied path to your systems environment variables and check the installation by typing - pandoc --version into your command line interface. + pandoc --version into your command line interface. + + pandoc.exe --version
Der Befehl "pandoc.exe" ist entweder falsch geschrieben oder
konnte nicht gefunden werden.
+ pandoc --version +
diff --git a/app/MindWork AI Studio/Dialogs/PandocDialog.razor.cs b/app/MindWork AI Studio/Dialogs/PandocDialog.razor.cs index 43ad24005..cf599d807 100644 --- a/app/MindWork AI Studio/Dialogs/PandocDialog.razor.cs +++ b/app/MindWork AI Studio/Dialogs/PandocDialog.razor.cs @@ -27,7 +27,6 @@ protected override async Task OnInitializedAsync() private async Task CheckPandocAvailabilityAsync() { - await Task.Delay(2500); this.isPandocAvailable = await Pandoc.CheckAvailabilityAsync(); this.showSkeleton = false; await this.InvokeAsync(this.StateHasChanged); diff --git a/app/MindWork AI Studio/Pages/About.razor b/app/MindWork AI Studio/Pages/About.razor index 5054de98d..3bec93f91 100644 --- a/app/MindWork AI Studio/Pages/About.razor +++ b/app/MindWork AI Studio/Pages/About.razor @@ -26,6 +26,13 @@ + +

Pandoc dialog

+ + Open dialog + +
+ diff --git a/app/MindWork AI Studio/Pages/About.razor.cs b/app/MindWork AI Studio/Pages/About.razor.cs index d4c2c20ed..eed9d2abc 100644 --- a/app/MindWork AI Studio/Pages/About.razor.cs +++ b/app/MindWork AI Studio/Pages/About.razor.cs @@ -1,5 +1,5 @@ using System.Reflection; - +using AIStudio.Dialogs; using AIStudio.Tools.Rust; using AIStudio.Tools.Services; @@ -18,6 +18,9 @@ public partial class About : ComponentBase [Inject] private ISnackbar Snackbar { get; init; } = null!; + [Inject] + private IDialogService DialogService { get; init; } = null!; + private static readonly Assembly ASSEMBLY = Assembly.GetExecutingAssembly(); private static readonly MetaDataAttribute META_DATA = ASSEMBLY.GetCustomAttribute()!; @@ -205,4 +208,9 @@ private async Task LatestVersion() { await Pandoc.FetchLatestVersionAsync(); } + + private async Task OpenDialog() + { + await this.DialogService.ShowAsync("install dialog"); + } } diff --git a/app/MindWork AI Studio/wwwroot/app.css b/app/MindWork AI Studio/wwwroot/app.css index 83c35efb8..c8ec7384f 100644 --- a/app/MindWork AI Studio/wwwroot/app.css +++ b/app/MindWork AI Studio/wwwroot/app.css @@ -109,4 +109,33 @@ /* Fixed the slider part of MudSplitter inside context div for inner scrolling component */ .inner-scrolling-context > .mud-splitter > .mud-slider > .mud-slider-container { padding-bottom: 12px; +} + +.code-block { + background-color: #2d2d2d; + color: #f8f8f2; + border-radius: 6px !important; + overflow: auto !important; + font-family: Consolas, "Courier New", monospace !important; + text-align: left !important; +} +.code-block pre { + margin: 0 !important; +} +.code-block code { + font-family: inherit !important; +} + +.inline-code-block { + background-color: #2d2d2d; + color: #f8f8f2; + border-radius: 6px; + font-family: Consolas, "Courier New", monospace; + text-align: left; + padding: 4px 6px; + margin: 0 2px; +} + +.no-elevation { + box-shadow: none !important; } \ No newline at end of file From 49dc84897b262f64b1c0664a58eeb3810785f3ad Mon Sep 17 00:00:00 2001 From: krut_ni Date: Mon, 26 May 2025 15:11:30 +0200 Subject: [PATCH 14/23] WIP: finished manual installation guide --- .../Dialogs/PandocDialog.razor | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/app/MindWork AI Studio/Dialogs/PandocDialog.razor b/app/MindWork AI Studio/Dialogs/PandocDialog.razor index 5341541c9..4d1638d52 100644 --- a/app/MindWork AI Studio/Dialogs/PandocDialog.razor +++ b/app/MindWork AI Studio/Dialogs/PandocDialog.razor @@ -35,20 +35,27 @@ Extract the archive to a folder of your choice. - (ex. C:\Users\%USERNAME%\pandoc) + + C:\Users\%USERNAME%\pandoc + /usr/local/bin/pandoc + /usr/local/bin/pandoc + - Open the folder and copy the path to the pandoc.exe file into you clipboard. - (ex. ) - C:\Users\%USERNAME%\pandoc - + Open the folder and copy the full path to the pandoc.exe file into you clipboard. + + C:\Users\%USERNAME%\pandoc\pandoc-3.7.0-windows-x86_64\pandoc-3.7.0 + /usr/local/bin/pandoc/pandoc-3.7.0-x86_64-macOS/pandoc-3.7.0 + /usr/local/bin/pandoc/pandoc-3.7.0-linux-amd64/pandoc-3.7.0 + Add the copied path to your systems environment variables and check the installation by typing pandoc --version into your command line interface. - pandoc.exe --version
Der Befehl "pandoc.exe" ist entweder falsch geschrieben oder
konnte nicht gefunden werden.
- pandoc --version + pandoc.exe --version
pandoc.exe 3.7.0
+ pandoc --version
pandoc.exe 3.7.0
+ pandoc --version
pandoc.exe 3.7.0
From cecf777827cc5e877b5100077dd052e91097f680 Mon Sep 17 00:00:00 2001 From: krut_ni Date: Mon, 26 May 2025 16:43:36 +0200 Subject: [PATCH 15/23] WIP: Adjusted styling for user experience and added installation with installers; Added async loading of license text --- .../Components/CodeTabs.razor | 2 +- .../Dialogs/PandocDialog.razor | 233 +++++++++++------- .../Dialogs/PandocDialog.razor.cs | 44 +++- 3 files changed, 186 insertions(+), 93 deletions(-) diff --git a/app/MindWork AI Studio/Components/CodeTabs.razor b/app/MindWork AI Studio/Components/CodeTabs.razor index 1a6dacc17..de1f8cbf9 100644 --- a/app/MindWork AI Studio/Components/CodeTabs.razor +++ b/app/MindWork AI Studio/Components/CodeTabs.razor @@ -1,4 +1,4 @@ - + @foreach (var block in blocks) { diff --git a/app/MindWork AI Studio/Dialogs/PandocDialog.razor b/app/MindWork AI Studio/Dialogs/PandocDialog.razor index 4d1638d52..67bd306c2 100644 --- a/app/MindWork AI Studio/Dialogs/PandocDialog.razor +++ b/app/MindWork AI Studio/Dialogs/PandocDialog.razor @@ -1,106 +1,157 @@ -@using ReverseMarkdown.Converters - + Install Pandoc @if (this.showInstallPage) { -
- - AI Studio relies on the third-party app Pandoc to process and retrieve data from local - Office files (ex. Word) and later other text formats like LaTeX. - - - Unfortunately Pandocs GPL license is not compatible with AI Studios licences. - Therefore you have to accept Pandocs GPL license before we can download and install Pandoc automatically for you (recommended). - However you can download it yourself manually with the instructions below. - -
- - - - Pandoc is distributed under the GNU General Public License v2 (GPL). - By clicking "Accept GPL and Install", you agree to the terms of the GPL license
and Pandoc will be installed automatically for you.
+
+ + AI Studio relies on the third-party app Pandoc to process and retrieve data from + local + Office files (ex. Word) and later other text formats like LaTeX. - Accept GPL and Install - - - If you prefer to install Pandoc yourself, please follow these steps: + Unfortunately Pandocs GPL license is not compatible with AI Studios licences. + Therefore you have to accept Pandocs GPL license before we can download and install Pandoc + automatically for you (recommended). + However you can download it yourself manually with the instructions below. - - - Accept the terms of the GPL license and download the latest archive with the button below. - - - Extract the archive to a folder of your choice. - - C:\Users\%USERNAME%\pandoc - /usr/local/bin/pandoc - /usr/local/bin/pandoc - - - - Open the folder and copy the full path to the pandoc.exe file into you clipboard. - - C:\Users\%USERNAME%\pandoc\pandoc-3.7.0-windows-x86_64\pandoc-3.7.0 - /usr/local/bin/pandoc/pandoc-3.7.0-x86_64-macOS/pandoc-3.7.0 - /usr/local/bin/pandoc/pandoc-3.7.0-linux-amd64/pandoc-3.7.0 - - - - Add the copied path to your systems environment variables and check the installation by typing - pandoc --version into your command line interface. - - pandoc.exe --version
pandoc.exe 3.7.0
- pandoc --version
pandoc.exe 3.7.0
- pandoc --version
pandoc.exe 3.7.0
-
-
-
- - Pandoc is distributed under the GNU General Public License v2 (GPL). - By clicking "Accept GPL and Download", you agree to the terms of the GPL license. - - Accept GPL and Download -
- -
- Reject GPL licence -
+ + + @if (this.isLoading) + { + + + + } + else if (!string.IsNullOrEmpty(this.licenseText)) + { +
@this.licenseText
+ } +
+
+
+ + + + Pandoc is distributed under the + GNU General Public License v2 (GPL) + + . + By clicking "Accept GPL and Install", you agree to the terms of the GPL license
and Pandoc + will be installed automatically for you.
+
+ Accept GPL and Install + +
+ + + If you prefer to install Pandoc yourself, please follow one of these two guides. Installers are only available for Windows and Mac. + + + + + + Accept the terms of the GPL license and download the latest installer with the download button below. + + pandoc-3.7.0-windows-x86_64.msi + pandoc-3.7.0.1-x86_64-macOS.pkg + pandoc-3.7.0.1-arm64-macOS.pkg + + + + Execute the installer and follow the instructions. + + + + + + + Accept the terms of the GPL license and download the latest archive with the download button below. + + + Extract the archive to a folder of your choice. + + C:\Users\%USERNAME%\pandoc + /usr/local/bin/pandoc + /usr/local/bin/pandoc + + + + Open the folder and copy the full path to the pandoc.exe file into you + clipboard. + + C:\Users\%USERNAME%\pandoc\pandoc-3.7.0-windows-x86_64\pandoc-3.7.0 + /usr/local/bin/pandoc/pandoc-3.7.0-x86_64-macOS/pandoc-3.7.0 + /usr/local/bin/pandoc/pandoc-3.7.0-linux-amd64/pandoc-3.7.0 + + + + Add the copied path to your systems environment variables and check the installation + by typing + pandoc --version + into your command line interface. + + > pandoc.exe --version
> pandoc.exe 3.7.0
+ > pandoc --version
> pandoc.exe 3.7.0
+ > pandoc --version
> pandoc.exe 3.7.0
+
+
+
+ + Pandoc is distributed under the + GNU General Public License v2 (GPL) + + . + By clicking "Accept GPL and Download", you agree to the terms of the GPL license. + + + Accept GPL and Download + +
+
+
+
+
+ Reject GPL licence +
} else { - - @if (showSkeleton) - { - - - } - else if (isPandocAvailable) - { - - - Pandoc ist auf Ihrem System verfügbar - - } - else - { - - - Pandoc ist auf Ihrem System nicht verfügbar - - Proceed - to installation - - } - + + @if (showSkeleton) + { + + + } + else if (isPandocAvailable) + { + + + Pandoc ist auf Ihrem System verfügbar + + } + else + { + + + Pandoc ist auf Ihrem System nicht verfügbar + + + Proceed + to installation + + } + }
diff --git a/app/MindWork AI Studio/Dialogs/PandocDialog.razor.cs b/app/MindWork AI Studio/Dialogs/PandocDialog.razor.cs index cf599d807..665513385 100644 --- a/app/MindWork AI Studio/Dialogs/PandocDialog.razor.cs +++ b/app/MindWork AI Studio/Dialogs/PandocDialog.razor.cs @@ -4,14 +4,20 @@ namespace AIStudio.Dialogs; public partial class PandocDialog : ComponentBase { + [Inject] + private HttpClient HttpClient { get; set; } = null!; + [CascadingParameter] private IMudDialogInstance MudDialog { get; set; } = null!; + + private static readonly string LICENCE_URI = "https://raw.githubusercontent.com/jgm/pandoc/master/COPYRIGHT"; private bool isPandocAvailable; private bool showSkeleton; private bool showInstallPage; + private string? licenseText; + private bool isLoading; - #region Overrides of ComponentBase protected override async Task OnInitializedAsync() @@ -33,4 +39,40 @@ private async Task CheckPandocAvailabilityAsync() } private void ProceedToInstallation() => this.showInstallPage = true; + + private async Task OnExpandedChanged(bool newVal) + { + if (newVal) + { + this.isLoading = true; + try + { + await Task.Delay(600); + + this.licenseText = await this.LoadLicenseTextAsync(); + } + catch (Exception ex) + { + Console.WriteLine($"Fehler beim Laden des Lizenztexts: {ex.Message}"); + this.licenseText = "Fehler beim Laden des Lizenztexts."; + } + finally + { + this.isLoading = false; + } + } + else + { + await Task.Delay(350); + this.licenseText = string.Empty; + } + } + + private async Task LoadLicenseTextAsync() + { + var response = await this.HttpClient.GetAsync(LICENCE_URI); + response.EnsureSuccessStatusCode(); + var content = await response.Content.ReadAsStringAsync(); + return content; + } } \ No newline at end of file From 012b28da27ea268f651286c699ac82d9c5475cf8 Mon Sep 17 00:00:00 2001 From: nilsk Date: Tue, 27 May 2025 01:11:54 +0200 Subject: [PATCH 16/23] Finished dialog and download of installers and archives --- .../Components/CodeTabs.razor | 2 +- .../Dialogs/PandocDialog.razor | 69 +++++++++---------- .../Dialogs/PandocDialog.razor.cs | 41 +++++++++-- app/MindWork AI Studio/Tools/Pandoc.cs | 45 +++++++++--- app/MindWork AI Studio/wwwroot/app.js | 10 +++ 5 files changed, 115 insertions(+), 52 deletions(-) diff --git a/app/MindWork AI Studio/Components/CodeTabs.razor b/app/MindWork AI Studio/Components/CodeTabs.razor index de1f8cbf9..7223cecb6 100644 --- a/app/MindWork AI Studio/Components/CodeTabs.razor +++ b/app/MindWork AI Studio/Components/CodeTabs.razor @@ -1,4 +1,4 @@ - + @foreach (var block in blocks) { diff --git a/app/MindWork AI Studio/Dialogs/PandocDialog.razor b/app/MindWork AI Studio/Dialogs/PandocDialog.razor index 67bd306c2..54989d8b0 100644 --- a/app/MindWork AI Studio/Dialogs/PandocDialog.razor +++ b/app/MindWork AI Studio/Dialogs/PandocDialog.razor @@ -33,39 +33,44 @@
- + Pandoc is distributed under the - GNU General Public License v2 (GPL) - - . + GNU General Public License v2 (GPL). By clicking "Accept GPL and Install", you agree to the terms of the GPL license
and Pandoc will be installed automatically for you.
- Accept GPL and Install + + Accept GPL and Install
- + If you prefer to install Pandoc yourself, please follow one of these two guides. Installers are only available for Windows and Mac. - + Accept the terms of the GPL license and download the latest installer with the download button below. + Eventually you need to allow the download of the installer in the download window. - pandoc-3.7.0-windows-x86_64.msi - pandoc-3.7.0.1-x86_64-macOS.pkg - pandoc-3.7.0.1-arm64-macOS.pkg + pandoc-@(PANDOC_VERSION)-windows-x86_64.msi + pandoc-@(PANDOC_VERSION)-x86_64-macOS.pkg + pandoc-@(PANDOC_VERSION)-arm64-macOS.pkg Execute the installer and follow the instructions. + + Pandoc is distributed under the GNU General Public License v2 (GPL). + By clicking "Accept GPL and download installer", you agree to the terms of the GPL license.
+
+ + Accept GPL and download installer +
@@ -81,36 +86,31 @@ - Open the folder and copy the full path to the pandoc.exe file into you + Open the folder and copy the full path to the pandoc.exe file into your clipboard. - C:\Users\%USERNAME%\pandoc\pandoc-3.7.0-windows-x86_64\pandoc-3.7.0 - /usr/local/bin/pandoc/pandoc-3.7.0-x86_64-macOS/pandoc-3.7.0 - /usr/local/bin/pandoc/pandoc-3.7.0-linux-amd64/pandoc-3.7.0 + C:\Users\%USERNAME%\pandoc\pandoc-@(PANDOC_VERSION) + /usr/local/bin/pandoc/pandoc-@(PANDOC_VERSION) + /usr/local/bin/pandoc/pandoc-@(PANDOC_VERSION) Add the copied path to your systems environment variables and check the installation - by typing - pandoc --version + by typing
pandoc --version into your command line interface. - > pandoc.exe --version
> pandoc.exe 3.7.0
- > pandoc --version
> pandoc.exe 3.7.0
- > pandoc --version
> pandoc.exe 3.7.0
+ > pandoc.exe --version
> pandoc.exe @(PANDOC_VERSION)
+ > pandoc --version
> pandoc.exe @(PANDOC_VERSION)
+ > pandoc --version
> pandoc.exe @(PANDOC_VERSION)
- Pandoc is distributed under the - GNU General Public License v2 (GPL) - - . - By clicking "Accept GPL and Download", you agree to the terms of the GPL license. + Pandoc is distributed under the GNU General Public License v2 (GPL). + By clicking "Accept GPL and archive", you agree to the terms of the GPL license.
- - Accept GPL and Download + + Accept GPL and download archive
@@ -122,8 +122,7 @@ } else { - + @if (showSkeleton) { - + + Pandoc ist auf Ihrem System nicht verfügbar - Proceed - to installation + Proceed to installation } diff --git a/app/MindWork AI Studio/Dialogs/PandocDialog.razor.cs b/app/MindWork AI Studio/Dialogs/PandocDialog.razor.cs index 665513385..19a2fcaab 100644 --- a/app/MindWork AI Studio/Dialogs/PandocDialog.razor.cs +++ b/app/MindWork AI Studio/Dialogs/PandocDialog.razor.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Components; +using AIStudio.Tools.Services; +using Microsoft.AspNetCore.Components; namespace AIStudio.Dialogs; @@ -7,10 +8,17 @@ public partial class PandocDialog : ComponentBase [Inject] private HttpClient HttpClient { get; set; } = null!; + [Inject] + private RustService RustService { get; init; } = null!; + + [Inject] + protected IJSRuntime JsRuntime { get; init; } = null!; + [CascadingParameter] private IMudDialogInstance MudDialog { get; set; } = null!; private static readonly string LICENCE_URI = "https://raw.githubusercontent.com/jgm/pandoc/master/COPYRIGHT"; + private static string PANDOC_VERSION = "1.0.0"; private bool isPandocAvailable; private bool showSkeleton; @@ -25,6 +33,7 @@ protected override async Task OnInitializedAsync() await base.OnInitializedAsync(); this.showSkeleton = true; await this.CheckPandocAvailabilityAsync(); + PANDOC_VERSION = await Pandoc.FetchLatestVersionAsync(); } #endregion @@ -33,13 +42,36 @@ protected override async Task OnInitializedAsync() private async Task CheckPandocAvailabilityAsync() { - this.isPandocAvailable = await Pandoc.CheckAvailabilityAsync(); + this.isPandocAvailable = await Pandoc.CheckAvailabilityAsync(false); this.showSkeleton = false; await this.InvokeAsync(this.StateHasChanged); } + private async Task InstallPandocAsync() => await Pandoc.InstallAsync(this.RustService); + private void ProceedToInstallation() => this.showInstallPage = true; - + + private async Task GetInstaller() + { + var uri = await Pandoc.GenerateInstallerUriAsync(); + var filename = this.FilenameFromUri(uri); + await this.JsRuntime.InvokeVoidAsync("triggerDownload", uri, filename); + + } + + private async Task GetArchive() + { + var uri = await Pandoc.GenerateUriAsync(); + var filename = this.FilenameFromUri(uri); + await this.JsRuntime.InvokeVoidAsync("triggerDownload", uri, filename); + } + + private string FilenameFromUri(string uri) + { + var index = uri.LastIndexOf('/'); + return uri[(index + 1)..]; + } + private async Task OnExpandedChanged(bool newVal) { if (newVal) @@ -53,8 +85,7 @@ private async Task OnExpandedChanged(bool newVal) } catch (Exception ex) { - Console.WriteLine($"Fehler beim Laden des Lizenztexts: {ex.Message}"); - this.licenseText = "Fehler beim Laden des Lizenztexts."; + this.licenseText = "Error loading license text, please consider following the links to the GPL."; } finally { diff --git a/app/MindWork AI Studio/Tools/Pandoc.cs b/app/MindWork AI Studio/Tools/Pandoc.cs index d89a430a6..f48e5ea99 100644 --- a/app/MindWork AI Studio/Tools/Pandoc.cs +++ b/app/MindWork AI Studio/Tools/Pandoc.cs @@ -10,7 +10,7 @@ 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/"; + private static readonly string DOWNLOAD_URL = "https://github.com/jgm/pandoc/releases/download"; private static readonly string LATEST_URL = "https://github.com/jgm/pandoc/releases/latest"; private static readonly Version MINIMUM_REQUIRED_VERSION = new (3, 6); private static readonly Version FALLBACK_VERSION = new (3, 6, 4); @@ -20,7 +20,7 @@ public static partial class Pandoc /// 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 CheckAvailabilityAsync() + public static async Task CheckAvailabilityAsync(bool showMessages = true) { try { @@ -35,7 +35,8 @@ public static async Task CheckAvailabilityAsync() using var process = Process.Start(startInfo); if (process == null) { - await MessageBus.INSTANCE.SendError(new (Icons.Material.Filled.Help, "The pandoc process could not be started.")); + if (showMessages) + 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; } @@ -44,7 +45,8 @@ public static async Task CheckAvailabilityAsync() await process.WaitForExitAsync(); if (process.ExitCode != 0) { - await MessageBus.INSTANCE.SendError(new (Icons.Material.Filled.Error, $"The pandoc process exited unexpectedly.")); + if (showMessages) + 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; } @@ -52,7 +54,8 @@ public static async Task CheckAvailabilityAsync() var versionMatch = PandocRegex().Match(output); if (!versionMatch.Success) { - await MessageBus.INSTANCE.SendError(new (Icons.Material.Filled.Terminal, $"pandoc --version returned an invalid format.")); + if (showMessages) + 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; } @@ -63,18 +66,21 @@ public static async Task CheckAvailabilityAsync() if (installedVersion >= MINIMUM_REQUIRED_VERSION) { - await MessageBus.INSTANCE.SendSuccess(new(Icons.Material.Filled.CheckCircle, $"Pandoc {installedVersion.ToString()} is installed.")); + if (showMessages) + 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()}).")); + if (showMessages) + 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 e) { - await MessageBus.INSTANCE.SendError(new (@Icons.Material.Filled.AppsOutage, "Pandoc is not installed.")); + if (showMessages) + 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; } @@ -177,7 +183,7 @@ public static async Task FetchLatestVersionAsync() { } // win arm not available - private static async Task GenerateUriAsync() + public static async Task GenerateUriAsync() { var version = await FetchLatestVersionAsync(); var baseUri = $"{DOWNLOAD_URL}/{version}/pandoc-{version}-"; @@ -191,6 +197,25 @@ private static async Task GenerateUriAsync() _ => string.Empty, }; } + + public static async Task GenerateInstallerUriAsync() + { + var version = await FetchLatestVersionAsync(); + var baseUri = $"{DOWNLOAD_URL}/{version}/pandoc-{version}-"; + + switch (CPU_ARCHITECTURE) + { + case "win-x64": + return $"{baseUri}windows-x86_64.msi"; + case "osx-x64": + return $"{baseUri}x86_64-macOS.pkg"; + case "osx-arm64": + return $"{baseUri}arm64-macOS.pkg\n"; + default: + await MessageBus.INSTANCE.SendError(new (Icons.Material.Filled.Terminal, $"Installers are not available on {CPU_ARCHITECTURE} systems.")); + return string.Empty; + } + } /// /// Returns the name of the pandoc executable based on the running operating system @@ -200,6 +225,6 @@ private static async Task GenerateUriAsync() [GeneratedRegex(@"pandoc(?:\.exe)?\s*([0-9]+\.[0-9]+)")] private static partial Regex PandocRegex(); - [GeneratedRegex(@"pandoc(?:\.exe)?\s*([0-9]+\.[0-9]+\.[0-9]+)")] + [GeneratedRegex(@"pandoc(?:\.exe)?\s*([0-9]+\.[0-9]+\.[0-9]+(?:\.[0-9]+)?)")] private static partial Regex VersionRegex(); } \ No newline at end of file diff --git a/app/MindWork AI Studio/wwwroot/app.js b/app/MindWork AI Studio/wwwroot/app.js index aa6b8e2be..bf1eb31c1 100644 --- a/app/MindWork AI Studio/wwwroot/app.js +++ b/app/MindWork AI Studio/wwwroot/app.js @@ -25,4 +25,14 @@ window.clearDiv = function (divName) { window.scrollToBottom = function(element) { element.scrollIntoView({ behavior: 'smooth', block: 'end', inline: 'nearest' }); +} + +window.triggerDownload = function(url, filename) { + const a = document.createElement('a'); + a.href = url; + a.setAttribute('download', filename); + a.style.display = 'none'; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); } \ No newline at end of file From 81b276be692f9678e392658363aa3fa4f1ecf637 Mon Sep 17 00:00:00 2001 From: nilsk Date: Tue, 27 May 2025 14:14:37 +0200 Subject: [PATCH 17/23] Finished dialog; installation and availability check logic --- .../Dialogs/PandocDialog.razor | 33 +++++----- .../Dialogs/PandocDialog.razor.cs | 35 +++++++++-- app/MindWork AI Studio/Tools/Pandoc.cs | 62 ++++++++++++------- 3 files changed, 84 insertions(+), 46 deletions(-) diff --git a/app/MindWork AI Studio/Dialogs/PandocDialog.razor b/app/MindWork AI Studio/Dialogs/PandocDialog.razor index 54989d8b0..a9b64504a 100644 --- a/app/MindWork AI Studio/Dialogs/PandocDialog.razor +++ b/app/MindWork AI Studio/Dialogs/PandocDialog.razor @@ -7,14 +7,14 @@ {
- AI Studio relies on the third-party app Pandoc to process and retrieve data from - local + AI Studio relies on the free and open-sourced third-party app Pandoc to process and retrieve data from local Office files (ex. Word) and later other text formats like LaTeX. - Unfortunately Pandocs GPL license is not compatible with AI Studios licences. - Therefore you have to accept Pandocs GPL license before we can download and install Pandoc - automatically for you (recommended). + Unfortunately Pandoc's GPL license is not compatible with AI Studios licences, nonetheless software under GPL is generally free to use and + free of charge as well. + Therefore you have to accept Pandoc's GPL license before we can download and install Pandoc for free + automatically for you (recommended). However you can download it yourself manually with the instructions below. @@ -27,7 +27,7 @@ } else if (!string.IsNullOrEmpty(this.licenseText)) { -
@this.licenseText
+ @this.licenseText }
@@ -38,10 +38,10 @@ Pandoc is distributed under the GNU General Public License v2 (GPL). By clicking "Accept GPL and Install", you agree to the terms of the GPL license
and Pandoc - will be installed automatically for you.
+ will be installed automatically for you. Software under GPL is free of charge and free to use.
- Accept GPL and Install + Accept GPL and install for free @@ -51,7 +51,7 @@ - + Accept the terms of the GPL license and download the latest installer with the download button below. Eventually you need to allow the download of the installer in the download window. @@ -66,7 +66,7 @@ Pandoc is distributed under the GNU General Public License v2 (GPL). - By clicking "Accept GPL and download installer", you agree to the terms of the GPL license.
+ By clicking "Accept GPL and download installer", you agree to the terms of the GPL license. Software under GPL is free of charge and free to use.
Accept GPL and download installer @@ -74,10 +74,10 @@
- + Accept the terms of the GPL license and download the latest archive with the download button below. - + Extract the archive to a folder of your choice. C:\Users\%USERNAME%\pandoc @@ -85,7 +85,7 @@ /usr/local/bin/pandoc - + Open the folder and copy the full path to the pandoc.exe file into your clipboard. @@ -107,7 +107,7 @@ Pandoc is distributed under the GNU General Public License v2 (GPL). - By clicking "Accept GPL and archive", you agree to the terms of the GPL license.
+ By clicking "Accept GPL and archive", you agree to the terms of the GPL license. Software under GPL is free of charge and free to use.
Accept GPL and download archive @@ -117,7 +117,7 @@
- Reject GPL licence + Reject GPL licence
} else @@ -151,7 +151,4 @@ } - - - \ No newline at end of file diff --git a/app/MindWork AI Studio/Dialogs/PandocDialog.razor.cs b/app/MindWork AI Studio/Dialogs/PandocDialog.razor.cs index 19a2fcaab..d1ca7392b 100644 --- a/app/MindWork AI Studio/Dialogs/PandocDialog.razor.cs +++ b/app/MindWork AI Studio/Dialogs/PandocDialog.razor.cs @@ -14,6 +14,9 @@ public partial class PandocDialog : ComponentBase [Inject] protected IJSRuntime JsRuntime { get; init; } = null!; + [Inject] + private IDialogService DialogService { get; init; } = null!; + [CascadingParameter] private IMudDialogInstance MudDialog { get; set; } = null!; @@ -42,12 +45,17 @@ protected override async Task OnInitializedAsync() private async Task CheckPandocAvailabilityAsync() { - this.isPandocAvailable = await Pandoc.CheckAvailabilityAsync(false); + this.isPandocAvailable = await Pandoc.CheckAvailabilityAsync(this.RustService); this.showSkeleton = false; await this.InvokeAsync(this.StateHasChanged); } - private async Task InstallPandocAsync() => await Pandoc.InstallAsync(this.RustService); + private async Task InstallPandocAsync() + { + await Pandoc.InstallAsync(this.RustService); + this.MudDialog.Close(DialogResult.Ok(true)); + await this.DialogService.ShowAsync("pandoc dialog"); + } private void ProceedToInstallation() => this.showInstallPage = true; @@ -56,7 +64,6 @@ private async Task GetInstaller() var uri = await Pandoc.GenerateInstallerUriAsync(); var filename = this.FilenameFromUri(uri); await this.JsRuntime.InvokeVoidAsync("triggerDownload", uri, filename); - } private async Task GetArchive() @@ -66,15 +73,33 @@ private async Task GetArchive() await this.JsRuntime.InvokeVoidAsync("triggerDownload", uri, filename); } + private async Task RejectLicense() + { + var message = "Pandoc is open-source and free of charge, but if you reject Pandoc's license, it can not be installed and some of AIStudios data retrieval features will be disabled (e.g. using Office files like Word)." + + "This decision can be revoked at any time. Are you sure you want to reject the license?"; + + var dialogParameters = new DialogParameters + { + { "Message", message }, + }; + + var dialogReference = await this.DialogService.ShowAsync("Reject Pandoc's licence", dialogParameters, DialogOptions.FULLSCREEN); + var dialogResult = await dialogReference.Result; + if (dialogResult is null || dialogResult.Canceled) + dialogReference.Close(); + else + this.Cancel(); + } + private string FilenameFromUri(string uri) { var index = uri.LastIndexOf('/'); return uri[(index + 1)..]; } - private async Task OnExpandedChanged(bool newVal) + private async Task OnExpandedChanged(bool isExpanded) { - if (newVal) + if (isExpanded) { this.isLoading = true; try diff --git a/app/MindWork AI Studio/Tools/Pandoc.cs b/app/MindWork AI Studio/Tools/Pandoc.cs index f48e5ea99..b989d2b9a 100644 --- a/app/MindWork AI Studio/Tools/Pandoc.cs +++ b/app/MindWork AI Studio/Tools/Pandoc.cs @@ -20,8 +20,27 @@ public static partial class Pandoc /// 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 CheckAvailabilityAsync(bool showMessages = true) + public static async Task CheckAvailabilityAsync(RustService rustService, bool showMessages = true) { + var installDir = await GetPandocDataFolder(rustService); + var subdirectories = Directory.GetDirectories(installDir); + + if (subdirectories.Length > 1) + { + await InstallAsync(rustService); + return true; + } + + var hasPandoc = false; + foreach (var subdirectory in subdirectories) + { + if (subdirectory.Contains("pandoc")) + hasPandoc = true; + } + + if (hasPandoc) + return true; + try { var startInfo = new ProcessStartInfo @@ -88,9 +107,8 @@ public static async Task CheckAvailabilityAsync(bool showMessages = true) public static async Task InstallAsync(RustService rustService) { - var dataDir = await rustService.GetDataDirectory(); - await MessageBus.INSTANCE.SendError(new (Icons.Material.Filled.Help, $"{dataDir}")); - var installDir = Path.Join(dataDir, "pandoc"); + var installDir = await GetPandocDataFolder(rustService); + ClearFolder(installDir); try { @@ -126,35 +144,31 @@ public static async Task InstallAsync(RustService rustService) LOG.LogError("Pandoc was not installed, the download archive is unknown:\n {Uri}", uri); return; } - - - 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.")); + $"Pandoc {await FetchLatestVersionAsync()} was installed successfully.")); } catch (Exception ex) { Console.WriteLine($"Fehler: {ex.Message}"); } } - - private static async Task ExtractZipAsync(string zipPath, string targetDir) + + private static void ClearFolder(string path) { + if (!Directory.Exists(path)) return; + try + { + foreach (var dir in Directory.GetDirectories(path)) + { + Directory.Delete(dir, true); + } + } + catch (Exception ex) + { + LOG.LogError(ex, "Error clearing pandoc folder."); + } } public static async Task FetchLatestVersionAsync() { @@ -222,6 +236,8 @@ public static async Task GenerateInstallerUriAsync() /// private static string GetPandocExecutableName() => RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "pandoc.exe" : "pandoc"; + private static async Task GetPandocDataFolder(RustService rustService) => Path.Join(await rustService.GetDataDirectory(), "pandoc"); + [GeneratedRegex(@"pandoc(?:\.exe)?\s*([0-9]+\.[0-9]+)")] private static partial Regex PandocRegex(); From b3108fd5a90324a13bd3013a1fd023c5ef6aa21c Mon Sep 17 00:00:00 2001 From: nilsk Date: Tue, 27 May 2025 16:06:15 +0200 Subject: [PATCH 18/23] finished pandoc logic; removed warnings --- .../Components/CodeBlock.razor.cs | 3 - .../Components/CodeTabs.razor.cs | 2 +- .../Dialogs/PandocDialog.razor.cs | 4 +- app/MindWork AI Studio/Tools/Pandoc.cs | 91 +++++++++++++------ 4 files changed, 68 insertions(+), 32 deletions(-) diff --git a/app/MindWork AI Studio/Components/CodeBlock.razor.cs b/app/MindWork AI Studio/Components/CodeBlock.razor.cs index da6caa3b2..1535bb00b 100644 --- a/app/MindWork AI Studio/Components/CodeBlock.razor.cs +++ b/app/MindWork AI Studio/Components/CodeBlock.razor.cs @@ -16,9 +16,6 @@ public partial class CodeBlock : ComponentBase [CascadingParameter] public CodeTabs? ParentTabs { get; set; } - - private static readonly string DARK_BACKGROUND_COLOR = "#2d2d2d"; - private static readonly string DARK_FOREGROUND_COLOR = "#f8f8f2"; protected override void OnInitialized() { diff --git a/app/MindWork AI Studio/Components/CodeTabs.razor.cs b/app/MindWork AI Studio/Components/CodeTabs.razor.cs index 1640e51a3..8c4abb9fb 100644 --- a/app/MindWork AI Studio/Components/CodeTabs.razor.cs +++ b/app/MindWork AI Studio/Components/CodeTabs.razor.cs @@ -23,6 +23,6 @@ internal void RegisterBlock(string title, RenderFragment fragment) private class CodeTabItem { public string Title { get; init; } = string.Empty; - public RenderFragment Fragment { get; init; } + public RenderFragment Fragment { get; init; } = null!; } } \ No newline at end of file diff --git a/app/MindWork AI Studio/Dialogs/PandocDialog.razor.cs b/app/MindWork AI Studio/Dialogs/PandocDialog.razor.cs index d1ca7392b..9226074c9 100644 --- a/app/MindWork AI Studio/Dialogs/PandocDialog.razor.cs +++ b/app/MindWork AI Studio/Dialogs/PandocDialog.razor.cs @@ -20,6 +20,7 @@ public partial class PandocDialog : ComponentBase [CascadingParameter] private IMudDialogInstance MudDialog { get; set; } = null!; + private static readonly ILogger LOG = Program.LOGGER_FACTORY.CreateLogger("PandocDialog"); private static readonly string LICENCE_URI = "https://raw.githubusercontent.com/jgm/pandoc/master/COPYRIGHT"; private static string PANDOC_VERSION = "1.0.0"; @@ -110,7 +111,8 @@ private async Task OnExpandedChanged(bool isExpanded) } catch (Exception ex) { - this.licenseText = "Error loading license text, please consider following the links to the GPL."; + this.licenseText = "Error loading license text, please consider following the links to read the GPL."; + LOG.LogError("Error loading GPL license text:\n{ErrorMessage}", ex.Message); } finally { diff --git a/app/MindWork AI Studio/Tools/Pandoc.cs b/app/MindWork AI Studio/Tools/Pandoc.cs index b989d2b9a..d87c835c3 100644 --- a/app/MindWork AI Studio/Tools/Pandoc.cs +++ b/app/MindWork AI Studio/Tools/Pandoc.cs @@ -9,16 +9,18 @@ namespace AIStudio.Tools; public static partial class Pandoc { - private static readonly ILogger LOG = Program.LOGGER_FACTORY.CreateLogger("PluginFactory"); + private static readonly ILogger LOG = Program.LOGGER_FACTORY.CreateLogger("PandocService"); private static readonly string DOWNLOAD_URL = "https://github.com/jgm/pandoc/releases/download"; private static readonly string LATEST_URL = "https://github.com/jgm/pandoc/releases/latest"; private static readonly Version MINIMUM_REQUIRED_VERSION = new (3, 6); - private static readonly Version FALLBACK_VERSION = new (3, 6, 4); + private static readonly Version FALLBACK_VERSION = new (3, 7, 0, 1); private static readonly string CPU_ARCHITECTURE = "win-x64"; /// - /// Checks if pandoc is available on the system and can be started as a process + /// Checks if pandoc is available on the system and can be started as a process or present in AiStudio's data dir /// + /// Global rust service to access file system and data dir + /// Controls if snackbars are shown to the user /// True, if pandoc is available and the minimum required version is met, else False. public static async Task CheckAvailabilityAsync(RustService rustService, bool showMessages = true) { @@ -30,16 +32,8 @@ public static async Task CheckAvailabilityAsync(RustService rustService, b await InstallAsync(rustService); return true; } - - var hasPandoc = false; - foreach (var subdirectory in subdirectories) - { - if (subdirectory.Contains("pandoc")) - hasPandoc = true; - } - if (hasPandoc) - return true; + if (HasPandoc(installDir)) return true; try { @@ -70,7 +64,7 @@ public static async Task CheckAvailabilityAsync(RustService rustService, b return false; } - var versionMatch = PandocRegex().Match(output); + var versionMatch = PandocCmdRegex().Match(output); if (!versionMatch.Success) { if (showMessages) @@ -78,11 +72,9 @@ public static async Task CheckAvailabilityAsync(RustService rustService, b 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 installedVersion = new Version(major, minor); - + var versions = versionMatch.Groups[1].Value; + var installedVersion = Version.Parse(versions); + if (installedVersion >= MINIMUM_REQUIRED_VERSION) { if (showMessages) @@ -104,7 +96,36 @@ public static async Task CheckAvailabilityAsync(RustService rustService, b return false; } } + + private static bool HasPandoc(string pandocDirectory) + { + try + { + var subdirectories = Directory.GetDirectories(pandocDirectory); + + foreach (var subdirectory in subdirectories) + { + var pandocPath = Path.Combine(subdirectory, "pandoc.exe"); + if (File.Exists(pandocPath)) + { + return true; + } + } + + return false; + } + catch (Exception ex) + { + LOG.LogInformation("Pandoc is not installed in the data directory and might have thrown and error:\n{ErrorMessage}", ex.Message); + return false; + } + } + /// + /// Automatically decompresses the latest pandoc archive into AiStudio's data directory + /// + /// Global rust service to access file system and data dir + /// None public static async Task InstallAsync(RustService rustService) { var installDir = await GetPandocDataFolder(rustService); @@ -136,7 +157,10 @@ public static async Task InstallAsync(RustService rustService) } else if (uri.Contains(".tar.gz")) { - Console.WriteLine("is zip"); + var tempTarPath = Path.Join(Path.GetTempPath(), "pandoc.tar.gz"); + await File.WriteAllBytesAsync(tempTarPath, fileBytes); + ZipFile.ExtractToDirectory(tempTarPath, installDir); + File.Delete(tempTarPath); } else { @@ -171,20 +195,25 @@ private static void ClearFolder(string path) } } + /// + /// Asynchronously fetch the content from Pandoc's latest release page and extract the latest version number + /// + /// Version numbers can have the following formats: x.x, x.x.x or x.x.x.x + /// Latest Pandoc version number public static async Task FetchLatestVersionAsync() { using var client = new HttpClient(); var response = await client.GetAsync(LATEST_URL); if (!response.IsSuccessStatusCode) { - LOG.LogError("Code {StatusCode}: Could not fetch pandocs latest page:\n {Response}", response.StatusCode, response.RequestMessage); + LOG.LogError("Code {StatusCode}: Could not fetch Pandoc's latest page:\n {Response}", response.StatusCode, response.RequestMessage); await MessageBus.INSTANCE.SendWarning(new (Icons.Material.Filled.Warning, $"The latest pandoc version was not found, installing version {FALLBACK_VERSION.ToString()} instead.")); return FALLBACK_VERSION.ToString(); } var htmlContent = await response.Content.ReadAsStringAsync(); - var versionMatch = VersionRegex().Match(htmlContent); + var versionMatch = LatestVersionRegex().Match(htmlContent); if (!versionMatch.Success) { LOG.LogError("The latest version regex returned nothing:\n {Value}", versionMatch.Groups.ToString()); @@ -196,7 +225,10 @@ public static async Task FetchLatestVersionAsync() { return version; } - // win arm not available + /// + /// Reads the systems architecture to find the correct archive + /// + /// Full URI to the right archive in Pandoc's repo public static async Task GenerateUriAsync() { var version = await FetchLatestVersionAsync(); @@ -212,6 +244,10 @@ public static async Task GenerateUriAsync() }; } + /// + /// Reads the systems architecture to find the correct Pandoc installer + /// + /// Full URI to the right installer in Pandoc's repo public static async Task GenerateInstallerUriAsync() { var version = await FetchLatestVersionAsync(); @@ -232,15 +268,16 @@ public static async Task GenerateInstallerUriAsync() } /// - /// Returns the name of the pandoc executable based on the running operating system + /// Reads the os platform to determine the used executable name /// + /// Name of the pandoc executable private static string GetPandocExecutableName() => RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "pandoc.exe" : "pandoc"; private static async Task GetPandocDataFolder(RustService rustService) => Path.Join(await rustService.GetDataDirectory(), "pandoc"); - [GeneratedRegex(@"pandoc(?:\.exe)?\s*([0-9]+\.[0-9]+)")] - private static partial Regex PandocRegex(); + [GeneratedRegex(@"pandoc(?:\.exe)?\s*([0-9]+\.[0-9]+(?:\.[0-9]+)?(?:\.[0-9]+)?)")] + private static partial Regex PandocCmdRegex(); - [GeneratedRegex(@"pandoc(?:\.exe)?\s*([0-9]+\.[0-9]+\.[0-9]+(?:\.[0-9]+)?)")] - private static partial Regex VersionRegex(); + [GeneratedRegex(@"pandoc(?:\.exe)?\s*([0-9]+\.[0-9]+(?:\.[0-9]+)?(?:\.[0-9]+)?)")] + private static partial Regex LatestVersionRegex(); } \ No newline at end of file From 647bc0075dc69f636e18ed617d4f09d02a395e9a Mon Sep 17 00:00:00 2001 From: nilsk Date: Tue, 27 May 2025 16:06:32 +0200 Subject: [PATCH 19/23] removed debugging from about page --- app/MindWork AI Studio/Pages/About.razor | 30 ---------------- app/MindWork AI Studio/Pages/About.razor.cs | 39 --------------------- 2 files changed, 69 deletions(-) diff --git a/app/MindWork AI Studio/Pages/About.razor b/app/MindWork AI Studio/Pages/About.razor index 3bec93f91..32c8b5df1 100644 --- a/app/MindWork AI Studio/Pages/About.razor +++ b/app/MindWork AI Studio/Pages/About.razor @@ -3,36 +3,6 @@
About MindWork AI Studio - - -

Pandoc Verfügbarkeit prüfen

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

@statusMessage

-
- - -

Pandoc Installation

- - Install Pandoc - -
- - -

Latest Version

- - Version - -
- - -

Pandoc dialog

- - Open dialog - -
- diff --git a/app/MindWork AI Studio/Pages/About.razor.cs b/app/MindWork AI Studio/Pages/About.razor.cs index eed9d2abc..e2f277dee 100644 --- a/app/MindWork AI Studio/Pages/About.razor.cs +++ b/app/MindWork AI Studio/Pages/About.razor.cs @@ -18,9 +18,6 @@ public partial class About : ComponentBase [Inject] private ISnackbar Snackbar { get; init; } = null!; - [Inject] - private IDialogService DialogService { get; init; } = null!; - private static readonly Assembly ASSEMBLY = Assembly.GetExecutingAssembly(); private static readonly MetaDataAttribute META_DATA = ASSEMBLY.GetCustomAttribute()!; @@ -177,40 +174,4 @@ private async Task CheckForUpdate() { 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.CheckAvailabilityAsync(); - 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.InstallAsync(this.RustService); - } - - private async Task LatestVersion() - { - await Pandoc.FetchLatestVersionAsync(); - } - - private async Task OpenDialog() - { - await this.DialogService.ShowAsync("install dialog"); - } } From 11ddaf7a147bbc84b52fb7d6145197bb00eadc35 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Thu, 29 May 2025 11:59:45 +0200 Subject: [PATCH 20/23] Refactor message types for clarity and consistency --- .../Layout/MainLayout.razor.cs | 6 +-- .../Tools/DataErrorMessage.cs | 16 +++++++ .../Tools/DataSuccessMessage.cs | 16 +++++++ .../Tools/DataWarningMessage.cs | 16 +++++++ app/MindWork AI Studio/Tools/Error.cs | 46 ------------------- app/MindWork AI Studio/Tools/MessageBus.cs | 6 +-- 6 files changed, 54 insertions(+), 52 deletions(-) create mode 100644 app/MindWork AI Studio/Tools/DataErrorMessage.cs create mode 100644 app/MindWork AI Studio/Tools/DataSuccessMessage.cs create mode 100644 app/MindWork AI Studio/Tools/DataWarningMessage.cs delete mode 100644 app/MindWork AI Studio/Tools/Error.cs diff --git a/app/MindWork AI Studio/Layout/MainLayout.razor.cs b/app/MindWork AI Studio/Layout/MainLayout.razor.cs index c95a69d48..586772f29 100644 --- a/app/MindWork AI Studio/Layout/MainLayout.razor.cs +++ b/app/MindWork AI Studio/Layout/MainLayout.razor.cs @@ -175,19 +175,19 @@ public async Task ProcessMessage(ComponentBase? sendingComponent, Event trigg break; case Event.SHOW_SUCCESS: - if (data is Success success) + if (data is DataSuccessMessage success) success.Show(this.Snackbar); break; case Event.SHOW_ERROR: - if (data is Error error) + if (data is DataErrorMessage error) error.Show(this.Snackbar); break; case Event.SHOW_WARNING: - if (data is Warning warning) + if (data is DataWarningMessage warning) warning.Show(this.Snackbar); break; diff --git a/app/MindWork AI Studio/Tools/DataErrorMessage.cs b/app/MindWork AI Studio/Tools/DataErrorMessage.cs new file mode 100644 index 000000000..d87d1fd00 --- /dev/null +++ b/app/MindWork AI Studio/Tools/DataErrorMessage.cs @@ -0,0 +1,16 @@ +namespace AIStudio.Tools; + +public readonly record struct DataErrorMessage(string Icon, string Message) +{ + public void Show(ISnackbar snackbar) + { + var icon = this.Icon; + snackbar.Add(this.Message, Severity.Error, config => + { + config.Icon = icon; + config.IconSize = Size.Large; + config.HideTransitionDuration = 600; + config.VisibleStateDuration = 14_000; + }); + } +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/DataSuccessMessage.cs b/app/MindWork AI Studio/Tools/DataSuccessMessage.cs new file mode 100644 index 000000000..2ececa184 --- /dev/null +++ b/app/MindWork AI Studio/Tools/DataSuccessMessage.cs @@ -0,0 +1,16 @@ +namespace AIStudio.Tools; + +public readonly record struct DataSuccessMessage(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/DataWarningMessage.cs b/app/MindWork AI Studio/Tools/DataWarningMessage.cs new file mode 100644 index 000000000..8ba3e59e2 --- /dev/null +++ b/app/MindWork AI Studio/Tools/DataWarningMessage.cs @@ -0,0 +1,16 @@ +namespace AIStudio.Tools; + +public readonly record struct DataWarningMessage(string Icon, string Message) +{ + public void Show(ISnackbar snackbar) + { + var icon = this.Icon; + snackbar.Add(this.Message, Severity.Warning, config => + { + config.Icon = icon; + config.IconSize = Size.Large; + config.HideTransitionDuration = 600; + config.VisibleStateDuration = 12_000; + }); + } +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/Error.cs b/app/MindWork AI Studio/Tools/Error.cs deleted file mode 100644 index a9e3af815..000000000 --- a/app/MindWork AI Studio/Tools/Error.cs +++ /dev/null @@ -1,46 +0,0 @@ -namespace AIStudio.Tools; - -public readonly record struct Error(string Icon, string Message) -{ - public void Show(ISnackbar snackbar) - { - var icon = this.Icon; - snackbar.Add(this.Message, Severity.Error, config => - { - config.Icon = icon; - config.IconSize = Size.Large; - config.HideTransitionDuration = 600; - config.VisibleStateDuration = 14_000; - }); - } -} - -public readonly record struct Warning(string Icon, string Message) -{ - public void Show(ISnackbar snackbar) - { - var icon = this.Icon; - snackbar.Add(this.Message, Severity.Warning, config => - { - config.Icon = icon; - config.IconSize = Size.Large; - config.HideTransitionDuration = 600; - config.VisibleStateDuration = 12_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/MessageBus.cs b/app/MindWork AI Studio/Tools/MessageBus.cs index 73344f2e3..7ed7cf72d 100644 --- a/app/MindWork AI Studio/Tools/MessageBus.cs +++ b/app/MindWork AI Studio/Tools/MessageBus.cs @@ -66,11 +66,11 @@ public async Task SendMessage(ComponentBase? sendingComponent, Event triggere } } - public Task SendError(Error error) => this.SendMessage(null, Event.SHOW_ERROR, error); + public Task SendError(DataErrorMessage dataErrorMessage) => this.SendMessage(null, Event.SHOW_ERROR, dataErrorMessage); - public Task SendWarning(Warning warning) => this.SendMessage(null, Event.SHOW_WARNING, warning); + public Task SendWarning(DataWarningMessage dataWarningMessage) => this.SendMessage(null, Event.SHOW_WARNING, dataWarningMessage); - public Task SendSuccess(Success success) => this.SendMessage(null, Event.SHOW_SUCCESS, success); + public Task SendSuccess(DataSuccessMessage dataSuccessMessage) => this.SendMessage(null, Event.SHOW_SUCCESS, dataSuccessMessage); public void DeferMessage(ComponentBase? sendingComponent, Event triggeredEvent, T? data = default) { From 175a3e040289e5bb1a8aa677bc3df1ca396033e6 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Thu, 29 May 2025 12:16:20 +0200 Subject: [PATCH 21/23] Removed unused imports --- app/MindWork AI Studio/Components/CodeBlock.razor.cs | 2 +- app/MindWork AI Studio/Pages/About.razor.cs | 2 +- app/MindWork AI Studio/Tools/Pandoc.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/MindWork AI Studio/Components/CodeBlock.razor.cs b/app/MindWork AI Studio/Components/CodeBlock.razor.cs index 1535bb00b..e5d60f13a 100644 --- a/app/MindWork AI Studio/Components/CodeBlock.razor.cs +++ b/app/MindWork AI Studio/Components/CodeBlock.razor.cs @@ -1,5 +1,5 @@ using Microsoft.AspNetCore.Components; -using MudBlazor.Utilities; +using Microsoft.AspNetCore.Components.Rendering; namespace AIStudio.Components; diff --git a/app/MindWork AI Studio/Pages/About.razor.cs b/app/MindWork AI Studio/Pages/About.razor.cs index e2f277dee..51727506d 100644 --- a/app/MindWork AI Studio/Pages/About.razor.cs +++ b/app/MindWork AI Studio/Pages/About.razor.cs @@ -1,5 +1,5 @@ using System.Reflection; -using AIStudio.Dialogs; + using AIStudio.Tools.Rust; using AIStudio.Tools.Services; diff --git a/app/MindWork AI Studio/Tools/Pandoc.cs b/app/MindWork AI Studio/Tools/Pandoc.cs index d87c835c3..bf857afba 100644 --- a/app/MindWork AI Studio/Tools/Pandoc.cs +++ b/app/MindWork AI Studio/Tools/Pandoc.cs @@ -2,7 +2,7 @@ using System.IO.Compression; using System.Runtime.InteropServices; using System.Text.RegularExpressions; -using AIStudio.Components; + using AIStudio.Tools.Services; namespace AIStudio.Tools; From 314a7876d35ff62353b598da29208a889e33d470 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Thu, 29 May 2025 12:17:19 +0200 Subject: [PATCH 22/23] Applied optimizations --- app/MindWork AI Studio/Components/CodeBlock.razor.cs | 9 +++++---- app/MindWork AI Studio/Components/CodeTabs.razor.cs | 6 ++++-- app/MindWork AI Studio/Tools/Pandoc.cs | 9 +++++---- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/app/MindWork AI Studio/Components/CodeBlock.razor.cs b/app/MindWork AI Studio/Components/CodeBlock.razor.cs index e5d60f13a..705e63107 100644 --- a/app/MindWork AI Studio/Components/CodeBlock.razor.cs +++ b/app/MindWork AI Studio/Components/CodeBlock.razor.cs @@ -12,7 +12,7 @@ public partial class CodeBlock : ComponentBase public string? Title { get; set; } = string.Empty; [Parameter] - public bool IsInline { get; set; } = false; + public bool IsInline { get; set; } [CascadingParameter] public CodeTabs? ParentTabs { get; set; } @@ -21,14 +21,15 @@ protected override void OnInitialized() { if (this.ParentTabs is not null && this.Title is not null) { - RenderFragment blockSelf = builder => + void BlockSelf(RenderTreeBuilder builder) { builder.OpenComponent(0); builder.AddAttribute(1, "Title", this.Title); builder.AddAttribute(2, "ChildContent", this.ChildContent); builder.CloseComponent(); - }; - this.ParentTabs.RegisterBlock(this.Title, blockSelf); + } + + this.ParentTabs.RegisterBlock(this.Title, BlockSelf); } } diff --git a/app/MindWork AI Studio/Components/CodeTabs.razor.cs b/app/MindWork AI Studio/Components/CodeTabs.razor.cs index 8c4abb9fb..28f5a12a4 100644 --- a/app/MindWork AI Studio/Components/CodeTabs.razor.cs +++ b/app/MindWork AI Studio/Components/CodeTabs.razor.cs @@ -7,8 +7,8 @@ public partial class CodeTabs : ComponentBase [Parameter] public RenderFragment? ChildContent { get; set; } - private List blocks = new(); - private int selectedIndex = 0; + private readonly List blocks = new(); + private int selectedIndex; internal void RegisterBlock(string title, RenderFragment fragment) { @@ -17,12 +17,14 @@ internal void RegisterBlock(string title, RenderFragment fragment) Title = title, Fragment = fragment, }); + this.StateHasChanged(); } private class CodeTabItem { public string Title { get; init; } = string.Empty; + public RenderFragment Fragment { get; init; } = null!; } } \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/Pandoc.cs b/app/MindWork AI Studio/Tools/Pandoc.cs index bf857afba..2f0d7bd1f 100644 --- a/app/MindWork AI Studio/Tools/Pandoc.cs +++ b/app/MindWork AI Studio/Tools/Pandoc.cs @@ -9,13 +9,14 @@ namespace AIStudio.Tools; public static partial class Pandoc { + private const string CPU_ARCHITECTURE = "win-x64"; + private const string DOWNLOAD_URL = "https://github.com/jgm/pandoc/releases/download"; + private const string LATEST_URL = "https://github.com/jgm/pandoc/releases/latest"; + private static readonly ILogger LOG = Program.LOGGER_FACTORY.CreateLogger("PandocService"); - private static readonly string DOWNLOAD_URL = "https://github.com/jgm/pandoc/releases/download"; - private static readonly string LATEST_URL = "https://github.com/jgm/pandoc/releases/latest"; private static readonly Version MINIMUM_REQUIRED_VERSION = new (3, 6); private static readonly Version FALLBACK_VERSION = new (3, 7, 0, 1); - private static readonly string CPU_ARCHITECTURE = "win-x64"; - + /// /// Checks if pandoc is available on the system and can be started as a process or present in AiStudio's data dir /// From 18a00c6bf17893f5d3950bd15ee5e2b42cb92580 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Thu, 29 May 2025 13:58:23 +0200 Subject: [PATCH 23/23] Formatting --- app/MindWork AI Studio/wwwroot/app.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/MindWork AI Studio/wwwroot/app.css b/app/MindWork AI Studio/wwwroot/app.css index c8ec7384f..a0d9e8604 100644 --- a/app/MindWork AI Studio/wwwroot/app.css +++ b/app/MindWork AI Studio/wwwroot/app.css @@ -119,9 +119,11 @@ font-family: Consolas, "Courier New", monospace !important; text-align: left !important; } + .code-block pre { margin: 0 !important; } + .code-block code { font-family: inherit !important; }