diff --git a/Knossos.NET/Classes/KnUtils.cs b/Knossos.NET/Classes/KnUtils.cs
index 09c15d64..e543645a 100644
--- a/Knossos.NET/Classes/KnUtils.cs
+++ b/Knossos.NET/Classes/KnUtils.cs
@@ -1391,5 +1391,28 @@ public static void CreateDesktopShortcut(string shortcutName, string destFileFul
Log.Add(Log.LogSeverity.Error, "KnUtils.CreateDesktopShortcut()", ex);
}
}
+
+ ///
+ /// Determines if the current OS meets the requirements for .NET 10 / "latest" channel updates.
+ /// Windows 10+ or macOS 12.0+ qualify.
+ ///
+ public static bool IsModernOS()
+ {
+ if (IsWindows)
+ {
+ // Windows build 10240 = Windows 10 RTM
+ return Environment.OSVersion.Version.Major >= 10;
+ }
+
+ if (IsMacOS)
+ {
+ var ver = Environment.OSVersion.Version;
+ // macOS 12.0
+ return ver.Major >= 12;
+ }
+
+ // Linux and anything else: treat as modern (no restriction planned)
+ return true;
+ }
}
}
diff --git a/Knossos.NET/Models/GitHubApi.cs b/Knossos.NET/Models/GitHubApi.cs
index e9bc2470..02c11194 100644
--- a/Knossos.NET/Models/GitHubApi.cs
+++ b/Knossos.NET/Models/GitHubApi.cs
@@ -1,6 +1,10 @@
-using System;
+using Knossos.NET.Classes;
+using System;
+using System.Collections.Generic;
+using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
+using System.Runtime.InteropServices;
using System.Text.Json;
using System.Threading.Tasks;
@@ -9,11 +13,27 @@ namespace Knossos.NET.Models
public static class GitHubApi
{
///
- /// Get the last release on Knet github repo
- /// URL of this repo is set on the Knossos class
+ /// Gets the latest applicable release from GitHub.
+ /// - Modern platforms (Win10+ / macOS 12.0+): uses /releases/latest (current behaviour).
+ /// - Legacy platforms: scans /releases, keeps only v1.3.x tags, returns the newest one.
///
- /// GitHubRelease or null if the api call failed
+ /// GitHubRelease or null if the API call failed.
public static async Task GetLastRelease()
+ {
+ if (KnUtils.IsModernOS())
+ {
+ return await GetLatestRelease();
+ }
+ else
+ {
+ return await GetLatestLegacyRelease();
+ }
+ }
+
+ ///
+ /// Calls /releases/latest — for modern platforms.
+ ///
+ private static async Task GetLatestRelease()
{
try
{
@@ -21,16 +41,99 @@ public static class GitHubApi
client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("product", "1"));
using var response = await client.GetAsync(Knossos.GitHubUpdateRepoURL + "/releases/latest");
var json = await response.Content.ReadAsStringAsync();
- return JsonSerializer.Deserialize(json)!;
+ return JsonSerializer.Deserialize(json);
}
catch (Exception ex)
{
- Log.Add(Log.LogSeverity.Error, "GitHubApi.GetLastRelease()", ex);
+ Log.Add(Log.LogSeverity.Error, "GitHubApi.GetLatestRelease()", ex);
return null;
}
}
+
+ ///
+ /// Scans /releases pages looking for v1.3.x tags and returns the newest one.
+ /// Stops paginating early once tags leave the 1.3.x range (assumes releases
+ /// are returned newest-first by the API).
+ ///
+ private static async Task GetLatestLegacyRelease()
+ {
+ try
+ {
+ var client = KnUtils.GetHttpClient();
+ client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("product", "1"));
+
+ var candidates = new List();
+ int page = 1;
+ const int perPage = 30; // GitHub default; max is 100
+
+ while (true)
+ {
+ var url = $"{Knossos.GitHubUpdateRepoURL}/releases?per_page={perPage}&page={page}";
+ using var response = await client.GetAsync(url);
+ var json = await response.Content.ReadAsStringAsync();
+ var releases = JsonSerializer.Deserialize>(json);
+
+ if (releases == null || releases.Count == 0)
+ break;
+
+ foreach (var release in releases)
+ {
+ if (IsLegacyTag(release.tag_name))
+ {
+ candidates.Add(release);
+ }
+ }
+
+ // If the oldest release on this page is already below 1.3.7, no need to go further
+ var lastTag = releases.Last().tag_name;
+ if (lastTag != null && IsBelowLegacyMinor(lastTag))
+ break;
+
+ if (releases.Count < perPage)
+ break; // last page
+
+ page++;
+ }
+
+ // Return the candidate with the highest semantic version
+ return candidates
+ .Where(r => r.tag_name != null)
+ .MaxBy(r => new SemanticVersion(NormalizeTag(r.tag_name!)));
+ }
+ catch (Exception ex)
+ {
+ Log.Add(Log.LogSeverity.Error, "GitHubApi.GetLatestLegacyRelease()", ex);
+ return null;
+ }
+ }
+
+ ///
+ /// Returns true if the tag is a v1.3.x release (e.g. "v1.3.7", "v1.3.10-rc1").
+ ///
+ private static bool IsLegacyTag(string? tag)
+ {
+ if (string.IsNullOrWhiteSpace(tag)) return false;
+ var version = new SemanticVersion(NormalizeTag(tag));
+ // Check major == 1 and minor == 3 by comparing against sentinels
+ return SemanticVersion.Compare(NormalizeTag(tag), "1.3.0") >= 0 &&
+ SemanticVersion.Compare(NormalizeTag(tag), "1.4.0") < 0;
+ }
+
+ ///
+ /// Returns true if the tag is strictly older than 1.3.7 — used as an early-exit
+ /// hint while paginating (GitHub returns releases newest-first).
+ ///
+ private static bool IsBelowLegacyMinor(string tag)
+ {
+ return SemanticVersion.Compare(NormalizeTag(tag), "1.3.7") < 0;
+ }
+
+ /// Strips the leading "v" that GitHub tags typically have.
+ private static string NormalizeTag(string tag) =>
+ tag.ToLower().Replace("v", "").Trim();
}
+
public class GitHubRelease
{
public string? url { get; set; }
diff --git a/Knossos.NET/ViewModels/GlobalSettingsViewModel.cs b/Knossos.NET/ViewModels/GlobalSettingsViewModel.cs
index 8f3ea01f..662fe40a 100644
--- a/Knossos.NET/ViewModels/GlobalSettingsViewModel.cs
+++ b/Knossos.NET/ViewModels/GlobalSettingsViewModel.cs
@@ -36,7 +36,11 @@ public partial class GlobalSettingsViewModel : ViewModelBase
private const long speed10MB = 170000000;
/* For display only */
- [ObservableProperty]
+ [ObservableProperty]
+ internal string knossosUpdateChannelInfo = "";
+ [ObservableProperty]
+ internal string knossosUpdateTooltip = "";
+ [ObservableProperty]
internal bool isPortableMode = false;
[ObservableProperty]
internal bool flagDataLoaded = false;
@@ -552,6 +556,13 @@ public string GlobalCmd
public GlobalSettingsViewModel()
{
isPortableMode = Knossos.inPortableMode;
+ if (!KnUtils.IsModernOS())
+ {
+ KnossosUpdateChannelInfo = "(Legacy Update Channel)";
+ var current = KnUtils.IsWindows ? $"Windows {Environment.OSVersion.Version.Major}" : $"MacOS {Environment.OSVersion.Version}";
+ var minimum = KnUtils.IsWindows ? "Windows 10+" : "MacOS 12+";
+ KnossosUpdateTooltip = $"Your version of {current} is not compatible with version 1.4 of Knossos.NET and above. To receive new features, you should upgrade to version {minimum}. You will still receive bugfix updates when they apply to version 1.3.";
+ }
}
public void CheckDisplaySettingsWarning()
diff --git a/Knossos.NET/Views/GlobalSettingsView.axaml b/Knossos.NET/Views/GlobalSettingsView.axaml
index db98d926..5523dd9c 100644
--- a/Knossos.NET/Views/GlobalSettingsView.axaml
+++ b/Knossos.NET/Views/GlobalSettingsView.axaml
@@ -73,6 +73,7 @@
+