Skip to content

feat(ui): theme management fixes and enhancements#1700

Merged
doodlum merged 3 commits into
community-shaders:devfrom
soda3000:dev-12-01-2026
Jan 13, 2026
Merged

feat(ui): theme management fixes and enhancements#1700
doodlum merged 3 commits into
community-shaders:devfrom
soda3000:dev-12-01-2026

Conversation

@soda3000
Copy link
Copy Markdown
Contributor

@soda3000 soda3000 commented Jan 12, 2026

Summary by CodeRabbit

  • Bug Fixes

    • Improved theme loading reliability with fallback to default if a preset fails.
    • Preserve user-selected preset across startup; re-applies presets after defaults are set.
    • Legacy theme data is handled and normalized when found.
  • New Features

    • Validation feedback and inline highlighting when creating themes with empty names.
    • Shows which settings changed when updating a theme and displays success/failure feedback.
  • Chores

    • Theme data now persists only in theme presets; main config no longer stores full Theme objects.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 12, 2026

📝 Walkthrough

Walkthrough

Restructures theme persistence to store Theme data in theme presets (not main config), loads themes via SelectedThemePreset with legacy support and fallback, adds guarded preset load/restore, and enhances settings UI with update feedback and validation.

Changes

Cohort / File(s) Summary
Theme Loading & Persistence
src/Menu.cpp, manifest_file, CMakeLists.txt
Refactors Load/Save/Init to keep Theme in presets: removes Theme from main config on save, restores presets via guarded LoadThemePreset (backup/restore), normalizes legacy Theme data if present, and re-applies user-selected preset after defaults.
Theme Update Feedback & Validation (UI)
src/Menu/SettingsTabRenderer.cpp
Adds runtime state for validation and update feedback (showValidationError, showUpdateFeedback, updateSuccess), ChangedSetting struct and recursive diffWalker to capture changes, displays success/failure messages and changed-setting list, and adds inline validation/highlight for Create Theme dialog.
Preset-Based State Management
src/State.cpp
Changes LoadTheme to use SelectedThemePreset from SettingsUser.json (discovering presets if needed) and to fall back to "Default" on failure; SaveTheme becomes a no-op that logs persistence to SettingsUser.json rather than writing SettingsTheme.json.

Sequence Diagram

sequenceDiagram
    participant Menu
    participant ThemeManager
    participant State
    participant Settings
    participant SettingsTabRenderer

    Menu->>Settings: Read SelectedThemePreset
    alt preset name present
        Menu->>ThemeManager: Ensure themes discovered
        ThemeManager-->>Menu: Presets available
        Menu->>Menu: Backup current Theme
        Menu->>Menu: LoadThemePreset(presetName)
        alt load success
            Menu->>Menu: Apply preset (fonts/icons scheduled)
            Menu->>Settings: Update SelectedThemePreset
        else load failure
            Menu->>Menu: Restore Theme from backup
            Menu->>Menu: Attempt "Default" preset
        end
    else no preset (legacy)
        Menu->>Settings: Read legacy Theme blob
        Menu->>Menu: Normalize & apply legacy Theme
    end

    SettingsTabRenderer->>Menu: SaveTheme()/Update request
    Menu->>SettingsTabRenderer: Compute diffs (ChangedSetting list)
    SettingsTabRenderer->>SettingsTabRenderer: Show update feedback / validation
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Suggested reviewers

  • doodlum

"a rabbit nibbles at JSON seams,
presets stitched from midnight dreams,
legacy whispers gently tamed,
backups saved, new themes proclaimed,
hop—fonts refreshed—huzzah! 🐇✨"

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'feat(ui): theme management fixes and enhancements' accurately summarizes the main changes, which involve refactoring theme loading/saving logic, adding theme preset management, and improving the theme UI with validation and feedback mechanisms.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

Using provided base ref: ea9121f
Using base ref: ea9121f
Base commit date: 2026-01-12T21:38:42Z (Monday, January 12, 2026 09:38 PM)
No actionable suggestions for changed features.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/Menu.cpp (1)

474-491: Duplicate assignment of SelectedThemePreset

settings.SelectedThemePreset = themeName; is assigned twice - first at line 474 and again at line 488. The second assignment is redundant.

Proposed fix
-			// Schedule deferred icon reload to apply theme-specific icon overrides
-			pendingIconReload = true;
-
-			// Apply background blur enabled state from theme
-			BackgroundBlur::SetEnabled(settings.Theme.BackgroundBlurEnabled);
-
-			// Mark preset as selected on successful load
-			settings.SelectedThemePreset = themeName;
-
-			logger::info("Applied theme preset: {}", themeName);
+			// Schedule deferred icon reload to apply theme-specific icon overrides
+			pendingIconReload = true;
+
+			// Apply background blur enabled state from theme
+			BackgroundBlur::SetEnabled(settings.Theme.BackgroundBlurEnabled);
+
+			logger::info("Applied theme preset: {}", themeName);
🤖 Fix all issues with AI agents
In @src/Menu.cpp:
- Line 504: Remove the stray comment artifact "// ... rest of the code remains
the same ..." from Menu.cpp (located around the Menu class / menu-related
functions area) so it doesn't appear in the compiled source; simply delete that
comment line and ensure no other code on that line was accidentally removed or
altered (verify symbols like Menu::render or adjacent functions remain
unchanged).
🧹 Nitpick comments (1)
src/Menu/SettingsTabRenderer.cpp (1)

533-554: Update feedback UI section

The feedback section appropriately:

  • Shows success/failure messages with theme-appropriate colors
  • Lists changed settings with before/after values when changes are detected
  • Uses proper indentation for the change list

Minor observation: showUpdateFeedback is never reset to false, so the feedback will persist until the tab is closed and reopened (static variable resets). Consider adding a dismiss mechanism if this becomes a UX concern.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ea9121f and 666f02e.

📒 Files selected for processing (3)
  • src/Menu.cpp
  • src/Menu/SettingsTabRenderer.cpp
  • src/State.cpp
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{cpp,cxx,cc,c,h,hpp,hxx,hlsl,hlsli,fx,fxh,py}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Do not include TODO/FIXME placeholders; provide complete, working solutions

Files:

  • src/State.cpp
  • src/Menu/SettingsTabRenderer.cpp
  • src/Menu.cpp
