Skip to content

fix: reorganize settings UI, fix content priority resolution, and eliminate CAS warnings (#281, #282, #283, #284)#285

Open
undead2146 wants to merge 7 commits intodevelopmentfrom
fix/settings-ui-and-content-priority
Open

fix: reorganize settings UI, fix content priority resolution, and eliminate CAS warnings (#281, #282, #283, #284)#285
undead2146 wants to merge 7 commits intodevelopmentfrom
fix/settings-ui-and-content-priority

Conversation

@undead2146
Copy link
Copy Markdown
Member

@undead2146 undead2146 commented Mar 16, 2026

Overview

This PR resolves four GitHub issues related to settings UI organization, content priority , and CAS storage warnings.

Changes

Settings UI Reorganization

Version Widget Enhancement

  • Make the version widget in the application clickable #283:
    Made the version widget in the bottom-right corner clickable
    • Clicking now copies the full version (including git hash) to clipboard
    • Shows a success notification when copied
    • Added tooltip "Click to copy version to clipboard"
    • Maintains original visual appearance with hand cursor on hover

Content Priority Fix

  • Community Outpost Zero Hour Camera mod has no effect #284:
    Fixed mods not taking effect in-game due to incorrect file priority resolution
    • Root cause: FullCopyStrategy was sorting by enum value instead of content type priority
    • Changed to use ContentTypePriority.GetPriority() for proper ordering
    • Ensures mod files (priority 100) correctly override base game files (priority 10)
    • Priority order: Mod (100) > Patch (90) > GameClient (50) > Addon (40) > GameInstallation (10)

CAS Storage Warning Fix

  • Eliminated "Could not resolve source path for manifest" warnings for CAS-stored content
    • Added source.path marker file creation after CAS storage
    • Updated ContentManifestPool to handle CAS-ONLY marker gracefully
    • Prevents spurious warnings in logs for downloaded content

Related Issues

Closes #281
Closes #282
Closes #283
Closes #284

Greptile Summary

This PR addresses four GitHub issues: settings UI reorganization (#281, #282), a clickable version widget (#283), a content priority fix (#284), and CAS storage warning elimination. The core logic fixes are solid — ContentTypePriority is now exhaustively mapped and FullCopyStrategy correctly uses semantic priority ordering. However, two issues remain:

  • "CAS-ONLY" magic string: The sentinel value written by ContentStorageService and compared in ContentManifestPool is hardcoded in both places rather than extracted to a shared constant in FileTypes.cs (which this same PR already uses for SourcePathFileName).
  • Missing cleanup in StoreCasContentAsync catch block: The PR adds Directory.CreateDirectory(contentDir) + File.WriteAllTextAsync(..., "CAS-ONLY", ...) to the CAS storage happy path, but the catch block's comment and cleanup logic were not updated — the newly created contentDir is not removed on failure, leaving an orphan directory.
  • Undocumented camera mod removal (flagged in a previous thread, still unresolved): Three camera mod entries (crgn, crzh, dczh) are removed from GenPatcherContentRegistry with no mention in any of the referenced issues or the PR description.

Confidence Score: 3/5

  • Mostly safe to merge, but the undocumented camera mod removal and the CAS cleanup gap warrant author confirmation before landing.
  • The core bug fixes (priority ordering, ContentTypePriority exhaustiveness, CAS warning suppression) are correct and well-tested by the backup/restore cleanup logic in StoreManifestOnlyAsync. The UI changes are clean. Score is reduced by: (1) the unexplained deletion of three GenPatcherContentRegistry entries with user-visible impact, and (2) the incomplete catch-block cleanup in StoreCasContentAsync which could leave orphan directories on disk.
  • GenHub/GenHub.Core/Models/CommunityOutpost/GenPatcherContentRegistry.cs (undocumented removal of camera mod entries) and GenHub/GenHub/Features/Content/Services/ContentStorageService.cs (incomplete catch-block cleanup for CAS path).

Important Files Changed

Filename Overview
GenHub/GenHub.Core/Constants/FileTypes.cs Adds SourcePathFileName = "source.path" constant. Good extraction, but "CAS-ONLY" sentinel value should also be added here as a constant.
GenHub/GenHub.Core/Models/CommunityOutpost/GenPatcherContentRegistry.cs Removes the "Camera Modifications" block (crgn, crzh, dczh entries) with no explanation in referenced issues or PR description. This is unrelated to issues #281#284 and will prevent users from installing camera mods via GenPatcher.
GenHub/GenHub.Core/Models/Workspace/ContentTypePriority.cs Exhaustively maps all 19 ContentType enum values. 15 types get numeric priorities (10–100); 4 meta/reference types explicitly throw ArgumentException. The _ fallback throws ArgumentOutOfRangeException to catch future unmapped additions. Resolves the previous thread concern entirely.
GenHub/GenHub/Features/Content/Services/ContentStorageService.cs Adds CAS-ONLY marker file creation to StoreCasContentAsync and adds thorough backup/restore cleanup logic to StoreManifestOnlyAsync. Two issues: (1) "CAS-ONLY" is a hardcoded magic string that should be a constant in FileTypes.cs; (2) the catch block in StoreCasContentAsync does not clean up the newly created contentDir.
GenHub/GenHub/Features/Manifest/ContentManifestPool.cs Correctly handles the new CAS-ONLY sentinel by returning null success instead of treating it as a real directory path. Uses StringComparison.OrdinalIgnoreCase which is good, but the "CAS-ONLY" literal should be a shared constant.
GenHub/GenHub/Features/Workspace/Strategies/FullCopyStrategy.cs Fixes the root cause of #284 by replacing OrderBy(item.Manifest.ContentType) (enum integer ordering) with OrderBy(ContentTypePriority.GetPriority(...)) (semantic priority ordering). Comment updated to reflect correct priority values.

Sequence Diagram

sequenceDiagram
    participant CS as ContentStorageService
    participant FS as FileSystem
    participant CAS as CasReferenceTracker
    participant MP as ContentManifestPool

    Note over CS,MP: StoreCasContentAsync (happy path)
    CS->>FS: StoreContentFilesAsync (CAS hash storage)
    CS->>CAS: TrackManifestReferencesAsync
    CS->>FS: WriteAllTextAsync(manifest.json)
    CS->>FS: CreateDirectory(contentDir)
    CS->>FS: WriteAllTextAsync(source.path, "CAS-ONLY")

    Note over CS,MP: Later — GetContentDirectoryAsync
    MP->>FS: ReadAllTextAsync(source.path)
    FS-->>MP: "CAS-ONLY"
    MP-->>MP: sourcePath == "CAS-ONLY" → return null (no warnings)

    Note over CS,MP: StoreCasContentAsync (failure path — gap)
    CS->>FS: WriteAllTextAsync(manifest.json) ✓
    CS->>FS: CreateDirectory(contentDir) ✓
    CS->>FS: WriteAllTextAsync(source.path, "CAS-ONLY") ✗ throws
    CS->>FS: DeleteFileIfExists(manifest.json)
    CS->>CAS: UntrackManifestAsync
    Note over FS: contentDir orphaned (not cleaned up)
Loading

Comments Outside Diff (2)

  1. GenHub/GenHub/Features/Content/Services/ContentStorageService.cs, line 301-323 (link)

    Failure cleanup in StoreContentAsync does not cover the newly written CAS-ONLY marker

    The StoreManifestOnlyAsync path received careful rollback logic (backup + restore or delete of source.path), but the equivalent cleanup is missing from StoreContentAsync. If WriteAllTextAsync(sourcePathFile, "CAS-ONLY", ...) at line 296 succeeds and a subsequent step throws (unlikely in current code, but fragile if the method grows), the contentDir and its source.path file will be left on disk even though the manifest JSON has been deleted.

    For consistency with the rollback strategy applied in StoreManifestOnlyAsync, consider adding symmetric cleanup here:

    catch (Exception ex)
    {
        _logger.LogError(ex, "Failed to store content for manifest {ManifestId}", manifest.Id);
        try
        {
            FileOperationsService.DeleteFileIfExists(manifestPath);
    
            // Clean up the CAS-ONLY marker and its directory if we created them
            var contentDir = Path.Combine(_storageRoot, DirectoryNames.Data, updatedManifest.Id.Value);
            var sourcePathFile = Path.Combine(contentDir, FileTypes.SourcePathFileName);
            FileOperationsService.DeleteFileIfExists(sourcePathFile);
            if (Directory.Exists(contentDir) && !Directory.EnumerateFileSystemEntries(contentDir).Any())
                Directory.Delete(contentDir);
    
            var untrackResult = await _referenceTracker.UntrackManifestAsync(manifest.Id, CancellationToken.None);
            ...
        }
    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: GenHub/GenHub/Features/Content/Services/ContentStorageService.cs
    Line: 301-323
    
    Comment:
    **Failure cleanup in `StoreContentAsync` does not cover the newly written CAS-ONLY marker**
    
    The `StoreManifestOnlyAsync` path received careful rollback logic (backup + restore or delete of `source.path`), but the equivalent cleanup is missing from `StoreContentAsync`. If `WriteAllTextAsync(sourcePathFile, "CAS-ONLY", ...)` at line 296 succeeds and a subsequent step throws (unlikely in current code, but fragile if the method grows), the `contentDir` and its `source.path` file will be left on disk even though the manifest JSON has been deleted.
    
    For consistency with the rollback strategy applied in `StoreManifestOnlyAsync`, consider adding symmetric cleanup here:
    
    ```csharp
    catch (Exception ex)
    {
        _logger.LogError(ex, "Failed to store content for manifest {ManifestId}", manifest.Id);
        try
        {
            FileOperationsService.DeleteFileIfExists(manifestPath);
    
            // Clean up the CAS-ONLY marker and its directory if we created them
            var contentDir = Path.Combine(_storageRoot, DirectoryNames.Data, updatedManifest.Id.Value);
            var sourcePathFile = Path.Combine(contentDir, FileTypes.SourcePathFileName);
            FileOperationsService.DeleteFileIfExists(sourcePathFile);
            if (Directory.Exists(contentDir) && !Directory.EnumerateFileSystemEntries(contentDir).Any())
                Directory.Delete(contentDir);
    
            var untrackResult = await _referenceTracker.UntrackManifestAsync(manifest.Id, CancellationToken.None);
            ...
        }
    ```
    
    How can I resolve this? If you propose a fix, please make it concise.
  2. GenHub/GenHub/Features/Content/Services/ContentStorageService.cs, line 301-319 (link)

    P1 contentDir not cleaned up in CAS catch block

    This PR newly adds Directory.CreateDirectory(contentDir) (line 294) and File.WriteAllTextAsync(sourcePathFile, "CAS-ONLY", ...) (line 296) inside the happy path of StoreCasContentAsync. However, the catch block only cleans up manifestPath and untracks CAS references — it does not remove contentDir or the partially-written source.path file.

    Concrete failure scenario:

    1. File.WriteAllTextAsync(manifestPath, ...) succeeds (line 289).
    2. Directory.CreateDirectory(contentDir) succeeds (line 294) — directory may be newly created.
    3. File.WriteAllTextAsync(sourcePathFile, "CAS-ONLY", ...) throws (line 296), e.g. due to a disk error.
    4. Catch block: manifestPath is deleted, CAS references are untracked — but contentDir (now empty) is left on disk as an orphan.

    On a subsequent re-store attempt, Directory.CreateDirectory would succeed on the pre-existing empty dir, and the write would be retried — so this is non-blocking. But the comment in the catch block says "only manifest file needs cleanup", which is no longer accurate after this PR adds the contentDir creation.

    Consider mirroring the cleanup done in StoreManifestOnlyAsync for consistency:

    // Also clean up the CAS-only marker directory if it was newly created by this call
    var contentDir = Path.Combine(_storageRoot, DirectoryNames.Data, updatedManifest.Id.Value);
    var sourcePathFile = Path.Combine(contentDir, FileTypes.SourcePathFileName);
    FileOperationsService.DeleteFileIfExists(sourcePathFile);
    // Only remove directory if empty, to avoid deleting legitimate content
    if (Directory.Exists(contentDir) && !Directory.EnumerateFileSystemEntries(contentDir).Any())
        Directory.Delete(contentDir);
    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: GenHub/GenHub/Features/Content/Services/ContentStorageService.cs
    Line: 301-319
    
    Comment:
    **`contentDir` not cleaned up in CAS catch block**
    
    This PR newly adds `Directory.CreateDirectory(contentDir)` (line 294) and `File.WriteAllTextAsync(sourcePathFile, "CAS-ONLY", ...)` (line 296) inside the happy path of `StoreCasContentAsync`. However, the catch block only cleans up `manifestPath` and untracks CAS references — it does not remove `contentDir` or the partially-written `source.path` file.
    
    Concrete failure scenario:
    1. `File.WriteAllTextAsync(manifestPath, ...)` succeeds (line 289).
    2. `Directory.CreateDirectory(contentDir)` succeeds (line 294) — directory may be newly created.
    3. `File.WriteAllTextAsync(sourcePathFile, "CAS-ONLY", ...)` throws (line 296), e.g. due to a disk error.
    4. Catch block: `manifestPath` is deleted, CAS references are untracked — but `contentDir` (now empty) is left on disk as an orphan.
    
    On a subsequent re-store attempt, `Directory.CreateDirectory` would succeed on the pre-existing empty dir, and the write would be retried — so this is non-blocking. But the comment in the catch block says *"only manifest file needs cleanup"*, which is no longer accurate after this PR adds the `contentDir` creation.
    
    Consider mirroring the cleanup done in `StoreManifestOnlyAsync` for consistency:
    ```csharp
    // Also clean up the CAS-only marker directory if it was newly created by this call
    var contentDir = Path.Combine(_storageRoot, DirectoryNames.Data, updatedManifest.Id.Value);
    var sourcePathFile = Path.Combine(contentDir, FileTypes.SourcePathFileName);
    FileOperationsService.DeleteFileIfExists(sourcePathFile);
    // Only remove directory if empty, to avoid deleting legitimate content
    if (Directory.Exists(contentDir) && !Directory.EnumerateFileSystemEntries(contentDir).Any())
        Directory.Delete(contentDir);
    ```
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: GenHub/GenHub/Features/Content/Services/ContentStorageService.cs
Line: 296

Comment:
**`"CAS-ONLY"` is an unextracted magic string**

The sentinel value `"CAS-ONLY"` is hardcoded here and again in `ContentManifestPool.cs:304` where it is compared with `StringComparison.OrdinalIgnoreCase`. Per the project's coding style, magic string constants must be extracted to a dedicated constants class.

The same PR already correctly extracted `"source.path"` into `FileTypes.SourcePathFileName`; the `"CAS-ONLY"` value should follow the same pattern. If the casing is ever changed on the write side (or a typo is introduced), the `OrdinalIgnoreCase` comparison in `ContentManifestPool` will silently mask the mismatch, and CAS-only content will start emitting the warnings again.

Add a constant to `FileTypes.cs`:

```csharp
/// <summary>
/// Sentinel value written to <see cref="SourcePathFileName"/> when content is stored
/// via CAS and has no source directory.
/// </summary>
public const string CasOnlySourceMarker = "CAS-ONLY";
```

Then replace both usages:
- `ContentStorageService.cs:296`: `await File.WriteAllTextAsync(sourcePathFile, FileTypes.CasOnlySourceMarker, cancellationToken);`
- `ContentManifestPool.cs:304`: `if (sourcePath.Trim().Equals(FileTypes.CasOnlySourceMarker, StringComparison.OrdinalIgnoreCase))`

**Rule Used:** Use dedicated constants classes instead of hardcod... ([source](https://app.greptile.com/review/custom-context?memory=53453b3b-b708-4856-b1b0-0cbc8bfe5330))

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: GenHub/GenHub/Features/Content/Services/ContentStorageService.cs
Line: 301-319

Comment:
**`contentDir` not cleaned up in CAS catch block**

This PR newly adds `Directory.CreateDirectory(contentDir)` (line 294) and `File.WriteAllTextAsync(sourcePathFile, "CAS-ONLY", ...)` (line 296) inside the happy path of `StoreCasContentAsync`. However, the catch block only cleans up `manifestPath` and untracks CAS references — it does not remove `contentDir` or the partially-written `source.path` file.

Concrete failure scenario:
1. `File.WriteAllTextAsync(manifestPath, ...)` succeeds (line 289).
2. `Directory.CreateDirectory(contentDir)` succeeds (line 294) — directory may be newly created.
3. `File.WriteAllTextAsync(sourcePathFile, "CAS-ONLY", ...)` throws (line 296), e.g. due to a disk error.
4. Catch block: `manifestPath` is deleted, CAS references are untracked — but `contentDir` (now empty) is left on disk as an orphan.

On a subsequent re-store attempt, `Directory.CreateDirectory` would succeed on the pre-existing empty dir, and the write would be retried — so this is non-blocking. But the comment in the catch block says *"only manifest file needs cleanup"*, which is no longer accurate after this PR adds the `contentDir` creation.

Consider mirroring the cleanup done in `StoreManifestOnlyAsync` for consistency:
```csharp
// Also clean up the CAS-only marker directory if it was newly created by this call
var contentDir = Path.Combine(_storageRoot, DirectoryNames.Data, updatedManifest.Id.Value);
var sourcePathFile = Path.Combine(contentDir, FileTypes.SourcePathFileName);
FileOperationsService.DeleteFileIfExists(sourcePathFile);
// Only remove directory if empty, to avoid deleting legitimate content
if (Directory.Exists(contentDir) && !Directory.EnumerateFileSystemEntries(contentDir).Any())
    Directory.Delete(contentDir);
```

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: 151027f

Context used:

  • Rule used - Use dedicated constants classes instead of hardcod... (source)

- Move 'Check for updates on startup' from Appearance to Updates section (#281)
- Move 'Settings File Location' from Appearance to new Data Directories section (#282)
- Make version widget clickable to copy version to clipboard (#283)
- Fix FullCopyStrategy to use ContentTypePriority for proper mod file precedence (#284)

The content priority fix ensures mod files correctly override base game files,
resolving the issue where the camera mod had no effect in-game.
- Add source.path marker file creation in ContentStorageService after CAS storage
- Update ContentManifestPool to handle CAS-ONLY marker gracefully
- Prevents 'Could not resolve source path' warnings for downloaded content
Camera mods are not functional and should not be downloadable until
properly fixed. Removing from registry to prevent user confusion.
Comment thread GenHub/GenHub/Features/Content/Services/ContentStorageService.cs Outdated
coderabbitai[bot]

This comment was marked as resolved.

Comment thread GenHub/GenHub/Features/Content/Services/ContentStorageService.cs Outdated
coderabbitai[bot]
coderabbitai Bot previously approved these changes Mar 16, 2026
Comment thread GenHub/GenHub/Common/Views/MainView.axaml Outdated
@community-outpost community-outpost deleted a comment from coderabbitai Bot Mar 16, 2026
@community-outpost community-outpost deleted a comment from kilo-code-bot Bot Mar 16, 2026
@community-outpost community-outpost deleted a comment from coderabbitai Bot Mar 16, 2026
… hash

The tooltip now explicitly states that clicking copies the full version
including git hash, not just the displayed short version.
… cleanup safety

- Add SourcePathFileName constant to FileTypes.cs and replace hardcoded strings
- Add priority mappings for all ContentTypes (MapPack, LanguagePack, etc.)
- Throw exceptions for meta types that shouldn't be in workspaces
- Fix recursive directory deletion to only remove directories created in current invocation
- Prevents destruction of pre-existing valid source.path data
Comment thread GenHub/GenHub/Common/Views/MainView.axaml
Comment thread GenHub/GenHub.Core/Models/Workspace/ContentTypePriority.cs Outdated
Comment thread GenHub/GenHub/Features/Content/Services/ContentStorageService.cs Outdated
@community-outpost community-outpost deleted a comment from kilo-code-bot Bot Mar 16, 2026
coderabbitai[bot]

This comment was marked as resolved.

@community-outpost community-outpost deleted a comment from coderabbitai Bot Mar 16, 2026
Comment thread GenHub/GenHub/Features/Content/Services/ContentStorageService.cs
coderabbitai[bot]
coderabbitai Bot previously approved these changes Mar 16, 2026
… appearance

Add style overrides to keep background transparent on hover and press,
ensuring the version widget looks identical to the original TextBlock.
coderabbitai[bot]

This comment was marked as resolved.

@community-outpost community-outpost deleted a comment from kilo-code-bot Bot Mar 17, 2026
@community-outpost community-outpost deleted a comment from coderabbitai Bot Mar 17, 2026
@community-outpost community-outpost deleted a comment from coderabbitai Bot Mar 17, 2026
coderabbitai[bot]
coderabbitai Bot previously approved these changes Mar 17, 2026
coderabbitai[bot]

This comment was marked as resolved.

@community-outpost community-outpost deleted a comment from kilo-code-bot Bot Mar 17, 2026
@community-outpost community-outpost deleted a comment from coderabbitai Bot Mar 17, 2026
@mnoserat
Copy link
Copy Markdown
Contributor

please fix the hover effect when you are hovering a button

@undead2146
Copy link
Copy Markdown
Member Author

undead2146 commented Mar 20, 2026

please fix the hover effect when you are hovering a button

@mnoserat
I dont understand be a bit more precise and provide a sceenshot.

@mnoserat
Copy link
Copy Markdown
Contributor

please fix the hover effect when you are hovering a button

@mnoserat I dont understand be a bit more precise and provide a sceenshot.

i will send a video on discord

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants