@if (!showLog)
{
@if (currentPageIndex == 0)
@@ -135,12 +135,13 @@
// Map friendly names to SCSS files
string themeFile = themeName switch
{
- "SimpleDark" => "SimpleDarkTheme.scss",
+ "Simple" => "/Assets/SimpleDarkTheme.scss",
"Computer95" => "/XGUI/DefaultStyles/Computer95.scss",
"Computer11" => "/XGUI/DefaultStyles/Computer11.scss",
"ComputerXP" => "/XGUI/DefaultStyles/ComputerXP.scss",
"Derma" => "/XGUI/DefaultStyles/Derma.scss",
- _ => "SimpleDarkTheme.scss"
+ "OliveGreen"=> "/XGUI/DefaultStyles/OliveGreen.scss",
+ _ => "/XGUI/DefaultStyles/OliveGreen.scss"
};
SetTheme(themeFile);
diff --git a/RTXLauncher.Fazor/MainWindow.razor.scss b/RTXLauncher.Fazor/MainWindow.razor.scss
index bbd4068..3e23611 100644
--- a/RTXLauncher.Fazor/MainWindow.razor.scss
+++ b/RTXLauncher.Fazor/MainWindow.razor.scss
@@ -1,6 +1,4 @@
// Main Window Styling for RTX Launcher Fazor
-// Import Simple Dark Theme (matches Avalonia's SimpleTheme Dark variant)
-@import "/SimpleDarkTheme.scss";
.window-content {
display: flex;
@@ -14,7 +12,7 @@
.top-bar {
display: flex;
flex-direction: row;
- border-bottom: 1px solid $theme-border-low;
+ border-bottom: 1px solid #505050;
padding-bottom: 10px;
gap: 10px;
}
@@ -47,8 +45,8 @@
.status-text {
font-size: 13px;
min-height: 20px;
- color: $theme-foreground;
- display: flex;
+ color: #DEDEDE;
+ display: flex;
align-items: center;
}
@@ -65,10 +63,7 @@
}
.content-area {
- flex: 1;
- background-color: $theme-control-mid;
- border: 1px solid $theme-border-low;
- border-radius: 3px;
+ flex: 1;
padding: 10px;
overflow: auto;
display: flex;
@@ -92,7 +87,7 @@
font-family: "Consolas", "Courier New", monospace;
padding: 5px;
overflow: auto;
- border: 1px solid $theme-border-low;
+ border: 1px solid #505050;
border-radius: 3px;
}
@@ -119,24 +114,24 @@
h1 {
font-size: 24px;
margin-bottom: 15px;
- color: $theme-foreground;
+ color: #DEDEDE;
}
h2 {
font-size: 18px;
margin-bottom: 10px;
- color: $theme-foreground;
+ color: #DEDEDE;
}
h3 {
font-size: 14px;
margin-bottom: 5px;
- color: $theme-foreground;
+ color: #DEDEDE;
}
p {
margin-bottom: 10px;
- color: $theme-foreground;
+ color: #DEDEDE;
line-height: 1.5;
}
@@ -152,7 +147,7 @@
display: block;
margin-bottom: 5px;
font-weight: 600;
- color: $theme-foreground;
+ color: #DEDEDE;
}
}
@@ -164,19 +159,19 @@
.step, .mod-item {
padding: 10px;
- background-color: $theme-control-mid;
- border: 1px solid $theme-border-low;
+ background-color: #2A2A2A;
+ border: 1px solid #505050;
border-radius: 3px;
h3 {
margin-bottom: 5px;
- color: $theme-foreground;
+ color: #DEDEDE;
font-size: 14px;
font-weight: 600;
}
p {
- color: $theme-foreground;
+ color: #DEDEDE;
margin: 5px 0;
}
}
@@ -187,11 +182,11 @@
label {
font-weight: 600;
margin-right: 10px;
- color: $theme-foreground;
+ color: #DEDEDE;
}
span {
- color: $theme-foreground;
+ color: #DEDEDE;
}
}
From 3e7308df5807ba9e9b3098e19a674d492665f062 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 11 Jan 2026 08:22:56 +0000
Subject: [PATCH 08/12] Replace @bind-value with value/@onchange pattern for
Fazor compatibility
Co-authored-by: Xenthio <28588188+Xenthio@users.noreply.github.com>
---
RTXLauncher.Fazor/AboutPage.razor | 2 +-
RTXLauncher.Fazor/AdvancedInstallPage.razor | 4 +--
RTXLauncher.Fazor/InstallPage.razor | 2 +-
RTXLauncher.Fazor/LauncherSettingsPage.razor | 4 +--
RTXLauncher.Fazor/ModsPage.razor | 2 +-
RTXLauncher.Fazor/SettingsPage.razor | 26 ++++++++++----------
6 files changed, 20 insertions(+), 20 deletions(-)
diff --git a/RTXLauncher.Fazor/AboutPage.razor b/RTXLauncher.Fazor/AboutPage.razor
index 9a19ee0..6482a66 100644
--- a/RTXLauncher.Fazor/AboutPage.razor
+++ b/RTXLauncher.Fazor/AboutPage.razor
@@ -20,7 +20,7 @@
-
+ { selectedUpdateSource = v; StateHasChanged(); })" style="width: 200px;">
@foreach (var source in updateSources)
{
diff --git a/RTXLauncher.Fazor/AdvancedInstallPage.razor b/RTXLauncher.Fazor/AdvancedInstallPage.razor
index a446cec..8a7f70a 100644
--- a/RTXLauncher.Fazor/AdvancedInstallPage.razor
+++ b/RTXLauncher.Fazor/AdvancedInstallPage.razor
@@ -77,7 +77,7 @@
-
+ { package.SelectedSource = v; StateHasChanged(); })" style="width: 200px;">
@foreach (var source in package.Sources)
{
@@ -89,7 +89,7 @@
{
-
+ { package.SelectedRelease = v; StateHasChanged(); })" style="width: 200px;">
@foreach (var release in package.Releases)
{
diff --git a/RTXLauncher.Fazor/InstallPage.razor b/RTXLauncher.Fazor/InstallPage.razor
index 1bffcb0..93709ed 100644
--- a/RTXLauncher.Fazor/InstallPage.razor
+++ b/RTXLauncher.Fazor/InstallPage.razor
@@ -30,7 +30,7 @@
-
+ { selectedFixesPackage = v; StateHasChanged(); })" style="width: 200px;">
@foreach (var package in availableFixesPackages)
{
diff --git a/RTXLauncher.Fazor/LauncherSettingsPage.razor b/RTXLauncher.Fazor/LauncherSettingsPage.razor
index 396cb4c..9528c82 100644
--- a/RTXLauncher.Fazor/LauncherSettingsPage.razor
+++ b/RTXLauncher.Fazor/LauncherSettingsPage.razor
@@ -13,13 +13,13 @@
@if (settingsData != null)
{
-
+ { settingsData.CheckForUpdatesOnLaunch = v; StateHasChanged(); })">
Check for updates on launch
-
+ { selectedTheme = v; OnThemeChanged(v); })" style="width: 200px;">
@foreach (var theme in themes)
{
diff --git a/RTXLauncher.Fazor/ModsPage.razor b/RTXLauncher.Fazor/ModsPage.razor
index 5980173..1992d56 100644
--- a/RTXLauncher.Fazor/ModsPage.razor
+++ b/RTXLauncher.Fazor/ModsPage.razor
@@ -6,7 +6,7 @@
-
+ { searchText = v; StateHasChanged(); })" />
diff --git a/RTXLauncher.Fazor/SettingsPage.razor b/RTXLauncher.Fazor/SettingsPage.razor
index 09407e6..597a5aa 100644
--- a/RTXLauncher.Fazor/SettingsPage.razor
+++ b/RTXLauncher.Fazor/SettingsPage.razor
@@ -16,21 +16,21 @@
-
+ { selectedResolution = v; OnResolutionChanged(v); })" style="width: 250px;">
@foreach (var res in resolutions)
{
}
- Use Custom Resolution
+ { settingsData.UseCustomResolution = v; StateHasChanged(); })">Use Custom Resolution
@if (settingsData != null && settingsData.UseCustomResolution)
{
-
+ { settingsData.Width = v; StateHasChanged(); })" placeholder="Width" style="width: 100px;" />
-
+ { settingsData.Height = v; StateHasChanged(); })" placeholder="Height" style="width: 100px;" />
}
@@ -41,7 +41,7 @@
@if (settingsData != null)
{
- Load Workshop Addons
+ { settingsData.LoadWorkshopAddons = v; StateHasChanged(); })">Load Workshop Addons
}
@@ -54,7 +54,7 @@
-
+ { selectedProtonBuild = v; OnProtonBuildChanged(v); })" style="width: 250px;">
@foreach (var label in protonBuildLabels)
{
@@ -68,7 +68,7 @@
{
-
+ { settingsData.LinuxProtonPath = v; StateHasChanged(); })" placeholder="/path/to/proton/executable" style="flex: 1;" />
}
@@ -76,13 +76,13 @@
-
+ { settingsData.LinuxSteamRootOverride = v; StateHasChanged(); })" placeholder="Leave empty for auto-detection" style="flex: 1;" />
-
+ { settingsData.LinuxVulkanDriver = v; StateHasChanged(); })" style="width: 200px;">
@foreach (var driver in vulkanDriverOptions)
{
@@ -91,7 +91,7 @@
- Enable Proton Logging (PROTON_LOG=1)
+ { settingsData.LinuxEnableProtonLog = v; StateHasChanged(); })">Enable Proton Logging (PROTON_LOG=1)
}
@@ -101,12 +101,12 @@
{
-
Open Console
-
Disable Chromium
+
{ settingsData.ConsoleEnabled = v; StateHasChanged(); })">Open Console
+
{ settingsData.DisableChromium = v; StateHasChanged(); })">Disable Chromium
-
+ { settingsData.CustomLaunchOptions = v; StateHasChanged(); })" placeholder="Custom Launch Options (e.g. -windowed -noborder)" style="flex: 1;" />
From f3a4dd171899beccd40abdea29612bde1f23b061 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 11 Jan 2026 08:40:08 +0000
Subject: [PATCH 09/12] Implement ModsPage with real ModDB integration and
ModDetailsPage
Co-authored-by: Xenthio <28588188+Xenthio@users.noreply.github.com>
---
RTXLauncher.Fazor/MainWindow.razor | 25 +-
RTXLauncher.Fazor/ModDetailsPage.razor | 306 +++++++++++++++++++++++++
RTXLauncher.Fazor/ModsPage.razor | 245 ++++++++++++++++----
3 files changed, 531 insertions(+), 45 deletions(-)
create mode 100644 RTXLauncher.Fazor/ModDetailsPage.razor
diff --git a/RTXLauncher.Fazor/MainWindow.razor b/RTXLauncher.Fazor/MainWindow.razor
index b22b465..69f185b 100644
--- a/RTXLauncher.Fazor/MainWindow.razor
+++ b/RTXLauncher.Fazor/MainWindow.razor
@@ -47,7 +47,11 @@
@if (!showLog)
{
- @if (currentPageIndex == 0)
+ @if (isShowingModDetails && currentModDetails != null)
+ {
+
+ }
+ else if (currentPageIndex == 0)
{
}
@@ -117,6 +121,8 @@
// Navigation
private SelectList? navigationList;
private int currentPageIndex = 0;
+ private ModInfo? currentModDetails = null;
+ private bool isShowingModDetails = false;
public MainWindow()
{
@@ -157,6 +163,23 @@
private void ShowPage(int pageIndex)
{
currentPageIndex = pageIndex;
+ isShowingModDetails = false;
+ currentModDetails = null;
+ StateHasChanged();
+ }
+
+ public void ShowModDetails(ModInfo mod)
+ {
+ currentModDetails = mod;
+ isShowingModDetails = true;
+ StateHasChanged();
+ }
+
+ public void ShowModsPage()
+ {
+ isShowingModDetails = false;
+ currentModDetails = null;
+ currentPageIndex = 6;
StateHasChanged();
}
diff --git a/RTXLauncher.Fazor/ModDetailsPage.razor b/RTXLauncher.Fazor/ModDetailsPage.razor
new file mode 100644
index 0000000..1baff45
--- /dev/null
+++ b/RTXLauncher.Fazor/ModDetailsPage.razor
@@ -0,0 +1,306 @@
+@using Sandbox.UI;
+@using global::Fazor.Controls;
+@using RTXLauncher.Core.Models
+@using RTXLauncher.Core.Services
+@using System.Collections.Generic
+@using Microsoft.AspNetCore.Components
+@namespace RTXLauncher.Fazor
+@inherits Panel
+
+
+
+
+
+
+
+
+
+ @if (modInfo != null)
+ {
+
+
+
+ @if (!string.IsNullOrEmpty(modInfo.Summary))
+ {
+
+
+
+
+ }
+
+ @if (!string.IsNullOrEmpty(modInfo.Author))
+ {
+
+ }
+
+ @if (!string.IsNullOrEmpty(modInfo.Genre))
+ {
+
+ }
+
+ @if (modInfo.TotalVisits.HasValue)
+ {
+
+ }
+
+ @if (modInfo.Rank.HasValue)
+ {
+
+ }
+
+ @if (modInfo.ReleaseDate.HasValue)
+ {
+
+ }
+
+
+
+
+
+ @if (isBusy && files.Count == 0)
+ {
+
+ }
+ else if (files.Count == 0)
+ {
+
+ }
+ else
+ {
+
+ @foreach (var file in files)
+ {
+
+
+
+
+ @if (file.SizeInBytes.HasValue && file.SizeInBytes.Value > 0)
+ {
+
+ }
+ @if (!string.IsNullOrEmpty(file.Filename))
+ {
+
+ }
+
+
+
+ @if (file.IsInstalled)
+ {
+
+
+ }
+ else
+ {
+
+ }
+
+
+
+ @if (downloadProgress > 0 && currentDownloadingFile == file)
+ {
+
+ }
+
+ }
+
+ }
+
+ }
+
+
+
+@code {
+ [Parameter]
+ public ModInfo? modInfo { get; set; }
+
+ private IModService? modService;
+ private List
files = new();
+ private bool isBusy = false;
+ private double downloadProgress = 0;
+ private ModFile? currentDownloadingFile = null;
+ private bool hasLoadedFiles = false;
+
+ public ModDetailsPage()
+ {
+ // Initialize service
+ var addonInstallService = new AddonInstallService();
+ var installedModsService = new InstalledModsService();
+ modService = new ModDBModService(addonInstallService, installedModsService);
+ }
+
+ private async void LoadFiles()
+ {
+ if (modService == null || modInfo == null || hasLoadedFiles) return;
+
+ hasLoadedFiles = true;
+ isBusy = true;
+ StateHasChanged();
+
+ try
+ {
+ files.Clear();
+ var filesList = await modService.GetFilesForModAsync(modInfo);
+ files.AddRange(filesList);
+ AddLogToMainWindow($"Loaded {files.Count} files for {modInfo.Title}");
+ }
+ catch (System.Exception ex)
+ {
+ AddLogToMainWindow($"Error loading mod files: {ex.Message}");
+ }
+ finally
+ {
+ isBusy = false;
+ StateHasChanged();
+ }
+ }
+
+ protected override int BuildHash()
+ {
+ var hash = System.HashCode.Combine(modInfo?.Title, files.Count, isBusy, downloadProgress);
+
+ // Trigger file loading if we have modInfo and haven't loaded yet
+ if (modInfo != null && !hasLoadedFiles && !isBusy)
+ {
+ LoadFiles();
+ }
+
+ return hash;
+ }
+
+ private async void InstallFile(ModFile file)
+ {
+ if (modService == null) return;
+
+ isBusy = true;
+ currentDownloadingFile = file;
+ downloadProgress = 0;
+ StateHasChanged();
+
+ try
+ {
+ var progress = new System.Progress(report =>
+ {
+ AddLogToMainWindow(report.Message);
+ downloadProgress = report.Percentage;
+ UpdateProgress(report.Percentage);
+ StateHasChanged();
+ });
+
+ // Simple confirmation provider (always returns true for now)
+ System.Func> confirmationProvider = async (message) =>
+ {
+ AddLogToMainWindow($"Confirmation: {message}");
+ return true;
+ };
+
+ await modService.InstallModFileAsync(modInfo, file, confirmationProvider, progress);
+
+ // Refresh files to update install status
+ await System.Threading.Tasks.Task.Delay(500);
+ LoadFiles();
+
+ AddLogToMainWindow($"Successfully installed {file.Title}");
+ }
+ catch (System.Exception ex)
+ {
+ AddLogToMainWindow($"Error installing mod: {ex.Message}");
+ }
+ finally
+ {
+ isBusy = false;
+ currentDownloadingFile = null;
+ downloadProgress = 0;
+ StateHasChanged();
+ }
+ }
+
+ private async void UninstallFile(ModFile file)
+ {
+ if (modService == null) return;
+
+ isBusy = true;
+ StateHasChanged();
+
+ try
+ {
+ var progress = new System.Progress(report =>
+ {
+ AddLogToMainWindow(report.Message);
+ });
+
+ // Call the uninstall method
+ await modService.UninstallModAsync(modInfo, progress);
+
+ // Refresh files to update install status
+ await System.Threading.Tasks.Task.Delay(500);
+ LoadFiles();
+
+ AddLogToMainWindow($"Successfully uninstalled {file.Title}");
+ }
+ catch (System.Exception ex)
+ {
+ AddLogToMainWindow($"Error uninstalling mod: {ex.Message}");
+ }
+ finally
+ {
+ isBusy = false;
+ StateHasChanged();
+ }
+ }
+
+ private void NavigateBack()
+ {
+ // Navigate back to mods page
+ var mainWindow = FindMainWindow();
+ if (mainWindow != null)
+ {
+ mainWindow.ShowModsPage();
+ }
+ }
+
+ private string FormatFileSize(long bytes)
+ {
+ string[] sizes = { "B", "KB", "MB", "GB", "TB" };
+ double len = bytes;
+ int order = 0;
+ while (len >= 1024 && order < sizes.Length - 1)
+ {
+ order++;
+ len = len / 1024;
+ }
+ return $"{len:0.##} {sizes[order]}";
+ }
+
+ private void AddLogToMainWindow(string message)
+ {
+ var mainWindow = FindMainWindow();
+ if (mainWindow != null)
+ {
+ mainWindow.AddLogMessage(message);
+ }
+ }
+
+ private void UpdateProgress(int percentage)
+ {
+ var mainWindow = FindMainWindow();
+ if (mainWindow != null)
+ {
+ mainWindow.UpdateProgress(percentage);
+ }
+ }
+
+ private MainWindow? FindMainWindow()
+ {
+ var current = Parent;
+ while (current != null)
+ {
+ if (current is MainWindow window)
+ {
+ return window;
+ }
+ current = current.Parent;
+ }
+ return null;
+ }
+}
diff --git a/RTXLauncher.Fazor/ModsPage.razor b/RTXLauncher.Fazor/ModsPage.razor
index 1992d56..00e9355 100644
--- a/RTXLauncher.Fazor/ModsPage.razor
+++ b/RTXLauncher.Fazor/ModsPage.razor
@@ -1,88 +1,245 @@
@using Sandbox.UI;
@using global::Fazor.Controls;
+@using RTXLauncher.Core.Models
+@using RTXLauncher.Core.Services
@using System.Collections.Generic
+@using System.Linq
@namespace RTXLauncher.Fazor
@inherits Panel
-
- { searchText = v; StateHasChanged(); })" />
-
-
-
-
- @foreach (var mod in mods)
+
+
+
+ { queryOptions.SearchText = v; StateHasChanged(); })" style="flex: 1;" />
+ { selectedSortOption = v; StateHasChanged(); })" style="width: 180px;">
+ @foreach (var sort in sortOptions)
+ {
+
+ }
+
+
+
+
+
+
+
+
+ @if (isBusy)
+ {
+
+ }
+ else if (mods.Count == 0)
{
-
-
-
-
+
+ }
+ else
+ {
+
+ @foreach (var mod in mods)
+ {
+
+
+
+
+ @if (mod.IsInstalled)
+ {
+
+ }
+
+
+
+
+
+
+
+ @if (mod.TotalVisits.HasValue)
+ {
+
+
+ }
+ @if (!string.IsNullOrEmpty(mod.Author))
+ {
+
+ }
+
+
+
+ @if (!string.IsNullOrEmpty(mod.Genre))
+ {
+
+ }
+ @if (mod.ReleaseDate.HasValue)
+ {
+
+ }
+
+
+
+
+
+ }
}
-
-
-
@code {
- private class ModItem
- {
- public string Title { get; set; } = "";
- public string Author { get; set; } = "";
- }
+ private IModService? modService;
+ private AddonInstallService? addonInstallService;
+ private InstalledModsService? installedModsService;
+
+ private ModQueryOptions queryOptions = new ModQueryOptions();
+ private List
mods = new();
+ private bool isBusy = false;
+ private bool canGoToPreviousPage = false;
+ private bool canGoToNextPage = false;
- private string searchText = "";
- private int currentPage = 1;
- private List mods = new();
+ private Dictionary sortOptions = new Dictionary
+ {
+ { "Popular (All Time)", "visitstotal-desc" },
+ { "Popular (Today)", "ranktoday-asc" },
+ { "Newest First", "dateup-desc" },
+ { "Oldest First", "dateup-asc" },
+ { "Name (A-Z)", "name-asc" }
+ };
+ private string selectedSortOption = "visitstotal-desc";
public ModsPage()
{
+ // Initialize services
+ addonInstallService = new AddonInstallService();
+ installedModsService = new InstalledModsService();
+ modService = new ModDBModService(addonInstallService, installedModsService);
+
+ // Load initial mods
LoadMods();
}
- private void LoadMods()
+ private async void LoadMods()
{
- mods = new List
- {
- new ModItem { Title = "Example Mod 1", Author = "Author1" },
- new ModItem { Title = "Example Mod 2", Author = "Author2" },
- new ModItem { Title = "Example Mod 3", Author = "Author3" }
- };
+ if (modService == null) return;
+
+ isBusy = true;
StateHasChanged();
+
+ try
+ {
+ mods.Clear();
+ queryOptions.SortOrder = selectedSortOption;
+
+ var modsList = await modService.GetAllModsAsync(queryOptions);
+
+ foreach (var mod in modsList)
+ {
+ mods.Add(new ModItemViewModel(mod));
+ }
+
+ UpdatePagination();
+ AddLogToMainWindow($"Loaded {mods.Count} mods from ModDB");
+ }
+ catch (System.Exception ex)
+ {
+ AddLogToMainWindow($"Error loading mods: {ex.Message}");
+ }
+ finally
+ {
+ isBusy = false;
+ StateHasChanged();
+ }
}
- private void SearchMods()
+ private void ApplyFilters()
{
- System.Console.WriteLine($"Searching for: {searchText}");
+ queryOptions.Page = 1; // Reset to first page
LoadMods();
}
- private void InstallMod(ModItem mod)
+ private void PreviousPage()
{
- System.Console.WriteLine($"Installing mod: {mod.Title}");
+ if (!canGoToPreviousPage) return;
+ queryOptions.Page--;
+ LoadMods();
}
- private void PreviousPage()
+ private void NextPage()
+ {
+ if (!canGoToNextPage) return;
+ queryOptions.Page++;
+ LoadMods();
+ }
+
+ private void UpdatePagination()
{
- if (currentPage > 1)
+ canGoToPreviousPage = queryOptions.Page > 1;
+ canGoToNextPage = mods.Count == 30; // ModDB shows 30 per page
+ }
+
+ private void ViewModDetails(ModItemViewModel mod)
+ {
+ var mainWindow = FindMainWindow();
+ if (mainWindow != null)
{
- currentPage--;
- LoadMods();
+ mainWindow.ShowModDetails(mod.Model);
+ AddLogToMainWindow($"Viewing details for: {mod.Title}");
}
}
- private void NextPage()
+ private void AddLogToMainWindow(string message)
{
- currentPage++;
- LoadMods();
+ var mainWindow = FindMainWindow();
+ if (mainWindow != null)
+ {
+ mainWindow.AddLogMessage(message);
+ }
+ }
+
+ private MainWindow? FindMainWindow()
+ {
+ var current = Parent;
+ while (current != null)
+ {
+ if (current is MainWindow window)
+ {
+ return window;
+ }
+ current = current.Parent;
+ }
+ return null;
}
protected override int BuildHash()
{
- return System.HashCode.Combine(searchText, currentPage, mods.Count);
+ return System.HashCode.Combine(queryOptions.SearchText, queryOptions.Page, mods.Count, isBusy);
+ }
+
+ // Helper ViewModel class
+ private class ModItemViewModel
+ {
+ public string Title { get; set; }
+ public string? Author { get; set; }
+ public string? Genre { get; set; }
+ public int? TotalVisits { get; set; }
+ public System.DateTime? ReleaseDate { get; set; }
+ public bool IsInstalled { get; set; }
+ public ModInfo Model { get; set; }
+
+ public ModItemViewModel(ModInfo mod)
+ {
+ Model = mod;
+ Title = mod.Title;
+ Author = mod.Author;
+ Genre = mod.Genre;
+ TotalVisits = mod.TotalVisits;
+ ReleaseDate = mod.ReleaseDate;
+ IsInstalled = mod.IsInstalled;
+ }
}
}
From 427c06014e7c98a96cb281087526a785eeb1c10e Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 11 Jan 2026 21:46:59 +0000
Subject: [PATCH 10/12] Fix @onchange event handlers to work with Fazor runtime
Co-authored-by: Xenthio <28588188+Xenthio@users.noreply.github.com>
---
RTXLauncher.Fazor/AboutPage.razor | 7 +-
RTXLauncher.Fazor/AdvancedInstallPage.razor | 14 ++-
RTXLauncher.Fazor/InstallPage.razor | 7 +-
RTXLauncher.Fazor/LauncherSettingsPage.razor | 16 ++--
RTXLauncher.Fazor/ModsPage.razor | 14 ++-
RTXLauncher.Fazor/SettingsPage.razor | 99 +++++++++++++++-----
6 files changed, 123 insertions(+), 34 deletions(-)
diff --git a/RTXLauncher.Fazor/AboutPage.razor b/RTXLauncher.Fazor/AboutPage.razor
index 6482a66..285e199 100644
--- a/RTXLauncher.Fazor/AboutPage.razor
+++ b/RTXLauncher.Fazor/AboutPage.razor
@@ -20,7 +20,7 @@
-
{ selectedUpdateSource = v; StateHasChanged(); })" style="width: 200px;">
+
@foreach (var source in updateSources)
{
@@ -158,6 +158,11 @@
}
}
+ private void OnUpdateSourceChanged()
+ {
+ StateHasChanged();
+ }
+
private MainWindow? FindMainWindow()
{
var current = Parent;
diff --git a/RTXLauncher.Fazor/AdvancedInstallPage.razor b/RTXLauncher.Fazor/AdvancedInstallPage.razor
index 8a7f70a..3da9e86 100644
--- a/RTXLauncher.Fazor/AdvancedInstallPage.razor
+++ b/RTXLauncher.Fazor/AdvancedInstallPage.razor
@@ -77,7 +77,7 @@
-
{ package.SelectedSource = v; StateHasChanged(); })" style="width: 200px;">
+
@foreach (var source in package.Sources)
{
@@ -89,7 +89,7 @@
{
-
{ package.SelectedRelease = v; StateHasChanged(); })" style="width: 200px;">
+
@foreach (var release in package.Releases)
{
@@ -199,6 +199,16 @@
isBusy = false;
StateHasChanged();
}
+
+ private void OnPackageSourceChanged()
+ {
+ StateHasChanged();
+ }
+
+ private void OnPackageReleaseChanged()
+ {
+ StateHasChanged();
+ }
protected override int BuildHash()
{
diff --git a/RTXLauncher.Fazor/InstallPage.razor b/RTXLauncher.Fazor/InstallPage.razor
index 93709ed..f18adc6 100644
--- a/RTXLauncher.Fazor/InstallPage.razor
+++ b/RTXLauncher.Fazor/InstallPage.razor
@@ -30,7 +30,7 @@
-
{ selectedFixesPackage = v; StateHasChanged(); })" style="width: 200px;">
+
@foreach (var package in availableFixesPackages)
{
@@ -214,6 +214,11 @@
}
}
+ private void OnFixesPackageChanged()
+ {
+ StateHasChanged();
+ }
+
private MainWindow? FindMainWindow()
{
var current = Parent;
diff --git a/RTXLauncher.Fazor/LauncherSettingsPage.razor b/RTXLauncher.Fazor/LauncherSettingsPage.razor
index 9528c82..5623d53 100644
--- a/RTXLauncher.Fazor/LauncherSettingsPage.razor
+++ b/RTXLauncher.Fazor/LauncherSettingsPage.razor
@@ -13,13 +13,13 @@
@if (settingsData != null)
{
-
{ settingsData.CheckForUpdatesOnLaunch = v; StateHasChanged(); })">
+
Check for updates on launch
-
{ selectedTheme = v; OnThemeChanged(v); })" style="width: 200px;">
+
@foreach (var theme in themes)
{
@@ -48,17 +48,21 @@
}
}
- private void OnThemeChanged(string newTheme)
+ private void OnThemeChanged()
{
- selectedTheme = newTheme;
if (settingsData != null)
{
- settingsData.Theme = newTheme;
+ settingsData.Theme = selectedTheme;
settingsService.SaveSettings(settingsData);
}
// Apply theme change
- ApplyTheme(newTheme);
+ ApplyTheme(selectedTheme);
+ StateHasChanged();
+ }
+
+ private void OnCheckForUpdatesChanged()
+ {
StateHasChanged();
}
diff --git a/RTXLauncher.Fazor/ModsPage.razor b/RTXLauncher.Fazor/ModsPage.razor
index 00e9355..9ad61c5 100644
--- a/RTXLauncher.Fazor/ModsPage.razor
+++ b/RTXLauncher.Fazor/ModsPage.razor
@@ -11,8 +11,8 @@
-
{ queryOptions.SearchText = v; StateHasChanged(); })" style="flex: 1;" />
- { selectedSortOption = v; StateHasChanged(); })" style="width: 180px;">
+
+
@foreach (var sort in sortOptions)
{
@@ -182,6 +182,16 @@
canGoToNextPage = mods.Count == 30; // ModDB shows 30 per page
}
+ private void OnSearchTextChanged()
+ {
+ StateHasChanged();
+ }
+
+ private void OnSortOptionChanged()
+ {
+ StateHasChanged();
+ }
+
private void ViewModDetails(ModItemViewModel mod)
{
var mainWindow = FindMainWindow();
diff --git a/RTXLauncher.Fazor/SettingsPage.razor b/RTXLauncher.Fazor/SettingsPage.razor
index 597a5aa..7ccfba9 100644
--- a/RTXLauncher.Fazor/SettingsPage.razor
+++ b/RTXLauncher.Fazor/SettingsPage.razor
@@ -16,21 +16,21 @@
-
{ selectedResolution = v; OnResolutionChanged(v); })" style="width: 250px;">
+
@foreach (var res in resolutions)
{
}
- { settingsData.UseCustomResolution = v; StateHasChanged(); })">Use Custom Resolution
+ Use Custom Resolution
@if (settingsData != null && settingsData.UseCustomResolution)
{
- { settingsData.Width = v; StateHasChanged(); })" placeholder="Width" style="width: 100px;" />
+
- { settingsData.Height = v; StateHasChanged(); })" placeholder="Height" style="width: 100px;" />
+
}
@@ -41,7 +41,7 @@
@if (settingsData != null)
{
- { settingsData.LoadWorkshopAddons = v; StateHasChanged(); })">Load Workshop Addons
+ Load Workshop Addons
}
@@ -54,7 +54,7 @@
-
{ selectedProtonBuild = v; OnProtonBuildChanged(v); })" style="width: 250px;">
+
@foreach (var label in protonBuildLabels)
{
@@ -68,7 +68,7 @@
{
- { settingsData.LinuxProtonPath = v; StateHasChanged(); })" placeholder="/path/to/proton/executable" style="flex: 1;" />
+
}
@@ -76,13 +76,13 @@
- { settingsData.LinuxSteamRootOverride = v; StateHasChanged(); })" placeholder="Leave empty for auto-detection" style="flex: 1;" />
+
- { settingsData.LinuxVulkanDriver = v; StateHasChanged(); })" style="width: 200px;">
+
@foreach (var driver in vulkanDriverOptions)
{
@@ -91,7 +91,7 @@
- { settingsData.LinuxEnableProtonLog = v; StateHasChanged(); })">Enable Proton Logging (PROTON_LOG=1)
+ Enable Proton Logging (PROTON_LOG=1)
}
@@ -101,12 +101,12 @@
{
-
{ settingsData.ConsoleEnabled = v; StateHasChanged(); })">Open Console
-
{ settingsData.DisableChromium = v; StateHasChanged(); })">Disable Chromium
+
Open Console
+
Disable Chromium
- { settingsData.CustomLaunchOptions = v; StateHasChanged(); })" placeholder="Custom Launch Options (e.g. -windowed -noborder)" style="flex: 1;" />
+
@@ -179,18 +179,18 @@
}
}
- private void OnResolutionChanged(string value)
+ private void OnResolutionChanged()
{
if (settingsData == null) return;
- if (value == "Native Resolution")
+ if (selectedResolution == "Native Resolution")
{
settingsData.Width = 0;
settingsData.Height = 0;
}
else
{
- var parts = value.Split('x');
+ var parts = selectedResolution.Split('x');
if (parts.Length == 2 && int.TryParse(parts[0], out var width) && int.TryParse(parts[1], out var height))
{
settingsData.Width = width;
@@ -200,6 +200,26 @@
StateHasChanged();
}
+ private void OnUseCustomResolutionChanged()
+ {
+ StateHasChanged();
+ }
+
+ private void OnWidthChanged()
+ {
+ StateHasChanged();
+ }
+
+ private void OnHeightChanged()
+ {
+ StateHasChanged();
+ }
+
+ private void OnLoadWorkshopAddonsChanged()
+ {
+ StateHasChanged();
+ }
+
private void LoadProtonBuilds()
{
if (settingsData == null) return;
@@ -245,14 +265,14 @@
}
}
- private void OnProtonBuildChanged(string value)
+ private void OnProtonBuildSelected()
{
- if (settingsData == null || string.IsNullOrEmpty(value)) return;
+ if (settingsData == null || string.IsNullOrEmpty(selectedProtonBuild)) return;
- settingsData.LinuxSelectedProtonLabel = value;
- isCustomProtonPathVisible = (value == "Custom");
+ settingsData.LinuxSelectedProtonLabel = selectedProtonBuild;
+ isCustomProtonPathVisible = (selectedProtonBuild == "Custom");
- if (value != "Custom" && !string.IsNullOrEmpty(settingsData.LinuxProtonPath))
+ if (selectedProtonBuild != "Custom" && !string.IsNullOrEmpty(settingsData.LinuxProtonPath))
{
settingsData.LinuxProtonPath = "";
}
@@ -277,11 +297,46 @@
if (availableProtonBuilds.Count > 0)
{
selectedProtonBuild = availableProtonBuilds[0].Label;
- OnProtonBuildChanged(selectedProtonBuild);
+ OnProtonBuildSelected();
}
StateHasChanged();
}
+ private void OnLinuxProtonPathChanged()
+ {
+ StateHasChanged();
+ }
+
+ private void OnLinuxSteamRootOverrideChanged()
+ {
+ StateHasChanged();
+ }
+
+ private void OnLinuxVulkanDriverChanged()
+ {
+ StateHasChanged();
+ }
+
+ private void OnLinuxEnableProtonLogChanged()
+ {
+ StateHasChanged();
+ }
+
+ private void OnConsoleEnabledChanged()
+ {
+ StateHasChanged();
+ }
+
+ private void OnDisableChromiumChanged()
+ {
+ StateHasChanged();
+ }
+
+ private void OnCustomLaunchOptionsChanged()
+ {
+ StateHasChanged();
+ }
+
private void LoadVulkanDrivers()
{
if (settingsData == null) return;
From 0d165d19a6a382497cf64379f2e90817853b1f72 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 11 Jan 2026 22:08:18 +0000
Subject: [PATCH 11/12] Use correct Fazor value binding pattern with
ValueChanged/OnTextEdited
Co-authored-by: Xenthio <28588188+Xenthio@users.noreply.github.com>
---
RTXLauncher.Fazor/AboutPage.razor | 5 +-
RTXLauncher.Fazor/AdvancedInstallPage.razor | 12 +-
RTXLauncher.Fazor/InstallPage.razor | 5 +-
RTXLauncher.Fazor/LauncherSettingsPage.razor | 17 ++-
RTXLauncher.Fazor/ModsPage.razor | 10 +-
RTXLauncher.Fazor/SettingsPage.razor | 113 +++++++++++++------
6 files changed, 111 insertions(+), 51 deletions(-)
diff --git a/RTXLauncher.Fazor/AboutPage.razor b/RTXLauncher.Fazor/AboutPage.razor
index 285e199..a31db42 100644
--- a/RTXLauncher.Fazor/AboutPage.razor
+++ b/RTXLauncher.Fazor/AboutPage.razor
@@ -20,7 +20,7 @@
-
+
@foreach (var source in updateSources)
{
@@ -158,8 +158,9 @@
}
}
- private void OnUpdateSourceChanged()
+ private void OnUpdateSourceChanged(string value)
{
+ selectedUpdateSource = value;
StateHasChanged();
}
diff --git a/RTXLauncher.Fazor/AdvancedInstallPage.razor b/RTXLauncher.Fazor/AdvancedInstallPage.razor
index 3da9e86..8d8d308 100644
--- a/RTXLauncher.Fazor/AdvancedInstallPage.razor
+++ b/RTXLauncher.Fazor/AdvancedInstallPage.razor
@@ -77,7 +77,7 @@
-
+
@foreach (var source in package.Sources)
{
@@ -89,7 +89,7 @@
{
-
+
@foreach (var release in package.Releases)
{
@@ -200,13 +200,17 @@
StateHasChanged();
}
- private void OnPackageSourceChanged()
+ private void OnPackageSourceChanged(string value)
{
+ // Note: This would need to know which package changed
+ // For now just trigger state update
StateHasChanged();
}
- private void OnPackageReleaseChanged()
+ private void OnPackageReleaseChanged(string value)
{
+ // Note: This would need to know which package changed
+ // For now just trigger state update
StateHasChanged();
}
diff --git a/RTXLauncher.Fazor/InstallPage.razor b/RTXLauncher.Fazor/InstallPage.razor
index f18adc6..7431b56 100644
--- a/RTXLauncher.Fazor/InstallPage.razor
+++ b/RTXLauncher.Fazor/InstallPage.razor
@@ -30,7 +30,7 @@
-
+
@foreach (var package in availableFixesPackages)
{
@@ -214,8 +214,9 @@
}
}
- private void OnFixesPackageChanged()
+ private void OnFixesPackageChanged(string value)
{
+ selectedFixesPackage = value;
StateHasChanged();
}
diff --git a/RTXLauncher.Fazor/LauncherSettingsPage.razor b/RTXLauncher.Fazor/LauncherSettingsPage.razor
index 5623d53..ffd4dd7 100644
--- a/RTXLauncher.Fazor/LauncherSettingsPage.razor
+++ b/RTXLauncher.Fazor/LauncherSettingsPage.razor
@@ -13,13 +13,13 @@
@if (settingsData != null)
{
-
+
Check for updates on launch
-
+
@foreach (var theme in themes)
{
@@ -48,21 +48,26 @@
}
}
- private void OnThemeChanged()
+ private void OnThemeChanged(string newTheme)
{
+ selectedTheme = newTheme;
if (settingsData != null)
{
- settingsData.Theme = selectedTheme;
+ settingsData.Theme = newTheme;
settingsService.SaveSettings(settingsData);
}
// Apply theme change
- ApplyTheme(selectedTheme);
+ ApplyTheme(newTheme);
StateHasChanged();
}
- private void OnCheckForUpdatesChanged()
+ private void OnCheckForUpdatesChanged(bool value)
{
+ if (settingsData != null)
+ {
+ settingsData.CheckForUpdatesOnLaunch = value;
+ }
StateHasChanged();
}
diff --git a/RTXLauncher.Fazor/ModsPage.razor b/RTXLauncher.Fazor/ModsPage.razor
index 9ad61c5..9e24ef1 100644
--- a/RTXLauncher.Fazor/ModsPage.razor
+++ b/RTXLauncher.Fazor/ModsPage.razor
@@ -11,8 +11,8 @@
-
-
+
+
@foreach (var sort in sortOptions)
{
@@ -182,13 +182,15 @@
canGoToNextPage = mods.Count == 30; // ModDB shows 30 per page
}
- private void OnSearchTextChanged()
+ private void OnSearchTextChanged(string value)
{
+ queryOptions.SearchText = value;
StateHasChanged();
}
- private void OnSortOptionChanged()
+ private void OnSortOptionChanged(string value)
{
+ selectedSortOption = value;
StateHasChanged();
}
diff --git a/RTXLauncher.Fazor/SettingsPage.razor b/RTXLauncher.Fazor/SettingsPage.razor
index 7ccfba9..3f9b6a5 100644
--- a/RTXLauncher.Fazor/SettingsPage.razor
+++ b/RTXLauncher.Fazor/SettingsPage.razor
@@ -16,21 +16,21 @@
-
+
@foreach (var res in resolutions)
{
}
- Use Custom Resolution
+ Use Custom Resolution
@if (settingsData != null && settingsData.UseCustomResolution)
{
-
+
-
+
}
@@ -41,7 +41,7 @@
@if (settingsData != null)
{
- Load Workshop Addons
+ Load Workshop Addons
}
@@ -54,7 +54,7 @@
-
+
@foreach (var label in protonBuildLabels)
{
@@ -68,7 +68,7 @@
{
-
+
}
@@ -76,13 +76,13 @@
-
+
-
+
@foreach (var driver in vulkanDriverOptions)
{
@@ -91,7 +91,7 @@
- Enable Proton Logging (PROTON_LOG=1)
+ Enable Proton Logging (PROTON_LOG=1)
}
@@ -101,12 +101,12 @@
{
-
Open Console
-
Disable Chromium
+
Open Console
+
Disable Chromium
-
+
@@ -179,18 +179,20 @@
}
}
- private void OnResolutionChanged()
+ private void OnResolutionChanged(string value)
{
if (settingsData == null) return;
- if (selectedResolution == "Native Resolution")
+ selectedResolution = value;
+
+ if (value == "Native Resolution")
{
settingsData.Width = 0;
settingsData.Height = 0;
}
else
{
- var parts = selectedResolution.Split('x');
+ var parts = value.Split('x');
if (parts.Length == 2 && int.TryParse(parts[0], out var width) && int.TryParse(parts[1], out var height))
{
settingsData.Width = width;
@@ -200,23 +202,39 @@
StateHasChanged();
}
- private void OnUseCustomResolutionChanged()
+ private void OnUseCustomResolutionChanged(bool value)
{
+ if (settingsData != null)
+ {
+ settingsData.UseCustomResolution = value;
+ }
StateHasChanged();
}
- private void OnWidthChanged()
+ private void OnWidthChanged(string value)
{
+ if (settingsData != null && int.TryParse(value, out var width))
+ {
+ settingsData.Width = width;
+ }
StateHasChanged();
}
- private void OnHeightChanged()
+ private void OnHeightChanged(string value)
{
+ if (settingsData != null && int.TryParse(value, out var height))
+ {
+ settingsData.Height = height;
+ }
StateHasChanged();
}
- private void OnLoadWorkshopAddonsChanged()
+ private void OnLoadWorkshopAddonsChanged(bool value)
{
+ if (settingsData != null)
+ {
+ settingsData.LoadWorkshopAddons = value;
+ }
StateHasChanged();
}
@@ -265,14 +283,15 @@
}
}
- private void OnProtonBuildSelected()
+ private void OnProtonBuildSelected(string value)
{
- if (settingsData == null || string.IsNullOrEmpty(selectedProtonBuild)) return;
+ if (settingsData == null || string.IsNullOrEmpty(value)) return;
- settingsData.LinuxSelectedProtonLabel = selectedProtonBuild;
- isCustomProtonPathVisible = (selectedProtonBuild == "Custom");
+ selectedProtonBuild = value;
+ settingsData.LinuxSelectedProtonLabel = value;
+ isCustomProtonPathVisible = (value == "Custom");
- if (selectedProtonBuild != "Custom" && !string.IsNullOrEmpty(settingsData.LinuxProtonPath))
+ if (value != "Custom" && !string.IsNullOrEmpty(settingsData.LinuxProtonPath))
{
settingsData.LinuxProtonPath = "";
}
@@ -297,43 +316,71 @@
if (availableProtonBuilds.Count > 0)
{
selectedProtonBuild = availableProtonBuilds[0].Label;
- OnProtonBuildSelected();
+ OnProtonBuildSelected(selectedProtonBuild);
}
StateHasChanged();
}
- private void OnLinuxProtonPathChanged()
+ private void OnLinuxProtonPathChanged(string value)
{
+ if (settingsData != null)
+ {
+ settingsData.LinuxProtonPath = value;
+ }
StateHasChanged();
}
- private void OnLinuxSteamRootOverrideChanged()
+ private void OnLinuxSteamRootOverrideChanged(string value)
{
+ if (settingsData != null)
+ {
+ settingsData.LinuxSteamRootOverride = value;
+ }
StateHasChanged();
}
- private void OnLinuxVulkanDriverChanged()
+ private void OnLinuxVulkanDriverChanged(string value)
{
+ if (settingsData != null)
+ {
+ settingsData.LinuxVulkanDriver = value;
+ }
StateHasChanged();
}
- private void OnLinuxEnableProtonLogChanged()
+ private void OnLinuxEnableProtonLogChanged(bool value)
{
+ if (settingsData != null)
+ {
+ settingsData.LinuxEnableProtonLog = value;
+ }
StateHasChanged();
}
- private void OnConsoleEnabledChanged()
+ private void OnConsoleEnabledChanged(bool value)
{
+ if (settingsData != null)
+ {
+ settingsData.ConsoleEnabled = value;
+ }
StateHasChanged();
}
- private void OnDisableChromiumChanged()
+ private void OnDisableChromiumChanged(bool value)
{
+ if (settingsData != null)
+ {
+ settingsData.DisableChromium = value;
+ }
StateHasChanged();
}
- private void OnCustomLaunchOptionsChanged()
+ private void OnCustomLaunchOptionsChanged(string value)
{
+ if (settingsData != null)
+ {
+ settingsData.CustomLaunchOptions = value;
+ }
StateHasChanged();
}
From 4b168c426a9c04e1a2c8b0eae9429c89035eda94 Mon Sep 17 00:00:00 2001
From: Xenthio <28588188+Xenthio@users.noreply.github.com>
Date: Fri, 31 Jul 2026 08:14:51 +1000
Subject: [PATCH 12/12] Update SimpleDarkTheme.scss
---
RTXLauncher.Fazor/Assets/SimpleDarkTheme.scss | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/RTXLauncher.Fazor/Assets/SimpleDarkTheme.scss b/RTXLauncher.Fazor/Assets/SimpleDarkTheme.scss
index 02b0daa..8adb14a 100644
--- a/RTXLauncher.Fazor/Assets/SimpleDarkTheme.scss
+++ b/RTXLauncher.Fazor/Assets/SimpleDarkTheme.scss
@@ -418,6 +418,18 @@ progressbar,
}
}
+.selectlist {
+ .selectlist-item {
+ &:hover {
+ background-color: $button-face;
+ }
+
+ &.selected {
+ background-color: $highlight;
+ color: $highlight-text;
+ }
+ }
+}
.simple-border {
border: 1px solid #808080;