diff --git a/RTXLauncher.Avalonia/RTXLauncher.Avalonia.csproj b/RTXLauncher.Avalonia/RTXLauncher.Avalonia.csproj
index ae57df6..314787a 100644
--- a/RTXLauncher.Avalonia/RTXLauncher.Avalonia.csproj
+++ b/RTXLauncher.Avalonia/RTXLauncher.Avalonia.csproj
@@ -46,6 +46,7 @@
+
diff --git a/RTXLauncher.Avalonia/Utilities/MarkdownFormatter.cs b/RTXLauncher.Avalonia/Utilities/MarkdownFormatter.cs
new file mode 100644
index 0000000..e8659b0
--- /dev/null
+++ b/RTXLauncher.Avalonia/Utilities/MarkdownFormatter.cs
@@ -0,0 +1,202 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace RTXLauncher.Avalonia.Utilities
+{
+ public class MarkdownFormatter
+ {
+ // Dictionary of prefixes and their color styles (following WinForms color scheme)
+ private static readonly Dictionary PrefixColors = new()
+ {
+ { "Fixed:", "#2F4F4F" }, // DarkSlateGray
+ { "Fixes:", "#2F4F4F" },
+ { "Bug fix:", "#2F4F4F" },
+ { "Added:", "#006400" }, // DarkGreen
+ { "New:", "#006400" },
+ { "What's New:", "#006400" },
+ { "Feature:", "#006400" },
+ { "Removed:", "#8B0000" }, // DarkRed
+ { "Changed:", "#FF8C00" }, // DarkOrange
+ { "What's Changed:", "#FF8C00" },
+ { "Updated:", "#FF8C00" },
+ { "Improved:", "#FF8C00" },
+ { "Technical Details:", "#8B008B" }, // DarkMagenta
+ { "Known Issues:", "#FF0000" }, // Red
+ { "Known Issue:", "#FF0000" },
+ { "Warning:", "#FF0000" },
+ { "Important:", "#FF0000" },
+ { "Note:", "#FF0000" },
+ { "New Contributors:", "#FF00FF" }, // Fuchsia
+ };
+
+ public static string FormatReleaseNotes(string newVersion, string currentVersion, string releaseNotes, bool isUpdate = true)
+ {
+ var markdown = new StringBuilder();
+
+ // Format the header section
+ FormatHeaderSection(markdown, newVersion, currentVersion, isUpdate);
+
+ // Handle missing release notes
+ if (string.IsNullOrWhiteSpace(releaseNotes))
+ {
+ markdown.AppendLine("*No release notes available for this version.*");
+ return markdown.ToString();
+ }
+
+ // Process each line
+ string[] lines = releaseNotes.Replace("\r\n", "\n").Split('\n');
+ bool inCodeBlock = false;
+
+ foreach (string line in lines)
+ {
+ string trimmedLine = line.TrimStart();
+
+ // Skip empty lines (preserve spacing)
+ if (string.IsNullOrWhiteSpace(line))
+ {
+ markdown.AppendLine();
+ continue;
+ }
+
+ // Handle code blocks
+ if (trimmedLine.StartsWith("```"))
+ {
+ inCodeBlock = !inCodeBlock;
+ markdown.AppendLine(line);
+ continue;
+ }
+
+ if (inCodeBlock)
+ {
+ markdown.AppendLine(line);
+ continue;
+ }
+
+ // Parse line components
+ (string content, int headerLevel, bool isBullet, string bulletPrefix) = ParseLineFormat(trimmedLine);
+
+ // Find special prefix if any
+ string matchedPrefix = PrefixColors.Keys.FirstOrDefault(prefix => content.StartsWith(prefix)) ?? string.Empty;
+
+ // Format the line based on its components
+ if (headerLevel > 0)
+ {
+ // Headers - add the appropriate markdown header syntax
+ string headerPrefix = new string('#', headerLevel);
+ if (!string.IsNullOrEmpty(matchedPrefix))
+ {
+ markdown.AppendLine($"{headerPrefix} {matchedPrefix}{content.Substring(matchedPrefix.Length)}");
+ }
+ else
+ {
+ markdown.AppendLine($"{headerPrefix} {content}");
+ }
+ }
+ else if (isBullet)
+ {
+ // Bullet points
+ if (!string.IsNullOrEmpty(matchedPrefix))
+ {
+ markdown.AppendLine($"- {matchedPrefix}{ProcessInlineMarkdown(content.Substring(matchedPrefix.Length))}");
+ }
+ else
+ {
+ markdown.AppendLine($"- {ProcessInlineMarkdown(content)}");
+ }
+ }
+ else if (!string.IsNullOrEmpty(matchedPrefix))
+ {
+ // Prefixed lines
+ markdown.AppendLine($"{matchedPrefix}{ProcessInlineMarkdown(content.Substring(matchedPrefix.Length))}");
+ }
+ else
+ {
+ // Regular text
+ markdown.AppendLine(ProcessInlineMarkdown(line));
+ }
+ }
+
+ return markdown.ToString();
+ }
+
+ private static void FormatHeaderSection(StringBuilder markdown, string newVersion, string currentVersion, bool isUpdate)
+ {
+ if (isUpdate)
+ {
+ markdown.AppendLine("## Update Available!");
+ markdown.AppendLine();
+ markdown.AppendLine($"**New version:** {newVersion}");
+ markdown.AppendLine($"Current version: {currentVersion}");
+ markdown.AppendLine();
+ }
+ else
+ {
+ markdown.AppendLine($"## You're up to date! ({currentVersion})");
+ markdown.AppendLine();
+ }
+ }
+
+ private static (string content, int headerLevel, bool isBullet, string bulletPrefix) ParseLineFormat(string line)
+ {
+ int headerLevel = 0;
+ bool isBullet = false;
+ string bulletPrefix = "";
+ string content = line;
+
+ // Check for headers
+ if (line.StartsWith("### "))
+ {
+ headerLevel = 3;
+ content = line.Substring(4);
+ }
+ else if (line.StartsWith("## "))
+ {
+ headerLevel = 2;
+ content = line.Substring(3);
+ }
+ else if (line.StartsWith("# "))
+ {
+ headerLevel = 1;
+ content = line.Substring(2);
+ }
+
+ // Check for bullets
+ if (line.StartsWith("- ") || line.StartsWith("* "))
+ {
+ isBullet = true;
+ bulletPrefix = line.StartsWith("- ") ? "- " : "* ";
+ content = line.Substring(2);
+ }
+
+ return (content, headerLevel, isBullet, bulletPrefix);
+ }
+
+ private static string ProcessInlineMarkdown(string text)
+ {
+ // Handle the special "Full Changelog" link pattern
+ if (text.Contains("**Full Changelog**:") && text.Contains("compare/"))
+ {
+ // Extract the version comparison (e.g., v1.0.4...v1.0.5)
+ Regex compareRegex = new Regex(@"compare/([^/\s\)]+)");
+ Match compareMatch = compareRegex.Match(text);
+
+ // Extract the URL
+ Regex urlRegex = new Regex(@"(https?://[^\s\)]+)");
+ Match urlMatch = urlRegex.Match(text);
+
+ if (urlMatch.Success)
+ {
+ string url = urlMatch.Groups[1].Value;
+ string versionCompare = compareMatch.Success ? compareMatch.Groups[1].Value : "versions";
+ return $"**Full Changelog**: [{versionCompare}]({url})";
+ }
+ }
+
+ // The markdown library should handle bold (**text**), italic (*text*), and links [text](url) automatically
+ return text;
+ }
+ }
+}
\ No newline at end of file
diff --git a/RTXLauncher.Avalonia/ViewModels/AboutViewModel.cs b/RTXLauncher.Avalonia/ViewModels/AboutViewModel.cs
index 92f1be8..160d346 100644
--- a/RTXLauncher.Avalonia/ViewModels/AboutViewModel.cs
+++ b/RTXLauncher.Avalonia/ViewModels/AboutViewModel.cs
@@ -95,10 +95,12 @@ private async Task CheckForUpdates()
}
if (UpdateAvailable && result.LatestUpdate != null)
- {
- ReleaseNotes = $"Update available: {result.LatestUpdate.Version}\n\n" +
- $"Current version: {CurrentVersion}\n\n" +
- (result.LatestUpdate.Release?.Body ?? "No release notes available.");
+ {
+ var releaseBody = result.LatestUpdate.Release?.Body ?? "No release notes available.";
+
+ // Use the markdown formatter to process the release notes
+ ReleaseNotes = Utilities.MarkdownFormatter.FormatReleaseNotes(
+ result.LatestUpdate.Version, CurrentVersion, releaseBody, true);
}
else
{
@@ -201,24 +203,25 @@ private void UpdateReleaseNotes(UpdateSource source)
{
if (source.IsStaging)
{
- ReleaseNotes = $"Development Build: {source.Version}\n\n" +
+ var markdown = "**Development Build:** " + source.Version + "\n\n" +
"This is the latest development build from the master branch.\n\n" +
- "Warning: This version may contain experimental features and bugs.";
+ "**Warning:** This version may contain experimental features and bugs.";
+ ReleaseNotes = markdown;
}
else if (source.Release != null)
{
var isNewer = Core.Utilities.VersionUtility.CompareVersions(source.Version, CurrentVersion) > 0;
- var status = isNewer ? "đ Newer version available" : "âšī¸ Same or older version";
- ReleaseNotes = $"{source.Name}\n{status}\n\n" +
- $"Published: {source.Release.PublishedAt:yyyy-MM-dd}\n\n" +
- (source.Release.Body ?? "No release notes available.");
+ // Use the markdown formatter to process the release notes
+ var releaseBody = source.Release.Body ?? "No release notes available.";
+ ReleaseNotes = Utilities.MarkdownFormatter.FormatReleaseNotes(
+ source.Version, CurrentVersion, releaseBody, isNewer);
UpdateAvailable = isNewer;
}
else
{
- ReleaseNotes = $"{source.Name}\n\nNo additional information available.";
+ ReleaseNotes = $"**{source.Name}**\n\nNo additional information available.";
}
}
}
\ No newline at end of file
diff --git a/RTXLauncher.Avalonia/Views/AboutView.axaml b/RTXLauncher.Avalonia/Views/AboutView.axaml
index ac69838..d42232c 100644
--- a/RTXLauncher.Avalonia/Views/AboutView.axaml
+++ b/RTXLauncher.Avalonia/Views/AboutView.axaml
@@ -1,6 +1,7 @@
@@ -19,12 +20,11 @@
-
+
+
+