Skip to content

Commit 2b23b04

Browse files
committed
Merge branch 'main' into discord-rpc
2 parents a163de1 + a8f2e88 commit 2b23b04

File tree

113 files changed

+5375
-1117
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

113 files changed

+5375
-1117
lines changed

CHANGELOG.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,46 @@
1+
# v0.14.3 2025-07-30
2+
This release adds search to the Library and enables creating collections. We also worked on adding support for the Epic Games Store version of Cyberpunk 2077; however, due to some problems with the 2.3 update, we've delayed this feature to a future release.
3+
4+
## Create Collections
5+
You can now organise your installed mods into collections in the left menu, allowing you to set up groups of mods that you can quickly toggle on or off. Simply click the new option below "My Mods" to get started.
6+
7+
![An animation showing the process of creating a new collection in the app.](https://staticdelivery.nexusmods.com/mods/2295/images/26/26-1753777005-5560856.gif)
8+
9+
From the Library page, you can select which Collection to add mods to. The newest collection will be the default option.
10+
11+
![An animation showing how to add multiple mods to a specific collection from the Library.](https://staticdelivery.nexusmods.com/mods/2295/images/26/26-1753777011-1671552141.gif)
12+
13+
In future versions, we will enable sharing your collections on Nexus Mods and editing collections you have downloaded from the website.
14+
15+
**Note: Uploading collections to Nexus Mods is disabled by default in this release. You can enable it in the Experimental Settings, but use it at your own risk.**
16+
17+
## Library Search
18+
One of the most requested features from community feedback was the ability to search installed mods. We've added this to the Library section and will be including it on other relevant pages in a future release.
19+
20+
![The Library filtered by the term "better" show results.](./docs/changelog-assets/90ea236a6c44e03555f00d8d155eb881.webp)
21+
22+
## More Features
23+
* The app will now load the latest game version data every 30 minutes in the background.
24+
25+
## Technical Changes
26+
* Added validation to app settings to prevent bad values from being saved.
27+
* Collection metadata will now refresh each time the page is loaded in the app.
28+
* Improve error reporting for the Nexus Mods API requests.
29+
30+
## Bug Fixes
31+
* Fixed a bug which prevented the app from properly recognising GOG DLCs.
32+
* Fixed an exception that would often appear when viewing External Changes.
33+
* Improved the error reporting around "Missing archive" errors to provide more useful information that will help fix it.
34+
35+
## Known Issues
36+
* Once a collection created by the user is removed from the app, it can no longer be edited, even if it was exported to the website.
37+
* Exported collections do not include a valid version number.
38+
* The game version is not checked when adding a collection, meaning you can install outdated mods without being warned.
39+
* The sort order for some columns does not work as expected.
40+
* The table header sorting and active tab states are not saved and are reset each time the view is loaded.
41+
* When deleting the second-to-last editable collection, an error message will show. This can be ignored.
42+
43+
144
# v0.13.4 2025-07-02
245
This release includes the ability to change your storage location, new game artwork and lots of bug fixes.
346

Binary file not shown.
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
# Jobs System
2+
3+
## Overview
4+
5+
The Jobs system in NexusMods.App is how long-running background tasks are spawned.
6+
7+
These feature progress tracking, cancellation support, and error handling.
8+
9+
Examples of jobs include downloading files and installations.
10+
11+
## Architecture
12+
13+
The Jobs system is built around several key components that work together to provide a comprehensive task management solution:
14+
15+
```mermaid
16+
graph TB
17+
subgraph "Job States"
18+
Created[Created]
19+
Running[Running]
20+
Paused[Paused]
21+
Completed[Completed]
22+
Cancelled[Cancelled]
23+
Failed[Failed]
24+
end
25+
26+
%% Job lifecycle
27+
Created --> Running
28+
Running --> Paused
29+
Paused --> Running
30+
Running --> Completed
31+
Running --> Cancelled
32+
Running --> Failed
33+
Paused --> Cancelled
34+
```
35+
36+
```mermaid
37+
graph LR
38+
subgraph "Job Creation & Usage"
39+
User[User Code] --> |creates| IJobDefinitionWithStart
40+
IJobDefinitionWithStart --> |passed to via 'Begin'| JobMonitor
41+
JobMonitor --> |creates| JobContext
42+
JobMonitor --> |returns| JobTask
43+
JobTask --> |returned to| User
44+
JobTask --> |wraps| JobContext
45+
JobContext --> |contains and cancels using| JobGroup["JobGroup<br/>(with cancellation token)"]
46+
JobContext --> |calls StartAsync on| IJobDefinitionWithStart
47+
end
48+
```
49+
50+
!!! note "JobGroup instances are created internally via JobGroupCreator"
51+
52+
But that class is currently effectively unused.
53+
54+
## Core Components
55+
56+
### IJobDefinition
57+
58+
The base interface for all job definitions. Job definitions describe what work needs to be done and contain any parameters needed for execution.
59+
60+
```csharp
61+
public interface IJobDefinition;
62+
63+
public interface IJobDefinition<TResultType> : IJobDefinition
64+
where TResultType : notnull;
65+
```
66+
67+
### IJobDefinitionWithStart
68+
69+
A specialized job definition that includes its own execution logic via the `StartAsync` method:
70+
71+
```csharp
72+
public interface IJobDefinitionWithStart<in TParent, TResultType> : IJobDefinition<TResultType>
73+
where TParent : IJobDefinition<TResultType>
74+
where TResultType : notnull
75+
{
76+
ValueTask<TResultType> StartAsync(IJobContext<TParent> context);
77+
}
78+
```
79+
80+
### IJobMonitor
81+
82+
The central component that manages job execution, tracking, and lifecycle:
83+
84+
```csharp
85+
public interface IJobMonitor
86+
{
87+
// Start a job with external task logic
88+
IJobTask<TJobType, TResultType> Begin<TJobType, TResultType>(
89+
TJobType job,
90+
Func<IJobContext<TJobType>, ValueTask<TResultType>> task);
91+
92+
// Start a self-executing job
93+
IJobTask<TJobType, TResultType> Begin<TJobType, TResultType>(TJobType job);
94+
95+
// Job management
96+
void Cancel(JobId jobId);
97+
void CancelGroup(IJobGroup group);
98+
void CancelAll();
99+
100+
// Observability
101+
ReadOnlyObservableCollection<IJob> Jobs { get; }
102+
IObservable<IChangeSet<IJob, JobId>> GetObservableChangeSet<TJob>();
103+
}
104+
```
105+
106+
### IJobContext
107+
108+
Provides the execution context for jobs, including progress reporting and cancellation support:
109+
110+
```csharp
111+
public interface IJobContext<out TJobDefinition> : IJobContext
112+
where TJobDefinition: IJobDefinition
113+
{
114+
TJobDefinition Definition { get; }
115+
Task YieldAsync();
116+
CancellationToken CancellationToken { get; }
117+
IJobMonitor Monitor { get; }
118+
IJobGroup Group { get; }
119+
120+
void SetPercent<TVal>(TVal current, TVal max);
121+
void SetRateOfProgress(double rate);
122+
}
123+
```
124+
125+
### IJobGroup
126+
127+
Groups related jobs together for batch operations and shared cancellation:
128+
129+
```csharp
130+
public interface IJobGroup : IReadOnlyCollection<IJob>
131+
{
132+
CancellationToken CancellationToken { get; }
133+
void Cancel();
134+
bool IsCancelled { get; }
135+
}
136+
```
137+
138+
## Usage Examples
139+
140+
**📋 [View All Job Examples - (in `src/Examples/Jobs`)](https://github.com/Nexus-Mods/NexusMods.App/tree/main/src/Examples/Jobs)**

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ nav:
112112
- UI Coding Guidelines: developers/development-guidelines/UICodingGuidelines.md
113113
- UI Styling Guidelines: developers/development-guidelines/UIStylingGuidelines.md
114114
- Using Workspaces: developers/development-guidelines/UsingWorkspaces.md
115+
- Jobs System: developers/development-guidelines/JobsSystem.md
115116
- Diagnostics: developers/development-guidelines/Diagnostics.md
116117
- Processes:
117118
- Release Process: developers/development-guidelines/Processes/ReleaseProcess.md

src/Examples/Examples.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,13 @@
1212
<PackageReference Include="Avalonia.Controls.DataGrid" />
1313
<PackageReference Include="Avalonia.Svg.Skia" />
1414
<PackageReference Include="Projektanker.Icons.Avalonia.MaterialDesign" />
15+
<PackageReference Include="NexusMods.Paths" />
16+
<PackageReference Include="Microsoft.Extensions.Logging" />
1517
</ItemGroup>
1618

1719
<ItemGroup>
20+
<ProjectReference Include="..\NexusMods.Jobs\NexusMods.Jobs.csproj" />
21+
<ProjectReference Include="..\NexusMods.FileExtractor\NexusMods.FileExtractor.csproj" />
1822
<ProjectReference Include="..\NexusMods.Abstractions.Settings\NexusMods.Abstractions.Settings.csproj" />
1923
<ProjectReference Include="..\NexusMods.App.UI\NexusMods.App.UI.csproj" />
2024
<ProjectReference Include="..\NexusMods.App\NexusMods.App.csproj" />

src/Examples/Jobs/README.md

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# Jobs Examples
2+
3+
This folder contains examples demonstrating how to use the NexusMods.App Jobs
4+
system for background task management with progress tracking, cancellation
5+
support, and error handling.
6+
7+
## Usage Examples
8+
9+
### Creating a Simple Job Definition
10+
11+
Simple job definitions (using the `Begin` method with a lambda) are used when
12+
you want to handle the execution logic in the same method where you call
13+
`Begin`.
14+
15+
The job definition serves as context/data, while the actual work is done in the
16+
lambda callback right where you need it.
17+
18+
See [**SimpleJobDefinitionExample.cs**](../../../tests/NexusMods.Jobs.Tests/Examples/SimpleJobDefinitionExample.cs).
19+
20+
### Self-Executing Job
21+
22+
A job with entirely self-contained logic/context, to fire and (sometimes)
23+
forget.
24+
25+
The fields you store in the job definition are effectively method parameters
26+
passed in to run the job.
27+
28+
See [**SelfExecutingJobExample.cs**](../../../tests/NexusMods.Jobs.Tests/Examples/SelfExecutingJobExample.cs).
29+
30+
## Best Practices
31+
32+
### Cancellation and Error Handling
33+
34+
Always call `context.YieldAsync()` around expensive, time-consuming code to
35+
support job cancellation.
36+
37+
**For Job Callers:** Use `JobMonitor.Cancel()`, `CancelGroup()`, or
38+
`CancelAll()` to cancel jobs.
39+
40+
**For Job Implementations:** Use `YieldAsync()` to check for cancellation at
41+
natural breakpoints.
42+
43+
**Jobs Cancelling Themselves:** Jobs can cancel themselves using
44+
`context.CancelAndThrow()` when conditions require termination.
45+
46+
**Interactive Cancellation:** Jobs may interact with external components (like
47+
UI dialogs) that can cancel. Propagate external `OperationCanceledException`
48+
through `context.CancelAndThrow()`.
49+
50+
See [**CancellationExample.cs**](../../../tests/NexusMods.Jobs.Tests/Examples/BestPractices/CancellationExample.cs) and
51+
[**InteractiveCancellationExample.cs**](../../../tests/NexusMods.Jobs.Tests/Examples/BestPractices/InteractiveCancellationExample.cs).
52+
53+
### Progress Reporting
54+
55+
Use percentage for determinate progress when total work is known. For
56+
indeterminate progress, use `Size.One` as maximum to avoid division by zero.
57+
58+
See [**DeterminateProgressExample.cs**](../../../tests/NexusMods.Jobs.Tests/Examples/BestPractices/DeterminateProgressExample.cs)
59+
and [**IndeterminateProgressExample.cs**](../../../tests/NexusMods.Jobs.Tests/Examples/BestPractices/IndeterminateProgressExample.cs).
60+
61+
### Factory Methods
62+
63+
Often you want to fire a job right away after it is created. In this case,
64+
make `Create()` helper functions to encapsulate the job creation and job
65+
starting logic as one.
66+
67+
Factory methods encapsulate dependency injection and job configuration,
68+
creating reusable job creation patterns.
69+
70+
See [**FactoryMethodExample.cs**](../../../tests/NexusMods.Jobs.Tests/Examples/BestPractices/FactoryMethodExample.cs).
71+
72+
## General Guidelines
73+
74+
- Use `record` types for job definitions as they should not mutate parameters
75+
- Handle resource cleanup within job execution logic before completion
76+
- Follow existing codebase patterns
77+
78+
## Caveats
79+
80+
!!! warning "Job Disposal Not Implemented"
81+
82+
While jobs can implement `IDisposable` or `IAsyncDisposable`, they're not
83+
actually disposed by the job system. If you need resource cleanup, handle
84+
it within the job's execution logic before completion.
85+
86+
## Common Patterns
87+
88+
- Use `record` types for job definitions as they should not mutate their
89+
parameters
90+
- Call `context.YieldAsync()` around expensive operations to support
91+
cancellation
92+
- Use `SetPercent()` with `Size.From()` for progress reporting
93+
- Access job data through `context.Definition`
94+
95+
## Related Documentation
96+
97+
For comprehensive architecture details and core interfaces, see the
98+
[Jobs System Documentation](../../../docs/developers/development-guidelines/JobsSystem.md).

src/Examples/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ public class Program
88
{
99
[STAThread]
1010
public static int Main() => 0;
11-
11+
1212
/// <summary>
1313
/// Don't Delete this method. It's used by the Avalonia Designer.
1414
/// </summary>

src/Examples/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ and [Using Workspaces](https://nexus-mods.github.io/NexusMods.App/development-gu
99

1010
- [Diagnostics](./Diagnostics/README.md)
1111
- [Using Diagnostics](./Diagnostics/UsingDiagnostics.cs)
12+
- [Jobs](./Jobs/README.md)
13+
- [Simple Job Definition Example](./Jobs/SimpleJobDefinitionExample.cs)
14+
- [Self-Executing Job Example](./Jobs/SelfExecutingJobExample.cs)
15+
- [Best Practices](./Jobs/BestPractices/README.md)
16+
- [Cancellation Examples](./Jobs/BestPractices/CancellationExample.cs)
17+
- [Progress Reporting Examples](./Jobs/BestPractices/ProgressReportingExample.cs)
18+
- [Factory Method Examples](./Jobs/BestPractices/FactoryMethodExample.cs)
1219
- [Workspaces](./Workspaces/README.md)
1320
- [Querying Windows and Workspaces](./Workspaces/001-querying-windows-and-workspaces.cs)
1421
- [Opening new Pages in a Workspace](./Workspaces/002-opening-new-pages.cs)

src/NexusMods.Abstractions.Games.Diagnostics/Emitters/ILoadoutDiagnosticEmitter.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
using System.Collections.Frozen;
12
using JetBrains.Annotations;
3+
using NexusMods.Abstractions.GameLocators;
24
using NexusMods.Abstractions.Loadouts;
5+
using NexusMods.Abstractions.Loadouts.Synchronizers;
36

47
namespace NexusMods.Abstractions.Diagnostics.Emitters;
58

@@ -16,5 +19,14 @@ public interface ILoadoutDiagnosticEmitter : IDiagnosticEmitter
1619
/// <summary>
1720
/// Diagnoses a loadout and creates instances of <see cref="Diagnostic"/>.
1821
/// </summary>
22+
[Obsolete("To be replaced with the overload that takes in the sync tree")]
1923
IAsyncEnumerable<Diagnostic> Diagnose(Loadout.ReadOnly loadout, CancellationToken cancellationToken);
24+
25+
/// <summary>
26+
/// Diagnoses a loadout and creates instances of <see cref="Diagnostic"/>.
27+
/// </summary>
28+
IAsyncEnumerable<Diagnostic> Diagnose(Loadout.ReadOnly loadout, FrozenDictionary<GamePath, SyncNode> syncTree, CancellationToken cancellationToken)
29+
{
30+
return Diagnose(loadout, cancellationToken);
31+
}
2032
}

src/NexusMods.Abstractions.Jobs/IJobMonitor.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ IJobTask<TJobType, TResultType> Begin<TJobType, TResultType>(TJobType job, Func<
1515
where TJobType : IJobDefinition<TResultType>
1616
where TResultType : notnull;
1717

18-
1918
/// <summary>
2019
/// Starts a job given the job definition.
2120
/// </summary>
@@ -38,6 +37,11 @@ IJobTask<TJobType, TResultType> Begin<TJobType, TResultType>(TJobType job)
3837
/// </summary>
3938
void Cancel(JobId jobId);
4039

40+
/// <summary>
41+
/// Cancels a specific job by its task
42+
/// </summary>
43+
void Cancel(IJobTask jobTask);
44+
4145
/// <summary>
4246
/// Cancels all jobs in the specified group
4347
/// </summary>

0 commit comments

Comments
 (0)