src/**/*.{cpp,cxx,cc,h,hpp,hxx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.{cpp,cxx,cc,h,hpp,hxx}: Ensure SE/AE/VR runtime compatibility; use runtime detection patterns (e.g., REL::RelocateMember())
Include robust error handling and resource management with graceful degradation in the plugin code

Files:

  • src/State.cpp
  • src/Menu/SettingsTabRenderer.cpp
  • src/Menu.cpp
**/*

⚙️ CodeRabbit configuration file

**/*: When reviewing PRs, please provide suggestions for:

  1. Conventional Commit Titles (if not following https://www.conventionalcommits.org/ or
    if the existing title does not describe the code changes):
    Format: type(scope): description
    Length: 50 characters limit for title, 72 for body
    Style: lowercase description, no ending period
    Examples:

    • feat(vr): add cross-eye sampling
    • fix(water): resolve flowmap bug
    • docs: update shader documentation
  2. Issue References (if PR fixes bugs or implements features):
    Suggest adding appropriate GitHub keywords:

    • "Fixes #123" or "Closes #123" for bug fixes
    • "Implements #123" or "Addresses #123" for features
    • "Related to #123" for partial implementations

Otherwise, use your standard review approach focusing on code quality.

Files:

  • src/State.cpp
  • src/Menu/SettingsTabRenderer.cpp
  • src/Menu.cpp
🧠 Learnings (1)
📓 Common learnings
Learnt from: alandtse
Repo: doodlum/skyrim-community-shaders PR: 0
File: :0-0
Timestamp: 2025-07-05T05:20:45.823Z
Learning: In the skyrim-community-shaders repository, file deletion error handling improvements that replace existence checks and try-catch blocks with std::filesystem::remove error-code-based approaches are considered bug fixes rather than refactoring, as they address inadequate error handling and misleading log messages.
Learnt from: alandtse
Repo: doodlum/skyrim-community-shaders PR: 0
File: :0-0
Timestamp: 2025-06-24T07:17:36.604Z
Learning: When reviewing PRs, always clarify the scope if there are multiple related features or dependencies. WeatherPicker was a separate PR that was already merged, while this PR focuses specifically on WetnessEffects climate preset system enhancements.
Learnt from: ThePagi
Repo: doodlum/skyrim-community-shaders PR: 1369
File: src/Features/SnowCover.cpp:277-293
Timestamp: 2025-08-05T18:22:40.578Z
Learning: In the skyrim-community-shaders SnowCover feature, the wstrtostr and strtowstr utility functions defined in src/Features/SnowCover.cpp are unused and should be removed rather than fixed, as confirmed by the original author ThePagi.
Learnt from: ThePagi
Repo: doodlum/skyrim-community-shaders PR: 1369
File: src/Features/SnowCover.cpp:515-515
Timestamp: 2025-10-02T14:20:33.454Z
Learning: In the Community Shaders codebase (skyrim-community-shaders repository), hardcoded shader resource slot numbers are used directly in code rather than being defined as named constants. This is the established convention and should not be flagged as an issue.
Learnt from: jiayev
Repo: doodlum/skyrim-community-shaders PR: 0
File: :0-0
Timestamp: 2025-08-03T18:37:19.690Z
Learning: ISReflectionsRayTracing.hlsl and ISWorldMap.hlsl in the skyrim-community-shaders repository are image-space post-processing shaders that perform color sampling and blending operations that need proper linear color space handling for the linear lighting system. ISReflectionsRayTracing handles screen-space reflections and should use conditional Color::IrradianceToLinear/Gamma conversions similar to ISCompositeLensFlareVolumetricLighting.hlsl. ISWorldMap performs 7x7 color accumulation that should be done in linear space similar to the pattern used in ISSAOComposite.hlsl.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build plugin and addons
🔇 Additional comments (10)
src/State.cpp (2)

848-874: LGTM - LoadTheme refactored to use preset-based loading

The implementation correctly:

  1. Returns early when no preset is selected
  2. Ensures default themes exist and discovers themes if needed
  3. Falls back to "Default" preset on failure and updates SelectedThemePreset accordingly

876-881: SaveTheme() now a no-op for backward compatibility

The function is intentionally converted to a no-op since theme data is now persisted via theme preset files rather than a separate SettingsTheme.json. The log message clearly documents this behavior change.

src/Menu/SettingsTabRenderer.cpp (4)

333-344: Static UI state variables for theme editing

The introduction of showValidationError, showUpdateFeedback, updateSuccess, and the ChangedSetting struct for tracking diffs is appropriate for ImGui's immediate-mode paradigm where state must persist across frames.


476-500: Recursive diff walker for theme comparison

The diffWalker lambda correctly:

  1. Handles object types by recursing through the union of keys
  2. Falls back to direct comparison for arrays and primitives
  3. Records path, old value, and new value for differences

One consideration: deeply nested themes could cause stack issues, but in practice theme JSON structures are shallow enough that this is not a concern.


566-584: Validation error UI for theme name input

The validation flow correctly:

  1. Highlights the input field border in red when empty and error is shown
  2. Displays an inline error message below the input
  3. Resets the error state when a valid name is provided (line 606)

Good UX pattern for inline form validation.


603-630: Create theme button handler with validation

The logic properly:

  1. Validates theme name before proceeding
  2. Sets showValidationError = true when name is empty
  3. Clears showValidationError = false on valid input before proceeding
  4. Logs success/failure appropriately
src/Menu.cpp (4)

216-241: Load() preserves Theme state and adds legacy migration

The implementation:

  1. Preserves current Theme before loading to prevent override from config
  2. Restores Theme after loading settings
  3. Provides legacy support for old configs that have Theme data but no SelectedThemePreset
  4. Normalizes font roles and validates fonts when loading legacy data

This ensures smooth migration for users upgrading from the old theme storage model.


271-278: Save() strips Theme from config output

Correctly removes the Theme object from the JSON output since theme data should only be stored in theme preset files. This enforces the new persistence model where SelectedThemePreset is the only theme-related field in the main config.


342-462: Extensive field-by-field fallback for malformed theme data

The manual field-by-field loading with individual try-catch blocks is verbose but provides robust handling for themes with incomplete or malformed data. This ensures older theme files or user-edited themes with missing fields don't crash the loader.

The approach is appropriate for user-facing data that may be hand-edited, though it creates a maintenance burden if new theme fields are added in the future.


534-546: Init() re-applies user preset after defaults

The logic correctly ensures the user's selected preset is applied after default theme initialization:

  1. Checks if a preset is selected
  2. Discovers themes if not yet done
  3. Attempts to load the preset with appropriate logging

This prevents the default theme from overriding the user's saved preference.

Comment thread src/Menu.cpp Outdated
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jan 12, 2026

✅ A pre-release build is available for this PR:
Download

Automated formatting by clang-format, prettier, and other hooks.
See https://pre-commit.ci for details.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/Menu/SettingsTabRenderer.cpp (2)

335-344: Stale feedback state when switching themes or re-entering the tab.

The showUpdateFeedback and changedSettings static variables are never reset when the user switches to a different theme or navigates away and back. This could display outdated feedback from a previous operation.

Consider resetting the feedback state when the theme selection changes:

♻️ Suggested fix

Add a reset when theme selection changes (around line 412-418):

 if (globals::menu->LoadThemePreset(selectedTheme)) {
     // Theme loaded successfully, update UI
     themeSettings = globals::menu->GetSettings().Theme;
+    // Reset update feedback when switching themes
+    showUpdateFeedback = false;
+    changedSettings.clear();
 }

456-462: Reset validation error state when opening the create theme popup.

When the popup is opened, the input fields are cleared (lines 460-462), but showValidationError is not reset. This means if a user previously triggered a validation error, cancelled the popup, and reopened it, they would see the error message immediately with an empty field.

♻️ Suggested fix
 if (isCreatingNewTheme) {
     // Show popup for new theme creation
     showCreateThemePopup = true;
     // Clear the input fields
     memset(newThemeName, 0, sizeof(newThemeName));
     memset(newThemeDisplayName, 0, sizeof(newThemeDisplayName));
     memset(newThemeDescription, 0, sizeof(newThemeDescription));
+    // Reset validation state when opening popup
+    showValidationError = false;
 }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 666f02e and 1c1d437.

📒 Files selected for processing (2)
  • src/Menu.cpp
  • src/Menu/SettingsTabRenderer.cpp
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{cpp,cxx,cc,c,h,hpp,hxx,hlsl,hlsli,fx,fxh,py}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Do not include TODO/FIXME placeholders; provide complete, working solutions

Files:

  • src/Menu/SettingsTabRenderer.cpp
  • src/Menu.cpp
src/**/*.{cpp,cxx,cc,h,hpp,hxx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.{cpp,cxx,cc,h,hpp,hxx}: Ensure SE/AE/VR runtime compatibility; use runtime detection patterns (e.g., REL::RelocateMember())
Include robust error handling and resource management with graceful degradation in the plugin code

Files:

  • src/Menu/SettingsTabRenderer.cpp
  • src/Menu.cpp
**/*

⚙️ CodeRabbit configuration file

**/*: When reviewing PRs, please provide suggestions for:

  1. Conventional Commit Titles (if not following https://www.conventionalcommits.org/ or
    if the existing title does not describe the code changes):
    Format: type(scope): description
    Length: 50 characters limit for title, 72 for body
    Style: lowercase description, no ending period
    Examples:

    • feat(vr): add cross-eye sampling
    • fix(water): resolve flowmap bug
    • docs: update shader documentation
  2. Issue References (if PR fixes bugs or implements features):
    Suggest adding appropriate GitHub keywords:

    • "Fixes #123" or "Closes #123" for bug fixes
    • "Implements #123" or "Addresses #123" for features
    • "Related to #123" for partial implementations

Otherwise, use your standard review approach focusing on code quality.

Files:

  • src/Menu/SettingsTabRenderer.cpp
  • src/Menu.cpp
🧠 Learnings (1)
📓 Common learnings
Learnt from: alandtse
Repo: doodlum/skyrim-community-shaders PR: 0
File: :0-0
Timestamp: 2025-07-05T05:20:45.823Z
Learning: In the skyrim-community-shaders repository, file deletion error handling improvements that replace existence checks and try-catch blocks with std::filesystem::remove error-code-based approaches are considered bug fixes rather than refactoring, as they address inadequate error handling and misleading log messages.
Learnt from: alandtse
Repo: doodlum/skyrim-community-shaders PR: 0
File: :0-0
Timestamp: 2025-06-24T07:17:36.604Z
Learning: When reviewing PRs, always clarify the scope if there are multiple related features or dependencies. WeatherPicker was a separate PR that was already merged, while this PR focuses specifically on WetnessEffects climate preset system enhancements.
Learnt from: ThePagi
Repo: doodlum/skyrim-community-shaders PR: 1369
File: src/Features/SnowCover.cpp:277-293
Timestamp: 2025-08-05T18:22:40.578Z
Learning: In the skyrim-community-shaders SnowCover feature, the wstrtostr and strtowstr utility functions defined in src/Features/SnowCover.cpp are unused and should be removed rather than fixed, as confirmed by the original author ThePagi.
Learnt from: ThePagi
Repo: doodlum/skyrim-community-shaders PR: 1369
File: src/Features/SnowCover.cpp:515-515
Timestamp: 2025-10-02T14:20:33.454Z
Learning: In the Community Shaders codebase (skyrim-community-shaders repository), hardcoded shader resource slot numbers are used directly in code rather than being defined as named constants. This is the established convention and should not be flagged as an issue.
Learnt from: jiayev
Repo: doodlum/skyrim-community-shaders PR: 0
File: :0-0
Timestamp: 2025-08-03T18:37:19.690Z
Learning: ISReflectionsRayTracing.hlsl and ISWorldMap.hlsl in the skyrim-community-shaders repository are image-space post-processing shaders that perform color sampling and blending operations that need proper linear color space handling for the linear lighting system. ISReflectionsRayTracing handles screen-space reflections and should use conditional Color::IrradianceToLinear/Gamma conversions similar to ISCompositeLensFlareVolumetricLighting.hlsl. ISWorldMap performs 7x7 color accumulation that should be done in linear space similar to the pattern used in ISSAOComposite.hlsl.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build plugin and addons
🔇 Additional comments (9)
src/Menu/SettingsTabRenderer.cpp (4)

5-5: LGTM!

The <set> include is appropriately added to support the std::set<std::string> usage in the diffWalker lambda for collecting unique keys from both JSON objects.


476-500: LGTM!

The diffWalker recursive lambda correctly handles JSON comparison by:

  • Iterating over the union of keys from both old and new objects
  • Recursively descending into nested objects
  • Recording differences with path, old value, and new value

The implementation properly handles missing keys (using empty json() as fallback) and null values.


566-584: LGTM!

The validation error highlighting correctly:

  • Pushes style color and var before the input field when validation fails
  • Pops them immediately after the input field (properly balanced)
  • Shows an inline error message for user feedback

The implementation follows ImGui best practices for conditional styling.


533-554: LGTM with minor UX consideration.

The update feedback display correctly shows success/failure status and lists changed settings. The implementation is functional.

Consider limiting the displayed changes or adding a dismiss button if themes can have many settings changed at once, but this is optional for the current scope.

src/Menu.cpp (5)

216-265: LGTM!

The Load() method correctly implements:

  • Theme state preservation during config loading (backup before, restore after)
  • Legacy support for old configs that have Theme data but no SelectedThemePreset
  • First-launch defaulting to "Default" theme
  • Fallback handling when saved preset fails to load

The separation of theme data from main config aligns well with the PR's goal of storing themes in preset files.


268-278: LGTM!

The Save() method correctly enforces the new theme persistence model:

  • Saves all settings including SelectedThemePreset
  • Explicitly removes Theme object from the output JSON
  • Theme data is now exclusively stored in preset files, not the main config

This clean separation prevents stale theme data from accumulating in the main config.


337-498: Robust error handling for malformed theme data.

The LoadThemePreset() method now includes comprehensive error handling:

  • Creates a backup before attempting to load
  • Handles json::out_of_range specifically for FullPalette array size mismatch (common when ImGui adds new color indices)
  • Falls back to default values for missing/malformed fields
  • Restores backup on any unhandled exception

The nested try-catch structure correctly differentiates between recoverable partial data issues and fatal errors.


351-420: Defensive field-by-field loading for theme resilience.

The individual try-catch blocks for each field allow themes with partial data to load successfully, using defaults for any missing or malformed fields. While the empty catch blocks are typically discouraged, this is an appropriate pattern here for graceful degradation when loading user-created theme files.


529-541: LGTM!

The theme re-application during Init() correctly:

  • Ensures theme discovery is complete before attempting to load
  • Re-applies the user's selected preset after the default theme baseline is established
  • Logs outcomes for debugging without failing initialization if the preset is unavailable

This ensures consistent theme application regardless of initialization order.

@doodlum doodlum merged commit 0bb5c37 into community-shaders:dev Jan 13, 2026
16 checks passed
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